Making Virtualenv Play Nice with Git
I like to do most of my Python development inside virtualenvs. I also create a Git repository for any project that matters or that will have any kind of continued development. Constantly switching between the different virtualenvs to work on different projects used to be tedious, but this issue was largely solved by the fantastic virtualenvwrapper.
Virtualenvwrapper has certainly improved the situation, but even so, I can’t help but worry that the cd project-x
, workon project-x
, (do some work), cd ..
, deactivate
work-flow is going to lead me to an early grave caused by a severe case of RSI. So in order to retain my good health, I’ve hacked together a bash function that automatically activates a virtualenv when you cd
into a Git repository, and deactivates it when you leave the repository.
By default, it assumes that the virtualenv’s name will be the same as the repository’s name, but this can be overridden by creating a file called .venv
in the repository’s root directory with the name of another virtualenv in it.
# Automatically activate Git projects' virtual environments based on the
# directory name of the project. Virtual environment name can be overridden
# by placing a .venv file in the project root with a virtualenv name in it
function workon_cwd {
# Check that this is a Git repo
GIT_DIR=`git rev-parse --git-dir 2> /dev/null`
if [ $? == 0 ]; then
# Find the repo root and check for virtualenv name override
GIT_DIR=`\cd $GIT_DIR; pwd`
PROJECT_ROOT=`dirname "$GIT_DIR"`
ENV_NAME=`basename "$PROJECT_ROOT"`
if [ -f "$PROJECT_ROOT/.venv" ]; then
ENV_NAME=`cat "$PROJECT_ROOT/.venv"`
fi
# Activate the environment only if it is not already active
if [ "$VIRTUAL_ENV" != "$WORKON_HOME/$ENV_NAME" ]; then
if [ -e "$WORKON_HOME/$ENV_NAME/bin/activate" ]; then
workon "$ENV_NAME" && export CD_VIRTUAL_ENV="$ENV_NAME"
fi
fi
elif [ $CD_VIRTUAL_ENV ]; then
# We've just left the repo, deactivate the environment
# Note: this only happens if the virtualenv was activated automatically
deactivate && unset CD_VIRTUAL_ENV
fi
}
# New cd function that does the virtualenv magic
function venv_cd {
cd "$@" && workon_cwd
}
alias cd="venv_cd"
Note: for this to work you will need virtualenv and virtualenvwrapper installed. To use it, just stick it in your .bashrc
somewhere below where your $WORKON_HOME
is specified.
Update: I now use virtualenvwrapper’s postactive and postdeactivate hooks for this purpose. Here’s the postactivate:
#!/bin/zsh
# Global virtualenvwrapper postactivate, lives in $WORKON_HOME/postactivate
# Remove virtual env from start of PS1 as it's in RPROMPT instead
PS1="$_OLD_VIRTUAL_PS1"
PROJECT_DIR="$HOME/projects/$(basename $VIRTUAL_ENV)"
if [ -d $PROJECT_DIR ]; then
# If we aren't already within the project dir, cd into it
if [[ ! `pwd` == "$PROJECT_DIR*" ]]; then
export PRE_VENV_ACTIVATE_DIR=`pwd`
cd "$PROJECT_DIR"
fi
fi
unset PROJECT_DIR
And the postdeactivate:
#!/bin/zsh
# Global virtualenvwrapper postactivate, lives in $WORKON_HOME/postdeactivate
if [ $PRE_VENV_ACTIVATE_DIR ]; then
cd $PRE_VENV_ACTIVATE_DIR
unset PRE_VENV_ACTIVATE_DIR
fi
This moves me into the corresponding directory in my ~/projects
folder when I use workon <project-name>
.