From 5b69c074dd842bb97edff691ed072a35e8bc1629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Sch=C3=BCller?= Date: Mon, 7 Oct 2024 21:19:30 +0200 Subject: [PATCH] otk/command: hide explain with command line argument "explain" test code is now only available with "compile --explain" --- src/otk/command.py | 12 +++++++++--- src/otk/document.py | 5 +++-- src/otk/transform.py | 18 ++++++++++-------- test/test_command.py | 5 +++-- test/test_compile.py | 4 ++-- test/test_example.py | 4 ++-- 6 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/otk/command.py b/src/otk/command.py index c000d3a2..389a1e83 100644 --- a/src/otk/command.py +++ b/src/otk/command.py @@ -31,7 +31,7 @@ def run(argv: List[str]) -> int: raise RuntimeError("Unknown subcommand") -def _process(arguments: argparse.Namespace, dry_run: bool) -> int: +def _process(arguments: argparse.Namespace, dry_run: bool, explain: bool = False) -> int: if not dry_run: # pylint: disable=R1732 dst = sys.stdout if arguments.output is None else open(arguments.output, "w", encoding="utf-8") @@ -46,7 +46,7 @@ def _process(arguments: argparse.Namespace, dry_run: bool) -> int: # # It only exists as convenience for the user so that they do not need # to use "-t" - doc = Omnifest(path) + doc = Omnifest(path, explain=explain) target_available = doc.targets target_requested = arguments.target @@ -76,7 +76,7 @@ def _process(arguments: argparse.Namespace, dry_run: bool) -> int: def compile(arguments: argparse.Namespace) -> int: # pylint: disable=redefined-builtin - return _process(arguments, dry_run=False) + return _process(arguments, dry_run=False, explain=arguments.explain) def validate(arguments: argparse.Namespace) -> int: @@ -129,6 +129,12 @@ def parser_create() -> argparse.ArgumentParser: default=None, help="Target to output, required if more than one target exists in an omnifest.", ) + parser_compile.add_argument( + "-e", + "--explain", + action="store_true", + help="Add more output to explain where the data came from", + ) parser_validate = subparsers.add_parser("validate", help="Validate an omnifest.") parser_validate.add_argument( diff --git a/src/otk/document.py b/src/otk/document.py index 14e94ed4..e8dbcc0a 100644 --- a/src/otk/document.py +++ b/src/otk/document.py @@ -19,7 +19,8 @@ class Omnifest: _osbuild_ctx: OSBuildContext _target: str - def __init__(self, path: pathlib.Path, target: str = "", *, warn_duplicated_defs: bool = False) -> None: + def __init__(self, path: pathlib.Path, target: str = "", *, warn_duplicated_defs: bool = False, + explain: bool = False) -> None: self._ctx = CommonContext(target_requested=target, warn_duplicated_defs=warn_duplicated_defs) self._target = target # XXX: redo using a type-safe target registry @@ -28,7 +29,7 @@ def __init__(self, path: pathlib.Path, target: str = "", *, warn_duplicated_defs raise OTKError("only target osbuild supported right now") self._osbuild_ctx = OSBuildContext(self._ctx) state = State() - tree = process_include(self._ctx, state, path) + tree = process_include(self._ctx, state, path, explain=explain) # XXX: review this code, the idea is to find top-level keys that # have no targets but that of course only works if there are # no targets in the resolving. this means we are currently forced diff --git a/src/otk/transform.py b/src/otk/transform.py index 7bd1de71..c577a16f 100644 --- a/src/otk/transform.py +++ b/src/otk/transform.py @@ -255,7 +255,7 @@ def process_defines(ctx: Context, state: State, tree: Any) -> None: ctx.define(state.define_subkey(key), value) -def process_include(ctx: Context, state: State, path: pathlib.Path) -> dict: +def process_include(ctx: Context, state: State, path: pathlib.Path, explain: bool = False) -> dict: """ Load a yaml file and send it to resolve() for processing. """ @@ -265,9 +265,10 @@ def process_include(ctx: Context, state: State, path: pathlib.Path) -> dict: path = (cur_path / pathlib.Path(path)).resolve() log.info("resolving %s", path) - # callbacks to store information about the source of all data in the yaml files - yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, hidden_attr_dict_constructor) - yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, hidden_attr_list_constructor) + if explain: + # callbacks to store information about the source of all data in the yaml files + yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, hidden_attr_dict_constructor) + yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, hidden_attr_list_constructor) try: with path.open(encoding="utf8") as fp: @@ -279,10 +280,11 @@ def process_include(ctx: Context, state: State, path: pathlib.Path) -> dict: except ParseDuplicatedYamlKeyError as err: raise ParseDuplicatedYamlKeyError(f"{err}", state.copy(path=path)) from err - # DEBUG - TBD: DELETE ME - print("--- Before resolve:") - print(json.dumps(data, cls=HiddenAttrDictJSONEncoder, indent=2)) - print("---") + if explain: + # DEBUG - TBD: DELETE ME + print("--- Before resolve:") + print(json.dumps(data, cls=HiddenAttrDictJSONEncoder, indent=2)) + print("---") if data is not None: return resolve(ctx, state.copy(path=path), data) diff --git a/test/test_command.py b/test/test_command.py index 0d288067..6737d249 100644 --- a/test/test_command.py +++ b/test/test_command.py @@ -17,11 +17,11 @@ def test_parse_no_command(capsys): @pytest.mark.parametrize( "command,results", [ - (["compile"], {"command": "compile", "output": None, "input": None}), + (["compile"], {"command": "compile", "output": None, "input": None, "explain": False}), (["compile", "foo"], {"command": "compile", "output": None, "input": "foo"}), ( ["compile", "-o", "output_manifest.yaml"], - {"command": "compile", "output": "output_manifest.yaml", "input": None}, + {"command": "compile", "output": "output_manifest.yaml", "input": None, "explain": False}, ), ( ["compile", "-o", "output_manifest.yaml", "input_omifest.yaml"], @@ -29,6 +29,7 @@ def test_parse_no_command(capsys): "command": "compile", "output": "output_manifest.yaml", "input": "input_omifest.yaml", + "explain": False, }, ), (["validate", "foo.yaml"], {"command": "validate", "input": "foo.yaml"}), diff --git a/test/test_compile.py b/test/test_compile.py index 984debe6..0c11e100 100644 --- a/test/test_compile.py +++ b/test/test_compile.py @@ -23,7 +23,7 @@ def test_compile_integration_file(tmp_path): input_path.write_text(FAKE_OTK_YAML) output_path = tmp_path / "output.txt" - arguments = argparse.Namespace(input=input_path, output=output_path, target="osbuild.qcow2") + arguments = argparse.Namespace(input=input_path, output=output_path, target="osbuild.qcow2", explain=False) ret = compile(arguments) assert ret == 0 @@ -37,7 +37,7 @@ def test_compile_integration_stdin(capsys, monkeypatch): os.lseek(mocked_stdin, 0, 0) monkeypatch.setattr("sys.stdin", os.fdopen(mocked_stdin)) - arguments = argparse.Namespace(input=None, output=None, target="osbuild.qcow2") + arguments = argparse.Namespace(input=None, output=None, target="osbuild.qcow2", explain=False) ret = compile(arguments) assert ret == 0 diff --git a/test/test_example.py b/test/test_example.py index d8d0508a..9ed48ff8 100644 --- a/test/test_example.py +++ b/test/test_example.py @@ -13,7 +13,7 @@ def test_command_compile_on_base_examples(tmp_path, src_yaml, _mirror): src_yaml = pathlib.Path(src_yaml) dst = tmp_path / "out.json" - ns = argparse.Namespace(input=src_yaml, output=dst, target="osbuild") + ns = argparse.Namespace(input=src_yaml, output=dst, target="osbuild", explain=False) command.compile(ns) @@ -28,7 +28,7 @@ def test_command_compile_on_base_examples(tmp_path, src_yaml, _mirror): def test_errors(src_yaml, _mirror): src_yaml = pathlib.Path(src_yaml) expected = src_yaml.with_suffix(".err").read_text(encoding="utf8").strip() - ns = argparse.Namespace(input=src_yaml, output="/dev/null", target="osbuild") + ns = argparse.Namespace(input=src_yaml, output="/dev/null", target="osbuild", explain=False) with pytest.raises(Exception) as exception: command.compile(ns) assert expected in str(exception.value)