Switching development contexts with virtualenvwrapper
Optimizing repetitive operations is one of my obsessions. I can’t stand following long sequences of steps to make the computer do what I want. The whole point of computers is to have them do more work for us, right?
As a developer with several ongoing projects, I frequently find myself switching contexts as one project becomes blocked (read: I lose interest) and I want to move on another. Typically that means unloading a bunch of files from my editor and loading others. Some IDEs, and even editors such as TextMate, use a “project” file to manage the files that belong to a project. Some go so far as to use the project file as a hint for how to build the library or application.
Using a project file typically involves pre-defining the members of a project, and then managing the project file as another artifact of the project itself. To switch contexts, you simply close one project file and all of its related code files, then open another. A good editor will even remember which files were open when the project was last in use, and restore your workspace to the same state. A great editor will let you automate the steps needed to switch contexts by loading different project files.
emacs desktop-mode
I’ve been an emacs user for something like 10 years now. I use vi frequently, and TextMate regularly as well, but most of the time I live in emacs for text editing and development tasks. The implementation of project files I use with emacs is desktop-mode. I like desktop-mode because instead of forcing me to pre-declare all of the files in my project, it assumes that all open buffers should be remembered. There are alternatives (there are always alternatives in emacs, right?).
Setup of desktop-mode is straightforward:
- Run
customize-group
on the “desktop” group. - Turn
desktop-save-mode
on to enable the minor mode. - Optionally change the base name for desktop files in
desktop-base-file-name
. I like using “emacs.desktop” so the file is not hidden and I know exactly what it contains. - Set a default search path for the desktop file in
desktop-path
. I set my path to include~/emacs
, but this is only the default. We will be saving a separate desktop in each virtualenv. - Set
desktop-save
to “Always save”. There are other values that work, but some require interaction with the editor during the context move to confirm file saves, and I want to avoid that.
There are a few other options that may be useful to tweak, depending on the other features of emacs you use. For example, I set desktop-clear-preserve-buffers so clearing the desktop does not delete the *Messages*
, *Org Agenda*
, or *scratch*
buffers since those are related to emacs operation and not limited to any one project.
virtualenvwrapper hooks
The next step is to set up the tool chain so emacs loads a new desktop when you change virtualenvs from the command line.
In addition to making it simpler to manage multiple virtual
environments, virtualenvwrapper
adds several
hook points to trigger changes outside of the virtual environment
when various events occur. For example, each time you run workon
to
change environments, the predeactivate
and postactivate
hooks are
run. The most interesting of these for our purposes is the
postactivate
hook, called right after the new environment is
activated.
I usually add a cd
command to the environment-specific postactivate
script (in $VIRTUAL_ENV/bin/postactivate
) to move my shell session to the working directory for that project and change the $PATH
, making it easier to run the tests for my project from the shell. For example:
pymotw_root=/Users/dhellmann/Documents/PyMOTW
cd $pymotw_root/src
PATH=$pymotw_root/src/bin:$VIRTUAL_ENV/gettext/bin:$PATH
Changing Desktops
In addition to the virtualenv-specific hook, there is also a global postactivate
script, and that’s the one I use to switch contexts in emacs. Edit $WORKON_HOME/postactivate
to add these lines:
# Change emacs' desktop directory
emacsclient -e "(desktop-change-dir "$VIRTUAL_ENV")" >/dev/null
The function desktop-change-dir
tells emacs where to save the desktop session file. By changing the directory each time we change environments, we can have a separate desktop file in each environment. Changing the directory automatically saves the old desktop first, of course, so the old project status is preserved until you come back to it.
Now that the hook is configured, the next step is to initialize the desktop for your projects. For each project, run workon
to activate it, then open several files from that project. As you workon
the next project, the session will be saved in $VIRTUAL_ENV/emacs.desktop
(assuming you set your desktop-base-file-name to emacs.desktop). Once you have initialized a few virtualenvs, switch back to the first. Emacs should load the desktop file for that environment and restore the buffers you were editing.
Other Editors
The example above works for emacs, but other editors may need to use the predeactivate
hook to save a project file before loading the new one from postactivate
. There are global and local versions of both hooks; refer to the virtualenvwrapper documentation for more details.