Skip to content

Python: UV and Pants Monorepo Setup

uv + Pants Python Monorepo Guide (lib_math, math scripts, tests)

Section titled “uv + Pants Python Monorepo Guide (lib_math, math scripts, tests)”
.
├── pants.toml
├── pyproject.toml
├── BUILD
├── 3rdparty/
│ └── BUILD
├── lib_math/
│ ├── BUILD
│ └── utils/
│ ├── __init__.py
│ └── core.py
├── math/
│ ├── BUILD
│ └── scripts/
│ ├── __init__.py
│ └── main.py
└── tests/
└── math/
├── BUILD
└── test_main.py

Note: naming a top-level package math can shadow the Python stdlib math. If that’s a problem for you, rename the folder/package (e.g. math_pkg). I’ll follow your requested layout as-is.


Terminal window
mkdir myrepo && cd myrepo
uv init --bare
uv add --dev pytest

This creates pyproject.toml and uv.lock.


2) Configure deps so Pants can read them (uv_requirements)

Section titled “2) Configure deps so Pants can read them (uv_requirements)”

Pants has a uv_requirements target that generates python_requirement targets from entries under [tool.uv] in pyproject.toml.

Edit pyproject.toml to include at least your dev deps there:

[project]
name = "myrepo"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = []
[tool.uv]
dev-dependencies = [
"pytest>=8",
]

Create pants.toml:

[GLOBAL]
pants_version = "2.30.0"
backend_packages = [
"pants.backend.python",
]
[python]
interpreter_constraints = ["CPython>=3.11,<3.13"]
enable_resolves = true
default_resolve = "python-default"
resolves = { python-default = "3rdparty/python-default.lock" }
[pytest]
install_from_resolve = "python-default"

# (optional) keep empty or add repo-wide targets later

This reads [tool.uv] from your root pyproject.toml and generates requirement targets.

uv_requirements(
name="reqs",
source="../pyproject.toml",
resolve="python-default",
)
python_sources(name="lib_math")
python_sources(name="math")
pex_binary(
name="run",
entry_point="math.scripts.main:main",
resolve="python-default",
)
python_tests(
name="tests",
resolve="python-default",
)

from .core import add
__all__ = ["add"]
def add(a: float, b: float) -> float:
return a + b
from lib_math.utils import add
def main() -> None:
print(add(2, 3))
if __name__ == "__main__":
main()
from lib_math.utils import add
def test_add() -> None:
assert add(2, 3) == 5

Generate lockfiles for your resolve:

Terminal window
pants generate-lockfiles --resolve=python-default

Run your script (PEX):

Terminal window
pants run math:run

Run tests:

Terminal window
pants test tests/math::