4141IGNORED_DIR_NAMES = {".git" , ".cpython-src" , ".pot-templates" }
4242
4343
44- def run (cmd : list , cwd : Path | None = None , check : bool = True ) -> subprocess .CompletedProcess :
44+ def run (
45+ cmd : list , cwd : Path | None = None , check : bool = True
46+ ) -> subprocess .CompletedProcess :
4547 print (f"$ { ' ' .join (str (c ) for c in cmd )} " )
4648 return subprocess .run (cmd , cwd = cwd , check = check )
4749
@@ -60,14 +62,25 @@ def iter_po_files(repo_root: Path = REPO_ROOT):
6062# Fetch + build .pot templates
6163# ---------------------------------------------------------------------------
6264
65+
6366def fetch_cpython_full (tag : str , workdir : Path ) -> None :
6467 """Full clone of CPython at `tag`. Used by the version-bump script,
6568 which needs the full doc tree to build every .pot (including ones for
6669 brand-new pages that a sparse checkout might not anticipate)."""
6770 if workdir .exists ():
6871 shutil .rmtree (workdir )
69- run (["git" , "clone" , "--depth" , "1" , "--branch" , tag ,
70- "https://github.com/python/cpython.git" , str (workdir )])
72+ run (
73+ [
74+ "git" ,
75+ "clone" ,
76+ "--depth" ,
77+ "1" ,
78+ "--branch" ,
79+ tag ,
80+ "https://github.com/python/cpython.git" ,
81+ str (workdir ),
82+ ]
83+ )
7184
7285
7386def fetch_cpython_sparse (tag : str , workdir : Path ) -> None :
@@ -76,8 +89,20 @@ def fetch_cpython_sparse(tag: str, workdir: Path) -> None:
7689 so we don't need the rest of the tree."""
7790 if workdir .exists ():
7891 shutil .rmtree (workdir )
79- run (["git" , "clone" , "--depth" , "1" , "--filter=blob:none" , "--sparse" ,
80- "--branch" , tag , "https://github.com/python/cpython.git" , str (workdir )])
92+ run (
93+ [
94+ "git" ,
95+ "clone" ,
96+ "--depth" ,
97+ "1" ,
98+ "--filter=blob:none" ,
99+ "--sparse" ,
100+ "--branch" ,
101+ tag ,
102+ "https://github.com/python/cpython.git" ,
103+ str (workdir ),
104+ ]
105+ )
81106 run (["git" , "sparse-checkout" , "set" , "Doc" , "Include" ], cwd = workdir )
82107
83108
@@ -97,12 +122,17 @@ def build_gettext(doc_dir: Path) -> Path:
97122# Merge
98123# ---------------------------------------------------------------------------
99124
125+
100126@dataclass
101127class MergeReport :
102128 updated : list = field (default_factory = list )
103129 new_po_created : list = field (default_factory = list )
104- missing_pot : list = field (default_factory = list ) # .po with no matching .pot upstream
105- new_pot_no_po : list = field (default_factory = list ) # .pot with no .po yet (new upstream page)
130+ missing_pot : list = field (
131+ default_factory = list
132+ ) # .po with no matching .pot upstream
133+ new_pot_no_po : list = field (
134+ default_factory = list
135+ ) # .pot with no .po yet (new upstream page)
106136
107137 def summary (self ) -> str :
108138 lines = [
@@ -124,8 +154,10 @@ def merge_existing(pot_root: Path, repo_root: Path = REPO_ROOT) -> MergeReport:
124154 rel = po_path .relative_to (repo_root )
125155 pot_path = pot_root / rel .with_suffix (".pot" )
126156 if not pot_path .exists ():
127- print (f" ! no matching .pot for { rel } "
128- f"(page may have been removed/renamed upstream -- review manually)" )
157+ print (
158+ f" ! no matching .pot for { rel } "
159+ f"(page may have been removed/renamed upstream -- review manually)"
160+ )
129161 report .missing_pot .append (rel )
130162 continue
131163 run (["msgmerge" , * MSGMERGE_FLAGS , str (po_path ), str (pot_path )])
@@ -148,8 +180,9 @@ def detect_new_pot_files(pot_root: Path, repo_root: Path = REPO_ROOT) -> list:
148180 return new_pot
149181
150182
151- def create_po_for_new_pot (pot_root : Path , rel_pot_paths : list , locale : str = "fa" ,
152- repo_root : Path = REPO_ROOT ) -> list :
183+ def create_po_for_new_pot (
184+ pot_root : Path , rel_pot_paths : list , locale : str = "fa" , repo_root : Path = REPO_ROOT
185+ ) -> list :
153186 """Create a fresh .po (via msginit) for each given new .pot. Only called
154187 from the version-bump script -- the nightly workflow reports these via
155188 detect_new_pot_files() but leaves creation to a human-reviewed run."""
@@ -158,18 +191,31 @@ def create_po_for_new_pot(pot_root: Path, rel_pot_paths: list, locale: str = "fa
158191 pot_path = pot_root / rel
159192 po_path = repo_root / rel .with_suffix (".po" )
160193 po_path .parent .mkdir (parents = True , exist_ok = True )
161- run (["msginit" , "--no-translator" , "-l" , locale ,
162- "-i" , str (pot_path ), "-o" , str (po_path )])
194+ run (
195+ [
196+ "msginit" ,
197+ "--no-translator" ,
198+ "-l" ,
199+ locale ,
200+ "-i" ,
201+ str (pot_path ),
202+ "-o" ,
203+ str (po_path ),
204+ ]
205+ )
163206 created .append (rel )
164207 return created
165208
166209
167- def merge_all (pot_root : Path , create_new : bool , locale : str = "fa" ,
168- repo_root : Path = REPO_ROOT ) -> MergeReport :
210+ def merge_all (
211+ pot_root : Path , create_new : bool , locale : str = "fa" , repo_root : Path = REPO_ROOT
212+ ) -> MergeReport :
169213 report = merge_existing (pot_root , repo_root )
170214 new_pot = detect_new_pot_files (pot_root , repo_root )
171215 if create_new :
172- report .new_po_created = create_po_for_new_pot (pot_root , new_pot , locale , repo_root )
216+ report .new_po_created = create_po_for_new_pot (
217+ pot_root , new_pot , locale , repo_root
218+ )
173219 else :
174220 report .new_pot_no_po = new_pot
175221 return report
@@ -179,14 +225,16 @@ def merge_all(pot_root: Path, create_new: bool, locale: str = "fa",
179225# Validate
180226# ---------------------------------------------------------------------------
181227
228+
182229def check_po_files (repo_root : Path = REPO_ROOT ) -> list :
183230 """Run msgfmt --check on every .po file. Returns a list of (path, stderr)
184231 for any that fail; empty list means all good."""
185232 bad = []
186233 for po_path in iter_po_files (repo_root ):
187234 result = subprocess .run (
188235 ["msgfmt" , "--check" , "-o" , "/dev/null" , str (po_path )],
189- capture_output = True , text = True ,
236+ capture_output = True ,
237+ text = True ,
190238 )
191239 if result .returncode != 0 :
192240 bad .append ((po_path , result .stderr .strip ()))
@@ -197,6 +245,7 @@ def check_po_files(repo_root: Path = REPO_ROOT) -> list:
197245# CLI -- the "sync-only" mode the workflow shells out to (item 4)
198246# ---------------------------------------------------------------------------
199247
248+
200249def _cli_sync_only (args : argparse .Namespace ) -> int :
201250 """Sparse clone + build gettext + merge into existing .po files +
202251 validate. This is everything the nightly workflow needs, in one call,
@@ -216,7 +265,9 @@ def _cli_sync_only(args: argparse.Namespace) -> int:
216265 report = merge_all (pot_root , create_new = False )
217266 print (f"\n { report .summary ()} " )
218267 if report .new_pot_no_po :
219- print ("\n New upstream pages with no .po yet (run update_python_version.py to create):" )
268+ print (
269+ "\n New upstream pages with no .po yet (run update_python_version.py to create):"
270+ )
220271 for rel in report .new_pot_no_po :
221272 print (f" - { rel } " )
222273
@@ -243,17 +294,20 @@ def main() -> None:
243294 sync = sub .add_parser (
244295 "sync-only" ,
245296 help = "Sparse-checkout sync used by the nightly workflow: fetch, "
246- "build gettext, merge into existing .po files, report new "
247- "upstream pages, validate. Does not create new .po files." ,
297+ "build gettext, merge into existing .po files, report new "
298+ "upstream pages, validate. Does not create new .po files." ,
248299 )
249300 sync .add_argument ("tag" , help = "CPython git tag to sync against, e.g. v3.14.7" )
250- sync .add_argument ("--keep-src" , action = "store_true" ,
251- help = "keep the scratch CPython checkout instead of deleting it" )
301+ sync .add_argument (
302+ "--keep-src" ,
303+ action = "store_true" ,
304+ help = "keep the scratch CPython checkout instead of deleting it" ,
305+ )
252306 sync .set_defaults (func = _cli_sync_only )
253307
254308 args = parser .parse_args ()
255309 sys .exit (args .func (args ))
256310
257311
258312if __name__ == "__main__" :
259- main ()
313+ main ()
0 commit comments