|
| 1 | +import tomli # or import tomllib for Python 3.11+ |
| 2 | +import hashlib |
| 3 | +import sys |
| 4 | +from pathlib import Path |
| 5 | +from collections import defaultdict |
| 6 | + |
| 7 | + |
| 8 | +def parse_pylock_toml(path): |
| 9 | + with open(path, "rb") as f: |
| 10 | + data = tomli.load(f) |
| 11 | + |
| 12 | + # This dictionary maps package names to (version, [hashes]) |
| 13 | + package_hashes = defaultdict(lambda: {"version": "", "hashes": []}) |
| 14 | + |
| 15 | + for entry in data.get("packages", []): |
| 16 | + name = entry["name"] |
| 17 | + version = entry["version"] |
| 18 | + all_hashes = [] |
| 19 | + |
| 20 | + # Handle wheels |
| 21 | + for wheel in entry.get("wheels", []): |
| 22 | + sha256 = wheel.get("hashes", {}).get("sha256") |
| 23 | + if sha256: |
| 24 | + all_hashes.append(sha256) |
| 25 | + |
| 26 | + # Handle sdist (if present) |
| 27 | + sdist = entry.get("sdist") |
| 28 | + if sdist and "hashes" in sdist: |
| 29 | + sha256 = sdist["hashes"].get("sha256") |
| 30 | + if sha256: |
| 31 | + all_hashes.append(sha256) |
| 32 | + |
| 33 | + package_hashes[name]["version"] = version |
| 34 | + package_hashes[name]["hashes"].extend(all_hashes) |
| 35 | + |
| 36 | + return package_hashes |
| 37 | + |
| 38 | + |
| 39 | +def write_requirements_txt(package_hashes, output_path="requirements.txt"): |
| 40 | + with open(output_path, "w") as f: |
| 41 | + for name, data in sorted(package_hashes.items()): |
| 42 | + version = data["version"] |
| 43 | + hashes = data["hashes"] |
| 44 | + |
| 45 | + if hashes: |
| 46 | + f.write(f"{name}=={version} \\\n") |
| 47 | + for i, h in enumerate(hashes): |
| 48 | + end = " \\\n" if i < len(hashes) - 1 else "\n" |
| 49 | + f.write(f" --hash=sha256:{h}{end}") |
| 50 | + else: |
| 51 | + f.write(f"{name}=={version}\n") |
| 52 | + |
| 53 | + print(f"✅ requirements.txt written to {output_path}") |
| 54 | + |
| 55 | +if __name__ == "__main__": |
| 56 | + if len(sys.argv) != 2: |
| 57 | + print("Usage: python pylock_to_requirements.py pylock.toml") |
| 58 | + sys.exit(1) |
| 59 | + |
| 60 | + path = Path(sys.argv[1]) |
| 61 | + if not path.exists(): |
| 62 | + print(f"❌ File not found: {path}") |
| 63 | + sys.exit(1) |
| 64 | + |
| 65 | + pkgs = parse_pylock_toml(path) |
| 66 | + dest = path.parent / (path.stem.replace('pylock','requirement_with_hash')+ '.txt') |
| 67 | + write_requirements_txt(pkgs, dest) |
0 commit comments