In response to @ericholscher and his recent blog post "Virtualenv Tips", I've decided to describe the way I use virtualenv in the hopes that someone might find it useful. Before you even bother reading this, though, you should install virtualenvwrapper, as most of my customizations are built into the various extra hooks it provides.
The first tip more a matter of philosophy. While virtualenv is designed to keep your python libraries separated, I tend to look at virtualenv as a way of separating entire workflows. When I
deactivate, a mental deactivation accompanies it. Let me explain a few of the customizations I make before getting back to why that happens.
With that established, the first thing I do is change the virtuanelvwrapper-wide
postdeactivate to change into my home directory. When making a new virtualenv, I change the
postactivate script to change into the project directory.
I also keep a set of mnemonic bash aliases that I use from project to project, but behave differently based on settings in my
postactivate files. For django projects, this usually implies:
cd ~/devel/project_name export PYTHONPATH=`pwd` export DJANGO_SETTINGS_MODULE='settings' alias rs="python manage.py runserver 0.0.0.0:8000" alias ds="python manage.py runserver_plus 0.0.0.0:8000" alias dsh="python manage.py shell" alias dbsh="python manage.py dbshell" alias dt="python manage.py test'
These aliases normally live in my bash_aliases, but sometimes different projects have different layouts, and I customize them here. Regardless,
source ~/.bash_aliases goes into my
postdeactivate to restore my "normal" environment.
I also configure some of my other programs to obey customizations provided by virtualenvs. In my .vimrc, I use the env variable supplied by virtualenv to change the indentation styles for different filetypes depending on the projects I'm working on:
" when hacking on (project), save html w/ tabs, not spaces if $VIRTUAL_ENV ~= "(project_name)" autocmd BufNewFile,BufRead *.jinja setlocal noexpandtab autocmd BufNewFile,BufRead *.jinja setlocal ts=2 autocmd BufNewFile,BufRead *.jinja setlocal shiftwidth=2 autocmd BufNewFile,BufRead *.jinja setlocal wm=2 " ... endif
I also have altered my base
~/.ipython/ipy_user_conf.py to include this code to import per-virtualenv customizations:
import IPython.ipapi, os ip = IPython.ipapi.get() def virtualenv(): return os.environ.get('VIRTUAL_ENV', None) def main(): execf('~/.ipython/virtualenv.py') ve = virtualenv() if ve: path = os.path.join(ve, 'ipy_user_conf.py') if os.path.isfile(path): execf(path) def execf(path): ip.ex('execfile("%s")' % os.path.realpath(path))
This will execute
$VIRTUAL_ENV/ipy_user_conf.py within the context of the new ipython interpreter, so I can go in there and add a bunch of import statements or shortcut functions. I also use (via the
execf function above) a virtualenv.py function which sets up my system-wide ipython with the virtualenv site settings.
Finally, in my
postactivate, I sometimes set which version control system I'm using on that project (since I swap between svn, hg, and git), but use aliases defined elsewhere to do some generic things quickly, like
alias vd="$VCS diff |gvim -".
A lot of these things could be streamlined or further automated. When I allow my different environments to be tailored to the projects at hand, I've found over time that
deactivate leave me in different mental states. After I've deactivated, I'm left with an environment that is almost unsuitable for development, and when I've activated a virtualenv, I'm instantly in the same environment I was when I was an hour and a half into a tough problem.