A virtual environment in Python: isolating a project's dependencies

A virtual environment gives each Python project its own set of libraries, so that upgrading one project never breaks the one next to it.
7 min read
Believemy logo

Definition

Two projects on the same computer, one that needs version 2.20 of a library, the other 2.31: installed in the same place, those two versions cannot coexist, and the last one to arrive breaks the project that worked the day before. Virtual environments exist for exactly that problem.

A virtual environment is a folder holding its own copy of the Python interpreter and its own package directory. While it is active, pip install writes into it, and import looks for its modules there before looking anywhere else. The rest of the machine knows nothing about it, and two neighbouring projects each keep their own version.

Three commands are enough to create one and step inside it.

BASH
# Create the .venv folder at the root of the project
python -m venv .venv

# Switch this terminal into the environment
source .venv/bin/activate

# Install requests in there, and nowhere else
pip install requests

Activating does nothing magical: the folder moves to the front of the search path, and for that terminal only.

On Windows, activation is written .venv\Scripts\activate. The folder name is free, but .venv has become the convention: editors spot it on their own and Git ignore files already know it.


The problem it solves

Without it, everything installs in the same place: the package directory of the system Python installation, shared by every project on the machine. Only one version of each library fits in there at a time.

And that version goes to whoever installs last. Upgrading requests for today's project upgrades it for all of them, so the project written six months earlier inherits a library it has never seen. Nothing gives any warning, and the failure surfaces too far from its cause for anyone to connect the two.

Two projects on one machine, with and without a virtual environmentWithout a virtual environment, both projects point at the same installation: the version the second one needs overwrites the first one's, which stops working. With a virtual environment, each project keeps its own version inside its folder, and upgrading one no longer reaches the other.No virtual environmentone project breaksProject Aneeds requests 2.20stops workingProject Bneeds requests 2.31keeps workingOne installation for the whole machinerequests 2.31: B overwrites A's versionWith a virtual environmentboth keep runningProject A.venvrequests 2.20Project B.venvrequests 2.31upgrading one no longer reaches the other

The two situations differ on four points, and the most important one is not the first.

Without a virtual environmentWith one
One version of each library, machine-wideOne version per project
The real dependencies stay unclearThe folder lists them exactly
An install can break a neighbouring projectThe blast radius stops at the folder
A colleague's machine reproduces something elseA requirements.txt is enough to rebuild it

The second point weighs as much as the first: a project whose dependencies live in the general installation sooner or later borrows a package installed for something entirely different, without anyone noticing. It runs where it was written, and goes missing on deployment day. So a virtual environment does not only isolate, it reveals.

Good to know

On many recent systems pip even refuses to install outside a virtual environment and answers externally-managed-environment. That is not a failure: it is the system installation protecting itself.


What it does not isolate

This is where most of the disappointment comes from: the word "environment" promises more than it delivers. A virtual environment isolates Python packages, and nothing else.

It does not pin the version of the language: that one is inherited from the interpreter used to create it, which the folder points at without containing it. A system upgrade can therefore make it unusable overnight, and the only way out is to recreate it.

Nor does it carry the system libraries that some compiled packages rely on, or the environment variables, or the database. Should you move to Docker, then? No: a virtual environment is not a container, it answers the narrower question of the dependencies declared on PyPI, at a cost close to zero.


The classic mistake of newcomers

The symptom is always the same: a ModuleNotFoundError on a library installed five minutes earlier. It really is somewhere on the disk, just not where the program is looking for it.

Two gestures produce that result. Installing without having activated, and the package lands in the general installation. Activating in one terminal then running the program from another, and the second window knows nothing about the environment.

Two commands settle the question without any guessing.

BASH
# Which interpreter is this terminal using?
which python

# And which Python is this pip attached to?
pip -V

If the paths shown do not run through the project folder, the environment is not active. One case stays confusing longer: editors have their own interpreter setting, and a script launched from the editor can ignore the environment activated in the terminal next to it.


What is shared, what is thrown away

A virtual environment does not travel, it gets rebuilt: the paths written inside it are absolute and point at one precise machine.

Warning

Renaming or moving the project folder is enough to break the environment it contains: the activation scripts still point at the old path, and the error message says nothing about it. Delete the folder and recreate it.

For the same reason, putting a virtual environment into Git is a false good idea: it is heavy and will run on nobody else's machine. What travels is the list of installed packages.

BASH
# On your machine: write down the installed packages
pip freeze > requirements.txt

# On another machine: rebuild an identical set
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

pip freeze records everything present, the dependencies of your dependencies included, with their exact versions. Two machines starting from the same requirements.txt end up with the same set of libraries, and a damaged environment stops being an incident: it is deleted and recreated without losing anything.


venv, virtualenv, Poetry, conda

Several tools answer the same need. The right question is not which one is best, but which one brings something the project actually demands.

ToolWhat sets it apart
venvShips with Python, described by PEP 405, enough in the vast majority of cases
virtualenvThe external ancestor, faster and compatible with older versions of the language
poetryHandles the environment and version locking in a single tool
condaAlso installs non-Python dependencies, common in scientific computing

Starting with venv remains the sound reflex: it is already there, and switching tools later costs little, since the dependency list itself travels.


Frequently asked questions

Question

Is a virtual environment needed for a ten-line script?

If the script only uses the standard library, there is nothing to isolate and the answer is no. It turns into yes with the first pip install command: that package installs somewhere, and that somewhere will serve every project that follows too.

Question

What should be done when the environment looks broken?

Delete it, then recreate it from the requirements.txt file. It is a disposable folder, and a clean reinstall settles half of the dependency problems faster than any diagnosis would.

Question

One environment per project, or a single one for the machine?

One per project, otherwise the original problem comes back untouched. The one reasonable exception: command-line tools used everywhere, such as ruff or a code formatter, which belong to no project and are better installed separately, with pipx.

Related terms

Discover our python glossary

Browse the terms and definitions most commonly used in development with Python.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.