Skip to content

Implement RDFC-1.0#245

Open
mielvds wants to merge 20 commits intomasterfrom
220-RDFC10
Open

Implement RDFC-1.0#245
mielvds wants to merge 20 commits intomasterfrom
220-RDFC10

Conversation

@mielvds
Copy link
Copy Markdown
Collaborator

@mielvds mielvds commented Feb 25, 2026

This PR is a rather big one. It fully implements URDNA2015, URDNA2012 and RDFC10 and has complete test-suite coverage on these algorithms (with the exception of the one on poisonous datasets, but more about that later). It 'fixes' #220

Some general remarks before I dive into the details:

  • I went back and forth on how approach this: move the canon stuff to a new repo first, or fix the problems first. I sided with the latter as the test harness is already in place and the code is too tangled to not break everything.
  • This introduces rdflib into PyLD, but limited to the normalization/canonicalization canon.py. This made the code much much cleaner, but ironically didn't fix what I introduced it for: nquad serialization. The relevant methods are copied over from rdflib until I can turn them into PRs for rdflib.
  • I added some functions to transform the legacy RDF.JS dataset structure in an rdflib.Dataset and back. This should ensure backwards-compatibility on some methods such as jsonld.normalization() and URDNA2015.main().
  • While switching to rdflib introduces significant changes, I tried to change as little as possible otherwise. Optimizing the algorithm implementations can be done later.

Overview of the changes:

  • added rdflib dependency in all relevant config and code files.
  • the change to rdflib mostly changed
    • RDF term type checking (e.g., checking is something is a bnode)
    • looping over triples/quads
    • serialization
    • constructing RDF terms (custom deepcopy is no longer needed)
  • util.py
    • added the functions from_legacy_dataset() and to_legacy_dataset() to easily go from an rdflib.Dataset to an RDFJS-like dict wherever needed.
    • Also added unittests in tests/test_util.py for these functions. We might want to move more general-purpose methods to this file.
  • canon.py:
    • the main logic was moved to URDNA2015._canonicalize(self, dataset: Dataset) while handling input and output remained in URDNA2015.main(). The latter now does the nquads parsing instead of JsonLdProcessor.normalize(), so all parsing and serialization is handled by the same class.
    • the method URDNA2015._canonicalize(self, dataset: Dataset) accepts an rdflib.Dataset and returns a tuple with
      • the canonicalized result as a nquads str and
      • the blank node identifier map as dict.
    • The method URDNA2015 .main(self, dataset: str | dict | Dataset, options) now accepts a rdflib.Dataset object in addition to an nquads str or the original RDFJS-likedict. It returns
      • a str: the serialized nquads result, or
      • a dict: the result as RDFJS-like dataset or the blank node identifier map when the new parameter outputMap is True.
    • the hashing algorithm is now an class attribute URDNA2015.hash_algorithm so it easily configurable (required for RDFC1.0)
    • the permutations() function now uses itertools.permutations instead of a custom implementation.
    • replacements for rdflib's _nq_row and _quoteLiteral, which should eventually move to a fix for rdflib's nquads serializer.
  • tests/runtests.py
    • (re-)enabled all skipped URDNA2015, URDNA2012
    • Added the RDFC10 tests
    • added support for testing blank-node identifier maps. If the result of a test is a dict and the expected value is a string, the expected value is now parsed as JSON.
    • added support for testing with different hashing algorithms
    • !! I did not include test 74c on dataset poisoning because I'm not sure how to handle that yet. We need to discuss possible guardrails first before continuing on this.

It's possible that we move this code out before ever merging, but at least it can be easily tested. That said, when importing this functionality as an external library, we include rdflib anyway. It therefore makes sense to continue replacing the RDFJS-like data structures elsewhere in the code, essentially making it fully rdflib compliant and dependent.

Since this involves RDFLib: @nicholascar, anyone who can maybe have a look at this? Also @davidlehn @BigBlueHat @anatoly-scherbakov

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 25, 2026

@mielvds mielvds marked this pull request as ready for review April 23, 2026 08:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant