Skip to content

Python Package Managers ​

Every Python project eventually needs a library you did not write. A package manager is the tool that fetches that library, figures out which version of it works with your other dependencies, and installs it into your environment. The Python ecosystem has several options. For this course we use uv, which is faster than every alternative and simple enough that you will rarely think about it beyond a handful of commands.

What you'll learn

  • uv add installs a package and writes it to pyproject.toml -- one command, declarative, reproducible
  • uv sync replays the lockfile: anyone cloning your project gets the exact same dependency tree
  • You will still see pip install in tutorials everywhere -- know what it does, but use uv in your own projects

The problem ​

You find a tutorial that says pip install openai. You run it, and it works. A week later you run pip install langchain, and suddenly openai stops working because langchain pulled in a conflicting version of a shared dependency. pip installs whatever it resolves in the moment. There is no record of exactly which versions were installed, and no way to recreate that exact state on another machine.

Package managers solve this by recording your dependencies and their resolved versions in a file. Anyone who clones your project runs a single command and gets the identical set of packages you had.

Options & when to use each ​

ToolWhat it doesWhen to use it
pipInstalls packages from PyPI with no lockfileFollowing tutorials, quick one-offs where reproducibility does not matter
uvInstalls, resolves, and locks dependencies; manages environmentsAny project you intend to keep or share -- the default for this course
PoetryDependency management with a declarative pyproject.toml and lockfileTeams already standardized on Poetry; functionally similar to uv but slower

uv is the tool we teach because it does everything pip does, faster, and adds the lockfile workflow that prevents the "it worked last week" problem. It also manages environments (uv venv) and can install Python itself (uv python install). One tool for the entire dependency lifecycle.

Build it ​

Install a package ​

With an active virtual environment (see the previous lesson):

bash
# Install a package and record it in pyproject.toml
uv add httpx

# Install multiple packages at once
uv add pydantic openai python-dotenv

# Install a specific version
uv add fastapi==0.115.0

After each uv add, two things happen: the package is installed into your .venv, and it is written to pyproject.toml under [project.dependencies]. Check the file after running a few adds:

bash
cat pyproject.toml

You will see entries like:

toml
[project]
dependencies = [
    "httpx>=0.28.0",
    "pydantic>=2.0",
    "openai>=1.0",
]

The lockfile ​

Every time you run uv add or uv lock, uv creates or updates uv.lock. This file records the exact version of every package and every transitive dependency (the dependencies of your dependencies). It is the guarantee that your project builds identically on every machine.

bash
# Create or update the lockfile from pyproject.toml
uv lock

# Install everything exactly as specified in the lockfile
uv sync

Commit uv.lock to version control. Do not commit .venv/. When someone clones your project:

bash
git clone https://github.com/you/my-agent-project
cd my-agent-project
uv sync
# .venv is created, all dependencies installed, identical to yours

Removing a package ​

bash
uv remove httpx
# Removes from pyproject.toml and from the environment

What you will see in tutorials: pip ​

Most tutorials outside this portal use pip. When you see pip install openai, translate it mentally: pip puts packages into whichever environment is active, same as uv add, but pip does not update pyproject.toml. If you are inside a uv-managed project and want to use pip for a one-off:

bash
# Inside an activated uv environment, pip installs to .venv
source .venv/bin/activate
pip install some-experimental-package
# Package is installed, but NOT recorded in pyproject.toml

If you decide to keep that package, add it properly afterward:

bash
uv add some-experimental-package

Exporting to requirements.txt ​

Some deployment systems still expect a requirements.txt file. uv can generate one:

bash
uv pip compile pyproject.toml -o requirements.txt

This produces a pinned requirements.txt from your pyproject.toml dependencies.

What goes wrong ​

MistakeHow you notice itThe fix
Used pip install instead of uv addPackage works but does not appear in pyproject.tomlRun uv add <package> to record it properly
Forgot to run uv sync after pullingModuleNotFoundError on a package your teammate addedRun uv sync. It installs everything from the lockfile
uv.lock merge conflictGit shows a conflict in uv.lock after merging a branchDelete uv.lock, run uv lock, commit the regenerated file. uv.lock is machine-generated, do not hand-edit it
Cache taking up disk spaceuv cache dir shows gigabytes usedRun uv cache clean. The cache speeds up installs but grows over time. Cleaning it is safe -- packages re-download when needed
Installed a package globally (outside an environment)uv add fails with permissions errorsNever run uv add or pip install without an active environment. If you see a permission error, activate the environment first

Confirm it worked ​

After setting up a project with dependencies, verify everything is reproducible:

bash
# 1. Check that pyproject.toml lists your dependencies
grep -A 5 "dependencies" pyproject.toml
# Should show the packages you added

# 2. Check that the lockfile exists and is recent
ls -lh uv.lock
# Should exist and have a timestamp matching your last uv add or uv lock

# 3. Simulate a fresh install
rm -rf .venv
uv sync
# Should recreate .venv and install all packages from scratch in seconds

# 4. Verify your code still runs
python -c "import httpx; print(httpx.__version__)"
# Should print the installed version without errors

Next: Git & Version Control