Virtual Environments
A virtual environment gives one Python project its own private set of installed packages. The project you work on today holds the versions it needs, and the project you return to next month holds different versions, and neither one disturbs the other or the Python your operating system ships.
Isolation is what a virtual environment buys you. Python resolves an import by walking sys.path, a list of directories it searches in order. Install every project's packages into one shared location and two projects that need different versions of the same library collide. Give each project its own directory of packages and the collision never happens.
What a virtual environment is
A virtual environment is a directory holding its own pyvenv.cfg file, its own site-packages directory for installed packages, and a Python executable copied or symlinked from a base installation. The base Python's standard library is shared rather than copied, so the environment stays small. The standard-library venv module creates one with python -m venv (Python venv docs).
The pyvenv.cfg file at the root marks the directory as an environment and configures the interpreter that starts there. It carries a home key pointing at the base installation, and an include-system-site-packages key, "set to true if venv is run with the --system-site-packages option, false otherwise" (Python venv docs). Left false, the environment sees only its own packages.
site-packages is the directory where installed third-party packages land. Each environment has its own. The base installation's site-packages stays off the environment's sys.path unless you opt in, so an import inside the environment resolves to the environment's packages first and the base packages not at all.
The mechanism: two prefixes
When Python starts inside an environment, it reports two locations. sys.prefix points at the environment. sys.base_prefix points at the base installation the environment was built from. In a plain base interpreter the two match.
That gives you a one-line test for whether code runs inside an environment: sys.prefix != sys.base_prefix is True inside one. PEP 668 frames the same check from the other side, reading sys.prefix == sys.base_prefix as the sign of a base install (PEP 668).
import sys
print(sys.prefix != sys.base_prefix) # True inside a virtual environment
The base installation holds the standard library once, and every environment built from it shares that library while keeping its own packages.
graph TD
base["Base Python<br/>standard library<br/>sys.base_prefix"]
base --> a[".venv (project A)<br/>own site-packages<br/>sys.prefix → A"]
base --> b[".venv (project B)<br/>own site-packages<br/>sys.prefix → B"]
Activation is a convenience, not the isolation
You activate an environment to make it the default for a shell. The activation script prepends the environment's bin directory (Scripts on Windows) to PATH and sets VIRTUAL_ENV, so typing python or pip runs the environment's copies.
Activation stays optional. The Python docs put it plainly: "You don't specifically need to activate a virtual environment, as you can just specify the full path to that environment's Python interpreter when invoking Python" (Python venv docs). Running .venv/bin/python reaches into the environment whether or not you activated it. Scripts and CI pipelines rely on this, because calling the interpreter by path drops the dependency on shell-specific activation.
# Create, activate, install
python -m venv .venv
source .venv/bin/activate # Windows (PowerShell): .venv\Scripts\Activate.ps1
python -m pip install httpx
# Or skip activation and call the interpreter directly
.venv/bin/python -m pip install httpx
Creating one: venv, virtualenv, or uv
Three tools create environments, and all three produce the same directory layout, so anything that reads a pyvenv.cfg works against any of them.
venvships in the standard library.python -m venv .venvbuilds an environment for the interpreter you ran it with, no install step required (Python venv docs). The packaging guide namesvenvand the PyPAvirtualenvproject as "the standard tools to create and use virtual environments manually" (PyPA tool recommendations).virtualenvis a third-party package that predatesvenv. It targets older and alternate interpreters and builds environments faster on some systems, at the cost of one extra dependency (PyPA tool recommendations).uv(from Astral) builds the same standard environments. "Unlikepip, uv requires using a virtual environment by default," anduv venvcreates one at.venv(uv docs). It also manages the project's Python version and locks dependencies in auv.lockfile (uv projects guide).
uv venv # creates .venv using a managed Python
uv pip install httpx # installs into it, no activation needed
For installing Python command-line tools rather than project dependencies, the packaging guide points at pipx, which puts each application in its own environment (PyPA tool recommendations).
Don't install into the system Python
The Python your operating system ships runs operating-system tooling. Installing project packages into it risks breaking that tooling, so modern distributions block it. PEP 668 defines an EXTERNALLY-MANAGED marker that a distribution places next to its standard library. When pip runs against a marked base interpreter, it "should exit with an error message indicating that package installation into this Python interpreter's directory are disabled outside of a virtual environment" (PEP 668).
The error reads externally-managed-environment, and the fix is the subject of this page: create a virtual environment and install there. Inside an environment sys.prefix != sys.base_prefix, so the marker no longer applies and pip installs normally (PEP 668).
Where this stands
venv plus pip is the official baseline, and every Python carries it (PyPA tool recommendations). uv folds environment creation, Python-version management, and locked dependencies into one fast tool, and it still produces standard environments, so the tools and editors that expect a pyvenv.cfg keep working (uv projects guide). Whichever you reach for, the rule underneath holds: one environment per project, and never the system Python.
The Python for Spring Boot Engineers guide puts the uv workflow to work across a full project.