app.py1import requests2print(requests.__version__)PyPI$ you type1python3 -m venv .venv2source .venv/bin/activate3pip install -r requirements.txt4python app.py5deactivate6python3 app.pysystem Pythonsite-packagespython3.venvsite-packagespythonrequirements.txt1requests==2.32.3in usein useModuleNotFoundError.venv(.venv)requests 2.32.32.32.3requests==2.32.3
Cards: the commands you type, app.py and requirements.txtTrays: the system Python and the virtual environment .venv, each with a python and a site-packages shelfPyPI: the Python Package Index, where pip downloads packagesTerminal: the commands and what they printA virtual environment is a folder, .venv, with its own python and its own site-packages; the project app.py needs requests 2.32.3, listed in requirements.txt. Each step lights the parts it is about. A token hops from a command to .venv or the terminal, and from PyPI into .venv's shelf; an "in use" tag sits on the python that the word python runs. Outside the venv the system Python cannot find requests and raises ModuleNotFoundError. In the last step .venv is deleted and rebuilt from requirements.txt.

Why

  1. Python keeps installed packages in one folder, site-packages. Every project run by that Python shares it. If one project needs version 1 of a package and another needs version 2, only one fits, and the projects clash.

Make a venv

Install with pip

Isolation

Try it yourself

;; step 1/9
>>> import site
>>> site.getsitepackages()   # example, macOS
['/opt/homebrew/lib/python3.14/site-packages', ...]

# pip puts packages here; every project that
# runs this Python sees the same folder

On macOS and Linux the command is often python3; inside an activated venv plain python works too. On Windows use py -m venv .venv to create one. The last step assumes the system Python does not have requests. If it does, python3 app.py works there too. Isolation runs both ways: the system Python never looks inside .venv, and by default (include-system-site-packages = false) a venv does not see the system's packages.