Source code for pronto.synonym

# coding: utf-8

import functools
import typing
import weakref
from typing import Optional, Set, FrozenSet, Iterable

from .xref import Xref
from .utils.impl import set
from .utils.meta import roundrepr, typechecked

if typing.TYPE_CHECKING:
    from .ontology import Ontology


_SCOPES = frozenset({"EXACT", "RELATED", "BROAD", "NARROW"})


[docs]@functools.total_ordering @roundrepr class SynonymType(object): """A user-defined synonym type. """ id: str description: str scope: Optional[str] __slots__ = ("__weakref__", "id", "description", "scope") @typechecked() def __init__(self, id: str, description: str, scope: Optional[str] = None): if scope is not None and scope not in _SCOPES: raise ValueError(f"invalid synonym scope: {scope}") self.id = id self.description = description self.scope = scope
[docs] def __eq__(self, other): if isinstance(other, SynonymType): return self.id == other.id return False
[docs] def __lt__(self, other): if isinstance(other, SynonymType): if self.id < other.id: return True return self.id == other.id and self.description < other.description return NotImplemented
[docs] def __hash__(self): return hash((SynonymType, self.id))
[docs]@functools.total_ordering @roundrepr class SynonymData(object): description: str scope: Optional[str] type: Optional[str] xrefs: Set[Xref] __slots__ = ("__weakref__", "description", "type", "xrefs", "scope")
[docs] def __eq__(self, other): if isinstance(other, SynonymData): return self.description == other.description and self.scope == other.scope return False
[docs] def __lt__(self, other): # FIXME? if not isinstance(other, SynonymData): return NotImplemented if self.type is not None and other.type is not None: return (self.description, self.scope, self.type, frozenset(self.xrefs)) < ( self.description, self.scope, other.type, frozenset(other.xrefs), ) else: return (self.description, self.scope, frozenset(self.xrefs)) < ( self.description, self.scope, frozenset(other.xrefs), )
[docs] def __hash__(self): return hash((self.description, self.scope))
def __init__( self, description: str, scope: Optional[str] = None, type: Optional[str] = None, xrefs: Optional[Iterable[Xref]] = None, ): if scope is not None and scope not in _SCOPES: raise ValueError(f"invalid synonym scope: {scope}") self.description = description self.scope = scope self.type = type self.xrefs = set(xrefs) if xrefs is not None else set()
[docs]@functools.total_ordering class Synonym(object): _ontology: "weakref.ReferenceType[Ontology]" _data: "weakref.ReferenceType[SynonymData]" def __init__(self, ontology: "Ontology", syndata: "SynonymData"): if syndata.type is not None: types = ontology.metadata.synonymtypedefs if not any(t.id == syndata.type for t in types): raise ValueError(f"undeclared synonym type: {syndata.type}") self._ontology = weakref.ref(ontology) self._data = weakref.ref(syndata)
[docs] def __eq__(self, other): if isinstance(other, Synonym): return self._data() == other._data() return False
[docs] def __lt__(self, other): if not isinstance(other, Synonym): return False return self._data().__lt__(other._data())
[docs] def __hash__(self): return hash(self._data())
[docs] def __repr__(self): return roundrepr.make( "Synonym", self.description, scope=(self.scope, None), type=(self.type, None), xrefs=(self.xrefs, set()), )
@property def description(self) -> str: return self._data().description @description.setter @typechecked(property=True) def description(self, description: str) -> None: self._data().description = description @property def type(self) -> Optional[SynonymType]: ontology, syndata = self._ontology(), self._data() if syndata.type is not None: return next(t for t in ontology.metadata.synonymtypedefs if t.id == syndata.type) return None @type.setter @typechecked(property=True) def type(self, type_: Optional[SynonymType]) -> None: synonyms = self._ontology().metadata.synonymtypedefs if type is not None and type_.id not in synonyms: raise ValueError(f"undeclared synonym type: {type_.id}") self._data().type = type_.id if type_ is not None else None @property def scope(self) -> Optional[str]: return self._data().scope @scope.setter @typechecked(property=True) def scope(self, scope: Optional[str]): if scope not in _SCOPES: raise ValueError(f"invalid synonym scope: {scope}") self._data().scope = scope @property def xrefs(self) -> FrozenSet[Xref]: return frozenset(self._data().xrefs)