Managing virtualenvs with a little bash

When you need to install something directly from PyPI, Python virtualenvs have been my go-to for over a decade.

A quick virtualenv intro

Most of my readers are probably already familiar with virtualenvs, but for completeness, I'll give you a brief introduction. A virtualenv (short for "virtual environment") is an isolated distribution of Python packages, where you can independently install packages without disturbing your system packages or other virtualenvs.

You can set one up like this, assuming you are using Python 3.3 or higher:

python3 -m venv ~/.venv/my-virtualenv

The directory specified here is just a convention. I keep all my virtualenvs in the .venv folder in my home directory, but you can pick whatever location you like.

To use the virtualenv, you must activate it:

source ~/.venv/my-virtualenv/bin/activate

This activation script is a special shell script that configures your current shell, pointing at all the right paths in order to use the virtual environment. source runs this script in your current shell session to set it up. You will notice that this adds (my-virtualenv) to the beginning of your shell prompt, reminding you that the "my-virtualenv" virtualenv is active. Now when you pip install amazing-package, the software will only be available in this virtual environment.

When you're done, you can deactivate it like so:

deactivate

Wonderful!

Managing many virtualenvs gets annoying

Over time, I end up accumulating many virtualenvs, which can become harder to manage. Maybe something like this:

$ ls ~/.venv/
my-virtualenv cool-project snakes-ahoy

I also don't want to type source ~/.venv/my-virtualenv/bin/activate every time I use the virtualenv, because it gets very repetitive—only the name of the venv is really needed.

But luckily, we can write a little bit of bash to make managing this less annoying. (Or you can use one of many Python developer tools that are designed to manage this, like pipx, but when I merely want to consume Python software, I might not have a development environment set up. So that's beyond the scope of this post!)

If you add the following shell function to your ~/.bashrc or ~/.bash_aliases file, it will nicely wrap our activation command:

setup-venv() {
        source "$HOME/.venv/$1/bin/activate"
}

Now all we need to run is

setup-venv my-virtualenv

So much quicker!

Spicing it up with tab completion

The first thing I noticed after writing this wrapper was that I started hitting tab on the virtual environment name, but... nothing happened. Wouldn't it be nice to know what virtualenvs I had available, and to not have to type out the whole long thing?

Well, we can write it ourselves 😄

If for some reason you don't already have bash completion installed, on a Debian-based system, you will need to install it with

apt install bash-completion

In order to configure our bash completion, we will create a new file, /etc/bash_completion.d/venv, with the following contents:

_list_venvs()
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts=$(find $HOME/.venv/ -mindepth 1 -maxdepth 1 -type d -printf "%f ")

    COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
    return 0
}
complete -F _list_venvs setup-venv

This file defines another shell function order to determine how to autocomplete the options for our setup-venv function.

$opts is where we define the options for our function. We generate it with a find command—looking at the .venv folder in the current user's home directory, then only including child folders (excluding the current directory itself, .venv, in our results) by using the min/max depth and type arguments, and printing just the individual directory names, deliminated by spaces using our print formatter.

Everything else is the standard scaffolding required to use bash completions.

Once you save this file and reload your shell, you'll see that you are able to use completions as expected!

setup-venv <tab>
my-virtualenv cool-project snakes-ahoy

setup-venv s<tab>
setup-venv snakes-ahoy

Complaints, comments, questions?

Hope this was helpful! If it wasn't, that's too bad. But don't worry—you can safely ignore this post.