Skip to main content

Python bindings for the shifty SHACL engine

Project description

shifty

A formalism-first SHACL validation and SHACL-AF inference engine written in Rust, grounded in the algebraic treatment of Common Foundations for SHACL, ShEx, and PG-Schema (arXiv:2502.01295). Available as a command-line tool and as Python bindings (pyshifty).

Features

  • Full SHACL Core validation — node and property shapes, all standard constraint components
  • SHACL-AF inference — forward-chaining sh:rule evaluation (Triple Rules, SPARQL Construct Rules) to a fixed point, with stratification analysis for recursive rulesets
  • Algebraic IR — shapes are lowered to a path algebra (π) and shape grammar (φ) before evaluation; the same IR drives both validation and inference
  • Native SPARQL execution — a subset of sh:sparql constraints and SPARQL Construct rules runs directly over an indexed dataset without a full SPARQL engine, with automatic fallback to Spareval for unsupported constructs
  • Multi-layer pipeline — parsing → algebraic lowering → normalization/CSE → physical planning → execution; each layer is independently inspectable
  • pyshifty-compatible Python APIvalidate() returns (conforms, report_graph, results_text) matching pyshifty's interface

Installation

CLI

cargo install --path crates/shifty-cli

Or build from source:

cargo build --release -p shifty-cli
# binary at target/release/shifty

Python

pip install pyshifty

The package installs as pyshifty but is imported as shifty:

import shifty

To build from source (requires Rust and maturin):

cd python
pip install maturin
maturin develop

CLI usage

Validate

shifty validate --shapes shapes.ttl --data data.ttl
conforms: false
violations: 1
  <http://example.org/bob>  [target: ∃ rdf:type .⊤]
      - (ex:name) 123 → expected datatype xsd:string

Emit a W3C sh:ValidationReport in Turtle:

shifty validate --shapes shapes.ttl --data data.ttl --report

JSON output:

shifty validate --shapes shapes.ttl --data data.ttl --format json

Graph mode controls which triples are visible to path traversal and SPARQL evaluation:

# default: focus nodes from data; paths/SPARQL use data ∪ shapes
shifty validate --shapes shapes.ttl --data data.ttl --graph-mode union

# focus nodes and evaluation use data only
shifty validate --shapes shapes.ttl --data data.ttl --graph-mode data

# focus nodes and evaluation both use data ∪ shapes
shifty validate --shapes shapes.ttl --data data.ttl --graph-mode union-all

Infer

Run SHACL-AF rules to a fixed point, then print the derived triples:

shifty infer --shapes rules.ttl --data data.ttl
inferred 3 triple(s):
  <http://example.org/r1> <http://example.org/area> "6"^^<http://www.w3.org/2001/XMLSchema#integer>
  ...

Inspect

Inspect how a shapes graph looks at each stage of the pipeline:

# Raw triples after parsing
shifty inspect --stage rdf shapes.ttl

# Lowered algebraic IR (φ/π notation)
shifty inspect --stage algebra shapes.ttl

# After normalization and common-subexpression elimination
shifty inspect --stage normalized shapes.ttl

# Stratification analysis (recursion detection)
shifty inspect --stage strata shapes.ttl

# Physical plan: focus sources + cost-ordered shape checks
shifty inspect --stage plan shapes.ttl

# SPARQL constraint capability: which queries run native vs. Spareval
shifty inspect --stage capability shapes.ttl

All stages support --format text (default), --format json; the algebra and normalized stages also support --format dot for Graphviz output.

Shapes files and data files may be local paths or HTTP/HTTPS URLs. Both --shapes and --data are repeatable to merge multiple files.

Python usage

import shifty

Validate (pyshifty-compatible)

shapes = """
@prefix sh:  <http://www.w3.org/ns/shifty#> .
@prefix ex:  <http://example.org/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

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: <http://example.org/> .

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

Graph inputs can be a string, bytes, pathlib.Path, or rdflib.Graph. If shacl_graph is omitted, shapes are expected to be embedded in the data graph.

To validate a shapes graph against itself, pass it once. The embedded path parses and plans one graph without constructing separate data and shapes graphs:

result = shifty.validate_algebra("shapes.ttl", infer=False)
conforms, report_graph, results_text = shifty.validate("shapes.ttl", infer=False)

pathlib.Path inputs are parsed directly in Rust. rdflib.Graph inputs are transferred as N-Triples.

Reuse prepared shapes

For multiple data graphs using the same shapes, cache parsing, normalization, and planning with PreparedValidator:

validator = shifty.PreparedValidator(shapes)

result = validator.validate_algebra(data, infer=False)
conforms, report_graph, results_text = validator.validate(data)

Validate with structured result

validate_algebra returns an AlgebraResult with typed Violation objects instead of an RDF report graph:

result = shifty.validate_algebra(data, shapes)
print(result.conforms)        # False
print(result.results_text)    # human-readable summary (built and cached on first access)
for v in result.violations:
    print(v.focus_node)       # IRI of the failing focus node
    print(v.shape_name)       # IRI of the violated shape, or None
    for r in v.reasons:
        print(r.message)      # human-readable failure description
        print(r.path)         # path that was checked, if applicable
        print(r.value)        # the offending value node

Set infer=False when validation should not first run embedded SHACL-AF rules to a fixed point.

Infer

Run SHACL-AF rules to a fixed point:

rules = """
@prefix sh: <http://www.w3.org/ns/shifty#> .
@prefix ex: <http://example.org/> .

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: <http://example.org/> .
ex:r1 a ex:Rectangle ; ex:width 3 ; ex:height 2 .
"""

result = shifty.infer(data, rules)
print(result.inferred_count)    # number of newly derived triples
g = result.graph()              # rdflib.Graph with original + inferred data

graph_mode

validate() and validate_algebra() accept a graph_mode keyword argument:

shifty.validate(data, shapes, graph_mode="union")      # default
shifty.validate(data, shapes, graph_mode="data")
shifty.validate(data, shapes, graph_mode="union-all")

When shacl_graph is omitted, all three modes are equivalent because focus discovery and evaluation use the same embedded graph. infer() does not accept graph_mode.

File inputs

import pathlib

conforms, report, text = shifty.validate(
    pathlib.Path("data.ttl"),
    pathlib.Path("shapes.ttl"),
)

Crate structure

crate role
shifty-algebra path algebra π, shape grammar φ, schema arena, rendering
shifty-parse Turtle/RDF → algebraic IR lowering
shifty-opt normalization, stratification, physical planning, native SPARQL lowering
shifty-engine validation + AF inference execution, SPARQL executor
shifty-cli shifty binary
pyshifty (python/) PyO3 bindings, published as pyshifty on PyPI

Design docs

The docs/ directory contains the full design:

License

BSD-3-Clause

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyshifty-0.1.4.tar.gz (201.5 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pyshifty-0.1.4-cp39-abi3-win_amd64.whl (2.6 MB view details)

Uploaded CPython 3.9+Windows x86-64

pyshifty-0.1.4-cp39-abi3-musllinux_1_2_x86_64.whl (29.6 MB view details)

Uploaded CPython 3.9+musllinux: musl 1.2+ x86-64

pyshifty-0.1.4-cp39-abi3-musllinux_1_2_aarch64.whl (29.2 MB view details)

Uploaded CPython 3.9+musllinux: musl 1.2+ ARM64

pyshifty-0.1.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (29.6 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ x86-64

pyshifty-0.1.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (29.2 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

pyshifty-0.1.4-cp39-abi3-macosx_11_0_arm64.whl (2.8 MB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

File details

Details for the file pyshifty-0.1.4.tar.gz.

File metadata

  • Download URL: pyshifty-0.1.4.tar.gz
  • Upload date:
  • Size: 201.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pyshifty-0.1.4.tar.gz
Algorithm Hash digest
SHA256 9e0853e8a6af2acb04404164bb1cb1c5ed07605a855d2bf9a9a0717e3ba787cb
MD5 2754a231fc49e73540ca0a512aa127a9
BLAKE2b-256 47bae59a822aa2cc7b0d249e9e7e43ada92041f436d36a5e383320e6635d8d74

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.4.tar.gz:

Publisher: release.yml on gtfierro/shifty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyshifty-0.1.4-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: pyshifty-0.1.4-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 2.6 MB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pyshifty-0.1.4-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 6d3694437d9f3fe5a1fac5448d7cf0b63d00663ed32d661f1eeee9eb70562b95
MD5 89d0b2b9a17e10a2a14a461cddcdbc2e
BLAKE2b-256 ea975e41f0bb09c116f8111da95fbc392d60b7188854234e3b43c380a4e62596

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.4-cp39-abi3-win_amd64.whl:

Publisher: release.yml on gtfierro/shifty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyshifty-0.1.4-cp39-abi3-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for pyshifty-0.1.4-cp39-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 a0ea104c531ca27cf20d2034bb3f92a02182d52ac887a18bee6e456845be007a
MD5 2888db5ef94f69f8a1e7d394aa939066
BLAKE2b-256 01ea8b56ed144d2cd8d196d48a396302b8384ac9ccdb7dd5c7dc0c9b4a090641

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.4-cp39-abi3-musllinux_1_2_x86_64.whl:

Publisher: release.yml on gtfierro/shifty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyshifty-0.1.4-cp39-abi3-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for pyshifty-0.1.4-cp39-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 295b69a237ddd5dd098b07d10a4eb29b51ab9d346021e5e6ac8ba4fa65bde52e
MD5 35c766b8b27882e1556c7fc84c6a673e
BLAKE2b-256 efbe4993131e4916c5282f97760be2a1f408bbd400e545903ffb4268208999e4

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.4-cp39-abi3-musllinux_1_2_aarch64.whl:

Publisher: release.yml on gtfierro/shifty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyshifty-0.1.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pyshifty-0.1.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 8561a264525620313ead509c99972c42cfd06aa86dbb615dd71ededc75f8f2f9
MD5 6b8520033243b18779f87d0ee1b17701
BLAKE2b-256 6340e9a6bae742ea333eab93d1f13599788622501151b7d283b8e5cc500e2c9c

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on gtfierro/shifty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyshifty-0.1.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pyshifty-0.1.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 6dbf3748d84419653966e869b687e137b1baa5170580d372184b0cc73bf03546
MD5 e5173136a9c8659141bde9a9de16fbc0
BLAKE2b-256 1059e310880e6c03a58000ad7802eda790ccdc967197553c45bfbe9b59df1675

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on gtfierro/shifty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyshifty-0.1.4-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pyshifty-0.1.4-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e94f047efd8abffd1e0bcb0d77eef3eb052c1c86b52a6bc983b160714f988350
MD5 768231c01bc0ed98cdde9fc9eda7e28b
BLAKE2b-256 272a0f7918fa06ce42638b6dc706bba73549ad133a54555bf768e4b867231fb7

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.4-cp39-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on gtfierro/shifty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page