Here’s a non-exhaustive list of programs that are all meant to help create or manage virtual environments in some way:
Hatch, VirtualEnvManager, autoenv, fades, inve, pew, pipenv, pyenv-virtualenv, pyenv-virtualenvwrapper, pyenv, pyvenv, rvirtualenv, tox, v, venv, vex, virtual-python, virtualenv-burrito, virtualenv-mv, virtualenv, virtualenvwrapper-win, virtualenvwrapper, workingenv
Clearly, this stuff must be really hard to get right. I also must be a moron, since, after having written some thousand lines of Python, I don’t even know what problem we are trying to solve here, and the abundance of relevant programs with subtly different names has deterred me from reading up on it so far.
So what is a virtual environment?
The official docs’ tutorial describes a virtual environment as
a self-contained directory tree that contains a Python installation for a particular version of Python, plus a number of additional packages.
So it’s a directory with a Python interpreter? Easy enough.
$ mkdir virtual_env $ cp /bin/python3 virtual_env/
Let’s see. Directory? Check. Contains a Python installation? Check. Contains a number of additional packages? Zero is a number! (Check.) Particular version? Um…
$ cd virtual_env/ $ ./python3 --version Python 3.6.3
I think that will do. Is it self-contained, though? It doesn’t contain itself…
Jokes aside, there are only two things missing to actually make our directory a virtual environment as specified by PEP 405, the proposal that integrated a standard mechanism for virtual environments with Python.1
- A file named
pyvenv.cfgcontaining the line
home = /usr/bin
(Both paths are subject to the OS and the second one also to the Python version used.)
$ echo 'home = /usr/bin' > pyvenv.cfg $ mkdir -p lib/python3.6/site-packages
I will also move the Python binary into a
$ mkdir bin && mv python3 bin/
Fair. We have a directory that formally qualifies as a virtual environment:
$ tree --noreport . ├── bin │ └── python3 ├── lib │ └── python3.6 │ └── site-packages └── pyvenv.cfg
This leads us to the next question.
What’s the point?
When we run our copy of the Python binary, the
pyvenv.cfg file changes what happens
during startup: the presence of the
home key tells Python the binary belongs to a
virtual environment, the key’s value (
/usr/bin) tells it where to find a complete Python
installation that includes the standard library.
The bottom line is that
./lib/python3.6/site-packages becomes part of the module search
path. The point is that we can now install packages to that location, in particular,
specific versions that may conflict with the dependencies of another Python program on the
For example, if your project needs exactly version 0.0.3 of left-pad:
$ pip3 install -t lib/python3.6/site-packages/ left-pad==0.0.3
Now this will work:
$ ./bin/python3 -c 'import left_pad'
While this should raise
ModuleNotFoundError, as desired:
$ python3 -c 'import left_pad'
Another project on the same system could have a different version of left-pad in its own virtual environment, without interfering with this one.
The standard tool for creating virtual environments
In practice, one does not simply create virtual environments by hand, which brings us back to the dauntingly long list of tools above. Fortunately, one of them is not like the others. While it’s predated by most of them, this one ships with Python as part of the standard library: venv.4
In its simplest form, venv is used to create a virtual environment like so:
$ python3 -m venv virtual_env
This creates the
virtual_env directory and also copies or symlinks the Python
$ cd virtual_env $ find -name python3 ./bin/python3
It also copies a bunch of other stuff: I get 650 files in 89 subdirectories amounting to
about 10 MiB in total. One of those files is the
pip binary, and we can use it to
install packages into the virtual environment without passing extra command-line
$ ./bin/pip install left-pad
You can read more about using venv and optional magic like “activate” scripts in the Python tutorial or venv’s documentation—this post is only meant to boil down what a virtual environment actually is.
A virtual environment is a directory containing a Python interpreter, a special
pyvenv.cfg file that affects startup of the interpreter, and some third-party Python
Python packages installed into a virtual environment will not interfere with other Python applications on the same system.
The “standard tool for creating virtual environments” is venv.
I think Ian Bicking’s
non_root_python.py qualifies as the first tool for creating
virtual environments. Based on that,
added to EasyInstall in version
0.6a6 in October 2005. Here’s a timeline summarizing some
virtual-python.pyis added to EasyInstall.
- Ian Bicking, the author of
virtual-python.pyis based—publishes a blog post about improving
virtual-python.pytitled “Working Environment Brainstorm”.
- Ian Bicking announces
- Ian Bicking announces an improved version of
- virtualenv’s first commit
- Ian Bicking announces virtualenv: “Workingenv is dead, long live Virtualenv!”
virtual-python.pyis removed from EasyInstall.
- PEP 405 is created.
- PEP 405 is accepted for inclusion in Python 3.3.
- Python 3.3 is released; venv and pyvenv become part of the standard library.
- Python 3.4 is released; “[venv] defaults to installing pip into all created virtual environments.”
- Python 3.5 is released. “The use of venv is now recommended for creating virtual environments.”
- Python 3.6 is released; “pyvenv was the recommended tool for creating virtual environments for Python 3.3 and 3.4, and is deprecated in Python 3.6.”
Before PEP 405 was accepted, virtual environments were purely the domain of third-party tools with no direct support from the language itself. ↩
I think this should not be necessary. But, because of what I assume to be a bug in CPython, it is. A
bin/subdirectory certainly is the conventional location for the binary, though. ↩
Actually, pyvenv also ships with Python, but was deprecated in version 3.6 (only 3 minor versions after its introduction). Both venv and pyvenv were added to Python in version 3.3. ↩