This repository contains an exemplary python project, structured using the uv package manager and declared with the modern pyproject.toml format. Its
purpose is to showcase the usefullness of uv for creating reproducible python development environments for research and beyond.
Note: Most of the information provided in this repository is taken directly from the excellent uv documentation, which should be the main source for any questions regarding the content of this project.
In this repository we present three alternative approaches for creating reproducible
python project with uv. Complexity as well as code reusability increase in acending order. If you are building a large project, with a lot of reused code,
the third alternative is certainly preferred in terms of maintainability.
example_script: A standalone uv python script, which declares all its dependencies in a header comment.example_application: An exemplary python application, where all dependecies are specified in apyproject.tomlfile and the code lies in amain.pyfile.example_library: A python library, consisting of a package and script, which import the package.
To be able to follow the steps below, make sure to install uv and clone the present repository.
This script created simple plot of the function:
[ y = x^2 ]
Its dependencies are matplolib and numpy, which are specified in the header comment of the main.py file. To run the script, enter the script directory and call
uv run main.pyBy doing so, uv will automatically provide all required dependencies for you.
You should see a plot of the above function as a result.
You may lock your script's dependencies for reproducibility:
uv lock --script main.pyNext, we present a simple application consisting of a main.py file inside the application/scripts directory and a pyproject.toml. The script implements and
plots a simple
[ y = sin(x) ]
function. However, in contrast to the first example, the dependencies are not
specified directly in the main.py, but in the pyproject.toml file. The main advantage of this approach is that we could easily add additional scripts to the application/scripts directory, without having to repeat the definition of dependencies.
To run the script, we must first create a virtual environment with all dependencies
and then run main.py within that environment. First, enter the application directory and run
uv syncThis created the application/.venv directory. Next run
uv run scripts/main.pyto run the script. You should see a plot of the above function as a result.
You may lock the dependencies by running
uv lockFinally, we move to the library structure, where all reusable code is located in a python package, and the scripts only contain the code that is different for every analysis.
As before, the dependencies are specified in the pyproject.toml file and therefore
shared across all scripts as well as with the package. Furthermore, the main.py script is located in the library/scripts directory. However, this time the main.py file imports a module of our own user-defined package, myproject, whose source files are located in the library/src/myproject directory. The name and the build system of the package are specified in the pyproject.toml file.
The main advantage of this structure over the application example stems from the fact that any code within the myproject package may now be shared across multiple scripts. This improves code reuse and simplifies maintenance for larger projects.
To run the main.py script, we simply follow the same steps as in the previous example: First enter the library directory. Run
uv syncto create the environment and execute the script with
uv run scripts/main.pyYou should see the plot of a [y = x^3] function. You may lock the dependency versions by running
uv lockThe project uses a src/ layout, which is the default for uv projects.
All reusable logic is inside src/myproject/ and can be imported like.
from myproject import cubic_utilPython version is pinned using the .python-version file. This ensures consistent Python versions across collaborators.
Dependencies are declared in pyproject.toml and locked in uv.lock. You can add a dependency like:
uv add numpyAll code can be run via uv run, which handles virtual environments automatically. Hence, there is no need for manually activating or deactivating environments:
uv run main.pyYou can lock your script's dependencies for reproducibility:
uv lock --script main.pyFor many more uv tipps and tricks we refer to the official documentation.