Understanding how Python virtualenv works is one of the things you should know when you get started with Python.
Python virtualenv creates an isolated environment in which you can install all Python dependencies you need for your project. If you work on multiple projects that require different dependencies using virtual environments allows to keep those environment separate. It also allows to have an environment that doesn’t interfere with the global system Python.
In this guide you will learn what you need to start using virtualenv for your projects. We will go through practical examples that will help you understand how virtualenv works.
Virtualenv…here we go!
Why Would You Use a Virtual Environment in Python
As explained in the previous section Python virtualenv allows you to create an environment that contains all the dependencies (packages) needed by Python to execute projects.
A virtual environment is a single directory that can be created as I explain in the next section.
For now the important concept is that…
Using virtual environments allows you to manage multiple projects without risking Python dependencies of one project to break another project.
A simple example is the scenario in which ProjectA requires version 1.0.0 of a package and ProjectB only works with version 1.1.0 of the same package.
Running both projects using the system Python environment would not be possible and that’s where virtualenv can help.
How Do You Create a Virtual Environment in Python?
The examples I will be showing here are based on Linux. The concepts are very similar for Mac and Windows, certain commands are slightly different on Windows.
Firstly, I have created two aliases inside .bashrc for the ec2-user to refer to python3 and pip3 simply as python and pip:
alias python=python3
alias pip=pip3
First of all, I will confirm that Python and PIP are installed:
[ec2-user@host ~]$ python --version
Python 3.7.8
[ec2-user@host ~]$ pip --version
pip 9.0.3 from /usr/lib/python3.7/site-packages (python 3.7)
As you can see I have Python 3.7.8 and PIP 9.0.3 on my system. The versions on your system might differ from those depending on your Linux distribution and version.
The first step to create a virtual environment is to install the virtualenv package using pip:
[ec2-user@host ~]$ pip install virtualenv --user
Collecting virtualenv
Using cached https://files.pythonhosted.org/packages/1d/09/9179b676c126b2687bf4110e5b88c8c52d9113f31bd5f8f6ab97d380e434/virtualenv-20.0.30-py2.py3-none-any.whl
Requirement already satisfied: appdirs<2,>=1.4.3 in /usr/local/lib/python3.7/site-packages (from virtualenv)
Requirement already satisfied: six<2,>=1.9.0 in /usr/local/lib/python3.7/site-packages (from virtualenv)
Requirement already satisfied: distlib<1,>=0.3.1 in /usr/local/lib/python3.7/site-packages (from virtualenv)
Requirement already satisfied: importlib-metadata<2,>=0.12; python_version < "3.8" in /usr/local/lib/python3.7/site-packages (from virtualenv)
Requirement already satisfied: filelock<4,>=3.0.0 in /usr/local/lib/python3.7/site-packages (from virtualenv)
Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.7/site-packages (from importlib-metadata<2,>=0.12; python_version < "3.8"->virtualenv)
Installing collected packages: virtualenv
Successfully installed virtualenv-20.0.30
The –user flag tells pip to install the virtualenv package in a local directory inside the home directory of the current user (ec2-user).
This command would fail if we don’t pass the –user flag because ec2-user doesn’t have access to install packages in the system Python library.
The following command confirms that virtualenv is installed successfully:
[ec2-user@host ~]$ virtualenv --version
virtualenv 20.0.30 from /home/ec2-user/.local/lib/python3.7/site-packages/virtualenv/__init__.py
Now, to create the virtual environment go to the directory where you want to develop your Python projects (called projects in this example) and run the following command:
virtualenv codefathertech
This creates a virtual environment called codefathertech. Here is the output on my Linux system:
[ec2-user@host blog]$ cd projects/
[ec2-user@host projects]$ virtualenv codefathertech
created virtual environment CPython3.7.8.final.0-64 in 909ms
creator CPython3Posix(dest=/opt/blog/projects/codefathertech, clear=False, global=False)
seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/ec2-user/.local/share/virtualenv)
added seed packages: pip==20.2.1, setuptools==49.2.1, wheel==0.34.2
activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator
The Structure of Python Virtual Environments
Before learning how to use the virtual environment we have created, let’s have a look at its directory structure. To do that I use the tree command with a display depth of the directory tree equal to 2.
The tree command is not available on my system and I can install it using:
sudo yum install tree
And here is the structure of the virtual environment we have created:
[ec2-user@host projects]$ tree -L 2 codefathertech
codefathertech
├── bin
│ ├── activate
│ ├── activate.csh
│ ├── activate.fish
│ ├── activate.ps1
│ ├── activate_this.py
│ ├── activate.xsh
│ ├── easy_install
│ ├── easy_install3
│ ├── easy_install-3.7
│ ├── easy_install3.7
│ ├── pip
│ ├── pip3
│ ├── pip-3.7
│ ├── pip3.7
│ ├── python -> /usr/bin/python3
│ ├── python3 -> python
│ ├── python3.7 -> python
│ ├── wheel
│ ├── wheel3
│ ├── wheel-3.7
│ └── wheel3.7
├── lib
│ └── python3.7
├── lib64
│ └── python3.7
└── pyvenv.cfg
5 directories, 22 files
The Python binary you see in the bin directory is a symlink to the system Python 3 binary. In the bin directory you can also see a script that we will analyse in the next section: activate.
When the virtualenv is created it also includes the lib directory that contains modules and packages.
But, how does Python know to look for packages in the lib directory inside the virtual environment?
That’s because the Python binary looks for lib directories relative to its path, and the first lib directory it finds is ../lib/python3.7/.
To create a virtualenv that uses a different version of Python e.g. Python 2.7, I can use the following command:
virtualenv -p /usr/bin/python2.7 codefathertech
And now let’s see how to use the virtual environment we have created!
Activate a Virtual Environment
Creating a virtual environment is not enough, in order to use it you also have to activate it.
If you don’t activate the virtualenv after creating it you will still be installing any packages in the system-wide Python distribution.
Remember to activate your virtualenv using the source command:
[ec2-user@host projects]$ source codefathertech/bin/activate
(codefathertech) [ec2-user@host projects]$
As soon as you activate your virtualenv the Linux prompt changes. You will see the name of the virtual environment surrounded by parentheses on the left.
Ok, but what does actually happen when you activate a virtualenv?
The first thing I want to find out is which Python interpreter I’m using before and after activating the virtual environment. To do that I can use the Linux which command:
Before
[ec2-user@host projects]$ which python
alias python='python3'
/usr/bin/python3
After
[ec2-user@host projects]$ source codefathertech/bin/activate
(codefathertech) [ec2-user@ip-172-31-28-249 projects]$ which python
alias python='python3'
/opt/blog/projects/codefathertech/bin/python3
How can the Python interpreter used by default change after activating the virtual environment?
Virtualenv and the PATH Environment Variable
The first thing I can think about that could make this happen is a change in the value of the PATH Linux environment variable.
The PATH environment variable lists the directories used by Linux to execute binaries without specifying their full path.
A quick way to confirm this is by using the echo command to print the value of the PATH environment variable before and after activating the Python virtualenv.
Before
[ec2-user@host ~]$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/ec2-user/.local/bin:/home/ec2-user/bin
After
[ec2-user@host projects]$ source codefathertech/bin/activate
(codefathertech) [ec2-user@ip-172-31-28-249 projects]$ echo $PATH
/opt/blog/projects/codefathertech/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/ec2-user/.local/bin:/home/ec2-user/bin
That’s confirmed!
As you can see, after activating the virtual environment the first directory in the PATH is the bin directory inside the virtual environment we created and activated.
If we want to dig a bit deeper, we can have a look at the content of the activate script inside /opt/blog/projects/codefathertech/bin/.
The following lines show what the activate script does. It updates and exports the PATH environment variable:
VIRTUAL_ENV='/opt/blog/projects/codefathertech'
export VIRTUAL_ENV
PATH="$VIRTUAL_ENV/bin:$PATH"
export PATH
Here you can learn more how export works with Linux environment variables.
And now let’s see what to do when we want to stop working in our virtual environment.
Deactivate a Virtual Environment
In the same way you activate your virtualenv to limit your actions to the environment, you can also deactivate it.
Deactivating a virtualenv brings you back to the original environment you came from.
Here’s an example…
(codefathertech) [ec2-user@host projects]$ deactivate
[ec2-user@host projects]$
As you can see from the left part of the prompt, before running the deactivate command I’m in the codefathertech virtualenv.
After executing the deactivate command the name of the virtualenv disappears from the prompt. This indicates that we are not in the virtual environment anymore.
Before moving to the next section, I would like to find out where the deactivate command is. I didn’t see it in the directory structure of the virtualenv where only the activate script was present.
Let’s see if we can solve this mystery!
Got it, deactivate is not a command…
…it’s a function defined in the activate script:
deactivate () {
unset -f pydoc >/dev/null 2>&1
# reset old environment variables
...
...
...
[full content of the function not included]
}
Good to know!
Installing Modules in a Python Virtualenv
So, now that we know how the structure of a virtualenv is, how to activate it and how to deactivate it. The next step is…
…understanding how new packages are installed in the lib directory that belongs to the virtualenv and not in the system lib directory.
The reason is similar to what we have seen with the python binary…
The bin directory inside the virtual environment also contains a pip binary that when executed installs packages inside codefathertech/lib/python3.7/.
From the moment in which you activate a virtualenv, every package you install using pip will end up in the virtual environment and will not be visible to the global Python environment.
Delete a Python Virtual Environment
Deleting a virtual environment is very simple. You can just delete its folder using the rm command after deactivating the virtual environment.
For instance, in my system I would run the following commands:
(codefathertech) [ec2-user@host projects]$ deactivate
[ec2-user@host projects]$ rm -fr codefathertech
Pretty simple!
Conclusion
You now know what a Python virtual environment is and how it works.
In this guide we have seen how to create a virtual env, how to activate it to start using it for one your projects and how to deactivate it.
In addition, we have also seen:
- How to install modules in a virtualenv.
- How Python keeps everything self-contained in that virtual environment.
Does it make sense to you? Do you have any questions?
Let me know in the comments!
Claudio Sabato is an IT expert with over 15 years of professional experience in Python programming, Linux Systems Administration, Bash programming, and IT Systems Design. He is a professional certified by the Linux Professional Institute.
With a Master’s degree in Computer Science, he has a strong foundation in Software Engineering and a passion for robotics with Raspberry Pi.