Skip to content

Commit

Permalink
Implement named references (#118)
Browse files Browse the repository at this point in the history
This PR adds an extension of the `Reference` classes that has a name.
  • Loading branch information
cthoyt authored Oct 30, 2024
1 parent bf166bb commit 5e97fd2
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/curies/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
DuplicatePrefixes,
DuplicateURIPrefixes,
DuplicateValueError,
NamedReference,
Record,
Records,
Reference,
Expand Down Expand Up @@ -37,6 +38,7 @@
"Records",
"ReferenceTuple",
"Reference",
"NamedReference",
"DuplicateValueError",
"DuplicateURIPrefixes",
"DuplicatePrefixes",
Expand Down
36 changes: 36 additions & 0 deletions src/curies/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"Converter",
"Reference",
"ReferenceTuple",
"NamedReference",
"Record",
"Records",
"DuplicateValueError",
Expand Down Expand Up @@ -208,6 +209,16 @@ def __lt__(self, other: "Reference") -> bool:
"""Sort the reference lexically first by prefix, then by identifier."""
return self.pair < other.pair

def __hash__(self) -> int:
return hash((self.prefix, self.identifier))

def __eq__(self, other: Any) -> bool:
return (
isinstance(other, Reference)
and self.prefix == other.prefix
and self.identifier == other.identifier
)

@property
def curie(self) -> str:
"""Get the reference as a CURIE string.
Expand Down Expand Up @@ -240,6 +251,31 @@ def from_curie(cls, curie: str, *, sep: str = ":") -> "Reference":
return cls(prefix=prefix, identifier=identifier)


class NamedReference(Reference):
"""A reference with a name."""

name: str = Field(
..., description="The name of the entity referenced by this object's prefix and identifier."
)

model_config = ConfigDict(frozen=True)

@classmethod
def from_curie(cls, curie: str, name: str, *, sep: str = ":") -> "NamedReference": # type:ignore
"""Parse a CURIE string and populate a reference.
:param curie: A string representation of a compact URI (CURIE)
:param name: The name of the reference
:param sep: The separator
:return: A reference object
>>> NamedReference.from_curie("chebi:1234", "6-methoxy-2-octaprenyl-1,4-benzoquinone")
NamedReference(prefix='chebi', identifier='1234', name='6-methoxy-2-octaprenyl-1,4-benzoquinone')
"""
prefix, identifier = _split(curie, sep=sep)
return cls(prefix=prefix, identifier=identifier, name=name)


RecordKey = tuple[str, str, str, str]


Expand Down
19 changes: 19 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
DuplicatePrefixes,
DuplicateURIPrefixes,
ExpansionError,
NamedReference,
NoCURIEDelimiterError,
PrefixStandardizationError,
Record,
Expand Down Expand Up @@ -100,6 +101,24 @@ def test_set_membership(self):
self.assertNotIn(Reference.from_curie(":1234"), collection)
self.assertNotIn(Reference.from_curie("abc:"), collection)

def test_named_set_membership(self):
"""Test membership in sets of named references."""
references = {
NamedReference.from_curie("a:1", "name1"),
NamedReference.from_curie("a:2", "name2"),
}
self.assertIn(Reference.from_curie("a:1"), references)
self.assertIn(NamedReference.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)

references_2 = {
Reference.from_curie("a:1"),
Reference.from_curie("a:2"),
}
self.assertIn(Reference.from_curie("a:1"), references_2)
self.assertIn(NamedReference.from_curie("a:1", "name1"), references_2)


class TestAddRecord(unittest.TestCase):
"""Test adding records."""
Expand Down

0 comments on commit 5e97fd2

Please sign in to comment.