Skip to content

Consider replacing Hatch with uv for project and dependency management #199

Open

Description

@negz

What problem are you facing?

When I created this SDK in November 2023, the Python build and project management space was crowded. There was Hatch, Poetry, PDM, Flit, setuptools, and probably others I'm forgetting. There wasn't a clear winner. I picked Hatch because it was a PyPA project, it had a clean pyproject.toml-based configuration, and it covered everything I needed: environments, scripts, building, versioning, and linting (via its Ruff integration).

Today we use Hatch for:

  • Building wheels and sdists (hatch build), using hatchling as the PEP 517 build backend.
  • Environments with isolated dependencies — a generate env for protoc, a docs env for pdoc, and a hatch-static-analysis env for Ruff.
  • Scripts like hatch run generate:protoc and hatch run docs:generate.
  • Version management — reading the version from __version__.py at build time.
  • Linting via hatch fmt, which wraps Ruff.
  • Testing via hatch test, which wraps pytest.

function-template-python uses the same setup, and any new functions built from it will inherit it.

Hatch works. I don't have a concrete problem with it.

What I do have is a growing sense that the ecosystem has converged on uv in the ~18 months since I made this choice. uv didn't exist when I picked Hatch — its first release was February 2024, three months after this SDK. Since then it's grown to 82k GitHub stars (vs Hatch's 7.2k and Poetry's 34.3k) and 14,600 dependent repositories (vs Hatch's 1,800). It has a full-time team at Astral and ships releases at a pace that's hard to ignore.

We already use Astral's Ruff for linting and formatting, and I'm interested in their ty type checker too. Adopting uv would mean our Python build toolchain is entirely Astral: uv for project management, Ruff for linting, and potentially ty for type checking. That's less build plumbing for contributors to learn and install.

uv would also give us a lockfile (uv.lock) for pinning transitive dependencies, which Hatch doesn't support. We pin direct dependencies today but have no control over what versions of their transitive dependencies get installed.

How could this Function help solve your problem?

I think we should evaluate replacing Hatch with uv for project and dependency management in this SDK and in function-template-python. I'll follow up with a comment breaking down what we'd gain and what we'd lose.

Activity

  1. negz commented on Mar 25, 2026

    @negz
    MemberAuthor

    Here's a breakdown of what we'd gain and lose by switching from Hatch to uv.

    Approach

    Use uv for everything — project management, building, and publishing. We can use uv's own build backend (uv_build) instead of hatchling. The two hatchling features we rely on today are replaceable:

    • Dynamic versioning from __version__.py. Today CI runs hatch version to write a version string to __version__.py, then hatchling reads it back during hatch build. Nothing in the SDK actually imports __version__ at runtime. With uv, CI could write the version directly into pyproject.toml as a static field before running uv build.
    • Package layout. We use tool.hatch.build.targets.wheel.packages to tell hatchling about our flat layout. uv_build supports the same thing via tool.uv.build-backend.module-root.

    What we'd gain

    A lockfile. uv produces a uv.lock that pins exact versions of all transitive dependencies, cross-platform. Today we pin direct deps but have no control over transitive ones.

    Less build plumbing. The entire Python toolchain becomes Astral: uv for project management and building, Ruff for linting, and potentially ty for type checking. No hatchling, no separate build backend.

    Better dependency management UX. uv add, uv remove, and uv sync are more ergonomic than hand-editing pyproject.toml.

    Built-in publishing. uv publish supports trusted publishing, replacing the separate gh-action-pypi-publish step.

    What we'd lose

    Named scripts. This is the main regression. Hatch scripts like hatch run generate:protoc and hatch run docs:generate give us short, memorable commands defined declaratively in pyproject.toml. Hatch effectively acts as a task runner without needing a separate tool.

    uv has no task runner. There's a heavily requested open issue (630+ upvotes) but no accepted design or timeline. Astral has said they're working on scheduling this, and are watching two Python packaging standardization proposals that could lead to a PEP-standard approach.

    Without a task runner, the commands get longer:

    Today (Hatch) After (uv)
    hatch fmt uv run ruff check && uv run ruff format
    hatch test uv run pytest
    hatch build uv build
    hatch run docs:generate uv run --group docs pdoc -m google crossplane/function -o docs
    hatch run generate:protoc uv run scripts/generate.py

    How other projects handle the task runner gap

    Most major Python projects that use uv (Pydantic, Polars, LangChain) work around this with a Makefile — targets that call uv run <tool>.

    There are also third-party task runners that integrate with pyproject.toml:

    • Poe the Poet — configure tasks in [tool.poe.tasks], run via uv run poe <task>.
    • taskipy — configure tasks in [tool.taskipy.tasks], run via uv run task <name>.

    These keep task definitions in pyproject.toml (like Hatch scripts) without a separate file, but they add a dev dependency.

  2. bobh66 commented on Mar 25, 2026

    @bobh66
    Collaborator

    My preference would be the Makefile, but that's because I have make foo in muscle memory.

  3. eric-carlsson commented on Jun 8, 2026

    @eric-carlsson
    Contributor

    Now that we have support for embedded Python functions in crossplane/cli, which also follows this setup, I think this is more relevant than ever.

    I'd add another item to the list of arguments for uv: environment management. uv makes it incredibly easy to recreate an environment with exact dependencies. This improves devex, enables better caching in build runners, and more.

    How do we move forward with this?

  4. fernandezcuesta commented on Sep 11, 2026

    @fernandezcuesta
    Contributor

    I migrated some time ago to uv in function-naming-convention only for the build, considerably faster than hatch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions