Preface

I had worked with both Ruby’s RVM and rbenv a little, and setting up something similar for Python version and package management seemed a little undocumented, ambiguous, and generally quirky to me. Maybe doing this should not require a blog post, though the following was the simplest way for me to accomplish this.

Also, this post on the same topic was very helpful.

Python

I choose to leave OS X’s stock Python install alone, which was a slightly out-of-date version 2.7.2 for the system executable at /usr/bin/python. Also, the Python package manager easy_install comes with 10.8, but it’s deprecated.

So, I wanted to start with a base Python version that was more recent than the OS X stock Python. I did this using Homebrew:

brew install python

This installed version 2.7.5. Note that the Homebrew install of Python also includes Pip (the preferred Python package manager these days) as well as Distribute (a lower-level package management piece that Pip builds upon).

Global Packages

At this point, if I use Pip to install a package, it will be installed globally. In general, I want to only install packages into specific environments and leave the global package store alone. However, I still need a global package for virtualenv itself:

pip install virtualenv

I may have to install other global packages in the future, but the only package I’ve installed explicitly for now is virtualenv.

Setting Up Virtualenv

I created a ~/.virtualenvs folder where I will create all or most of my environments:

mkdir ~/.virtualenvs

For example, I can create a global environment for Python 2.7 packages, and a global environment for Python 3 packages. Note that I can also create project-specific environments if I want.

Then, I added the following default settings to my .bashrc file:

# virtualenv settings
export PIP_REQUIRE_VIRTUALENV=true # pip should only run if there is a virtualenv currently activated
export PIP_RESPECT_VIRTUALENV=true # tell pip to install packages into the active virtualenv environment
export VIRTUALENV_DISTRIBUTE=true  # tell virtualenv to use Distribute instead of legacy setuptools

The first setting is the most important. It tells pip to only run if inside a virtual environment. This keeps global packages untouched and stable, and it prevents a package from accidentally being installed globally when you mean to install it in a particular virtual environment. However, if global packages need to be installed/uninstalled/upgraded later, I’ll need to temporarily comment out and disable this setting.

Also note the VIRTUALENV_DISTRIBUTE option. This tells virtualenv to use the newer Distribute supporting library. From the official Python package page here: Distribute is a fork of the Setuptools project. Distribute is intended to replace Setuptools as the standard method for working with Python module distributions.

Now, we can create an environment and activate it:

cd ~/.virtualenvs
virtualenv default
. ~/.virtualenvs/default/bin/activate

With this environment active, any calls to the Python, Pip, etc. executables will pass through it, and packages will be installed to this environment.

Listing and Uninstalling Environments

Since I plan to create most environments in ~/.virtualenvs, I can just check that directory to see all installed environments. Likewise, to remove an environment, I can just remove the corresponding directory there.

Creating a Default Environment

To activate a default environment on login, I also added this to my .bashrc file:

# set a default virtualenv
. ~/.virtualenvs/default/bin/activate

Final Thoughts

Virtualenvwrapper

Virtualenvwrapper claims to ease several of the pain points and manual steps I described above, but I wanted to see if I could use virtualenv directly without adding another layer of tooling to learn.

Modifying the Shell

When you activate an environment, virtualenv modifies your prompt to indicate which environment you’re using. If this is undesirable, you can add the following line to your .bashrc file:

export VIRTUAL_ENV_DISABLE_PROMPT=true # tell virtualenv not to modify my prompt

If you disable this indicator in your prompt, you can check which environment you’re currently using by echoing the $VIRTUAL_ENV variable.

An Environment’s Bindings to a Python Executable

When a new environment is created, it is bound to the current system Python executable by default (whatever which python points to). In my case, this will be the Python 2 executable installed by Homebrew. You can, however, bind a new virtual environment to a particular Python executable by using the -p flag. For example, you could do virtualenv -p python3 foo-py3 to create a Python 3 environment or even virtualenv -p /usr/bin/python2.6 foo-py2.6 to call out a specific path to a Python executable.