Wrangling Your Python Virtual Environments
I recommend that Python developers at every level from casual learner on up learn to use virtualenv. I've gotten in the habit of just creating a new virtualenv every time I try out a package or module. I also depend on virtualenv to help with managing numerous client sites as well as my personal projects. Buildout is another perfectly acceptable tool for this but I see Buildout as more of a framework for managing builds and deployments whereas virtualenv does one thing and does it well: creates a new isolated Python runtime environment. virtualenv is easy to get started with for small projects and experimentation. There is also nothing that prevents one from utilizing virtualenv on larger projects. It is becoming increasingly common to see a combination of virtualenv and fabric (and often pip) for providing a very manageable and repeatable means of reducing the installation, updating, and deployment process to a few simple commands that you can run locally.
As easy as it is to create and activate a single virtualenv,
~/projects$ virtualenv myenv ~/projects$ cd myenv ~/projects/myenv$ source bin/activate (myenv) ~/projects/myenv$
virtualenv doesn't provide any facilities for organizing or managing multiple environments. This is why I also highly recommend using virtualenvwrapper. If it doesn't fit in well with how you like to organize things, take a peek at the source and then write your own - the code is short and easy to understand even if you're not a Basher.
Customizing Your Environments
I love the idea of having a single command to run that ultimately wraps up all steps for activating the virtualenv and updating the system environment with everything you'll need for your development session. With every project I'm working on I always want to reduce the repetitive commands that it takes to do all the things I usually do: start a Django development server, open a Python shell, open a DB console, run pylint, etc. In addition to the useful handful of commands provided by virtualenvwrapper for general management, it also provides a few hooks that make it simple to wire up your environments with all kinds of shortcuts and utilities.
The two hooks that I make the most use of are the global postmkvirtualenv and the environment postactivate. postmkvirtualenv is sourced after the environment is created and activated. I use this hook to automatically populate the new environment's site-packages with a few packages I tend to make extensive use of. Here is an example:
easy_install pip pip install ipython pip install pudb
Note that since this hook is sourced and the environment is already activated, pip will install the packages into the environment's site packages which is what we desire. Now I know that whenever I create a new virtualenv it will already have my favorite tools ready to go. A nice complement to this idea is to utilize the environment postactivate to add some environment-specific shortcuts. For a Django project I'll have something like the following:
cd /path/to/my/project #Django command shortcuts alias dj='python manage.py' alias djr='dj runserver' alias djrp='dj runserver_plus' alias djdb='dj dbshell' alias djs='dj shell' alias djsp='dj shell_plus' alias djt='dj test' alias djm='dj migrate' alias djsm='dj startmigration'
Now when this environment is activated my current working directory will be the project source directory and I'll have some easy shortcuts for running common Django operations during development. With this hook in place I can issue the following from anywhere in the filesystem and have a running Django dev server for my project:
~/some/path$ workon myproj ~/projects/myproj$ djr Validating models... 0 errors found Django version 1.1, using settings 'myproj.settings' Development server is running at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
When I'm ready to work on another project, I kill the dev server, rinse and repeat:
~/projects/myproj$ workon otherproj ~/projects/otherproj$ djr
I could also leave myproj running and open otherproj in another shell console:
~/projects/myproj$ workon otherproj ~/projects/otherproj$ djr 8001
Then myproj would still be available at localhost:8000 and otherproj would be available at localhost:8001
Extending the Environment Management Commands
Even though the majority of the things I work on are Django projects, I don't necessarily want the overhead of having Django installed automatically in every new virtualenv I create. If I'm just trying out a new package or doing some one-off data processing, I don't need Django around and would rather get started instantly. So I added my own command called mkdjangoenv:
function mkdjangoenv () { mkvirtualenv "$@" virtualenvwrapper_source_hook "$WORKON_HOME/install_django_libs" }
I'm just piggybacking on what's there and running an extra hook called install_django_libs which looks like:
pip install Django==1.1 pip install django-debug-toolbar pip install django-extensions pip install django-test-utils pip install south
After running mkdjangoenv I'll have a new Django-ready environment with a few handy development tools ready to go. You may also want to extend this to automatically call startproject or however you like to provision the scaffolding for new Django projects.
Hopefully you can see that with a small amount of effort and a desire to eliminate repetitive tasks you can easily take your environments by the horns. Every second not spent fretting over package management is time better spent on your ideas. pip combines beautifully with the ideas discussed here and I'll go over how to take advantage of pip to solidify the ideas discussed here.

