Skip to content

Commit ff22bb3

Browse files
committed
Add merge.py
1 parent c1fa00a commit ff22bb3

File tree

1 file changed

+141
-0
lines changed

1 file changed

+141
-0
lines changed

merge.py

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
"""Tool to merge cpython pot files to python-docs-fr po files for a
2+
given branch.
3+
"""
4+
5+
import re
6+
import shutil
7+
from pathlib import Path
8+
import argparse
9+
import subprocess
10+
from subprocess import PIPE
11+
from tqdm import tqdm
12+
13+
14+
def run(*args: str | Path, **kwargs) -> subprocess.CompletedProcess:
15+
"""Run a shell command with subprocess.run() with check=True and
16+
encoding="UTF-8".
17+
"""
18+
return subprocess.run(list(args), encoding="UTF-8", check=True, **kwargs)
19+
20+
21+
def parse_args():
22+
parser = argparse.ArgumentParser(description=__doc__)
23+
parser.add_argument(
24+
"--cpython_repo",
25+
default=Path("../cpython"),
26+
type=Path,
27+
help="Use this given cpython clone.",
28+
)
29+
parser.add_argument("branch", help="Merge from this branch")
30+
return parser.parse_args()
31+
32+
33+
def setup_repo(repo_path: Path, branch: str):
34+
"""Ensure we're up-to-date."""
35+
run("git", "-C", repo_path, "checkout", branch)
36+
run("git", "-C", repo_path, "pull", "--ff-only")
37+
38+
39+
def copy_new_files(new_files: set[Path], pot_path: Path) -> None:
40+
"""Just copy new po files to our hierarchy."""
41+
print(f"{len(new_files)} new files.")
42+
for file in new_files:
43+
file.parent.mkdir(parents=True, exist_ok=True)
44+
src = (pot_path / file).with_suffix(".pot")
45+
run("msgcat", "-o", file, src)
46+
47+
48+
def update_known_files(known_files: set[Path], pot_path: Path) -> None:
49+
"""msgmerge updated pot files in our po files."""
50+
print(f"{len(known_files)} files to update.")
51+
for file in tqdm(known_files, desc="merging pot files"):
52+
src = (pot_path / file).with_suffix(".pot")
53+
run("msgmerge", "-q", "--backup=off", "--force-po", "-U", file, src)
54+
55+
56+
def remove_old_files(old_files: set[Path]) -> None:
57+
"""Remove files removed upstream."""
58+
print(f"{len(old_files)} removed files.")
59+
60+
for file in old_files:
61+
run("git", "rm", file)
62+
63+
64+
def clean_paths(files: set[Path]) -> None:
65+
"""Ensure the path present in po files are always relative.
66+
67+
This avoid having diffs on those paths when we change something in
68+
a script.
69+
"""
70+
for file in tqdm(files, desc="Cleaning rst path in pot files"):
71+
contents = file.read_text(encoding="UTF-8")
72+
contents = re.sub("^#: .*Doc/", "#: ", contents, flags=re.M)
73+
file.write_text(contents, encoding="UTF-8")
74+
75+
76+
def update_makefile(cpython_repo: Path) -> None:
77+
"""Update CPYTHON_CURRENT_COMMIT in the Makefile.
78+
79+
So that when we run `make` it use the same commit than the one
80+
used to generate the `po` files.
81+
"""
82+
makefile = Path("Makefile").read_text(encoding="UTF-8")
83+
head = run(
84+
"git", "-C", cpython_repo, "rev-parse", "HEAD", stdout=PIPE
85+
).stdout.strip()
86+
makefile = re.sub(
87+
"^CPYTHON_CURRENT_COMMIT :=.*$",
88+
f"CPYTHON_CURRENT_COMMIT := {head}",
89+
makefile,
90+
flags=re.M,
91+
)
92+
Path("Makefile").write_text(makefile, encoding="UTF-8")
93+
run("git", "add", "Makefile")
94+
95+
96+
def git_add_relevant_files():
97+
"""Add only files with relevant modifications.
98+
99+
This only add files with actual modifications, not just metadata
100+
modifications, to avoid noise in history.
101+
"""
102+
modified_files = run("git", "ls-files", "-m", stdout=PIPE).stdout.split("\n")
103+
modified_po_files = [line for line in modified_files if line.endswith(".po")]
104+
for file in modified_po_files:
105+
diff = run("git", "diff", "-U0", file, stdout=PIPE).stdout
106+
if len(diff.split("\n")) > 8:
107+
run("git", "add", file)
108+
else:
109+
run("git", "checkout", "--", file)
110+
run("rm", "-f", "whatsnew/changelog.po") # We don't translate this file.
111+
112+
113+
def main():
114+
args = parse_args()
115+
setup_repo(args.cpython_repo, args.branch)
116+
run(
117+
*["sphinx-build", "-jauto", "-QDgettext_compact=0", "-bgettext", ".", "../pot"],
118+
cwd=args.cpython_repo / "Doc",
119+
)
120+
pot_path = args.cpython_repo / "pot"
121+
upstream = {
122+
file.relative_to(pot_path).with_suffix(".po")
123+
for file in pot_path.glob("**/*.pot")
124+
}
125+
downstream = {
126+
Path(po)
127+
for po in run("git", "ls-files", "*.po", stdout=PIPE).stdout.splitlines()
128+
}
129+
copy_new_files(upstream - downstream, pot_path=pot_path)
130+
update_known_files(upstream & downstream, pot_path=pot_path)
131+
remove_old_files(downstream - upstream)
132+
clean_paths((upstream - downstream) | (upstream & downstream))
133+
shutil.rmtree(pot_path)
134+
run("powrap", "-m")
135+
update_makefile(args.cpython_repo)
136+
git_add_relevant_files()
137+
run("git", "commit", "-m", "Make merge")
138+
139+
140+
if __name__ == "__main__":
141+
main()

0 commit comments

Comments
 (0)