#!/usr/bin/env python3 """Fail unless requirements.lock is the exact frozen export of uv.lock.""" from __future__ import annotations import shutil import subprocess from pathlib import Path REPO = Path(__file__).resolve().parents[1] UV_LOCK = REPO / "uv.lock" REQUIREMENTS_LOCK = REPO / "requirements.lock" _EXPORT_ARGS = ( "export", "--frozen", "--no-emit-workspace", "--no-dev", "--format", "requirements-txt", "--no-emit-package", "kotakneoapi", "--no-emit-package", "upstox-python-sdk", ) class ExportError(RuntimeError): """Raised when a fresh frozen uv export cannot be produced.""" def _export_requirements() -> str: uv = shutil.which("uv") if uv is None: raise ExportError("uv is unavailable; cannot verify requirements.lock") try: completed = subprocess.run( # noqa: S603 - resolved trusted tool with fixed arguments [uv, *_EXPORT_ARGS], cwd=REPO, check=False, capture_output=True, text=True, timeout=120, ) except (OSError, subprocess.TimeoutExpired) as exc: raise ExportError(f"uv export failed: {type(exc).__name__}") from exc if completed.returncode != 0: detail = completed.stderr.strip().splitlines() suffix = f": {detail[-1]}" if detail else "" raise ExportError(f"uv export failed with exit {completed.returncode}{suffix}") if not completed.stdout: raise ExportError("uv export returned no requirements") return completed.stdout def _without_generated_header(value: str) -> str: lines = value.splitlines(keepends=True) if ( len(lines) >= 2 and lines[0].startswith("# This file was autogenerated by uv") and lines[1].startswith("# uv export") ): return "".join(lines[2:]) return value def main() -> int: missing = [str(path) for path in (UV_LOCK, REQUIREMENTS_LOCK) if not path.is_file()] if missing: print("missing lockfile(s): " + ", ".join(missing)) return 1 try: actual = REQUIREMENTS_LOCK.read_text(encoding="utf-8") exported = _export_requirements() except (OSError, UnicodeError, ExportError) as exc: print(str(exc)) return 1 if _without_generated_header(actual) != _without_generated_header(exported): command = "uv " + " ".join(_EXPORT_ARGS) + " --output-file requirements.lock" print("requirements.lock does not match the frozen uv export") print(f"regenerate with: {command}") return 1 return 0 if __name__ == "__main__": raise SystemExit(main())