pyobo 0.10.12__py3-none-any.whl → 0.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyobo/__init__.py +0 -2
- pyobo/__main__.py +0 -2
- pyobo/api/__init__.py +0 -2
- pyobo/api/alts.py +6 -7
- pyobo/api/hierarchy.py +14 -15
- pyobo/api/metadata.py +3 -4
- pyobo/api/names.py +31 -32
- pyobo/api/properties.py +6 -7
- pyobo/api/relations.py +12 -11
- pyobo/api/species.py +5 -6
- pyobo/api/typedefs.py +1 -3
- pyobo/api/utils.py +61 -5
- pyobo/api/xrefs.py +4 -5
- pyobo/aws.py +3 -5
- pyobo/cli/__init__.py +0 -2
- pyobo/cli/aws.py +0 -2
- pyobo/cli/cli.py +0 -4
- pyobo/cli/database.py +1 -3
- pyobo/cli/lookup.py +0 -2
- pyobo/cli/utils.py +0 -2
- pyobo/constants.py +0 -33
- pyobo/getters.py +19 -26
- pyobo/gilda_utils.py +9 -10
- pyobo/identifier_utils.py +10 -10
- pyobo/mocks.py +5 -6
- pyobo/normalizer.py +24 -24
- pyobo/obographs.py +3 -3
- pyobo/plugins.py +3 -4
- pyobo/py.typed +0 -0
- pyobo/reader.py +19 -21
- pyobo/registries/__init__.py +0 -2
- pyobo/registries/metaregistry.py +6 -8
- pyobo/resource_utils.py +1 -3
- pyobo/resources/__init__.py +0 -2
- pyobo/resources/ncbitaxon.py +2 -3
- pyobo/resources/ro.py +2 -4
- pyobo/sources/README.md +15 -0
- pyobo/sources/__init__.py +0 -2
- pyobo/sources/agrovoc.py +3 -3
- pyobo/sources/antibodyregistry.py +2 -3
- pyobo/sources/biogrid.py +4 -4
- pyobo/sources/ccle.py +3 -4
- pyobo/sources/cgnc.py +1 -3
- pyobo/sources/chebi.py +2 -4
- pyobo/sources/chembl.py +1 -3
- pyobo/sources/civic_gene.py +2 -3
- pyobo/sources/complexportal.py +3 -5
- pyobo/sources/conso.py +2 -4
- pyobo/sources/cpt.py +1 -3
- pyobo/sources/credit.py +1 -1
- pyobo/sources/cvx.py +1 -3
- pyobo/sources/depmap.py +3 -4
- pyobo/sources/dictybase_gene.py +1 -3
- pyobo/sources/drugbank.py +6 -7
- pyobo/sources/drugbank_salt.py +3 -4
- pyobo/sources/drugcentral.py +5 -7
- pyobo/sources/expasy.py +11 -12
- pyobo/sources/famplex.py +3 -5
- pyobo/sources/flybase.py +2 -4
- pyobo/sources/geonames.py +1 -1
- pyobo/sources/gmt_utils.py +5 -6
- pyobo/sources/go.py +4 -6
- pyobo/sources/gwascentral_phenotype.py +1 -3
- pyobo/sources/gwascentral_study.py +2 -3
- pyobo/sources/hgnc.py +6 -7
- pyobo/sources/hgncgenefamily.py +2 -4
- pyobo/sources/icd10.py +3 -4
- pyobo/sources/icd11.py +3 -4
- pyobo/sources/icd_utils.py +6 -7
- pyobo/sources/interpro.py +3 -5
- pyobo/sources/itis.py +1 -3
- pyobo/sources/kegg/__init__.py +0 -2
- pyobo/sources/kegg/api.py +3 -4
- pyobo/sources/kegg/genes.py +3 -4
- pyobo/sources/kegg/genome.py +1 -3
- pyobo/sources/kegg/pathway.py +5 -6
- pyobo/sources/mesh.py +19 -21
- pyobo/sources/mgi.py +1 -3
- pyobo/sources/mirbase.py +4 -6
- pyobo/sources/mirbase_constants.py +0 -2
- pyobo/sources/mirbase_family.py +1 -3
- pyobo/sources/mirbase_mature.py +1 -3
- pyobo/sources/msigdb.py +4 -5
- pyobo/sources/ncbigene.py +3 -5
- pyobo/sources/npass.py +1 -3
- pyobo/sources/omim_ps.py +1 -3
- pyobo/sources/pathbank.py +3 -5
- pyobo/sources/pfam.py +1 -3
- pyobo/sources/pfam_clan.py +1 -3
- pyobo/sources/pid.py +3 -5
- pyobo/sources/pombase.py +1 -3
- pyobo/sources/pubchem.py +2 -3
- pyobo/sources/reactome.py +2 -4
- pyobo/sources/rgd.py +2 -3
- pyobo/sources/rhea.py +7 -8
- pyobo/sources/ror.py +3 -2
- pyobo/sources/selventa/__init__.py +0 -2
- pyobo/sources/selventa/schem.py +1 -3
- pyobo/sources/selventa/scomp.py +1 -3
- pyobo/sources/selventa/sdis.py +1 -3
- pyobo/sources/selventa/sfam.py +1 -3
- pyobo/sources/sgd.py +1 -3
- pyobo/sources/slm.py +1 -3
- pyobo/sources/umls/__init__.py +0 -2
- pyobo/sources/umls/__main__.py +0 -2
- pyobo/sources/umls/get_synonym_types.py +1 -1
- pyobo/sources/umls/umls.py +2 -4
- pyobo/sources/uniprot/__init__.py +0 -2
- pyobo/sources/uniprot/uniprot.py +4 -4
- pyobo/sources/uniprot/uniprot_ptm.py +6 -5
- pyobo/sources/utils.py +3 -5
- pyobo/sources/wikipathways.py +1 -3
- pyobo/sources/zfin.py +2 -3
- pyobo/ssg/__init__.py +3 -2
- pyobo/struct/__init__.py +0 -2
- pyobo/struct/reference.py +13 -15
- pyobo/struct/struct.py +102 -96
- pyobo/struct/typedef.py +9 -10
- pyobo/struct/utils.py +0 -2
- pyobo/utils/__init__.py +0 -2
- pyobo/utils/cache.py +14 -6
- pyobo/utils/io.py +9 -10
- pyobo/utils/iter.py +5 -6
- pyobo/utils/misc.py +1 -3
- pyobo/utils/ndex_utils.py +6 -7
- pyobo/utils/path.py +4 -5
- pyobo/version.py +3 -5
- pyobo/xrefdb/__init__.py +0 -2
- pyobo/xrefdb/canonicalizer.py +27 -18
- pyobo/xrefdb/priority.py +0 -2
- pyobo/xrefdb/sources/__init__.py +3 -4
- pyobo/xrefdb/sources/biomappings.py +0 -2
- pyobo/xrefdb/sources/cbms2019.py +0 -2
- pyobo/xrefdb/sources/chembl.py +0 -2
- pyobo/xrefdb/sources/compath.py +1 -3
- pyobo/xrefdb/sources/famplex.py +3 -5
- pyobo/xrefdb/sources/gilda.py +0 -2
- pyobo/xrefdb/sources/intact.py +5 -5
- pyobo/xrefdb/sources/ncit.py +1 -3
- pyobo/xrefdb/sources/pubchem.py +2 -5
- pyobo/xrefdb/sources/wikidata.py +2 -4
- pyobo/xrefdb/xrefs_pipeline.py +15 -16
- {pyobo-0.10.12.dist-info → pyobo-0.11.0.dist-info}/LICENSE +1 -1
- pyobo-0.11.0.dist-info/METADATA +723 -0
- pyobo-0.11.0.dist-info/RECORD +171 -0
- {pyobo-0.10.12.dist-info → pyobo-0.11.0.dist-info}/WHEEL +1 -1
- pyobo-0.11.0.dist-info/entry_points.txt +2 -0
- pyobo-0.10.12.dist-info/METADATA +0 -499
- pyobo-0.10.12.dist-info/RECORD +0 -169
- pyobo-0.10.12.dist-info/entry_points.txt +0 -15
- {pyobo-0.10.12.dist-info → pyobo-0.11.0.dist-info}/top_level.txt +0 -0
pyobo/struct/struct.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
1
|
"""Data structures for OBO."""
|
|
4
2
|
|
|
5
3
|
import gzip
|
|
@@ -7,6 +5,7 @@ import json
|
|
|
7
5
|
import logging
|
|
8
6
|
import os
|
|
9
7
|
from collections import defaultdict
|
|
8
|
+
from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence
|
|
10
9
|
from dataclasses import dataclass, field
|
|
11
10
|
from datetime import datetime
|
|
12
11
|
from operator import attrgetter
|
|
@@ -16,17 +15,8 @@ from typing import (
|
|
|
16
15
|
Any,
|
|
17
16
|
Callable,
|
|
18
17
|
ClassVar,
|
|
19
|
-
Collection,
|
|
20
|
-
Dict,
|
|
21
|
-
Iterable,
|
|
22
|
-
Iterator,
|
|
23
|
-
List,
|
|
24
|
-
Mapping,
|
|
25
18
|
Optional,
|
|
26
|
-
Sequence,
|
|
27
|
-
Set,
|
|
28
19
|
TextIO,
|
|
29
|
-
Tuple,
|
|
30
20
|
Union,
|
|
31
21
|
)
|
|
32
22
|
|
|
@@ -104,7 +94,7 @@ class Synonym:
|
|
|
104
94
|
)
|
|
105
95
|
|
|
106
96
|
#: References to articles where the synonym appears
|
|
107
|
-
provenance:
|
|
97
|
+
provenance: list[Reference] = field(default_factory=list)
|
|
108
98
|
|
|
109
99
|
def to_obo(self) -> str:
|
|
110
100
|
"""Write this synonym as an OBO line to appear in a [Term] stanza."""
|
|
@@ -168,7 +158,7 @@ abbreviation = SynonymTypeDef(
|
|
|
168
158
|
acronym = SynonymTypeDef(reference=Reference(prefix="omo", identifier="0003012", name="acronym"))
|
|
169
159
|
|
|
170
160
|
|
|
171
|
-
ReferenceHint = Union[Reference, "Term",
|
|
161
|
+
ReferenceHint = Union[Reference, "Term", tuple[str, str], str]
|
|
172
162
|
|
|
173
163
|
|
|
174
164
|
def _ensure_ref(reference: ReferenceHint) -> Reference:
|
|
@@ -199,26 +189,26 @@ class Term(Referenced):
|
|
|
199
189
|
definition: Optional[str] = None
|
|
200
190
|
|
|
201
191
|
#: References to articles in which the term appears
|
|
202
|
-
provenance:
|
|
192
|
+
provenance: list[Reference] = field(default_factory=list)
|
|
203
193
|
|
|
204
194
|
#: Relationships defined by [Typedef] stanzas
|
|
205
|
-
relationships:
|
|
195
|
+
relationships: dict[TypeDef, list[Reference]] = field(default_factory=lambda: defaultdict(list))
|
|
206
196
|
|
|
207
197
|
#: Properties, which are not defined with Typedef and have scalar values instead of references.
|
|
208
|
-
properties:
|
|
198
|
+
properties: dict[str, list[str]] = field(default_factory=lambda: defaultdict(list))
|
|
209
199
|
|
|
210
200
|
#: Relationships with the default "is_a"
|
|
211
|
-
parents:
|
|
201
|
+
parents: list[Reference] = field(default_factory=list)
|
|
212
202
|
|
|
213
203
|
#: Synonyms of this term
|
|
214
|
-
synonyms:
|
|
204
|
+
synonyms: list[Synonym] = field(default_factory=list)
|
|
215
205
|
|
|
216
206
|
#: Equivalent references
|
|
217
|
-
xrefs:
|
|
218
|
-
xref_types:
|
|
207
|
+
xrefs: list[Reference] = field(default_factory=list)
|
|
208
|
+
xref_types: list[Reference] = field(default_factory=list)
|
|
219
209
|
|
|
220
210
|
#: Alternate Identifiers
|
|
221
|
-
alt_ids:
|
|
211
|
+
alt_ids: list[Reference] = field(default_factory=list)
|
|
222
212
|
|
|
223
213
|
#: The sub-namespace within the ontology
|
|
224
214
|
namespace: Optional[str] = None
|
|
@@ -228,7 +218,7 @@ class Term(Referenced):
|
|
|
228
218
|
|
|
229
219
|
type: Literal["Term", "Instance"] = "Term"
|
|
230
220
|
|
|
231
|
-
def __hash__(self):
|
|
221
|
+
def __hash__(self):
|
|
232
222
|
return hash((self.__class__, self.prefix, self.identifier))
|
|
233
223
|
|
|
234
224
|
@classmethod
|
|
@@ -321,7 +311,7 @@ class Term(Referenced):
|
|
|
321
311
|
raise ValueError("can not append a collection of parents containing a null parent")
|
|
322
312
|
self.parents.extend(references)
|
|
323
313
|
|
|
324
|
-
def get_properties(self, prop) ->
|
|
314
|
+
def get_properties(self, prop) -> list[str]:
|
|
325
315
|
"""Get properties from the given key."""
|
|
326
316
|
return self.properties[prop]
|
|
327
317
|
|
|
@@ -343,7 +333,7 @@ class Term(Referenced):
|
|
|
343
333
|
raise ValueError
|
|
344
334
|
return r[0]
|
|
345
335
|
|
|
346
|
-
def get_relationships(self, typedef: TypeDef) ->
|
|
336
|
+
def get_relationships(self, typedef: TypeDef) -> list[Reference]:
|
|
347
337
|
"""Get relationships from the given type."""
|
|
348
338
|
return self.relationships[typedef]
|
|
349
339
|
|
|
@@ -399,16 +389,17 @@ class Term(Referenced):
|
|
|
399
389
|
self.properties[prop].append(value)
|
|
400
390
|
|
|
401
391
|
def _definition_fp(self) -> str:
|
|
402
|
-
|
|
392
|
+
if self.definition is None:
|
|
393
|
+
raise AssertionError
|
|
403
394
|
return f'"{obo_escape_slim(self.definition)}" [{comma_separate(self.provenance)}]'
|
|
404
395
|
|
|
405
|
-
def iterate_relations(self) -> Iterable[
|
|
396
|
+
def iterate_relations(self) -> Iterable[tuple[TypeDef, Reference]]:
|
|
406
397
|
"""Iterate over pairs of typedefs and targets."""
|
|
407
398
|
for typedef, targets in sorted(self.relationships.items(), key=_sort_relations):
|
|
408
399
|
for target in sorted(targets, key=lambda ref: ref.preferred_curie):
|
|
409
400
|
yield typedef, target
|
|
410
401
|
|
|
411
|
-
def iterate_properties(self) -> Iterable[
|
|
402
|
+
def iterate_properties(self) -> Iterable[tuple[str, str]]:
|
|
412
403
|
"""Iterate over pairs of property and values."""
|
|
413
404
|
for prop, values in sorted(self.properties.items()):
|
|
414
405
|
for value in sorted(values):
|
|
@@ -470,7 +461,7 @@ class Term(Referenced):
|
|
|
470
461
|
|
|
471
462
|
|
|
472
463
|
#: A set of warnings, used to make sure we don't show the same one over and over
|
|
473
|
-
_TYPEDEF_WARNINGS:
|
|
464
|
+
_TYPEDEF_WARNINGS: set[tuple[str, str]] = set()
|
|
474
465
|
|
|
475
466
|
|
|
476
467
|
def _sort_relations(r):
|
|
@@ -489,6 +480,8 @@ def _sort_properties(r):
|
|
|
489
480
|
|
|
490
481
|
|
|
491
482
|
class BioregistryError(ValueError):
|
|
483
|
+
"""An error raised for non-canonical prefixes."""
|
|
484
|
+
|
|
492
485
|
def __str__(self) -> str:
|
|
493
486
|
return dedent(
|
|
494
487
|
f"""
|
|
@@ -518,10 +511,10 @@ class Obo:
|
|
|
518
511
|
format_version: ClassVar[str] = "1.2"
|
|
519
512
|
|
|
520
513
|
#: Type definitions
|
|
521
|
-
typedefs: ClassVar[Optional[
|
|
514
|
+
typedefs: ClassVar[Optional[list[TypeDef]]] = None
|
|
522
515
|
|
|
523
516
|
#: Synonym type definitions
|
|
524
|
-
synonym_typedefs: ClassVar[Optional[
|
|
517
|
+
synonym_typedefs: ClassVar[Optional[list[SynonymTypeDef]]] = None
|
|
525
518
|
|
|
526
519
|
#: An annotation about how an ontology was generated
|
|
527
520
|
auto_generated_by: ClassVar[Optional[str]] = None
|
|
@@ -541,7 +534,7 @@ class Obo:
|
|
|
541
534
|
bioversions_key: ClassVar[Optional[str]] = None
|
|
542
535
|
|
|
543
536
|
#: Root terms to use for the ontology
|
|
544
|
-
root_terms: ClassVar[Optional[
|
|
537
|
+
root_terms: ClassVar[Optional[list[Reference]]] = None
|
|
545
538
|
|
|
546
539
|
#: The date the ontology was generated
|
|
547
540
|
date: Optional[datetime] = field(default_factory=datetime.today)
|
|
@@ -555,7 +548,7 @@ class Obo:
|
|
|
555
548
|
#: The hierarchy of terms
|
|
556
549
|
_hierarchy: Optional[nx.DiGraph] = field(init=False, default=None, repr=False)
|
|
557
550
|
#: A cache of terms
|
|
558
|
-
_items: Optional[
|
|
551
|
+
_items: Optional[list[Term]] = field(init=False, default=None, repr=False)
|
|
559
552
|
|
|
560
553
|
term_sort_key: ClassVar[Optional[Callable[["Obo", Term], int]]] = None
|
|
561
554
|
|
|
@@ -590,7 +583,7 @@ class Obo:
|
|
|
590
583
|
return get_version(self.bioversions_key)
|
|
591
584
|
except KeyError:
|
|
592
585
|
logger.warning(f"[{self.bioversions_key}] bioversions doesn't list this resource ")
|
|
593
|
-
except
|
|
586
|
+
except OSError:
|
|
594
587
|
logger.warning(f"[{self.bioversions_key}] error while looking up version")
|
|
595
588
|
return None
|
|
596
589
|
|
|
@@ -661,13 +654,14 @@ class Obo:
|
|
|
661
654
|
def iterate_obo_lines(self) -> Iterable[str]:
|
|
662
655
|
"""Iterate over the lines to write in an OBO file."""
|
|
663
656
|
yield f"format-version: {self.format_version}"
|
|
664
|
-
yield f"date: {self.date_formatted}"
|
|
665
657
|
|
|
666
658
|
if self.auto_generated_by is not None:
|
|
667
659
|
yield f"auto-generated-by: {self.auto_generated_by}"
|
|
668
660
|
|
|
669
661
|
if self.data_version is not None:
|
|
670
662
|
yield f"data-version: {self.data_version}"
|
|
663
|
+
else:
|
|
664
|
+
yield f"date: {self.date_formatted}"
|
|
671
665
|
|
|
672
666
|
for prefix, url in sorted((self.idspaces or {}).items()):
|
|
673
667
|
yield f"idspace: {prefix} {url}"
|
|
@@ -716,7 +710,7 @@ class Obo:
|
|
|
716
710
|
@staticmethod
|
|
717
711
|
def _write_lines(it, file: Optional[TextIO]):
|
|
718
712
|
for line in it:
|
|
719
|
-
print(line, file=file)
|
|
713
|
+
print(line, file=file)
|
|
720
714
|
|
|
721
715
|
def write_obonet_gz(self, path: Union[str, Path]) -> None:
|
|
722
716
|
"""Write the OBO to a gzipped dump in Obonet JSON."""
|
|
@@ -895,16 +889,16 @@ class Obo:
|
|
|
895
889
|
self._items = sorted(self.iter_terms(force=self.force), key=key)
|
|
896
890
|
return self._items
|
|
897
891
|
|
|
898
|
-
def __iter__(self) -> Iterator["Term"]:
|
|
892
|
+
def __iter__(self) -> Iterator["Term"]:
|
|
899
893
|
if self.iter_only:
|
|
900
894
|
return iter(self.iter_terms(force=self.force))
|
|
901
895
|
return iter(self._items_accessor)
|
|
902
896
|
|
|
903
|
-
def ancestors(self, identifier: str) ->
|
|
897
|
+
def ancestors(self, identifier: str) -> set[str]:
|
|
904
898
|
"""Return a set of identifiers for parents of the given identifier."""
|
|
905
899
|
return nx.descendants(self.hierarchy, identifier) # note this is backwards
|
|
906
900
|
|
|
907
|
-
def descendants(self, identifier: str) ->
|
|
901
|
+
def descendants(self, identifier: str) -> set[str]:
|
|
908
902
|
"""Return a set of identifiers for the children of the given identifier."""
|
|
909
903
|
return nx.ancestors(self.hierarchy, identifier) # note this is backwards
|
|
910
904
|
|
|
@@ -914,11 +908,12 @@ class Obo:
|
|
|
914
908
|
.. code-block:: python
|
|
915
909
|
|
|
916
910
|
from pyobo import get_obo
|
|
917
|
-
obo = get_obo('go')
|
|
918
911
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
912
|
+
obo = get_obo("go")
|
|
913
|
+
|
|
914
|
+
interleukin_10_complex = "1905571" # interleukin-10 receptor complex
|
|
915
|
+
all_complexes = "0032991"
|
|
916
|
+
assert obo.is_descendant("1905571", "0032991")
|
|
922
917
|
"""
|
|
923
918
|
return ancestor in self.ancestors(descendant)
|
|
924
919
|
|
|
@@ -931,11 +926,12 @@ class Obo:
|
|
|
931
926
|
.. code-block:: python
|
|
932
927
|
|
|
933
928
|
from pyobo import get_obo
|
|
934
|
-
obo = get_obo('go')
|
|
935
929
|
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
930
|
+
obo = get_obo("go")
|
|
931
|
+
|
|
932
|
+
identifier = "1905571" # interleukin-10 receptor complex
|
|
933
|
+
is_complex = "0032991" in nx.descendants(obo.hierarchy, identifier) # should be true
|
|
934
|
+
"""
|
|
939
935
|
if self._hierarchy is None:
|
|
940
936
|
self._hierarchy = nx.DiGraph()
|
|
941
937
|
for term in self._iter_terms(desc=f"[{self.ontology}] getting hierarchy"):
|
|
@@ -1006,10 +1002,10 @@ class Obo:
|
|
|
1006
1002
|
|
|
1007
1003
|
def get_metadata(self) -> Mapping[str, Any]:
|
|
1008
1004
|
"""Get metadata."""
|
|
1009
|
-
return
|
|
1010
|
-
version
|
|
1011
|
-
date
|
|
1012
|
-
|
|
1005
|
+
return {
|
|
1006
|
+
"version": self.data_version,
|
|
1007
|
+
"date": self.date and self.date.isoformat(),
|
|
1008
|
+
}
|
|
1013
1009
|
|
|
1014
1010
|
def iterate_ids(self, *, use_tqdm: bool = False) -> Iterable[str]:
|
|
1015
1011
|
"""Iterate over identifiers."""
|
|
@@ -1017,11 +1013,11 @@ class Obo:
|
|
|
1017
1013
|
if term.prefix == self.ontology:
|
|
1018
1014
|
yield term.identifier
|
|
1019
1015
|
|
|
1020
|
-
def get_ids(self, *, use_tqdm: bool = False) ->
|
|
1016
|
+
def get_ids(self, *, use_tqdm: bool = False) -> set[str]:
|
|
1021
1017
|
"""Get the set of identifiers."""
|
|
1022
1018
|
return set(self.iterate_ids(use_tqdm=use_tqdm))
|
|
1023
1019
|
|
|
1024
|
-
def iterate_id_name(self, *, use_tqdm: bool = False) -> Iterable[
|
|
1020
|
+
def iterate_id_name(self, *, use_tqdm: bool = False) -> Iterable[tuple[str, str]]:
|
|
1025
1021
|
"""Iterate identifier name pairs."""
|
|
1026
1022
|
for term in self._iter_terms(use_tqdm=use_tqdm, desc=f"[{self.ontology}] getting names"):
|
|
1027
1023
|
if term.prefix == self.ontology and term.name:
|
|
@@ -1031,19 +1027,23 @@ class Obo:
|
|
|
1031
1027
|
"""Get a mapping from identifiers to names."""
|
|
1032
1028
|
return dict(self.iterate_id_name(use_tqdm=use_tqdm))
|
|
1033
1029
|
|
|
1034
|
-
def iterate_id_definition(self, *, use_tqdm: bool = False) -> Iterable[
|
|
1030
|
+
def iterate_id_definition(self, *, use_tqdm: bool = False) -> Iterable[tuple[str, str]]:
|
|
1035
1031
|
"""Iterate over pairs of terms' identifiers and their respective definitions."""
|
|
1036
1032
|
for term in self._iter_terms(use_tqdm=use_tqdm, desc=f"[{self.ontology}] getting names"):
|
|
1037
1033
|
if term.identifier and term.definition:
|
|
1038
|
-
yield
|
|
1039
|
-
|
|
1040
|
-
|
|
1034
|
+
yield (
|
|
1035
|
+
term.identifier,
|
|
1036
|
+
term.definition.strip('"')
|
|
1037
|
+
.replace("\n", " ")
|
|
1038
|
+
.replace("\t", " ")
|
|
1039
|
+
.replace(" ", " "),
|
|
1040
|
+
)
|
|
1041
1041
|
|
|
1042
1042
|
def get_id_definition_mapping(self, *, use_tqdm: bool = False) -> Mapping[str, str]:
|
|
1043
1043
|
"""Get a mapping from identifiers to definitions."""
|
|
1044
1044
|
return dict(self.iterate_id_definition(use_tqdm=use_tqdm))
|
|
1045
1045
|
|
|
1046
|
-
def get_obsolete(self, *, use_tqdm: bool = False) ->
|
|
1046
|
+
def get_obsolete(self, *, use_tqdm: bool = False) -> set[str]:
|
|
1047
1047
|
"""Get the set of obsolete identifiers."""
|
|
1048
1048
|
return {
|
|
1049
1049
|
term.identifier
|
|
@@ -1059,7 +1059,7 @@ class Obo:
|
|
|
1059
1059
|
|
|
1060
1060
|
def iterate_id_species(
|
|
1061
1061
|
self, *, prefix: Optional[str] = None, use_tqdm: bool = False
|
|
1062
|
-
) -> Iterable[
|
|
1062
|
+
) -> Iterable[tuple[str, str]]:
|
|
1063
1063
|
"""Iterate over terms' identifiers and respective species (if available)."""
|
|
1064
1064
|
if prefix is None:
|
|
1065
1065
|
prefix = NCBITAXON_PREFIX
|
|
@@ -1086,7 +1086,7 @@ class Obo:
|
|
|
1086
1086
|
]
|
|
1087
1087
|
return pd.DataFrame(rows, columns=["prefix", "identifier", "name"])
|
|
1088
1088
|
|
|
1089
|
-
def iter_typedef_id_name(self) -> Iterable[
|
|
1089
|
+
def iter_typedef_id_name(self) -> Iterable[tuple[str, str]]:
|
|
1090
1090
|
"""Iterate over typedefs' identifiers and their respective names."""
|
|
1091
1091
|
for typedef in self.typedefs or []:
|
|
1092
1092
|
yield typedef.identifier, typedef.name
|
|
@@ -1099,7 +1099,7 @@ class Obo:
|
|
|
1099
1099
|
# PROPS #
|
|
1100
1100
|
#########
|
|
1101
1101
|
|
|
1102
|
-
def iterate_properties(self, *, use_tqdm: bool = False) -> Iterable[
|
|
1102
|
+
def iterate_properties(self, *, use_tqdm: bool = False) -> Iterable[tuple[Term, str, str]]:
|
|
1103
1103
|
"""Iterate over tuples of terms, properties, and their values."""
|
|
1104
1104
|
# TODO if property_prefix is set, try removing that as a prefix from all prop strings.
|
|
1105
1105
|
for term in self._iter_terms(
|
|
@@ -1110,10 +1110,10 @@ class Obo:
|
|
|
1110
1110
|
|
|
1111
1111
|
@property
|
|
1112
1112
|
def properties_header(self):
|
|
1113
|
-
"""Property dataframe header."""
|
|
1113
|
+
"""Property dataframe header."""
|
|
1114
1114
|
return [f"{self.ontology}_id", "property", "value"]
|
|
1115
1115
|
|
|
1116
|
-
def iter_property_rows(self, *, use_tqdm: bool = False) -> Iterable[
|
|
1116
|
+
def iter_property_rows(self, *, use_tqdm: bool = False) -> Iterable[tuple[str, str, str]]:
|
|
1117
1117
|
"""Iterate property rows."""
|
|
1118
1118
|
for term, prop, value in self.iterate_properties(use_tqdm=use_tqdm):
|
|
1119
1119
|
yield term.identifier, prop, value
|
|
@@ -1127,7 +1127,7 @@ class Obo:
|
|
|
1127
1127
|
|
|
1128
1128
|
def iterate_filtered_properties(
|
|
1129
1129
|
self, prop: str, *, use_tqdm: bool = False
|
|
1130
|
-
) -> Iterable[
|
|
1130
|
+
) -> Iterable[tuple[Term, str]]:
|
|
1131
1131
|
"""Iterate over tuples of terms and the values for the given property."""
|
|
1132
1132
|
for term in self._iter_terms(use_tqdm=use_tqdm):
|
|
1133
1133
|
for _prop, value in term.iterate_properties():
|
|
@@ -1155,7 +1155,7 @@ class Obo:
|
|
|
1155
1155
|
|
|
1156
1156
|
def get_filtered_properties_multimapping(
|
|
1157
1157
|
self, prop: str, *, use_tqdm: bool = False
|
|
1158
|
-
) -> Mapping[str,
|
|
1158
|
+
) -> Mapping[str, list[str]]:
|
|
1159
1159
|
"""Get a mapping from a term's identifier to the property values."""
|
|
1160
1160
|
return multidict(
|
|
1161
1161
|
(term.identifier, value)
|
|
@@ -1168,7 +1168,7 @@ class Obo:
|
|
|
1168
1168
|
|
|
1169
1169
|
def iterate_relations(
|
|
1170
1170
|
self, *, use_tqdm: bool = False
|
|
1171
|
-
) -> Iterable[
|
|
1171
|
+
) -> Iterable[tuple[Term, TypeDef, Reference]]:
|
|
1172
1172
|
"""Iterate over tuples of terms, relations, and their targets."""
|
|
1173
1173
|
for term in self._iter_terms(
|
|
1174
1174
|
use_tqdm=use_tqdm, desc=f"[{self.ontology}] getting relations"
|
|
@@ -1185,17 +1185,23 @@ class Obo:
|
|
|
1185
1185
|
|
|
1186
1186
|
def iter_relation_rows(
|
|
1187
1187
|
self, use_tqdm: bool = False
|
|
1188
|
-
) -> Iterable[
|
|
1188
|
+
) -> Iterable[tuple[str, str, str, str, str]]:
|
|
1189
1189
|
"""Iterate the relations' rows."""
|
|
1190
1190
|
for term, typedef, reference in self.iterate_relations(use_tqdm=use_tqdm):
|
|
1191
|
-
yield
|
|
1191
|
+
yield (
|
|
1192
|
+
term.identifier,
|
|
1193
|
+
typedef.prefix,
|
|
1194
|
+
typedef.identifier,
|
|
1195
|
+
reference.prefix,
|
|
1196
|
+
reference.identifier,
|
|
1197
|
+
)
|
|
1192
1198
|
|
|
1193
1199
|
def iterate_filtered_relations(
|
|
1194
1200
|
self,
|
|
1195
1201
|
relation: RelationHint,
|
|
1196
1202
|
*,
|
|
1197
1203
|
use_tqdm: bool = False,
|
|
1198
|
-
) -> Iterable[
|
|
1204
|
+
) -> Iterable[tuple[Term, Reference]]:
|
|
1199
1205
|
"""Iterate over tuples of terms and ther targets for the given relation."""
|
|
1200
1206
|
_target_prefix, _target_identifier = get_reference_tuple(relation)
|
|
1201
1207
|
for term, typedef, reference in self.iterate_relations(use_tqdm=use_tqdm):
|
|
@@ -1204,7 +1210,7 @@ class Obo:
|
|
|
1204
1210
|
|
|
1205
1211
|
@property
|
|
1206
1212
|
def relations_header(self) -> Sequence[str]:
|
|
1207
|
-
"""Header for the relations dataframe."""
|
|
1213
|
+
"""Header for the relations dataframe."""
|
|
1208
1214
|
return [f"{self.ontology}_id", RELATION_PREFIX, RELATION_ID, TARGET_PREFIX, TARGET_ID]
|
|
1209
1215
|
|
|
1210
1216
|
def get_relations_df(self, *, use_tqdm: bool = False) -> pd.DataFrame:
|
|
@@ -1235,7 +1241,7 @@ class Obo:
|
|
|
1235
1241
|
target_prefix: str,
|
|
1236
1242
|
*,
|
|
1237
1243
|
use_tqdm: bool = False,
|
|
1238
|
-
) -> Iterable[
|
|
1244
|
+
) -> Iterable[tuple[Term, Reference]]:
|
|
1239
1245
|
"""Iterate over relationships between one identifier and another."""
|
|
1240
1246
|
for term, reference in self.iterate_filtered_relations(
|
|
1241
1247
|
relation=relation, use_tqdm=use_tqdm
|
|
@@ -1258,9 +1264,9 @@ class Obo:
|
|
|
1258
1264
|
|
|
1259
1265
|
>>> from pyobo.sources.hgnc import get_obo
|
|
1260
1266
|
>>> obo = get_obo()
|
|
1261
|
-
>>> human_mapt_hgnc_id =
|
|
1262
|
-
>>> mouse_mapt_mgi_id =
|
|
1263
|
-
>>> hgnc_mgi_orthology_mapping = obo.get_relation_mapping(
|
|
1267
|
+
>>> human_mapt_hgnc_id = "6893"
|
|
1268
|
+
>>> mouse_mapt_mgi_id = "97180"
|
|
1269
|
+
>>> hgnc_mgi_orthology_mapping = obo.get_relation_mapping("ro:HOM0000017", "mgi")
|
|
1264
1270
|
>>> assert mouse_mapt_mgi_id == hgnc_mgi_orthology_mapping[human_mapt_hgnc_id]
|
|
1265
1271
|
"""
|
|
1266
1272
|
return {
|
|
@@ -1284,9 +1290,9 @@ class Obo:
|
|
|
1284
1290
|
|
|
1285
1291
|
>>> from pyobo.sources.hgnc import get_obo
|
|
1286
1292
|
>>> obo = get_obo()
|
|
1287
|
-
>>> human_mapt_hgnc_id =
|
|
1288
|
-
>>> mouse_mapt_mgi_id =
|
|
1289
|
-
>>> assert mouse_mapt_mgi_id == obo.get_relation(human_mapt_hgnc_id,
|
|
1293
|
+
>>> human_mapt_hgnc_id = "6893"
|
|
1294
|
+
>>> mouse_mapt_mgi_id = "97180"
|
|
1295
|
+
>>> assert mouse_mapt_mgi_id == obo.get_relation(human_mapt_hgnc_id, "ro:HOM0000017", "mgi")
|
|
1290
1296
|
"""
|
|
1291
1297
|
relation_mapping = self.get_relation_mapping(
|
|
1292
1298
|
relation=relation, target_prefix=target_prefix, use_tqdm=use_tqdm
|
|
@@ -1299,7 +1305,7 @@ class Obo:
|
|
|
1299
1305
|
target_prefix: str,
|
|
1300
1306
|
*,
|
|
1301
1307
|
use_tqdm: bool = False,
|
|
1302
|
-
) -> Mapping[str,
|
|
1308
|
+
) -> Mapping[str, list[str]]:
|
|
1303
1309
|
"""Get a mapping from the term's identifier to the target's identifiers."""
|
|
1304
1310
|
return multidict(
|
|
1305
1311
|
(term.identifier, reference.identifier)
|
|
@@ -1315,7 +1321,7 @@ class Obo:
|
|
|
1315
1321
|
typedef: TypeDef,
|
|
1316
1322
|
*,
|
|
1317
1323
|
use_tqdm: bool = False,
|
|
1318
|
-
) -> Mapping[str,
|
|
1324
|
+
) -> Mapping[str, list[Reference]]:
|
|
1319
1325
|
"""Get a mapping from identifiers to a list of all references for the given relation."""
|
|
1320
1326
|
return multidict(
|
|
1321
1327
|
(term.identifier, reference)
|
|
@@ -1329,18 +1335,18 @@ class Obo:
|
|
|
1329
1335
|
# SYNONYMS #
|
|
1330
1336
|
############
|
|
1331
1337
|
|
|
1332
|
-
def iterate_synonyms(self, *, use_tqdm: bool = False) -> Iterable[
|
|
1338
|
+
def iterate_synonyms(self, *, use_tqdm: bool = False) -> Iterable[tuple[Term, Synonym]]:
|
|
1333
1339
|
"""Iterate over pairs of term and synonym object."""
|
|
1334
1340
|
for term in self._iter_terms(use_tqdm=use_tqdm, desc=f"[{self.ontology}] getting synonyms"):
|
|
1335
1341
|
for synonym in sorted(term.synonyms, key=attrgetter("name")):
|
|
1336
1342
|
yield term, synonym
|
|
1337
1343
|
|
|
1338
|
-
def iterate_synonym_rows(self, *, use_tqdm: bool = False) -> Iterable[
|
|
1344
|
+
def iterate_synonym_rows(self, *, use_tqdm: bool = False) -> Iterable[tuple[str, str]]:
|
|
1339
1345
|
"""Iterate over pairs of identifier and synonym text."""
|
|
1340
1346
|
for term, synonym in self.iterate_synonyms(use_tqdm=use_tqdm):
|
|
1341
1347
|
yield term.identifier, synonym.name
|
|
1342
1348
|
|
|
1343
|
-
def get_id_synonyms_mapping(self, *, use_tqdm: bool = False) -> Mapping[str,
|
|
1349
|
+
def get_id_synonyms_mapping(self, *, use_tqdm: bool = False) -> Mapping[str, list[str]]:
|
|
1344
1350
|
"""Get a mapping from identifiers to a list of sorted synonym strings."""
|
|
1345
1351
|
return multidict(self.iterate_synonym_rows(use_tqdm=use_tqdm))
|
|
1346
1352
|
|
|
@@ -1348,7 +1354,7 @@ class Obo:
|
|
|
1348
1354
|
# XREFS #
|
|
1349
1355
|
#########
|
|
1350
1356
|
|
|
1351
|
-
def iterate_xrefs(self, *, use_tqdm: bool = False) -> Iterable[
|
|
1357
|
+
def iterate_xrefs(self, *, use_tqdm: bool = False) -> Iterable[tuple[Term, Reference]]:
|
|
1352
1358
|
"""Iterate over xrefs."""
|
|
1353
1359
|
for term in self._iter_terms(use_tqdm=use_tqdm, desc=f"[{self.ontology}] getting xrefs"):
|
|
1354
1360
|
for xref in term.xrefs:
|
|
@@ -1356,20 +1362,20 @@ class Obo:
|
|
|
1356
1362
|
|
|
1357
1363
|
def iterate_filtered_xrefs(
|
|
1358
1364
|
self, prefix: str, *, use_tqdm: bool = False
|
|
1359
|
-
) -> Iterable[
|
|
1365
|
+
) -> Iterable[tuple[Term, Reference]]:
|
|
1360
1366
|
"""Iterate over xrefs to a given prefix."""
|
|
1361
1367
|
for term, xref in self.iterate_xrefs(use_tqdm=use_tqdm):
|
|
1362
1368
|
if xref.prefix == prefix:
|
|
1363
1369
|
yield term, xref
|
|
1364
1370
|
|
|
1365
|
-
def iterate_xref_rows(self, *, use_tqdm: bool = False) -> Iterable[
|
|
1371
|
+
def iterate_xref_rows(self, *, use_tqdm: bool = False) -> Iterable[tuple[str, str, str]]:
|
|
1366
1372
|
"""Iterate over terms' identifiers, xref prefixes, and xref identifiers."""
|
|
1367
1373
|
for term, xref in self.iterate_xrefs(use_tqdm=use_tqdm):
|
|
1368
1374
|
yield term.identifier, xref.prefix, xref.identifier
|
|
1369
1375
|
|
|
1370
1376
|
@property
|
|
1371
1377
|
def xrefs_header(self):
|
|
1372
|
-
"""The header for the xref dataframe."""
|
|
1378
|
+
"""The header for the xref dataframe."""
|
|
1373
1379
|
return [f"{self.ontology}_id", TARGET_PREFIX, TARGET_ID]
|
|
1374
1380
|
|
|
1375
1381
|
def get_xrefs_df(self, *, use_tqdm: bool = False) -> pd.DataFrame:
|
|
@@ -1390,7 +1396,7 @@ class Obo:
|
|
|
1390
1396
|
|
|
1391
1397
|
def get_filtered_multixrefs_mapping(
|
|
1392
1398
|
self, prefix: str, *, use_tqdm: bool = False
|
|
1393
|
-
) -> Mapping[str,
|
|
1399
|
+
) -> Mapping[str, list[str]]:
|
|
1394
1400
|
"""Get filtered xrefs as a dictionary."""
|
|
1395
1401
|
return multidict(
|
|
1396
1402
|
(term.identifier, xref.identifier)
|
|
@@ -1401,18 +1407,18 @@ class Obo:
|
|
|
1401
1407
|
# ALTS #
|
|
1402
1408
|
########
|
|
1403
1409
|
|
|
1404
|
-
def iterate_alts(self) -> Iterable[
|
|
1410
|
+
def iterate_alts(self) -> Iterable[tuple[Term, Reference]]:
|
|
1405
1411
|
"""Iterate over alternative identifiers."""
|
|
1406
1412
|
for term in self:
|
|
1407
1413
|
for alt in term.alt_ids:
|
|
1408
1414
|
yield term, alt
|
|
1409
1415
|
|
|
1410
|
-
def iterate_alt_rows(self) -> Iterable[
|
|
1416
|
+
def iterate_alt_rows(self) -> Iterable[tuple[str, str]]:
|
|
1411
1417
|
"""Iterate over pairs of terms' primary identifiers and alternate identifiers."""
|
|
1412
1418
|
for term, alt in self.iterate_alts():
|
|
1413
1419
|
yield term.identifier, alt.identifier
|
|
1414
1420
|
|
|
1415
|
-
def get_id_alts_mapping(self) -> Mapping[str,
|
|
1421
|
+
def get_id_alts_mapping(self) -> Mapping[str, list[str]]:
|
|
1416
1422
|
"""Get a mapping from identifiers to a list of alternative identifiers."""
|
|
1417
1423
|
return multidict((term.identifier, alt.identifier) for term, alt in self.iterate_alts())
|
|
1418
1424
|
|
|
@@ -1422,14 +1428,14 @@ def make_ad_hoc_ontology(
|
|
|
1422
1428
|
_name: str,
|
|
1423
1429
|
_auto_generated_by: Optional[str] = None,
|
|
1424
1430
|
_format_version: str = "1.2",
|
|
1425
|
-
_typedefs: Optional[
|
|
1426
|
-
_synonym_typedefs: Optional[
|
|
1431
|
+
_typedefs: Optional[list[TypeDef]] = None,
|
|
1432
|
+
_synonym_typedefs: Optional[list[SynonymTypeDef]] = None,
|
|
1427
1433
|
_date: Optional[datetime] = None,
|
|
1428
1434
|
_data_version: Optional[str] = None,
|
|
1429
1435
|
_idspaces: Optional[Mapping[str, str]] = None,
|
|
1430
|
-
_root_terms: Optional[
|
|
1436
|
+
_root_terms: Optional[list[Reference]] = None,
|
|
1431
1437
|
*,
|
|
1432
|
-
terms:
|
|
1438
|
+
terms: list[Term],
|
|
1433
1439
|
) -> "Obo":
|
|
1434
1440
|
"""Make an ad-hoc ontology."""
|
|
1435
1441
|
|
|
@@ -1456,7 +1462,7 @@ def make_ad_hoc_ontology(
|
|
|
1456
1462
|
return AdHocOntology()
|
|
1457
1463
|
|
|
1458
1464
|
|
|
1459
|
-
def _convert_typedefs(typedefs: Optional[Iterable[TypeDef]]) ->
|
|
1465
|
+
def _convert_typedefs(typedefs: Optional[Iterable[TypeDef]]) -> list[Mapping[str, Any]]:
|
|
1460
1466
|
"""Convert the type defs."""
|
|
1461
1467
|
if not typedefs:
|
|
1462
1468
|
return []
|
|
@@ -1466,10 +1472,10 @@ def _convert_typedefs(typedefs: Optional[Iterable[TypeDef]]) -> List[Mapping[str
|
|
|
1466
1472
|
def _convert_typedef(typedef: TypeDef) -> Mapping[str, Any]:
|
|
1467
1473
|
"""Convert a type def."""
|
|
1468
1474
|
# TODO add more later
|
|
1469
|
-
return typedef.reference.
|
|
1475
|
+
return typedef.reference.model_dump()
|
|
1470
1476
|
|
|
1471
1477
|
|
|
1472
|
-
def _convert_synonym_typedefs(synonym_typedefs: Optional[Iterable[SynonymTypeDef]]) ->
|
|
1478
|
+
def _convert_synonym_typedefs(synonym_typedefs: Optional[Iterable[SynonymTypeDef]]) -> list[str]:
|
|
1473
1479
|
"""Convert the synonym type defs."""
|
|
1474
1480
|
if not synonym_typedefs:
|
|
1475
1481
|
return []
|
pyobo/struct/typedef.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
1
|
"""Default typedefs, references, and other structures."""
|
|
4
2
|
|
|
3
|
+
from collections.abc import Iterable
|
|
5
4
|
from dataclasses import dataclass, field
|
|
6
|
-
from typing import
|
|
5
|
+
from typing import Optional, Union
|
|
7
6
|
|
|
8
7
|
from .reference import Reference, Referenced
|
|
9
8
|
from ..identifier_utils import normalize_curie
|
|
@@ -71,11 +70,11 @@ class TypeDef(Referenced):
|
|
|
71
70
|
is_symmetric: Optional[bool] = None
|
|
72
71
|
domain: Optional[Reference] = None
|
|
73
72
|
range: Optional[Reference] = None
|
|
74
|
-
parents:
|
|
75
|
-
xrefs:
|
|
73
|
+
parents: list[Reference] = field(default_factory=list)
|
|
74
|
+
xrefs: list[Reference] = field(default_factory=list)
|
|
76
75
|
inverse: Optional[Reference] = None
|
|
77
76
|
created_by: Optional[str] = None
|
|
78
|
-
holds_over_chain: Optional[
|
|
77
|
+
holds_over_chain: Optional[list[Reference]] = None
|
|
79
78
|
#: Whether this relationship is a metadata tag. Properties that are marked as metadata tags are
|
|
80
79
|
#: used to record object metadata. Object metadata is additional information about an object
|
|
81
80
|
#: that is useful to track, but does not impact the definition of the object or how it should
|
|
@@ -83,7 +82,7 @@ class TypeDef(Referenced):
|
|
|
83
82
|
#: structured notes about a term, for example.
|
|
84
83
|
is_metadata_tag: Optional[bool] = None
|
|
85
84
|
|
|
86
|
-
def __hash__(self) -> int:
|
|
85
|
+
def __hash__(self) -> int:
|
|
87
86
|
return hash((self.__class__, self.prefix, self.identifier))
|
|
88
87
|
|
|
89
88
|
def iterate_obo_lines(self) -> Iterable[str]:
|
|
@@ -140,10 +139,10 @@ class TypeDef(Referenced):
|
|
|
140
139
|
return cls.from_triple(prefix=prefix, identifier=identifier, name=name)
|
|
141
140
|
|
|
142
141
|
|
|
143
|
-
RelationHint = Union[Reference, TypeDef,
|
|
142
|
+
RelationHint = Union[Reference, TypeDef, tuple[str, str], str]
|
|
144
143
|
|
|
145
144
|
|
|
146
|
-
def get_reference_tuple(relation: RelationHint) ->
|
|
145
|
+
def get_reference_tuple(relation: RelationHint) -> tuple[str, str]:
|
|
147
146
|
"""Get tuple for typedef/reference."""
|
|
148
147
|
if isinstance(relation, (Reference, TypeDef)):
|
|
149
148
|
return relation.prefix, relation.identifier
|
|
@@ -366,7 +365,7 @@ has_homepage = TypeDef(
|
|
|
366
365
|
reference=Reference(prefix="foaf", identifier="homepage", name="homepage"), is_metadata_tag=True
|
|
367
366
|
)
|
|
368
367
|
|
|
369
|
-
default_typedefs:
|
|
368
|
+
default_typedefs: dict[tuple[str, str], TypeDef] = {
|
|
370
369
|
v.pair: v for k, v in locals().items() if isinstance(v, TypeDef)
|
|
371
370
|
}
|
|
372
371
|
|
pyobo/struct/utils.py
CHANGED
pyobo/utils/__init__.py
CHANGED