Here are my notes on starting a brand new, versioned, readily distributed Python project. Examples show a bash session, but Python, virtualenv, pip, distribute, paster, and hg all work on Windows (from whence more and more Python GIS programmers come) as well.
1. Create a fresh virtual environment. Why? So you don't clutter your system Python with in-development code, and to keep possibly conflicting versions of dependencies out of your development environment. It's probably even more useful for working on code that we may clone from another repository than it is for starting from scratch.
2. Install paster if it wasn't already installed under our original Python (or if you used the --no-site-packages option). It's a script that creates a basic, normal source layout for a new package, prompts us for essential project metadata, and writes a working setup.py.
$ cd foo $ ./bin/pip install PasteScript Downloading/unpacking PasteScript Downloading PasteScript-1.7.3.tar.gz (127Kb): 127Kb downloaded Running setup.py egg_info for package PasteScript Downloading/unpacking Paste>=1.3 (from PasteScript) Downloading Paste-1.7.2.tar.gz (373Kb): 373Kb downloaded Running setup.py egg_info for package Paste Downloading/unpacking PasteDeploy (from PasteScript) Downloading PasteDeploy-1.3.3.tar.gz Running setup.py egg_info for package PasteDeploy warning: no files found matching 'docs/*.html' warning: no previously-included files found matching 'docs/rebuild' Installing collected packages: Paste, PasteDeploy, PasteScript ... Successfully installed Paste PasteDeploy PasteScript
3. Create the new project.
$ ./bin/paster create -t basic_package foogis Selected and implied templates: PasteScript#basic_package A basic setuptools-enabled package Variables: egg: foogis package: foogis project: foogis Enter version (Version (like 0.1)) ['']: 0.1 Enter description (One-line description of the package) ['']: FooGIS Enter long_description (Multi-line description (in reST)) ['']: Enter keywords (Space-separated keywords/tags) ['']: gis Enter author (Author name) ['']: Sean Gillies Enter author_email (Author email) ['']: email@example.com Enter url (URL of homepage) ['']: http://example.com/foogis Enter license_name (License name) ['']: DWTFYWWI Enter zip_safe (True/False: if the package can be distributed as a .zip file) [False]: Creating template basic_package Creating directory ./foogis Recursing into +package+ Creating ./foogis/foogis/ Copying __init__.py to ./foogis/foogis/__init__.py Copying setup.cfg to ./foogis/setup.cfg Copying setup.py_tmpl to ./foogis/setup.py Running /tmp/foo/bin/python2.6 setup.py egg_info
What we get is
$ find foogis foogis foogis/foogis foogis/foogis/__init__.py foogis/foogis.egg-info foogis/foogis.egg-info/dependency_links.txt foogis/foogis.egg-info/entry_points.txt foogis/foogis.egg-info/not-zip-safe foogis/foogis.egg-info/PKG-INFO foogis/foogis.egg-info/SOURCES.txt foogis/foogis.egg-info/top_level.txt foogis/setup.cfg foogis/setup.py
The package code iself is in foogis/foogis. The foogis directory holds distribution files. Metadata, README, etc.
4. This is a good time to get everything under revision control (except the egg-info, as Tarek points out).
6. Write some tests. The sooner we start testing, the better. Few things are more painful than writing tests a few hundred lines of code down the road.
Here's the first test, taking advantage of nose's conventions for finding tests.
Nose lets you start testing immediately, avoiding the intricacies of unittest until you need them. Before we run the tests, we'll fully activate the virtual environment, adjusting executable paths so that we don't have to be explicit about them (It's true, as pointed out in comments, that we could have done this at the outset).
Without any code, the tests fail, of course.
$ nosetests foogis E ====================================================================== ERROR: Failure: ImportError (cannot import name Point) ---------------------------------------------------------------------- Traceback (most recent call last): File "/private/tmp/foo/lib/python2.6/site-packages/nose/loader.py", line 382, in loadTestsFromName addr.filename, addr.module) File "/private/tmp/foo/lib/python2.6/site-packages/nose/importer.py", line 39, in importFromPath return self.importFromDir(dir_path, fqname) File "/private/tmp/foo/lib/python2.6/site-packages/nose/importer.py", line 86, in importFromDir mod = load_module(part_fqname, fh, filename, desc) File "/private/tmp/foo/foogis/foogis/tests.py", line 1, in <module> from foogis import Point ImportError: cannot import name Point ---------------------------------------------------------------------- Ran 1 test in 0.001s FAILED (errors=1)
7. Write code and test.
Now, we run nosetests again with the coverage module:
The tests pass, but we're missing a test of the __repr__ method on line 6. Let's add one.
and re-run the tests.
8. Commit the changes and make a distribution.
$ hg add foogis/tests.py $ hg commit -m "Added a Point class, with tests" $ python setup.py sdist running sdist running egg_info writing foogis.egg-info/PKG-INFO writing top-level names to foogis.egg-info/top_level.txt writing dependency_links to foogis.egg-info/dependency_links.txt writing entry points to foogis.egg-info/entry_points.txt reading manifest file 'foogis.egg-info/SOURCES.txt' writing manifest file 'foogis.egg-info/SOURCES.txt' creating foogis-0.1dev creating foogis-0.1dev/foogis creating foogis-0.1dev/foogis.egg-info making hard links in foogis-0.1dev... hard linking setup.cfg -> foogis-0.1dev hard linking setup.py -> foogis-0.1dev hard linking foogis/__init__.py -> foogis-0.1dev/foogis hard linking foogis/tests.py -> foogis-0.1dev/foogis hard linking foogis.egg-info/PKG-INFO -> foogis-0.1dev/foogis.egg-info hard linking foogis.egg-info/SOURCES.txt -> foogis-0.1dev/foogis.egg-info hard linking foogis.egg-info/dependency_links.txt -> foogis-0.1dev/foogis.egg-info hard linking foogis.egg-info/entry_points.txt -> foogis-0.1dev/foogis.egg-info hard linking foogis.egg-info/not-zip-safe -> foogis-0.1dev/foogis.egg-info hard linking foogis.egg-info/top_level.txt -> foogis-0.1dev/foogis.egg-info copying setup.cfg -> foogis-0.1dev Writing foogis-0.1dev/setup.cfg creating dist tar -cf dist/foogis-0.1dev.tar foogis-0.1dev gzip -f9 dist/foogis-0.1dev.tar removing 'foogis-0.1dev' (and everything under it)
The file at dist/foogis-0.1dev.tar.gz is ready to be distributed to users of our package. Let's get them to install it using pip.
Do read the comment below about disabling (in setup.cfg) the "dev" tag in the distribution version string. Paste's "basic_package" template isn't the optimal template for every developer community. I'm familiar with the many additional features of the "ZopeSkel" template from Zope and Plone. I can imagine that commercial or semi-commercial efforts to grow Python developer communities (particularly thinking of ESRI here) might also be served well by specialized project templates.