views:

775

answers:

2

So I'm attempting to write a Django reusable app that provides a method for displaying your Twitter feed on your page. I know well that it already exists 20 times. It's an academic exercise. :)

Directory structure is pretty simple:

myproject
|__  __init__.py
|__  manage.py
|__  settings.py
|__  myapp
     |__  __init__.py
     |__  admin.py
     |__  conf
          |__  __init__.py
          |__  appsettings.py
     |__  feedparser.py
     |__  models.py
     |__  templates
          |__  __init__.py
     |__  templatetags
          |__  __init__.py
          |__  twitterfeed.py
     |__  views.py
|__  templates
          |__  base.html
|__  urls.py

When running the Django shell, the functions defined in twitterfeed.py work perfectly. I also believe that I have the template tags properly named and registered.

As you can see, I use the excellent Universal Feed Parser. My problem is not within UFP itself, but in UFP's inability to be called while importing the template tag library. When I {% load twitterfeed %} in base.py, I get the following error:

'twitterfeed' is not a valid tag library: Could not load template library from django.templatetags.twitterfeed, No module named feedparser

I import feedparser using the following statement:

import re, datetime, time, myapp.feedparser

The best I can tell, this error message is slightly deceiving. I think there's an ImportError going on when the template library is loaded, and this is Django's interpretation of it.

Is there any way I can import feedparser.py within my reusable app without requiring users of the app to place feedparser somewhere in their PythonPath?

Thanks!

+4  A: 

I solve this kind of problem (shipping libraries that are dependencies for my overall project) in the following way. First, I create an "ext" directory in the root of my project (in your case that would be myproject/ext). Then I place dependencies such as feedparser in that ext directory - myproject/ext/feedparser

Finally, I change my manage.py script to insert the ext/ directory at the front of sys.path. This means both ./manage.py runserver and ./manage.py shell will pick up the correct path:

# manage.py
import os, sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'ext'))
# ... rest of manage.py

I find this works really well if you don't want to mess around with things like virtualenvs. When you deploy your project you have to make sure the path is correct as well - I usually solve this by adding the same sys.path.insert line to the start of my mod_wsgi app.wsgi file.

Simon Willison
Thanks Simon!So...it's safe to say that it's not possible to accomplish in a way that maintains app portability?
cpharmston
I wouldn't say that... you can definitely include dependencies within an app (rather than an ext/ directory), but it can be a bit more fiddly, as you've discovered from your question. I'm lazy, so I tend to just use the ext/ method because I know it works.
Simon Willison
Including apps directly within the app causes subtle bugs: both the app directory and its parent are in the path, so it's possible to import it twice, as both "mypackage.app" and "app". It's safer to stick it in a subdirectory in the path, so there's only one possible name for it ("app").
Glenn Maynard
A: 

This looks like one of those annoying relative path issues - solved in Python 2.6 and higher (where you can do import ..feedparser etc) but often a bit tricky on older versions. One cheap and cheerful way to fix this could be just to move feedparser.py in to your templatetags directory, as a sibling to twitterfeed.py

Simon Willison