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:

cd $pymotw_root/src

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.