#!/usr/bin/env python3 """ poetry.lock โ†’ a pip requirements file, exact and hashed. Construction ยง1.1: the lock file is the pin. A Dockerfile that installs `pydantic==2.6.1` by hand pins the three packages it names and lets pip choose every transitive one on the day of the build. This prints the `main` group of a poetry.lock โ€” every package, its exact version, its environment marker if it has one, and the hashes the lock recorded โ€” so that `pip install --require-hashes --no-deps -r` installs what was locked and nothing else. Usage: lock_to_requirements.py path/to/poetry.lock > requirements.txt """ import sys import tomllib def main(lock_path: str) -> int: with open(lock_path, "rb") as f: lock = tomllib.load(f) lines = [] for pkg in sorted(lock["package"], key=lambda p: p["name"]): if "main" not in pkg.get("groups", ["main"]): continue hashes = sorted({f["hash"] for f in pkg.get("files", [])}) if not hashes: print(f"error: {pkg['name']} has no hashes in {lock_path}", file=sys.stderr) return 1 requirement = f"{pkg['name']}=={pkg['version']}" if pkg.get("markers"): requirement += f" ; {pkg['markers']}" lines.append(" \\\n ".join([requirement] + [f"--hash={h}" for h in hashes])) print("\n".join(lines)) return 0 if __name__ == "__main__": if len(sys.argv) != 2: print(__doc__, file=sys.stderr) sys.exit(2) sys.exit(main(sys.argv[1]))