10  Dependencies

In contrast to our own code that we can import or to some built-in modules such as pathlib that we can import directly after installing python, there are third-party libraries that we want to use but need to be first installed, such as numpy and pandas. This libraries are also called “packages”.

There are several repositories (also called “indexes”) where people publish python packages and from which we can download them. The official one is PyPI (Python Package Index).

These are open-source libraries maintained mostly by voluntary contributions of people of the community – and you can also contribute to them!

These packages are being actively developed and thus in permanent evolution. In order to avoid compatibility problems and to ensure reproducibility, we might want to specify the exact versions of the packages we use. It turns out that is not an easy problem and the reason why need a package manager.

The good news is that uv does that for us too.

10.1 Managing dependencies

The standard to specify dependencies (and other things) in python projects is to use a file called pyproject.toml that looks like this:

[project]
name = "example_package_name"
version = "0.0.1"
authors = [
  { name="Example Author", email="author@example.com" },
]
description = "A small example package"
readme = "README.md"
requires-python = ">=3.10,<3.12"
dependencies = [
    "numpy==1.2.0",
    "pandas<2.0.0",
    "scikit-learn>=1.0,<2.0"
]
Note

Take a look at the pyproject.toml file in you project, we already have one that uv automatically created for us!

The “dependencies” section is the one we care about here. There we list all the third-party libraries that our project requires. Notice that we can specify both exact versions (with “==”) as well as constraints (with “<,>,>=,<=”).

In general we don’t want to deal manually with those aspects – that’s the job of a package manager.

Note

The packages usually follow a so-called “Semantic versioning” convention on how to name the versions of the package to avoid compatibility problems. In short: Given a version number MAJOR.MINOR.PATCH.

MAJOR version when you make incompatible API changes
MINOR version when you add functionality in a backward compatible manner
PATCH version when you make backward compatible bug fixes

Read more about that here.

We will use here the package manager uv. You can read the docs for more details, which I strongly recommend since the documentation is pretty good and constantly being updated. To add a dependency we type:

uv add PACKAGE-NAME

For example:

uv add numpy

That will add the dependency to our list as well as updating the “lock” file. Read more about this workflow and managing dependencies here.

The point is that after installing the dependency we can just import it like any other module.

To remove a package from the dependencies of our project we do:

uv remove PACKAGE-NAME

For example:

uv remove numpy