Skip to content

v0.0.2

Choose a tag to compare

@jdsika jdsika released this 05 Aug 05:51
· 61 commits to main since this release
v0.0.2
31bfb8d

Fixed

RDF collections could be lost or duplicated by canonicalization (#1, #2)

deterministic_turtle relied on rdflib's Turtle serializer to render rdf:List
structures using the compact ( ... ) syntax. That syntax can only express a list whose
tail is referenced once. When canonicalization produced a graph where several lists shared
a tail — common in OWL ontologies, where owl:unionOf and owl:oneOf lists overlap — the
compact form silently dropped triples or re-stated a shared tail under a fresh blank node.
The output parsed cleanly and looked plausible, so the loss was not detectable by eye.

The canonical form is now verified to round-trip before it is returned: the rendered
Turtle is re-parsed and compared to the input graph, and if it does not match, a serializer
that emits list structures explicitly is used instead. If neither renders faithfully the
function raises rather than returning a lossy result.

Added

A property-based test harness (tests/test_canonicalization_properties.py) asserting four
invariants over seeded random graphs and six hand-built collection-sharing arrangements:

Property
P1 Lossless — the output parses back to a graph isomorphic to the input
P2 Idempotent — canonicalizing the output reproduces it byte-for-byte
P3 Label-independent — renaming blank nodes does not change the output
P4 Order-independent — shuffling input triples does not change the output

Plus checks for dangling list references, list cell counts, and byte-level drift across ten
repeated passes.

Upgrading

Anyone canonicalizing RDF that contains rdf:List structures — owl:unionOf,
owl:intersectionOf, owl:oneOf, sh:in, sh:or — should upgrade. Output for graphs
without shared list tails is unchanged, so this is a safe drop-in.