From 38a000c411fe9c2cd4ec555dbda77e442d689354 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 2 Oct 2024 15:35:42 +0200 Subject: [PATCH] otk: add file to variable substitution errors too This commit adds file context for errors from variable substitution too. --- src/otk/transform.py | 8 ++++++-- src/otk/tree.py | 3 ++- test/test_substitute_vars.py | 15 ++++++++++----- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/otk/transform.py b/src/otk/transform.py index 52876c87..4580e862 100644 --- a/src/otk/transform.py +++ b/src/otk/transform.py @@ -24,7 +24,7 @@ from .constant import NAME_VERSION, PREFIX, PREFIX_DEFINE, PREFIX_INCLUDE, PREFIX_OP, PREFIX_TARGET from .context import Context, validate_var_name from .error import ( - IncludeNotFoundError, + IncludeNotFoundError, OTKError, ParseError, ParseTypeError, ParseValueError, ParseDuplicatedYamlKeyError, TransformDirectiveTypeError, TransformDirectiveUnknownError, ) @@ -287,7 +287,11 @@ def substitute_vars(ctx: Context, state: State, data: str) -> Any: # return its value directly. if m := re.fullmatch(pattern, data): validate_var_name(m.group("name")) - return ctx.variable(m.group("name")) + try: + var = ctx.variable(m.group("name")) + except OTKError as exc: + raise exc.__class__(str(exc), state) + return var if matches := re.finditer(pattern, data): for m in matches: diff --git a/src/otk/tree.py b/src/otk/tree.py index 2fb5fa63..ed68a2ef 100644 --- a/src/otk/tree.py +++ b/src/otk/tree.py @@ -20,7 +20,8 @@ def wrapper(*args, **kwargs): if not isinstance(args[2], kind): # XXX: this needs state to give proper errors raise TransformDirectiveTypeError( - f"otk.define expects a {kind!r} as its argument but received a `{type(args[2])}`: `{args[2]!r}`") + f"otk.define expects a {kind!r} as its argument but " + f"received a `{type(args[2])}`: `{args[2]!r}`", args[1]) return function(*args, **kwargs) return wrapper diff --git a/test/test_substitute_vars.py b/test/test_substitute_vars.py index a310ff2c..64e156d5 100644 --- a/test/test_substitute_vars.py +++ b/test/test_substitute_vars.py @@ -47,7 +47,7 @@ def test_sub_var_multiple(): def test_sub_var_missing_var_in_context(): - state = State("") + state = State("foo.yaml") context = CommonContext() # toplevel expected_err = r"could not resolve 'a' as 'a' is not defined" @@ -60,7 +60,8 @@ def test_sub_var_missing_var_in_context(): substitute_vars(context, state, "${a.b}") # no subtree context.define("a", "foo") - expected_err = r"tried to look up 'a.b', but the value of prefix 'a' is not a dictionary but " + expected_err = r"foo.yaml: tried to look up 'a.b', but the value of " \ + "prefix 'a' is not a dictionary but " with pytest.raises(TransformVariableTypeError, match=expected_err): substitute_vars(context, state, "${a.b}") @@ -127,12 +128,16 @@ def test_substitute_vars(): def test_substitute_vars_unhappy(): - state = State("") + state = State("foo.yaml") ctx = CommonContext() ctx.define("dict", {}) - with pytest.raises(TransformDirectiveTypeError): + with pytest.raises(TransformDirectiveTypeError) as exc: substitute_vars(ctx, state, 1) + assert "foo.yaml: otk.define expects a as its argument but received a `" in str( + exc.value) - with pytest.raises(TransformDirectiveTypeError): + with pytest.raises(TransformDirectiveTypeError) as exc: substitute_vars(ctx, state, "a${dict}b") + assert "foo.yaml: string 'a${dict}b' resolves to an incorrect type, " \ + "expected int, float, or str but got dict" in str(exc.value)