From 4a42b1ad7fbcb55e096d60af25d992f2d8652a21 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Wed, 5 Feb 2025 11:58:50 +0100 Subject: [PATCH] Add intermediate named reference class (#155) --- src/curies/__init__.py | 2 ++ src/curies/api.py | 42 +++++++++++++++++++++++++++++++++++++++++- tests/test_api.py | 4 ++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/curies/__init__.py b/src/curies/__init__.py index 8e25bc7e..2f494319 100644 --- a/src/curies/__init__.py +++ b/src/curies/__init__.py @@ -5,6 +5,7 @@ DuplicatePrefixes, DuplicateURIPrefixes, DuplicateValueError, + NamableReference, NamedReference, Prefix, PrefixMap, @@ -39,6 +40,7 @@ "DuplicatePrefixes", "DuplicateURIPrefixes", "DuplicateValueError", + "NamableReference", "NamedReference", "Prefix", "PrefixMap", diff --git a/src/curies/api.py b/src/curies/api.py index 2c70f9dc..7faf02ba 100644 --- a/src/curies/api.py +++ b/src/curies/api.py @@ -45,6 +45,7 @@ "DuplicatePrefixes", "DuplicateURIPrefixes", "DuplicateValueError", + "NamableReference", "NamedReference", "Prefix", "PrefixMap", @@ -458,7 +459,46 @@ def from_curie( return cls.model_validate({"prefix": prefix, "identifier": identifier}, context=converter) -class NamedReference(Reference): +class NamableReference(Reference): + """A reference, maybe with a name.""" + + name: str | None = Field( + None, + description="The name of the entity referenced by this object's prefix and identifier, if exists.", + ) + + model_config = ConfigDict(frozen=True) + + @classmethod + def from_curie( # type:ignore + cls, + curie: str, + name: str | None = None, + *, + sep: str = ":", + converter: Converter | None = None, + ) -> NamableReference: + """Parse a CURIE string and populate a reference. + + :param curie: A string representation of a compact URI (CURIE) + :param name: The optional name of the reference + :param sep: The separator + :param converter: The converter to use as context when parsing + :return: A reference object + + >>> NamableReference.from_curie("chebi:1234") + NamableReference(prefix='chebi', identifier='1234', name=None) + + >>> NamableReference.from_curie("chebi:1234", "6-methoxy-2-octaprenyl-1,4-benzoquinone") + NamableReference(prefix='chebi', identifier='1234', name='6-methoxy-2-octaprenyl-1,4-benzoquinone') + """ + prefix, identifier = _split(curie, sep=sep) + return cls.model_validate( + {"prefix": prefix, "identifier": identifier, "name": name}, context=converter + ) + + +class NamedReference(NamableReference): """A reference with a name.""" name: str = Field( diff --git a/tests/test_api.py b/tests/test_api.py index dd7fa6eb..6cac9033 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -17,6 +17,7 @@ DuplicatePrefixes, DuplicateURIPrefixes, ExpansionError, + NamableReference, NamedReference, NoCURIEDelimiterError, PrefixStandardizationError, @@ -108,7 +109,9 @@ def test_named_set_membership(self) -> None: NamedReference.from_curie("a:2", "name2"), } self.assertIn(Reference.from_curie("a:1"), references) + self.assertIn(NamableReference.from_curie("a:1"), references) self.assertIn(NamedReference.from_curie("a:1", "name1"), references) + self.assertIn(NamableReference.from_curie("a:1", "name1"), references) # the following is a weird case, but shows how this works self.assertIn(NamedReference.from_curie("a:1", "name2"), references) @@ -117,6 +120,7 @@ def test_named_set_membership(self) -> None: Reference.from_curie("a:2"), } self.assertIn(Reference.from_curie("a:1"), references_2) + self.assertIn(NamableReference.from_curie("a:1", "name1"), references_2) self.assertIn(NamedReference.from_curie("a:1", "name1"), references_2)