Source code for djangordf.reasoning.owlrl
"""Optional wrapper around the third-party ``owlrl`` library.
Importing this module without ``owlrl`` installed raises
:class:`ImportError`; the :func:`materialize` method also raises
:class:`ImproperlyConfigured` with an install hint if the import
fails late.
"""
from typing import Optional
from django.core.exceptions import ImproperlyConfigured
from rdflib import Graph, URIRef
from .base import Reasoner, _backend_triple_count, _resolve_graph
try: # pragma: no cover - exercised when owlrl is installed
import owlrl # type: ignore[import-untyped]
_HAS_OWLRL = True
except ImportError: # pragma: no cover
owlrl = None
_HAS_OWLRL = False
[docs]
class OWLRLReasoner(Reasoner):
"""Materialise via the ``owlrl`` library's OWL-RL closure.
The reasoner loads the source graph into an in-memory
``rdflib.Graph``, runs the OWL-RL closure, and writes the *new*
triples back into the target graph through the backend.
"""
def __init__(self, profile: Optional[object] = None):
if not _HAS_OWLRL:
raise ImproperlyConfigured(
"OWLRLReasoner requires the 'owlrl' package; "
"install it with `pip install owlrl`."
)
self.profile = profile or owlrl.OWLRL_Semantics
[docs]
def materialize(
self,
backend=None,
*,
source_graph=None,
target_graph=None,
max_iterations: int = 50,
) -> int:
from ..conf import get_backend
backend = backend if backend is not None else get_backend()
src = _resolve_graph(
source_graph, URIRef("urn:djangordf:default"),
)
tgt = _resolve_graph(target_graph, src)
before_count = _backend_triple_count(backend, tgt)
# Pull the source graph into memory.
source_dump = backend.query(
"CONSTRUCT { ?s ?p ?o } WHERE { "
f"GRAPH <{src}> {{ ?s ?p ?o }} }}"
)
graph = Graph()
for triple in source_dump:
graph.add(triple)
# Snapshot existing triples in the target so we can compute
# the delta even when target != source.
existing = set()
if tgt != src:
target_dump = backend.query(
"CONSTRUCT { ?s ?p ?o } WHERE { "
f"GRAPH <{tgt}> {{ ?s ?p ?o }} }}"
)
for triple in target_dump:
existing.add(triple)
else:
existing = set(graph)
owlrl.DeductiveClosure(self.profile).expand(graph)
new_triples = [t for t in graph if t not in existing]
if new_triples:
backend.add(new_triples, graph=tgt)
return _backend_triple_count(backend, tgt) - before_count