Una is currently a bit broken since uv v0.4.0. Figuring out what to do next.
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:
- A CLI to ensure that all imports are correctly specified as dependencies.
- 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.
You can see an example repo here:
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
git init
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
rm -rf src
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 the each pyproject.toml
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
# this will inject the cowsay-python external dependency
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
And run it:
docker build --tag unarepo-printer .
docker run --rm -it unarepo-printer python -c 'from unarepo.printer import run; run()'
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"
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. │
╰───────────────────────────────────────────────────────╯
Read more at the official documentation.
It covers additional things like:
- type-checking, testing, editor integration
- and more!
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.
Una is distributed under the terms of the MIT license. Some code is from the python-polylith project (c) 2022 David Vujic