DEV Community

Michael
Michael

Posted on

Easy workflow for switching Python virtual environments with ZSH!

The Problem

The problem I set out to solve was simply to avoid manually typing pyenv/venv commands
when moving into directories containing different projects, and around my directory tree.

theproblem

If you are like me, you would be working on different things at different times, and
switching projects around seamlessly becomes a requirement to avoid waste of time
and loss of attention and context.

My environment

For the sake of clarity, I am using a Macbook Pro with macOS Mojave, and I have iTerm2
set up with ZSH and oh-my-zsh as my shell.

Virtualenvwrapper

Essentially I opted to use virtualenvwrapper instead of pyenv, which I had been using for a few months before.
This nifty library contains an feature called workon, that allows one, among other things, to activate separate Python environments.

From the virtualenvwrapper's docs:

Install with Pip

virtualenvwrapper should be installed into the same global site-packages area where virtualenv is installed. You may need administrative privileges to do that. The easiest way to install it is using pip:

$ pip install virtualenvwrapper

Shell Startup File

Add three lines to your shell startup file (.zshrc, .bashrc, .profile, etc.) to set the location where the virtual environments should live, the location of your development project directories, and the location of the script installed with this package:

export WORKON_HOME=$HOME/.virtualenvs
export PROJECT_HOME=$HOME/Devel
source /usr/local/bin/virtualenvwrapper.sh

Manage your virtualenvs

Also from the docs, here is a quick list of commands to get started with workon:

workon

A list of environments, empty, is printed.

mkvirtualenv temp

A new environment, temp is created and activated.
It copies the current version of python to make it.

workon

This time, the temp environment is included.

workon temp

Activates that environment

deactivate

Deactivates that same environment

Now that we have an easy way to separate and use our virtual environments, let's talk about Jump!

Jump

Jump is a shell integrations which uses fuzzy search to helps you move around your directory structure more easily.
It is a Go application that essentially records your keystrokes and your most used directories
into its own database.
Jump allows you to do stuff like this:

gyazothing

Installing Jump:

On Mac:

brew install jump

After which, you need to add the following line in your zshrc

eval "$(jump shell)"

When you first use Jump it won't do much, as its database is empty; it will learn as you use it.

An alternative to jump, in case you don't want to install Go stuff on your machine (why?), is z,
which is entirely written in shell script.

Moving around seemlessly between projects

Now you can jump anywhere in your directory tree, and switch virtual environments as your heart desires.
But how do we make the experience more seamless?

We can hack our bashrc / zshrc file by adding a snippet of bash, which will run every time we cd into a new directory, checking the presence of a .venv file.

Said .venv file will simply contain the name of the virtual environment we wish to activate.

echo "fooenv" >> .venv

We then need to modify our zshrc and add this (admittedly hacky) snippet:

# Workon
# Support for bash
PROMPT_COMMAND='prompt'

# Mirrored support for zsh.
precmd() { eval "$PROMPT_COMMAND" }

function prompt()
{
    # Check for directory change
    if [ "$PWD" != "$MYOLDPWD" ]; then
        MYOLDPWD="$PWD"
        # Run workon if .venv exists
        if [[ -e .venv ]]; then
            workon `cat .venv`
        # Run deactivate and redirect any errors to null
        elif [[ ! -e .venv ]]; then
                deactivate 2>/dev/null
        fi
    fi
}

Alternatively to the above ugly bash hack, we can simply use the ZSH Plugin for virtualenvwrapper, which does pretty much the same thing, but better. :)

Also with the plugin, Python environments with the same name as the repository will be automatically activated on cd, unless we use the .venv method as described.

After we source our rc file (or close and reopen our terminal),
We can jump around like this:

gyazo thing jumping around

Original post on my blog, sk1u.com

Top comments (1)

Collapse
 
moopet profile image
Ben Sinclair

What you've done works in bash and zsh, so why does the post title single out zsh?

I'd do this as a function that shadows cd instead, so it only runs when you change directory, not on every prompt.

I'd use echo "fooenv" > .venv instead of >> so you don't accidentally append to an existing file.

You could also make it so it searches back up the directory tree for any .venv file further up (like git does) so you could cd into any child directory and it'd still work.