pyobo 0.11.2__py3-none-any.whl → 0.12.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.
Files changed (227) hide show
  1. pyobo/.DS_Store +0 -0
  2. pyobo/__init__.py +95 -20
  3. pyobo/__main__.py +0 -0
  4. pyobo/api/__init__.py +81 -10
  5. pyobo/api/alts.py +52 -42
  6. pyobo/api/combine.py +39 -0
  7. pyobo/api/edges.py +68 -0
  8. pyobo/api/hierarchy.py +231 -203
  9. pyobo/api/metadata.py +14 -19
  10. pyobo/api/names.py +207 -127
  11. pyobo/api/properties.py +117 -113
  12. pyobo/api/relations.py +68 -94
  13. pyobo/api/species.py +24 -21
  14. pyobo/api/typedefs.py +11 -11
  15. pyobo/api/utils.py +66 -13
  16. pyobo/api/xrefs.py +108 -114
  17. pyobo/cli/__init__.py +0 -0
  18. pyobo/cli/cli.py +35 -50
  19. pyobo/cli/database.py +183 -161
  20. pyobo/{xrefdb/xrefs_pipeline.py → cli/database_utils.py} +54 -73
  21. pyobo/cli/lookup.py +163 -195
  22. pyobo/cli/utils.py +19 -6
  23. pyobo/constants.py +102 -3
  24. pyobo/getters.py +196 -118
  25. pyobo/gilda_utils.py +79 -200
  26. pyobo/identifier_utils/__init__.py +41 -0
  27. pyobo/identifier_utils/api.py +296 -0
  28. pyobo/identifier_utils/model.py +130 -0
  29. pyobo/identifier_utils/preprocessing.json +812 -0
  30. pyobo/identifier_utils/preprocessing.py +61 -0
  31. pyobo/identifier_utils/relations/__init__.py +8 -0
  32. pyobo/identifier_utils/relations/api.py +162 -0
  33. pyobo/identifier_utils/relations/data.json +5824 -0
  34. pyobo/identifier_utils/relations/data_owl.json +57 -0
  35. pyobo/identifier_utils/relations/data_rdf.json +1 -0
  36. pyobo/identifier_utils/relations/data_rdfs.json +7 -0
  37. pyobo/mocks.py +9 -6
  38. pyobo/ner/__init__.py +9 -0
  39. pyobo/ner/api.py +72 -0
  40. pyobo/ner/normalizer.py +33 -0
  41. pyobo/obographs.py +43 -39
  42. pyobo/plugins.py +5 -4
  43. pyobo/py.typed +0 -0
  44. pyobo/reader.py +1358 -395
  45. pyobo/reader_utils.py +155 -0
  46. pyobo/resource_utils.py +42 -22
  47. pyobo/resources/__init__.py +0 -0
  48. pyobo/resources/goc.py +75 -0
  49. pyobo/resources/goc.tsv +188 -0
  50. pyobo/resources/ncbitaxon.py +4 -5
  51. pyobo/resources/ncbitaxon.tsv.gz +0 -0
  52. pyobo/resources/ro.py +3 -2
  53. pyobo/resources/ro.tsv +0 -0
  54. pyobo/resources/so.py +0 -0
  55. pyobo/resources/so.tsv +0 -0
  56. pyobo/sources/README.md +12 -8
  57. pyobo/sources/__init__.py +52 -29
  58. pyobo/sources/agrovoc.py +0 -0
  59. pyobo/sources/antibodyregistry.py +11 -12
  60. pyobo/sources/bigg/__init__.py +13 -0
  61. pyobo/sources/bigg/bigg_compartment.py +81 -0
  62. pyobo/sources/bigg/bigg_metabolite.py +229 -0
  63. pyobo/sources/bigg/bigg_model.py +46 -0
  64. pyobo/sources/bigg/bigg_reaction.py +77 -0
  65. pyobo/sources/biogrid.py +1 -2
  66. pyobo/sources/ccle.py +7 -12
  67. pyobo/sources/cgnc.py +0 -5
  68. pyobo/sources/chebi.py +1 -1
  69. pyobo/sources/chembl/__init__.py +9 -0
  70. pyobo/sources/{chembl.py → chembl/chembl_compound.py} +13 -25
  71. pyobo/sources/chembl/chembl_target.py +160 -0
  72. pyobo/sources/civic_gene.py +55 -15
  73. pyobo/sources/clinicaltrials.py +160 -0
  74. pyobo/sources/complexportal.py +24 -24
  75. pyobo/sources/conso.py +14 -22
  76. pyobo/sources/cpt.py +0 -0
  77. pyobo/sources/credit.py +1 -9
  78. pyobo/sources/cvx.py +27 -5
  79. pyobo/sources/depmap.py +9 -12
  80. pyobo/sources/dictybase_gene.py +2 -7
  81. pyobo/sources/drugbank/__init__.py +9 -0
  82. pyobo/sources/{drugbank.py → drugbank/drugbank.py} +11 -16
  83. pyobo/sources/{drugbank_salt.py → drugbank/drugbank_salt.py} +3 -8
  84. pyobo/sources/drugcentral.py +17 -13
  85. pyobo/sources/expasy.py +31 -34
  86. pyobo/sources/famplex.py +13 -18
  87. pyobo/sources/flybase.py +3 -8
  88. pyobo/sources/gard.py +62 -0
  89. pyobo/sources/geonames/__init__.py +9 -0
  90. pyobo/sources/geonames/features.py +28 -0
  91. pyobo/sources/{geonames.py → geonames/geonames.py} +87 -26
  92. pyobo/sources/geonames/utils.py +115 -0
  93. pyobo/sources/gmt_utils.py +6 -7
  94. pyobo/sources/go.py +20 -13
  95. pyobo/sources/gtdb.py +154 -0
  96. pyobo/sources/gwascentral/__init__.py +9 -0
  97. pyobo/sources/{gwascentral_phenotype.py → gwascentral/gwascentral_phenotype.py} +5 -7
  98. pyobo/sources/{gwascentral_study.py → gwascentral/gwascentral_study.py} +1 -7
  99. pyobo/sources/hgnc/__init__.py +9 -0
  100. pyobo/sources/{hgnc.py → hgnc/hgnc.py} +56 -70
  101. pyobo/sources/{hgncgenefamily.py → hgnc/hgncgenefamily.py} +8 -18
  102. pyobo/sources/icd/__init__.py +9 -0
  103. pyobo/sources/{icd10.py → icd/icd10.py} +35 -37
  104. pyobo/sources/icd/icd11.py +148 -0
  105. pyobo/sources/{icd_utils.py → icd/icd_utils.py} +66 -20
  106. pyobo/sources/interpro.py +4 -9
  107. pyobo/sources/itis.py +0 -5
  108. pyobo/sources/kegg/__init__.py +0 -0
  109. pyobo/sources/kegg/api.py +16 -38
  110. pyobo/sources/kegg/genes.py +9 -20
  111. pyobo/sources/kegg/genome.py +1 -7
  112. pyobo/sources/kegg/pathway.py +9 -21
  113. pyobo/sources/mesh.py +58 -24
  114. pyobo/sources/mgi.py +3 -10
  115. pyobo/sources/mirbase/__init__.py +11 -0
  116. pyobo/sources/{mirbase.py → mirbase/mirbase.py} +8 -11
  117. pyobo/sources/{mirbase_constants.py → mirbase/mirbase_constants.py} +0 -0
  118. pyobo/sources/{mirbase_family.py → mirbase/mirbase_family.py} +4 -8
  119. pyobo/sources/{mirbase_mature.py → mirbase/mirbase_mature.py} +3 -7
  120. pyobo/sources/msigdb.py +74 -39
  121. pyobo/sources/ncbi/__init__.py +9 -0
  122. pyobo/sources/ncbi/ncbi_gc.py +162 -0
  123. pyobo/sources/{ncbigene.py → ncbi/ncbigene.py} +18 -19
  124. pyobo/sources/nih_reporter.py +60 -0
  125. pyobo/sources/nlm/__init__.py +9 -0
  126. pyobo/sources/nlm/nlm_catalog.py +48 -0
  127. pyobo/sources/nlm/nlm_publisher.py +36 -0
  128. pyobo/sources/nlm/utils.py +116 -0
  129. pyobo/sources/npass.py +6 -8
  130. pyobo/sources/omim_ps.py +10 -3
  131. pyobo/sources/pathbank.py +4 -8
  132. pyobo/sources/pfam/__init__.py +9 -0
  133. pyobo/sources/{pfam.py → pfam/pfam.py} +3 -8
  134. pyobo/sources/{pfam_clan.py → pfam/pfam_clan.py} +2 -7
  135. pyobo/sources/pharmgkb/__init__.py +15 -0
  136. pyobo/sources/pharmgkb/pharmgkb_chemical.py +89 -0
  137. pyobo/sources/pharmgkb/pharmgkb_disease.py +77 -0
  138. pyobo/sources/pharmgkb/pharmgkb_gene.py +108 -0
  139. pyobo/sources/pharmgkb/pharmgkb_pathway.py +63 -0
  140. pyobo/sources/pharmgkb/pharmgkb_variant.py +84 -0
  141. pyobo/sources/pharmgkb/utils.py +86 -0
  142. pyobo/sources/pid.py +1 -6
  143. pyobo/sources/pombase.py +6 -10
  144. pyobo/sources/pubchem.py +4 -9
  145. pyobo/sources/reactome.py +5 -11
  146. pyobo/sources/rgd.py +11 -16
  147. pyobo/sources/rhea.py +37 -36
  148. pyobo/sources/ror.py +69 -42
  149. pyobo/sources/selventa/__init__.py +0 -0
  150. pyobo/sources/selventa/schem.py +4 -7
  151. pyobo/sources/selventa/scomp.py +1 -6
  152. pyobo/sources/selventa/sdis.py +4 -7
  153. pyobo/sources/selventa/sfam.py +1 -6
  154. pyobo/sources/sgd.py +6 -11
  155. pyobo/sources/signor/__init__.py +7 -0
  156. pyobo/sources/signor/download.py +41 -0
  157. pyobo/sources/signor/signor_complexes.py +105 -0
  158. pyobo/sources/slm.py +12 -15
  159. pyobo/sources/umls/__init__.py +7 -1
  160. pyobo/sources/umls/__main__.py +0 -0
  161. pyobo/sources/umls/get_synonym_types.py +20 -4
  162. pyobo/sources/umls/sty.py +57 -0
  163. pyobo/sources/umls/synonym_types.tsv +1 -1
  164. pyobo/sources/umls/umls.py +18 -22
  165. pyobo/sources/unimod.py +46 -0
  166. pyobo/sources/uniprot/__init__.py +1 -1
  167. pyobo/sources/uniprot/uniprot.py +40 -32
  168. pyobo/sources/uniprot/uniprot_ptm.py +4 -34
  169. pyobo/sources/utils.py +3 -2
  170. pyobo/sources/wikipathways.py +7 -10
  171. pyobo/sources/zfin.py +5 -10
  172. pyobo/ssg/__init__.py +12 -16
  173. pyobo/ssg/base.html +0 -0
  174. pyobo/ssg/index.html +26 -13
  175. pyobo/ssg/term.html +12 -2
  176. pyobo/ssg/typedef.html +0 -0
  177. pyobo/struct/__init__.py +54 -8
  178. pyobo/struct/functional/__init__.py +1 -0
  179. pyobo/struct/functional/dsl.py +2572 -0
  180. pyobo/struct/functional/macros.py +423 -0
  181. pyobo/struct/functional/obo_to_functional.py +385 -0
  182. pyobo/struct/functional/ontology.py +270 -0
  183. pyobo/struct/functional/utils.py +112 -0
  184. pyobo/struct/reference.py +331 -136
  185. pyobo/struct/struct.py +1413 -643
  186. pyobo/struct/struct_utils.py +1078 -0
  187. pyobo/struct/typedef.py +162 -210
  188. pyobo/struct/utils.py +12 -5
  189. pyobo/struct/vocabulary.py +138 -0
  190. pyobo/utils/__init__.py +0 -0
  191. pyobo/utils/cache.py +13 -11
  192. pyobo/utils/io.py +17 -31
  193. pyobo/utils/iter.py +5 -5
  194. pyobo/utils/misc.py +41 -53
  195. pyobo/utils/ndex_utils.py +0 -0
  196. pyobo/utils/path.py +76 -70
  197. pyobo/version.py +3 -3
  198. {pyobo-0.11.2.dist-info → pyobo-0.12.0.dist-info}/METADATA +228 -229
  199. pyobo-0.12.0.dist-info/RECORD +202 -0
  200. pyobo-0.12.0.dist-info/WHEEL +4 -0
  201. {pyobo-0.11.2.dist-info → pyobo-0.12.0.dist-info}/entry_points.txt +1 -0
  202. pyobo-0.12.0.dist-info/licenses/LICENSE +21 -0
  203. pyobo/aws.py +0 -162
  204. pyobo/cli/aws.py +0 -47
  205. pyobo/identifier_utils.py +0 -142
  206. pyobo/normalizer.py +0 -232
  207. pyobo/registries/__init__.py +0 -16
  208. pyobo/registries/metaregistry.json +0 -507
  209. pyobo/registries/metaregistry.py +0 -135
  210. pyobo/sources/icd11.py +0 -105
  211. pyobo/xrefdb/__init__.py +0 -1
  212. pyobo/xrefdb/canonicalizer.py +0 -214
  213. pyobo/xrefdb/priority.py +0 -59
  214. pyobo/xrefdb/sources/__init__.py +0 -60
  215. pyobo/xrefdb/sources/biomappings.py +0 -36
  216. pyobo/xrefdb/sources/cbms2019.py +0 -91
  217. pyobo/xrefdb/sources/chembl.py +0 -83
  218. pyobo/xrefdb/sources/compath.py +0 -82
  219. pyobo/xrefdb/sources/famplex.py +0 -64
  220. pyobo/xrefdb/sources/gilda.py +0 -50
  221. pyobo/xrefdb/sources/intact.py +0 -113
  222. pyobo/xrefdb/sources/ncit.py +0 -133
  223. pyobo/xrefdb/sources/pubchem.py +0 -27
  224. pyobo/xrefdb/sources/wikidata.py +0 -116
  225. pyobo-0.11.2.dist-info/RECORD +0 -157
  226. pyobo-0.11.2.dist-info/WHEEL +0 -5
  227. pyobo-0.11.2.dist-info/top_level.txt +0 -1
pyobo/api/hierarchy.py CHANGED
@@ -1,291 +1,319 @@
1
1
  """High-level API for hierarchies."""
2
2
 
3
3
  import logging
4
+ import warnings
4
5
  from collections.abc import Iterable
5
6
  from functools import lru_cache
6
- from typing import Optional
7
+ from typing import overload
7
8
 
8
9
  import networkx as nx
9
-
10
- from .names import get_name
11
- from .properties import get_filtered_properties_mapping
12
- from .relations import get_filtered_relations_df
13
- from ..identifier_utils import wrap_norm_prefix
14
- from ..struct import TypeDef, has_member, is_a, part_of
10
+ from curies import ReferenceTuple
11
+ from typing_extensions import NotRequired, Unpack
12
+
13
+ from .edges import get_edges
14
+ from .names import get_name, get_references
15
+ from .properties import get_literal_properties
16
+ from .utils import _get_pi
17
+ from ..constants import GetOntologyKwargs
18
+ from ..struct import has_member, has_part, is_a, member_of, part_of
15
19
  from ..struct.reference import Reference
20
+ from ..struct.struct_utils import ReferenceHint, _ensure_ref
16
21
 
17
22
  __all__ = [
23
+ "get_ancestors",
24
+ "get_children",
25
+ "get_descendants",
18
26
  "get_hierarchy",
19
27
  "get_subhierarchy",
20
- "get_descendants",
21
- "get_ancestors",
22
28
  "has_ancestor",
23
29
  "is_descendent",
24
- "get_children",
25
30
  ]
26
31
 
27
-
28
32
  logger = logging.getLogger(__name__)
29
33
 
30
34
 
35
+ class HierarchyKwargs(GetOntologyKwargs):
36
+ """Keyword argument hints for hierarchy getter functions."""
37
+
38
+ include_part_of: NotRequired[bool]
39
+ include_has_member: NotRequired[bool]
40
+
41
+
31
42
  def get_hierarchy(
32
43
  prefix: str,
33
44
  *,
34
- include_part_of: bool = True,
35
- include_has_member: bool = False,
36
- extra_relations: Optional[Iterable[TypeDef]] = None,
37
- properties: Optional[Iterable[str]] = None,
38
- use_tqdm: bool = False,
39
- force: bool = False,
40
- version: Optional[str] = None,
45
+ extra_relations: Iterable[ReferenceHint] | None = None,
46
+ properties: Iterable[ReferenceHint] | None = None,
47
+ **kwargs: Unpack[HierarchyKwargs],
41
48
  ) -> nx.DiGraph:
42
49
  """Get hierarchy of parents as a directed graph.
43
50
 
44
51
  :param prefix: The name of the namespace.
45
- :param include_part_of: Add "part of" relations. Only works if the relations are properly
46
- defined using bfo:0000050 ! part of or bfo:0000051 ! has part
47
- :param include_has_member: Add "has member" relations. These aren't part of the BFO, but
48
- are hacked into PyOBO using :data:`pyobo.struct.typedef.has_member` for relationships like
49
- from protein families to their actual proteins.
50
- :param extra_relations: Other relations that you want to include in the hierarchy. For
51
- example, it might be useful to include the positively_regulates
52
- :param properties: Properties to include in the data part of each node. For example, might want
53
- to include SMILES strings with the ChEBI tree.
54
- :param use_tqdm: Show a progress bar
52
+ :param include_part_of: Add "part of" relations. Only works if the relations are
53
+ properly defined using bfo:0000050 ! part of or bfo:0000051 ! has part
54
+ :param include_has_member: Add "has member" relations. These aren't part of the BFO,
55
+ but are hacked into PyOBO using :data:`pyobo.struct.typedef.has_member` for
56
+ relationships like from protein families to their actual proteins.
57
+ :param extra_relations: Other relations that you want to include in the hierarchy.
58
+ For example, it might be useful to include the positively_regulates
59
+ :param properties: Properties to include in the data part of each node. For example,
60
+ might want to include SMILES strings with the ChEBI tree.
55
61
  :param force: should the resources be reloaded when extracting relations?
62
+
56
63
  :returns: A directional graph representing the hierarchy
57
64
 
58
- This function thinly wraps :func:`_get_hierarchy_helper` to make it easier to work with the lru_cache mechanism.
65
+ This function thinly wraps :func:`_get_hierarchy_helper` to make it easier to work
66
+ with the lru_cache mechanism.
59
67
  """
60
68
  return _get_hierarchy_helper(
61
69
  prefix=prefix,
62
- include_part_of=include_part_of,
63
- include_has_member=include_has_member,
64
- extra_relations=tuple(sorted(extra_relations or [], key=lambda t: t.curie)),
65
- properties=tuple(sorted(properties or [])),
66
- use_tqdm=use_tqdm,
67
- force=force,
68
- version=version,
70
+ extra_relations=_tp(prefix, extra_relations),
71
+ properties=_tp(prefix, properties),
72
+ **kwargs,
73
+ )
74
+
75
+
76
+ def _tp(prefix: str, references: Iterable[ReferenceHint] | None) -> tuple[Reference, ...]:
77
+ return tuple(
78
+ sorted(_ensure_ref(reference, ontology_prefix=prefix) for reference in references or [])
69
79
  )
70
80
 
71
81
 
72
82
  @lru_cache
73
- @wrap_norm_prefix
74
83
  def _get_hierarchy_helper(
75
84
  prefix: str,
76
85
  *,
77
- extra_relations: tuple[TypeDef, ...],
78
- properties: tuple[str, ...],
79
- include_part_of: bool,
80
- include_has_member: bool,
81
- use_tqdm: bool,
82
- force: bool = False,
83
- version: Optional[str] = None,
86
+ extra_relations: tuple[Reference, ...],
87
+ properties: tuple[Reference, ...],
88
+ include_part_of: bool = False,
89
+ include_has_member: bool = False,
90
+ **kwargs: Unpack[GetOntologyKwargs],
84
91
  ) -> nx.DiGraph:
92
+ predicates, reverse_predicates = _get_predicate_sets(
93
+ extra_relations, include_part_of, include_has_member
94
+ )
95
+
85
96
  rv = nx.DiGraph()
97
+ rv.add_nodes_from(get_references(prefix, **kwargs))
86
98
 
87
- is_a_df = get_filtered_relations_df(
88
- prefix=prefix,
89
- relation=is_a,
90
- use_tqdm=use_tqdm,
91
- force=force,
92
- version=version,
93
- )
94
- for source_id, target_ns, target_id in is_a_df.values:
95
- rv.add_edge(f"{prefix}:{source_id}", f"{target_ns}:{target_id}", relation="is_a")
99
+ for s, p, o in get_edges(prefix, **kwargs):
100
+ if p in predicates:
101
+ rv.add_edge(s, o, relation=p)
102
+ elif p in reverse_predicates:
103
+ rv.add_edge(o, s, relation=p)
96
104
 
97
- if include_has_member:
98
- has_member_df = get_filtered_relations_df(
99
- prefix=prefix,
100
- relation=has_member,
101
- use_tqdm=use_tqdm,
102
- force=force,
103
- version=version,
104
- )
105
- for target_id, source_ns, source_id in has_member_df.values:
106
- rv.add_edge(f"{source_ns}:{source_id}", f"{prefix}:{target_id}", relation="is_a")
105
+ properties_ = set(properties)
106
+ for s, p, op in get_literal_properties(prefix, **kwargs):
107
+ if s in rv and p in properties_:
108
+ rv.nodes[s][p] = op.value
109
+
110
+ return rv
107
111
 
112
+
113
+ def _get_predicate_sets(
114
+ extra_relations: Iterable[Reference], include_part_of: bool, include_has_member: bool
115
+ ) -> tuple[set[Reference], set[Reference]]:
116
+ predicates: set[Reference] = {is_a.reference, *extra_relations}
117
+ reverse_predicates: set[Reference] = set()
108
118
  if include_part_of:
109
- part_of_df = get_filtered_relations_df(
110
- prefix=prefix,
111
- relation=part_of,
112
- use_tqdm=use_tqdm,
113
- force=force,
114
- version=version,
115
- )
116
- for source_id, target_ns, target_id in part_of_df.values:
117
- rv.add_edge(f"{prefix}:{source_id}", f"{target_ns}:{target_id}", relation="part_of")
118
-
119
- has_part_df = get_filtered_relations_df(
120
- prefix=prefix,
121
- relation=part_of,
122
- use_tqdm=use_tqdm,
123
- force=force,
124
- version=version,
125
- )
126
- for target_id, source_ns, source_id in has_part_df.values:
127
- rv.add_edge(f"{source_ns}:{source_id}", f"{prefix}:{target_id}", relation="part_of")
128
-
129
- for relation in extra_relations:
130
- if not isinstance(relation, (TypeDef, Reference)):
131
- raise TypeError
132
- relation_df = get_filtered_relations_df(
133
- prefix=prefix,
134
- relation=relation,
135
- use_tqdm=use_tqdm,
136
- force=force,
137
- version=version,
138
- )
139
- for source_id, target_ns, target_id in relation_df.values:
140
- rv.add_edge(
141
- f"{prefix}:{source_id}", f"{target_ns}:{target_id}", relation=relation.identifier
142
- )
143
-
144
- for prop in properties:
145
- props = get_filtered_properties_mapping(
146
- prefix=prefix, prop=prop, use_tqdm=use_tqdm, force=force
147
- )
148
- for identifier, value in props.items():
149
- curie = f"{prefix}:{identifier}"
150
- if curie in rv:
151
- rv.nodes[curie][prop] = value
119
+ predicates.add(part_of.reference)
120
+ reverse_predicates.add(has_part.reference)
121
+ if include_has_member:
122
+ predicates.add(has_member.reference)
123
+ reverse_predicates.add(member_of.reference)
124
+ return predicates, reverse_predicates
152
125
 
153
- return rv
154
126
 
127
+ # docstr-coverage:excused `overload`
128
+ @overload
129
+ def is_descendent(
130
+ prefix: str,
131
+ identifier: str,
132
+ ancestor_prefix: str = ...,
133
+ ancestor_identifier: str = ...,
134
+ /,
135
+ **kwargs: Unpack[HierarchyKwargs],
136
+ ) -> bool: ...
155
137
 
138
+
139
+ # docstr-coverage:excused `overload`
140
+ @overload
156
141
  def is_descendent(
157
- prefix, identifier, ancestor_prefix, ancestor_identifier, *, version: Optional[str] = None
142
+ descendant: Reference,
143
+ ancestor: Reference,
144
+ _1: None = ...,
145
+ _2: None = ...,
146
+ /,
147
+ **kwargs: Unpack[HierarchyKwargs],
148
+ ) -> bool: ...
149
+
150
+
151
+ def is_descendent(
152
+ prefix: str | Reference,
153
+ identifier: str | Reference,
154
+ ancestor_prefix: str | None = None,
155
+ ancestor_identifier: str | None = None,
156
+ /,
157
+ **kwargs: Unpack[HierarchyKwargs],
158
158
  ) -> bool:
159
159
  """Check that the first identifier has the second as a descendent.
160
160
 
161
- Check that go:0070246 ! natural killer cell apoptotic process is a
162
- descendant of go:0006915 ! apoptotic process::
161
+ :param prefix: The prefix for the descendant
162
+ :param identifier: The local unique identifier for the descendant
163
+ :param ancestor_prefix: The prefix for the ancestor
164
+ :param ancestor_identifier: The local unique identifier for the ancestor
165
+ :param kwargs: Keyword arguments for :func:`get_hierarchy`
166
+ :return: If the decendant has the given ancestor
167
+
168
+ Check that ``GO:0070246`` (natural killer cell apoptotic process) is a descendant of
169
+ ``GO:0006915`` (apoptotic process)
170
+
171
+ >>> nk_apoptosis = Reference.from_curie(
172
+ ... "GO:0070246", name="natural killer cell apoptotic process"
173
+ ... )
174
+ >>> apoptosis = Reference.from_curie("GO:0006915", name="apoptotic process")
175
+ >>> assert is_descendent(nk_apoptosis, apoptosis)
176
+
177
+ Using deprecated old-style arguments:
178
+
163
179
  >>> assert is_descendent("go", "0070246", "go", "0006915")
164
180
  """
165
- descendants = get_descendants(ancestor_prefix, ancestor_identifier, version=version)
166
- return descendants is not None and f"{prefix}:{identifier}" in descendants
181
+ descendant, ancestor = _get_double_reference(
182
+ prefix, identifier, ancestor_prefix, ancestor_identifier
183
+ )
184
+ descendants = get_descendants(ancestor, **kwargs)
185
+ return descendants is not None and descendant in descendants
167
186
 
168
187
 
169
188
  @lru_cache
170
189
  def get_descendants(
171
- prefix: str,
172
- identifier: Optional[str] = None,
173
- include_part_of: bool = True,
174
- include_has_member: bool = False,
175
- use_tqdm: bool = False,
176
- force: bool = False,
177
- **kwargs,
178
- ) -> Optional[set[str]]:
190
+ prefix: str | Reference | ReferenceTuple,
191
+ identifier: str | None = None,
192
+ /,
193
+ **kwargs: Unpack[HierarchyKwargs],
194
+ ) -> set[Reference] | None:
179
195
  """Get all the descendants (children) of the term as CURIEs."""
180
- curie, prefix, identifier = _pic(prefix, identifier)
181
- hierarchy = get_hierarchy(
182
- prefix=prefix,
183
- include_has_member=include_has_member,
184
- include_part_of=include_part_of,
185
- use_tqdm=use_tqdm,
186
- force=force,
187
- **kwargs,
188
- )
189
- if curie not in hierarchy:
196
+ t = _get_pi(prefix, identifier)
197
+ hierarchy = get_hierarchy(prefix=t.prefix, **kwargs)
198
+ if t not in hierarchy:
190
199
  return None
191
- return nx.ancestors(hierarchy, curie) # note this is backwards
192
-
193
-
194
- def _pic(prefix, identifier=None) -> tuple[str, str, str]:
195
- if identifier is None:
196
- curie = prefix
197
- prefix, identifier = prefix.split(":")
198
- else:
199
- curie = f"{prefix}:{identifier}"
200
- return curie, prefix, identifier
200
+ return nx.ancestors(hierarchy, t) # note this is backwards
201
201
 
202
202
 
203
203
  @lru_cache
204
204
  def get_children(
205
- prefix: str,
206
- identifier: Optional[str] = None,
207
- include_part_of: bool = True,
208
- include_has_member: bool = False,
209
- use_tqdm: bool = False,
210
- force: bool = False,
211
- **kwargs,
212
- ) -> Optional[set[str]]:
205
+ prefix: str | Reference | ReferenceTuple,
206
+ identifier: str | None = None,
207
+ /,
208
+ **kwargs: Unpack[HierarchyKwargs],
209
+ ) -> set[Reference] | None:
213
210
  """Get all the descendants (children) of the term as CURIEs."""
214
- curie, prefix, identifier = _pic(prefix, identifier)
215
- hierarchy = get_hierarchy(
216
- prefix=prefix,
217
- include_has_member=include_has_member,
218
- include_part_of=include_part_of,
219
- use_tqdm=use_tqdm,
220
- force=force,
221
- **kwargs,
222
- )
223
- if curie not in hierarchy:
211
+ t = _get_pi(prefix, identifier)
212
+ hierarchy = get_hierarchy(prefix=t.prefix, **kwargs)
213
+ if t not in hierarchy:
224
214
  return None
225
- return set(hierarchy.predecessors(curie))
215
+ return set(hierarchy.predecessors(t))
216
+
217
+
218
+ # docstr-coverage:excused `overload`
219
+ @overload
220
+ def has_ancestor(
221
+ prefix: str,
222
+ identifier: str,
223
+ ancestor_prefix: str = ...,
224
+ ancestor_identifier: str = ...,
225
+ /,
226
+ **kwargs: Unpack[HierarchyKwargs],
227
+ ) -> bool: ...
228
+
229
+
230
+ # docstr-coverage:excused `overload`
231
+ @overload
232
+ def has_ancestor(
233
+ descendant: Reference,
234
+ ancestor: Reference,
235
+ _1: None = ...,
236
+ _2: None = ...,
237
+ /,
238
+ **kwargs: Unpack[HierarchyKwargs],
239
+ ) -> bool: ...
226
240
 
227
241
 
228
242
  def has_ancestor(
229
- prefix, identifier, ancestor_prefix, ancestor_identifier, *, version: Optional[str] = None
243
+ prefix: str | Reference,
244
+ identifier: str | Reference,
245
+ ancestor_prefix: str | None = None,
246
+ ancestor_identifier: str | None = None,
247
+ /,
248
+ **kwargs: Unpack[HierarchyKwargs],
230
249
  ) -> bool:
231
250
  """Check that the first identifier has the second as an ancestor.
232
251
 
233
- Check that go:0008219 ! cell death is an ancestor of go:0006915 ! apoptotic process::
252
+ :param prefix: The prefix for the descendant
253
+ :param identifier: The local unique identifier for the descendant
254
+ :param ancestor_prefix: The prefix for the ancestor
255
+ :param ancestor_identifier: The local unique identifier for the ancestor
256
+ :param kwargs: Keyword arguments for :func:`get_hierarchy`
257
+ :return: If the decendant has the given ancestor
258
+
259
+ Check that ``GO:0008219`` (cell death) is an ancestor of ``GO:0006915``
260
+ (apoptotic process):
261
+
262
+ >>> apoptosis = Reference.from_curie("GO:0006915", name="apoptotic process")
263
+ >>> cell_death = Reference.from_curie("GO:0008219", name="cell death")
264
+ >>> assert has_ancestor(apoptosis, cell_death)
265
+
266
+ The same, using the deprecated argumentation style:
267
+
234
268
  >>> assert has_ancestor("go", "0006915", "go", "0008219")
235
269
  """
236
- ancestors = get_ancestors(prefix, identifier, version=version)
237
- return ancestors is not None and f"{ancestor_prefix}:{ancestor_identifier}" in ancestors
270
+ descendant, ancestor = _get_double_reference(
271
+ prefix, identifier, ancestor_prefix, ancestor_identifier
272
+ )
273
+ ancestors = get_ancestors(descendant, **kwargs)
274
+ return ancestors is not None and ancestor in ancestors
275
+
276
+
277
+ def _get_double_reference(
278
+ a: str | Reference, b: str | Reference, c: str | None, d: str | None
279
+ ) -> tuple[Reference, Reference]:
280
+ if isinstance(a, Reference) and isinstance(b, Reference):
281
+ return a, b
282
+ elif all(isinstance(x, str) for x in (a, b, c, d)):
283
+ warnings.warn("passing strings is deprecated", DeprecationWarning, stacklevel=2)
284
+ return Reference(prefix=a, identifier=b), Reference(prefix=c, identifier=d)
285
+ else:
286
+ raise TypeError
238
287
 
239
288
 
240
289
  @lru_cache
241
290
  def get_ancestors(
242
- prefix: str,
243
- identifier: Optional[str] = None,
244
- include_part_of: bool = True,
245
- include_has_member: bool = False,
246
- use_tqdm: bool = False,
247
- force: bool = False,
248
- **kwargs,
249
- ) -> Optional[set[str]]:
291
+ prefix: str | Reference | ReferenceTuple,
292
+ identifier: str | None = None,
293
+ /,
294
+ **kwargs: Unpack[HierarchyKwargs],
295
+ ) -> set[Reference] | None:
250
296
  """Get all the ancestors (parents) of the term as CURIEs."""
251
- curie, prefix, identifier = _pic(prefix, identifier)
252
- hierarchy = get_hierarchy(
253
- prefix=prefix,
254
- include_has_member=include_has_member,
255
- include_part_of=include_part_of,
256
- use_tqdm=use_tqdm,
257
- force=force,
258
- **kwargs,
259
- )
260
- if curie not in hierarchy:
297
+ t = _get_pi(prefix, identifier)
298
+ hierarchy = get_hierarchy(prefix=t.prefix, **kwargs)
299
+ if t not in hierarchy:
261
300
  return None
262
- return nx.descendants(hierarchy, curie) # note this is backwards
301
+
302
+ return nx.descendants(hierarchy, t) # note this is backwards
263
303
 
264
304
 
265
305
  def get_subhierarchy(
266
- prefix: str,
267
- identifier: Optional[str] = None,
268
- include_part_of: bool = True,
269
- include_has_member: bool = False,
270
- use_tqdm: bool = False,
271
- force: bool = False,
272
- **kwargs,
306
+ prefix: str | Reference | ReferenceTuple,
307
+ identifier: str | None = None,
308
+ /,
309
+ **kwargs: Unpack[HierarchyKwargs],
273
310
  ) -> nx.DiGraph:
274
311
  """Get the subhierarchy for a given node."""
275
- curie, prefix, identifier = _pic(prefix, identifier)
276
- hierarchy = get_hierarchy(
277
- prefix=prefix,
278
- include_has_member=include_has_member,
279
- include_part_of=include_part_of,
280
- use_tqdm=use_tqdm,
281
- force=force,
282
- **kwargs,
283
- )
284
- logger.info(
285
- "getting descendants of %s:%s ! %s", prefix, identifier, get_name(prefix, identifier)
286
- )
287
- curies = nx.ancestors(hierarchy, curie) # note this is backwards
312
+ t = _get_pi(prefix, identifier)
313
+ hierarchy = get_hierarchy(prefix=t.prefix, **kwargs)
314
+ logger.info("getting descendants of %s ! %s", t.curie, get_name(t))
315
+ descendants = set(nx.ancestors(hierarchy, t)) | {t} # note this is backwards
288
316
  logger.info("inducing subgraph")
289
- sg = hierarchy.subgraph(curies).copy()
317
+ sg = hierarchy.subgraph(descendants).copy()
290
318
  logger.info("subgraph has %d nodes/%d edges", sg.number_of_nodes(), sg.number_of_edges())
291
319
  return sg
pyobo/api/metadata.py CHANGED
@@ -1,15 +1,17 @@
1
1
  """High-level API for metadata."""
2
2
 
3
3
  import logging
4
- from collections.abc import Mapping
5
4
  from functools import lru_cache
6
- from typing import Optional
5
+ from typing import Any, cast
7
6
 
8
- from .utils import get_version
7
+ from typing_extensions import Unpack
8
+
9
+ from .utils import get_version_from_kwargs
10
+ from ..constants import GetOntologyKwargs, check_should_force
9
11
  from ..getters import get_ontology
10
12
  from ..identifier_utils import wrap_norm_prefix
11
13
  from ..utils.cache import cached_json
12
- from ..utils.path import prefix_cache_join
14
+ from ..utils.path import CacheArtifact, get_cache_path
13
15
 
14
16
  __all__ = [
15
17
  "get_metadata",
@@ -20,21 +22,14 @@ logger = logging.getLogger(__name__)
20
22
 
21
23
  @lru_cache
22
24
  @wrap_norm_prefix
23
- def get_metadata(
24
- prefix: str, *, force: bool = False, version: Optional[str] = None
25
- ) -> Mapping[str, str]:
25
+ def get_metadata(prefix: str, **kwargs: Unpack[GetOntologyKwargs]) -> dict[str, Any]:
26
26
  """Get metadata for the ontology."""
27
- if version is None:
28
- version = get_version(prefix)
29
- path = prefix_cache_join(prefix, name="metadata.json", version=version)
30
-
31
- @cached_json(path=path, force=force)
32
- def _get_json() -> Mapping[str, str]:
33
- if force:
34
- logger.debug("[%s] forcing reload for metadata", prefix)
35
- else:
36
- logger.debug("[%s] no cached metadata found. getting from OBO loader", prefix)
37
- ontology = get_ontology(prefix, force=force, version=version)
27
+ version = get_version_from_kwargs(prefix, kwargs)
28
+ path = get_cache_path(prefix, CacheArtifact.metadata, version=version)
29
+
30
+ @cached_json(path=path, force=check_should_force(kwargs))
31
+ def _get_json() -> dict[str, Any]:
32
+ ontology = get_ontology(prefix, **kwargs)
38
33
  return ontology.get_metadata()
39
34
 
40
- return _get_json()
35
+ return cast(dict[str, Any], _get_json())