diff options
author | Pierre Le Marre <dev@wismill.eu> | 2024-08-04 12:43:54 +0200 |
---|---|---|
committer | Pierre Le Marre <dev@wismill.eu> | 2024-08-19 18:46:20 +0200 |
commit | c4f76b584fe10bc2037966a70efad9bc5b682a4a (patch) | |
tree | 8a8a6b3f4023861dddfd499941b56820bf32d065 /rules | |
parent | 6c12fe8c46bd10f12116053473dcd910e23c1ecf (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-x | rules/merge.py | 71 |
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]) |