Skip to content

Commit b39cc47

Browse files
committed
clean up .pyc files before packaging
1 parent da85674 commit b39cc47

2 files changed

Lines changed: 95 additions & 0 deletions

File tree

.github/workflows/package.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ jobs:
149149
cp -a ../git/dist/libexec/git-core/{git,git-remote-http,git-remote-https} toolkit/libexec/git-core/
150150
ln toolkit/libexec/git-core/git toolkit/bin/
151151
cp -r $PYTHON_PREFIX/* toolkit/
152+
uv run python ../clean_pyc.py toolkit/
152153
mkdir -p config
153154
cp ../deploy.unix.yaml config/deploy.yaml
154155
@@ -188,6 +189,7 @@ jobs:
188189
cp -a ../git/dist/libexec/git-core/{git,git-remote-http,git-remote-https} toolkit/libexec/git-core/
189190
ln toolkit/libexec/git-core/git toolkit/bin/
190191
cp -r $PYTHON_PREFIX/* toolkit/
192+
uv run python ../clean_pyc.py toolkit/
191193
find toolkit/ -type f -name '*.so*' -or -type f -executable -exec strip --strip-unneeded {} \;
192194
mkdir -p config
193195
cp ../deploy.unix.yaml config/deploy.yaml
@@ -225,6 +227,7 @@ jobs:
225227
$git_files = @("git.exe","git-remote-http.exe","git-remote-https.exe","libcurl-4.dll") | ForEach-Object { "..\git\mingw64\bin\" + $_ }
226228
..\pedeps\bin\copypedeps.exe -r $git_files toolkit\git\mingw64\bin\
227229
cp -r ($env:PYTHON_PREFIX + "\*") toolkit\
230+
uv run python ..\clean_pyc.py toolkit\
228231
mkdir -ea 0 config
229232
cp ..\deploy.windows.yaml config\deploy.yaml
230233
cp ..\target\release\alas-launcher.exe alas-launcher.exe

clean_pyc.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import argparse
2+
import os
3+
from pathlib import Path
4+
from typing import Iterable, Tuple
5+
6+
7+
def find_pyc_files(root: Path) -> Iterable[Path]:
8+
"""Yield all .pyc files under root (recursive)."""
9+
for dirpath, _, filenames in os.walk(root):
10+
for fn in filenames:
11+
if fn.endswith('.pyc'):
12+
yield Path(dirpath) / fn
13+
14+
15+
def corresponding_py_for_pyc(pyc_path: Path) -> Path:
16+
"""Return the Path to the likely corresponding .py source for a .pyc file.
17+
18+
Rules:
19+
- If the .pyc is inside a __pycache__ directory, the source is one level up
20+
with the base module name (strip everything after the first dot in the
21+
pyc file name).
22+
- Otherwise, replace the .pyc suffix with .py in the same directory.
23+
"""
24+
if pyc_path.parent.name == '__pycache__':
25+
# Example: __pycache__/module.cpython-38.opt-1.pyc -> ../module.py
26+
base = pyc_path.stem.split('.', 1)[0]
27+
return pyc_path.parent.parent / (base + '.py')
28+
else:
29+
return pyc_path.with_suffix('.py')
30+
31+
32+
def clean_pyc(root: Path, dry_run: bool = True, verbose: bool = False) -> Tuple[int, int]:
33+
"""Remove .pyc files that have corresponding .py sources.
34+
35+
Returns a tuple (checked, removed).
36+
"""
37+
checked = 0
38+
removed = 0
39+
for pyc in find_pyc_files(root):
40+
checked += 1
41+
src = corresponding_py_for_pyc(pyc)
42+
if src.exists():
43+
if verbose:
44+
print(f"Will remove: {pyc} (found source: {src})")
45+
if not dry_run:
46+
try:
47+
pyc.unlink()
48+
removed += 1
49+
except Exception as e:
50+
print(f"Failed to remove {pyc}: {e}")
51+
else:
52+
if verbose:
53+
print(f"Keep: {pyc} (no source {src})")
54+
return checked, removed
55+
56+
57+
def parse_args() -> argparse.Namespace:
58+
p = argparse.ArgumentParser(
59+
description='Delete .pyc files when corresponding .py sources exist.'
60+
)
61+
p.add_argument('path', nargs='?', default='.', help='Root path to scan')
62+
p.add_argument('--dry-run', action='store_true', help='Only show what would be deleted')
63+
p.add_argument('--verbose', action='store_true', help='Show verbose output')
64+
return p.parse_args()
65+
66+
67+
def main() -> int:
68+
args = parse_args()
69+
root = Path(args.path).resolve()
70+
if not root.exists():
71+
print(f'Path does not exist: {root}')
72+
return 2
73+
74+
dry_run = args.dry_run
75+
76+
if args.verbose:
77+
print(f'Scanning: {root}')
78+
print(f'dry_run={dry_run}')
79+
80+
checked, removed = clean_pyc(root, dry_run=dry_run, verbose=args.verbose)
81+
82+
print(f"Checked .pyc files: {checked}")
83+
if dry_run:
84+
print(f"Dry run: would remove {removed} files")
85+
else:
86+
print(f"Removed {removed} files")
87+
88+
return 0
89+
90+
91+
if __name__ == '__main__':
92+
raise SystemExit(main())

0 commit comments

Comments
 (0)