Shapes graphs and data graphs¶
Shifty controls the source of shape definitions separately from the triples available during evaluation:
Where do shape definitions come from?
Which triples are visible during evaluation?
The inputs determine where shapes come from. graph_mode determines which
triples are visible to validation.
Where shapes come from¶
The rule is the same in every frontend.
With a single graph, Shifty reads both shape definitions and data from it.
This supports combined files, where sh:NodeShape definitions sit alongside
instance data.
With separate shapes and data inputs, Shifty compiles the schema only from
the shapes graph. SHACL vocabulary in the data graph, such as sh:property
or sh:NodeShape, does not contribute constraints.
Invocation |
Shapes read from |
Data read from |
|---|---|---|
|
the one graph |
the one graph |
|
|
|
|
the one graph |
the one graph |
|
|
|
Why the asymmetry¶
Data graphs may include shapes copied from examples or supplied by an upstream exporter. Reading those as constraints would change the validation schema whenever the data changed. Keeping shape definitions in the supplied shapes graph makes the schema predictable and matches SHACL’s separation of shapes and data.
Shapes embedded in data¶
Both --shapes and the Python shapes argument accept multiple sources
and union them. To include shapes embedded in data, add the data file as a
shapes input:
shifty validate --shapes shapes.ttl --shapes data.ttl --data data.ttl
conforms, report, text = shifty.validate(data, [shapes, data])
To validate a combined graph in Python, omit the second argument or pass
None. An explicitly supplied shapes graph that contains no triples raises
ValueError, preventing an accidental vacuous validation from being reported
as successful.
shifty.validate(combined) # combined is both
shifty.validate(combined, None) # same
shifty.validate(combined, rdflib.Graph()) # ValueError: empty shapes graph
This guard applies only when the supplied shapes graph has zero triples. A nonempty schema whose targets select no focus nodes remains a valid conforming run.
Which triples are visible¶
The second question is entirely separate, and applies after the schema is
fixed. It is controlled by graph_mode (--graph-mode on the CLI), and it
governs both where focus nodes are selected and what path traversal,
class-hierarchy lookup, and SPARQL can see.
Graph visibility with separate shapes and data inputs. Inference extends the
data side before validation; $shapesGraph keeps naming the shapes source.¶
Mode |
Focus selection |
Evaluation graph |
|---|---|---|
|
Data |
Data |
|
Data |
Data ∪ shapes |
|
Data ∪ shapes |
Data ∪ shapes |
The default is union because of class hierarchies. sh:class ex:Sensor
has to hold for an ex:TemperatureSensor when the ontology says
ex:TemperatureSensor rdfs:subClassOf ex:Sensor — and that axiom is almost
always authored with the shapes, not with the instance data. Under data
mode the validator cannot see it, and every subclass instance fails a
constraint it satisfies.
Use data to check whether the data graph is self-contained, including its
ontology definitions. The default, union, validates nodes selected from the
data graph with vocabulary from both graphs. union-all also
selects focus nodes from the shapes graph, which is useful when the shapes file
contains instances you intend to validate too. It can also select ontology
resources in the shapes graph as validation targets.
infer() takes no graph_mode. Graph modes describe what validation can
see; inference derives additions to the data graph. With separate inputs, rules
can read the data and shapes graphs while selecting their focus nodes from data.
With combined input, rules read the evolving data graph.
SPARQL $shapesGraph always names the original shapes source. It does not
grow when inference adds data triples, even when one input file supplies both
shapes and data. To change the named shapes graph, compile a new shapes source.
Note that expanding the evaluation graph can flip a result in either direction.
It usually makes a constraint easier to satisfy — more triples to traverse —
but it also gives sh:closed more predicates to object to, and gives
sh:maxCount more values to count. Widening the graph is not monotonically
more permissive.