Changelog¶
All notable changes to this project are documented here. Releases follow Semantic Versioning.
[Unreleased]¶
[0.6.3] — 2026-08-26¶
Fixed¶
Iterating a store-backed view (
get_union/get_closure,OntoEnvStore) from more than one Python thread could deadlock the whole interpreter. The read paths held a Rust mutex — the shared term cache, and the backend handle itself — while re-entering Python to buildURIRef/Literalobjects. A thread that lost the interpreter mid-construction could not get it back because another thread was blocking on the mutex with the GIL held, so every thread in the process stopped, silently and permanently. Locks are now taken detached from the interpreter, and no lock is held across rdflib term construction.
[0.6.2] — 2026-08-12¶
Added¶
OntoEnv.temporary_snapshot()(Python) andOntoEnv::new_temporary()(Rust) create isolated in-memory snapshots that copy both catalog metadata and graph contents.
Changed¶
temporary=Truealways creates an empty in-memory environment. Incompatible lifecycle-option combinations now fail with a clear error instead of being silently ignored.
[0.6.0] — 2026-07-27¶
Added¶
ontoenv recoverrebuilds a persistent environment catalog afterCatalogRecoveryError, using normal environment discovery and removingcatalog.pendingonly after successful publication.
Deprecated¶
OntoEnv(..., create_or_use_cached=True)is now a compatibility-only alias for the create-or-reopen lifecycle. It emitsDeprecationWarning; useOntoEnv.connect(path)instead. Removal is planned for 0.7.
Changed (breaking)¶
The documented Rust MSRV is now 1.88, matching rdf5d’s edition 2024 and the resolved dependency floor.
OntoEnv.get_graph(uri)now returns a read-only store-backedrdflib.Graphview instead of a mutable in-memory copy. Mutating the returned graph raisesValueError. Use the newOntoEnv.copy_graph(uri)for the previous behavior (mutable in-memoryrdflib.Graphcopy).OntoEnv.get_closure(uri)now returns a read-only, zero-copyontoenv.ViewGraphinstead of a materialized mutablerdflib.Graph. The materializing behavior moved to the newOntoEnv.copy_closure(uri). AViewGraphdoes not subclassrdflib.Graph; it delegates triple-pattern lookups (triples,subjects/predicates/objects),len,in, and SPARQLquery()to the Rust backend, reading directly from the rdf5d mmap snapshot. It is read-only —add/addN/removeraiseValueError. The view presents a single flattened, de-duplicated graph with the same triple set ascopy_closure: resolvedowl:importsstripped, ontology declarations collapsed onto the root (addingroot a owl:Ontologyif absent), and SHACLsh:prefixes/sh:declareconsolidated onto the root. Keyword argsremove_owl_imports=True/rewrite_sh_prefixes=Trueopt out of the respective transform.OntoEnv.get_union(uris)returns a read-onlyontoenv.ViewGraph— a raw merge across the listed named graphs (no closure transform, no cross-graph de-duplication). Usecopy_unionfor a mutable merge.OntoEnv.copy_union(...)defaults torewrite_sh_prefixes=Falseandremove_owl_imports=False(a raw union, matchingget_union); passTrueto opt into the transforms.copy_closuredefaults both toTrue.OntoEnv.snapshot_as_dataset(backend=..., store=...)andOntoEnv.to_rdflib_dataset(mode=...)are deprecated in favor ofOntoEnv.get_dataset()(read-only view) andOntoEnv.copy_dataset()(mutable copy). The old names still work and emitDeprecationWarning.GraphIO::union_graphnow returns(Dataset, Vec<FailedImport>)— always best-effort: per-id errors (bad graphname, ensure_loaded failure, mid-graph store iteration error) are recorded in the failures list and the offending id is skipped, but the rest of the union is still assembled. The previous behavior silently dropped failures with no signal.OntoEnv::get_union_graphconsumes the failures list: in strict mode any failure becomes an error; in non-strict mode the partial union is returned withUnionGraph.failed_importspopulated so the caller knows what’s missing.Catalog adoption and recovery now require a stable, fully readable backend snapshot instead of publishing partial metadata when a graph read fails.
Added¶
OntoEnv.recover(path, graph_store=None)rebuilds a catalog after an interrupted mutation and removes the recovery marker only after the new catalog is published.Python
UnresolvedImportErrordistinguishes an unresolvedowl:importstarget passed tocopy_graphfrom otherValueErrorfailures.Python
OntoEnv.updatenow accepts an optional source location and always replaces that source’s stored graph while following its imports.update(force=True)replacesupdate(all=True);all=remains as a deprecated compatibility alias.An authoritative RDF5D metadata catalog at
.ontoenv/catalog.r5tu, with graph-free warm opens, automatic legacy JSON migration, backend drift detection, recovery markers, explicitcreate/open/adoptlifecycle APIs, a state-drivenconnect(sync=...)entry point, and incremental/targeted/full graph-store synchronization reports.Durable RDF5D snapshot replacement using unique same-directory temporary files, file and directory synchronization, and atomic publication.
OntoEnv.copy_graph(uri) -> rdflib.Graph— materialize a mutable in-memory copy of a single ontology.Pythonic container/context-manager protocols on
OntoEnv:len(env),uri in env,env[uri](shorthand forget_graph),for name in env(iterates ontology URIs), andwith OntoEnv(...) as env:(callsclose()on exit).bool(env)is alwaysTrue— useenv is Noneto detect absence.OntoEnv.copy_closure(uri, graph=None, rewrite_sh_prefixes=True, remove_owl_imports=True, recursion_depth=-1) -> (Graph, list[str])— materialize the flattened imports closure into a mutablerdflib.Graph(the view-returningget_closure’s mutable counterpart).OntoEnv.iter_triples(uri) -> Iterator[(s, p, o)]andOntoEnv.iter_closure_triples(uri, recursion_depth=-1) -> Iterator[(s, p, o)]— streaming triples as rdflib terms, skipping the rdflibGraphwrapper. Closure iteration is not de-duplicated.ontoenv.ViewGraph— read-only, non-rdflib.Graphview returned byget_closureandget_union. Exposestriples,subjects/predicates/objects,query(SPARQL scoped to the view),len,in,serialize, and namespace bindings; mutation raisesValueError._RdfLibStoreBackendscoped methods:iter_triples_scoped,triples_scoped,subjects_scoped,predicates_scoped,objects_scoped, andquery_scoped— the Rust primitivesViewGraphdelegates to, scoped to a list of named graphs.Internal
OntoEnv.get_graph(uri)Dataset cache: subsequentget_graphcalls reuse the underlying store; mutating methods (add,add_no_imports,update,flush) invalidate it.OntoEnv.refresh_dataset(dataset)method — re-snapshot the env into an existingOntoEnvStore-backed Dataset. Replaces the top-levelrefresh_dataset_from_env(dataset, env)helper.Environment::get_ontology_by_id(&GraphIdentifier) -> Option<&Ontology>— direct lookup that skips the configuredResolutionPolicy.rdf5d:
closuremodule (rdf5d::ClosurePatch,ClosureTripleIds,ClosureSparqlView) — zero-copy closure semantics over aSnapshot. AClosurePatchprecomputes the closure transform (imports stripping, ontology-declaration collapse, SHACL-prefix consolidation) as removals (akeeppredicate) plus a small additions “patch graph”, all in on-disk term-id space; the iterator and SPARQL views apply it lazily to present a single flattened, de-duplicated graph. Used byOntoEnv.get_closure.rdf5d:
Snapshot::build_indexes()andR5tuFile::build_term_index()— eagerly build all four permutation indexes (and the reverse term-id index) at bind time.GraphIO::ensure_loaded(&GraphIdentifier) -> Result<()>trait hook for persistent backends to lazily load named graphs into the in-memory store. Default impl is a no-op.FailedImport::ontology()andFailedImport::error()expose union failures to Rust callers without parsing display strings.
Removed¶
Top-level re-exports
ontoenv.dataset_from_envandontoenv.refresh_dataset_from_env— useenv.get_dataset(...)andenv.refresh_dataset(...)instead. The functions still exist inontoenv.rdflib_storeas the underlying implementation.
Fixed¶
ontoenv dep-graphno longer defines a short-ofor--output, which collided with the global-o/--offlineflag and madeontoenv dep-graph --helppanic on debug builds. Use the long--outputform.Python reopen paths now distinguish omitted configuration from explicit values for strict/offline/name-validation/cache settings, resolution policy, cache TTL, search paths, and include/exclude filters. Explicit
False,"default", and empty lists are honored. Writable connections persist overrides; read-only connections keep them session-local; no reopen path implicitly scans or re-ingests graph data.Runtime configuration setters now update the active graph backend and resolution policy. Python now exposes the documented cache-mode and remote-cache-TTL getters and setters, and
require_ontology_namescontrols ontology-declaration validation independently of strict mode.Non-strict
import_dependencies(..., fetch_missing=True)andget_dependencies(..., fetch_missing=True)now commit tolerated unresolved imports without leavingcatalog.pending, and every attempted unresolved target retained in the current environment state is classified asUnresolvedImportErrorbycopy_graph, including targets originating in best-effort fetches for transient caller graphs.Successful non-strict
add/add_from_bytesingestion with unresolved imports no longer leaves a recovery marker; the partial environment remains reopenable.Writable persistent stores enumerate graph IDs from their lazy RDF5D directory, allowing non-empty 0.5 catalogs to migrate without a false backend-mismatch error.
ViewGraph.triplesscoped-pattern branch:triples_scopedreturns(triple, contexts)rows, but the code yielded the whole row — now unpacks to the bare triple.OntoEnv.add(..., rename=...)rename test expectation: the minimal<old> a owl:Ontology .fixture contains exactly one triple, andrename_ontology_iri_graphrewrites the subject to yield one triple; thetest_add_with_rename_overrides_iriassertion oflen == 2(“type + declare”) was a stale expectation with no matching fixture, corrected tolen == 1.add_ids_to_dependency_graphis now transactional with respect to the in-memory env state: a mid-traversal failure (e.g. a strict-mode unresolved import) no longer leavesenv,dependency_graph,dependency_graph_index, andfailed_resolutionsdesynced from each other.Dependency-graph construction now resolves imports by
GraphIdentifierinstead of going throughResolutionPolicy, so the graph reflects the exact ontology being added rather than whatever the policy maps the name to.Python maps lifecycle errors by their Rust types rather than matching error message text.
Performance¶
get_closureview reads are served directly from the rdf5d mmap snapshot with no materialization. Permutation indexes are built eagerly (in parallel) when the snapshot is bound, and each_RdfLibStoreBackendkeeps a persistent term-id → rdflib-term cache shared acrosstriples()calls, so repeated scans skip rdflibURIRef/Literalconstruction. Full closure iteration on the Brick closure runs in ~30 ms warm.get_closureBFS-walks the pre-builtdependency_graphviaNodeIndexinstead of resolving each import by name on every step. A newdependency_graph_indexmap is kept in sync with the graph.Environment::get_ontologyshort-circuits exact-id hits and skips the per-callVec<&Ontology>policy fallback.GraphIO::union_graphstreams quads from the store directly into the targetDataset, dropping the intermediate per-idGraphallocation.get_union_graphandget_namespacesborrow ontologies fromenv.ontologies()instead of cloning each one throughget_ontology.
[0.5.5]¶
Added¶
OntoEnv.as_dataset(backend="auto", store=None)— return a read-onlyrdflib.Datasetview of the environment.backend="rdf5d"is a zero-copy mmap-backed view over the persistent.ontoenv/store.r5tusnapshot;backend="copy"materializes an in-memory copy;backend="auto"picks rdf5d when the snapshot file exists and copy otherwise.New
ontoenv.OntoEnvStorerdflibStore(also registered as the rdflib plugin"ontoenv") that serves SPARQL through the Rust backend, withdataset_from_env/refresh_dataset_from_envhelpers inontoenv.rdflib_store.rdf5d: SPARQL backend (
rdf5d::SparqlDatasetView) and a Brick benchmark comparing it to Oxigraph + RocksDB; rdf5d wins on the tested patterns (≈18% faster on bound-graph queries, ≈2× faster on full scans).
Changed¶
Rdf5dSnapshot::openis now O(graphs) rather than O(triples) — per-logical-graph unique-triple counts are computed lazily viaOnceLock, with a single-gid fast path that trusts the GDIRn_triplesdirectly. Reverse term lookup (find_term_id) memoizes againstR5tuFile::find_decoded_termso repeated SPARQL bindings of the same IRI stop re-scanning the term table.Copy-fallback Dataset construction (the
backend="copy"/backend="auto"fallback path) builds the materializedOxDatasetdirectly from the inner RustOntoEnv, dropping the previous round-trip through an intermediaterdflib.Dataset.
Deprecated¶
OntoEnv.to_rdflib_dataset(mode=...)— useOntoEnv.as_dataset(backend=..., store=...)instead. The old method still works (and forwards to the new one) but now emitsDeprecationWarning. The new method renames the parameter (mode→backend) and accepts an optionalstore=to rebind an existing rdflibStore; error messages now referencebackend=accordingly.
[0.5.4]¶
Added¶
Progress reporting for
updatecommand; output suppressed when stderr is not a TTYRDF5D: compact string/literal dictionaries, streaming spill policy, workload profiling, and optimized reader/metadata layout
GitHub badge in README; rdf5d architecture documentation
Changed¶
RocksDB is no longer compiled by default. OntoEnv uses an in-memory Oxigraph store backed by the custom RDF5D on-disk format, so the heavyweight RocksDB C++ dependency was unnecessary. It is now opt-in via
--features rocksdbacross all crates (ontoenv,ontoenv-cli, and the Python bindings). This significantly reduces compile times and binary size for the common case.Store::flush()is gated behind the same feature flag since that method only exists when RocksDB is compiled in.Upgraded reqwest 0.12 → 0.13
Internal
lib/srchelpers extracted and dead code removedRemoved deprecated
tempdirdependency
Fixed¶
ontoenv addnow correctly handles JSON-LD files served with atext/plaincontent-type header (e.g. GitHub raw URLs); URL extension and content sniffing are used when content-type is genericext_to_rdf_formatnow maps.jsonld,.json,.rdf,.owl, and.nqextensions for local file loadingFormat fallback in
load_staging_store_from_bytesnow cycles through all supported formats (NQuads, TriG, JSON-LD) instead of only Turtle/RDF-XML/N-TriplesAll
cargo clippy -D warningserrors resolved across the workspace
[0.5.3] — 2026-04-03¶
Added¶
list_closureandmissing_importsnow accept a transientrdflib.Graphin addition to ontology IRIsgraph_store: newinit_from_storeandrefresh_from_storeconstructors
Changed¶
Updated
.pyistub with all recent API additionsUpdated GitHub Actions versions
[0.5.2] — 2026-04-02¶
Added¶
missing_importsmethod in Python API to list unresolved ontology importsThree-level import chain test for
missing_imports
Changed¶
Improved documentation
[0.5.1] — 2026-03-04¶
Fixed¶
Linux wheel build
Changed¶
Updated license year to 2025
[0.5.0] — 2026-03-03¶
Added¶
In-memory
rdflib.Graphobjects can now be passed directly toOntoEnv.addin PythonExternal Python graph store protocol support (duck-typed; no ABC required)
namespacesCLI command and Python/Rust API methodsh:prefix conflict detection in
rewrite_sh_prefixesontology include/exclude regex and glob filters (
Config.include/Config.exclude)Content hash-based caching to avoid redundant re-parses
Sphinx documentation with GitHub Pages deployment
llms.txtfor LLM-friendly docsoxrdflibintegration
Changed¶
Lazy loading of graphs from RDF5D on first access
get_dependencies_graphrenamed toget_dependenciesSHACL prefix rewrite now correctly targets the root ontology
Python build switched to
abi3wheels (Python 3.12 default)Search directories made explicit in config
Fixed¶
Windows cross-platform path and file URI handling
CI flakes: mtime sleep guards, Windows file IRIs, locked-file skipping
import_graphdepth/cycle handling and QUDT URI updates
[0.4.0] — 2025-11-07¶
Added¶
RDF5D custom storage format (
.r5tu) replaces SQLite-backed Oxigraph on-disk store; zstd-compressed, CRC-verified, with lazy graph loadingInterprocess read/write locking via
fs2(exclusive writer, shared readers)Parallel remote ontology fetching via staged ingestion
New
fetchmodule with layered format detection, content sniffing, Link header following, and extension candidate fallbacksuse_cached_ontologiesoption to skip unchanged ontologiesget_dependencies/get_dependencies_graphmethod (Rust + Python)--allflag forupdateto force-reload all ontologiesONTOENV_LOGenvironment variable for log controlConcurrency tests (Python and Rust)
new_onlineconstructor as the default for Python
Changed¶
Upgraded to oxigraph 0.5
updatecommand gains--allflag;update_allalias addedConfig drops
ConfigBuilderfrom Python API; flags passed directly toOntoEnvimport_graphmerges full closure with SHACL rewriteNamespace prefix map built at ontology init time
RDF5D localized into this repo as a workspace crate
Fixed¶
Failed ontology resolutions tracked to avoid redundant retries
Correct graph-name handling for oxigraph queries
File URI generation and angle bracket stripping in IRIs
[0.3.0] — 2025-07-24¶
Added¶
whycommand andimportersmethod (replacesget_dependents) — explains why an ontology is in the environmentmissing_importsmethod to list unresolvedowl:importslistsubcommand for locations, ontologies, and missing importsconfigsubcommand (replacesset) withget,unset,add,remove,listoperationsadd_no_importsflag to load an ontology without following itsowl:importsrecursion_depthparameter forget_closureandimport_dependenciesRecursive
.ontoenvdirectory search from the current working directoryNamespace prefix extraction and utility functions
ExternalStoreGraphIOfor integrating with other Oxigraph-based packagesComprehensive Python
unittestsuite
Changed¶
refreshcommand renamed toupdateget-closureCLI subcommand renamed toclosureConfigbuilder pattern replaces direct struct constructionimport_dependenciesreturns a list of URIs and modifies graph in-placeaddauto-detects whether the argument is a URL or file path
Fixed¶
Namespace map deserialization robustness
Self-import filtering to prevent recursion
no_searchrespected when loading config from fileGraph content compared (not just mtime) to detect updates
[0.2.1] — 2025-06-06¶
Fixed¶
Improved detection of changed files
[0.2.0] — 2025-05-07¶
Added¶
New
GraphIOtrait abstraction (PersistentGraphIO,MemoryGraphIO,ReadOnlyPersistentGraphIO)UnionGraphstruct returned fromget_union_graphflushmethod for explicit store writessizestats reportingio_typeaccessor onGraphIOSerialization of
EnvironmentstructPython
flushbinding
Changed¶
OntoEnv::newmade private; use named constructorssearch_directoriesmade a positional CLI argumentPoetry replaced with
uvfor Python toolingTemporary environments improved;
--forceflag for reset
Fixed¶
Offline retrieval error propagation
Store initialization and path handling
Stat report accuracy
[0.1.10] — 2025-03-19¶
Added¶
get_dependentsmethodType hints stub file (
.pyi) forConfigandOntoEnvJSON-based ontology URI/file config (
fetchsubcommand)no_searchflag to disable directory walkingRead-only mode for
OntoEnvread_formatfallback logic for ambiguous RDF inputsAccept
text/turtlecontent-type header
Changed¶
Bulk loading of graphs for performance
Mutex lock scope reduced in
get_graphto lower deadlock risk
Fixed¶
Store re-opened unnecessarily on repeated calls — fixed by caching
Mutex unlocking on drop
[0.1.9] — 2024-08-28¶
Added¶
list_closuremethodstatusandversionCLI commandsrdflibgraph conversion method (to_rdflib)Git hash embedded in CLI binary via
build.rs
Changed¶
Build system improvements (musl, zig cross-compilation)
[0.1.8] — 2024-06-15¶
Added¶
Read-only mode fallback in Python
More test coverage
Fixed¶
Strict mode now respected throughout
Resolution of ontology locations
Python state persistence across calls
[0.1.6] — 2024-04-29¶
Changed¶
Switched from OpenSSL to
rustls(no system SSL dependency)Cross-platform build improvements (aarch64, x86 macOS, Linux musl)
[0.1.5] — 2024-04-28¶
Added¶
--recreateflag forinitto force reinitialize an existing environment
[0.1.4] — 2024-04-26¶
Added¶
import_graphmethod (Python)Graph transforms (SHACL prefix rewriting, import removal)
README
Fixed¶
URL handling and path normalization
Detection of removed files
[0.1.2] — 2024-04-13¶
Initial release.
Features¶
Core ontology environment management: discover, load, and resolve
owl:importstransitivelyCLI:
init,add,closure,get,dump,statusPython bindings via PyO3/maturin
Offline mode
Strict mode (require
owl:Ontologydeclarations)Directory walking with glob patterns
CI/CD for Linux, macOS, and Windows wheels