v0.1.0
First release with the determinism guarantee actually enforced end to end.
0.0.2 was extracted from an early prototype and had drifted behind the
hardened copy that upstream LinkML merged and reviewed. This release closes
that gap and adds a public primitive so tools that already canonicalize can
gain diff stability without replacing their own pipeline.
Fixed
- The pyoxigraph fallback was not deterministic. Graphs pyoxigraph rejects
(literal predicates, relative IRIs) fell back to a plaingraph.serialize(),
which labels blank nodes from run-local state, so output differed between
processes — on exactly the inputs that need reproducibility most. json-ldbypassed canonicalization entirely. Any format outside
pyoxigraph's reach returned raw rdflib output, leaking run-local blank-node
labels and rdflib's set-iteration node ordering.- Hash bases raised
ValueError. rdflib relativizes againstgraph.base
by naive string prefixing, which is not RFC-3986-correct:http://ex.org/d#a
under basehttp://ex.org/d#was emitted as<a>and re-resolved to a
different IRI, tripping the round-trip guard. Hash namespaces are the most
common OWL style. The base is no longer carried over. - A relative base crashed the serializer. Relative bases are legal in
rdflib;"Invalid base IRI"is now recovered from, like"Invalid prefix". deterministic_jsonrejected non-string dict keys, raisingTypeError
on input thatjson.dumpsserializes fine.- Unused prefix declarations are filtered to the namespaces actually used.
Added
wl_blank_node_labels() and wl_relabel_quads() expose the Weisfeiler-Lehman
relabelling step on its own. A tool that already runs RDFC-1.0 does not need
deterministic_turtle — that would replace its own prefix, base and fallback
handling. It can insert one step instead:
dataset.canonicalize(CanonicalizationAlgorithm.RDFC_1_0)
quads = wl_relabel_quads(list(dataset)) # <- the only added lineFormat-agnostic, and measures the same: on a 20-shape graph, adding one class
changes 62 lines to 4.
wl_relabel_quads also remaps blank nodes used as graph names; leaving them
unmapped splits one node in two and silently breaks the dataset.
Changed
The diff-stability guarantee is now stated accurately: WL bounds relabelling to
roughly iterations hops around a change, not to the directly involved blank
nodes, and a node referenced from many subjects re-labels whenever any of those
references changes.
Canonicalization normalizations are documented: language-tag lowercasing
(case-insensitive per RDF 1.1), xsd:string identity, prefix filtering, and the
dropped base.
Testing
188 to 210 tests, on Python 3.10-3.13. Determinism is asserted across separate
interpreter processes for all seven formats — run-local blank-node ids are
stable within one process, so same-process tests cannot catch this bug class.
Every regression test was verified to fail without its fix.