Validate a graph¶
The common variations on running a validation, and what each one changes.
Basic run¶
shifty validate --shapes shapes.ttl --data data.ttl
conforms: false — 1 violation in 2 findings
Finding 1 of 2
target class(ex:Person)
severity Violation
shape ex:PersonShape
failure at least 1 value(s) required along ex:email, found 0
path ex:email
found 0 value(s) along the path; at least 1 required
requirement ∃[1..] ex:email
affects ex:bob
value node (the focus node itself)
also fails Finding 2
Finding 2 of 2
target class(ex:Person)
severity Violation
shape ex:PersonShape
failure test(datatype(xsd:string)) not satisfied
path ex:name
requirement test(datatype(xsd:string))
affects ex:bob
value node "123"^^xsd:integer
also fails Finding 1
notation
∃[m..n] p . X between m and n values along p satisfy X
conforms, report_graph, results_text = shifty.validate(data, shapes)
Both --shapes and --data accept local paths or http(s) URLs, and
both are repeatable — several files are merged into one graph before anything
else happens.
An explicitly supplied shapes graph with zero triples is rejected. Omitting the
second Python argument uses shapes embedded in the data graph; omitting CLI
--data makes the --shapes graph serve both roles.
Omitting --data (or the second Python argument) makes the single graph play
both roles. If that is not what you expect, read
Shapes graphs and data graphs before going further; it is the most
common source of a validation that passes for the wrong reason.
Get a machine-readable report¶
The default CLI output is a summary for a human. For a W3C
sh:ValidationReport graph, serialized as Turtle:
shifty validate --shapes shapes.ttl --data data.ttl --report
For JSON:
shifty validate --shapes shapes.ttl --data data.ttl --format json
From Python, validate() already returns the report as an rdflib.Graph,
so serialize it however you like:
conforms, report_graph, _ = shifty.validate(data, shapes)
print(report_graph.serialize(format="turtle"))
If you would rather have structured objects than an RDF graph to query,
validate_algebra() returns violations as Python objects with the failing
focus node, the property path, the offending value, and a stable constraint
kind to branch on:
result = shifty.validate_algebra(data, shapes)
for violation in result.violations:
for reason in violation.reasons:
if reason.constraint_kind == shifty.ConstraintKind.Cardinality:
print(violation.focus_node, reason.path, reason.message)
Branch on constraint_kind, never on the text of message.
Skip inference¶
By default a validation run first executes any SHACL-AF sh:rule entries in
the shapes graph to a fixed point, and validates the extended graph. If your
shapes contain no rules this costs almost nothing; if they do, and you want to
validate only what is asserted:
shifty validate --shapes shapes.ttl --data data.ttl --no-infer
conforms, report, text = shifty.validate(data, shapes, infer=False)
Going the other way — keeping what inference derives — is in_place=True,
covered in Inference during validation.
Validate against only some shapes¶
A large shapes graph often contains many independent profiles, and you want one
of them. --shape-name (repeatable; --entry-shape is an alias) restricts
which shapes act as entry points:
shifty validate --shapes shapes.ttl --data data.ttl \
--shape-name http://example.org/PersonShape
conforms, report, text = shifty.validate(
data, shapes,
shape_names=["http://example.org/PersonShape"],
)
Only target-bearing statements owned by the named shapes select focus nodes.
Helper shapes those entries reach through sh:node, sh:property,
qualified value shapes, or boolean combinations are still evaluated as normal —
this narrows what is checked, not what the checks mean. IRIs may be bare or in
angle brackets.
Change which triples are visible¶
graph_mode controls the graph that property paths, class hierarchies, and
SPARQL see during evaluation. It is independent of where shape definitions
come from.
shifty validate --shapes shapes.ttl --data data.ttl --graph-mode union-all
conforms, report, text = shifty.validate(data, shapes, graph_mode="union-all")
data— focus nodes and evaluation use the data graph alone.union(default) — focus nodes from data; evaluation sees data ∪ shapes.union-all— both focus selection and evaluation see data ∪ shapes.
The default exists because class hierarchies and ontology axioms are usually
authored alongside the shapes, and sh:class needs to traverse
rdfs:subClassOf to work. Reach for data when you want a strict check
that the data stands on its own; reach for union-all when the shapes graph
also contains instances you intend to validate.
The shapes and data graph explanation tabulates these modes and covers the separate question of where shape definitions come from.
Choose what counts as failure¶
SHACL constraints carry a sh:severity. By default every severity — info,
warning, and violation — makes the run non-conforming. To ignore the milder
ones:
shifty validate --shapes shapes.ttl --data data.ttl --minimum-severity violation
conforms, report, text = shifty.validate(
data, shapes, minimum_severity="violation",
)
Results below the threshold remain in the W3C report and Python result objects;
the threshold controls their conforms value. The CLI’s default text and JSON
summaries omit below-threshold findings.
Validate many graphs against one schema¶
Compiling a shapes graph — parsing, lowering, normalizing, planning — is a
fixed cost paid before any data is examined, and for a large ontology it
dominates a small validation. PreparedValidator pays it once:
validator = shifty.PreparedValidator(shapes)
for path in data_files:
conforms, report, text = validator.validate(path)
This matters more than it sounds like it should: on the Brick corpus, whose models are small against a 229k-triple shapes closure, most of a per-process run’s wall clock is this setup. See Benchmarks.
Handle unsupported constructs¶
A few SHACL features are supported partially (see
Feature support). on_unsupported decides what happens
when the engine meets one:
conforms, report, text = shifty.validate(data, shapes, on_unsupported="error")
"ignore" (the default) makes a best effort and may return an unreliable
answer; "error" refuses, so the problem surfaces instead of being silently
absorbed. If you are validating anything you will act on, "error" is the
safer default and the one to start with.
Invalid shapes are separate from on_unsupported. A malformed SHACL
constraint or rule — including malformed SPARQL or an unresolved query prefix
— raises an error before validation starts. It is never treated as an omitted
unsupported feature.
See also¶
CLI reference and Python API reference — every flag and argument.
Explain why a node passed or failed — when the report is not enough.