Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Python API guide

Use the public purrdf package. Its Store, terms and query results call the same native engine as the Rust API. Import validation and entailment through purrdf.shapes, purrdf.shex and purrdf.entail.

The complete signature reference records the canonical stubs for both the main package and purrdf.geo, including properties, overloads, namespace aliases and supporting types. Read the sections below for ownership, configuration, result interpretation and failure behavior. Underscore types in the signatures describe implementation or return types; their presence does not make them public constructors. For example, use purrdf.shapes.PreparedShapes in annotations, rather than importing _PreparedShapes.

TypedDict declarations describe dictionary result schemas; do not assume that each schema name is an exported native constructor. Obtain those records from the documented producer and read their named fields.

Load and query

Store.load(input, format, *, path, base) accepts text or bytes, or a file selected by path. Supply exactly one input source and a format. Its return value is the parsed document’s resolved prefix map, sorted by prefix; it is empty for formats without prefix declarations. Relative references require the document’s own base declaration or an explicit absolute base.

import purrdf

store = purrdf.Store()
prefixes = store.load(
    '@prefix ex: <https://example.org/> . ex:alice ex:name "Alice" .',
    purrdf.RdfFormat.TURTLE,
)
assert prefixes == [("ex", "https://example.org/")]
rows = list(store.query(
    "SELECT ?name WHERE { <https://example.org/alice> "
    "<https://example.org/name> ?name }"
))
assert rows[0]["name"].value == "Alice"

SELECT returns QuerySolutions. A row supports variable names, Variable objects and integer positions, including negative indexing. An unbound projected cell is None; it is not omitted. Missing variable names raise KeyError, and out-of-range positions raise IndexError. ASK returns QueryBoolean. Default-graph CONSTRUCT/DESCRIBE results iterate over Triple; a result carrying named graphs iterates over Quad.

RDF terms and exact values

NamedNode.value is its IRI. Literal.value preserves its lexical form; datatype, language and direction are separate properties. Language-tag identity folds case. Construct typed literals with datatype=NamedNode(iri), language literals with language=, and directional language literals with both language= and direction=.

name = purrdf.NamedNode("https://example.org/alice")
text = purrdf.Literal("你好", language="zh-Hans")
assert name.value == "https://example.org/alice"
assert text.value == "你好"
assert text.language == "zh-Hans"

The default preserves every digit when a quotient terminates. Otherwise it rounds to 18 fractional digits, half to even. division="exact" leaves a non-terminating quotient unbound as an expression error; division="N" truncates to N fractional digits, and division="N:ROUNDING" selects explicit rounding. exact-or-N[:ROUNDING] keeps exact terminating quotients and rounds only repeating ones. An invalid policy raises ValueError before execution.

result = store.query("SELECT ((1 / 1048576) AS ?value) {}")
assert list(result)[0]["value"].value == "0.00000095367431640625"
result = store.query("SELECT ((2 / 3) AS ?value) {}", division="2:half-even")
assert list(result)[0]["value"].value == "0.67"
xsd = "http://www.w3.org/2001/XMLSchema#"
assert purrdf.xsd_canonical_lexical("+00042", xsd + "integer") == "42"
assert purrdf.xsd_value_compare("1.00", xsd + "decimal", "1", xsd + "integer") == 0

result = store.query(
    'PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> '
    'SELECT (xsd:dateTimeStamp("2024-02-29T12:30:00.123456789012345678901Z"'
    '^^xsd:dateTime) AS ?value) {}'
)
calendar = list(result)[0]["value"]
assert calendar.value == "2024-02-29T12:30:00.123456789012345678901Z"
assert calendar.datatype.value == xsd + "dateTimeStamp"

Use lexical forms for exact decimal and temporal values rather than converting through Python float or a representation with fewer fractional digits. The XSD helpers return None for unsupported datatypes or malformed lexical forms; comparison also returns None when the value space does not order the pair, such as NaN.

Prepared queries

Declare parameter names with Store.prepare(..., parameters=[...]). Every run must bind every declared name to an RDF term. An unknown name or a missing binding raises an error. Configuration is fixed at preparation; source rows are read from the store for each run. Parameter names are supplied without ?.

prepared = store.prepare(
    "SELECT ?name WHERE { ?person <https://example.org/name> ?name }",
    parameters=["person"],
)
assert prepared.parameters == ["person"]
rows = list(prepared.run(person=purrdf.NamedNode("https://example.org/alice")))
assert rows[0]["name"].value == "Alice"

Governors and incomplete answers

query_governed adds fuel, deadline_ms, max_answers, max_intermediate_cells, max_scratch_bytes, max_remote_requests, no_ceiling and cancel to the query options. Ceilings are inclusive. Omitted ceilings retain metering. no_ceiling=True declines accounting and resource caps, retains deadline/cancellation, and conflicts with explicitly supplied caps.

stopped = store.query_governed("SELECT ?s ?p ?o WHERE { ?s ?p ?o }", fuel=0)
assert not stopped.is_complete
assert stopped.result is None
assert stopped.tripped is not None
complete = store.query_governed(
    "SELECT ?name WHERE { <https://example.org/alice> <https://example.org/name> ?name }")
assert complete.is_complete
assert list(complete.result)[0]["name"].value == "Alice"

A complete result is available only when is_complete is true. On interruption inspect tripped, partial and evidence; never treat partial answers as the whole result. update_governed returns its own outcome, and an interrupted update applies nothing.

Named graphs and engine configuration

The default Store remembers declared empty named graphs. remember_empty_graphs=False selects the explicit row-driven alternative. These modes change graph existence, not the interpretation of populated RDF statements.

remembered = purrdf.Store()
remembered.update("CREATE GRAPH <https://example.org/empty>")
assert remembered.named_graphs() == [purrdf.NamedNode("https://example.org/empty")]
implicit = purrdf.Store(remember_empty_graphs=False)
implicit.update("CREATE GRAPH <https://example.org/empty>")
assert implicit.named_graphs() == []

Query, update and prepared entry points accept explicit extension namespaces, property-function namespaces, standpoint predicates, relation tables, aggregate namespace, dated XPath regex profile, geo configuration and division policy. Namespace-dependent extensions need caller-supplied configuration. Relation declarations cross the boundary as data, not Python callbacks.

Relation tables, graph tables and traversals

relations maps an exact predicate IRI to (input_arity, output_arity, rows). Rows contain native RDF terms and must have the flattened width the declaration names. relations_from_graph supplies a list head and the same arities, reading the table from the query’s dataset. path_relations supplies the permitted predicate/direction steps, minimum and maximum hops, path and expansion guards, and either walk or shortest. These are data declarations, not Python callbacks; evaluation can therefore release the GIL.

A traversal emits one row per hop, binding the endpoint, path identity, length, step, node and traversed statement. The statement is an RDF 1.2 triple term that can join back to ordinary graph patterns. Every traversal limit is explicit; zero minimum hops, invalid or duplicated steps and unusable guards raise ValueError. A valid alternative with no matching edge contributes no row.

The optional attestation is (generation, incompleteness), each a string or None. It records the index version and the host’s own reason for incomplete data, independently. None means no declaration, not certification of a whole index. A governed query returns these declarations in relation_witness; an ungoverned call cannot report an attested shortfall and refuses it. Duplicate registration across the three relation maps, ragged tables and torn RDF lists are errors. An exact registered IRI needs no namespace setting; declaring property_fn_namespaces makes an unregistered IRI under those prefixes a hard error instead of an ordinary graph pattern.

aggregate_namespace registers the ten built-in statistical aggregates: MEDIAN, PERCENTILE, STDDEV, STDDEV_POP, VARIANCE, VAR_POP, MODE, FIRST, LAST and TOPK. It does not register arbitrary Python accumulator callbacks. xpath_regex selects one of XPATH_REGEX_PROFILES; omission retains the compatibility law. Pattern/flag rejection is an expression error, while a native xpath-* resource refusal raises ValueError. standpoint_predicates supplies the caller’s two predicate IRIs; the library invents none.

Reading execution evidence

CancellationToken.cancel() is monotone; inspect cancelled and create a new token for another independent cancellation lifetime. A governor trip returns an outcome, while KeyboardInterrupt remains an exception. TrippedGovernor exposes kind, label, dimension, limit, consumed, estimate and cause; an admission estimate is not measured consumption.

GovernorEvidence.consumed and limits are per-dimension mappings, also readable through consumed_in() and limit_for(). expression_errors counts absorbed numeric expression errors by qualified XPath code, with zero entries retained. For example, a projected division error can produce an unbound cell while the query itself completes; COALESCE may recover it.

PartialAnswers.certainty distinguishes certain (every returned row is an answer), at-most (every answer is among the returned candidates) and unknown (no rows may be used). is_positional_prefix is a separate property; a sound submultiset need not be the first rows of the complete result. barrier names the operator that blocked certification. Read result only through its certificate. UpdateOutcome.is_applied=False means no mutation was published.

The always-present QueryOutcome.relation_witness maps invoked relation IRIs to their invocation count, distinct generations and declared incompleteness reasons. An empty mapping does not certify completeness. Invocation counts describe the execution schedule and must not be compared as stable index identities across thread counts or budgets. See the governor profile for accounting, precedence and interval guarantees.

Containers, mutation and snapshots

TypeUse and ownership
StoreMutable quad store with query, update, load, prepare and checkpoint operations. Its add and remove return None.
MutableDatasetThe same shared query/update interface, with boolean add/remove, contains, quads_for_pattern and explicit compact.
DatasetA mutable quad collection with iteration, length and in-place blank-node canonicalization. It is not the frozen RdfDataset.
RdfDatasetAn immutable parsed RDF 1.2 dataset with counts and canonical/text/GTS/columnar export.
QuadIterThe quad iterator returned by the mutable containers; consume it through the normal iterator protocol.

load and bulk_load return the document’s final resolved prefix map, not the number of statements. add_graph implements the constructor’s graph-existence mode; silent=True suppresses an existing remembered slot, not an invalid graph name. named_graphs excludes the default graph and includes declared empty slots when the store remembers them.

After bulk loading, Store.checkpoint() folds the current delta into the frozen base without changing contents. change_size() then reports additions and removals since that checkpoint. Checkpoint once after loading before an incremental validation sequence; otherwise the initial load is itself the pending change. Compaction is explicit work, not something to perform after every individual mutation.

A prepared query retains its originating store and reads that store’s current contents on each run. Graph-derived relation tables and traversals are rebuilt for that run’s dataset and the plan is admitted under them again. Fixed configuration does not mean a frozen copy of the data. A PreparedQuery is not thread-safe and cannot be re-entered while a run is in flight.

Queries materialize owned results before returning. QuerySolutions, QueryTriples and QueryQuads are iterators; copy their rows when a later pass needs them again. QuerySolutions.variables supplies the ordered schema. QueryQuads.graph_names returns sorted graph tokens in N-Triples term syntax. Serializing a graph-carrying query result to a single-graph syntax raises ValueError; it does not silently erase graph names. Quad-template CONSTRUCT is a first-party extension, not defined by SPARQL 1.2.

Terms, formats and serialization

NamedNode and BlankNode expose value; Variable.value is its variable name. Triple exposes subject, predicate and object; a Quad adds graph_name. RDF 1.2 permits triple terms in object position, not subject position. A default graph is represented by DefaultGraph; the quad constructor also accepts None for that graph. Terms support their documented equality and hashing; language-tag equality folds case, while Literal.value preserves its lexical form. A plain literal’s datatype getter is xsd:string; language and directed language literals expose rdf:langString and rdf:dirLangString respectively.

The RdfFormat members are TURTLE, N_TRIPLES, N_QUADS, TRIG, TRIX, HEXTUPLES, JSON_LD, YAML_LD and RDF_XML. A document’s own base declaration wins over a supplied base. A relative reference with neither raises ValueError carrying iri-relative-no-base; no retrieval IRI is fabricated. On output, formats without a base declaration emit absolute IRIs.

parse returns a list of quads. serialize and the store dump overloads either write bytes to a binary output or return bytes when no output is passed. from_graph selects the dump’s graph; base selects the output base and composes with that selection. CompiledJsonLdContext accepts strict options JSON, can be built with from_prefixes, and exposes canonical_context_json for retention. Supply either the supported options or compiled context as declared; do not assume ordinary Python dictionaries are interchangeable with JSON text.

dump_with_loss returns SerializeLoss: bytes plus statement_rows_dropped, directional_literals_dropped, named_graph_rows_dropped and empty_named_graphs_dropped. The first three partition lost rows or decorations by cause; the fourth counts declaration-only graphs, which have no row to count. Read all counters. They are realized loss for this output, unlike the static format-pair matrix returned by loss_matrix_json() or rdf_gts_loss_matrix_json().

to_json_ld, serialize_jsonld, from_json_ld, to_rdf_xml and from_rdf_xml bridge RDF bytes and their text forms. statement_vocab is an explicit caller-supplied metadata vocabulary; its class, subject, predicate, object and objectLiteral values are absolute IRIs. There is no invented namespace fallback. project_statements_rdf12 and normalize_rdf12_to_owl provide the statement-codec conversions; canonicalize_turtle returns canonical bytes with optional extra prefix mappings.

Dataset.canonicalize relabels blank nodes in place under CanonicalizationAlgorithm.RDFC_1_0 or UNSTABLE. RdfDataset.to_nquads() uses the shipped flat canonicalization profile: it agrees with RDFC-1.0 on the RDF 1.1 subset, while the RDF 1.2 profile has its own reserved-vocabulary, statement-layer and refusal contract. Do not label an RDF 1.2 digest as RDFC-1.0. Canonicalization refusal raises ValueError. Read the canonicalization chapter before using bytes as identities.

The XSD helpers work on lexical strings and datatype IRIs: xsd_value_compare returns an order or None when unsupported, malformed or unordered; xsd_canonical_lexical, xsd_decode_binary and xsd_normalize_whitespace return their value or None. Avoid converting an exact decimal or fractional second through Python float first.

SPARQL Results and provenance

serialize_sparql_solutions takes the ordered variable list and rows with one cell per variable, retaining None for unbound cells. serialize_sparql_boolean handles ASK. Both emit deterministic bytes in JSON, XML, CSV or TSV, accepting the aliases and media types in SparqlResultsFormat. parse_sparql_results reads JSON or XML and returns a discriminated tuple: ("SELECT", variables, rows) or ("ASK", boolean). CSV and TSV have no native reader here.

Provenance is additive and requires provenance_namespace=(prefix, iri); query_hash supplies the caller’s identity. provenance_from_json and provenance_from_xml read that namespace back as query_hash and engine. Absent provenance yields None fields, not an invented identity.

SHACL validation, preparation and rules

Use purrdf.shapes; purrdf.shacl is the compatibility alias. The one-call shapes.validate takes Turtle shapes and N-Triples data and returns a report dictionary. shapes.Shapes parses reusable shapes. validate_nt returns a ValidationReport; validate_store reads a native snapshot from Store or MutableDataset without a text round trip. A later mutation changes the next report, not a report already returned.

ValidationReport exposes conforms, shapes_graph_well_formed, diagnostics and results, plus to_ntriples() and to_sarif(). Result messages retain their text, language, direction and datatype. Empty sh:in or sh:xone lists produce mandatory diagnostics beside results; they do not change conformance or the report graph. The one-call report records conformance_disallows: omission uses SHACL’s Violation/Warning/Info default set, which excludes Debug and Trace. An empty disallow set or non-IRI severity raises.

shapes_ttl = '''
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix ex: <https://example.org/> .
ex:PersonShape a sh:NodeShape;
    sh:targetNode ex:alice;
    sh:property [ sh:path ex:name; sh:minCount 1 ] .
'''
shapes = purrdf.shapes.Shapes(shapes_ttl)
assert shapes.validate_store(store).conforms
prepared_shapes = shapes.prepare()
product = purrdf.shapes.ShapesProduct.open(prepared_shapes.to_product())
restored = product.admit_expecting(product.identity_digest())
assert restored.validate_nt(
    '<https://example.org/alice> <https://example.org/name> "Alice" .'
).conforms

Imports and execution environment

Every shapes entry point resolves imports from the supplied ordered imports table of (IRI, Turtle document) pairs. It fetches nothing. Unresolved or invalid imports, unused entries and incompatible versions raise shapes.ShapesImportError; branch on kind and inspect iris. Data-graph sh:shapesGraph links must resolve from material in hand. Previously prepared shapes cannot silently acquire an additional linked graph when data arrives; an unheld link is refused.

shapes_base (or Shapes(..., base=...)) resolves relative shapes IRIs. shapes_graph names the graph read by SHACL-SPARQL through $shapesGraph; omission invents no graph. This is the SHACL 1.0 pre-binding removed from SHACL 1.2. subclass_of_in_shapes_graph=True also reads shapes-graph subclass triples for class membership; rdf:type still comes from the data graph. xpath_regex applies the selected dated law to facets and embedded SPARQL. Resource refusal raises rather than returning an empty or conforming report.

Shapes.extension_usage inspects declarations before validation: sites distinguishes calls from data predicates under the supplied namespaces and registrations; unreadable and complete expose admission failure. This distinguishes an unrecognized relation from a valid relation over no rows. shapes.lint_shapes performs a cold audit and reports findings, load errors, mandatory diagnostics, superseded validators, unexecuted query declarations, import anchoring and deterministic report text. Neither is data validation.

Prepared products and incremental reports

Shapes.prepare() returns PreparedShapes; to_product() writes deterministic bytes. shapes.pack_product() combines parsing, preparation and writing. ShapesProduct.open() verifies the envelope and reads its self-description, without admitting executable preparation. Inspect format_version, stage_id, stage_known, identity_digest, identity_components and explain; to_bytes returns the envelope. admit restores a known stage; rebuild reconstructs it when necessary. Their _expecting forms bind the expected identity and reject mismatches with dimension shapes-graph. certify checks the product’s certification obligations. Branch on ShapesProductError.dimension, not exception text. PreparedShapes.provenance() distinguishes parsed, restored-admitted and restored-rebuilt preparations.

PreparedShapes.validate_store_changes(store) returns ChangeValidation. Read bounded before report.conforms: when true, the report covers affected focus nodes only, with no claim about unreachable old violations. When false, it covers a full validation of the mutated graph. focus_nodes is a count on the bounded arm and None otherwise; reason identifies the construct requiring a full run. The pending change comes from the caller’s last checkpoint; validation does not redefine that checkpoint.

Rules and node expressions

shapes.entail applies a shapes graph’s default rule set and returns base plus inferred triples. It differs from purrdf.entail.materialize, which uses a regime’s rule table without a shapes graph. shapes.apply_rules runs an SRL document and returns only the inference graph, with proof text when explain=True. check_rules admits a rule set at the selected checking level: level accepts syntax, well-formed or stratified; omission selects stratified. It reports declarations, imports, versions and strata. Stored-fact, join-step, generated-term and generating-round controls are explicit keyword arguments; exhaustion raises a limit-specific ValueError.

eval_node_expr selects exactly one expression: a named IRI/blank label, a unique walk from expr_at through expr_via, or a one-root inline expr_turtle document. focus and scope supply terms; the reserved focusNode cannot be rebound. Ambiguous selectors, missing/multivalued walk steps and evaluation failures raise. Outputs are ordered N-Triples term strings beside mandatory diagnostics. See SHACL and prepared products for the full contracts.

ShEx

purrdf.shex.parse converts ShExC or ShExJ into canonical ShExJ text. shex.validate takes a schema, data and a fixed shape map. Each (node, shape) pair is echoed in ShexResultEntry, with conformant and optional reason. Nodes may be bare/bracketed IRIs, _: labels or Turtle literal tokens; START selects the start shape. Schema formats are shexc and shexj; data formats are turtle, ntriples and nquads. base resolves relative IRIs in both inputs. A pattern rejected by the selected XPath law makes its entry nonconformant; a native resource refusal raises for the whole map. See ShEx.

Entailment, decisions and proofs

purrdf.entail.Regime names SIMPLE, RDF, RDFS, OWL_RL, OWL_DIRECT, RIF and D; functions also accept the lowercase CLI spellings. Unknown spellings raise. materialize returns (RdfDataset, report) and materialize_nt returns (canonical_text, report); the report is mandatory. program is the RIF document and must be empty for other regimes. Required imports and premise_iris describe the premise’s import closure; [] means none. Non-absolute premise IRIs, unresolved imports and unreachable table entries raise rather than being fetched or ignored. rules, implemented_rules and extensions expose the specification table, implemented subset and named non-normative additions.

Chase reports describe rule coverage; OWL Direct-Semantics services return decision certificates. An incomplete rule table differs from a decision that exhausted its work cap. Unpack and retain the certificate with the answer.

ServiceQuestion
consistencyWhether the knowledge base has a model; false and unknown are distinct.
classifyNamed-class equivalence, transitive subsumption, direct edges and unsatisfiable classes.
realizeEntailed individual types and most-specific direct types.
instancesNamed instances of one class, supplied as an N-Triples term.
entailsWhether one OWL RDF-mapping axiom is entailed; different from graph_entails.
profileSyntactic OWL profile certification and blockers, without decision search.
extract_moduleA locality module for a seed signature under bot, top or star.
justifyA sufficient minimal axiom subset, with sufficiency/minimality re-decided.
explain_conclusionA checkable chase derivation, not a tableau justification.

step_cap and work_cap only narrow the decision engine’s derived caps; zero selects its own cap, not zero work. Certificates report rounds, work, decision count, peak nodes, case splits and depth. Budget exhaustion is unknown, not a negative theorem. Except for consistency, decision services refuse an ontology without a model instead of presenting its vacuous entailments as ordinary answers. An unmentioned class can have an empty instance answer without an error.

entail.Reasoner retains parsed data, lazily builds a knowledge base when needed and reuses both across corresponding service methods. Caps are fixed at construction. proofs=True opts into traces; records_proofs reports that choice. prove returns answer, certificate and proof; proof_services lists supported service names. A non-recording session refuses proof requests. check_proof checks against the caller’s ontology, question, answer and certificate; an unrecorded proof is never verified. Empty answer/certificate requests a weaker check whose report says so. A justification and an execution proof answer different questions.

certain_answers asks for substitutions true in every model, not just matches in one closure. Predicate variables and variables inside triple terms bind; blank nodes are non-distinguished. Read limit lines for non-exhaustive boundaries. graph_entails asks about a conclusion graph, with distinct entailed, not-entailed and undecided verdicts. verify_entailment rechecks a present warrant against the premise as written. These calls require imports and premise IRIs, and refuse regimes whose inputs their signatures cannot express. A variable in a literal’s datatype position is refused.

Store.query_entailment_governed has two phases. Stopped closure construction produces EntailmentQueryOutcome.phase == "closure-stopped", without a query outcome or closure report. Otherwise phase is answered, with an ordinary governed query outcome and report. Graph-backed relations read the closure. An OWL Direct closure exposing generated existential witnesses through a traversal is refused with reasoning-closure-relation-witness. See entailment for the full service and certificate grammars.

Geographic sessions

purrdf.geo.GeoProfile owns an immutable strict geographic profile; to_json returns its canonical version-one record with explicit limits. GeoSession accepts that profile, another session, profile JSON or None; omission selects the standard CRS84 profile. profile() returns the selected profile. Query, update and prepared methods accept these same choices through geo.

GeoSession.call evaluates a strict scalar/batch JSON request; point_index constructs immutable native point buckets and returns GeoPointIndex, whose call refines searches through the same comparison law. Semantic outcomes remain canonical JSON; operational refusal raises ValueError. These paths use the shared Rust codec, with no Python geometry implementation. See GeoSPARQL.

GTS containers and immutable datasets

The GTS functions are available at the package root and grouped under purrdf.gts; the namespace aliases refer to the same operations and types. gts_from_quads and gts_from_rdf12_bytes ingest RDF bytes into a snapshot under the requested profile, transform and base. snapshot_content_id_native reads the corresponding snapshot identity.

compile_gts_native accepts the base document, optional RDF 1.2 layer, named graphs and their scopes, document/report blobs and slice artifacts. Signing inputs and the rsyncable threshold are explicit compiler arguments. Blob rows carry bytes, media type and representation; slice artifact rows additionally name the slice IRI, name, role and logical path. Keep these tuple positions as declared instead of guessing from the final element’s type.

compile_gts_with_report returns GtsCompileResult, pairing snapshot_bytes with the ingestion receipt from that same parse and intern operation. gts_ingest_report is the receipt-only accessor. GtsIngestReport records rows_consumed, terms_interned, declarations_omitted and scratch_bytes. Declaration-only named graphs have no row in the frozen distribution payload; their IRIs are reported as omitted instead of being silently interned and changing the snapshot identity. Do not treat an empty quad table as evidence that no declaration was lost. feedback_bundle_native attaches report blobs to the supplied RDF input.

RdfDataset owns a frozen parsed dataset. Its quad_count, term_count and length are inspection methods. to_nquads provides the canonical flat text described above; serialize_jsonld supports the declared JSON-LD/YAML-LD options and compiled context. to_gts, columnar_rows and to_parquet_files export the same native carrier. A dictionary’s numeric term identifiers are local to that owner; resolve a term when crossing independent datasets.

frozen = purrdf.RdfDataset(
    '<https://example.org/alice> <https://example.org/name> "Alice" .',
    purrdf.RdfFormat.N_TRIPLES,
)
container = frozen.to_gts()
view = purrdf.GtsFoldViewNative.from_bytes(container)
assert view.quad_count() == 1
alice_id = view.tid_of_iri("https://example.org/alice")
assert alice_id is not None
assert view.public_text(alice_id, "https://example.org/name") == "Alice"

Fold views and language selection

GtsFoldViewNative.from_bytes reads a container; from_parts admits caller-supplied term, quad, reifier and annotation tables. Both refuse a term that reaches itself with gts-self-reaching-term, rather than exposing a renderer that recurses forever. curie_prefixes supplies the host’s CURIE mappings; only the documented W3C namespaces are built in.

The four count methods report table sizes. term_tuple, is_iri, is_bnode, is_literal, iri, lex, lang, datatype, nq_token and python_value inspect a local term; tid_of_iri performs reverse lookup and curie compacts an IRI. quads, subjects_by_type, objects, value, predicate_objects, has and rdf_list query the selected scope. reifiers and annotations return their statement-layer tables. Do not compare their integer IDs with those of another independently constructed view.

Omitted scope or None selects the default graph. purrdf.GTS_ALL_SCOPE selects all graphs; a named-graph IRI selects that graph, and an unknown IRI has no matching rows. The all-graph sentinel is not a named-graph IRI.

tag_map and available_languages expose the view’s language choices. public_text and public_literal select the public value; the _with_fallback forms also take requested languages and report whether fallback was used. public_texts returns the requested alternatives with that metadata. Retain the language and fallback fields when the presentation depends on whether the requested translation actually existed.

Relational and Parquet exports

relational_rows and gts_relational_rows_from_bytes return GtsRelationalRows: terms, quads, reifiers, annotations and blobs, with the directions array positionally parallel to terms. Direction is not an extra field appended to the positional term tuple. ColumnarRows instead carries the native v1 schema and projected five-table rows, whose dense value-order identifiers need not equal the original GTS IDs.

gts_columnar_rows_from_bytes returns that projection; gts_columnar_parquet_from_bytes and RdfDataset.to_parquet_files return filename-to-bytes mappings. gts_to_sqlite, gts_to_duckdb and gts_to_parquet write the five-table projection to the requested destination. SQLite uses the standard library, DuckDB needs its optional extra, and Parquet writing uses the native codec; PyArrow is an interoperability reader, not the writer. See GTS and the Columnar profile.

Graph and tabular projection packages

project takes RDF text/bytes, its RdfFormat, an explicit ProjectionProfile and strict configuration JSON. It returns ProjectionPackage with the selected profile, deterministic USTAR archive and structured losses. Each ProjectionLoss has code, source, target, note and optional location; inspect the records rather than assuming every profile round-trips. The optional assets input belongs to the package configuration.

The profiles are lpg-csv, neo4j-csv, open-cypher, graphml, csvw-exact, csvw-terms, okf-terms, obo-graphs, skos, croissant-1.1, ro-crate-1.3, datacite-4.6, dcat-3, dcat-rdf, void and frictionless-data-package-1. lift supports the LiftProfile subset and returns ProjectionLift.dataset plus losses; a one-way profile is not made bidirectional by passing its name to lift.

For streamed LPG artifacts, project_artifacts invokes the artifact callback with (event, path, chunk). The ArtifactEvent values delimit the lifecycle: begin-package, begin-artifact, chunk, finish-artifact, commit-package and abort-package. Publish retained artifacts only after commit, and handle abort. The optional progress callback receives immutable ProjectionProgress snapshots with phase and node/edge/artifact/byte counts. The returned ProjectionStream reports its profile and losses. These are explicit artifact callbacks, unlike the data-only SPARQL relation declarations. See projections for each profile’s strict configuration and round-trip contract.

SSSOM mapping documents

validate_sssom returns SssomDiagnostic dictionaries containing severity, code, message, check and optional instance. A clean document yields []; a parse failure is a FATAL diagnostic with check parse, not a successful empty validation. sssom_to_rdf produces RDF from TSV; sssom_roundtrip_tsv writes the parsed mapping document, and sssom_default_validation_types exposes the native default validation types. These are the native SSSOM codec, not an import of the external sssom package.

Slice catalogs and ownership

purrdf.slice.SliceCatalog.discover(root, namespace) reads manifests using the caller’s namespace. records contains SliceRecord values with manifest, manifest path, slice directory and artifact inventory; core_slice_iris reports the core members. ManifestView exposes identifier, slice IRI, label, title, tier, creators and consumers. ArtifactRecord exposes role, logical path, media type, raw digest, semantic digest and content.

fix_deps() returns ManifestPatch values with original and proposed text; the caller decides when to write those patches. OwnershipAnalyzer performs the analysis eagerly at construction; analyze() returns the retained OwnershipReport, and analysis_graph_turtle() serializes its graph. The report exposes ownership errors, a defect flag and DependencyEdge values with source, destination, semantic/physical status and reconciliation. This is manifest/artifact ownership, not an inference from directory names. See slices.

Ranked retrieval and exact fusion

purrdf.retrieval exposes plan, compile and search, plus cold document checks and fusion-law design queries. plan executes nothing. It records a canonical request, per-stratum depths, consulted statistics, producer decisions and unserved terms. compile admits that plan and emits its per-stratum SPARQL. search runs the full plan/compile/execute/fuse ladder. There are no separate Python stream-handle entry points for execute and fuse.

Text producers map a producer IRI to (stratum, predicate, graph, analyzer). The analyzer requires its lexicon choice and declares the documented lexicon artifacts or entries, scalar guards, accent/stemming/input/projection modes and substring budgets as needed. A graph selector is any, default or a named graph IRI. Optional trailing positions are domains, index attestation and search fidelity, in that order; an ambiguous or unsupported tuple width raises. The index attestation is (generation, incompleteness), while fidelity is (completeness, order). These pairs describe different facts.

knn_producers and hnsw_producers register exact scan and approximate graph search over caller-supplied (IRI, vector) rows, seeded by an entity request. Vectors have the same finite width and unique valid IRIs; cosine refuses a zero vector. Both candidate and neighbor guards are explicit and positive. The producer metrics are cosine, negative_dot and squared_euclidean. HNSW additionally declares (m, m0, ef_construction, ef_search) and validates m >= 2, m0 >= m, ef_construction >= m0 and ef_search >= 1. A producer IRI or stratum cannot be registered twice across the three maps. The caller also supplies the statistics source and revision; cardinalities and integer parts-per-million selectivities are optional. Unknown measurements are None, not zero.

The request is a sequence of typed tuples; producer applicability is reported by planning, so writing a request kind does not invent a producer for it.

KindTuple
Lexical("lexical", text, language_or_none, predicate_or_none)
Vector("vector", components, metric, hint_or_none), with request metric cosine, dot or euclidean
Spatial("spatial", geometry, predicate, max_distance_raw_or_none)
Temporal("temporal", predicate, lower_or_none, upper_or_none)
Numeric("numeric", predicate, lower_raw_or_none, upper_raw_or_none)
Entity("entity", term), including an N-Triples IRI term for a vector-space seed

Candidate domains are either unrestricted (None) or one distinct host domain tag. Empty or multiple distinct tags are refused; repeated copies of one tag are one domain. A domain declaration licenses exclusion, so a producer naming a candidate in disjoint domains is an error, not a declaration silently widened after scores have been certified. Repeated candidates from a unique stream are also refused.

retrieval = purrdf.retrieval
notes = '''
<https://example.org/a> <https://example.org/note> "the quick brown fox" .
<https://example.org/b> <https://example.org/note> "a quick red fox" .
'''
ranking = retrieval.search(
    notes,
    [("lexical", "quick fox", None, "https://example.org/note")],
    text_producers={
        "https://example.org/pf/search": (
            "https://example.org/stratum/lexical",
            "https://example.org/note", "any", {"lexicon": "empty"},
        )
    },
    weights={"https://example.org/stratum/lexical": retrieval.SCALE},
    statistics={"source": "host-statistics", "revision": "r1"},
    k=60, decay="reciprocal_rank", top_k=10,
)
assert len(ranking["rows"]) == 2
assert isinstance(ranking["rows"][0]["score"], str)

Law, depths and document checks

Weights are Python integers in raw fixed-point units: SCALE is one whole unit and SCALE_DIGITS its exponent. Scores and contributions return exact decimal strings, never floating-point approximations. k, decay and top_k are explicit. reciprocal_rank truncates the reciprocal before applying weight; weighted_reciprocal_rank rounds the weighted quotient once. They are different content-addressed laws. Mixing a raw weight of 1 with weights expressed as multiples of SCALE changes the weighting by that factor.

top_k is also a planning input. Over disjoint candidate domains it can narrow reads while preserving the answer. A compiled unit’s depth is the reportable row bound; its SPARQL LIMIT includes one extra probe row. Do not report that probe as a value or read the bound from the text. declared_rows=None means no row count was declared, not zero. On compile, weights, k and decay are supplied together or omitted together; without a law, no rank-resolution claim is invented.

canonical_bytes and plan_id identify a plan. certify_plan checks a received document’s recorded depth derivations and consulted-statistics projections; it does not certify that external statistics are current. explain_depth reads the recorded limiting cause and is not the certification operation. Branch on retrieval.PlanDocumentError.refusal for rejected documents.

The design-time queries are weight_for_depth, class_width, deepest_rank_within_width and crossing_rank_at. They ask respectively for the minimum separating weight, a rank’s equal-contribution class width, the deepest prefix within a width tolerance, and the head rank that clears a fusion threshold. None means no finite result within the expressible rank domain, not an enormous measured number. Inputs must satisfy the law’s positive weight/rank/smoothing/tolerance constraints. The width-tolerance query walks the nonmonotone curve and holds the GIL; use it for design, not in a hot loop.

Reading ranking evidence

Keep the three identities: plan_id names the question and planning inputs, profile_id names the fusion law, and evidence_id names the answering index generations and exclusion evidence. planned_resolution describes the depths before execution; observed_resolution reports ranks pulled, collisions, exclusion lookups and rows materialized. Early certification can make the observed work smaller; that is not a correction to the plan.

Read each stratum’s terminal statuses with its fidelities. exhausted means its search produced no more rows, not that a lossy search found every possible match. depth_reached, row_bound_reached and ceiling_reached name different stopping conditions. The shared vocabulary also includes supplied_query_ended, execution_failed and terms_rejected for the broader Rust producer/bundle seam; do not collapse these to an empty-result flag.

attestations records index generation and admitted incompleteness; fidelities independently records complete/lossy search and faithful/perturbed order, with the producer’s own reasons. Silence about index completeness does not certify a whole corpus. HNSW remains a lossy producer even if its graph was built exactly from the supplied vectors.

exactness names deficit, inflation and unbounded strata. Missing candidates can move other candidates up in rank, so degraded evidence can make scores too high as well as too low. Each row’s interval quantifies a bounded error or names perturbed-order strata; certain_prefix counts leading rows whose places survive the declared degradation. unemitted_ceiling bounds what a candidate outside the answer could be worth, or is None when perturbed order prevents a finite bound. These claims concern membership, not proof that every unreturned item was absent. Keep domains and exclusion_bases with the evidence that licensed skipped streams. See the retrieval design.

Errors and boundary choices

Invalid configuration, resource refusal, expression error and certified partial results are different outcomes. Preserve structured diagnostic attributes when provided: message_id and presentation identify native failures; ShapesImportError.kind, ShapesProductError.dimension and PlanDocumentError.refusal are their respective stable discriminants. Do not parse localized message text to decide whether a result is complete. An unbound SELECT cell is neither a missing column nor necessarily an exception.

Use the rdflib compatibility chapter when porting an rdflib consumer. query_rdflib accepts its named/default graph controls, while the native entry points retain the contracts described here. The separate purrdf-rdflib shadow distribution claims the rdflib import name and must not share an environment with the real rdflib. Direct public calls go through purrdf, never the internal purrdf_native extension module.