Changelog

Changelog

All notable changes to this project are documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

[0.12.0] - 2026-05-22

Added

  • MultiLangString — one model field for multiple @lang literals on a single predicate; round-trips through from_graph, sync_to_graph, and model_to_triples. Closes #1.

  • TypedLiteral / TypedLiteralListset[TypedLiteral] and list[TypedLiteral] preserve per-object XSD datatypes on one predicate. Closes #2.

  • Multi-valued URI refsset[ResourceRef], list[ResourceRef], and ref_field on set/list (export/import + hydrate_refs for collections). Closes #3.

  • OntologyRegistry — load OWL/RDFS TTL (or static registration) for subtypes_of, inverse_of, and optional apply_hints_to_model. Closes #4.

  • BackPopulates / back_populates= — paired inverse metadata across two models; inverse_pair, optional Rdf.ontology_registry validation, subjects_via_back_populates / models_via_back_populates. Closes #5.

Closes #6 (SparqlModel 0.13 mapping parity tracking).

Fixed

  • OntologyRegistry.load_graph — clear graph-derived owl:inverseOf index before re-indexing (stale inverses after reload).

  • back_populates — raise ValueError when the peer class exists but its field lacks reciprocal metadata.

  • MultiLangString — immutable by_lang mapping; normalize language tags to lowercase on import.

  • store_flush — skip flush on read-only disk stores (no ResourceWarning on close()).

Changed

  • Docs — 0.12.x guides, API reference pages (ontology_registry, terms.lang, terms.typed_literal), ROADMAP matrix; pytest disables pytest_green_light in project config.

[0.11.0] - 2026-05-20

Breaking

  • SHACL removedtriplemodel[shacl] extra, validate_graph, and shacl_shapes= on to_graph / to_dataset / serialize are removed. Use pyshacl directly if you need SHACL validation.

  • **rdflib_kwargs renamed to **format_kwargs on parse/serialize helpers and TripleModel.parse / serialize.

  • bind_namespaces(..., strategy="rdflib") removed; use "core" (same behavior).

  • Graph.parse / Dataset.parsepublicID= renamed to base_iri=.

See docs/MIGRATION_0.11.md for migration details.

Added (pyoxigraph surface)

  • Store opsbulk_load_into_graph, dump_store, load_store, backup_store, optimize_store, store_flush, list_named_graphs, ensure_named_graph, clear_named_graph, remove_named_graph, iter_quads_for_pattern (re-exported from triplemodel / triplemodel.io.stores).

  • Parse — first-class lenient=, without_named_graphs=, rename_blank_nodes= on parse_into_graph, dataset/model parse helpers; infer_format uses RdfFormat.from_extension / from_media_type as fallback.

  • SPARQLrun_sparql / PreparedModelQuery.execute passthrough for use_default_graph_as_union, default_graph, named_graphs, base_iri; parse_query_results and SparqlResult.serialize.

  • Terms — optional direction on LangString and Lang (RDF 1.2 base direction).

  • Canonicalizecanonicalize_quads for in-memory blank-node-stable diffs.

  • Exampleexamples/stores/bulk_load_backup.py.

Removed

  • triplemodel.validation package and all rdflib imports.

  • Direct rdflib dependency (previously only in [shacl] extra).

[0.10.1] - 2026-05-20

Fixed

  • infer_format — reject hext, longTurtle, and trix at inference time with a clear error (not only on serialize); infer from URL paths with ? query and # fragment.

  • parse_into_graph / parse_into_dataset — route source=bytes, BytesIO, and StringIO through data=; reject both source and data.

  • RdfDataset.parse — HTTP(S) and file:// URL sources (parity with RdfGraph.parse).

  • merge_graphs — preserve namespace bindings from input graphs.

  • fetch_url — raise HTTPError on HTTP status ≥ 400.

  • examples/realworld/schema_org_ngos.py — use triplemodel.vocab.XSD (no rdflib import).

Changed

  • graph_value — optional on_duplicate ("first" default; "warn" / "error" match import).

  • JSON-LDRdf.jsonld_context and unsupported parse/serialize kwargs emit UserWarning (pyoxigraph has no @context parameter).

  • Docstrings — aligned with pyoxigraph backend (io/ops, codegen/parse, store/formats, model_to_triples, file I/O guides).

  • Store cleanupcleanup_ephemeral_store_path in io/stores (replaces unused _cleanup_ephemeral_store in io/files).

  • Tests — removed unused tests/_rdflib_plugin_fixtures.py; added tests/test_io_warnings.py.

[0.10.0] - 2026-05-19

Breaking

  • RDF engine — rdflib replaced by pyoxigraph; import Store from triplemodel (or use RdfGraph internally). See MIGRATION_0.10.md.

  • Removedregister_parser, register_serializer, register_store; remote open_sparql_graph / load_sparql (raise NotImplementedError); triplemodel[sqlalchemy] extra.

  • Formatshext, longTurtle, trix no longer supported (pyoxigraph limits).

Added

  • triplemodel.Store — public alias for the pyoxigraph-backed graph type.

  • Disk storeopen_graph("disk", path) for on-disk pyoxigraph.Store; read_only and create=False supported.

  • RdfGraph.close() — closes the store and removes ephemeral on-disk directories from parse_into_store_graph.

  • Migration guideMIGRATION_0.10.md.

Fixed

  • Streaming / store parseparse_into_store_graph cleans up temporary disk directories via graph.close(); legacy sqlalchemy / berkeleydb names emit DeprecationWarning and map to disk.

  • parse_into_store_graph — on parse failure, call graph.close() so ephemeral temp directories are not left on disk.

  • Store cleanupRdfGraph.close() and _cleanup_ephemeral_store emit ResourceWarning when flush or destroy_store fails (still non-throwing).

  • CIstores job installs [dev,shacl]; removed pytest filterwarnings entries that required optional modules or referenced rdflib-only deprecations.

  • Examplesexit_criteria_09.py (0.10 Store smoke), examples/stores/disk_store.py; exit_criteria_08 disk store smoke.

  • Makefile ci — use compat-pyoxigraph on Python 3.13+; drop removed sqlalchemy extra from install line.

  • Tests — per-module unique Rdf.type_uri via tests._type_uri.module_type_uri(); removed global pytest filter for duplicate registration warnings.

Changed

  • SHACL — optional rdflib only in [shacl] extra (bridge for pyshacl).

  • Docs / CI — guides, cookbook, README, ECOSYSTEM, ROADMAP matrix updated for pyoxigraph 0.10; GitHub Actions checkout@v5 and setup-python@v6 (Node 24–ready runners).

  • Docsdocs/installation.md, docs/index.md, docs/guides/index.md: pyoxigraph as core engine; rdflib only in [shacl] extra; codegen API and ROADMAP format-matrix footnotes; parse_url SSRF note in file I/O guide.

  • Packaging — removed unused berkeleydb optional extra; PyPI keywords pyoxigraph / oxigraph (not rdflib).

  • SPARQLload_sparql() fails fast with the same NotImplementedError as open_sparql_graph() (remote endpoints).

[0.9.0] - 2026-05-18

Added

  • rdflib plugin passthroughregister_parser, register_serializer, register_store in triplemodel.plugins

  • API stabilitydocs/API_STABILITY.md; frozen triplemodel.__all__ from 0.9 onward

  • Cookbookdocs/cookbook/ (formats, SPARQL/Fuseki, Dataset, SHACL, stores, real-world, plugins)

  • Compatibility docsdocs/COMPATIBILITY.md; CI compat job with min pydantic==2.5.0 and rdflib==7.0.0

  • Matrix audit — rdflib coverage matrix marked done / partial / TBD / out of scope

  • Examplesexamples/exit_criteria_09.py

Changed

  • ROADMAP — 0.9.0 exit criteria complete; migration guide N/A (no production adopters)

  • SparqlModel (SM-5) — recommended pin triplemodel>=0.9,<2 in ECOSYSTEM_SPARQLMODEL.md

  • Guides — user guide version 0.9.x; plugin registration documented in guide 15

  • Docs — README limitations and guides 04/05/11: inverse sync on add, discovery via forward predicates only, skolemize ordering, bnode fresh policy on sync

  • make examples — runs exit_criteria_03.py and exit_criteria_04.py; exit_criteria_09.py plugin smoke parser strips Turtle-style <...> URIs (no rdflib URI warnings)

Fixed

  • sync_to_graph(..., mode="patch") — skolemize runs after cleanup and export (not before stale nested cleanup), matching documented shared-graph behaviour with replace / add

  • sync_to_graph(..., mode="add") — clears incoming inverse triples before appending forward predicates (same reconciliation as replace / patch for inverse= fields)

  • AddGraphMode — wired through sync_to_graph for mode="add" (was bypassing inverse cleanup)

[0.8.0] - 2026-05-18

Added

  • Predicate-map caching — cached field → predicate IRIs for the default resolver (predicate_map_for_class, owned_predicates_for_class)

  • Strict importRdf.strict_import and Rdf.warn_unmapped_fields; enforcement in graph_to_model

  • Chunked importiter_graph_to_models, graph_to_models(..., chunk_size=)

  • Streaming loadload_models_streaming, parse_into_store_graph for large N-Triples/N-Quads

  • Store helpersopen_graph, graph_store_session, store_commit, store_rollback, destroy_store

  • Optional extrastriplemodel[sqlalchemy], triplemodel[berkeleydb]

  • Plugin hookstriplemodel.plugins (register_predicate_resolver, re-exports)

  • Codegen (experimental)triplemodel-codegen CLI for OWL/RDFS → stub models

  • Guidedocs/guides/15-stores-scale-and-strict.md

  • Examplesexamples/exit_criteria_08.py, examples/stores/ (disk store; historical sqlalchemy example removed in 0.10), examples/codegen/

Changed

  • Public exports — store, streaming, and chunked import helpers on the triplemodel package root

  • Field resolverowned_predicates() delegates to cached maps when using the default resolver

  • CI / dev — test workflow installs sqlalchemy extra; root Makefile with make ci and make release-check

Fixed

  • load_models / load_models_streaming — import kwargs (validate_type, on_duplicate, etc.) no longer forwarded to rdflib parse() (fixes TypeError on multi-class loads and streaming)

  • iter_graph_to_models — reject non-positive chunk_size instead of silently loading nothing

  • Predicate-map cache — honor config= overrides on graph_to_model / owned_predicates_for_class

  • Ephemeral SQLAlchemy stores — auto-created temp DB files from load_models_streaming are removed after load

  • CodegenFileNotFoundError for missing ontology paths; UserWarning when duplicate field names are skipped

[0.7.0] - 2026-05-18

Added

  • Graph comparisongraphs_equal, graph_diff, model_diff, GraphDiff

  • CBDcbd_graph, cbd_model, TripleModel.cbd

  • RDFS helperssubject_type_closure, subclass_uris, resolve_model_class_with_rdfs, transitive_objects, transitive_subjects

  • Subclass dispatchresolve_model_class follows rdfs:subClassOf when Rdf.resolve_subclass is true (default)

  • Batch hydrationhydrate_refs, model_join for shared reference URIs

  • Vocabulary registryVocabularyRegistry (register, bind_vocab, model_for_subject)

  • Transitive importTransitive / rdf_field(..., transitive=True) expands multi-hop object URIs on import

  • Guidedocs/guides/14-graph-algorithms-and-rdfs.md

  • Exampleexamples/exit_criteria_07.py (CBD + RDFS subclass dispatch)

Changed

  • Wikidata capitalsexamples/realworld/wikidata_capitals.py uses hydrate_refs for country labels

  • Public exports — graph algorithm and RDFS helpers on the triplemodel package root

  • Bulk dispatchall_from_graph_dispatch() discovers subjects via resolve_model_class (RDFS-aware), not only direct registered rdf:type triples

Fixed

  • Import skolemization — nested ref_field / embed imports and hydrate_refs pass de_skolemize=False after the outermost de_skolemize; bulk dispatch de-skolemizes once per graph

  • Rdf.resolve_subclassresolve_model_class() honors per-class config when use_subclass is omitted (previously always used RDFS closure)

  • Docs — guide 14 real-world example paths; docs/examples.md FOAF nick field type; ROADMAP 0.6 PyPI version; SPARQL helpers on root API index

Deferred

  • OWL/RDFS codegen CLI — planned for 0.8+ (see ROADMAP)

[0.6.0] - 2026-05-18

Added

  • SPARQL passthroughask, construct_models, select_models, apply_update, run_sparql, prepare_model_query, PreparedModelQuery

  • Remote endpointsload_sparql, open_sparql_graph (SPARQLStore / SPARQLUpdateStore)

  • Helpersinit_ns_from_model, init_bindings_from_model, detect_query_form, graph_from_construct_result

  • Class methodsconstruct_from_sparql, select_from_sparql, load_sparql, ask_sparql on TripleModel

  • Guidedocs/guides/13-sparql-and-endpoints.md

  • Exampleexamples/exit_criteria_06.py

Fixed

  • select_models — map subject URI bindings to Rdf.id_field via id_from_subject_uri even when the subject variable appears in field_map

  • init_bindings_from_model — bind URIRef(subject_uri()) for id_field (not XSD string literals) so prepared-query FILTER works on in-memory graphs

  • load_sparql — infer query form from rdflib prepared Query objects (not only raw strings)

  • load_models — single-class path uses parse() instead of deprecated parse_file()

Changed

  • Public exportsrun_sparql, init_ns_from_model, graph_from_construct_result on the triplemodel package root

[0.5.0] - 2026-05-17

Added

  • Rdf.graph_iri (alias Rdf.graph) — map a model class to a named graph IRI in a Dataset

  • Dataset I/Oparse_into_dataset, load_dataset, dump_dataset, model_to_dataset, models_to_dataset, load_models_from_dataset

  • Instance / class methodsto_dataset, from_dataset, all_from_dataset, sync_to_dataset

  • Config helpersget_graph_context, resolve_graph_iri, is_quad_format

  • Quad helpersiter_model_quads, quads_in_context

  • Dispatchall_from_dataset_dispatch, graph_to_model_dispatch_from_dataset

  • Public exportsiter_model_quads, quads_in_context, all_from_dataset, graph_to_model_from_dataset, graph_to_models_from_dataset on triplemodel package root

  • all_from_dataset_dispatch(..., model_classes=...) — optional filter to load only specified model classes from a dataset

  • model_class_for_type_uri — resolve registered class for an rdf:type IRI

  • Guidedocs/guides/12-datasets-and-named-graphs.md

  • Exampleexamples/exit_criteria_05.py (two named graphs, TriG round-trip)

Fixed

  • all_from_dataset_dispatch — dedupe subjects with multiple registered rdf:type values; hydrate via subclass dispatch (aligned with all_from_graph_dispatch)

  • graph_to_model_dispatch_from_dataset — union rdf:type across named graphs before resolving class; prefer the named graph matching the resolved model’s Rdf.graph_iri; raise when the subject appears in multiple graphs and none match

  • TripleModel.parse_url — graph path uses inferred resolved_format consistently with the dataset path

  • graph_to_models — apply de_skolemize once per bulk import instead of per instance

Changed

  • Duplicate type_uri warning — documents that dispatch uses the last registration per process

  • parse / parse_file / parse_url — use Dataset when format is TriG/N-Quads or Rdf.graph_iri is set

  • serialize — use to_dataset + dump_dataset when format is TriG/N-Quads or Rdf.graph_iri is set

  • load_models — single-parse multi-class load uses Dataset for quad formats or when any class has graph_iri

[0.4.1] - 2026-05-16

Added

  • load_graph — public alias for parse_into_graph

  • load_models_from_graph — load multiple TripleModel classes from one Graph without re-parsing

  • load_models(path, *classes) — single file parse returning dict[type, list[Model]] when multiple classes are passed

  • Rdf.instance_of / Rdf.instance_type_uri — property-based subject discovery (Wikidata wdt:P31, etc.)

  • ref_field(predicate, model=...) — URI foreign-key fields that hydrate a linked model on import

  • XSD partial datesgYear, gMonth, gMonthDay import via literal registry; rdf_field(..., literal_datatype="xsd:gYear") for export

  • Predicate mapping validation — invalid rdf_predicate IRIs fail at class definition; warn when predicate equals a prefix namespace URI

  • Guidedocs/guides/11-real-world-patterns.md

Changed

  • examples/realworld/ — Nobel/DCAT use load_models; Wikidata uses instance_of + ref_field; Schema.org uses typed gYear for foundingDate

  • tests/test_realworld_examples.py — API coverage via tests/test_041_features.py

Fixed

  • ref_field import — hydrate URI foreign keys correctly when the parent model uses Rdf.embed = "bnode" (ref links always use URI semantics, not blank-node embed)

[0.4.0] - 2026-05-17

Fixed

  • Stale nested IRI + inversereplace / patch remove incoming inverse triples when a nested IRI child is dropped from the parent

  • Stale nested bnode + inverse — same cleanup when a nested blank-node child is removed

  • Inverse on syncreplace / patch clear all incoming inverse triples for inverse fields (including reassignment), not only when the field is cleared; forward predicates remain the export source of truth

  • Inverse import — multiple inverse subjects are sorted by IRI string for deterministic import (lexicographically first wins with on_duplicate="warn")

  • patch + skolemize — stale nested blank-node cleanup runs before graph skolemization

  • Subject discoveryall_from_graph without type_uri no longer treats inverse-predicate link sources as subjects

  • Dispatchgraph_to_model_dispatch / all_from_graph_dispatch accept resolver=; bulk dispatch and type-based graph_to_models return instances in stable subject-URI order

  • Inverse import conflicts — warn or error when both forward and inverse triples exist; forward objects win

  • Duplicate Rdf.type_uriUserWarning when a second model class registers the same type_uri

  • from_graph list duplicates — honor on_duplicate for list/set fields (including multiple rdf:List heads)

  • Malformed RDF lists — clear ValueError when a list head lacks rdf:first

  • Stale error message — nested collection rejection now references 0.4

Added

  • File I/OTripleModel.parse, parse_file, parse_url, and serialize delegate to rdflib; load_models / dump_model helpers

  • Format autodetection — filename suffix and explicit format= passthrough (Turtle, TriG, N-Triples, N-Quads, RDF/XML, N3, Hextuples, longTurtle, JSON-LD when rdflib supports it)

  • Rdf.base_uri — default publicID for resolving relative IRIs on parse

  • Rdf.jsonld_context — default JSON-LD @context for parse/serialize when format is json-ld

  • Subclass dispatchparse(..., dispatch=True) and graph_to_model_dispatch pick the most specific registered class by rdf:type

  • InverseOf / rdf_field(..., inverse=...) — import from inverse predicates; export uses the canonical forward predicate

  • SHACL (optional)triplemodel[shacl] extra; validate_graph and shacl_shapes= on to_graph / serialize

  • examples/exit_criteria_04.py — Turtle / JSON-LD / graph round-trip exit criteria

  • Real-world examplesexamples/realworld/ (Nobel linked data, DCAT catalog, Wikidata capitals excerpt, Schema.org NGOs) with bundled TTL, offline CI tests, and data provenance notes

Changed

  • Nested collection error — versionless message for unsupported list[TripleModel] / set[TripleModel]; rejected at class definition

  • inverse= on collectionslist / set fields with inverse= are rejected at class definition

  • Import APIfrom_graph, all_from_graph, and parse* accept resolver= and registry=; all_from_graph / graph_to_models accept de_skolemize=

  • Nested import — parent on_duplicate is honored when hydrating nested embeds

  • Dev tooling — pin pytest>=8.3,<9 for reproducible CI; sphinx-autodoc-typehints>=3 for Sphinx 8.2 doc builds

  • Docs — README limitations (inverse, skolemize graph-wide, dispatch scope); features/API tables; guides for sync, file I/O, and namespaces

  • parse_url User-Agent — uses triplemodel/{version} from package metadata

  • Release docsRELEASING.md updated for 0.4.0; README limitations cover dispatch and sync defaults

  • PyPI trove classifier Development Status :: 4 - Beta (0.1.x–0.3.x were released as alpha)

[0.3.0] - 2026-05-17

Added

  • RDF listslist[T] fields serialize as rdf:List via rdflib Collection; set[T] maps to multiple objects per predicate (unordered)

  • Language-tagged literalsLangString, Annotated[str, Lang("en")], and Lang field metadata for Dublin Core–style @lang values

  • ResourceRef — validated IRI holder for resource object fields (preferred over bare str when the object is always a resource)

  • OpaqueLiteral — preserves unknown or custom XSD/datatype literals on import when no converter is registered

  • rdf:HTML / rdf:XMLLiteral — round-trip as str when the field type is str

  • Blank-node embed hardening — stale blank-node subgraphs removed on replace / patch; Rdf.blank_node_policy ("fresh" | "stable") for deterministic nested blank nodes

  • Skolemizationskolemize / de_skolemize kwargs on to_graph, sync_to_graph, model_to_graph, and from_graph; Rdf.skolemize_export / Rdf.skolemize_import defaults

  • examples/exit_criteria_03.py — DC title with language tag, blank-node Address embed, ordered nick rdf:List

  • Guide: docs/guides/09-rdf-lists-and-lang.md

  • Runnable doc snippets (examples/doc/) with golden outputs checked in CI (tests/test_doc_examples.py)

Changed

  • Breaking: list[T] is no longer “multiple objects per predicate”; use set[T] for that semantics (see docs/guides/03-multi-valued-fields.md)

  • README and user guides describe current list / set collection semantics (no version-specific migration section)

Fixed

  • Union fields (str | int) import using literal datatype to pick the matching member type

  • Export rdf:List fields on nested IRI and blank-node embeds; patch sync clears and updates nested lists and scalars

  • Replace-mode blank-node cleanup order; remove_rdf_list clears non-list blank-node subgraphs

  • Documentation updated for 0.3 list/set semantics

[0.2.0] - 2026-05-17

Added

  • Multi-valued fieldslist[T] and set[T] map to multiple objects per predicate on import/export

  • Nested TripleModel — embed related resources with Rdf.embed ("iri" or "bnode")

  • Graph sync modessync_to_graph, to_graph(..., mode=), and GraphMode ("add", "replace", "patch") to remove stale owned triples

  • NamespacesRdf.prefixes, expand_curie, bind_namespaces, CURIE predicates in rdf_field("foaf:name")

  • triplemodel.vocab — re-exports common rdflib namespaces (FOAF, DC, SKOS, …)

  • Literal registryregister_literal_type with defaults for Decimal, UUID, and Enum

  • Graph helpersmerge_graphs, graph_value, graph_set, objects_for_field

  • IriId metadata and full-IRI id_field values when Rdf.namespace is set

  • Rdf.graph_mode — when to_graph() / sync_to_graph() / model_to_graph() omit mode=, they use the class Rdf.graph_mode (sync_to_graph still defaults to "replace" when graph_mode is "add")

  • all_from_graph without type_uri — discovers subjects that have triples for mapped field predicates when no RDF type is configured

  • graph_set_many — helper for multi-object predicate updates (used by patch sync)

  • Subpackagestriplemodel.io, triplemodel.fields, triplemodel.config, triplemodel.terms, triplemodel.embed, triplemodel.metadata with single-responsibility modules (export, import, discovery, writer, sync modes, embed strategies)

  • triplemodel.protocols — public extension points: RdfResource, PredicateResolver, LiteralRegistry, EmbedStrategy, GraphWriteMode, plus register_rdf_resource / is_rdf_resource_class for nested-type detection without importing TripleModel from cardinality helpers

  • LiteralRegistry classregister_literal_type remains a thin wrapper over default_registry

  • Advanced kwargsmodel_to_graph / sync_to_graph accept optional registry= and resolver= for tests and custom converters (all graph modes)

  • Root exports: GraphMode, sync_to_graph, expand_curie, bind_namespaces, merge_graphs, LiteralRegistry, RdfResource, register_rdf_resource, freeze_prefixes, default_registry

Changed

  • Scalar fields still use on_duplicate for multiple objects; collection fields import all values

  • to_graph defaults to mode="add" (0.1 behaviour); use sync_to_graph(..., mode="replace") to drop cleared fields

  • Preferred imports — graph I/O via triplemodel.io; field helpers via triplemodel.fields; configuration via triplemodel.config. The package root still re-exports the common developer surface

  • objects_for_field — resolves field predicates with the same prefix/CURIE expansion as export/import (fixes inconsistency with graph_value / graph_set)

  • freeze_prefixes — public name (was _freeze_prefixes); accepts dict or list[tuple[str, str]] for Rdf.prefixes

  • Invalid Rdf.embed / Rdf.graph_mode values emit a UserWarning and fall back to "iri" / "add"

  • Internal layout: no import cycles between io, embed, terms, fields, and config; model_to_graph delegates non-add modes to sync mode handlers instead of cross-importing monolithic modules

Fixed

  • patch sync for multi-valued fieldssync_to_graph(..., mode="patch") and to_graph(..., mode="patch") now replace all objects per predicate in one step, so list/set fields keep every value instead of only the last

  • Nested IRI embed + replace sync — clears owned triples on embedded child subjects before re-export

  • Stale nested IRI childrenreplace and patch remove owned triples for nested child subjects that are no longer linked (including mbox=None)

  • set exportNone elements are skipped on export, matching list behaviour

  • list[TripleModel] / set[TripleModel] — rejected with a clear ValueError on export and import

  • graph_to_model with URIRef subjectsid_field is derived from the subject URI when the URI is passed as a URIRef

  • replace / patch extension kwargs — custom resolver= and registry= are honored in all graph write modes (not only add)

  • to_graph(..., mode="patch") on a new graphPatchGraphMode now honors bind and binds Rdf.prefixes (same as add / replace); previously only sync_to_graph bound prefixes for patch writes

  • Nested import with custom registrygraph_to_model(..., registry=...) now passes the registry through import_nested_value and embed strategies so nested fields use the same literal converters as top-level fields

Removed

  • Private modulestriplemodel._graph, _sync, _embed, _config, _fields, _cardinality, _types, _registry, _graph_ops, _namespaces are no longer importable

  • _unwrap_optional — use triplemodel.metadata.cardinality.unwrap_annotation (also exported from triplemodel.metadata)

Documentation

  • examples/foaf_person_02.py demonstrates 0.2 exit criteria

  • Triple ownership documented for SparqlModel integration (SM-1)

  • API reference updated for new module paths; README documents import path changes

[0.1.0] - 2026-05-17

Added

  • TripleModel base class with to_graph(), from_graph(), all_from_graph(), and subject_uri()

  • rdf_field() / Predicate for mapping Pydantic fields to RDF predicates

  • Nested Rdf config (namespace, type_uri, id_field)

  • Low-level helpers: model_to_graph, model_to_triples, graph_to_model, graph_to_models, models_to_graph

  • Public subject-IRI helpers: subject_base(), id_from_subject_uri()

  • XSD scalar round-trip (str, int, float, bool, date, datetime)

  • Package constants: RDF, RDFS, XSD, RDF_TYPE

  • py.typed PEP 561 marker for type checkers (verified in wheel via CI build)

  • from_graph / all_from_graph / graph_to_model options: validate_type (default True), on_duplicate ("warn" | "ignore" | "error")

  • Export OnDuplicate from the package root for type checkers

Fixed

  • get_rdf_config walks the class MRO so subclasses inherit a parent’s nested Rdf config

  • from_graph checks rdf:type against Rdf.type_uri when set (pass validate_type=False to skip)

  • Pydantic validation failures on import are raised as ValueError with model class and subject URI context

  • Duplicate predicate objects on import emit a warning by default (first value still used until 0.2 multi-value support)

  • Safe subject-id extraction (no false matches when one namespace is a prefix of another)

  • Percent-encoding of id segments on subject IRI export; decode on import

  • Plain string literals use xsd:string

  • XSD booleans via Literal.toPython() when datatype is xsd:boolean

  • BNode values rejected when importing into str fields

  • Import errors include field, predicate, and subject context

  • unwrap_annotation peels Annotated[...] so Annotated[int, Predicate(...)] imports with correct XSD coercion