Skip to content

Commit e839420

Browse files
authored
[codex] migrate funowl integration to py-horned-owl (#868)
* migrate funowl integration to py-horned-owl * fix lint issues in owl migration files * skip ubergraph live tests on remote timeouts * expand horned owl graph support and docs * add missing graph projection owl fixture
1 parent 74b7461 commit e839420

19 files changed

Lines changed: 4185 additions & 2942 deletions

File tree

docs/datamodels/funowl/index.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
FunOwl
44
=======
55

6-
FunOwl is a :term:`Datamodel` for expressing :term:`OWL`.
6+
``funowl`` is the historical name used in OAK for the OWL datamodel-facing
7+
adapter and documentation surface.
78

8-
FunOwl provides an OWL :term:`Axiom`-oriented datamodel, contrasting from a more :term:`Graph`-oriented data model
9+
The implementation is now backed by
10+
`py-horned-owl <https://github.com/ontology-tools/py-horned-owl>`_, which
11+
provides the OWL axiom-oriented object model used by :class:`OwlInterface`.
912

10-
See `FunOWL docs <https://github.com/hsolbrig/funowl>`_.
13+
This contrasts with OAK's more :term:`Graph`-oriented interfaces.

docs/faq/general.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,9 @@ SubClassOf, SomeValuesFrom, Annotations).
163163
There are a variety of ways of consuming OWL in OAK
164164

165165
- The recommended way is to use :ref:`sql_implementation`, which works off of RDF/OWL compiled to sqlite3
166-
- You can also use :ref:`funowl_implementation`, but this requires the ontology is in :term:`Functional Syntax`
166+
- You can also use :ref:`funowl_implementation`; the selector name is historical,
167+
but the adapter is now backed by ``py-horned-owl`` and is the default for local
168+
``.owl``, ``.ofn``, ``.omn``, and ``.owx`` paths
167169
- You can use a local or remote OWL ontologies serialized as RDF via the :ref:`sparql_implementation`
168170
- Using a tool like :ref:`ROBOT` to convert an OWL ontology to a serialization like :term:`OBO Format`
169171

@@ -199,4 +201,3 @@ Can I use OAK as a text annotator?
199201
-----------------------------------
200202

201203
Yes. See the :ref:`text_annotator_interface`.
202-

docs/intro/tutorial06.rst

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
Part 6: Working With OWL
2-
=====================
2+
========================
33

4-
OAK comes bundled with the `funowl <https://github.com/hsolbrig/funowl/>`_ library
4+
OAK exposes an experimental ``funowl`` adapter for local OWL files.
5+
The selector name is historical; the adapter is now backed by
6+
`py-horned-owl <https://github.com/ontology-tools/py-horned-owl>`_.
7+
Plain local ``.owl``, ``.ofn``, ``.omn``, and ``.owx`` paths default to this
8+
adapter; explicit schemes such as ``sqlite:foo.owl`` still override that
9+
default.
510

611

712
OWL Datamodel
@@ -12,7 +17,102 @@ See :ref:`funowl`
1217
OwlInterface
1318
------------
1419

15-
TODO
20+
``OwlInterface`` is the low-level OAK view for working with OWL axioms and OWL-
21+
specific structures directly.
22+
23+
Loading a local OWL file
24+
^^^^^^^^^^^^^^^^^^^^^^^^
25+
26+
For local files, plain ``.owl``, ``.ofn``, ``.omn``, and ``.owx`` paths now
27+
default to the horned-owl-backed OWL adapter:
28+
29+
.. code-block:: python
30+
31+
from oaklib import get_adapter
32+
33+
oi = get_adapter("path/to/my-ontology.owl")
34+
print(type(oi).__name__)
35+
36+
If you want a different backend, be explicit in the selector:
37+
38+
.. code-block:: python
39+
40+
sqlite_oi = get_adapter("sqlite:path/to/my-ontology.owl")
41+
sparql_oi = get_adapter("sparql:path/to/my-ontology.owl")
42+
43+
Inspecting asserted OWL axioms
44+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
45+
46+
You can iterate over raw axioms, or use helpers for common axiom types:
47+
48+
.. code-block:: python
49+
50+
from oaklib import get_adapter
51+
52+
oi = get_adapter("path/to/my-ontology.owl")
53+
54+
for axiom in oi.subclass_axioms(subclass="GO:0005634"):
55+
print(type(axiom).__name__, axiom)
56+
57+
labels = list(
58+
oi.annotation_assertion_axioms(
59+
subject="GO:0005634",
60+
property="rdfs:label",
61+
)
62+
)
63+
64+
eq_axioms = list(oi.equivalence_axioms(about="GO:0031965"))
65+
66+
If you need the underlying horned-owl ontology object, use:
67+
68+
.. code-block:: python
69+
70+
ontology = oi.owl_ontology()
71+
print(type(ontology).__name__)
72+
73+
Projected graph operations
74+
^^^^^^^^^^^^^^^^^^^^^^^^^^
75+
76+
The horned-owl adapter also projects a graph view from supported OWL patterns,
77+
so graph-oriented APIs can often be used alongside axiom APIs:
78+
79+
.. code-block:: python
80+
81+
from oaklib import get_adapter
82+
83+
oi = get_adapter("path/to/my-ontology.owl")
84+
85+
direct = list(oi.relationships(subjects=["GO:0005634"]))
86+
entailed = list(
87+
oi.relationships(
88+
subjects=["GO:0005634"],
89+
include_entailed=True,
90+
)
91+
)
92+
ancestors = list(oi.ancestors("GO:0005634", predicates=["rdfs:subClassOf"]))
93+
94+
Logical definitions are exposed in OBO Graph form:
95+
96+
.. code-block:: python
97+
98+
ldefs = list(oi.logical_definitions(subjects=["GO:0031965"]))
99+
for ldef in ldefs:
100+
print(ldef.definedClassId, ldef.genusIds, ldef.restrictions)
101+
102+
Reasoning status
103+
^^^^^^^^^^^^^^^^
104+
105+
``OwlInterface`` does not currently provide a pluggable OWL reasoner. In the
106+
current horned-owl-backed implementation:
107+
108+
- ``reasoner_configurations()`` returns an empty list
109+
- axiom filtering does not accept a ``ReasonerConfiguration``
110+
- ``include_entailed=True`` on graph methods gives a lightweight projected
111+
closure over supported OWL patterns, not full OWL reasoning
112+
113+
For precomputed closure and broader graph-style querying, the recommended
114+
production path remains the :ref:`sql_implementation`. For external OWL
115+
reasoners, see the ROBOT plugin below.
16116

17117
ROBOT Plugin
18118
------------

docs/packages/implementations/funowl.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,12 @@ FunOwl Adapter
44
===============
55

66
.. currentmodule:: oaklib.implementations.funowl.funowl_implementation
7-
7+
8+
The ``funowl`` adapter keeps its historical selector and class name for backward
9+
compatibility, but it is now implemented on top of
10+
`py-horned-owl <https://github.com/ontology-tools/py-horned-owl>`_ rather than
11+
the old ``funowl`` package. Plain local ``.owl``, ``.ofn``, ``.omn``, and
12+
``.owx`` paths resolve here by default unless you choose an explicit scheme such
13+
as ``sqlite:`` or ``sparql:``.
14+
815
.. autoclass:: FunOwlImplementation

docs/packages/interfaces/owl.rst

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,61 @@ OWL Interface
44
-------------
55

66
.. currentmodule:: oaklib.interfaces.owl_interface
7-
7+
8+
``OwlInterface`` exposes OWL axioms and OWL-specific helpers directly, without
9+
forcing everything through a graph abstraction.
10+
11+
Quick examples
12+
^^^^^^^^^^^^^^
13+
14+
Load a local OWL ontology using the default selector logic:
15+
16+
.. code-block:: python
17+
18+
from oaklib import get_adapter
19+
20+
oi = get_adapter("path/to/my-ontology.owl")
21+
22+
Inspect common OWL axiom types:
23+
24+
.. code-block:: python
25+
26+
from oaklib import get_adapter
27+
28+
oi = get_adapter("path/to/my-ontology.owl")
29+
30+
subclass_axioms = list(oi.subclass_axioms(subclass="GO:0005634"))
31+
label_axioms = list(
32+
oi.annotation_assertion_axioms(
33+
subject="GO:0005634",
34+
property="rdfs:label",
35+
)
36+
)
37+
eq_axioms = list(oi.equivalence_axioms(about="GO:0031965"))
38+
39+
Project graph-style relationships from OWL axioms:
40+
41+
.. code-block:: python
42+
43+
from oaklib import get_adapter
44+
45+
oi = get_adapter("path/to/my-ontology.owl")
46+
47+
direct = list(oi.relationships(subjects=["GO:0005634"]))
48+
entailed = list(
49+
oi.relationships(
50+
subjects=["GO:0005634"],
51+
include_entailed=True,
52+
)
53+
)
54+
55+
Reasoning
56+
^^^^^^^^^
57+
58+
``OwlInterface`` does not currently expose a general OWL reasoner. The
59+
horned-owl-backed implementation can still provide a lightweight projected
60+
closure for graph APIs when ``include_entailed=True`` is used, but that should
61+
not be interpreted as complete OWL reasoning.
62+
863
.. autoclass:: OwlInterface
964
:members:

docs/packages/selectors.rst

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ Examples of scheme-less descriptors, implicit implementation:
3131

3232
- :code:`tests/input/go-nucleus.obo` - local :term:`OBO Format` file loaded with pronto
3333
- :code:`tests/input/go-nucleus.json` - local :term:`OBO Graphs` json format file loaded with pronto
34-
- :code:`tests/input/go-nucleus.owl` - local OWL rdf/xml format file (loaded with rdflib at the moment, may change)
34+
- :code:`tests/input/go-nucleus.owl` - local OWL file loaded with the horned-owl-backed :ref:`funowl_implementation`
35+
- :code:`tests/input/go-nucleus.ofn` - local :term:`Functional Syntax` OWL file loaded with the horned-owl-backed :ref:`funowl_implementation`
36+
- :code:`tests/input/go-nucleus.omn` - local Manchester OWL file loaded with the horned-owl-backed :ref:`funowl_implementation`
37+
- :code:`tests/input/go-nucleus.owx` - local OWL/XML file loaded with the horned-owl-backed :ref:`funowl_implementation`
3538
- :code:`tests/input/go-nucleus.owl.ttl` - local OWL :term:`Turtle` file (loaded with rdflib at the moment, may change)
3639
- :code:`tests/input/go-nucleus.db` - local sqlite3 db loaded with SqlImplementation
3740
- :code:`http://purl.obolibrary.org/obo/pato.obo` - NOT IMPLEMENTED; download locally for now
@@ -44,6 +47,8 @@ Examples of explicit schemes:
4447
- :code:`pronto:tests/input/go-nucleus.obo` - local :term:`OBO Format` file loaded with pronto
4548
- :code:`pronto:tests/input/go-nucleus.json` - local :term:`OBO Graphs` json format file loaded with pronto
4649
- :code:`pronto:tests/input/go-nucleus.owl` - local OWL rdf/xml format file (loaded with pronto at the moment may change)
50+
- :code:`funowl:tests/input/go-nucleus.owl` - local OWL file loaded with the horned-owl-backed OWL object model
51+
- :code:`funowl:tests/input/go-nucleus.ofn` - local :term:`Functional Syntax` OWL file loaded with the horned-owl-backed OWL object model
4752
- :code:`pronto:tests/input/go-nucleus.db` - local sqlite3 db loaded with SqlImplementation
4853
- :code:`prontolib:pato.obo` - remote :term:`OBO Format` file loaded from OBO Library with pronto
4954
- :code:`prontolib:pato.owl` - remote owl format file loaded from OBO Library with pronto
@@ -120,7 +125,11 @@ funowl
120125

121126
Implementation: :ref:`funowl_implementation`
122127

123-
NOT IMPLEMENTED YET
128+
This selector name is retained for backward compatibility. The implementation is
129+
now backed by ``py-horned-owl`` while continuing to accept ``funowl:...``
130+
selectors. Plain local paths ending in ``.owl``, ``.ofn``, ``.omn``, or
131+
``.owx`` also resolve to this implementation by default; use an explicit scheme
132+
such as ``sqlite:`` or ``sparql:`` to force a different backend.
124133

125134
Functions
126135
----

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ dependencies = [
2323
"appdirs>=1.4.4",
2424
"semsql>=0.3.1",
2525
"kgcl-schema>=0.6.9,<1",
26-
"funowl>=0.2.0",
26+
"py-horned-owl>=1.1.0",
2727
"kgcl-rdflib==0.5.0",
2828
"llm>=0.14,<1",
2929
"pystow>=0.7.28",
@@ -154,4 +154,4 @@ target-version = "py310"
154154

155155
[tool.ruff.lint.mccabe]
156156
# Unlike Flake8, default to a complexity level of 10.
157-
max-complexity = 10
157+
max-complexity = 10

0 commit comments

Comments
 (0)