From 78af0b5469a7acbc4fd366085ed3f0899548e588 Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 10:37:34 +0200 Subject: [PATCH 01/19] Use correct field name in tests --- test/hermes_test/model/types/test_ld_context.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index 03fd1cf3..4964c062 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -18,13 +18,13 @@ def ctx(): def test_ctx(): ctx = ContextPrefix(["u1", {"2": "u2"}]) - assert ctx.prefix[None] == "u1" - assert ctx.prefix["2"] == "u2" + assert ctx.context[None] == "u1" + assert ctx.context["2"] == "u2" def test_codemeta_prefix(ctx): """Default vocabulary in context has the correct base IRI.""" - assert ctx.prefix[None] == "https://codemeta.github.io/terms/" + assert ctx.context[None] == "https://codemeta.github.io/terms/" def test_get_codemeta_item(ctx): From 4cb2af04ca5eb0caab54d22558444c6883b55b55 Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 11:11:36 +0200 Subject: [PATCH 02/19] Mark test as expectedly failing due to #419 --- test/hermes_test/model/types/test_ld_context.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index 4964c062..4481769e 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -22,6 +22,8 @@ def test_ctx(): assert ctx.context["2"] == "u2" +@pytest.mark.xfail(raises=AssertionError, reason="Currently, the wrong CodeMeta IRI is used in the implementation: " + "https://github.com/softwarepub/hermes/issues/419") def test_codemeta_prefix(ctx): """Default vocabulary in context has the correct base IRI.""" assert ctx.context[None] == "https://codemeta.github.io/terms/" From 32c41f100e3006155434e04142c619971c10baa5 Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 11:24:39 +0200 Subject: [PATCH 03/19] Make passing xfail tests fail test suite --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index d28f60fd..49b64ee7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -111,7 +111,9 @@ flake8 = "poetry run flake8 ./test/ ./src/ --count --select=E9,F63,F7,F82 --stat [tool.pytest.ini_options] +minversion = "6.0" norecursedirs = "docs/*" +xfail_strict = true testpaths = [ "test" ] From 9e7ecf587917a80c1af544cdd9f410d8e1c14089 Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 11:27:09 +0200 Subject: [PATCH 04/19] Mark another test as expectedly failing due to #419 --- test/hermes_test/model/types/test_ld_context.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index 4481769e..d4620f49 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -29,6 +29,9 @@ def test_codemeta_prefix(ctx): assert ctx.context[None] == "https://codemeta.github.io/terms/" +@pytest.mark.xfail(raises=AssertionError, reason="Currently, the wrong CodeMeta IRI is used in the implementation," + "so expanding terms doesn't work correctly, see " + "https://github.com/softwarepub/hermes/issues/419") def test_get_codemeta_item(ctx): """Context returns fully expanded terms for default vocabulary in the context.""" item = ctx["maintainer"] From d4e23665b51fb40053306ec1634fb03b6aefc941 Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 12:48:48 +0200 Subject: [PATCH 05/19] Parametrize xfailing test getting CodeMeta items, re-format --- .../model/types/test_ld_context.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index d4620f49..400923c2 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -22,19 +22,25 @@ def test_ctx(): assert ctx.context["2"] == "u2" -@pytest.mark.xfail(raises=AssertionError, reason="Currently, the wrong CodeMeta IRI is used in the implementation: " - "https://github.com/softwarepub/hermes/issues/419") +@pytest.mark.xfail( + raises=AssertionError, + reason="Currently, the wrong CodeMeta IRI is used in the implementation: " + "https://github.com/softwarepub/hermes/issues/419", +) def test_codemeta_prefix(ctx): """Default vocabulary in context has the correct base IRI.""" assert ctx.context[None] == "https://codemeta.github.io/terms/" -@pytest.mark.xfail(raises=AssertionError, reason="Currently, the wrong CodeMeta IRI is used in the implementation," - "so expanding terms doesn't work correctly, see " - "https://github.com/softwarepub/hermes/issues/419") -def test_get_codemeta_item(ctx): +@pytest.mark.xfail( + raises=AssertionError, + reason="Currently, the wrong CodeMeta IRI is used in the implementation, so expanding terms doesn't work correctly, " + "see https://github.com/softwarepub/hermes/issues/419", +) +@pytest.mark.parametrize("compacted", ["maintainer", (None, "maintainer")]) +def test_get_item_from_default_vocabulary_pass(ctx, compacted): """Context returns fully expanded terms for default vocabulary in the context.""" - item = ctx["maintainer"] + item = ctx[compacted] assert item == "https://codemeta.github.io/terms/maintainer" From 1f4501c73e3734e4e3c25ad878e3578a2fe30fee Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 12:51:01 +0200 Subject: [PATCH 06/19] Test correct inputs for getting expanded terms from prefixed vocabularies --- test/hermes_test/model/types/test_ld_context.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index 400923c2..e328b539 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -52,10 +52,18 @@ def test_get_item_from_default_vocabulary_pass(ctx, compacted): "hermes:semanticVersion", "https://schema.software-metadata.pub/hermes-content/1.0/semanticVersion", # TODO: Change on #393 fix ), + (("schema", "Organization"), "http://schema.org/Organization"), + ( + ("hermes", "semanticVersion"), + "https://schema.software-metadata.pub/hermes-content/1.0/semanticVersion", + ), # TODO: Change on #393 fix ], ) -def test_get_prefixed_items(ctx, compacted, expanded): - """Context returns fully expanded terms for prefixed vocabularies in the context.""" +def test_get_item_from_prefixed_vocabulary_pass(ctx, compacted, expanded): + """ + Context returns fully expanded terms for prefixed vocabularies in the context, + for all accepted parameter formats. + """ item = ctx[compacted] assert item == expanded From 5512f7ece2f01f0447c0133a11a6a80e6b378936 Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 12:52:48 +0200 Subject: [PATCH 07/19] Test raises when prefix doesn't exist for compacted input --- .../model/types/test_ld_context.py | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index e328b539..a0e2dd5e 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -68,9 +68,24 @@ def test_get_item_from_prefixed_vocabulary_pass(ctx, compacted, expanded): assert item == expanded -def test_get_protocol_items_pass(ctx): - item = ctx["https://schema.org/Organisation"] - assert item == "https://schema.org/Organisation" +@pytest.mark.parametrize( + "not_exist", + [ + "https://foo.bar/baz", + "foobar:baz", + ("foobar", "baz"), + ], +) +def test_get_item_from_prefixed_vocabulary_raises_on_prefix_not_exist(ctx, not_exist): + """ + Tests that an exception is raised when trying to get compacted items for which there is no + prefixed vocabulary in the context, and that the raised exception is not raised due to side effects. + """ + with pytest.raises(Exception) as e: # FIXME: Replace with custom error + ctx[not_exist] + assert "cannot access local variable" not in str( + e.value + ) def test_get_protocol_items_fail(ctx): From 3262691e2132bd8b695c39c2c16eb9354c002c42 Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 12:53:13 +0200 Subject: [PATCH 08/19] Test raises when term doesn't exist for compacted input --- .../model/types/test_ld_context.py | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index a0e2dd5e..02595d74 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -88,10 +88,27 @@ def test_get_item_from_prefixed_vocabulary_raises_on_prefix_not_exist(ctx, not_e ) -def test_get_protocol_items_fail(ctx): - with pytest.raises(Exception) as e: - ctx["https://foo.bar/baz"] - assert "cannot access local variable" not in str(e.value) # FIXME: Replace with custom error +@pytest.mark.parametrize( + "not_exist", + [ + "baz", + "hermes:baz", + "schema:baz", + (None, "baz"), + ("hermes", "baz"), + ("schema", "baz"), + ], +) +def test_get_item_item_from_prefixed_vocabulary_raises_on_term_not_exist(ctx, not_exist): + """ + Tests that an exception is raised when trying to get compacted items for which the vocabulary exists, + but doesn't contain the requested term, and that the raised exception is not raised due to side effects. + """ + with pytest.raises(Exception) as e: # FIXME: Replace with custom error + ctx[not_exist] + assert "cannot access local variable" not in str( + e.value + ) @pytest.mark.parametrize( From 241f914af1e1932db54cda6ba7dff8c3d1837d27 Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 12:59:13 +0200 Subject: [PATCH 09/19] Remove duplicate tests, and not-to-be-tested parameters - Remove duplicate tests of __get_item__ on tuples - This includes test parameters that we decided not to test, as the API doesn't see them as valid (list inputs), although they're supported at runtime. - Related: https://github.com/softwarepub/hermes/pull/403#discussion_r2245059253 --- test/hermes_test/model/types/test_ld_context.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index 02595d74..e44cace5 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -111,20 +111,6 @@ def test_get_item_item_from_prefixed_vocabulary_raises_on_term_not_exist(ctx, no ) -@pytest.mark.parametrize( - "compacted,expanded", - [ - ([None, "maintainer"], "https://codemeta.github.io/terms/maintainer"), - (["schema", "Organization"], "http://schema.org/Organization"), - ((None, "maintainer"), "https://codemeta.github.io/terms/maintainer"), - (("schema", "Organization"), "http://schema.org/Organization"), - ], -) -def test_get_valid_non_str_items(ctx, compacted, expanded): - """Context returns fully expanded terms for valid non-string inputs.""" - assert ctx[compacted] == expanded - - @pytest.mark.parametrize( "non_str,error_type", [(0, TypeError), (None, TypeError), ([], ValueError), ({"foo"}, ValueError)], From ebe92008dd5b20f51c29ba82c3acee1986a6d619 Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 13:19:06 +0200 Subject: [PATCH 10/19] Improve reporting unexpected exceptions and add tests for expanded inputs --- .../model/types/test_ld_context.py | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index e44cace5..33137b7c 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -71,7 +71,6 @@ def test_get_item_from_prefixed_vocabulary_pass(ctx, compacted, expanded): @pytest.mark.parametrize( "not_exist", [ - "https://foo.bar/baz", "foobar:baz", ("foobar", "baz"), ], @@ -83,9 +82,8 @@ def test_get_item_from_prefixed_vocabulary_raises_on_prefix_not_exist(ctx, not_e """ with pytest.raises(Exception) as e: # FIXME: Replace with custom error ctx[not_exist] - assert "cannot access local variable" not in str( - e.value - ) + if any(s in str(e.value) for s in ["cannot access local variable", "referenced before assignment"]): + pytest.fail("Unexpected exception raised not due to the expected cause.") @pytest.mark.parametrize( @@ -99,16 +97,33 @@ def test_get_item_from_prefixed_vocabulary_raises_on_prefix_not_exist(ctx, not_e ("schema", "baz"), ], ) -def test_get_item_item_from_prefixed_vocabulary_raises_on_term_not_exist(ctx, not_exist): +def test_get_item_from_prefixed_vocabulary_raises_on_term_not_exist(ctx, not_exist): """ Tests that an exception is raised when trying to get compacted items for which the vocabulary exists, but doesn't contain the requested term, and that the raised exception is not raised due to side effects. """ with pytest.raises(Exception) as e: # FIXME: Replace with custom error ctx[not_exist] - assert "cannot access local variable" not in str( - e.value - ) + if any(s in str(e.value) for s in ["cannot access local variable", "referenced before assignment"]): + pytest.fail("Unexpected exception raised not due to the expected cause.") + +@pytest.mark.parametrize("expanded", ["https://schema.org/Organisation", "https://schema.software-metadata.pub/hermes-content/1.0/semanticVersion"]) +def test_get_item_from_expanded_pass(ctx, expanded): + """ + Tests that getting items via their fully expanded terms works as expected. + """ + assert ctx[expanded] == expanded + + +def test_get_item_from_expanded_fail(ctx): + """ + Tests that context raises on unsupported expanded term input. + """ + with pytest.raises(Exception) as e: + ctx["https://foo.bar/baz"] + if any(s in str(e.value) for s in ["cannot access local variable", "referenced before assignment"]): + pytest.fail("Unexpected exception raised not due to the expected cause.") + @pytest.mark.parametrize( From de0f24a9f157bd54d77d7a420865910e154fc60d Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 13:29:26 +0200 Subject: [PATCH 11/19] Add test for unimplemented and undecided functionality --- .../model/types/test_ld_context.py | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index 33137b7c..b4c9eee7 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -35,7 +35,7 @@ def test_codemeta_prefix(ctx): @pytest.mark.xfail( raises=AssertionError, reason="Currently, the wrong CodeMeta IRI is used in the implementation, so expanding terms doesn't work correctly, " - "see https://github.com/softwarepub/hermes/issues/419", + "see https://github.com/softwarepub/hermes/issues/419", ) @pytest.mark.parametrize("compacted", ["maintainer", (None, "maintainer")]) def test_get_item_from_default_vocabulary_pass(ctx, compacted): @@ -82,7 +82,10 @@ def test_get_item_from_prefixed_vocabulary_raises_on_prefix_not_exist(ctx, not_e """ with pytest.raises(Exception) as e: # FIXME: Replace with custom error ctx[not_exist] - if any(s in str(e.value) for s in ["cannot access local variable", "referenced before assignment"]): + if any( + s in str(e.value) + for s in ["cannot access local variable", "referenced before assignment"] + ): pytest.fail("Unexpected exception raised not due to the expected cause.") @@ -104,15 +107,33 @@ def test_get_item_from_prefixed_vocabulary_raises_on_term_not_exist(ctx, not_exi """ with pytest.raises(Exception) as e: # FIXME: Replace with custom error ctx[not_exist] - if any(s in str(e.value) for s in ["cannot access local variable", "referenced before assignment"]): + if any( + s in str(e.value) + for s in ["cannot access local variable", "referenced before assignment"] + ): pytest.fail("Unexpected exception raised not due to the expected cause.") -@pytest.mark.parametrize("expanded", ["https://schema.org/Organisation", "https://schema.software-metadata.pub/hermes-content/1.0/semanticVersion"]) + +@pytest.mark.parametrize( + "expanded", + [ + "https://codemeta.github.io/terms/maintainer", + "https://schema.org/Organisation", + "https://schema.software-metadata.pub/hermes-content/1.0/semanticVersion", + ], +) +@pytest.mark.xfail( + raises=NotImplementedError, + reason="Passing back expanded terms on their input if they are valid in the context " + "is not yet implemented (or decided).", +) def test_get_item_from_expanded_pass(ctx, expanded): """ Tests that getting items via their fully expanded terms works as expected. """ - assert ctx[expanded] == expanded + with pytest.raises(Exception) as e: + assert ctx[expanded] == expanded + raise NotImplementedError def test_get_item_from_expanded_fail(ctx): @@ -121,11 +142,13 @@ def test_get_item_from_expanded_fail(ctx): """ with pytest.raises(Exception) as e: ctx["https://foo.bar/baz"] - if any(s in str(e.value) for s in ["cannot access local variable", "referenced before assignment"]): + if any( + s in str(e.value) + for s in ["cannot access local variable", "referenced before assignment"] + ): pytest.fail("Unexpected exception raised not due to the expected cause.") - @pytest.mark.parametrize( "non_str,error_type", [(0, TypeError), (None, TypeError), ([], ValueError), ({"foo"}, ValueError)], From 3793e7ca4999652c3bf5675a5660bb283289655e Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 13:32:30 +0200 Subject: [PATCH 12/19] Report unexpectedly raised exception --- test/hermes_test/model/types/test_ld_context.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index b4c9eee7..9213e257 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -86,7 +86,7 @@ def test_get_item_from_prefixed_vocabulary_raises_on_prefix_not_exist(ctx, not_e s in str(e.value) for s in ["cannot access local variable", "referenced before assignment"] ): - pytest.fail("Unexpected exception raised not due to the expected cause.") + pytest.fail(f"Unexpected exception raised not due to the expected cause: {e.value}.") @pytest.mark.parametrize( @@ -111,7 +111,7 @@ def test_get_item_from_prefixed_vocabulary_raises_on_term_not_exist(ctx, not_exi s in str(e.value) for s in ["cannot access local variable", "referenced before assignment"] ): - pytest.fail("Unexpected exception raised not due to the expected cause.") + pytest.fail(f"Unexpected exception raised not due to the expected cause: {e.value}.") @pytest.mark.parametrize( @@ -146,7 +146,7 @@ def test_get_item_from_expanded_fail(ctx): s in str(e.value) for s in ["cannot access local variable", "referenced before assignment"] ): - pytest.fail("Unexpected exception raised not due to the expected cause.") + pytest.fail(f"Unexpected exception raised not due to the expected cause: {e.value}.") @pytest.mark.parametrize( From 6c4a523bf43806b3ea38e610d965c6f6d499443e Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 16:00:30 +0200 Subject: [PATCH 13/19] Raise new error when getting term whose prafix is not in context --- src/hermes/model/types/ld_context.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/hermes/model/types/ld_context.py b/src/hermes/model/types/ld_context.py index 9bb8209a..64aac256 100644 --- a/src/hermes/model/types/ld_context.py +++ b/src/hermes/model/types/ld_context.py @@ -5,6 +5,7 @@ # SPDX-FileContributor: Michael Meinel # SPDX-FileContributor: Stephan Druskat +from hermes.model.error import HermesContextError CODEMETA_PREFIX = "https://doi.org/10.5063/schema/codemeta-2.0" CODEMETA_CONTEXT = [CODEMETA_PREFIX] @@ -91,10 +92,12 @@ def __getitem__(self, compressed_term: str | tuple) -> str: else: prefix, term = None, compressed_term - if prefix in self.context: - iri = self.context[prefix] + term + try: + base_iri = self.context[prefix] + except KeyError as ke: + raise HermesContextError(prefix) from ke - return iri + return base_iri + term iri_map = ContextPrefix(ALL_CONTEXTS) From 124231175e4f045eca36647d8082166524aac214 Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 16:03:26 +0200 Subject: [PATCH 14/19] Test that non-existent prefix raises error on getting item --- .../model/types/test_ld_context.py | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index 9213e257..ec62b808 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -10,6 +10,8 @@ ALL_CONTEXTS, ) +from hermes.model.error import HermesContextError + @pytest.fixture def ctx(): @@ -69,24 +71,25 @@ def test_get_item_from_prefixed_vocabulary_pass(ctx, compacted, expanded): @pytest.mark.parametrize( - "not_exist", + "prefix,not_exist", [ - "foobar:baz", - ("foobar", "baz"), + ("foobar", item) + for item in [ + "foobar:baz", + ("foobar", "baz"), + ] ], ) -def test_get_item_from_prefixed_vocabulary_raises_on_prefix_not_exist(ctx, not_exist): +def test_get_item_from_prefixed_vocabulary_raises_on_prefix_not_exist( + ctx, prefix, not_exist +): """ Tests that an exception is raised when trying to get compacted items for which there is no - prefixed vocabulary in the context, and that the raised exception is not raised due to side effects. + prefixed vocabulary in the context. """ - with pytest.raises(Exception) as e: # FIXME: Replace with custom error - ctx[not_exist] - if any( - s in str(e.value) - for s in ["cannot access local variable", "referenced before assignment"] - ): - pytest.fail(f"Unexpected exception raised not due to the expected cause: {e.value}.") + with pytest.raises(HermesContextError) as hce: + _ = ctx[not_exist] + assert str(hce.value) == prefix @pytest.mark.parametrize( @@ -146,7 +149,9 @@ def test_get_item_from_expanded_fail(ctx): s in str(e.value) for s in ["cannot access local variable", "referenced before assignment"] ): - pytest.fail(f"Unexpected exception raised not due to the expected cause: {e.value}.") + pytest.fail( + f"Unexpected exception raised not due to the expected cause: {e.value}." + ) @pytest.mark.parametrize( From 9621e77878c10324784e112322d61a6e569a915e Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 16:13:08 +0200 Subject: [PATCH 15/19] Add xfailing test for returning only existing terms from given vocabulary --- .../model/types/test_ld_context.py | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index ec62b808..44194f43 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -93,28 +93,35 @@ def test_get_item_from_prefixed_vocabulary_raises_on_prefix_not_exist( @pytest.mark.parametrize( - "not_exist", + "term,not_exist", [ - "baz", - "hermes:baz", - "schema:baz", - (None, "baz"), - ("hermes", "baz"), - ("schema", "baz"), + ("baz", item) + for item in [ + "baz", + "hermes:baz", + "schema:baz", + (None, "baz"), + ("hermes", "baz"), + ("schema", "baz"), + ] ], ) -def test_get_item_from_prefixed_vocabulary_raises_on_term_not_exist(ctx, not_exist): +@pytest.mark.xfail( + raises=NotImplementedError, + reason="Not yet implemented/decided: Check if terms exist in given vocabulary.", +) +def test_get_item_from_prefixed_vocabulary_raises_on_term_not_exist( + ctx, term, not_exist +): """ Tests that an exception is raised when trying to get compacted items for which the vocabulary exists, - but doesn't contain the requested term, and that the raised exception is not raised due to side effects. + but doesn't contain the requested term. """ - with pytest.raises(Exception) as e: # FIXME: Replace with custom error - ctx[not_exist] - if any( - s in str(e.value) - for s in ["cannot access local variable", "referenced before assignment"] - ): - pytest.fail(f"Unexpected exception raised not due to the expected cause: {e.value}.") + with pytest.raises(HermesContextError) as hce: + _ = ctx[not_exist] + with pytest.raises(Exception): + assert str(hce.value) == term + raise NotImplementedError @pytest.mark.parametrize( @@ -128,7 +135,7 @@ def test_get_item_from_prefixed_vocabulary_raises_on_term_not_exist(ctx, not_exi @pytest.mark.xfail( raises=NotImplementedError, reason="Passing back expanded terms on their input if they are valid in the context " - "is not yet implemented (or decided).", + "is not yet implemented (or decided).", ) def test_get_item_from_expanded_pass(ctx, expanded): """ From 9a980b4783924af4e086980dd5bec6a4a498e5b2 Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 16:27:12 +0200 Subject: [PATCH 16/19] Test raising context errors where implemented --- .../model/types/test_ld_context.py | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index 44194f43..a6f61b13 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -150,15 +150,8 @@ def test_get_item_from_expanded_fail(ctx): """ Tests that context raises on unsupported expanded term input. """ - with pytest.raises(Exception) as e: + with pytest.raises(HermesContextError) as e: ctx["https://foo.bar/baz"] - if any( - s in str(e.value) - for s in ["cannot access local variable", "referenced before assignment"] - ): - pytest.fail( - f"Unexpected exception raised not due to the expected cause: {e.value}." - ) @pytest.mark.parametrize( @@ -175,20 +168,31 @@ def test_get_non_str_item_fail(ctx, non_str, error_type): "item", [ "", - "fooBar", + pytest.param( + "fooBar", + marks=pytest.mark.xfail( + reason="Not yet implemented/decided: Check if terms exist in given vocabulary." + ), + ), [0, "foo"], (0, "foo"), {"foo": "bar", "baz": "foo"}, - "schema:fooBar", - "hermes:fooBar", + pytest.param( + "schema:fooBar", + marks=pytest.mark.xfail( + reason="Not yet implemented/decided: Check if terms exist in given vocabulary." + ), + ), + pytest.param( + "hermes:fooBar", + marks=pytest.mark.xfail( + reason="Not yet implemented/decided: Check if terms exist in given vocabulary." + ), + ), "codemeta:maintainer", # Prefixed CodeMeta doesn't exist in context - # Even a dict with valid terms should fail, as it is unclear what to expect - {None: "maintainer", "schema": "Organization"}, ], ) def test_get_item_validate_fail(ctx, item): - """Context raises on terms that don't exist in the context.""" - with pytest.raises( - Exception - ): # FIXME: Replace with custom error, e.g., hermes.model.errors.InvalidTermException + """Context raises on theoretically valid compressed terms that don't exist in the context.""" + with pytest.raises(HermesContextError): ctx[item] From b808152b2d720ab6f7c0a0ab50ff83ee00c8a349 Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 16:34:38 +0200 Subject: [PATCH 17/19] Raise context error on empty string, reformat --- src/hermes/model/types/ld_context.py | 45 ++++++++++++++++++---------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/hermes/model/types/ld_context.py b/src/hermes/model/types/ld_context.py index 64aac256..3d60bb41 100644 --- a/src/hermes/model/types/ld_context.py +++ b/src/hermes/model/types/ld_context.py @@ -16,16 +16,26 @@ PROV_PREFIX = "http://www.w3.org/ns/prov#" PROV_CONTEXT = [{"prov": PROV_PREFIX}] -HERMES_RT_PREFIX = 'https://schema.software-metadata.pub/hermes-runtime/1.0/' -HERMES_RT_CONTEXT = [{'hermes-rt': HERMES_RT_PREFIX}] -HERMES_CONTENT_CONTEXT = [{'hermes': 'https://schema.software-metadata.pub/hermes-content/1.0/'}] +HERMES_RT_PREFIX = "https://schema.software-metadata.pub/hermes-runtime/1.0/" +HERMES_RT_CONTEXT = [{"hermes-rt": HERMES_RT_PREFIX}] +HERMES_CONTENT_CONTEXT = [ + {"hermes": "https://schema.software-metadata.pub/hermes-content/1.0/"} +] HERMES_CONTEXT = [{**HERMES_RT_CONTEXT[0], **HERMES_CONTENT_CONTEXT[0]}] -HERMES_BASE_CONTEXT = [*CODEMETA_CONTEXT, {**SCHEMA_ORG_CONTEXT[0], **HERMES_CONTENT_CONTEXT[0]}] -HERMES_PROV_CONTEXT = [{**SCHEMA_ORG_CONTEXT[0], **HERMES_RT_CONTEXT[0], **PROV_CONTEXT[0]}] +HERMES_BASE_CONTEXT = [ + *CODEMETA_CONTEXT, + {**SCHEMA_ORG_CONTEXT[0], **HERMES_CONTENT_CONTEXT[0]}, +] +HERMES_PROV_CONTEXT = [ + {**SCHEMA_ORG_CONTEXT[0], **HERMES_RT_CONTEXT[0], **PROV_CONTEXT[0]} +] -ALL_CONTEXTS = [*CODEMETA_CONTEXT, {**SCHEMA_ORG_CONTEXT[0], **PROV_CONTEXT[0], **HERMES_CONTEXT[0]}] +ALL_CONTEXTS = [ + *CODEMETA_CONTEXT, + {**SCHEMA_ORG_CONTEXT[0], **PROV_CONTEXT[0], **HERMES_CONTEXT[0]}, +] class ContextPrefix: @@ -38,6 +48,7 @@ class ContextPrefix: arbitrary strings used to prefix terms from a specific vocabulary to their respective vocabulary IRI strings.; - as a dict mapping prefixes to vocabulary IRIs, where the default vocabulary has a prefix of None. """ + def __init__(self, vocabularies: list[str | dict]): """ @param vocabularies: A list of linked data vocabularies. Items can be vocabulary base IRI strings and/or @@ -55,11 +66,13 @@ def __init__(self, vocabularies: list[str | dict]): if isinstance(vocab, str): vocab = {None: vocab} - self.context.update({ - prefix: base_iri - for prefix, base_iri in vocab.items() - if isinstance(base_iri, str) - }) + self.context.update( + { + prefix: base_iri + for prefix, base_iri in vocab.items() + if isinstance(base_iri, str) + } + ) def __getitem__(self, compressed_term: str | tuple) -> str: """ @@ -85,12 +98,14 @@ def __getitem__(self, compressed_term: str | tuple) -> str: """ if not isinstance(compressed_term, str): prefix, term = compressed_term - elif ':' in compressed_term: - prefix, term = compressed_term.split(':', 1) - if term.startswith('://'): + elif ":" in compressed_term: + prefix, term = compressed_term.split(":", 1) + if term.startswith("://"): prefix, term = True, compressed_term - else: + elif compressed_term != "": prefix, term = None, compressed_term + else: + raise HermesContextError(compressed_term) try: base_iri = self.context[prefix] From 0d61163ef944d58aa08174af429b55bf82dfd82f Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 16:41:32 +0200 Subject: [PATCH 18/19] Pacify flake8 --- test/hermes_test/model/types/test_ld_context.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/hermes_test/model/types/test_ld_context.py b/test/hermes_test/model/types/test_ld_context.py index a6f61b13..2c155b6f 100644 --- a/test/hermes_test/model/types/test_ld_context.py +++ b/test/hermes_test/model/types/test_ld_context.py @@ -36,8 +36,8 @@ def test_codemeta_prefix(ctx): @pytest.mark.xfail( raises=AssertionError, - reason="Currently, the wrong CodeMeta IRI is used in the implementation, so expanding terms doesn't work correctly, " - "see https://github.com/softwarepub/hermes/issues/419", + reason="Currently, the wrong CodeMeta IRI is used in the implementation, so expanding terms doesn't work correctly," + " see https://github.com/softwarepub/hermes/issues/419", ) @pytest.mark.parametrize("compacted", ["maintainer", (None, "maintainer")]) def test_get_item_from_default_vocabulary_pass(ctx, compacted): @@ -141,7 +141,7 @@ def test_get_item_from_expanded_pass(ctx, expanded): """ Tests that getting items via their fully expanded terms works as expected. """ - with pytest.raises(Exception) as e: + with pytest.raises(Exception): assert ctx[expanded] == expanded raise NotImplementedError @@ -150,7 +150,7 @@ def test_get_item_from_expanded_fail(ctx): """ Tests that context raises on unsupported expanded term input. """ - with pytest.raises(HermesContextError) as e: + with pytest.raises(HermesContextError): ctx["https://foo.bar/baz"] From e9ca17599c64d56f33af45ff2ffe37674b327676 Mon Sep 17 00:00:00 2001 From: Stephan Druskat Date: Thu, 7 Aug 2025 16:44:38 +0200 Subject: [PATCH 19/19] Satisfy REUSE --- test/hermes_test/model/types/test_pyld_util.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/hermes_test/model/types/test_pyld_util.py b/test/hermes_test/model/types/test_pyld_util.py index fa4e539d..3117ad04 100644 --- a/test/hermes_test/model/types/test_pyld_util.py +++ b/test/hermes_test/model/types/test_pyld_util.py @@ -1,3 +1,8 @@ +# SPDX-FileCopyrightText: 2025 German Aerospace Center (DLR) +# SPDX-FileContributor: Michael Meinel +# +# SPDX-License-Identifier: Apache-2.0 + import pytest from hermes.model.types import pyld_util