pyobo 0.11.2__py3-none-any.whl → 0.12.1__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 (228) 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 -117
  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 +107 -114
  17. pyobo/cli/__init__.py +0 -0
  18. pyobo/cli/cli.py +35 -50
  19. pyobo/cli/database.py +210 -160
  20. pyobo/cli/database_utils.py +155 -0
  21. pyobo/cli/lookup.py +163 -195
  22. pyobo/cli/utils.py +19 -6
  23. pyobo/constants.py +102 -3
  24. pyobo/getters.py +209 -191
  25. pyobo/gilda_utils.py +52 -250
  26. pyobo/identifier_utils/__init__.py +33 -0
  27. pyobo/identifier_utils/api.py +305 -0
  28. pyobo/identifier_utils/preprocessing.json +873 -0
  29. pyobo/identifier_utils/preprocessing.py +27 -0
  30. pyobo/identifier_utils/relations/__init__.py +8 -0
  31. pyobo/identifier_utils/relations/api.py +162 -0
  32. pyobo/identifier_utils/relations/data.json +5824 -0
  33. pyobo/identifier_utils/relations/data_owl.json +57 -0
  34. pyobo/identifier_utils/relations/data_rdf.json +1 -0
  35. pyobo/identifier_utils/relations/data_rdfs.json +7 -0
  36. pyobo/mocks.py +9 -6
  37. pyobo/ner/__init__.py +9 -0
  38. pyobo/ner/api.py +72 -0
  39. pyobo/ner/normalizer.py +33 -0
  40. pyobo/obographs.py +48 -40
  41. pyobo/plugins.py +5 -4
  42. pyobo/py.typed +0 -0
  43. pyobo/reader.py +1354 -395
  44. pyobo/reader_utils.py +155 -0
  45. pyobo/resource_utils.py +42 -22
  46. pyobo/resources/__init__.py +0 -0
  47. pyobo/resources/goc.py +75 -0
  48. pyobo/resources/goc.tsv +188 -0
  49. pyobo/resources/ncbitaxon.py +4 -5
  50. pyobo/resources/ncbitaxon.tsv.gz +0 -0
  51. pyobo/resources/ro.py +3 -2
  52. pyobo/resources/ro.tsv +0 -0
  53. pyobo/resources/so.py +0 -0
  54. pyobo/resources/so.tsv +0 -0
  55. pyobo/sources/README.md +12 -8
  56. pyobo/sources/__init__.py +52 -29
  57. pyobo/sources/agrovoc.py +0 -0
  58. pyobo/sources/antibodyregistry.py +11 -12
  59. pyobo/sources/bigg/__init__.py +13 -0
  60. pyobo/sources/bigg/bigg_compartment.py +81 -0
  61. pyobo/sources/bigg/bigg_metabolite.py +229 -0
  62. pyobo/sources/bigg/bigg_model.py +46 -0
  63. pyobo/sources/bigg/bigg_reaction.py +77 -0
  64. pyobo/sources/biogrid.py +1 -2
  65. pyobo/sources/ccle.py +7 -12
  66. pyobo/sources/cgnc.py +9 -6
  67. pyobo/sources/chebi.py +1 -1
  68. pyobo/sources/chembl/__init__.py +9 -0
  69. pyobo/sources/{chembl.py → chembl/chembl_compound.py} +13 -25
  70. pyobo/sources/chembl/chembl_target.py +160 -0
  71. pyobo/sources/civic_gene.py +55 -15
  72. pyobo/sources/clinicaltrials.py +160 -0
  73. pyobo/sources/complexportal.py +24 -24
  74. pyobo/sources/conso.py +14 -22
  75. pyobo/sources/cpt.py +0 -0
  76. pyobo/sources/credit.py +1 -9
  77. pyobo/sources/cvx.py +27 -5
  78. pyobo/sources/depmap.py +9 -12
  79. pyobo/sources/dictybase_gene.py +2 -7
  80. pyobo/sources/drugbank/__init__.py +9 -0
  81. pyobo/sources/{drugbank.py → drugbank/drugbank.py} +11 -16
  82. pyobo/sources/{drugbank_salt.py → drugbank/drugbank_salt.py} +3 -8
  83. pyobo/sources/drugcentral.py +17 -13
  84. pyobo/sources/expasy.py +31 -34
  85. pyobo/sources/famplex.py +13 -18
  86. pyobo/sources/flybase.py +8 -13
  87. pyobo/sources/gard.py +62 -0
  88. pyobo/sources/geonames/__init__.py +9 -0
  89. pyobo/sources/geonames/features.py +28 -0
  90. pyobo/sources/{geonames.py → geonames/geonames.py} +87 -26
  91. pyobo/sources/geonames/utils.py +115 -0
  92. pyobo/sources/gmt_utils.py +6 -7
  93. pyobo/sources/go.py +20 -13
  94. pyobo/sources/gtdb.py +154 -0
  95. pyobo/sources/gwascentral/__init__.py +9 -0
  96. pyobo/sources/{gwascentral_phenotype.py → gwascentral/gwascentral_phenotype.py} +5 -7
  97. pyobo/sources/{gwascentral_study.py → gwascentral/gwascentral_study.py} +1 -7
  98. pyobo/sources/hgnc/__init__.py +9 -0
  99. pyobo/sources/{hgnc.py → hgnc/hgnc.py} +56 -70
  100. pyobo/sources/{hgncgenefamily.py → hgnc/hgncgenefamily.py} +8 -18
  101. pyobo/sources/icd/__init__.py +9 -0
  102. pyobo/sources/{icd10.py → icd/icd10.py} +35 -37
  103. pyobo/sources/icd/icd11.py +148 -0
  104. pyobo/sources/{icd_utils.py → icd/icd_utils.py} +66 -20
  105. pyobo/sources/interpro.py +4 -9
  106. pyobo/sources/itis.py +0 -5
  107. pyobo/sources/kegg/__init__.py +0 -0
  108. pyobo/sources/kegg/api.py +16 -38
  109. pyobo/sources/kegg/genes.py +9 -20
  110. pyobo/sources/kegg/genome.py +1 -7
  111. pyobo/sources/kegg/pathway.py +9 -21
  112. pyobo/sources/mesh.py +58 -24
  113. pyobo/sources/mgi.py +3 -10
  114. pyobo/sources/mirbase/__init__.py +11 -0
  115. pyobo/sources/{mirbase.py → mirbase/mirbase.py} +8 -11
  116. pyobo/sources/{mirbase_constants.py → mirbase/mirbase_constants.py} +0 -0
  117. pyobo/sources/{mirbase_family.py → mirbase/mirbase_family.py} +4 -8
  118. pyobo/sources/{mirbase_mature.py → mirbase/mirbase_mature.py} +3 -7
  119. pyobo/sources/msigdb.py +74 -39
  120. pyobo/sources/ncbi/__init__.py +9 -0
  121. pyobo/sources/ncbi/ncbi_gc.py +162 -0
  122. pyobo/sources/{ncbigene.py → ncbi/ncbigene.py} +18 -19
  123. pyobo/sources/nih_reporter.py +60 -0
  124. pyobo/sources/nlm/__init__.py +9 -0
  125. pyobo/sources/nlm/nlm_catalog.py +48 -0
  126. pyobo/sources/nlm/nlm_publisher.py +36 -0
  127. pyobo/sources/nlm/utils.py +116 -0
  128. pyobo/sources/npass.py +6 -8
  129. pyobo/sources/omim_ps.py +11 -4
  130. pyobo/sources/pathbank.py +4 -8
  131. pyobo/sources/pfam/__init__.py +9 -0
  132. pyobo/sources/{pfam.py → pfam/pfam.py} +3 -8
  133. pyobo/sources/{pfam_clan.py → pfam/pfam_clan.py} +2 -7
  134. pyobo/sources/pharmgkb/__init__.py +15 -0
  135. pyobo/sources/pharmgkb/pharmgkb_chemical.py +89 -0
  136. pyobo/sources/pharmgkb/pharmgkb_disease.py +77 -0
  137. pyobo/sources/pharmgkb/pharmgkb_gene.py +108 -0
  138. pyobo/sources/pharmgkb/pharmgkb_pathway.py +63 -0
  139. pyobo/sources/pharmgkb/pharmgkb_variant.py +84 -0
  140. pyobo/sources/pharmgkb/utils.py +86 -0
  141. pyobo/sources/pid.py +1 -6
  142. pyobo/sources/pombase.py +6 -10
  143. pyobo/sources/pubchem.py +4 -9
  144. pyobo/sources/reactome.py +5 -11
  145. pyobo/sources/rgd.py +11 -16
  146. pyobo/sources/rhea.py +37 -36
  147. pyobo/sources/ror.py +69 -42
  148. pyobo/sources/selventa/__init__.py +0 -0
  149. pyobo/sources/selventa/schem.py +4 -7
  150. pyobo/sources/selventa/scomp.py +1 -6
  151. pyobo/sources/selventa/sdis.py +4 -7
  152. pyobo/sources/selventa/sfam.py +1 -6
  153. pyobo/sources/sgd.py +6 -11
  154. pyobo/sources/signor/__init__.py +7 -0
  155. pyobo/sources/signor/download.py +41 -0
  156. pyobo/sources/signor/signor_complexes.py +105 -0
  157. pyobo/sources/slm.py +12 -15
  158. pyobo/sources/umls/__init__.py +7 -1
  159. pyobo/sources/umls/__main__.py +0 -0
  160. pyobo/sources/umls/get_synonym_types.py +20 -4
  161. pyobo/sources/umls/sty.py +57 -0
  162. pyobo/sources/umls/synonym_types.tsv +1 -1
  163. pyobo/sources/umls/umls.py +18 -22
  164. pyobo/sources/unimod.py +46 -0
  165. pyobo/sources/uniprot/__init__.py +1 -1
  166. pyobo/sources/uniprot/uniprot.py +40 -32
  167. pyobo/sources/uniprot/uniprot_ptm.py +4 -34
  168. pyobo/sources/utils.py +3 -2
  169. pyobo/sources/wikipathways.py +7 -10
  170. pyobo/sources/zfin.py +5 -10
  171. pyobo/ssg/__init__.py +12 -16
  172. pyobo/ssg/base.html +0 -0
  173. pyobo/ssg/index.html +26 -13
  174. pyobo/ssg/term.html +12 -2
  175. pyobo/ssg/typedef.html +0 -0
  176. pyobo/struct/__init__.py +54 -8
  177. pyobo/struct/functional/__init__.py +1 -0
  178. pyobo/struct/functional/dsl.py +2572 -0
  179. pyobo/struct/functional/macros.py +423 -0
  180. pyobo/struct/functional/obo_to_functional.py +385 -0
  181. pyobo/struct/functional/ontology.py +272 -0
  182. pyobo/struct/functional/utils.py +112 -0
  183. pyobo/struct/reference.py +331 -136
  184. pyobo/struct/struct.py +1484 -657
  185. pyobo/struct/struct_utils.py +1078 -0
  186. pyobo/struct/typedef.py +162 -210
  187. pyobo/struct/utils.py +12 -5
  188. pyobo/struct/vocabulary.py +138 -0
  189. pyobo/utils/__init__.py +0 -0
  190. pyobo/utils/cache.py +16 -15
  191. pyobo/utils/io.py +51 -41
  192. pyobo/utils/iter.py +5 -5
  193. pyobo/utils/misc.py +41 -53
  194. pyobo/utils/ndex_utils.py +0 -0
  195. pyobo/utils/path.py +73 -70
  196. pyobo/version.py +3 -3
  197. pyobo-0.12.1.dist-info/METADATA +671 -0
  198. pyobo-0.12.1.dist-info/RECORD +201 -0
  199. pyobo-0.12.1.dist-info/WHEEL +4 -0
  200. {pyobo-0.11.2.dist-info → pyobo-0.12.1.dist-info}/entry_points.txt +1 -0
  201. pyobo-0.12.1.dist-info/licenses/LICENSE +21 -0
  202. pyobo/aws.py +0 -162
  203. pyobo/cli/aws.py +0 -47
  204. pyobo/identifier_utils.py +0 -142
  205. pyobo/normalizer.py +0 -232
  206. pyobo/registries/__init__.py +0 -16
  207. pyobo/registries/metaregistry.json +0 -507
  208. pyobo/registries/metaregistry.py +0 -135
  209. pyobo/sources/icd11.py +0 -105
  210. pyobo/xrefdb/__init__.py +0 -1
  211. pyobo/xrefdb/canonicalizer.py +0 -214
  212. pyobo/xrefdb/priority.py +0 -59
  213. pyobo/xrefdb/sources/__init__.py +0 -60
  214. pyobo/xrefdb/sources/biomappings.py +0 -36
  215. pyobo/xrefdb/sources/cbms2019.py +0 -91
  216. pyobo/xrefdb/sources/chembl.py +0 -83
  217. pyobo/xrefdb/sources/compath.py +0 -82
  218. pyobo/xrefdb/sources/famplex.py +0 -64
  219. pyobo/xrefdb/sources/gilda.py +0 -50
  220. pyobo/xrefdb/sources/intact.py +0 -113
  221. pyobo/xrefdb/sources/ncit.py +0 -133
  222. pyobo/xrefdb/sources/pubchem.py +0 -27
  223. pyobo/xrefdb/sources/wikidata.py +0 -116
  224. pyobo/xrefdb/xrefs_pipeline.py +0 -180
  225. pyobo-0.11.2.dist-info/METADATA +0 -711
  226. pyobo-0.11.2.dist-info/RECORD +0 -157
  227. pyobo-0.11.2.dist-info/WHEEL +0 -5
  228. pyobo-0.11.2.dist-info/top_level.txt +0 -1
@@ -0,0 +1,671 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyobo
3
+ Version: 0.12.1
4
+ Summary: A python package for handling and generating OBO
5
+ Keywords: snekpack,cookiecutter,ontologies,biomedical ontologies,life sciences,natural sciences,bioinformatics,cheminformatics,Open Biomedical Ontologies,OBO
6
+ Author: Charles Tapley Hoyt
7
+ Author-email: Charles Tapley Hoyt <cthoyt@gmail.com>
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Framework :: Pytest
15
+ Classifier: Framework :: tox
16
+ Classifier: Framework :: Sphinx
17
+ Classifier: Natural Language :: English
18
+ Classifier: Programming Language :: Python
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3 :: Only
24
+ Classifier: Typing :: Typed
25
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
26
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
27
+ Requires-Dist: obonet>=0.3.0
28
+ Requires-Dist: click
29
+ Requires-Dist: tqdm
30
+ Requires-Dist: pyyaml
31
+ Requires-Dist: pandas
32
+ Requires-Dist: requests
33
+ Requires-Dist: protmapper
34
+ Requires-Dist: more-itertools
35
+ Requires-Dist: more-click>=0.0.2
36
+ Requires-Dist: humanize
37
+ Requires-Dist: tabulate
38
+ Requires-Dist: cachier
39
+ Requires-Dist: pystow>=0.7.0
40
+ Requires-Dist: bioversions>=0.8.0
41
+ Requires-Dist: bioregistry>=0.12.7
42
+ Requires-Dist: bioontologies>=0.7.1
43
+ Requires-Dist: ssslm>=0.0.13
44
+ Requires-Dist: zenodo-client>=0.3.6
45
+ Requires-Dist: class-resolver>=0.6.0
46
+ Requires-Dist: psycopg2-binary
47
+ Requires-Dist: pydantic>=2.0
48
+ Requires-Dist: curies>=0.10.13
49
+ Requires-Dist: python-dateutil
50
+ Requires-Dist: networkx>=3.4
51
+ Requires-Dist: drugbank-downloader
52
+ Requires-Dist: chembl-downloader
53
+ Requires-Dist: umls-downloader>=0.1.3
54
+ Requires-Dist: clinicaltrials-downloader>=0.0.2
55
+ Requires-Dist: nih-reporter-downloader>=0.0.1
56
+ Requires-Dist: typing-extensions
57
+ Requires-Dist: rdflib
58
+ Requires-Dist: ssslm[gilda] ; extra == 'gilda'
59
+ Requires-Dist: ssslm[gilda-slim] ; extra == 'gilda-slim'
60
+ Maintainer: Charles Tapley Hoyt
61
+ Maintainer-email: Charles Tapley Hoyt <cthoyt@gmail.com>
62
+ Requires-Python: >=3.10
63
+ Project-URL: Bug Tracker, https://github.com/biopragmatics/pyobo/issues
64
+ Project-URL: Documentation, https://pyobo.readthedocs.io
65
+ Project-URL: Funding, https://github.com/sponsors/cthoyt
66
+ Project-URL: Homepage, https://github.com/biopragmatics/pyobo
67
+ Project-URL: Repository, https://github.com/biopragmatics/pyobo.git
68
+ Provides-Extra: gilda
69
+ Provides-Extra: gilda-slim
70
+ Description-Content-Type: text/markdown
71
+
72
+ <!--
73
+ <p align="center">
74
+ <img src="https://github.com/biopragmatics/pyobo/raw/main/docs/source/logo.png" height="150">
75
+ </p>
76
+ -->
77
+
78
+ <h1 align="center">
79
+ PyOBO
80
+ </h1>
81
+
82
+ <p align="center">
83
+ <a href="https://github.com/biopragmatics/pyobo/actions/workflows/tests.yml">
84
+ <img alt="Tests" src="https://github.com/biopragmatics/pyobo/actions/workflows/tests.yml/badge.svg" /></a>
85
+ <a href="https://pypi.org/project/pyobo">
86
+ <img alt="PyPI" src="https://img.shields.io/pypi/v/pyobo" /></a>
87
+ <a href="https://pypi.org/project/pyobo">
88
+ <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/pyobo" /></a>
89
+ <a href="https://github.com/biopragmatics/pyobo/blob/main/LICENSE">
90
+ <img alt="PyPI - License" src="https://img.shields.io/pypi/l/pyobo" /></a>
91
+ <a href='https://pyobo.readthedocs.io/en/latest/?badge=latest'>
92
+ <img src='https://readthedocs.org/projects/pyobo/badge/?version=latest' alt='Documentation Status' /></a>
93
+ <a href="https://codecov.io/gh/biopragmatics/pyobo/branch/main">
94
+ <img src="https://codecov.io/gh/biopragmatics/pyobo/branch/main/graph/badge.svg" alt="Codecov status" /></a>
95
+ <a href="https://github.com/cthoyt/cookiecutter-python-package">
96
+ <img alt="Cookiecutter template from @cthoyt" src="https://img.shields.io/badge/Cookiecutter-snekpack-blue" /></a>
97
+ <a href="https://github.com/astral-sh/ruff">
98
+ <img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff" style="max-width:100%;"></a>
99
+ <a href="https://github.com/biopragmatics/pyobo/blob/main/.github/CODE_OF_CONDUCT.md">
100
+ <img src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg" alt="Contributor Covenant"/></a>
101
+ <a href="https://doi.org/10.5281/zenodo.3381961">
102
+ <img src="https://zenodo.org/badge/DOI/10.5281/zenodo.3381961.svg" alt="DOI"></a>
103
+ </p>
104
+
105
+ Tools for biological identifiers, names, synonyms, xrefs, hierarchies,
106
+ relations, and properties through the perspective of OBO.
107
+
108
+ ## Example Usage
109
+
110
+ Note! PyOBO is no-nonsense. This means that there's no repetitive prefixes in
111
+ identifiers. It also means all identifiers are strings, no exceptions.
112
+
113
+ Note! The first time you run these, they have to download and cache all
114
+ resources. We're not in the business of redistributing data, so all scripts
115
+ should be completely reproducible.
116
+
117
+ ### Mapping Identifiers and CURIEs
118
+
119
+ Get mapping of ChEBI identifiers to names:
120
+
121
+ ```python
122
+ import pyobo
123
+
124
+ chebi_id_to_name = pyobo.get_id_name_mapping("chebi")
125
+ assert "fluazifop-P-butyl" == chebi_id_to_name["132964"]
126
+
127
+ # or more directly
128
+ assert "fluazifop-P-butyl" == pyobo.get_name("chebi", "132964")
129
+ ```
130
+
131
+ Get reverse mapping of ChEBI names to identifiers:
132
+
133
+ ```python
134
+ import pyobo
135
+
136
+ chebi_name_to_id = pyobo.get_name_id_mapping("chebi")
137
+ assert "132964" == chebi_name_to_id["fluazifop-P-butyl"]
138
+ ```
139
+
140
+ Maybe you live in CURIE world and just want to normalize something like
141
+ `CHEBI:132964`:
142
+
143
+ ```python
144
+ import pyobo
145
+
146
+ assert "fluazifop-P-butyl" == pyobo.get_name_by_curie("CHEBI:132964")
147
+ ```
148
+
149
+ Sometimes you accidentally got an old CURIE. It can be mapped to the more recent
150
+ one using alternative identifiers listed in the underlying OBO with:
151
+
152
+ ```python
153
+ import pyobo
154
+ from pyobo import Reference
155
+
156
+ # Look up DNA-binding transcription factor activity (go:0003700)
157
+ # based on an old id
158
+ primary_curie = pyobo.get_primary_curie("go:0001071")
159
+ assert primary_curie == "go:0003700"
160
+
161
+ # If it's already the primary, it just gets returned
162
+ assert Reference.from_curie("go:0003700") == pyobo.get_primary_curie("go:0003700")
163
+ ```
164
+
165
+ ### Mapping Species
166
+
167
+ Some resources have species information for their term. Get a mapping of
168
+ WikiPathway identifiers to species (as NCBI taxonomy identifiers):
169
+
170
+ ```python
171
+ import pyobo
172
+
173
+ wikipathways_id_to_species = pyobo.get_id_species_mapping("wikipathways")
174
+
175
+ # Apoptosis (Homo sapiens)
176
+ assert "9606" == wikipathways_id_to_species["WP254"]
177
+ ```
178
+
179
+ Or, you don't have time for two lines:
180
+
181
+ ```python
182
+ import pyobo
183
+
184
+ # Apoptosis (Homo sapiens)
185
+ taxonomy_id = pyobo.get_species("wikipathways", "WP254")
186
+ assert taxonomy_id == "9606"
187
+ ```
188
+
189
+ ### Grounding
190
+
191
+ Maybe you've got names/synonyms you want to try and map back to ChEBI synonyms.
192
+ Given the brand name `Fusilade II` of `CHEBI:132964`, it should be able to look
193
+ it up and its preferred label.
194
+
195
+ ```python
196
+ import pyobo
197
+
198
+ reference = pyobo.ground("chebi", "Fusilade II")
199
+ assert reference.prefix == "chebi"
200
+ assert reference.identifier == "132964"
201
+ assert reference.name == "fluazifop-P-butyl"
202
+
203
+ # When failure happens...
204
+ reference = pyobo.ground("chebi", "Definitely not a real name")
205
+ assert reference is None
206
+ ```
207
+
208
+ If you're not really sure which namespace a name might belong to, you can try a
209
+ few in a row (prioritize by ones that cover the appropriate entity type to avoid
210
+ false positives in case of conflicts):
211
+
212
+ ```python
213
+ import pyobo
214
+
215
+ # looking for phenotypes/pathways
216
+ reference = pyobo.ground(["efo", "go"], "ERAD")
217
+ assert reference.prefix == "go"
218
+ assert reference.identifier == "0030433"
219
+ assert reference.name == "ubiquitin-dependent ERAD pathway"
220
+ ```
221
+
222
+ ### Cross-referencing
223
+
224
+ Get xrefs from ChEBI to PubChem:
225
+
226
+ ```python
227
+ import pyobo
228
+
229
+ chebi_id_to_pubchem_compound_id = pyobo.get_filtered_xrefs("chebi", "pubchem.compound")
230
+
231
+ pubchem_compound_id = chebi_id_to_pubchem_compound_id["132964"]
232
+ assert pubchem_compound_id == "3033674"
233
+ ```
234
+
235
+ If you don't have time for two lines:
236
+
237
+ ```python
238
+ import pyobo
239
+
240
+ pubchem_compound_id = pyobo.get_xref("chebi", "132964", "pubchem.compound")
241
+ assert pubchem_compound_id == "3033674"
242
+ ```
243
+
244
+ Get xrefs from Entrez to HGNC, but they're only available through HGNC, so you
245
+ need to flip them:
246
+
247
+ ```python
248
+ import pyobo
249
+
250
+ hgnc_id_to_ncbigene_id = pyobo.get_filtered_xrefs("hgnc", "ncbigene")
251
+ ncbigene_id_to_hgnc_id = {
252
+ ncbigene_id: hgnc_id
253
+ for hgnc_id, ncbigene_id in hgnc_id_to_ncbigene_id.items()
254
+ }
255
+ mapt_hgnc = ncbigene_id_to_hgnc_id["4137"]
256
+ assert mapt_hgnc == "6893"
257
+ ```
258
+
259
+ Since this is a common pattern, there's a keyword argument `flip` that does this
260
+ for you:
261
+
262
+ ```python
263
+ import pyobo
264
+
265
+ ncbigene_id_to_hgnc_id = pyobo.get_filtered_xrefs("hgnc", "ncbigene", flip=True)
266
+ mapt_hgnc_id = ncbigene_id_to_hgnc_id["4137"]
267
+ assert mapt_hgnc_id == "6893"
268
+ ```
269
+
270
+ If you don't have time for two lines (I admit this one is a bit confusing) and
271
+ need to flip it:
272
+
273
+ ```python
274
+ import pyobo
275
+
276
+ hgnc_id = pyobo.get_xref("hgnc", "4137", "ncbigene", flip=True)
277
+ assert hgnc_id == "6893"
278
+ ```
279
+
280
+ ### Properties
281
+
282
+ Get properties, like SMILES. The semantics of these are defined on an OBO-OBO
283
+ basis.
284
+
285
+ ```python
286
+ import pyobo
287
+
288
+ # I don't make the rules. I wouldn't have chosen this as the key for this property. It could be any string
289
+ chebi_smiles_property = "http://purl.obolibrary.org/obo/chebi/smiles"
290
+ chebi_id_to_smiles = pyobo.get_filtered_properties_mapping("chebi", chebi_smiles_property)
291
+
292
+ smiles = chebi_id_to_smiles["132964"]
293
+ assert smiles == "C1(=CC=C(N=C1)OC2=CC=C(C=C2)O[C@@H](C(OCCCC)=O)C)C(F)(F)F"
294
+ ```
295
+
296
+ If you don't have time for two lines:
297
+
298
+ ```python
299
+ import pyobo
300
+
301
+ smiles = pyobo.get_property("chebi", "132964", "http://purl.obolibrary.org/obo/chebi/smiles")
302
+ assert smiles == "C1(=CC=C(N=C1)OC2=CC=C(C=C2)O[C@@H](C(OCCCC)=O)C)C(F)(F)F"
303
+ ```
304
+
305
+ ### Hierarchy
306
+
307
+ Check if an entity is in the hierarchy:
308
+
309
+ ```python
310
+ import pyobo
311
+ from pyobo import Reference
312
+
313
+ # check that go:0008219 ! cell death is an ancestor of go:0006915 ! apoptotic process
314
+ assert Reference.from_curie("go:0008219") in pyobo.get_ancestors("go", "0006915")
315
+
316
+ # check that go:0070246 ! natural killer cell apoptotic process is a
317
+ # descendant of go:0006915 ! apoptotic process
318
+ apopototic_process_descendants = pyobo.get_descendants("go", "0006915")
319
+ assert Reference.from_curie("go:0070246") in apopototic_process_descendants
320
+ ```
321
+
322
+ Get the sub-hierarchy below a given node:
323
+
324
+ ```python
325
+ import pyobo
326
+ from pyobo import Reference
327
+
328
+ # get the descendant graph of go:0006915 ! apoptotic process
329
+ apopototic_process_subhierarchy = pyobo.get_subhierarchy("go", "0006915")
330
+
331
+ # check that go:0070246 ! natural killer cell apoptotic process is a
332
+ # descendant of go:0006915 ! apoptotic process through the subhierarchy
333
+ assert Reference.from_curie("go:0070246") in apopototic_process_subhierarchy
334
+ ```
335
+
336
+ Get a hierarchy with properties preloaded in the node data dictionaries:
337
+
338
+ ```python
339
+ import pyobo
340
+ from pyobo import Reference
341
+
342
+ prop = "http://purl.obolibrary.org/obo/chebi/smiles"
343
+ chebi_hierarchy = pyobo.get_hierarchy("chebi", properties=[prop])
344
+
345
+ assert Reference.from_curie("chebi:132964") in chebi_hierarchy
346
+ assert prop in chebi_hierarchy.nodes["chebi:132964"]
347
+ assert chebi_hierarchy.nodes["chebi:132964"][prop] == "C1(=CC=C(N=C1)OC2=CC=C(C=C2)O[C@@H](C(OCCCC)=O)C)C(F)(F)F"
348
+ ```
349
+
350
+ ### Relations
351
+
352
+ Get all orthologies (`ro:HOM0000017`) between HGNC and MGI (note: this is one
353
+ way)
354
+
355
+ ```python
356
+ >>> import pyobo
357
+ >>> human_mapt_hgnc_id = "6893"
358
+ >>> mouse_mapt_mgi_id = "97180"
359
+ >>> hgnc_mgi_orthology_mapping = pyobo.get_relation_mapping("hgnc", "ro:HOM0000017", "mgi")
360
+ >>> assert mouse_mapt_mgi_id == hgnc_mgi_orthology_mapping[human_mapt_hgnc_id]
361
+ ```
362
+
363
+ If you want to do it in one line, use:
364
+
365
+ ```python
366
+
367
+ >>> import pyobo
368
+ >>> human_mapt_hgnc_id = "6893"
369
+ >>> mouse_mapt_mgi_id = "97180"
370
+ >>> assert mouse_mapt_mgi_id == pyobo.get_relation("hgnc", "ro:HOM0000017", "mgi", human_mapt_hgnc_id)
371
+ ```
372
+
373
+ ### Writings Tests that Use PyOBO
374
+
375
+ If you're writing your own code that relies on PyOBO, and unit testing it (as
376
+ you should) in a continuous integration setting, you've probably realized that
377
+ loading all the resources on each build is not so fast. In those scenarios, you
378
+ can use some of the pre-build patches like in the following:
379
+
380
+ ```python
381
+ import unittest
382
+ import pyobo
383
+ from pyobo.mocks import get_mock_id_name_mapping
384
+
385
+ mock_id_name_mapping = get_mock_id_name_mapping({
386
+ "chebi": {
387
+ "132964": "fluazifop-P-butyl",
388
+ },
389
+ })
390
+
391
+ class MyTestCase(unittest.TestCase):
392
+ def my_test(self):
393
+ with mock_id_name_mapping:
394
+ # use functions directly, or use your functions that wrap them
395
+ pyobo.get_name("chebi", "1234")
396
+ ```
397
+
398
+ ## Preprocessing CURIEs, URIs, and unqualified identifiers
399
+
400
+ In order to normalize references and identify resources, PyOBO uses the
401
+ [Bioregistry](https://github.com/bioregistry/bioregistry). It used to be a part
402
+ of PyOBO, but has since been externalized for more general reuse.
403
+
404
+ At
405
+ [src/pyobo/identifier_utils/preprocessing.json](https://github.com/pyobo/pyobo/blob/master/src/pyobo/src/pyobo/identifier_utils/preprocessing.json)
406
+ is the curated set of pre-processing rules. These are used in combination with
407
+ the `curies` package to do pre-processing steps on CURIEs, URIs, and unqualified
408
+ identifiers beyond what is possible with the Bioregistry. See
409
+ https://curies.readthedocs.io/en/latest/preprocessing.html.
410
+
411
+ ## Troubleshooting
412
+
413
+ The OBO Foundry seems to be pretty unstable with respect to the URLs to OBO
414
+ resources. If you get an error like:
415
+
416
+ ```
417
+ pyobo.getters.MissingOboBuild: OBO Foundry is missing a build for: mondo
418
+ ```
419
+
420
+ Then you should check the corresponding page on the OBO Foundry (in this case,
421
+ http://www.obofoundry.org/ontology/mondo.html) and make update to the `url`
422
+ entry for that namespace in the Bioregistry.
423
+
424
+ ## 🚀 Installation
425
+
426
+ The most recent release can be installed from
427
+ [PyPI](https://pypi.org/project/pyobo/) with uv:
428
+
429
+ ```console
430
+ $ uv pip install pyobo
431
+ ```
432
+
433
+ or with pip:
434
+
435
+ ```console
436
+ $ python3 -m pip install pyobo
437
+ ```
438
+
439
+ The most recent code and data can be installed directly from GitHub with uv:
440
+
441
+ ```console
442
+ $ uv pip install git+https://github.com/biopragmatics/pyobo.git
443
+ ```
444
+
445
+ or with pip:
446
+
447
+ ```console
448
+ $ python3 -m pip install git+https://github.com/biopragmatics/pyobo.git
449
+ ```
450
+
451
+ ## 👐 Contributing
452
+
453
+ Contributions, whether filing an issue, making a pull request, or forking, are
454
+ appreciated. See
455
+ [CONTRIBUTING.md](https://github.com/biopragmatics/pyobo/blob/master/.github/CONTRIBUTING.md)
456
+ for more information on getting involved.
457
+
458
+ ## 👋 Attribution
459
+
460
+ ### ⚖️ License
461
+
462
+ The code in this package is licensed under the MIT License.
463
+
464
+ <!--
465
+ ### 📖 Citation
466
+
467
+ Citation goes here!
468
+ -->
469
+
470
+ <!--
471
+ ### 🎁 Support
472
+
473
+ This project has been supported by the following organizations (in alphabetical order):
474
+
475
+ - [Biopragmatics Lab](https://biopragmatics.github.io)
476
+
477
+ -->
478
+
479
+ <!--
480
+ ### 💰 Funding
481
+
482
+ This project has been supported by the following grants:
483
+
484
+ | Funding Body | Program | Grant Number |
485
+ |---------------|--------------------------------------------------------------|--------------|
486
+ | Funder | [Grant Name (GRANT-ACRONYM)](https://example.com/grant-link) | ABCXYZ |
487
+ -->
488
+
489
+ ### 🍪 Cookiecutter
490
+
491
+ This package was created with
492
+ [@audreyfeldroy](https://github.com/audreyfeldroy)'s
493
+ [cookiecutter](https://github.com/cookiecutter/cookiecutter) package using
494
+ [@cthoyt](https://github.com/cthoyt)'s
495
+ [cookiecutter-snekpack](https://github.com/cthoyt/cookiecutter-snekpack)
496
+ template.
497
+
498
+ ## 🛠️ For Developers
499
+
500
+ <details>
501
+ <summary>See developer instructions</summary>
502
+
503
+ The final section of the README is for if you want to get involved by making a
504
+ code contribution.
505
+
506
+ ### Development Installation
507
+
508
+ To install in development mode, use the following:
509
+
510
+ ```console
511
+ $ git clone git+https://github.com/biopragmatics/pyobo.git
512
+ $ cd pyobo
513
+ $ uv pip install -e .
514
+ ```
515
+
516
+ Alternatively, install using pip:
517
+
518
+ ```console
519
+ $ python3 -m pip install -e .
520
+ ```
521
+
522
+ ### Updating Package Boilerplate
523
+
524
+ This project uses `cruft` to keep boilerplate (i.e., configuration, contribution
525
+ guidelines, documentation configuration) up-to-date with the upstream
526
+ cookiecutter package. Install cruft with either `uv tool install cruft` or
527
+ `python3 -m pip install cruft` then run:
528
+
529
+ ```console
530
+ $ cruft update
531
+ ```
532
+
533
+ More info on Cruft's update command is available
534
+ [here](https://github.com/cruft/cruft?tab=readme-ov-file#updating-a-project).
535
+
536
+ ### 🥼 Testing
537
+
538
+ After cloning the repository and installing `tox` with
539
+ `uv tool install tox --with tox-uv` or `python3 -m pip install tox tox-uv`, the
540
+ unit tests in the `tests/` folder can be run reproducibly with:
541
+
542
+ ```console
543
+ $ tox -e py
544
+ ```
545
+
546
+ Additionally, these tests are automatically re-run with each commit in a
547
+ [GitHub Action](https://github.com/biopragmatics/pyobo/actions?query=workflow%3ATests).
548
+
549
+ ### 📖 Building the Documentation
550
+
551
+ The documentation can be built locally using the following:
552
+
553
+ ```console
554
+ $ git clone git+https://github.com/biopragmatics/pyobo.git
555
+ $ cd pyobo
556
+ $ tox -e docs
557
+ $ open docs/build/html/index.html
558
+ ```
559
+
560
+ The documentation automatically installs the package as well as the `docs` extra
561
+ specified in the [`pyproject.toml`](pyproject.toml). `sphinx` plugins like
562
+ `texext` can be added there. Additionally, they need to be added to the
563
+ `extensions` list in [`docs/source/conf.py`](docs/source/conf.py).
564
+
565
+ The documentation can be deployed to [ReadTheDocs](https://readthedocs.io) using
566
+ [this guide](https://docs.readthedocs.io/en/stable/intro/import-guide.html). The
567
+ [`.readthedocs.yml`](.readthedocs.yml) YAML file contains all the configuration
568
+ you'll need. You can also set up continuous integration on GitHub to check not
569
+ only that Sphinx can build the documentation in an isolated environment (i.e.,
570
+ with `tox -e docs-test`) but also that
571
+ [ReadTheDocs can build it too](https://docs.readthedocs.io/en/stable/pull-requests.html).
572
+
573
+ #### Configuring ReadTheDocs
574
+
575
+ 1. Log in to ReadTheDocs with your GitHub account to install the integration at
576
+ https://readthedocs.org/accounts/login/?next=/dashboard/
577
+ 2. Import your project by navigating to https://readthedocs.org/dashboard/import
578
+ then clicking the plus icon next to your repository
579
+ 3. You can rename the repository on the next screen using a more stylized name
580
+ (i.e., with spaces and capital letters)
581
+ 4. Click next, and you're good to go!
582
+
583
+ ### 📦 Making a Release
584
+
585
+ #### Configuring Zenodo
586
+
587
+ [Zenodo](https://zenodo.org) is a long-term archival system that assigns a DOI
588
+ to each release of your package.
589
+
590
+ 1. Log in to Zenodo via GitHub with this link:
591
+ https://zenodo.org/oauth/login/github/?next=%2F. This brings you to a page
592
+ that lists all of your organizations and asks you to approve installing the
593
+ Zenodo app on GitHub. Click "grant" next to any organizations you want to
594
+ enable the integration for, then click the big green "approve" button. This
595
+ step only needs to be done once.
596
+ 2. Navigate to https://zenodo.org/account/settings/github/, which lists all of
597
+ your GitHub repositories (both in your username and any organizations you
598
+ enabled). Click the on/off toggle for any relevant repositories. When you
599
+ make a new repository, you'll have to come back to this
600
+
601
+ After these steps, you're ready to go! After you make "release" on GitHub (steps
602
+ for this are below), you can navigate to
603
+ https://zenodo.org/account/settings/github/repository/biopragmatics/pyobo to see
604
+ the DOI for the release and link to the Zenodo record for it.
605
+
606
+ #### Registering with the Python Package Index (PyPI)
607
+
608
+ You only have to do the following steps once.
609
+
610
+ 1. Register for an account on the
611
+ [Python Package Index (PyPI)](https://pypi.org/account/register)
612
+ 2. Navigate to https://pypi.org/manage/account and make sure you have verified
613
+ your email address. A verification email might not have been sent by default,
614
+ so you might have to click the "options" dropdown next to your address to get
615
+ to the "re-send verification email" button
616
+ 3. 2-Factor authentication is required for PyPI since the end of 2023 (see this
617
+ [blog post from PyPI](https://blog.pypi.org/posts/2023-05-25-securing-pypi-with-2fa/)).
618
+ This means you have to first issue account recovery codes, then set up
619
+ 2-factor authentication
620
+ 4. Issue an API token from https://pypi.org/manage/account/token
621
+
622
+ #### Configuring your machine's connection to PyPI
623
+
624
+ You have to do the following steps once per machine.
625
+
626
+ ```console
627
+ $ uv tool install keyring
628
+ $ keyring set https://upload.pypi.org/legacy/ __token__
629
+ $ keyring set https://test.pypi.org/legacy/ __token__
630
+ ```
631
+
632
+ Note that this deprecates previous workflows using `.pypirc`.
633
+
634
+ #### Uploading to PyPI
635
+
636
+ After installing the package in development mode and installing `tox` with
637
+ `uv tool install tox --with tox-uv` or `python3 -m pip install tox tox-uv`, run
638
+ the following from the console:
639
+
640
+ ```console
641
+ $ tox -e finish
642
+ ```
643
+
644
+ This script does the following:
645
+
646
+ 1. Uses [bump-my-version](https://github.com/callowayproject/bump-my-version) to
647
+ switch the version number in the `pyproject.toml`, `CITATION.cff`,
648
+ `src/pyobo/version.py`, and [`docs/source/conf.py`](docs/source/conf.py) to
649
+ not have the `-dev` suffix
650
+ 2. Packages the code in both a tar archive and a wheel using
651
+ [`uv build`](https://docs.astral.sh/uv/guides/publish/#building-your-package)
652
+ 3. Uploads to PyPI using
653
+ [`uv publish`](https://docs.astral.sh/uv/guides/publish/#publishing-your-package).
654
+ 4. Push to GitHub. You'll need to make a release going with the commit where the
655
+ version was bumped.
656
+ 5. Bump the version to the next patch. If you made big changes and want to bump
657
+ the version by minor, you can use `tox -e bumpversion -- minor` after.
658
+
659
+ #### Releasing on GitHub
660
+
661
+ 1. Navigate to https://github.com/biopragmatics/pyobo/releases/new to draft a
662
+ new release
663
+ 2. Click the "Choose a Tag" dropdown and select the tag corresponding to the
664
+ release you just made
665
+ 3. Click the "Generate Release Notes" button to get a quick outline of recent
666
+ changes. Modify the title and description as you see fit
667
+ 4. Click the big green "Publish Release" button
668
+
669
+ This will trigger Zenodo to assign a DOI to your release as well.
670
+
671
+ </details>