RizTech Academy logo
RizTech Academy
Modules, Packages and EnvironmentsLesson 4 of 520 min

Installing packages with pip and pinning requirements

With an environment active, installing packages is one command. Doing it so that your project still works in six months, on somebody else's machine, takes a little more.

pip

python -m pip install requests
python -m pip install requests pandas
python -m pip install "django>=5.0,<6.0"
python -m pip install --upgrade requests
python -m pip uninstall requests

python -m pip rather than bare pip, for the reason in the last lesson: it installs into the Python you just named.

python -m pip list
python -m pip show requests

show gives the version, the location, and — usefully — what depends on it and what it requires.

Where packages come from

PyPI, the Python Package Index, at pypi.org. Anyone can publish to it, which is worth remembering.

Check before installing something unfamiliar. Typosquatting is real: packages named requets or python-dateutil2 exist to catch mistyped installs, and some have shipped malware. A minute on the PyPI page tells you a lot — recent releases, a linked repository, download numbers that match its reputation.

For anything widely used — requests, pandas, django — you are fine. The risk is in obscure packages and typos.

Pinning versions

This is the part that matters.

python -m pip install requests

That installs whatever is current today. A colleague running the same command next month gets a different version, and possibly different behaviour.

Record what you actually used:

python -m pip freeze > requirements.txt
certifi==2026.6.15
charset-normalizer==3.4.1
idna==3.10
requests==2.32.3
urllib3==2.3.0

Exact versions, including the packages your packages needed. Anyone can now reproduce your environment:

python -m pip install -r requirements.txt

Commit requirements.txt. Never commit .venv/. The file is the recipe; the folder is the meal.

Two kinds of requirements file

pip freeze captures everything, which is right for deploying and heavy-handed for describing a project. You cannot tell from that list which packages you actually asked for — certifi and idna are there because requests needed them.

A common approach is to keep two files. requirements.in holds what you chose:

requests
pandas

and requirements.txt is the full pinned output generated from it. Tools like pip-tools or uv do that generation.

For a small project, one hand-maintained file with loose constraints is fine:

requests>=2.32,<3.0
pandas>=2.0

>=2.32,<3.0 accepts bug fixes and refuses the next major version — which, by the convention most packages follow, is where breaking changes land.

What not to do is leave it unpinned entirely and hope. A build that worked last week and fails today, with no change on your side, is almost always an unpinned dependency that released a new version.

Development dependencies

Testing and linting tools are needed to work on a project, not to run it:

# requirements-dev.txt
-r requirements.txt
pytest
ruff
mypy

The -r requirements.txt line includes the main file, so developers install one thing and get both. Production installs only requirements.txt — smaller, faster, less to go wrong.

pyproject.toml

Modern projects increasingly declare dependencies in pyproject.toml instead:

[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
    "requests>=2.32,<3.0",
]

[project.optional-dependencies]
dev = ["pytest", "ruff"]
python -m pip install -e .
python -m pip install -e ".[dev]"

-e installs your project in editable mode, so your own code is importable from anywhere in the project while still being edited in place.

You will meet both. requirements.txt is everywhere and simple; pyproject.toml is the direction things are moving and holds tool configuration too. Either is fine — recognising both matters more.

Reading a version number

2.32.3 is major, minor, patch, by the semantic versioning convention:

  • Patch — bug fixes, safe to take
  • Minor — new features, backwards compatible
  • Major — breaking changes; read the notes before upgrading

The convention is widely followed and not guaranteed. Pinning is what protects you either way.

Keeping up to date

python -m pip list --outdated

Upgrading has a real cost, and so does not upgrading — old dependencies accumulate security problems and eventually become painful to move off. A reasonable habit: review monthly, take patch updates readily, read release notes for major ones, and do it when you have time to test rather than the day before a deadline.

Never upgrade everything at once on a project you cannot test.

When install fails

error: externally-managed-environment on Linux means you are trying to install into the system Python, which your distribution protects. The fix is a virtual environment — this is the operating system telling you to do the right thing.

A compiler error during install usually means a package with C extensions has no prebuilt wheel for your platform or Python version. Often the Python version is too new; check what the package supports.

Could not find a version that satisfies the requirement — a typo in the name, or the package genuinely does not support your Python version.

Permission errors mean you are installing globally without permission. The answer is a virtual environment, not sudo pip. Running sudo pip can damage the Python your operating system depends on.

Practice

  1. In a fresh environment, install requests and confirm the import works.
  2. Run pip show requests and note what it requires.
  3. Run pip freeze and see the packages you did not ask for.
  4. Write requirements.txt, delete the environment, rebuild it from the file, and confirm everything works.
  5. Install a specific older version with ==, then upgrade it and compare pip list before and after.
  6. Create requirements-dev.txt including the main file plus pytest. Install from it.
  7. Run pip list --outdated.
  8. Write a minimal pyproject.toml with one dependency and install it with -e ..
  9. Look up requests on pypi.org. Find its repository, licence and release history.
  10. Try installing without an environment active on Linux and read whatever your system says.

Next: putting all of this together into a project structure you would not be embarrassed to share.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship