|
1 | | -""" |
2 | | -Rewrite wheel tags from cp312-cp312-PLATFORM to py3-none-PLATFORM. |
3 | | -
|
4 | | -This is appropriate when the wheel contains OS-specific non-Python binaries |
5 | | -(e.g., Inform7) but no Python extension modules (ABI-independent). |
6 | | -""" |
| 1 | +"""Retag wheel from cpXYZ-cpXYZ-PLATFORM to py3-none-PLATFORM.""" |
7 | 2 |
|
8 | 3 | import argparse |
9 | 4 | import os |
10 | | -import re |
11 | 5 | import zipfile |
12 | 6 | from pathlib import Path |
13 | 7 |
|
14 | 8 |
|
15 | | -WHEEL_TAG_RE = re.compile(r"^Tag:\s*(\S+)\s*$") |
16 | | - |
17 | | - |
18 | | -def parse_platform_tag(filename: str) -> str: |
19 | | - # {dist}-{ver}-{python tag}-{abi tag}-{platform tag}.whl |
20 | | - m = re.match(r"^.+-[^-]+-[^-]+-(?P<plat>.+)\.whl$", filename) |
21 | | - if not m: |
22 | | - raise ValueError(f"Cannot parse wheel filename: {filename}") |
23 | | - return m.group("plat") |
24 | | - |
25 | | - |
26 | 9 | def retag(path: Path) -> Path: |
27 | | - plat = parse_platform_tag(path.name) |
28 | | - new_name = re.sub(r"-[^-]+-[^-]+-" + re.escape(plat) + r"\.whl$", f"-py3-none-{plat}.whl", path.name) |
29 | | - # If the regex didn't match (unexpected format), fall back to explicit construction: |
30 | | - if new_name == path.name: |
31 | | - # Use a more explicit split |
32 | | - parts = path.name[:-4].split("-") |
33 | | - if len(parts) < 5: |
34 | | - raise ValueError(f"Unexpected wheel name: {path.name}") |
35 | | - new_name = "-".join(parts[:-3] + ["py3", "none", parts[-1]]) + ".whl" |
36 | | - |
| 10 | + # Extract platform tag from filename: name-ver-pytag-abi-platform.whl |
| 11 | + parts = path.stem.split("-") |
| 12 | + platform = parts[-1] |
| 13 | + new_name = "-".join(parts[:-3] + ["py3", "none", platform]) + ".whl" |
37 | 14 | out = path.with_name(new_name) |
38 | 15 |
|
39 | | - with zipfile.ZipFile(path, "r") as zin, zipfile.ZipFile(out, "w", compression=zipfile.ZIP_DEFLATED) as zout: |
40 | | - wheel_files = [n for n in zin.namelist() if n.endswith(".dist-info/WHEEL")] |
41 | | - if len(wheel_files) != 1: |
42 | | - raise RuntimeError(f"Expected exactly one .dist-info/WHEEL, found: {wheel_files}") |
43 | | - wheel_meta = wheel_files[0] |
| 16 | + with zipfile.ZipFile(path, "r") as zin, zipfile.ZipFile( |
| 17 | + out, "w", compression=zipfile.ZIP_DEFLATED |
| 18 | + ) as zout: |
| 19 | + wheel_meta = next(n for n in zin.namelist() if n.endswith(".dist-info/WHEEL")) |
44 | 20 |
|
45 | 21 | for info in zin.infolist(): |
46 | 22 | data = zin.read(info.filename) |
47 | | - if info.filename == wheel_meta: |
48 | | - text = data.decode("utf-8") |
49 | | - lines = text.splitlines(True) |
50 | | - |
51 | | - kept = [] |
52 | | - for line in lines: |
53 | | - if WHEEL_TAG_RE.match(line.strip()): |
54 | | - continue |
55 | | - kept.append(line) |
56 | 23 |
|
57 | | - kept.append(f"Tag: py3-none-{plat}\n") |
58 | | - data = "".join(kept).encode("utf-8") |
| 24 | + if info.filename == wheel_meta: |
| 25 | + lines = data.decode("utf-8").splitlines(keepends=True) |
| 26 | + # Remove existing Tag: lines and add new one |
| 27 | + lines = [l for l in lines if not l.strip().startswith("Tag:")] |
| 28 | + if lines and not lines[-1].endswith("\n"): |
| 29 | + lines[-1] += "\n" |
| 30 | + lines.append(f"Tag: py3-none-{platform}\n") |
| 31 | + data = "".join(lines).encode("utf-8") |
59 | 32 |
|
60 | 33 | zout.writestr(info, data) |
61 | 34 |
|
62 | | - # Remove original so only retagged wheel remains |
63 | | - os.remove(path) |
| 35 | + try: |
| 36 | + os.remove(path) |
| 37 | + except OSError: |
| 38 | + pass |
| 39 | + |
64 | 40 | return out |
65 | 41 |
|
66 | 42 |
|
67 | 43 | def main() -> None: |
68 | | - ap = argparse.ArgumentParser() |
69 | | - ap.add_argument("wheel", nargs="+") |
70 | | - args = ap.parse_args() |
| 44 | + parser = argparse.ArgumentParser() |
| 45 | + parser.add_argument("dest_dir", type=Path, help="Directory containing wheels to retag") |
| 46 | + args = parser.parse_args() |
| 47 | + |
| 48 | + if not args.dest_dir.is_dir(): |
| 49 | + raise ValueError(f"Directory not found: {args.dest_dir}") |
| 50 | + |
| 51 | + wheels = list(args.dest_dir.glob("*.whl")) |
| 52 | + if not wheels: |
| 53 | + print(f"No wheels found in {args.dest_dir}") |
| 54 | + return |
71 | 55 |
|
72 | | - for w in args.wheel: |
73 | | - out = retag(Path(w)) |
74 | | - print(f"Retagged: {w} -> {out.name}") |
| 56 | + for wheel in wheels: |
| 57 | + out = retag(wheel) |
| 58 | + print(f"Retagged: {wheel.name} -> {out.name}") |
75 | 59 |
|
76 | 60 |
|
77 | 61 | if __name__ == "__main__": |
|
0 commit comments