Python API Reference
====================
.. raw:: html
The
pyshifty package exposes the full Shifty engine through
PyO3 bindings with native
rdflib interop.
Pre-built wheels are on PyPI — no Rust toolchain required.
Install
-------
.. code-block:: bash
pip install pyshifty # Python 3.9+
.. code-block:: python
import shifty # the package imports as `shifty`
Graph inputs throughout the API can be a ``str`` (Turtle text), ``bytes``,
``pathlib.Path``, or an ``rdflib.Graph``.
validate
--------
The primary validation entry point is compatible with the ``pyshacl`` interface:
.. code-block:: python
conforms, report_graph, results_text = shifty.validate(data, shapes)
- ``data`` — the data graph to validate
- ``shapes`` — the SHACL shapes graph (optional; if omitted, shapes are read from ``data``)
- Returns: ``(bool, rdflib.Graph, str)`` — conforms flag, W3C ``sh:ValidationReport`` graph, human-readable summary
.. code-block:: python
shapes = """
@prefix sh: .
@prefix ex: .
@prefix xsd: .
ex:PersonShape a sh:NodeShape ;
sh:targetClass ex:Person ;
sh:property [
sh:path ex:name ;
sh:minCount 1 ;
sh:datatype xsd:string ;
] ;
sh:property [
sh:path ex:age ;
sh:maxCount 1 ;
sh:datatype xsd:integer ;
] .
"""
data = """
@prefix ex: .
ex:Alice a ex:Person ; ex:name "Alice" ; ex:age 30 .
ex:Bob a ex:Person .
"""
conforms, report_graph, results_text = shifty.validate(data, shapes)
# conforms → False
# report_graph → rdflib.Graph with sh:ValidationReport
# results_text → human-readable summary
Keyword arguments
~~~~~~~~~~~~~~~~~
.. list-table::
:widths: 25 75
:header-rows: 1
* - Argument
- Description
* - ``graph_mode``
- ``"data"`` (default), ``"union"``, or ``"union-all"`` — controls which triples are visible to path traversal and SPARQL evaluation
* - ``infer``
- ``True`` (default) — run SHACL-AF ``sh:rule`` entries to a fixed point before validating; set ``False`` to skip inference
.. code-block:: python
# Skip inference, validate data only
conforms, report, text = shifty.validate(data, shapes, infer=False)
# Use the fully-merged graph for path evaluation
conforms, report, text = shifty.validate(data, shapes, graph_mode="union")
Embedded shapes
~~~~~~~~~~~~~~~
If shapes are embedded in the data graph, omit the second argument or pass ``None``:
.. code-block:: python
conforms, report, text = shifty.validate("combined.ttl")
conforms, report, text = shifty.validate(combined_graph, None)
Do **not** pass an empty ``rdflib.Graph()`` for this case — an empty graph is treated
as an explicit empty shapes graph and will produce no violations.
validate_algebra
~~~~~~~~~~~~~~~~
Returns structured ``Violation`` objects instead of an RDF report graph:
.. code-block:: python
result = shifty.validate_algebra(data, shapes)
print(result.conforms) # False
for v in result.violations:
print(v.focus_node) # IRI of the failing focus node
print(v.shape_name) # shape that targeted this node (if any)
for r in v.reasons:
print(r.message) # human-readable failure description
print(r.path) # property path checked, if applicable
print(r.value) # the offending value node
Accepts the same ``infer=`` and ``graph_mode=`` keyword arguments as ``validate()``.
PreparedValidator
~~~~~~~~~~~~~~~~~
For repeated validation against the same shapes graph, compile once:
.. code-block:: python
validator = shifty.PreparedValidator(shapes)
# Validate many data graphs against the compiled shapes
for data_file in data_files:
conforms, report, text = validator.validate(data_file)
# Or use the structured result form
result = validator.validate_algebra(data, infer=False)
File inputs
~~~~~~~~~~~
.. code-block:: python
import pathlib
conforms, report, text = shifty.validate(
pathlib.Path("data.ttl"),
pathlib.Path("shapes.ttl"),
)
``pathlib.Path`` inputs are parsed directly by Rust. ``rdflib.Graph`` inputs
use N-Triples for the Python-to-Rust transfer to avoid rdflib's slower Turtle
serializer.
infer
-----
Run SHACL-AF ``sh:rule`` entries to a fixed point:
.. code-block:: python
result = shifty.infer(data, rules)
print(result.inferred_count) # number of newly derived triples
g = result.graph() # rdflib.Graph with original + inferred data
.. code-block:: python
rules = """
@prefix sh: .
@prefix ex: .
ex:RectangleShape a sh:NodeShape ;
sh:targetClass ex:Rectangle ;
sh:rule [
a sh:TripleRule ;
sh:subject sh:this ;
sh:predicate ex:area ;
sh:object [ sh:path ex:width ] ;
] .
"""
data = """
@prefix ex: .
ex:r1 a ex:Rectangle ; ex:width 3 ; ex:height 2 .
"""
result = shifty.infer(data, rules)
print(result.inferred_count) # 1
g = result.graph() # graph contains ex:r1 ex:area 3 .
If rules are embedded in the data graph, omit the second argument or pass ``None``:
.. code-block:: python
result = shifty.infer(combined_data_and_rules)
result = shifty.infer(combined_data_and_rules, None)
Passing ``rdflib.Graph()`` as the second argument means "run with an explicit
empty rules graph" — no embedded rules will be parsed.
graph_mode
----------
Both ``validate()`` and ``validate_algebra()`` accept a ``graph_mode`` keyword argument
that controls which triples are visible to path traversal and SPARQL evaluation:
.. list-table::
:widths: 20 80
:header-rows: 1
* - Mode
- Behaviour
* - ``"data"`` *(default)*
- Focus nodes come from the data graph; path traversal and SPARQL use data only
* - ``"union"``
- Focus nodes from data; paths and SPARQL use data ∪ shapes
* - ``"union-all"``
- Focus nodes and evaluation both use data ∪ shapes
``infer()`` does not accept ``graph_mode``.
For full API documentation including the ``RepairSession`` interface, see
`docs.rs/shifty-engine `_.