Managing Virtual Environments on Python

March 22, 2018 5 minutes

With the exception of small scripts and programs that only use built-in libraries, pretty much the first step before starting to work in a new Python project is the setup of a virtualenv. As a reminder, virtual environments provide you:

  • Dependency isolation: A requires libX v2 and B requires v3? If you get into that situation without venvs, you already lost. See you in DLL hell ;)
  • Stable development environment: With requirements.txt (or Pipfile.lock for Pipenv) libraries stay put until you update them explicitly. Useful to avoid finding surprises when you or a colleague download a project you began 1 year ago and where many dependencies got a major version update.
  • Non-root package installation: All the Linux distros I’ve used require root privileges to install packages globally. You can do pip install --user, but you still have the two previous problems.

There are already enough tutorials about using the virtualenv command, and I personally no longer use it directly. We’ll cover 3 4 common use cases, in a kind of continuation of the previous post.

  1. Global Python install & virtualenvwrapper
  2. Pyenv & pyenv-virtualenvwrapper
  3. Pyenv & Pipenv (highly recommended)
  4. Global Python install & Pipenv (a good compromise)

Global Python install and virtualenvwrapper

You can install virtualenvwrapper before or after installing your desired Python version somewhere in your system. This assumes Ubuntu 16.04LTS, like the previous post.

The installation consists of 5 steps. You do this only once:

  1. pip3 install virtualenvwrapper
  2. echo 'export WORKON_HOME=~/.virtualenvs' >> ~/.bashrc
  3. echo 'source /usr/local/bin/virtualenvwrapper.sh' >> ~/.bashrc
  4. sed -i 's/h python/h python3/1' /usr/local/bin/virtualenvwrapper.sh
  5. source ~/.bashrc

By default virtualenvwrapper.sh tries to execute python to load its module, but as we used pip3 to install it, it will not find it. We change it using sed so it uses python3 instead.

Using virtualenvwrapper is simple, and you can find documentation at: virtualenvwrapper quickstart and in many other tutorials.

Creating a new virtual environment using mkvirtualenv will use Python 3.5 by default (that’s to what python3 is linked to). If we want to use Python 3.6, we can pass it as an argument to mkvirtualenv:

$ mkvirtualenv -p python3.6 myEnv
mkvirtualenv -p python3.6 myEnv
... other output ...
(myEnv) $ python --version
Python 3.6.4

If you try to use a non-installed version, it will fail:

$ mkvirtualenv -p python3.7 myOtherEnv
The path python3.7 (from --python=python3.7) does not exist

Pyenv and pyenv-virtualenvwrapper

For installing pyenv check the previous post.

For ease of use, pyenv provides a plugin called [pyenv-virtualwrapper][]. It requires at least one version of Python installed and set.

$ pyenv install 3.6.4
$ pyenv global 3.6.4

You could use pyenv shell or pyenv local too in the previous example.

Installing the plugin it’s just a matter of cloning the Github repository:

$ git clone https://github.com/pyenv/pyenv-virtualenvwrapper.git $(pyenv root)/plugins/pyenv-virtualenvwrapper

Running pyenv virtualenvwrapper will setup virtualenvwrapper in your current shell. If virtualenvwrapper is not installed, then it will install it in the currently set Python; if it is already installed, then it will just make the virtualenvwrapper commands available, which are the same as usual.

Any new virtual environment created will use the currently set Python by default. You can use a different version with the -p flag, but it requires you to set multiple versions at once, as it’s described here .

$ pyenv shell 3.6.4 2.7.6
$ pyenv virtualenvwrapper
$ mkvirtualenv -p python2.7 myOldie
...output...
(myOldie) $ mkvirtualenv myNewie
...output...

In the creation of myNewie we have myOldie active, but it makes no difference. It’s just that the previous command activates that virtual environment after creating it.

Pyenv and Pipenv

Pipenv is a Pyenv aware tool to manage both the creation of virtualenvs and the installation of packages using pip at the same time.

Just by doing pipenv install, it automatically creates a new virtualenv if one doesn’t exist, generates requirements.txt-like files and installs your desired Python version in the virtualenv for you (even if you don’t have it installed), if you use Pyenv.

  • To install: just run pip install pipenv with a Python set on the current shell.
  • To install a package: pipenv install thepkg. As said before, this will create a new virtualenv for the “project” if one doesn’t exist. It will use the currently set Python.
  • To install a package, initializing the virtualenv with a specific version of python: pipenv --python=2.7.14 thepkg

Then you can activate the virtual environment by running pipenv shell.

Additionally, you can set the PIPENV_VENV_IN_PROJECT environment variable to 1 to install the virtual environments together with the projects (same directory as Pipfile and Pipfile.lock). If you come from .NET like I do, surely you’ll like it too as then it becomes like NuGet.

More documentation can be found at the Pipenv page.

Global Python install and Pipenv

Finally, if you like Pipenv, and you already have a Python installation that you want to use, note that’s possible to do so by running: pipenv --python=python3.6 thepkg.

Closing notes

I hope this post gives you an idea of what are some of the options at your disposal when it comes to creating virtual environments. Missing however is using virtualenv directly, and it may be a good option to do so if you want to reduce your dependencies as much as reasonably possible.

Personally, I prefer using Pipenv together with Pyenv, as they complement each other very nicely, and if you want to test your code in several Python versions, you are bound to run into Pyenv anyways.

In a future post, we’ll cover how to deploy a Flask webapp into an Ubuntu VPS, following what we have done until now.