pip install shacl2cypher validates graphs from Python
SHACL → Cypher compiler
Validate a property graph with shapes, not hand-written queries.
shacl2cypher compiles standard W3C SHACL shapes into named, read-only Cypher queries. Run them against Neo4j or LadybugDB and get back every broken rule, the node or relationship that broke it, and the value at fault. Reports come as a table, JSON, JUnit or SARIF.
$ shacl2cypher validate shapes.ttl --ladybug graph.lbug --node-key id
STATUS SEVERITY VIOLATIONS TIME(ms) RULE
failed Violation 1 31 PersonShape.name.minCount
- Person id=p3: Person needs a name
failed Warning 1 3 PersonShape.name.pattern
- Person id=p1: ex:PersonShape violates sh:pattern on ex:name (value: "ann")
failed Violation 1 8 PersonShape.worksFor.maxCount
- Person id=p1: ex:PersonShape violates sh:maxCount on ex:worksFor
3 rules: 0 passed, 3 failed, 0 timed out, 0 errors, 0 skipped, 0 guaranteed by schema; 3 violations in 42 ms
The problem it removes
Property graphs built by applications have no native SHACL engine. The rules that keep the data sane end up spread across application code, half-enforced database constraints, and ad-hoc Cypher that someone runs by hand. shacl2cypher puts them in one declarative file and turns that file into queries.
Write SHACL, not a new DSL
Shapes are plain W3C SHACL in Turtle. The optional s2c: hints are ignored by
other SHACL engines, so the same file still validates an RDF export with pySHACL.
One query per constraint
Each constraint becomes its own named query, so it can be run, timed and baselined on its own. A failure names the rule, the focus node and the offending value.
Read-only by construction
Neo4j queries run in a transaction that is always rolled back. LadybugDB files are opened read-only. Validation never changes your data.
How it works
Compilation is a pure, deterministic pipeline. The same shapes, schema snapshot and options always give a byte-identical manifest.
Load
Every shapes file, plus its
owl:imports, is read into one union graph, and every triple remembers its file and line.Resolve
Classes become labels, and predicates become properties or relationship types. The mapping uses naming conventions, then a schema snapshot, then explicit
s2c:annotations, which always win.Lower and render
Each constraint is lowered into a dialect-neutral IR and rendered as null-safe Cypher for Neo4j 5 or LadybugDB.
Emit and run
compilewritesmanifest.jsonandqueries.cypher.validateruns every summary query, then detail queries for the rules that failed, and writes a report with a CI-friendly exit code.
What you get
sh:or or sh:node fails, the row lists which inner constraints failed. A standard SHACL report only names the outer one.ex:PersonShape/ex:name/sh:minCount. Fingerprints change only when a rule's meaning changes, so CI can tell a rule changed even when its name did not.pip install shacl2cypher and npm install shacl2cypher run the same compiler in-process, with output byte-identical to the CLI's. Python on PyPI →Databases
| Database | Status | Notes |
|---|---|---|
| Neo4j 5 | Supported | Cypher without APOC. Schema snapshot optional. Connects over Bolt. |
| LadybugDB | Supported | Embedded and strictly typed. A schema snapshot is required and is read from the database file automatically. |
| FalkorDB 4.20 | In development | A dedicated renderer is being built from spike findings. See the FalkorDB article. |