What you'll learn
- Explain what a virtual environment is and why every project gets its own
- Create and activate one with
venv, and install packages into it withpip - Pin exact versions in a requirements file so a project installs the same way everywhere
- Meet
uv, the faster tool that does the same jobs and more
Concept
Third-party packages are code other people wrote that you install with pip and then import: a web framework, an HTTP client, a testing tool. They come from the Python Package Index, PyPI, and there are half a million of them. Installing them into the Python that came with your operating system is a mistake you make once: two projects need different versions of the same package, and one of them breaks.
A virtual environment solves this. It is a folder, usually called .venv inside the project, containing a private copy of the Python launcher and its own site-packages where installed packages live. Activate it and python and pip point at that folder; deactivate it and they point back at the system. Each project gets its own, so their dependencies never meet.

The workflow is three commands. python -m venv .venv creates the environment. Activating it is source .venv/bin/activate on macOS and Linux or .venv\Scripts\activate on Windows, and your prompt changes to show the name. pip install httpx then installs into it. pip list shows what is there, and pip install --upgrade or pip uninstall do what they say. Never commit .venv to git; it is rebuilt from the requirements file.
| Command | Does |
|---|---|
python -m venv .venv |
create an environment in .venv |
source .venv/bin/activate |
use it (Windows: .venv\Scripts\activate) |
pip install httpx==0.28.1 |
install one package at an exact version |
pip freeze > requirements.txt |
record everything installed, with versions |
pip install -r requirements.txt |
install exactly that list somewhere else |
deactivate |
go back to the system Python |
Pinning means writing the exact version, httpx==0.28.1, rather than the bare name httpx. Without a pin, the same pip install gives a different version next month, and a project that worked stops working for no visible reason. pip freeze writes every installed package with its exact version into requirements.txt; a teammate, a server, or you in a year runs pip install -r requirements.txt and gets the identical set. This is the single habit that separates code that runs on your machine from code that runs anywhere.
uv is a newer tool that replaces venv and pip with one fast command and adds what they lack: it downloads Python versions for you, it keeps dependencies in pyproject.toml, and it writes a lockfile that records the complete resolved set. uv init, uv add httpx, and uv run python main.py cover the daily loop, and uv sync rebuilds an environment from the lockfile in seconds. This course's own tooling runs on it. Learn pip because every tutorial uses it; use uv for your own projects from the Expert level on.

Code
Where the running Python lives, and whether it is inside a virtual environment. This course's snippets run inside one, which the output reflects.
# where_am_i.py
import sys
from pathlib import Path
print(sys.version.split()[0])
print(sys.prefix != sys.base_prefix, "<- inside a virtual environment")
print(Path(sys.executable).name.startswith("python"))
3.14.7
True <- inside a virtual environment
TrueInspecting installed packages from Python itself. importlib.metadata reads the same information pip list shows; here it confirms the pinned client library this course uses in later lessons.
# installed.py
from importlib.metadata import version, distributions
print("httpx", version("httpx"))
print("pytest", version("pytest"))
names = sorted({d.metadata["Name"].lower() for d in distributions()})
print("httpx" in names and "pytest" in names)
httpx 0.28.1
pytest 9.1.1
TrueA requirements file is plain text. Writing one by hand for the packages this course pins, then reading it back, shows there is nothing magic about the format.
# requirements_demo.py
from pathlib import Path
pins = {"httpx": "0.28.1", "pytest": "9.1.1", "mypy": "2.3.1", "ruff": "0.16.6"}
Path("requirements.txt").write_text("".join(f"{name}=={ver}\n" for name, ver in pins.items()), encoding="utf-8")
for line in Path("requirements.txt").read_text(encoding="utf-8").splitlines():
name, ver = line.split("==")
print(f"{name:<8} pinned to {ver}")
httpx pinned to 0.28.1
pytest pinned to 9.1.1
mypy pinned to 2.3.1
ruff pinned to 0.16.6The commands themselves, for reference. They are shown, not run, because creating environments is something you do in your terminal, once per project.
# setup_project.sh
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install httpx==0.28.1
pip freeze > requirements.txt
deactivate
# the same project with uv
uv init my-project && cd my-project
uv add httpx==0.28.1
uv run python main.py
Try it yourself
In a new folder, create a virtual environment, activate it, and confirm with python -c "import sys; print(sys.prefix)" that the path points inside .venv. Install httpx==0.28.1, run pip freeze, and save it to requirements.txt. Deactivate, delete the .venv folder, recreate it, and reinstall from the requirements file to prove the round trip works. If you have uv, do the same with uv init and uv add.
Common mistakes
- Installing into the system Python.
pip installoutside an environment pollutes every project on the machine; if your prompt does not show(.venv), activate first. - Unpinned requirements.
httpxalone means "whatever is newest today"; writehttpx==0.28.1so tomorrow behaves like today. - Committing
.venv. It is large, machine-specific, and rebuilt fromrequirements.txtoruv.lock; add it to.gitignore.
What's next
- A virtual environment is a private folder of packages per project; activate it before installing anything.
pip install name==versionandpip freeze > requirements.txtmake installs repeatable anywhere.uvdoes the same jobs faster and adds a lockfile; learn pip, use uv for your own projects.
You can bring in the world's Python code without breaking anything. Before you reach for it, though, meet what is already built in: the standard library.