Python Virtual Environments
The Iron Law of Python Management says we should always create a new virtual environment for each python project.
At the time of writing, PyPI (Python Package Index) has over 480,000 packages listed. PyPI does not guarantee that all the packages listed are compatible and can be installed together.
Virtual environments provide us a way to isolate different python packages from one another. Some reasons why you may want virtual environments:
- Install packages with conflicting dependencies
- Test how the code behaves with a different set of package versions
- Share your coding environment with someone else
- Different package requirements for different python projects
1 Built-in Python venv
module
Python has shipped the venv
module since Python 3.3. As long as you are using a supported version of Python, then you do not need to install any more tools to create a virtual environment.
In general, you do not want to install any packages in your base python environment. This can lead to confusion to where packages are installed as you switch around different virtual environments.
2 Create a venv
Virtual Environment
Because of the Iron Law of Python Management, each project (i.e., folder) will have their own python venv
.
2.1 Python Project Folder
First we need to make sure we are in the folder our python project exists in. You can confirm your location with:
pwd
The result should give you the path to your current Python project.
2.2 Python Version
pyenv
allows us to install and use different versions of python. You can see all the versions you have installed with:
pyenv versions
You can also see what python you are using with:
pyenv which python
This will list the full path to the python binary that is currently being used. The currently activated python version will be used to create your venv
. This is why you want to make sure the environment is clean (i.e., no extraneous python packages installed).
In this example, we will use Python 3.12.0
.
pyenv shell 3.12.0
2.3 Create venv
The general command for creating a venv
is:
python -m venv <PATH TO NEW VENV>
We will typically use .venv
or venv
for the venv name. So, the above command will look like this if we were to use venv
as our venv
name.
python -m venv venv
This will create a venv
folder in your current directory that will store all the packages and dependencies we will install.
There are pros and cons with how you name your venv
.
- If you use the
.venv
name with the.
in the beginning, thevenv
folder that will be created will be hidden because of the leading.
in the.venv
name. Because it’s hidden, it may not always be readily apparent that the current project has avenv
virtual environment. - If you use the
venv
name so the folder is not hidden, you will always see thevenv
folder in the project.
Whether or not you use venv
or .venv
, these names have become standardized where many other tools will know to look for these folder names when listing virtual environments.
- You can also choose to name your
venv
the name of your project, e.g.,python -m venv my_project
. This will make it explicit and clear what environment you are working in if you are jumping between project folders with a commonvenv
environment. However, because themy_project
name is not standard and fairly arbitrary, not every tool will recognize you have a pythonvenv
virtual environment in your directory.
The venv
folder also contains a link to the Python version that will be used. This is why it’s important to:
- Use the correct python version before creating the
venv
- Make sure the original python environment does not have anything installed in it
The venv
python module will install pip
for us by default. You can also see from the tree
diagram below, how all the different ways we can call python
are linked together.
my_python_project % tree -L 4 .
.
└── venv
├── bin
│ ├── Activate.ps1
│ ├── activate
│ ├── activate.csh
│ ├── activate.fish
│ ├── pip
│ ├── pip3
│ ├── pip3.12
│ ├── python -> /Users/danielchen/.pyenv/versions/3.12.0/bin/python
│ ├── python3 -> python
│ └── python3.12 -> python
├── include
│ └── python3.12
├── lib
│ └── python3.12
│ └── site-packages
└── pyvenv.cfg
3 Activate
With our venv
named venv
created, we need to “activate” it.
source venv/bin/activate
/bin/Activate.ps1 venv
This will typically prepend the name of the virtual environment to your terminal prompt. Since we named our venv
the name venv
we should see venv
in the beginning of our prompt.
Before activate:
%
After:
(venv) %
4 Install Packages
Now that we have our venv
virtual environment activated, we are finally able to install packages. Let’s install the Python pandas
library.
4.1 Starting with an Empty Environment
Before we do that, let’s prove that our environment is empty, and pandas
is not installed.
We can see that pip freeze
returns nothing. Meaning nothing is currently installed in this environment.
(venv) % pip freeze
(venv) %
If we load up the python
interpreter, we also cannot import the pandas
library.
(venv) % python
Python 3.12.0 (main, Oct 3 2023, 15:47:53) [Clang 15.0.0 (clang-1500.0.40.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pandas
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'pandas'
>>> exit()
(venv) %
5 Install Packages into Environment
Now we can use pip
to install pandas
pip install pandas
This will install pandas
along with any dependencies it needs.
(venv) % pip install pandas
Collecting pandas
Obtaining dependency information for pandas from https://files.pythonhosted.org/packages/38/1b/e425daceff79695e67d115230bdeb57bbdd6cfff8c46d532e4e64d3dc966/pandas-2.1.1-cp312-cp312-macosx_11_0_arm64.whl.metadata
Using cached pandas-2.1.1-cp312-cp312-macosx_11_0_arm64.whl.metadata (18 kB)
Collecting numpy>=1.26.0 (from pandas)
Obtaining dependency information for numpy>=1.26.0 from https://files.pythonhosted.org/packages/7a/72/6d1cbdf0d770016bc9485f9ef02e73d5cb4cf3c726f8e120b860a403d307/numpy-1.26.0-cp312-cp312-macosx_11_0_arm64.whl.metadata
Using cached numpy-1.26.0-cp312-cp312-macosx_11_0_arm64.whl.metadata (53 kB)
Collecting python-dateutil>=2.8.2 (from pandas)
Using cached python_dateutil-2.8.2-py2.py3-none-any.whl (247 kB)
Collecting pytz>=2020.1 (from pandas)
Obtaining dependency information for pytz>=2020.1 from https://files.pythonhosted.org/packages/32/4d/aaf7eff5deb402fd9a24a1449a8119f00d74ae9c2efa79f8ef9994261fc2/pytz-2023.3.post1-py2.py3-none-any.whl.metadata
Using cached pytz-2023.3.post1-py2.py3-none-any.whl.metadata (22 kB)
Collecting tzdata>=2022.1 (from pandas)
Using cached tzdata-2023.3-py2.py3-none-any.whl (341 kB)
Collecting six>=1.5 (from python-dateutil>=2.8.2->pandas)
Using cached six-1.16.0-py2.py3-none-any.whl (11 kB)
Using cached pandas-2.1.1-cp312-cp312-macosx_11_0_arm64.whl (10.6 MB)
Using cached numpy-1.26.0-cp312-cp312-macosx_11_0_arm64.whl (13.7 MB)
Using cached pytz-2023.3.post1-py2.py3-none-any.whl (502 kB)
Installing collected packages: pytz, tzdata, six, numpy, python-dateutil, pandas
Successfully installed numpy-1.26.0 pandas-2.1.1 python-dateutil-2.8.2 pytz-2023.3.post1 six-1.16.0 tzdata-2023.3
Now if we run pip freeze
you will see pandas
, the version installed, and all of its installed dependencies.
(venv) % pip freeze
numpy==1.26.0
pandas==2.1.1
python-dateutil==2.8.2
pytz==2023.3.post1
six==1.16.0
tzdata==2023.3
We can now import
pandas without the ModuleNotFoundError
.
(venv) % python
Python 3.12.0 (main, Oct 3 2023, 15:47:53) [Clang 15.0.0 (clang-1500.0.40.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pandas as pd
>>>
5.1 Install Packages as You Need
As long as you are in the correct venv
, you can pip install
any package you need from the Python Package Index (PyPI).
pip install matplotlib plotnine seaborn
6 Save Your Environment to requirements.txt
We’ve been using pip freeze
to see what packages are installed in the current environment. We can actually save the contents of this output to a requirements.txt
so the installed packages and versions are documented. We can then version control and/or give this file to other people for them to replicate our venv
virtual environment.
pip freeze > requirements.txt
This will save a requirements.txt
file into our current working directory.
7 Deactivate
Once you’re done with your current python venv
, make sure you run deactivate
so you do not pip install
packages you do not need in your project.
deactivate
This will exit out of your venv
, and you should see your prompt change back.
In the venv
:
(venv) % deactivate
Deactivated:
my_python_project %
And we’re back to our original package environment
% python
Python 3.12.0 (main, Oct 3 2023, 15:47:53) [Clang 15.0.0 (clang-1500.0.40.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pandas
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'pandas'
8 Install Packages from requirements.txt
If you or someone else wants to set up a new venv
with a requirements.txt
file, make sure you follow the steps on this page to create a new venv
first.
Then, with the new venv
activated, you can install all the packages and versions from the requirements.txt
with:
pip install -r requirements.txt
This is one way reproducible programming environments are created in Python.
9 Conclusion
It’s important that every python project has its own virtual environment. Ideally it also has the environment saved into a requirements.txt
file. This is the Iron Law of Python Management.