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.5.tar.gz (213.6 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.5-cp39-abi3-win_amd64.whl (2.7 MB view details)

Uploaded CPython 3.9+Windows x86-64

pyshifty-0.1.5-cp39-abi3-musllinux_1_2_x86_64.whl (30.0 MB view details)

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

pyshifty-0.1.5-cp39-abi3-musllinux_1_2_aarch64.whl (29.6 MB view details)

Uploaded CPython 3.9+musllinux: musl 1.2+ ARM64

pyshifty-0.1.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (30.1 MB view details)

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

pyshifty-0.1.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (29.6 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

pyshifty-0.1.5-cp39-abi3-macosx_11_0_arm64.whl (2.9 MB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

File details

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

File metadata

  • Download URL: pyshifty-0.1.5.tar.gz
  • Upload date:
  • Size: 213.6 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.5.tar.gz
Algorithm Hash digest
SHA256 2b39cff2c889e314a1228d77fce7955f3258a72c06664aa65ec77ca66b8a7235
MD5 e1c2570e7c291c9af1c0c2b2763a4530
BLAKE2b-256 b9cd323b9374ebfcfdb8464a39e1898563f51e51e7be6344583c267a6a83325a

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.5.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.5-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: pyshifty-0.1.5-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 2.7 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.5-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 2db0bcdba0fc05fff7519cb3196667e1529f5aa94d93e688a92f16be628adc66
MD5 e635ea5820868400a6325dc82701a76e
BLAKE2b-256 ade29bfadd077bf00723ed671e18a4edbcf273c07c7d0dff88bef2a0f6b0143d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.5-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.5-cp39-abi3-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for pyshifty-0.1.5-cp39-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 310b3ee586d6152d936ddc802566701858a7f6bd092f341955b5736c094978a6
MD5 57bb8ee9880a7cab633f87fe0c85dd7a
BLAKE2b-256 73958b6289e93f24f9ea6787c2e68f16bf75216708c5e52b62d99d3d4c1c93bd

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.5-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.5-cp39-abi3-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for pyshifty-0.1.5-cp39-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 3d14f3fdcd5c6553962e4632d72b666b6b5620d6a1d4d24232039705bea6ad1a
MD5 c6f0b7ab56e2534ec92c466f5c04f7b0
BLAKE2b-256 b8cd17eef3c21444d44000aa9e8c177e6778e50e8a1d7c95bdd47eb797cf48fc

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.5-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.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pyshifty-0.1.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f569ddc55bcad44749e92f21f83ed1c97e1fa9273ce03acdb2216969f34d3e60
MD5 14e88c529d3610f82cb507306a463d42
BLAKE2b-256 e8cd24e6a6c9f75340c038d7637cc664daa72392d0f18ce49695ea4606ac3e36

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.5-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.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pyshifty-0.1.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 12708694d611ef2e2b1db1fa2fedcd62cae5248dd8a38298c8b693f8ae4803ca
MD5 8c372f6a13bdbcac2d5c457b88423873
BLAKE2b-256 9e5525a03bfa50f4c9a29f93c3b9588d09b7e82611276931e3564021d12835e2

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.5-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.5-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pyshifty-0.1.5-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 bb4363d83a1974740b115efeb23478ac78ca71f6dde6e1cfbfa4c92b1e6ad88a
MD5 085e45c3390047b979d04e47b0710309
BLAKE2b-256 fae1cef8665b7e82f92503aa424949a5bfef993e8198458992b65f4fce36b50f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyshifty-0.1.5-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