From 3d6103cc6490f7e24b126c6684e77b1476501dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Sch=C3=BCller?= Date: Tue, 8 Oct 2024 12:40:33 +0200 Subject: [PATCH] test: introduce new error with filereference use the new hidden attributes for nicer error messages --- src/otk/document.py | 9 +++++---- src/otk/transform.py | 12 +++++------- test/data/error/03-lonely-keys.err | 3 ++- test/data/error/12-two-lonely-keys.err | 3 +++ test/data/error/12-two-lonely-keys.yaml | 10 ++++++++++ 5 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 test/data/error/12-two-lonely-keys.err create mode 100644 test/data/error/12-two-lonely-keys.yaml diff --git a/src/otk/document.py b/src/otk/document.py index e8dbcc0a..bb49096f 100644 --- a/src/otk/document.py +++ b/src/otk/document.py @@ -9,12 +9,13 @@ from .transform import process_include from .traversal import State from .target import OSBuildTarget +from .utils import HiddenAttrDict log = logging.getLogger(__name__) class Omnifest: - _tree: dict[str, Any] + _tree: HiddenAttrDict[str, Any] _ctx: CommonContext _osbuild_ctx: OSBuildContext _target: str @@ -40,7 +41,7 @@ def __init__(self, path: pathlib.Path, target: str = "", *, warn_duplicated_defs self._tree = tree @classmethod - def ensure(cls, deserialized_data: dict[str, Any]) -> None: + def ensure(cls, deserialized_data: HiddenAttrDict[str, Any]) -> None: """Take a dictionary and ensure that its keys and values would be considered an Omnifest.""" @@ -50,10 +51,10 @@ def ensure(cls, deserialized_data: dict[str, Any]) -> None: raise ParseVersionError(f"omnifest must contain a key by the name of {NAME_VERSION!r}") # no toplevel keys without a target or an otk directive - targetless_keys = [key for key in deserialized_data + targetless_keys = [f" * \"{key}\" in {deserialized_data.get_attribute(key, 'src')}" for key in deserialized_data if not key.startswith(PREFIX)] if len(targetless_keys): - raise ParseError(f"otk file contains top-level keys {targetless_keys} without a target") + raise ParseError(f"otk file contains top-level keys without a target:\n{'\n'.join(targetless_keys)}") target_available = _targets(deserialized_data) if not target_available: diff --git a/src/otk/transform.py b/src/otk/transform.py index 4b7dca36..ebdba4cc 100644 --- a/src/otk/transform.py +++ b/src/otk/transform.py @@ -68,7 +68,7 @@ def _find_data(node_list, key): def _add_hidden_attributes(obj, key, key_data): line_number = key_data.start_mark.line + 1 column = key_data.start_mark.column + 1 - filename = key_data.start_mark.name + filename = os.path.relpath(key_data.start_mark.name, os.path.curdir) obj.set_attribute(key, "src", f"{filename}:{line_number}") obj.set_attribute(key, "filename", filename) obj.set_attribute(key, "linenumber", line_number) @@ -253,7 +253,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, explain: bool = False) -> dict: +def process_include(ctx: Context, state: State, path: pathlib.Path, explain: bool = False) -> HiddenAttrDict: """ Load a yaml file and send it to resolve() for processing. """ @@ -263,10 +263,8 @@ def process_include(ctx: Context, state: State, path: pathlib.Path, explain: boo path = (cur_path / pathlib.Path(path)).resolve() log.info("resolving %s", path) - 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) + 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: @@ -286,7 +284,7 @@ def process_include(ctx: Context, state: State, path: pathlib.Path, explain: boo if data is not None: return resolve(ctx, state.copy(path=path), data) - return {} + return HiddenAttrDict() def op(ctx: Context, state: State, tree: Any, key: str) -> Any: diff --git a/test/data/error/03-lonely-keys.err b/test/data/error/03-lonely-keys.err index a46b772e..3ddf1d9d 100644 --- a/test/data/error/03-lonely-keys.err +++ b/test/data/error/03-lonely-keys.err @@ -1 +1,2 @@ -otk file contains top-level keys ['targetless'] without a target +otk file contains top-level keys without a target: + * "targetless" in test/data/error/03-lonely-keys.yaml:6 diff --git a/test/data/error/12-two-lonely-keys.err b/test/data/error/12-two-lonely-keys.err new file mode 100644 index 00000000..8ab49163 --- /dev/null +++ b/test/data/error/12-two-lonely-keys.err @@ -0,0 +1,3 @@ +otk file contains top-level keys without a target: + * "targetless" in test/data/error/12-two-lonely-keys.yaml:6 + * "targetless2" in test/data/error/12-two-lonely-keys.yaml:9 diff --git a/test/data/error/12-two-lonely-keys.yaml b/test/data/error/12-two-lonely-keys.yaml new file mode 100644 index 00000000..c2255a10 --- /dev/null +++ b/test/data/error/12-two-lonely-keys.yaml @@ -0,0 +1,10 @@ +otk.version: 1 + +otk.target.osbuild.foo: + a: 1 + +targetless: + key: value + +targetless2: + key2: value2