To continue my last post about reusable Django apps, I’d like to talk about setuptools, show you a way to install Django by using setuptools and propose the reintroduction into Django’s code, even if setuptools have been dumped by the developers not long ago due to manageability reasons. Features like entry points, egg-files, the develop command, automatic versioning and tight integration with the Python Package Index make it worth installing.
Reusable Django apps
I started django-reusableapps considering the widespread use of setuptools in highly dynamic Python web projects like Turbogears and Pylons and the still unresolved problem of a smart “Django apps” API. Being the responsible Google Summer of Code student for this topic, I now decided (some months after my GSoC) to go with setuptools, dump my GSoC code and rethink the whole reason why Django apps should be pluggable: DRY.
It’s very easy to develop a custom Django app nowadays, but quite hard to quickly create meshed up projects, manage production sites and share the involved app with a larger audience in a semi-automatic manner like the Python Package Index.
The entry points for example, enable Python applications (such as Django or any Django-based app) to provide hooks to other applications in an automatic but unobstrusive, hence powerful fashion – a perfect choice for an Application API, from my point of view.
django-reusableapps resembles the plugin loading mechanism of Trac and should be considered a very temporary solution, until Django supports app-loading from eggs natively.
Using setuptools with Django aps
To start with a setuptools-based Django installation you need a new setup.py file to replace the version provided by Django (note this currently only works with a current subversion checkout). Please delete the symbolic link or the django directory you might have created in the site-packages directory by following Django’s installation instructions.
Then, add the following text to the setup.cfg file in the django source directory:
[egg_info] tag_build = dev tag_svn_revision = 1
Please have a look at Phillip’s comment, update your setup.py and setup.cfg files and reinstall Django.
This tells setuptools to generate version numbers like 0.97dev-r6706 during the installation, marking it as a developer snapshot which would be superseded by a 0.97 release, once it’s released.
You can then use virtually any command of setup.py: install, develop, bdist_egg, bdist_mpkg, bdist_wininst, bdist_rpm etc.
One bonus of the new setup.py file is the instant availablitiy of a django command, the same utility known as django-admin.py and manage.py - just by declaring a console_scripts entry point in the setup():
entry_points = {
'console_scripts': 'django = django.core.management:execute_from_command_line',
},
Django’s SVN release and setuptools
There is an easy way with setuptools to keep track of the changes in Django’s svn tree. Use the develop command to avoid installing Django over and over again after each subversion update:
$ cd django_src $ sudo python setup.py develop ... Creating /Library/Python/2.5/site-packages/Django.egg-link (link to .) Adding Django 0.97dev-r6704 to easy-install.pth file Installed /Users/Jannis/Code/django_src ...
This tells setuptools to create a link in the site-packages directory to the current directory with the Django checkout. Updating Django with subversion is also easy:
$ cd django_src $ svn update U django/contrib/admin/templatetags/admin_list.py U django/template/defaultfilters.py U tests/regressiontests/templates/filters.py Updated to revision 6708. $ sudo python setup.py develop ... Creating /Library/Python/2.5/site-packages/Django.egg-link (link to .) Removing Django 0.97dev-r6704 from easy-install.pth file Adding Django 0.97dev-r6706 to easy-install.pth file Installed /Users/Jannis/Code/django_src ...
You need to run python setup.py develop after every update in order to keep the version string of Django in sync with the checkout. The already existing link to the “old” version (0.97dev-r6704) is then replaces by a link to the “new” version (0.97dev-r6706).
Django can be a dependency of a Django-based app
While you build a Django app, you might consider to include information about dependencies which are required to be installed by using setuptools' install_requires keyword. Fortunately this also applies to Django developer snapshots, if originally installed with setuptools like I described above. Just add a keyword to setup() of your app’s setup.py:
install_requires = ['Django >= 0.97dev-r6706,==dev',],
This tells setuptools to install Django (if necessary) either by using a current subversion checkout or the newest release if available.
Django can have dependencies and extras
Django tries hard to have as less dependencies as possible by bundling some essential packages (e.g. simplejson) and informing users to install packages if required (e.g. Flup, PIL, PyYaml and all database adapters). These dependencies could also be defined in a setup.py file in the extras_require keyword:
extras_require = {
'MySQL': ["MySQLdb>=1.2.1p2"],
‘SQLite’: ["pysqlite>=2.0.3"],
‘PostgreSQL’: ["psycopg>=1.1.21"],
‘PostgreSQL2′: ["psycopg2>=2.0.5"],
‘Oracle’: ["cx_Oracle>=4.3.1"],
‘PyYaml’: ["PyYaml"],
}
Later, while installing Django, extras could be specified in square brackets — for example one or more extras:
$ easy_install Django[PostgreSQL] $ easy_install Django[SQLite, PyYaml] $ easy_install http://code.djangoproject.com/svn/django/trunk/
Now imagine the same functionality with Django-based apps. Think of installing a weblog and all its recommended dependencies by running:
$ easy_install ColtraneBlog[DefaultThemes]
Reusable Django-based apps in the (near?) future
I hereby propose the reintroduction of setuptools because of the great advantages while designing a future “Django Applications” API.
Why not add the app-loading mechanism of django-reusableapps to Django’s core. Specifically add it to django.db.models.loading or another module which looks for qualified apps, either depending on INSTALLED_APPS or the django.apps entry point.

November 22nd, 2007 at 11:37 pm
The django-reusableapps links throughout the article are wrong, they should point to http://code.google.com/p/django-reusableapps/ I believe.
November 22nd, 2007 at 11:42 pm
Thanks Jonathan! It’s fixed now..
November 26th, 2007 at 5:40 am
You’re missing a ‘tag_build = dev’ from that setup.cfg; you need your versions to be like “0.97dev-rNNNN” in order to be treated as older versions than a plain “0.97″.
November 26th, 2007 at 11:45 am
Thanks Phillip!
Django has its own function to determine the current SVN version (django.get_version). It takes a Django version string like (0, 97, ‘pre’) and returns something like u’0.97-pre-SVN-6708′, which is unfortunately wrong, since it gives the last revision number of the whole SVN repository including branches and tag, not only from the trunk.
You are right, I forgot to change the version string to just including the pure version number (like 0.97) and let setuptools handle the rest. My setup.py file and the required keywords for setup.cfg are now modified. Thanks again!
November 26th, 2007 at 10:09 pm
With a little hack you can use setuptools with distutils setup.py’s. Setuptools monkeypatches distutils.core.setup(), so anything run after you import setuptools gets all the setuptools features. To make use of this you can do:
python -c “import setuptools, os; __file__=os.path.abspath(’setup.py’); execfile(’setup.py’)” develop
Or whatever command you want to run.
November 28th, 2007 at 1:27 pm
I didn’t know that setuptools works in this way. So, it should be feasible to provide a setup.py file which elegantly falls back to distutils if setuptools can’t be imported.
The only delicate issue is the package data, though distuils *has* support for “package_data” in 2.4, it’s not Django’s minimal Python version support 2.3.
So the only solution is the “data_files” keyword, automatically filled like in the current http://code.djangoproject.com/browser/django/trunk/setup.py and a “try: import except: ImportError”-switch, right?
December 2nd, 2007 at 2:19 am
Is there a way to get this working with a git checkout of django? For now it only creates a django0.97dev package, so no revision is included. Does setuptools check for .svn to get the info?
December 2nd, 2007 at 2:38 pm
Yeah, it checks for the svn revision in get_svn_revision() (egg_info.py in setuptools). This is unfortunate and should be replaced by a pluggable interface. Could you please try setuptools-git?
I guess the actual version string parser of setuptools needs increasing revision numbers, but don’t know enough about git’s internals. Did you make a clone of the Django repo with git-svn? Maby the “find-rev” command of git-svn could be used for this?
December 2nd, 2007 at 6:09 pm
“git svn log –limit 1″ did the trick for me (I cloned the repo), but if someone is using a git checkout from a git-svn repo, git svn log won’t work, so I am using “git log” and search for git-svn-id :)
The patch at http://apolloner.eu/files/setuptools_git_svn.diff (for egg_info.py) tries to use the “git log” command if “tag_svn_revision” is true and a .git file/dir exists.
Now I am able to run python setup.py develop :)
December 2nd, 2007 at 6:15 pm
Wow, this is great stuff. This should definitely be applicable to bzr and all the other versioning system with svn support.