1. Motivation

I wanted to put up a small snippet of code I did at work as a public github project, as it seemed like a common usage pattern that other folks can use of the shelf or copy from.

Within Amazon, we have our own python project management and dependency management tooling that automates away a lot of the boilerplate setup in defining and distributing a Python project.

They abstract away the setup and usage for tools and configurations like pyproject.toml, venv, mypy, black, pytest, etc. They essentially run these tools under the hood, but we as developers don’t need to care about setting these up every time we start a new project.

This also helps in standardizing linting rules, static type checker strictness, flake8 checks, etc, across a team, or organization.

I wanted to test out some external alternatives, that perform a similar functions. My goal, write less boilerplate configuration, and only care about the code. I also need a easy way to manage my dependencies and, run my linting and, testing tools.


2. The Tools

I was suprised, there are a lot of tools to choose from. Here’s a list of some tools I came across and checked out.

  • Peotry
  • PDM
  • Hatch
  • Rye
  • uv
  • just the usual pip

I ended up choosing uv, for the following reasons.

  • as the documentation seemed well structured enough that it was easy to follow.
  • It has a straight forward pip interface, if I am not really inclined to learn any uv specifics.
  • It covered a lot of features like
    • managing python versions
    • a script executor
    • project manager
      • init, to automate some boilerplate setup of a project.
      • add and remove dependencies.
      • build projects for distributions
      • and run projects in an isolated fashion
  • It let’s you install and run tools like black and ruff(the makers of uv also made ruff).
  • It has some utility functions to manage caches.
  • uv advertises to be 10 to 100x faster than pip, and in my experience just downloading and adding dependencies really has been a lot faster than pip.

My analysis was not purely a technical one, but was driven more on what I thought was easy to use. Another partial driver was the media hype when the tool was portrayed as an pip replacement. I wanted to check out what the hype is.


3. Installing uv

The uv project has got a standalone installer linked below, it’s as simple of running it to install uv.

It’s, always recommended to download and review scripts downloaded from the internet before running them. Maybe skip the pipe and add a -o to your curl and glance quickly if everything is dandy.


4. Setting up a project

I am working on building a CLI tool, and I want to setup my project as an application that can be packaged, and potentially just pipx installed (or maybe uv installed).

To init such a project run:

uv init --app --package example

You will see a directory of your project setup with this file structure, after running this command.

❯ tree example
example
├── pyproject.toml
├── README.md
└── src
    └── example
        └── __init__.py

2 directories, 3 files

4. Setting up a virtual environment

To start a venv for your project you can run this command:

uv venv

Once run you’ll see an output like so:

❯ uv venv
Using CPython 3.10.12 interpreter at: /usr/bin/python3
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate

5. Adding dependencies to your project

Now let’s add dependencies to the project.

For my project I have two deps boto3 and requests

You can add dependencies with this command:

uv add boto3 requests

When you run this command uv will pull these dependencies into your project and update the pyproject.toml .

uv aggressively caches packages across projects. If the dependencies have already been cached from another project, it will pull that into the current project and not re-download the dependencies.

❯ uv add boto3 requests
Resolved 12 packages in 364ms
   Built example @ file:///home/vasuper/personal_dev/example
Prepared 5 packages in 371ms
Installed 12 packages in 111ms
 + boto3==1.35.41
 + botocore==1.35.41
 + certifi==2024.8.30
 + charset-normalizer==3.4.0
 + example==0.1.0 (from file:///home/vasuper/personal_dev/example)
 + idna==3.10
 + jmespath==1.0.1
 + python-dateutil==2.9.0.post0
 + requests==2.32.3
 + s3transfer==0.10.3
 + six==1.16.0
 + urllib3==2.2.3
 +
❯ cat pyproject.toml
[project]
name = "example"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
authors = [
    { name = "Vasudevan Perumal", email = "vasu3797@gmail.com" }
]
requires-python = ">=3.10"
dependencies = [
    "boto3>=1.35.41",
    "requests>=2.32.3",
]

[project.scripts]
example = "example:main"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

6. Running your app or any command

uv has an run command one can use to run a project or just any adhoc script. UV manages the dependencies for you without manually managing environments.

uv run example
❯ uv run example
Hello from example!

You can also run any command with uv run within your project context. For example you can run the pytest against your project with:

uv run pytest

7. Freezing an environment

uv creates a uv.lock file that acts as an cross platform lock file, which contains the exact resolved versions that are installed in the project environment. This can be committed source control to provide consistent and reproducible installation on different machines.

You can also lock the dependencies declared in pyproject.toml into a requirements.txt with this command:

uv pip compile pyproject.toml -o requirements.txt

You can then use this requirements in your environment

uv pip sync requirements.txt

8. Build the project for distribution

You can build your project using uv by running

uv build

This will build the project into a distributable wheel or tarball.

uv build
Building source distribution...
Building wheel from source distribution...
Successfully built dist/example-0.1.0.tar.gz and dist/example-0.1.0-py3-none-any.whl

9. Publishing the project to a PyPI registry

uv also supports publishing the built project to a pypi registry.

uv publish

You can setup a PyPI token with --token or UV_PUBLISH_TOKEN, or set a username with --username or UV_PUBLISH_USERNAME and password with --password or UV_PUBLISH_PASSWORD.


10. The End

uv is a really fast python management tool, and it’s user experience is pretty good. I would recommend it for folks looking for a tool to fit a similar need. In the next post I’ll share what I built and managed with uv.


References