5.4. pip and Package Management
Table of Contents
Why Package Management Matters
Modern backend development depends on thousands of reusable libraries. Instead of writing everything from scratch, you install and reuse these libraries in your projects.
Python uses a tool called pip to:
- Download packages from the internet
- Install them into your environment
- Uninstall or upgrade them when needed
A package manager solves three main problems:
- Discovering packages by name.
- Installing correct versions with all their dependencies.
- Reproducing the same setup on another machine.
Without proper package management, your code might:
- Work on your computer, but not on the server.
- Break when a library releases a new incompatible version.
- Be impossible to set up again in the future.
In this chapter, you will learn how to use pip in a way that is safe and suitable for backend projects.
Installing and Upgrading Packages with pip
When you installed Python, you normally got pip along with it.
You can check that pip is available with:
pip --versionor, more explicit:
python -m pip --version
Using python -m pip is often safer, because it guarantees you use the pip that belongs to that specific Python interpreter.
Basic installation
To install a package:
pip install requestsThis will:
- Download the latest version of
requestsfrom the Python Package Index (PyPI). - Install
requestsand any libraries it depends on.
You can then use it in your code:
import requests
response = requests.get("https://example.com")
print(response.status_code)Installing a specific version
You often want fixed versions in backend projects, so your environment is predictable.
pip install "requests==2.31.0"Other version operators:
| Operator | Meaning | Example |
|---|---|---|
== | Exactly this version | requests==2.31.0 |
>= | This version or newer | requests>=2.28.0 |
<= | This version or older | requests<=2.31.0 |
> | Strictly higher version | requests>2.0.0 |
< | Strictly lower version | requests<3.0.0 |
~= | Compatible release, same major.minor series | requests~=2.31 |
Example:
pip install "fastapi~=0.111"This means “install FastAPI 0.111.x, but not 0.112 or higher”.
In backend projects, always pin versions in your dependency files, for example package==1.2.3. This makes deployments and production behavior predictable.
Upgrading and reinstalling
Upgrade a package to the latest version:
pip install --upgrade requests
# or shorter:
pip install -U requestsUpgrade to a specific version:
pip install "requests==2.32.0"Force reinstall (useful if an installation got corrupted):
pip install --force-reinstall "requests==2.31.0"Uninstalling packages
To remove a package:
pip uninstall requests
You will be asked to confirm. Answer y or yes to proceed.
Listing and Inspecting Installed Packages
Understanding what is installed in your environment is important, especially on servers or in virtual environments.
Listing installed packages
To list all installed packages:
pip listExample output (shortened):
Package Version
---------- -------
pip 24.0
setuptools 69.0.0
requests 2.31.0
fastapi 0.111.0
uvicorn 0.30.0To include packages that pip did not install (like some system packages):
pip list --localShowing details about a package
To see detailed information about a specific package:
pip show requestsExample output:
Name: requests
Version: 2.31.0
Summary: Python HTTP for Humans.
Home-page: https://requests.readthedocs.io
Author: Kenneth Reitz
License: Apache 2.0
Location: /path/to/venv/lib/python3.11/site-packages
Requires: certifi, charset-normalizer, idna, urllib3
Required-by: some-other-packageThis helps you:
- See the installed version.
- See where it is installed.
- Understand dependencies (
Requires). - See what depends on it (
Required-by).
Checking outdated packages
To see which installed packages have newer versions available:
pip list --outdatedExample:
Package Version Latest Type
-------- ------- ------ -----
uvicorn 0.30.0 0.31.0 wheel
fastapi 0.111.0 0.112.0 wheelIn production projects, you normally do not upgrade everything blindly. You upgrade carefully and test.
Requirements Files and Dependency Freezing
For real backend projects, you must be able to recreate the exact same environment on:
- your local machine
- teammates' machines
- CI servers
- production servers
You do this using a requirements file.
What is a requirements file?
A requirements.txt file is a plain text file that lists all your project dependencies.
Simple example:
fastapi==0.111.0
uvicorn[standard]==0.30.0
sqlalchemy==2.0.30
psycopg2-binary==2.9.9
python-dotenv==1.0.1Installing from a requirements file
To install everything from this file:
pip install -r requirements.txtThis is how you:
- Set up a new environment for a project
- Set up CI/CD
- Set up production servers
Creating a requirements file manually
For small projects, you can just write requirements.txt by hand.
Example scenario:
You create a new FastAPI project and install some packages:
pip install fastapi uvicorn[standard]
pip install sqlalchemy psycopg2-binary
Then you create requirements.txt:
fastapi==0.111.0
uvicorn[standard]==0.30.0
sqlalchemy==2.0.30
psycopg2-binary==2.9.9You get the exact installed versions with:
pip show fastapi uvicorn sqlalchemy psycopg2-binary
or by using pip freeze as explained next.
Freezing dependencies with pip freeze
pip freeze prints all installed packages in a format that pip install -r understands:
pip freezeExample output (shortened):
anyio==4.4.0
fastapi==0.111.0
idna==3.7
pydantic==2.8.0
sniffio==1.3.1
starlette==0.37.2
typing_extensions==4.12.2
uvicorn==0.30.0You can redirect this into a file:
pip freeze > requirements.txt
Now requirements.txt contains the full dependency tree, not just your top level packages, for example:
fastapi==0.111.0
pydantic==2.8.0
starlette==0.37.2
typing_extensions==4.12.2
uvicorn==0.30.0
...
pip freeze includes everything in your current environment. Only run it inside a clean virtual environment that belongs to a single project. Never run it in a global interpreter.
Top level vs full freeze
There are two common strategies:
| Strategy | Description | Pros | Cons |
|---|---|---|---|
| Top level only | You list only the libraries you directly use | Short and readable file | Less precise, installs latest sub-deps |
| Full freeze (pip freeze) | You list every installed dependency | Fully reproducible environments | Long file, includes indirect dependencies |
For backend projects you want reproducibility, so full freeze is common, especially for production.
A typical approach:
- Maintain
requirements.inwith only top level dependencies:
fastapi
uvicorn[standard]
sqlalchemy
psycopg2-binary- Generate
requirements.txtusing a tool (such aspip-tools) that pins all versions.
You will learn about more advanced tools later. For now it is enough to understand the idea.
Optional Dependencies and Extras
Some packages provide optional features that you can enable with extras. You saw a glimpse of this with uvicorn[standard].
What are extras?
An extra is a named group of additional dependencies that add some feature, usually written in square brackets.
Example:
pip install "uvicorn[standard]"
uvicorn defines an extra called standard. It might include things like:
uvloopfor faster event loophttptoolsfor faster HTTP parsing
You get a more fully featured Uvicorn server with one command.
You can also combine extras:
pip install "somepackage[redis,postgres]"This installs:
somepackagebase- Additional dependencies for the
redisextra - Additional dependencies for the
postgresextra
You do not need to know how to define extras yet, just how to use them when a package documents them.
Optional dependencies in practice
Examples you will likely see in backend work:
pip install "sqlalchemy[asyncio]"
pip install "fastapi[all]"
pip install "httpx[http2]"You should always follow the project documentation. It will mention which extras you should enable for your use case.
Managing Dependencies Across Environments
Backend applications typically have multiple environments:
- Development on your machine
- Testing or CI
- Production
You usually want:
- Core dependencies everywhere
- Extra tools only in development or testing
Splitting dependencies: prod vs dev
You can use multiple requirements files.
Example:
requirements.txt (for production):
fastapi==0.111.0
uvicorn[standard]==0.30.0
sqlalchemy==2.0.30
psycopg2-binary==2.9.9
python-dotenv==1.0.1
requirements-dev.txt (for development):
-r requirements.txt
pytest==8.2.0
pytest-asyncio==0.23.0
pytest-cov==5.0.0
black==24.4.2
isort==5.13.2
The -r requirements.txt line tells pip: “first install everything from requirements.txt”.
To install only production dependencies:
pip install -r requirements.txtTo install both production and dev dependencies:
pip install -r requirements-dev.txtExample: different environments
Imagine your FastAPI project:
- Production server should not have development tools like linters and test frameworks.
- Developers need tests and code formatters.
So your workflow:
- On your development machine:
pip install -r requirements-dev.txt- On the production server:
pip install -r requirements.txtThis keeps production environments smaller and reduces attack surface.
Common pip Commands for Backend Projects
Here is a summary of pip commands you will use again and again.
Installation commands
| Task | Command example |
|---|---|
| Install latest version | pip install fastapi |
| Install specific version | pip install "fastapi==0.111.0" |
| Install with extras | pip install "uvicorn[standard]" |
| Install from requirements | pip install -r requirements.txt |
| Install from local directory | pip install . (in a directory with pyproject.toml) |
Inspection commands
| Task | Command example |
|---|---|
| Check pip version | pip --version |
| List installed packages | pip list |
| List outdated packages | pip list --outdated |
| Show details for a package | pip show fastapi |
| Generate a full freeze file | pip freeze > requirements.txt |
Maintenance commands
| Task | Command example |
|---|---|
| Upgrade a package | pip install --upgrade fastapi |
| Upgrade to specific ver | pip install "fastapi==0.112.0" |
| Uninstall a package | pip uninstall fastapi |
| Reinstall a package | pip install --force-reinstall package |
Typical Workflow in a Backend Project
To put all of this together, here is a simple but realistic example workflow.
Starting a new backend project
- Create and activate a virtual environment (covered in the “Virtual Environments” chapter).
- Install initial dependencies:
pip install fastapi uvicorn[standard]- Start coding:
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello Backend"}- Run the app:
uvicorn main:app --reload- Freeze dependencies:
pip freeze > requirements.txtNow you have a reproducible environment.
Adding a new library later
You decide to store data in PostgreSQL using SQLAlchemy:
- Install new dependencies:
pip install sqlalchemy psycopg2-binary- Update
requirements.txt:
pip freeze > requirements.txt- Commit code and
requirements.txtto version control.
Setting up on another machine
On another computer or a server:
- Create and activate a virtual environment.
- Install dependencies:
pip install -r requirements.txtNow this environment has exactly the same package versions, so your backend behaves the same.
Good Practices and Common Pitfalls
You now know the mechanics of pip. To use it safely in backend work, keep these rules in mind.
Good practices
- Use virtual environments for every project.
- Pin versions in production using
==. - Store requirements files in your repository.
- Split dev and prod requirements if needed.
- Use
python -m pipto avoid mixing Python versions. - Document how to install dependencies in your project README.
Example README section:
## Setup
1. Create virtual environment:
python -m venv .venv
2. Activate it:
# Windows:
.venv\Scripts\activate
# macOS / Linux:
source .venv/bin/activate
3. Install dependencies:
pip install -r requirements.txtCommon pitfalls
| Pitfall | Why it is a problem | Better approach |
|---|---|---|
| Using global Python for all projects | Package conflicts between projects | Use one virtual environment per project |
| Not pinning versions in production | Apps break when packages release incompatible updates | Use == versions in requirements.txt |
Running pip freeze in a polluted environment | Requirements include unrelated packages | Use clean virtual environments per project |
| Installing dev tools in production | Larger attack surface and slower deployments | Separate requirements.txt and requirements-dev.txt |
Using pip without specifying Python version | Risk of mixing up Python 3 vs system default | Use python -m pip |
By following these patterns you will be able to manage your backend project dependencies in a professional, reliable way.
Views: 8
KAHIBARO