Skip to main content

Una

TLDR:

  • uv is the best way to use Python, and it has some monorepo/workspace support.
  • If your build artifacts are Dockerfiles, uv is probably enough. See an example at carderne/postmodern-mono.
  • But if you want to build Python wheels (or similar) in monorepos, read on!
Una logo

Easy monorepos with Python and uv


Una is a tool to build and productionise Python monorepos with uv.

uv has Workspaces, but no ability to build them. This means if you have dependencies between packages in your workspace, there's no good way to distribute or productionise the end result.

Una solves this. No additional configuration is needed: if you have a functional uv Workspace, just add Una. It consists of the following two things:

  1. A CLI to ensure that all imports are correctly specified as dependencies (you don't have to use this).
  2. A build plugin that enables production builds of individual apps within a monorepo by injecting local dependencies and transitive third-party dependencies.

Una doesn't try to replicate a full build system such as Bazel or Pants. It just makes it possible to have a simple monorepo with interdependencies.

Una works much like a Rust workspace, with each package having its own pyproject.toml. In general, packages should either be libraries (imported but not run) or apps (run but never imported), but Una will not enforce this.

It only works with the Hatch build backend.

Examples

You can see an example repo here:

Quickstart

This will give you a quick view of how this all works.

First install uv:

curl -LsSf https://astral.sh/uv/install.sh | sh

And start your workspace:

uv init unarepo   # choose another name if you prefer
cd unarepo
uv add --dev una

Then setup the Una workspace. This will generate a structure and an example lib and app.

uv run una create workspace
uv sync

Have a look at what's been generated:

tree

Have a look at the generated __init__.py files in the apps/printer and libs/greeter packages. An external dependency (cowsay-python) has also been added to the latter's pyproject.toml.

The magic of Una then comes in to resolve the graph of direct and transitive dependencies, which looks like this:

printer --> greeter --> cowsay-python

You can do this by running the following. This checks all imports and ensures they are added to project.dependencies and tool.uv.sources in each pyproject.

uv run una sync

Have a look at what happened:

tail apps/printer/pyproject.toml

It added greeter as an internal dependency to printer. It didn't add cowsay-python, as transitive external dependencies are only resolved at build-time.

Now you can build your app. Note that you must specify the --wheel parameter. Una doesn't currently work for builds that do source -> sdist -> wheel, as these break some things with uv virtual envs.

uvx --from build pyproject-build --installer=uv \
    --outdir=dist --wheel apps/printer

And see the result:

ls dist/

And you can do whatever you want with that wheel! What about stick it in a Dockerfile, have you ever seen such a simple one?

FROM python
COPY dist dist
RUN pip install dist/*.whl

Build it:

docker build --tag unarepo-printer .

And run it:

docker run --rm -it unarepo-printer \
  python -c 'from unarepo.printer import run; run()'
┌────────────────┐
│Hello from una! │
└────────────────┘
               \
                \
                  ^__^
                  (oo)\_______
                  (__)\       )\/\
                      ||----w |
                      ||     ||

You can also get a handy table of your internal inter-dependencies:

┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┓
┃ Package \ Imports ┃ printer ┃ greeter ┃
┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━┩
│           printer │ ✓       │ ✓       │
│           greeter │         │ ✓       │
└───────────────────┴─────────┴─────────┘

Installation

The CLI tool isn't strictly necessary, as all the stuff that lets the monorepo builds work is in the separate (and tiny) hatch-una package. But you will likely struggle to manage your monorepo without the tool!

So you may as well install it:

uv add --dev una

As for the build-time hatch-una, it will automatically be installed by build tools when it spots this in your pyproject.toml (this will be configured automatically by the CLI):

[build-system]
requires = ["hatchling", "hatch-una"]
build-backend = "hatchling.build"

Usage

The CLI has a few commands and options, have a look:

uv run una --help

 Usage: una [OPTIONS] COMMAND [ARGS]...

╭─ Options ─────────────────────────────────────────────╮
│ --help          Show this message and exit.           │
╰───────────────────────────────────────────────────────╯
╭─ Commands ────────────────────────────────────────────╮
│ create   Commands for creating workspace and packages.│
│ sync     Update packages with missing dependencies.   │
╰───────────────────────────────────────────────────────╯

Documentation

Read more at the official documentation.

It covers additional things like:

Contributing

See the instructions at the official documentation.

Very briefly, local development is with uv:

uv sync
make all  # will fmt, lint, typecheck and test

Then open a PR.

License

Una is distributed under the terms of the MIT license. Some code is from the python-polylith project (c) 2022 David Vujic

Metadata

Release files for una 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for una 0.7.0
File Size Uploaded Provenance
una-0.7.0.tar.gz 13.4 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for una 0.7.0
File Interpreter ABI Platform
una-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 30.6 kB

Release files / una-0.7.0.tar.gz

Download URL una-0.7.0.tar.gz
Size 13.4 kB
Tags Source
SHA-256 checksum
How to use checksums
796023ab67b1d4c873478bfdf3f631ad43f73cb5f341df0a47bdbd42a34ab283
BLAKE2b-256 checksum
How to use checksums
716715be22839dba21a561fadc88250d9b8f6928b97e0b1c8acc498f9818dacb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Dec 2, 2025.

Transparency log

Release files / una-0.7.0-py3-none-any.whl

Download URL una-0.7.0-py3-none-any.whl
Size 17.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9c3208146efff14d61a03e42bb82f9a6c08168c1ebba32a1c26b5b4c60a4c870
BLAKE2b-256 checksum
How to use checksums
dcf4ec5bdc2da3124cf0330daec9f1b36ca9d71cbd51429d3dfd8b04f554efaa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Dec 2, 2025.

Transparency log

Security for una 0.7.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for una-0.7.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Dec 2, 2025.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page