aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoek Le Blansch <loek.le-blansch.pv@renesas.com>2025-10-24 13:04:37 +0200
committerLoek Le Blansch <loek.le-blansch.pv@renesas.com>2025-10-24 13:04:37 +0200
commit30cdc59df8d3c6f27d11fa015174962db65a277b (patch)
tree4f928ad6568e13fa6befe124c5f79acd5cbdd773
parentb819354e3d7b2181995ac11510da2226564eebfb (diff)
python black
-rw-r--r--.pre-commit-config.yaml4
-rw-r--r--patchtree/cli.py95
-rw-r--r--patchtree/config.py70
-rw-r--r--patchtree/context.py79
-rw-r--r--patchtree/diff.py81
-rw-r--r--patchtree/patch.py135
-rw-r--r--patchtree/process.py86
-rw-r--r--setup.py1
8 files changed, 293 insertions, 258 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 40a64e2..b21ca20 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -11,3 +11,7 @@ repos:
hooks:
- id: codespell
args: [--quiet-level=2]
+ - repo: https://github.com/psf/black-pre-commit-mirror
+ rev: 25.9.0
+ hooks:
+ - id: black
diff --git a/patchtree/cli.py b/patchtree/cli.py
index b9cd952..5ec8213 100644
--- a/patchtree/cli.py
+++ b/patchtree/cli.py
@@ -5,62 +5,67 @@ from pathlib import Path
from .context import Context
from .config import Config
+
def load_config() -> Config:
- init = {}
- cfg = Path("ptconfig.py")
- if cfg.exists():
- exec(cfg.read_text(), init)
- valid_fields = [field.name for field in fields(Config)]
- init = {key: value for key, value in init.items() if key in valid_fields}
- return Config(**init)
+ init = {}
+ cfg = Path("ptconfig.py")
+ if cfg.exists():
+ exec(cfg.read_text(), init)
+ valid_fields = [field.name for field in fields(Config)]
+ init = {key: value for key, value in init.items() if key in valid_fields}
+ return Config(**init)
+
def parse_arguments(config: Config) -> Context:
- parser = config.argument_parser(
- prog='patchtree',
- description='patch file generator',
- )
- parser.add_argument(
- '-o', '--out',
- help="output file (stdout by default)",
- type=str,
- )
- parser.add_argument('target', help="target directory or archive")
- parser.add_argument("patch", help="patch input glob(s)", nargs="+")
-
- options = parser.parse_args()
-
- try:
- return config.context(options)
- except Exception as e:
- parser.error(str(e))
+ parser = config.argument_parser(
+ prog="patchtree",
+ description="patch file generator",
+ )
+ parser.add_argument(
+ "-o",
+ "--out",
+ help="output file (stdout by default)",
+ type=str,
+ )
+ parser.add_argument("target", help="target directory or archive")
+ parser.add_argument("patch", help="patch input glob(s)", nargs="+")
+
+ options = parser.parse_args()
+
+ try:
+ return config.context(options)
+ except Exception as e:
+ parser.error(str(e))
+
def main():
- config = load_config()
+ config = load_config()
- context = parse_arguments(config)
+ context = parse_arguments(config)
- file_set: set[Path] = set()
- for pattern in context.options.patch:
- for path in Path('.').glob(pattern):
- if not path.is_file():
- continue
- file_set.add(path)
- files = sorted(file_set)
+ file_set: set[Path] = set()
+ for pattern in context.options.patch:
+ for path in Path(".").glob(pattern):
+ if not path.is_file():
+ continue
+ file_set.add(path)
+ files = sorted(file_set)
- if len(files) == 0:
- print("no files to patch!", file=stderr)
- return 0
+ if len(files) == 0:
+ print("no files to patch!", file=stderr)
+ return 0
- config.header(context)
+ config.header(context)
- for file in files:
- patch = config.patch(config, file)
- patch.write(context)
+ for file in files:
+ patch = config.patch(config, file)
+ patch.write(context)
- context.output.flush()
- context.output.close()
+ context.output.flush()
+ context.output.close()
+
+ return 0
- return 0
if __name__ == "__main__":
- exit(main())
+ exit(main())
diff --git a/patchtree/config.py b/patchtree/config.py
index b4772a9..065b03c 100644
--- a/patchtree/config.py
+++ b/patchtree/config.py
@@ -8,49 +8,55 @@ from .process import *
from .diff import *
DEFAULT_PROCESSORS: dict[str, type[Process]] = {
- "jinja": ProcessJinja2,
- "cocci": ProcessCoccinelle,
- "smpl": ProcessCoccinelle,
+ "jinja": ProcessJinja2,
+ "cocci": ProcessCoccinelle,
+ "smpl": ProcessCoccinelle,
}
DEFAULT_DIFFS: dict[str, type[Diff]] = {
- ".gitignore": IgnoreDiff,
+ ".gitignore": IgnoreDiff,
}
+
class Header:
- context: Context
- name = "patchtree"
- license = None
+ context: Context
+ name = "patchtree"
+ license = None
+
+ def __init__(self, context: Context):
+ self.context = context
- def __init__(self, context: Context):
- self.context = context
+ self.write_version()
+ self.write_version_extra()
+ self.write_license()
- self.write_version()
- self.write_version_extra()
- self.write_license()
+ def write_version(self):
+ version = metadata.version("patchtree")
+ self.context.output.write(f"{self.name} output (version {version})\n")
- def write_version(self):
- version = metadata.version("patchtree")
- self.context.output.write(f"{self.name} output (version {version})\n")
+ def write_version_extra(self):
+ pass
- def write_version_extra(self):
- pass
+ def write_license(self):
+ if self.license is None:
+ return
+ self.context.output.write(f"{self.license}\n")
- def write_license(self):
- if self.license is None:
- return
- self.context.output.write(f"{self.license}\n")
@dataclass
class Config:
- context: type[Context] = Context
- patch: type[Patch] = Patch
- argument_parser: type[ArgumentParser] = ArgumentParser
- process_delimiter: str = "#"
- processors: dict[str, type[Process]] = field(default_factory=lambda: DEFAULT_PROCESSORS)
- diff_strategies: dict[str, type[Diff]] = field(default_factory=lambda: DEFAULT_DIFFS)
- header: type[Header] = Header
-
- def __post_init__(self):
- self.processors = {**DEFAULT_PROCESSORS, **self.processors}
- self.diff_strategies = {**DEFAULT_DIFFS, **self.diff_strategies}
+ context: type[Context] = Context
+ patch: type[Patch] = Patch
+ argument_parser: type[ArgumentParser] = ArgumentParser
+ process_delimiter: str = "#"
+ processors: dict[str, type[Process]] = field(
+ default_factory=lambda: DEFAULT_PROCESSORS
+ )
+ diff_strategies: dict[str, type[Diff]] = field(
+ default_factory=lambda: DEFAULT_DIFFS
+ )
+ header: type[Header] = Header
+
+ def __post_init__(self):
+ self.processors = {**DEFAULT_PROCESSORS, **self.processors}
+ self.diff_strategies = {**DEFAULT_DIFFS, **self.diff_strategies}
diff --git a/patchtree/context.py b/patchtree/context.py
index 993ec64..8fa7abd 100644
--- a/patchtree/context.py
+++ b/patchtree/context.py
@@ -5,43 +5,44 @@ from zipfile import is_zipfile, Path as ZipPath
from os import path
from sys import stdout
+
class Context:
- """
- Generic "context" struct that Diff and the main() function depend on for
- application context.
- """
-
- fs: Union[DiskPath, ZipPath]
- output: IO = stdout
- options: Namespace
-
- def __init__(self, options: Namespace):
- self.options = options
-
- target = options.target
- if not path.exists(target):
- raise Exception(f"cannot open `{target}'")
-
- if options.out is not None:
- if options.out == "-":
- self.output = stdout
- else:
- self.output = open(options.out, "w+")
-
- if path.isdir(target):
- self.fs = DiskPath(target)
- elif is_zipfile(target):
- self.fs = ZipPath(target)
- else:
- raise Exception("cannot read `{target}'")
-
- def get_dir(self, dir: str) -> list[str]:
- here = self.fs.joinpath(dir)
- print(here)
- return [path.name for path in here.iterdir()]
-
- def get_content(self, file: str) -> str | None:
- here = self.fs.joinpath(file)
- if not here.exists():
- return None
- return here.read_bytes().decode()
+ """
+ Generic "context" struct that Diff and the main() function depend on for
+ application context.
+ """
+
+ fs: Union[DiskPath, ZipPath]
+ output: IO = stdout
+ options: Namespace
+
+ def __init__(self, options: Namespace):
+ self.options = options
+
+ target = options.target
+ if not path.exists(target):
+ raise Exception(f"cannot open `{target}'")
+
+ if options.out is not None:
+ if options.out == "-":
+ self.output = stdout
+ else:
+ self.output = open(options.out, "w+")
+
+ if path.isdir(target):
+ self.fs = DiskPath(target)
+ elif is_zipfile(target):
+ self.fs = ZipPath(target)
+ else:
+ raise Exception("cannot read `{target}'")
+
+ def get_dir(self, dir: str) -> list[str]:
+ here = self.fs.joinpath(dir)
+ print(here)
+ return [path.name for path in here.iterdir()]
+
+ def get_content(self, file: str) -> str | None:
+ here = self.fs.joinpath(file)
+ if not here.exists():
+ return None
+ return here.read_bytes().decode()
diff --git a/patchtree/diff.py b/patchtree/diff.py
index 5613858..9058b02 100644
--- a/patchtree/diff.py
+++ b/patchtree/diff.py
@@ -1,49 +1,56 @@
from difflib import unified_diff
-class Diff:
- """
- The base Diff class just produces a regular diff from the (possibly absent)
- SDK10 file. This effectively adds a new file or replaces the SDK10 source file
- with the file in the patch directory.
- """
-
- file: str
-
- content_a: str | None
- content_b: str = ""
- def __init__(self, file: str):
- self.file = file
+class Diff:
+ """
+ The base Diff class just produces a regular diff from the (possibly absent)
+ SDK10 file. This effectively adds a new file or replaces the SDK10 source file
+ with the file in the patch directory.
+ """
- def compare(self) -> str:
- fromfile = f"a/{self.file}"
- tofile = f"b/{self.file}"
+ file: str
- if self.content_a is None:
- fromfile = "/dev/null"
- self.content_a = ""
+ content_a: str | None
+ content_b: str = ""
- a = self.content_a.splitlines()
- b = self.content_b.splitlines()
- diff = unified_diff(a, b, fromfile, tofile, lineterm="")
- return "\n".join(diff) + "\n"
+ def __init__(self, file: str):
+ self.file = file
- def diff(self) -> str:
- return self.compare()
+ def compare(self) -> str:
+ fromfile = f"a/{self.file}"
+ tofile = f"b/{self.file}"
-class IgnoreDiff(Diff):
- """
- IgnoreDiff is slightly different and is used to ensure all the lines in the
- patch source ignore file are present in the SDK version. This ensures no
- duplicate ignore lines exist after patching.
- """
+ if self.content_a is None:
+ fromfile = "/dev/null"
+ self.content_a = ""
- def diff(self):
- lines_a = (self.content_a or "").splitlines()
- lines_b = self.content_b.splitlines()
+ a = self.content_a.splitlines()
+ b = self.content_b.splitlines()
+ diff = unified_diff(a, b, fromfile, tofile, lineterm="")
+ return "\n".join(diff) + "\n"
- add_lines = set(lines_b) - set(lines_a)
+ def diff(self) -> str:
+ return self.compare()
- self.content_b = "\n".join((*lines_a, *add_lines,))
- return self.compare()
+class IgnoreDiff(Diff):
+ """
+ IgnoreDiff is slightly different and is used to ensure all the lines in the
+ patch source ignore file are present in the SDK version. This ensures no
+ duplicate ignore lines exist after patching.
+ """
+
+ def diff(self):
+ lines_a = (self.content_a or "").splitlines()
+ lines_b = self.content_b.splitlines()
+
+ add_lines = set(lines_b) - set(lines_a)
+
+ self.content_b = "\n".join(
+ (
+ *lines_a,
+ *add_lines,
+ )
+ )
+
+ return self.compare()
diff --git a/patchtree/patch.py b/patchtree/patch.py
index cc0c1c6..415cf26 100644
--- a/patchtree/patch.py
+++ b/patchtree/patch.py
@@ -6,71 +6,74 @@ from pathlib import Path
from .diff import Diff
if TYPE_CHECKING:
- from .process import Process
- from .context import Context
- from .config import Config
+ from .process import Process
+ from .context import Context
+ from .config import Config
+
class Patch:
- config: Config
- patch: Path
-
- file: str
- file_name: str = ""
- file_type: str = ""
- processors: list[str] = []
-
- def __init__(self, config: Config, patch: Path):
- self.patch = patch
- self.config = config
-
- self.file_name = patch.name
-
- # find preprocessors
- idx = self.file_name.find(config.process_delimiter)
- if idx >= 0:
- self.processors = self.file_name[idx:].split(config.process_delimiter)
- self.processors = [template.strip() for template in self.processors]
- self.processors = [template for template in self.processors if len(template) > 0]
- self.processors.reverse()
- self.file_name = self.file_name[:idx]
-
- # save the path to the target file
- self.file = str(patch.parent.joinpath(self.file_name))
-
- # find and split at file extension
- idx = self.file_name.find(".")
- if idx >= 0:
- self.file_type = self.file_name[idx:]
- self.file_name = self.file_name[:idx]
-
- def get_diff(self) -> type[Diff]:
- return self.config.diff_strategies.get(self.file_type, Diff)
-
- def get_processors(self) -> list[type[Process]]:
- processors = []
- for processor in self.processors:
- if processor not in self.config.processors:
- continue
- processors.append(self.config.processors[processor])
- return processors
-
- def write(self, context: Context) -> None:
- diff_class = self.get_diff()
- processor_classes = self.get_processors()
-
- diff = diff_class(self.file)
-
- # read file A contents
- content_a = context.get_content(self.file)
-
- # read file B contents
- content_b = self.patch.read_text()
- for processor_class in processor_classes:
- processor = processor_class(context)
- content_b = processor.transform(content_a, content_b)
-
- diff.content_a = content_a
- diff.content_b = content_b
-
- delta = diff.diff()
- context.output.write(delta)
+ config: Config
+ patch: Path
+
+ file: str
+ file_name: str = ""
+ file_type: str = ""
+ processors: list[str] = []
+
+ def __init__(self, config: Config, patch: Path):
+ self.patch = patch
+ self.config = config
+
+ self.file_name = patch.name
+
+ # find preprocessors
+ idx = self.file_name.find(config.process_delimiter)
+ if idx >= 0:
+ self.processors = self.file_name[idx:].split(config.process_delimiter)
+ self.processors = [template.strip() for template in self.processors]
+ self.processors = [
+ template for template in self.processors if len(template) > 0
+ ]
+ self.processors.reverse()
+ self.file_name = self.file_name[:idx]
+
+ # save the path to the target file
+ self.file = str(patch.parent.joinpath(self.file_name))
+
+ # find and split at file extension
+ idx = self.file_name.find(".")
+ if idx >= 0:
+ self.file_type = self.file_name[idx:]
+ self.file_name = self.file_name[:idx]
+
+ def get_diff(self) -> type[Diff]:
+ return self.config.diff_strategies.get(self.file_type, Diff)
+
+ def get_processors(self) -> list[type[Process]]:
+ processors = []
+ for processor in self.processors:
+ if processor not in self.config.processors:
+ continue
+ processors.append(self.config.processors[processor])
+ return processors
+
+ def write(self, context: Context) -> None:
+ diff_class = self.get_diff()
+ processor_classes = self.get_processors()
+
+ diff = diff_class(self.file)
+
+ # read file A contents
+ content_a = context.get_content(self.file)
+
+ # read file B contents
+ content_b = self.patch.read_text()
+ for processor_class in processor_classes:
+ processor = processor_class(context)
+ content_b = processor.transform(content_a, content_b)
+
+ diff.content_a = content_a
+ diff.content_b = content_b
+
+ delta = diff.diff()
+ context.output.write(delta)
diff --git a/patchtree/process.py b/patchtree/process.py
index 0e86f2e..b8b87bd 100644
--- a/patchtree/process.py
+++ b/patchtree/process.py
@@ -7,55 +7,63 @@ from subprocess import Popen
from pathlib import Path
if TYPE_CHECKING:
- from .context import Context
+ from .context import Context
+
class Process:
- context: Context
+ context: Context
+
+ def __init__(self, context: Context):
+ self.context = context
- def __init__(self, context: Context):
- self.context = context
+ def transform(self, content_a: str | None, content_b: str) -> str:
+ return content_b
- def transform(self, content_a: str | None, content_b: str) -> str:
- return content_b
class ProcessJinja2(Process):
- environment: Environment = Environment(
- trim_blocks=True,
- lstrip_blocks=True,
- )
+ environment: Environment = Environment(
+ trim_blocks=True,
+ lstrip_blocks=True,
+ )
+
+ def transform(self, content_a, content_b) -> str:
+ template_vars = self.get_template_vars()
+ return self.environment.from_string(content_b).render(**template_vars)
- def transform(self, content_a, content_b) -> str:
- template_vars = self.get_template_vars()
- return self.environment.from_string(content_b).render(**template_vars)
+ def get_template_vars(self) -> dict[str, Any]:
+ return {}
- def get_template_vars(self) -> dict[str, Any]:
- return {}
class ProcessCoccinelle(Process):
- def transform(self, content_a, content_b) -> str:
- content_a = content_a or ""
-
- if len(content_b.strip()) == 0:
- return content_a
-
- temp_a = Path(mkstemp()[1])
- temp_b = Path(mkstemp()[1])
- temp_sp = Path(mkstemp()[1])
-
- temp_a.write_text(content_a)
- temp_sp.write_text(content_b)
- cmd = (
- "spatch", "--very-quiet", "--no-show-diff",
- "--sp-file", str(temp_sp),
- str(temp_a), "-o", str(temp_b),
- )
- coccinelle = Popen(cmd)
- coccinelle.wait()
+ def transform(self, content_a, content_b) -> str:
+ content_a = content_a or ""
+
+ if len(content_b.strip()) == 0:
+ return content_a
+
+ temp_a = Path(mkstemp()[1])
+ temp_b = Path(mkstemp()[1])
+ temp_sp = Path(mkstemp()[1])
+
+ temp_a.write_text(content_a)
+ temp_sp.write_text(content_b)
+ cmd = (
+ "spatch",
+ "--very-quiet",
+ "--no-show-diff",
+ "--sp-file",
+ str(temp_sp),
+ str(temp_a),
+ "-o",
+ str(temp_b),
+ )
+ coccinelle = Popen(cmd)
+ coccinelle.wait()
- content_b = temp_b.read_text()
+ content_b = temp_b.read_text()
- temp_a.unlink()
- temp_b.unlink()
- temp_sp.unlink()
+ temp_a.unlink()
+ temp_b.unlink()
+ temp_sp.unlink()
- return content_b
+ return content_b
diff --git a/setup.py b/setup.py
index 8bf1ba9..6068493 100644
--- a/setup.py
+++ b/setup.py
@@ -1,2 +1,3 @@
from setuptools import setup
+
setup()