#1675·prek

Proposal: uv-synced Python hook environments (lockfile + groups)

Author: CarliJoyCreated Feb 19, 2026Updated Sep 4, 2026

Related issues

Several issues appear to share the same missing primitive: creating/updating Python hook environments via a uv sync-style operation, instead of installing ad-hoc dependencies.

  • #1311 — need for reproducible, lockfile-based hook environments (language-specific lockfiles / pinned resolution)
  • #1462 — manage hook tool dependencies (ruff/mypy/ty, etc.) via pyproject.toml dependency groups and keep them in a single lockfile
  • #1485 — type checking (ty / mypy) typically requires all project dependencies to be installed (similar to #780)
  • #998 — local hooks using uv run work but feel like a workaround that could be first-class
  • maybe #1054 could be soultion of using repo: local and run.
  • https://github.com/astral-sh/uv-pre-commit/issues/37 also wanting to use uv run for local repos

Problem

Current Python hook behaviour forces trade-offs:

  • Type checkers often require the full dependency closure of the project so imports and types resolve. This makes “use active venv” attractive but less reproducible (#1485).
  • Users want lockfile-backed reproducibility for hook environments, ideally using the same lockfile as the project (#1311).
  • Users want a single source of truth where hook tool versions live in pyproject.toml and are locked in uv.lock (#1462).
  • Local repo workflows already rely on uv run, but this is not an explicit first-class model (#998).

Core idea

Add a uv-backed mode that:

  1. creates a dedicated hook venv (as Python hooks already do),
  2. populates it via lockfile-driven sync (uv.lock) using defined dependency groups in frozen mode.

Or put differently: make using prek with uv-based projects feel like using uv.


Option A — new language variant python-uv

Introduce a new language (name bikesheddable) meaning:

“Create and populate the hook environment using uv sync semantics.”

Example:

yaml
- repo: local
  hooks:
    - id: mypy
      name: mypy (uv-synced)
      language: python-uv
      entry: mypy
      dependency_groups: [typecheck]

Characteristics:

  • python-uv implicitly uses uv.lock if present (frozen mode).
  • No extra flags are required — behaviour is defined by the language.
  • Enables defining a dedicated dependency group (e.g. typecheck) that installs everything required for type checking (#1485).
  • Allows tool versions to live in pyproject.toml and be locked in uv.lock (#1462).
  • Makes local uv workflows first-class (#998).
  • Since language can be overridden via prek.toml / .pre-commit-config.yaml, hook-providing repos can publish normal Python hooks while consuming repos enforce lockfile usage by overriding the language.

Option B — extend language: python with lockfile + groups/extras

Instead of a new language, extend language: python with opt-in configuration.

Example:

yaml
- repo: local
  hooks:
    - id: mypy
      language: python
      entry: mypy
      lockfile: uv.lock
      dependency_groups: [typecheck]

Semantics:

  • Setting lockfile switches environment population to uv sync-style behaviour.
  • dependency_groups / extras select what is installed.
  • Default behaviour remains unchanged if lockfile is not specified.

Lockfile handling

Initial scope:

  • Only support uv.lock.

Future extension:

  • Support additional lock formats (e.g. py.lock) without changing the high-level configuration model.

This directly addresses reproducibility concerns from #1311 while keeping the initial scope small.


Why this solves the linked issues

  • #1311 (lockfiles) — hook environments become reproducible and pinned to uv.lock.
  • #1462 (dependency groups) — hook tool dependencies can be defined in pyproject.toml and locked once.
  • #1485 (type checking) — instead of using the active venv, hooks can create a fast, dedicated uv-synced environment containing all required dependencies.
  • #998 (local uv workflows) — uv-managed hook environments become first-class rather than relying on uv run patterns.

I tend to option A.