|
17 | 17 | import zipfile |
18 | 18 |
|
19 | 19 | from pathlib import Path |
| 20 | +from typing import Set |
| 21 | +from typing import Tuple |
20 | 22 | from typing import Union |
21 | 23 |
|
22 | 24 | from colorama import Fore |
23 | 25 |
|
24 | 26 | from _helper_functions import print_color |
25 | 27 |
|
26 | 28 |
|
| 29 | +def _dedupe_wheel_paths(wheels_dir: Path) -> list[Path]: |
| 30 | + """Collect *.whl under wheels_dir once per inode (rglob can list the same file twice via symlinks).""" |
| 31 | + wheels: list[Path] = [] |
| 32 | + seen: Set[Tuple[int, int]] = set() |
| 33 | + for p in sorted(wheels_dir.rglob("*.whl")): |
| 34 | + try: |
| 35 | + if not p.is_file(): |
| 36 | + continue |
| 37 | + st = p.stat() |
| 38 | + key = (st.st_dev, st.st_ino) |
| 39 | + except OSError: |
| 40 | + continue |
| 41 | + if key in seen: |
| 42 | + continue |
| 43 | + seen.add(key) |
| 44 | + wheels.append(p) |
| 45 | + return wheels |
| 46 | + |
| 47 | + |
27 | 48 | def get_platform() -> str: |
28 | 49 | return platform.system() |
29 | 50 |
|
@@ -91,7 +112,7 @@ def fix_universal2_wheel_name(wheel_path: Path, error_msg: str) -> Union[Path, s |
91 | 112 | if "'arm64,x86_64'" in error_msg or "'x86_64,arm64'" in error_msg: |
92 | 113 | # Missing BOTH architectures - wheel is corrupted, delete it |
93 | 114 | print_color(" -> Deleting corrupted wheel (missing native binaries for all architectures)", Fore.RED) |
94 | | - wheel_path.unlink() |
| 115 | + wheel_path.unlink(missing_ok=True) |
95 | 116 | return "delete" |
96 | 117 | elif "'x86_64'" in error_msg: |
97 | 118 | # Missing x86_64, so it only has arm64 |
@@ -138,8 +159,8 @@ def main() -> None: |
138 | 159 | temp_dir: Path = Path("./temp_repair") |
139 | 160 | temp_dir.mkdir(exist_ok=True) |
140 | 161 |
|
141 | | - # Find all wheel files |
142 | | - wheels: list[Path] = list(wheels_dir.rglob("*.whl")) |
| 162 | + # Find all wheel files (dedupe: same inode can appear twice via symlinks / layout quirks) |
| 163 | + wheels: list[Path] = _dedupe_wheel_paths(wheels_dir) |
143 | 164 |
|
144 | 165 | if not wheels: |
145 | 166 | print_color(f"No wheels found in {wheels_dir} - nothing to repair", Fore.YELLOW) |
@@ -190,7 +211,8 @@ def main() -> None: |
190 | 211 | # PEP 427: wheels are zip files; invalid magic usually means truncated/corrupt CI artifact |
191 | 212 | if not zipfile.is_zipfile(wheel): |
192 | 213 | print_color(" -> Deleting file (not a valid zip / wheel archive)", Fore.RED) |
193 | | - wheel.unlink() |
| 214 | + # missing_ok: duplicate paths or prior partial runs can leave nothing to remove |
| 215 | + wheel.unlink(missing_ok=True) |
194 | 216 | deleted_count += 1 |
195 | 217 | continue |
196 | 218 |
|
@@ -255,7 +277,7 @@ def main() -> None: |
255 | 277 | and "This does not look like a platform wheel, no ELF executable" in error_msg |
256 | 278 | ): |
257 | 279 | print_color(" -> Deleting corrupted wheel", Fore.RED) |
258 | | - wheel.unlink() |
| 280 | + wheel.unlink(missing_ok=True) |
259 | 281 | deleted_count += 1 |
260 | 282 | continue |
261 | 283 |
|
@@ -302,19 +324,19 @@ def main() -> None: |
302 | 324 | if repaired: |
303 | 325 | # A repaired wheel was created successfully |
304 | 326 | if repaired.name != wheel.name: |
305 | | - wheel.unlink() # Remove original |
| 327 | + wheel.unlink(missing_ok=True) # Remove original |
306 | 328 | final_path = wheel.parent / repaired.name |
307 | 329 | repaired.rename(final_path) |
308 | 330 | print_color(f" -> Replaced with repaired wheel: {repaired.name}", Fore.GREEN) |
309 | 331 | else: |
310 | 332 | # Name unchanged |
311 | | - wheel.unlink() |
| 333 | + wheel.unlink(missing_ok=True) |
312 | 334 | repaired.rename(wheel) |
313 | 335 | final_path = wheel |
314 | 336 | print_color(f" -> Repaired successfully: {repaired.name}", Fore.GREEN) |
315 | 337 | if not zipfile.is_zipfile(final_path): |
316 | 338 | print_color(" -> Deleting repaired output (not a valid zip archive)", Fore.RED) |
317 | | - final_path.unlink() |
| 339 | + final_path.unlink(missing_ok=True) |
318 | 340 | deleted_count += 1 |
319 | 341 | else: |
320 | 342 | repaired_count += 1 |
|
0 commit comments