Ctrl K
ComunidadGuías…

scripts/normalize_uv_lock_registry.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Normalize package indexes in ``uv.lock`` to public PyPI.

Local ``uv`` runs resolve against whatever index is configured on the
developer's machine (e.g. the Databricks PyPI proxy via ``UV_INDEX_URL``
or ``~/.config/uv``), and ``uv`` rewrites every
``source = { registry = "<url>" }`` entry in ``uv.lock`` to that index.
For this OSS repo the committed lockfile must always point at public
PyPI (``https://pypi.org/simple``) so the lock is reproducible for
contributors who don't have the proxy — CI already pins
``UV_INDEX_URL: https://pypi.org/simple`` for the same reason.

This is a pre-commit *fixer*: it rewrites the lockfile in place and
exits non-zero when it changed anything, so the commit aborts and the
developer re-stages the normalized lockfile (mirroring
``end-of-file-fixer`` and friends). Only ``registry`` sources are
touched; ``git`` / ``path`` / ``editable`` sources are left alone.

Pass ``--check`` to validate without writing: it exits non-zero (and
names the offending URLs) when a file is *not* already canonical, but
leaves it untouched. CI runs this mode against the committed lockfile
*before* any ``uv`` command — a plain ``uv run pre-commit`` can't catch a
committed proxy URL, because ``uv`` re-syncs the working tree to CI's
own index (``pypi.org``) first and masks it.

Usage::

    python scripts/normalize_uv_lock_registry.py uv.lock           # fix
    python scripts/normalize_uv_lock_registry.py --check uv.lock   # verify
"""

from __future__ import annotations

import re
import sys
from pathlib import Path

# The canonical public index the committed lockfile must always use.
_CANONICAL_INDEX = "https://pypi.org/simple"

# The canonical file host for wheel/sdist direct URLs.
_CANONICAL_FILES_HOST = "https://files.pythonhosted.org"

# Matches a uv.lock registry source, capturing the surrounding literal so
# only the URL between the quotes is replaced, e.g.
#   source = { registry = "https://pypi-proxy.example.com/simple" }
_REGISTRY_RE = re.compile(r'(registry = ")[^"]*(")')

# Matches any non-pypi.org host in a direct wheel/sdist url = "..." entry so
# proxy-resolved URLs (e.g. pypi-proxy.cloud.databricks.com) can be rewritten
# to files.pythonhosted.org.  The path component (/packages/…) is identical
# between the proxy and the canonical host.
_DIRECT_URL_RE = re.compile(
    r'(url = ")(https?://(?!files\.pythonhosted\.org)[^"]+?/packages/)([^"]*")'
)


def non_canonical_entries(text: str) -> list[str]:
    """Return the non-canonical registry URLs and direct-URL hosts in *text*.

    :param text: Full contents of a ``uv.lock`` file.
    :returns: Each non-canonical URL, in order, with duplicates preserved.
    """
    bad: list[str] = [
        m.group(1)
        for m in re.finditer(r'registry = "([^"]*)"', text)
        if m.group(1) != _CANONICAL_INDEX
    ]
    bad += [m.group(2) for m in _DIRECT_URL_RE.finditer(text)]
    return bad


def normalize_text(text: str) -> str:
    """Return *text* with registry and distribution URLs rewritten to canonical hosts.

    :param text: Full contents of a ``uv.lock`` file.
    :returns: The normalized text.
    """
    text = _REGISTRY_RE.sub(rf"\g<1>{_CANONICAL_INDEX}\g<2>", text)
    return _DIRECT_URL_RE.sub(rf"\g<1>{_CANONICAL_FILES_HOST}/packages/\g<3>", text)


def main(argv: list[str]) -> int:
    """Normalize (or, with ``--check``, validate) each given lockfile.

    :param argv: Filenames to process, optionally preceded/followed by the
        ``--check`` flag (passed by pre-commit or CI).
    :returns: In fix mode, ``1`` when a file was modified (so the commit
        aborts and the change is re-staged) else ``0``. In ``--check``
        mode, ``1`` when any file is not already canonical (printing the
        offenders) else ``0``; no file is written.
    """
    check = "--check" in argv
    files = [a for a in argv if a != "--check"]

    if check:
        ok = True
        for name in files:
            offenders = non_canonical_entries(Path(name).read_text())
            if offenders:
                ok = False
                unique = sorted(set(offenders))
                print(
                    f"{name}: {len(offenders)} non-canonical registry source(s) "
                    f"(expected {_CANONICAL_INDEX}): {', '.join(unique)}"
                )
                print(
                    "Fix with: python scripts/normalize_uv_lock_registry.py "
                    f"{name} && git add {name}"
                )
        return 0 if ok else 1

    changed = False
    for name in files:
        path = Path(name)
        original = path.read_text()
        normalized = normalize_text(original)
        if normalized != original:
            path.write_text(normalized)
            print(f"{name}: normalized package index to {_CANONICAL_INDEX}")
            changed = True
    return 1 if changed else 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))