obographs 0.0.2__tar.gz → 0.0.4__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: obographs
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: A python data model for OBO Graphs
5
5
  Keywords: snekpack,cookiecutter
6
6
  Author: Charles Tapley Hoyt
@@ -23,15 +23,8 @@ Classifier: Programming Language :: Python :: 3.13
23
23
  Classifier: Programming Language :: Python :: 3 :: Only
24
24
  Classifier: Typing :: Typed
25
25
  Requires-Dist: pydantic
26
- Requires-Dist: curies
26
+ Requires-Dist: curies>=0.10.19
27
27
  Requires-Dist: typing-extensions
28
- Requires-Dist: sphinx>=8 ; extra == 'docs'
29
- Requires-Dist: sphinx-rtd-theme>=3.0 ; extra == 'docs'
30
- Requires-Dist: sphinx-automodapi ; extra == 'docs'
31
- Requires-Dist: autodoc-pydantic ; extra == 'docs'
32
- Requires-Dist: requests ; extra == 'network'
33
- Requires-Dist: pytest ; extra == 'tests'
34
- Requires-Dist: coverage[toml] ; extra == 'tests'
35
28
  Maintainer: Charles Tapley Hoyt
36
29
  Maintainer-email: Charles Tapley Hoyt <cthoyt@gmail.com>
37
30
  Requires-Python: >=3.10
@@ -40,9 +33,6 @@ Project-URL: Documentation, https://obographs.readthedocs.io
40
33
  Project-URL: Funding, https://github.com/sponsors/cthoyt
41
34
  Project-URL: Homepage, https://github.com/cthoyt/obographs
42
35
  Project-URL: Repository, https://github.com/cthoyt/obographs.git
43
- Provides-Extra: docs
44
- Provides-Extra: network
45
- Provides-Extra: tests
46
36
  Description-Content-Type: text/markdown
47
37
 
48
38
  <!--
@@ -97,7 +87,7 @@ graph_raw = obographs.read(url)
97
87
  The OBO Graph JSON schema uses non-Pythonic names, and it's inherently not aware
98
88
  of semantics - it uses a combination of URIs and ad-hoc symbols as identifiers.
99
89
  `obographs` implements a standardization workflow that creates new data
100
- structures with parsed/normalized URIs and symbols that has Pythonic nams. Use
90
+ structures with parsed/normalized URIs and symbols that has Pythonic names. Use
101
91
  it like:
102
92
 
103
93
  ```python
@@ -129,18 +119,15 @@ $ python3 -m pip install obographs
129
119
  The most recent code and data can be installed directly from GitHub with uv:
130
120
 
131
121
  ```console
132
- $ uv --preview pip install git+https://github.com/cthoyt/obographs.git
122
+ $ uv pip install git+https://github.com/cthoyt/obographs.git
133
123
  ```
134
124
 
135
125
  or with pip:
136
126
 
137
127
  ```console
138
- $ UV_PREVIEW=1 python3 -m pip install git+https://github.com/cthoyt/obographs.git
128
+ $ python3 -m pip install git+https://github.com/cthoyt/obographs.git
139
129
  ```
140
130
 
141
- Note that this requires setting `UV_PREVIEW` mode enabled until the uv build
142
- backend becomes a stable feature.
143
-
144
131
  ## 👐 Contributing
145
132
 
146
133
  Contributions, whether filing an issue, making a pull request, or forking, are
@@ -203,18 +190,15 @@ To install in development mode, use the following:
203
190
  ```console
204
191
  $ git clone git+https://github.com/cthoyt/obographs.git
205
192
  $ cd obographs
206
- $ uv --preview pip install -e .
193
+ $ uv pip install -e .
207
194
  ```
208
195
 
209
196
  Alternatively, install using pip:
210
197
 
211
198
  ```console
212
- $ UV_PREVIEW=1 python3 -m pip install -e .
199
+ $ python3 -m pip install -e .
213
200
  ```
214
201
 
215
- Note that this requires setting `UV_PREVIEW` mode enabled until the uv build
216
- backend becomes a stable feature.
217
-
218
202
  ### Updating Package Boilerplate
219
203
 
220
204
  This project uses `cruft` to keep boilerplate (i.e., configuration, contribution
@@ -50,7 +50,7 @@ graph_raw = obographs.read(url)
50
50
  The OBO Graph JSON schema uses non-Pythonic names, and it's inherently not aware
51
51
  of semantics - it uses a combination of URIs and ad-hoc symbols as identifiers.
52
52
  `obographs` implements a standardization workflow that creates new data
53
- structures with parsed/normalized URIs and symbols that has Pythonic nams. Use
53
+ structures with parsed/normalized URIs and symbols that has Pythonic names. Use
54
54
  it like:
55
55
 
56
56
  ```python
@@ -82,18 +82,15 @@ $ python3 -m pip install obographs
82
82
  The most recent code and data can be installed directly from GitHub with uv:
83
83
 
84
84
  ```console
85
- $ uv --preview pip install git+https://github.com/cthoyt/obographs.git
85
+ $ uv pip install git+https://github.com/cthoyt/obographs.git
86
86
  ```
87
87
 
88
88
  or with pip:
89
89
 
90
90
  ```console
91
- $ UV_PREVIEW=1 python3 -m pip install git+https://github.com/cthoyt/obographs.git
91
+ $ python3 -m pip install git+https://github.com/cthoyt/obographs.git
92
92
  ```
93
93
 
94
- Note that this requires setting `UV_PREVIEW` mode enabled until the uv build
95
- backend becomes a stable feature.
96
-
97
94
  ## 👐 Contributing
98
95
 
99
96
  Contributions, whether filing an issue, making a pull request, or forking, are
@@ -156,18 +153,15 @@ To install in development mode, use the following:
156
153
  ```console
157
154
  $ git clone git+https://github.com/cthoyt/obographs.git
158
155
  $ cd obographs
159
- $ uv --preview pip install -e .
156
+ $ uv pip install -e .
160
157
  ```
161
158
 
162
159
  Alternatively, install using pip:
163
160
 
164
161
  ```console
165
- $ UV_PREVIEW=1 python3 -m pip install -e .
162
+ $ python3 -m pip install -e .
166
163
  ```
167
164
 
168
- Note that this requires setting `UV_PREVIEW` mode enabled until the uv build
169
- backend becomes a stable feature.
170
-
171
165
  ### Updating Package Boilerplate
172
166
 
173
167
  This project uses `cruft` to keep boilerplate (i.e., configuration, contribution
@@ -1,12 +1,10 @@
1
1
  [build-system]
2
- requires = ["uv>=0.5.13,<0.6.0"]
3
- # The uv backend entered preview mode in https://github.com/astral-sh/uv/pull/8886/files
4
- # with the 0.5.0 release. See also https://github.com/astral-sh/uv/issues/3957 for tracking.
5
- build-backend = "uv"
2
+ requires = ["uv_build>=0.6.6,<0.7"]
3
+ build-backend = "uv_build"
6
4
 
7
5
  [project]
8
6
  name = "obographs"
9
- version = "0.0.2"
7
+ version = "0.0.4"
10
8
  description = "A python data model for OBO Graphs"
11
9
  readme = "README.md"
12
10
  authors = [
@@ -52,11 +50,12 @@ license-files = [
52
50
  requires-python = ">=3.10"
53
51
  dependencies = [
54
52
  "pydantic",
55
- "curies",
53
+ "curies>=0.10.19",
56
54
  "typing-extensions",
57
55
  ]
58
56
 
59
- [project.optional-dependencies]
57
+ # see https://peps.python.org/pep-0735/ and https://docs.astral.sh/uv/concepts/dependencies/#dependency-groups
58
+ [dependency-groups]
60
59
  tests = [
61
60
  "pytest",
62
61
  "coverage[toml]",
@@ -67,10 +66,51 @@ docs = [
67
66
  "sphinx_automodapi",
68
67
  "autodoc_pydantic",
69
68
  ]
69
+ lint = [
70
+ "ruff",
71
+ ]
72
+ typing = [
73
+ { include-group = "tests" },
74
+ "mypy",
75
+ "pydantic",
76
+ "types-requests",
77
+ ]
78
+ docs-lint = [
79
+ { include-group = "docs" },
80
+ "doc8",
81
+ ]
82
+ format-docs = [
83
+ { include-group = "docs" },
84
+ "docstrfmt",
85
+ ]
86
+ doctests = [
87
+ "xdoctest",
88
+ "pygments",
89
+ ]
90
+ pyroma = [
91
+ "pyroma",
92
+ "pygments",
93
+ ]
94
+ # follow https://github.com/astral-sh/uv/issues/6298 for switching to a uv-based version bump workflow
95
+ bump = [
96
+ "bump-my-version",
97
+ ]
98
+ build = [
99
+ "uv",
100
+ "uv-build",
101
+ ]
102
+ release = [
103
+ { include-group = "build" },
104
+ "uv",
105
+ "keyring",
106
+ ]
70
107
  network = [
71
108
  "requests",
72
109
  ]
73
110
 
111
+ # see https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#dependencies-optional-dependencies
112
+ # [project.optional-dependencies]
113
+
74
114
  # See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#urls
75
115
  # and also https://packaging.python.org/en/latest/specifications/well-known-project-urls/
76
116
  [project.urls]
@@ -114,6 +154,9 @@ source = [
114
154
  omit = [
115
155
  "tests/*",
116
156
  "docs/*",
157
+ "src/obographs/version.py",
158
+ "src/obographs/__main__.py",
159
+ "src/obographs/cli.py",
117
160
  ]
118
161
 
119
162
  [tool.coverage.paths]
@@ -189,7 +232,7 @@ known-first-party = [
189
232
  docstring-code-format = true
190
233
 
191
234
  [tool.bumpversion]
192
- current_version = "0.0.2"
235
+ current_version = "0.0.4"
193
236
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(?:-(?P<release>[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+(?P<build>[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?"
194
237
  serialize = [
195
238
  "{major}.{minor}.{patch}-{release}+{build}",
@@ -0,0 +1,73 @@
1
+ """A python data model for OBO Graphs."""
2
+
3
+ from .model import (
4
+ Definition,
5
+ DomainRangeAxiom,
6
+ Edge,
7
+ EquivalentNodeSet,
8
+ ExistentialRestrictionExpression,
9
+ Graph,
10
+ GraphDocument,
11
+ LogicalDefinition,
12
+ Meta,
13
+ Node,
14
+ NodeType,
15
+ Property,
16
+ PropertyChainAxiom,
17
+ PropertyType,
18
+ Synonym,
19
+ Xref,
20
+ read,
21
+ )
22
+ from .standardized import (
23
+ StandardizedBaseModel,
24
+ StandardizedDefinition,
25
+ StandardizedDomainRangeAxiom,
26
+ StandardizedEdge,
27
+ StandardizedEquivalentNodeSet,
28
+ StandardizedExistentialRestriction,
29
+ StandardizedGraph,
30
+ StandardizedGraphDocument,
31
+ StandardizedLogicalDefinition,
32
+ StandardizedMeta,
33
+ StandardizedNode,
34
+ StandardizedProperty,
35
+ StandardizedPropertyChainAxiom,
36
+ StandardizedSynonym,
37
+ StandardizedXref,
38
+ )
39
+
40
+ __all__ = [
41
+ "Definition",
42
+ "DomainRangeAxiom",
43
+ "Edge",
44
+ "EquivalentNodeSet",
45
+ "ExistentialRestrictionExpression",
46
+ "Graph",
47
+ "GraphDocument",
48
+ "LogicalDefinition",
49
+ "Meta",
50
+ "Node",
51
+ "NodeType",
52
+ "Property",
53
+ "PropertyChainAxiom",
54
+ "PropertyType",
55
+ "StandardizedBaseModel",
56
+ "StandardizedDefinition",
57
+ "StandardizedDomainRangeAxiom",
58
+ "StandardizedEdge",
59
+ "StandardizedEquivalentNodeSet",
60
+ "StandardizedExistentialRestriction",
61
+ "StandardizedGraph",
62
+ "StandardizedGraphDocument",
63
+ "StandardizedLogicalDefinition",
64
+ "StandardizedMeta",
65
+ "StandardizedNode",
66
+ "StandardizedProperty",
67
+ "StandardizedPropertyChainAxiom",
68
+ "StandardizedSynonym",
69
+ "StandardizedXref",
70
+ "Synonym",
71
+ "Xref",
72
+ "read",
73
+ ]
@@ -9,27 +9,35 @@
9
9
 
10
10
  from __future__ import annotations
11
11
 
12
+ import gzip
12
13
  import json
13
14
  import logging
14
15
  from collections import defaultdict
15
16
  from pathlib import Path
16
- from typing import TYPE_CHECKING, Any, Literal, TypeAlias, overload
17
+ from typing import TYPE_CHECKING, Literal, TypeAlias, overload
17
18
 
19
+ import curies
20
+ from curies.vocabulary import SynonymScopeOIO
18
21
  from pydantic import BaseModel, Field
19
22
 
20
23
  if TYPE_CHECKING:
21
- import curies
22
-
23
24
  from .standardized import StandardizedGraph
24
25
 
25
26
  __all__ = [
26
27
  "Definition",
28
+ "DomainRangeAxiom",
27
29
  "Edge",
30
+ "EquivalentNodeSet",
31
+ "ExistentialRestrictionExpression",
28
32
  "Graph",
29
33
  "GraphDocument",
34
+ "LogicalDefinition",
30
35
  "Meta",
31
36
  "Node",
37
+ "NodeType",
32
38
  "Property",
39
+ "PropertyChainAxiom",
40
+ "PropertyType",
33
41
  "Synonym",
34
42
  "Xref",
35
43
  "read",
@@ -40,31 +48,24 @@ logger = logging.getLogger(__name__)
40
48
  OBO_URI_PREFIX = "http://purl.obolibrary.org/obo/"
41
49
  OBO_URI_PREFIX_LEN = len(OBO_URI_PREFIX)
42
50
 
43
- SynonymPredicate: TypeAlias = Literal[
44
- "hasExactSynonym",
45
- "hasBroadSynonym",
46
- "hasNarrowSynonym",
47
- "hasRelatedSynonym",
48
- ]
49
51
  NodeType: TypeAlias = Literal["CLASS", "PROPERTY", "INDIVIDUAL"]
50
52
 
51
- TimeoutHint = int | float | None
53
+ #: When node type is ``PROPERTY``, this is extra information
54
+ PropertyType: TypeAlias = Literal["ANNOTATION", "OBJECT", "DATA"]
52
55
 
53
- #: A mapping from OBO flat file format internal synonym types to OBO in OWL vocabulary
54
- #: identifiers. See https://owlcollab.github.io/oboformat/doc/GO.format.obo-1_4.html
55
- OBO_SYNONYM_TO_OIO: dict[str, SynonymPredicate] = {
56
- "EXACT": "hasExactSynonym",
57
- "BROAD": "hasBroadSynonym",
58
- "NARROW": "hasNarrowSynonym",
59
- "RELATED": "hasRelatedSynonym",
60
- }
56
+ TimeoutHint = int | float | None
61
57
 
62
58
 
63
59
  class Property(BaseModel):
64
60
  """Represent a property inside a metadata element."""
65
61
 
66
62
  pred: str
67
- val: str
63
+ val: str | None = Field(
64
+ None,
65
+ description="Stores the value of the property. This can be a string representing a "
66
+ "literal or IRI. This isn't supposed to be nullable, but it happens a lot - might be a "
67
+ "bug in OWLAPI or ROBOT",
68
+ )
68
69
  xrefs: list[str] | None = None
69
70
  meta: Meta | None = None
70
71
 
@@ -86,8 +87,8 @@ class Synonym(BaseModel):
86
87
  """Represents a synonym inside an object meta."""
87
88
 
88
89
  val: str | None = Field(default=None)
89
- pred: str = Field(default="hasExactSynonym")
90
- synonymType: str | None = Field(examples=["OMO:0003000"]) # noqa:N815
90
+ pred: SynonymScopeOIO = Field(default="hasExactSynonym")
91
+ synonymType: str | None = Field(None, examples=["OMO:0003000"]) # noqa:N815
91
92
  xrefs: list[str] = Field(
92
93
  default_factory=list,
93
94
  description="A list of CURIEs/IRIs for provenance for the synonym",
@@ -123,7 +124,52 @@ class Node(BaseModel):
123
124
  id: str = Field(..., description="The IRI for the node")
124
125
  lbl: str | None = Field(None, description="The name of the node")
125
126
  meta: Meta | None = None
126
- type: NodeType = Field(..., description="Type of node")
127
+ type: NodeType | None = Field(None, description="Type of node")
128
+ propertyType: PropertyType | None = Field( # noqa:N815
129
+ None, description="Type of property, if the node type is a property"
130
+ )
131
+
132
+
133
+ class DomainRangeAxiom(BaseModel):
134
+ """Represents a domain/range axiom."""
135
+
136
+ predicateId: str # noqa:N815
137
+ domainClassIds: list[str] | None = None # noqa:N815
138
+ rangeClassIds: list[str] | None = None # noqa:N815
139
+ allValuesFromEdges: list[Edge] | None = None # noqa:N815
140
+ meta: Meta | None = None
141
+
142
+
143
+ class PropertyChainAxiom(BaseModel):
144
+ """Represents a property chain axiom."""
145
+
146
+ predicateId: str # noqa:N815
147
+ chainPredicateIds: list[str] # noqa:N815
148
+ meta: Meta | None = None
149
+
150
+
151
+ class ExistentialRestrictionExpression(BaseModel):
152
+ """Represents an existential restriction."""
153
+
154
+ propertyId: str # noqa:N815
155
+ fillerId: str # noqa:N815
156
+
157
+
158
+ class LogicalDefinition(BaseModel):
159
+ """Represents a logical definition chain axiom."""
160
+
161
+ definedClassId: str # noqa:N815
162
+ genusIds: list[str] | None = None # noqa:N815
163
+ restrictions: list[ExistentialRestrictionExpression] | None = None
164
+ meta: Meta | None = None
165
+
166
+
167
+ class EquivalentNodeSet(BaseModel):
168
+ """Represents a set of equivalent nodes."""
169
+
170
+ representativeNodeId: str # noqa:N815
171
+ nodeIds: list[str] # noqa:N815
172
+ meta: Meta | None = None
127
173
 
128
174
 
129
175
  class Graph(BaseModel):
@@ -133,10 +179,10 @@ class Graph(BaseModel):
133
179
  meta: Meta | None = None
134
180
  nodes: list[Node] = Field(default_factory=list)
135
181
  edges: list[Edge] = Field(default_factory=list)
136
- equivalentNodesSets: list[Any] = Field(default_factory=list) # noqa:N815
137
- logicalDefinitionAxioms: list[Any] = Field(default_factory=list) # noqa:N815
138
- domainRangeAxioms: list[Any] = Field(default_factory=list) # noqa:N815
139
- propertyChainAxioms: list[Any] = Field(default_factory=list) # noqa:N815
182
+ equivalentNodesSets: list[EquivalentNodeSet] = Field(default_factory=list) # noqa:N815
183
+ logicalDefinitionAxioms: list[LogicalDefinition] = Field(default_factory=list) # noqa:N815
184
+ domainRangeAxioms: list[DomainRangeAxiom] = Field(default_factory=list) # noqa:N815
185
+ propertyChainAxioms: list[PropertyChainAxiom] = Field(default_factory=list) # noqa:N815
140
186
 
141
187
  def standardize(self, converter: curies.Converter) -> StandardizedGraph:
142
188
  """Standardize the graph."""
@@ -205,12 +251,14 @@ def read(
205
251
 
206
252
  elif isinstance(source, str | Path):
207
253
  path = Path(source).expanduser().resolve()
208
- if path.is_file():
209
- if path.suffix.endswith(".gz"):
210
- raise NotImplementedError
211
- else:
212
- with path.open() as file:
213
- graph_document = GraphDocument.model_validate(json.load(file))
254
+ if not path.is_file():
255
+ raise FileNotFoundError
256
+ if path.suffix.endswith(".gz"):
257
+ with gzip.open(path, mode="rt") as file:
258
+ graph_document = GraphDocument.model_validate(json.load(file))
259
+ else:
260
+ with path.open() as file:
261
+ graph_document = GraphDocument.model_validate(json.load(file))
214
262
  else:
215
263
  raise TypeError(f"Unhandled source: {source}")
216
264