3838REPO_ROOT = Path (__file__ ).resolve ().parent .parent
3939
4040BOT_NAME_RE = re .compile (
41- r'github[^\w]*actions|\[bot\]|not committed yet' , re .IGNORECASE )
41+ r"github[^\w]*actions|\[bot\]|not committed yet" , re .IGNORECASE
42+ )
4243BOT_EMAIL_RE = re .compile (
43- r' github-actions|transifex|\[bot\]|@users\.noreply\.github\.com' ,
44+ r" github-actions|transifex|\[bot\]|@users\.noreply\.github\.com" ,
4445 re .IGNORECASE ,
4546)
4647MECHANICAL_SUBJECT_RE = re .compile (
47- r' ^(?:sync\s+translations\s+with\s+cpython\b'
48- r' |update\s+\.po\s+files(?:\s*\(\d+\))?\s*$'
49- r' |update\s+farsi\s+translations\s+from\s+transifex\b)' ,
48+ r" ^(?:sync\s+translations\s+with\s+cpython\b"
49+ r" |update\s+\.po\s+files(?:\s*\(\d+\))?\s*$"
50+ r" |update\s+farsi\s+translations\s+from\s+transifex\b)" ,
5051 re .IGNORECASE ,
5152)
5253
7071SKIP_DIRS = {".git" , ".cpython-src" , ".venv" , "__pycache__" , "venv" }
7172
7273TEAMMD_ROW_RE = re .compile (
73- r' ^\|\s*(?P<user>[^|]+?)\s*\|\s*(?P<role>[^|]+?)\s*\|\s*'
74- r' (?P<t>\d+(?:\s*\([^)]*\))?)\s*\|$'
74+ r" ^\|\s*(?P<user>[^|]+?)\s*\|\s*(?P<role>[^|]+?)\s*\|\s*"
75+ r" (?P<t>\d+(?:\s*\([^)]*\))?)\s*\|$"
7576)
7677
7778
7879# ---------------------------------------------------------------------------
7980# Privacy helper
8081# ---------------------------------------------------------------------------
8182
83+
8284def redact_email (email : str ) -> str :
8385 """Return a partially redacted email for warning messages.
8486
@@ -109,6 +111,7 @@ def _blur(s: str) -> str:
109111# Git helpers
110112# ---------------------------------------------------------------------------
111113
114+
112115def _is_mechanical (subject : str ) -> bool :
113116 return bool (MECHANICAL_SUBJECT_RE .search (subject ))
114117
@@ -124,9 +127,18 @@ def git_blame_porcelain(path: Path) -> dict[str, dict]:
124127 ``lines`` (a set of 1-based line numbers blamed to that commit).
125128 """
126129 result = subprocess .run (
127- ["git" , "-C" , str (REPO_ROOT ), "blame" , "--porcelain" ,
128- "--" , str (path .resolve ().relative_to (REPO_ROOT ))],
129- capture_output = True , text = True , check = False ,
130+ [
131+ "git" ,
132+ "-C" ,
133+ str (REPO_ROOT ),
134+ "blame" ,
135+ "--porcelain" ,
136+ "--" ,
137+ str (path .resolve ().relative_to (REPO_ROOT )),
138+ ],
139+ capture_output = True ,
140+ text = True ,
141+ check = False ,
130142 )
131143 if result .returncode != 0 :
132144 return {}
@@ -137,20 +149,26 @@ def git_blame_porcelain(path: Path) -> dict[str, dict]:
137149 for raw_line in result .stdout .splitlines ():
138150 # Commit header: "<40-char-hash> <orig-line> <result-line> [<num-lines>]"
139151 parts = raw_line .split ()
140- if len (parts ) >= 3 and len (parts [0 ]) == 40 and parts [0 ].isalnum () and parts [1 ].isdigit () and parts [2 ].isdigit ():
152+ if (
153+ len (parts ) >= 3
154+ and len (parts [0 ]) == 40
155+ and parts [0 ].isalnum ()
156+ and parts [1 ].isdigit ()
157+ and parts [2 ].isdigit ()
158+ ):
141159 h = parts [0 ]
142160 result_line = int (parts [2 ])
143161 current_hash = h
144162 if h not in commits :
145163 commits [h ] = {"name" : "" , "email" : "" , "subject" : "" , "lines" : set ()}
146164 commits [h ]["lines" ].add (result_line )
147165 elif raw_line .startswith ("author " ) and current_hash :
148- commits [current_hash ]["name" ] = raw_line [len ("author " ):].strip ()
166+ commits [current_hash ]["name" ] = raw_line [len ("author " ) :].strip ()
149167 elif raw_line .startswith ("author-mail " ) and current_hash :
150- email = raw_line [len ("author-mail " ):].strip ().strip ("<>" )
168+ email = raw_line [len ("author-mail " ) :].strip ().strip ("<>" )
151169 commits [current_hash ]["email" ] = email .lower ()
152170 elif raw_line .startswith ("summary " ) and current_hash :
153- commits [current_hash ]["subject" ] = raw_line [len ("summary " ):]
171+ commits [current_hash ]["subject" ] = raw_line [len ("summary " ) :]
154172
155173 return commits
156174
@@ -212,6 +230,7 @@ def real_author_for_lines(
212230# .po file walking
213231# ---------------------------------------------------------------------------
214232
233+
215234def collect_files (paths : list [str ]) -> list [Path ]:
216235 files = []
217236 for arg in paths :
@@ -314,6 +333,7 @@ def teammd_totals() -> dict[str, int]:
314333# Output
315334# ---------------------------------------------------------------------------
316335
336+
317337def print_report (counts : dict [str , int ]) -> None :
318338 total = sum (counts .values ())
319339 print (f"Total non-fuzzy translated entries attributed: { total } \n " )
@@ -371,17 +391,21 @@ def update_teammd(counts: dict[str, int]) -> None:
371391# Entry point
372392# ---------------------------------------------------------------------------
373393
394+
374395def main () -> None :
375396 parser = argparse .ArgumentParser (
376397 description = __doc__ ,
377398 formatter_class = argparse .RawDescriptionHelpFormatter ,
378399 )
379400 parser .add_argument (
380- "paths" , nargs = "*" , default = ["." ],
401+ "paths" ,
402+ nargs = "*" ,
403+ default = ["." ],
381404 help = "Files or directories to scan (default: whole repo)" ,
382405 )
383406 parser .add_argument (
384- "--update-teammd" , action = "store_true" ,
407+ "--update-teammd" ,
408+ action = "store_true" ,
385409 help = "rewrite TEAM.md from the computed counts" ,
386410 )
387411 args = parser .parse_args ()
0 commit comments