Hi, I'm ThadeusB.

I code stuff. I raise bees. I game.

Seemless Python VirtualEnv Integration

At home and at work I end up needing to manage lots of virtual environments for my python projects. The problem with virtual environments has two elements to it. How do you know which environment you are in?How do you remember which virtual environment should go to which project.

These two problems could easily be solved by following a standard nomenclature, a projects virtual environment should be named after the project. Virtualenv wrapper does a good job of solving the problem of multiple virtual environments spread across multiple projects. Just type in virtualenv-wrapper <name of virtualenv> and the environment is good to go. These two are a gimme.

Usually I find that remembering the names of the virtual environments or where I put them is still a pain in the behind. Sometimes I will take a branch of my source code, and throw off another virtualenv just for it. What if I happen to be changing directories between the two projects? The virtualenv would just stay with the last one I activated. I also have lately been frustrated that every time I open a new tab in the terminal editor I lose the virtualenv my old terminal was in. More often than not I need to launch command A in one window, and command B in another window, both within the same virtualenv.

So I hacked up a little bash function that solves the problem for me. I don't need to remember where my virtualenvs are located, and I don't even need to know their names. The basic concept is that I override the cd command with a custom command that will loop through all directories specified to the cd and find a special named file, if this file is located it will source it, activating the virtualenv for me as I cd into the directory!

I create a special file named “activate.sh” inside that projects root directory. This script contains a simple one line bash command source <path/to/virtualenv/bin/activate>. This way if running source activate.sh it will place that virtualenv as my active environment.

The first way to search for this file is by scanning every directory from the pwd command. This way no matter where I am at, I might find an activate.sh and activate the environment. This is very useful whenever a new terminal instance is spawned (like opening a new tab in gnome Terminal). It assures I am in the same environment as the tab it just spawned from.

The second way is to just search all directories that were passed as an argument to the cd command. This way it would only end up activating only when it cd into a directory containing the activate.sh file.

Low and behold, the code to make my development life so much easier!

Sample activate.sh file

source ~/.workspaces/venvs/FlaskEnv/bin/activate

Code that lives in ~/.bash_aliases

activator="activate.sh"

# Searches entire pwd for activate.sh
function activate_global_cd {
    # Change IFS temporarily for iterating through directories
    OLD_IFS="$IFS"
    IFS="/"

    ROOT=""

    # Actually change directory
    cd "$1"

    _dfiles="`pwd`"

    for token in $_dfiles
    do
        ROOT="$ROOT$token/"
        activator="${ROOT}activate.sh"

        if [ -f "$activator" ]
        then
            echo "sourcing $activator"
            source "$activator"
        fi
    done

    # Restore IFS to its original state
    IFS="$OLD_IFS"
}

# Searches only the arguments directories for activate.sh
function activate_cd {
    # Change IFS temporarily for iterating through directories
    OLD_IFS="$IFS"
    IFS="/"

    ROOT="`pwd`/"

    # Actually change directory
    cd "$1"

    _dfiles="$1"

    for token in $_dfiles
    do
        ROOT="$ROOT$token/"
        activator="${ROOT}activate.sh"

        if [ -f "$activator" ]
        then
            echo "sourcing $activator"
            source "$activator"
        fi
    done

    # Restore IFS to its original state
    IFS="$OLD_IFS"
}

alias cd='activate_cd'

# Always do a global search when starting a new terminal instance.
activate_global_cd