Skip to content

Commit

Permalink
Correctly handle unspecified synonym scope (#328)
Browse files Browse the repository at this point in the history
1. Switch default synonym specificity to "related" instead of "exact"
since that's what happens when you use ROBOT to convert something like
`synonym: "s" []`
2. Switch to automatically output a specificity if a type is available,
since `synonym: "s" <type> []` is not allowed
  • Loading branch information
cthoyt authored Jan 19, 2025
1 parent e275ab2 commit eb88b1d
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 24 deletions.
8 changes: 4 additions & 4 deletions src/pyobo/struct/functional/macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,13 +221,13 @@ class IsCyclic(BooleanAnnotationMacro):
class SynonymMacro(Macro):
"""A macro for synonym assertion.
You can just make a quick assertion, which defaults to exact:
You can just make a quick assertion, which defaults to ``RELATED``:
>>> SynonymMacro("hgnc:16793", "ULBP4").to_funowl()
'AnnotationAssertion(oboInOwl:hasExactSynonym hgnc:16793 "ULBP4")'
'AnnotationAssertion(oboInOwl:hasRelatedSynonym hgnc:16793 "ULBP4")'
You can make the predicate more explicit either with OBO-style
scoping (EXACT, BROAD, NARROW, CLOSE) or a CURIE/:class:`curies.Reference`/URIRef
scoping (``EXACT``, ``BROAD``, ``NARROW``, ``RELATED``) or a CURIE/:class:`curies.Reference`/URIRef
>>> SynonymMacro("hgnc:16793", "ULBP4", "EXACT").to_funowl()
'AnnotationAssertion(oboInOwl:hasExactSynonym hgnc:16793 "ULBP4")'
Expand Down Expand Up @@ -257,7 +257,7 @@ def __init__(
if synonym_type is not None:
annotations.append(f.Annotation(HAS_SYNONYM_TYPE, synonym_type))
if scope is None:
scope = v.has_exact_synonym
scope = v.has_related_synonym
elif isinstance(scope, str) and scope.upper() in t.get_args(v.SynonymScope):
scope = v.synonym_scopes[scope.upper()] # type:ignore[index]
super().__init__(
Expand Down
32 changes: 22 additions & 10 deletions src/pyobo/struct/struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@

logger = logging.getLogger(__name__)

DEFAULT_SPECIFICITY: _cv.SynonymScope = "EXACT"
#: This is what happens if no specificity is given
DEFAULT_SPECIFICITY: _cv.SynonymScope = "RELATED"

#: Columns in the SSSOM dataframe
SSSOM_DF_COLUMNS = [
Expand Down Expand Up @@ -163,17 +164,28 @@ def _fp(
) -> str:
if synonym_typedefs is None:
synonym_typedefs = {}
std = _synonym_typedef_warn(ontology_prefix, self.type, synonym_typedefs)
if std is not None and std.specificity is not None:
specificity = std.specificity
elif self.specificity:
specificity = self.specificity
else:
specificity = DEFAULT_SPECIFICITY
x = f'"{self._escape(self.name)}" {specificity}'

x = f'"{self._escape(self.name)}"'

# Add on the specificity, e.g., EXACT
synonym_typedef = _synonym_typedef_warn(ontology_prefix, self.type, synonym_typedefs)
if synonym_typedef is not None and synonym_typedef.specificity is not None:
x = f"{x} {synonym_typedef.specificity}"
elif self.specificity is not None:
x = f"{x} {self.specificity}"
elif self.type is not None:
# it's not valid to have a synonym type without a specificity,
# so automatically assign one if we'll need it
x = f"{x} {DEFAULT_SPECIFICITY}"

# Add on the synonym type, if exists
if self.type is not None:
x = f"{x} {reference_escape(self.type, ontology_prefix=ontology_prefix)}"
return f"{x} [{comma_separate_references(self.provenance)}]"

# the provenance list is required, even if it's empty :/
x = f"{x} [{comma_separate_references(self.provenance)}]"

return x

@staticmethod
def _escape(s: str) -> str:
Expand Down
12 changes: 6 additions & 6 deletions tests/test_struct/test_obo/test_struct_term.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,12 +382,12 @@ def test_9_append_exact_synonym(self) -> None:
[Term]
id: GO:0050069
name: lysine dehydrogenase activity
synonym: "L-lysine:NAD+ oxidoreductase" EXACT []
synonym: "L-lysine:NAD+ oxidoreductase" []
""",
ofn="""
Declaration(Class(GO:0050069))
AnnotationAssertion(rdfs:label GO:0050069 "lysine dehydrogenase activity")
AnnotationAssertion(oboInOwl:hasExactSynonym GO:0050069 "L-lysine:NAD+ oxidoreductase")
AnnotationAssertion(oboInOwl:hasRelatedSynonym GO:0050069 "L-lysine:NAD+ oxidoreductase")
""",
)

Expand Down Expand Up @@ -450,12 +450,12 @@ def test_9_provenance_and_type(self) -> None:
[Term]
id: GO:0050069
name: lysine dehydrogenase activity
synonym: "L-lysine:NAD+ oxidoreductase" EXACT OMO:1234567 [orcid:0000-0003-4423-4370]
synonym: "L-lysine:NAD+ oxidoreductase" RELATED OMO:1234567 [orcid:0000-0003-4423-4370]
""",
ofn="""
Declaration(Class(GO:0050069))
AnnotationAssertion(rdfs:label GO:0050069 "lysine dehydrogenase activity")
AnnotationAssertion(Annotation(oboInOwl:hasDbXref orcid:0000-0003-4423-4370) Annotation(oboInOwl:hasSynonymType OMO:1234567) oboInOwl:hasExactSynonym GO:0050069 "L-lysine:NAD+ oxidoreductase")
AnnotationAssertion(Annotation(oboInOwl:hasDbXref orcid:0000-0003-4423-4370) Annotation(oboInOwl:hasSynonymType OMO:1234567) oboInOwl:hasRelatedSynonym GO:0050069 "L-lysine:NAD+ oxidoreductase")
""",
)

Expand All @@ -473,12 +473,12 @@ def test_9_append_synonym_missing_typedef(self) -> None:
[Term]
id: GO:0050069
name: lysine dehydrogenase activity
synonym: "L-lysine:NAD+ oxidoreductase" EXACT OMO:1234567 []
synonym: "L-lysine:NAD+ oxidoreductase" RELATED OMO:1234567 []
""",
ofn="""
Declaration(Class(GO:0050069))
AnnotationAssertion(rdfs:label GO:0050069 "lysine dehydrogenase activity")
AnnotationAssertion(Annotation(oboInOwl:hasSynonymType OMO:1234567) oboInOwl:hasExactSynonym GO:0050069 "L-lysine:NAD+ oxidoreductase")
AnnotationAssertion(Annotation(oboInOwl:hasSynonymType OMO:1234567) oboInOwl:hasRelatedSynonym GO:0050069 "L-lysine:NAD+ oxidoreductase")
""",
)
self.assertIn(
Expand Down
24 changes: 22 additions & 2 deletions tests/test_struct/test_obo/test_typedef.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,19 +303,39 @@ def test_9_synonym(self) -> None:
"""\
[Typedef]
id: RO:0000087
synonym: "bears role" EXACT []
synonym: "bears role" []
""",
typedef,
)
self.assert_funowl_lines(
"""\
Declaration(ObjectProperty(RO:0000087))
AnnotationAssertion(oboInOwl:hasExactSynonym RO:0000087 "bears role")
AnnotationAssertion(oboInOwl:hasRelatedSynonym RO:0000087 "bears role")
""",
typedef,
)

typedef = TypeDef(reference=REF, synonyms=[Synonym("bears role", type=v.previous_name)])
self.assert_obo_stanza(
"""\
[Typedef]
id: RO:0000087
synonym: "bears role" RELATED OMO:0003008 []
""",
typedef,
)
self.assert_funowl_lines(
"""\
Declaration(ObjectProperty(RO:0000087))
AnnotationAssertion(Annotation(oboInOwl:hasSynonymType OMO:0003008) oboInOwl:hasRelatedSynonym RO:0000087 "bears role")
""",
typedef,
)

typedef = TypeDef(
reference=REF,
synonyms=[Synonym("bears role", type=v.previous_name, specificity="EXACT")],
)
self.assert_obo_stanza(
"""\
[Typedef]
Expand Down
6 changes: 4 additions & 2 deletions tests/test_struct/test_ofn/test_obo_to_ofn.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def test_simple_conversion(self) -> None:
)
term.append_synonym("test-synonym-1")
term.append_synonym("test-synonym-2", type=synonym_typedef)
term.append_synonym("test-synonym-3", specificity="EXACT")

obo_ontology = make_ad_hoc_ontology(
_ontology="go",
Expand Down Expand Up @@ -76,8 +77,9 @@ def test_simple_conversion(self) -> None:
Declaration(Class(GO:1234567))
AnnotationAssertion(rdfs:label GO:1234567 "test")
AnnotationAssertion(oboInOwl:inSubset GO:1234567 obo:go#SUBSET-1)
AnnotationAssertion(oboInOwl:hasExactSynonym GO:1234567 "test-synonym-1")
AnnotationAssertion(Annotation(oboInOwl:hasSynonymType OMO:0003008) oboInOwl:hasExactSynonym GO:1234567 "test-synonym-2")
AnnotationAssertion(oboInOwl:hasRelatedSynonym GO:1234567 "test-synonym-1")
AnnotationAssertion(Annotation(oboInOwl:hasSynonymType OMO:0003008) oboInOwl:hasRelatedSynonym GO:1234567 "test-synonym-2")
AnnotationAssertion(oboInOwl:hasExactSynonym GO:1234567 "test-synonym-3")
)
""").strip(),
ofn_ontology.to_funowl().strip(),
Expand Down

0 comments on commit eb88b1d

Please sign in to comment.