# UV Development Guide [UV](https://docs.astral.sh/uv/) is a fast, modern Python package and project manager that replaces tools like `pip`, `poetry`, and `virtualenv` with a single unified tool. In {term}`pntOS-Python`, UV manages the workspace containing `pntos-api` and `pntos-cobra` packages, handles all dependencies, and builds distributable wheels. UV offers significant advantages over traditional pip-based workflows: - **Faster installations** through parallel downloads and optimized caching - **Workspace support** for managing multiple related packages - **Reproducible builds** via the `uv.lock` lock file - **Simplified tooling** with one command for most operations This guide covers how to use UV for development in this repository, and is intended to build on the concepts discussed in [](./pyproject.md). ## Common UV Workflows For quick-reference, here are some of the most common `uv` commands and workflows when working within the {term}`pntOS-Python` context. The concepts in this section will be expanded upon in subsequent sections. ### Quick Reference | Task | Command | Notes | | -------------------------------- | --------------------------------- | --------------------------------------- | | **Install all dependencies** | `uv sync` | Installs workspace packages as editable | | **Install with specific Python** | `uv sync --python 3.12` | Useful for ROS or version testing | | **Frozen install (CI)** | `uv sync --frozen` | Fails if lock file out of sync | | **Add dependency** | `uv add numpy` | Updates `pyproject.toml` and `uv.lock` | | **Add dev dependency** | `uv add --dev pytest` | Adds to `[dependency-groups]` | | **Remove dependency** | `uv remove numpy` | Also uninstalls if no longer needed | | **Update all dependencies** | `uv lock` | Respects version constraints | | **Update specific package** | `uv lock --upgrade-package numpy` | Only updates one package | | **Build wheel** | `uv build pntos-api` | Creates wheel in `dist/` directory | | **Run command** | `uv run pytest` | Execute without activating venv | | **Clean cache** | `uv cache clean` | Clear cached packages | | **Reinstall packages** | `uv sync --reinstall` | Force reinstall all packages | For pip compatibility workflows, see the [Contributing Guide](contributing.md). ### Detailed Workflows `````{tab-set} ````{tab-item} Installing Dependencies **Basic installation:** ```shell uv sync ``` Reads all workspace `pyproject.toml` files, resolves dependencies, updates `uv.lock`, and installs everything into a Python virtual environment (`.venv/`). For more information, see the [Installation Guide](installation.md). **For specific Python version:** ```shell uv sync --python 3.12 # For ROS Jazzy compatibility ``` **In CI/CD (frozen):** ```shell uv sync --frozen ``` Uses exact versions from `uv.lock` without updating it. Fails if lock file is stale. ```` ````{tab-item} Managing Dependencies **Add dependencies:** ```shell uv add numpy "matplotlib>=3.5" # Core dependency uv add --dev pytest "ruff>=0.1" # Dev dependency ``` Automatically updates both `pyproject.toml` and `uv.lock`. **Remove dependencies:** ```shell uv remove numpy uv remove --dev pytest ``` Uninstalls the package if no longer needed by other dependencies. **Note:** Workspace member dependencies (`pntos-api`, `pntos-cobra`) are automatically installed as editable packages. ```` ````{tab-item} Updating & Building **Update dependencies:** ```shell uv lock # Update all to latest compatible uv lock --upgrade-package numpy # Update specific package ``` Always commit `uv.lock` after updating. **Generate requirements files:** ```shell util/generate_requirements.sh # Or manually: uv export --frozen --no-hashes -o requirements.txt uv export --frozen --no-dev --no-hashes -o requirements-minimal.txt ``` **Build wheels:** ```shell uv build pntos-api pntos-cobra ``` Creates wheels in `dist/` directory. Requires proper `[build-system]` configuration (see [Build System Configuration](#build-system-configuration)). ```` ````{tab-item} Running Commands **Without activating environment:** ```shell uv run pytest uv run python my_script.py ``` Best for one-off commands and CI/CD. **With activated environment:** To activate a Python virtual environment: ```{include} snippets/activate_venv.md ```
Once activated, simply run commands inside the virtual environment: ```shell pytest python my_script.py ``` Best for interactive development with many commands. ```` ````` Now, let's dive into greater detail on some important UV concepts. ## UV Workspaces UV workspaces manage multiple related packages in a single repository with a unified lock file. {term}`pntOS-Python` uses this structure: - **Root** (`pntos-python`): Meta-package coordinating the workspace - **Members**: `pntos-api` and `pntos-cobra` packages This allows downstream projects to depend on either package individually while letting developers work with both simultaneously. ### Configuration Example workspace configuration from the root `pyproject.toml`: ```toml [project] dependencies = [ "pntos-api", # The [tool.uv.*] fields below tell uv what to do with these "pntos-cobra", # Other deps ] [tool.uv] package = false # Meta-package, not distributed [tool.uv.workspace] members = ["pntos-api", "pntos-cobra"] [tool.uv.sources] pntos-api = { workspace = true } # Use local version pntos-cobra = { workspace = true } ``` There are several benefits to this workspace approach: | Benefit | Description | | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | **Unified lock file** | One `uv.lock` ensures consistent versions across all packages | | **Instant updates** | Changes to workspace members immediately available ([editable installs](https://setuptools.pypa.io/en/latest/userguide/development_mode.html)) | | **Shared dependencies** | Common packages installed once | | **Simple commands** | Single `uv sync` for everything in both top-level meta-project (`pntos-python`) and workspaces (`pntos-api`, `pntos-cobra`) | ## Build System Configuration To create distributable wheels, configure the build system in each package's `pyproject.toml`. Both `pntos-api` and `pntos-cobra` use [Hatchling](https://hatch.pypa.io/latest/). Given this structure: ``` pntos-python/ ├── pyproject.toml ├── pntos-api/ │ ├── pyproject.toml │ └── src/ │ └── pntos/ │ ├── __init__.py │ └── api/ │ └── __init__.py └── pntos-cobra/ ├── pyproject.toml └── src/ └── pntos/ ├── __init__.py └── cobra/ └── __init__.py ``` Here is the build configuration within both workspace `pyproject.toml` files: ```toml [build-system] build-backend = "hatchling.build" requires = ["hatchling"] [tool.hatch.metadata] allow-direct-references = true # Required for Git dependencies [tool.hatch.build.targets.wheel] packages = ["src/pntos"] # Package location for src/ layout ``` Both packages specify `src/pntos` as the build directory, placing them in the `pntos` namespace. If only one workspace is installed downstream, only that module (`pntos.api` or `pntos.cobra`) is available. If both are installed, both sub-namespaces exist under `pntos`. ## Custom Package Indexes UV can use custom package indexes in addition to PyPI for private or organization-specific packages such as a self-hosted package index on a github instance. ## Lock File Management The `uv.lock` file ensures reproducible, deterministic builds by recording exact versions, sources, and hashes for all dependencies. ### Why Lock Files Matter | Without `uv.lock` | With `uv.lock` | | -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | Different developers get different versions
`dependencies = ["numpy>=1.24"]` → might get 1.24.0, 1.26.0, or 2.0.0 | Everyone gets the exact same version
Everyone gets exactly `numpy==1.24.3` | | "Works on my machine" problems
Hard-to-reproduce CI/CD issues | Consistent environments
Reproducible bugs | ### Lock File Updates **Automatically updated:** - `uv sync` (when `pyproject.toml` changed) - `uv add` or `uv remove` - `uv lock` (explicit regeneration) **Not updated:** - `uv sync --frozen` (uses existing lock) - Editing Python code ### Best Practices **Always commit `uv.lock` to version control** to ensure: - Team members use identical dependencies - CI/CD builds are reproducible - Production matches development **Resolving Git conflicts in uv.lock:** ```{danger} Never manually edit `uv.lock` to resolve conflicts. Always regenerate it. ``` ```shell # 1. Accept one version (yours or theirs) git checkout --ours uv.lock # or --theirs # 2. Regenerate and verify uv lock uv sync util/generate_requirements.sh # 3. Commit git add uv.lock requirements*.txt git commit ``` **In CI/CD:** - Use `uv sync --frozen` for reproducible builds - Verify lock file is up-to-date (catches forgotten commits) - Fail builds if out of sync ## Troubleshooting | Problem | Symptoms | Solutions | | ------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Dependency conflicts** | `error: No solution found when resolving dependencies` | • Use less restrictive version constraints (`>=` instead of `==`)
• Run `uv lock --upgrade` to try newer versions
• Check for conflicting package sources | | **Python version issues** | Can't find Python version, compatibility errors | • Specify version: `uv sync --python 3.12`
• Check installation: `which python3.10 python3.12`
• Loosen `requires-python` constraint in `pyproject.toml` | | | **Cache issues** | Stale packages, corruption suspected | • `uv cache clean`
• Delete and recreate: `rm -rf .venv && uv sync`
• `uv sync --reinstall` | | **Stale lock file** | CI fails with "lock file out of sync" | • Regenerate: `uv lock`
• Sync and commit: `uv sync && git add uv.lock requirements*.txt` | | **Build failures** | `uv build` errors, missing dependencies | • Verify `[build-system]` in `pyproject.toml`
• Check `[tool.hatch.build.targets.wheel]` points to correct package
• Enable `allow-direct-references = true` for Git deps
• Run `uv sync` first | | **Workspace issues** | Members not recognized, changes not reflected | • Verify workspace config in root `pyproject.toml`
• Check member directories have valid `pyproject.toml`
• Reinstall: `uv sync --reinstall-package pntos-api`
• Ensure package names match across configs | | **Performance issues** | Commands unusually slow | • Check cache is enabled: `uv cache dir`
• Verify network connectivity
• Use `--offline` for repeated installs | **Additional Resources:** - [UV Documentation](https://docs.astral.sh/uv/) - [UV GitHub Issues](https://github.com/astral-sh/uv/issues) - [pntOS-Python Installation Guide](installation.md) - [Contribution Guide](./contributing.md)