Virtual environments and pip
Create an isolated environment for each project, install third-party packages into it with pip, and record what you installed so someone else can repeat it.
What you will learn
- Create and use a virtual environment with python -m venv
- Install, list, upgrade and remove packages with pip
- Explain why a system Python often refuses pip installs
- Record dependencies in requirements.txt and rebuild an environment from it
On this page
The standard library will take you far, but sooner or later you want a package someone else published. Installing it for the whole machine causes trouble: one project needs version 1 of a library, another needs version 2, and an upgrade for one silently breaks the other. A virtual environment gives each project its own private set of installed packages.
The commands in this lesson run in a terminal, not inside Python. Blocks marked console are sessions to type yourself. The site’s checks do not run them. If a terminal still feels new, the fundamentals lesson on tools covers it.
What a virtual environment is
A virtual environment is a folder containing a Python interpreter and its own site-packages directory, the place where installed packages go. The Python documentation describes one as disposable: it should be easy to delete and recreate, it is not checked into version control, and it is not meant to be moved or copied.
Create one inside your project folder, conventionally named .venv:
$ python -m venv .venvOn Windows the launcher is often py, so the command is py -m venv .venv. Either way, python -m venv runs the standard library’s venv module, and .venv is the folder it makes. Inside you will find a bin folder (Scripts on Windows) with the interpreter and pip, and a lib/pythonX.Y/site-packages folder (Lib\site-packages on Windows) that starts nearly empty.
Activating it
Activating puts the environment’s bin folder first on your PATH, so the commands python and pip mean the environment’s own.
macOS and Linux (bash, zsh)
$ source .venv/bin/activate(.venv) $Windows PowerShell
PS> .venv\Scripts\Activate.ps1(.venv) PS>Windows cmd.exe
C:\> .venv\Scripts\activate.bat(.venv) C:\>The (.venv) prefix reminds you that you are inside. To leave, type deactivate.
Activation is a convenience, not a requirement. You can always call the environment’s interpreter by its path, as in .venv/bin/python script.py, and it behaves the same way. That is how editors and scheduled jobs usually do it.
To check which Python you are really running, ask the interpreter. In a virtual environment, sys.prefix points at the environment and sys.base_prefix points at the Python it was built from, so they differ.
import sys
if sys.prefix != sys.base_prefix: print("virtual environment:", sys.prefix)else: print("base installation:", sys.prefix)$ python where_am_i.pyvirtual environment: /home/you/project/.venvOutside the environment, the same script prints base installation: followed by the path of your main Python.
Installing with pip
pip is Python’s package installer, and it comes with the environment. Always run it as python -m pip, which guarantees it belongs to the same interpreter you will run your code with. Here we install requests, a widely used library for HTTP:
(.venv) $ python -m pip install requestsCollecting requests...Installing collected packages: urllib3, idna, charset-normalizer, certifi, requestsSuccessfully installed certifi-... charset-normalizer-... idna-... requests-... urllib3-...The output is trimmed here and the version numbers elided, since yours will differ. requests depends on four other packages, and pip installed them all. Now the import works inside the environment:
import requests
print(requests.codes.ok)(.venv) $ python check_requests.py200This script needs requests installed, so it is the one example in this module that the site’s automatic checks skip. Outside the environment it would stop with ModuleNotFoundError: No module named 'requests', which is the expected way for isolation to look.
A few other commands you will use constantly:
$ python -m pip list show what is installed$ python -m pip install "requests>=2" install with a version constraint$ python -m pip install --upgrade requests$ python -m pip uninstall requests$ python -m pip show requests version, dependencies, install locationRecording what you installed
If you hand your project to someone else, they need to know what to install. pip freeze prints every installed package with its exact version, in a form pip can read back:
(.venv) $ python -m pip freeze > requirements.txt(.venv) $ cat requirements.txtcertifi==...charset-normalizer==...idna==...requests==...urllib3==...Anyone, including you in six months, can rebuild the environment from that file:
$ python -m venv .venv$ source .venv/bin/activate(.venv) $ python -m pip install -r requirements.txtThis is why disposable is a feature. If the environment breaks, delete .venv and recreate it from the requirements file. The next lessons describe a more capable way to record dependencies, in pyproject.toml, and a faster installer, uv. The ideas carry over unchanged.
Why the system Python says no
On many Linux distributions and on some macOS setups, running pip install against the system Python fails with an error that the environment is externally managed. This is PEP 668: the operating system’s package manager owns that Python, and a second installer writing into the same folders could break system tools. The cure the message points to is the one you have already learned: make a virtual environment and install there. Do not reach for sudo pip, and do not override the protection with --break-system-packages unless you know exactly what you are doing.
Run where_am_i.py twice, once with the environment activated and once after deactivate. Then run it a third time without activating, but by calling the environment’s interpreter directly by path. Which of the three print “virtual environment”?
Expected: three lines, one per run in the order described, each reading virtual environment: ... or base installation: ....
Show solution
The first and the third:
$ source .venv/bin/activate(.venv) $ python where_am_i.pyvirtual environment: /home/you/project/.venv(.venv) $ deactivate$ python where_am_i.pybase installation: /usr$ .venv/bin/python where_am_i.pyvirtual environment: /home/you/project/.venvThe interpreter at .venv/bin/python knows it lives in an environment from its own location, so activation is not needed. Activation only changes what the bare word python means in your shell. On Windows the path is .venv\Scripts\python.exe.
In a new empty folder, create a virtual environment, install requests, and run check_requests.py. Write requirements.txt with pip freeze. Then delete .venv entirely, recreate it, install from the requirements file, and run the script again. It should print 200 both times.
Expected: requirements.txt contains a line starting requests==, and the two runs of the script print:
200200Show solution
$ python -m venv .venv$ source .venv/bin/activate(.venv) $ python -m pip install requests(.venv) $ python check_requests.py200(.venv) $ python -m pip freeze > requirements.txt(.venv) $ deactivate$ rm -rf .venv$ python -m venv .venv$ source .venv/bin/activate(.venv) $ python -m pip install -r requirements.txt(.venv) $ python check_requests.py200On Windows, delete the folder with rmdir /s /q .venv in cmd.exe, and use the Windows activation command from the tabs. The point is that the environment is rebuilt from the text file alone, with no state kept in .venv.
A learner runs pip install requests and sees “Successfully installed”. They then run python check_requests.py and get ModuleNotFoundError: No module named 'requests'. Give two likely causes and the single command that settles it.
Expected: two causes in one sentence each, and one command line of the form python -m pip install requests, after which python check_requests.py prints 200.
Show solution
The pip that ran and the python that ran belong to different interpreters. Typical causes: the environment was active in one terminal window but the script was run from another, or the bare pip on the PATH belongs to a different Python installation than the bare python.
The command that settles it is the one that ties the two together:
$ python -m pip install requestspython -m pip installs into the interpreter named by python, so the same python finds the package afterwards. To see which interpreter you have, run python -c "import sys; print(sys.executable)".
Summary
- A virtual environment is a disposable folder of installed packages, made with
python -m venv .venv, used by activating it or by calling its interpreter by path. - Install with
python -m pip install, so pip and python are the same interpreter. Record withpip freeze > requirements.txtand restore withpip install -r. - A system Python marked externally managed (PEP 668) is a reason to use a virtual environment, not to override the warning.