summaryrefslogtreecommitdiff
path: root/rules
diff options
context:
space:
mode:
authorPierre Le Marre <dev@wismill.eu>2024-08-04 12:43:54 +0200
committerPierre Le Marre <dev@wismill.eu>2024-08-19 18:46:20 +0200
commitc4f76b584fe10bc2037966a70efad9bc5b682a4a (patch)
tree8a8a6b3f4023861dddfd499941b56820bf32d065 /rules
parent6c12fe8c46bd10f12116053473dcd910e23c1ecf (diff)
rules: Improve merge script
- Merge section with same header, independently of the whitespaces. - Improve Python (typing) Part-of: <https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config/-/merge_requests/728>
Diffstat (limited to 'rules')
-rwxr-xr-xrules/merge.py71
1 files changed, 45 insertions, 26 deletions
diff --git a/rules/merge.py b/rules/merge.py
index bdf21269..868879f3 100755
--- a/rules/merge.py
+++ b/rules/merge.py
@@ -1,11 +1,32 @@
#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+from __future__ import annotations
import argparse
import sys
+from collections import defaultdict
+from contextlib import AbstractContextManager, nullcontext
+from dataclasses import dataclass
from pathlib import Path
+from typing import TextIO, cast
+
+
+@dataclass
+class Header:
+ text: str
+ key: tuple[str, ...]
+
+ @classmethod
+ def empty(cls) -> Header:
+ return Header("", ())
+
+ @classmethod
+ def parse(cls, raw: str) -> Header:
+ return Header(raw, tuple(raw.split()))
-def handle_file(path):
+def handle_file(path: Path) -> tuple[Header, Path]:
"""
Return a tuple of (header, path) for the file at path.
If the file does not have a header, the header is the empty string.
@@ -13,12 +34,12 @@ def handle_file(path):
with open(path) as fd:
header = fd.readline()
if header.startswith("! "):
- return header, path
+ return Header.parse(header), path
else:
- return "", path
+ return Header.empty(), path
-def merge(dest, files):
+def merge(dest: TextIO, files):
"""
Merge the content of all files into the file dest.
@@ -31,7 +52,7 @@ def merge(dest, files):
as header, these need to get written out first.
"""
- def sort_basename(path):
+ def sort_basename(path: Path) -> str:
return path.name
# sort the file list by basename
@@ -41,43 +62,41 @@ def merge(dest, files):
# out. We use section_names to keep the same order as we get the files
# passed in (superfluous with python 3.6+ since the dict keeps the
# insertion order anyway).
- sections = {"": []}
- section_names = [""]
+ sections: dict[tuple[str, ...], list[Path]] = defaultdict(list)
+ section_names = [Header.empty()]
for path in files:
# files may exist in srcdir or builddir, depending whether they're
# generated
header, path = handle_file(path)
- paths = sections.get(header, [])
- paths.append(path)
- sections[header] = paths
- if header not in section_names:
+ if header.key not in sections:
section_names.append(header)
+ sections[header.key].append(path)
for header in section_names:
- if header:
+ if header.text:
dest.write("\n")
- dest.write(header)
- for f in sections[header]:
+ dest.write(header.text)
+ for f in sections[header.key]:
with open(f) as fd:
- if header:
+ if header.text:
fd.readline() # drop the header
dest.write(fd.read())
if __name__ == "__main__":
parser = argparse.ArgumentParser("rules file merge script")
- parser.add_argument("--dest", type=str, default=None)
- parser.add_argument("--srcdir", type=str)
- parser.add_argument("--builddir", type=str)
- parser.add_argument("files", nargs="+", type=str)
+ parser.add_argument("--dest", type=Path, default=None)
+ parser.add_argument("--srcdir", type=Path)
+ parser.add_argument("--builddir", type=Path)
+ parser.add_argument("files", nargs="+", type=Path)
ns = parser.parse_args()
if ns.dest is None:
- dest = sys.stdout
+ dest: AbstractContextManager = nullcontext(sys.stdout)
else:
- dest = ns.dest
+ dest = cast(Path, ns.dest).open("wt")
- with dest or open(dest, "w") as fd:
+ with dest as fd:
basename = Path(sys.argv[0]).name
fd.write(
"// DO NOT EDIT THIS FILE - IT WAS AUTOGENERATED BY {} FROM rules/*.part\n".format(
@@ -86,15 +105,15 @@ if __name__ == "__main__":
)
fd.write("//\n")
- def find_file(f):
+ def find_file(f: Path) -> Path:
if ns.builddir:
- path = Path(ns.builddir) / f
+ path = cast(Path, ns.builddir) / f
if path.exists():
return path
if ns.srcdir:
- path = Path(ns.srcdir) / f
+ path = cast(Path, ns.srcdir) / f
if path.exists():
return path
- return Path(f)
+ return f
merge(fd, [find_file(f) for f in ns.files])