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@langliterals on a single predicate; round-trips throughfrom_graph,sync_to_graph, andmodel_to_triples. Closes #1.TypedLiteral/TypedLiteralList—set[TypedLiteral]andlist[TypedLiteral]preserve per-object XSD datatypes on one predicate. Closes #2.Multi-valued URI refs —
set[ResourceRef],list[ResourceRef], andref_fieldonset/list(export/import +hydrate_refsfor collections). Closes #3.OntologyRegistry— load OWL/RDFS TTL (or static registration) forsubtypes_of,inverse_of, and optionalapply_hints_to_model. Closes #4.BackPopulates/back_populates=— paired inverse metadata across two models;inverse_pair, optionalRdf.ontology_registryvalidation,subjects_via_back_populates/models_via_back_populates. Closes #5.
Closes #6 (SparqlModel 0.13 mapping parity tracking).
Fixed
OntologyRegistry.load_graph— clear graph-derivedowl:inverseOfindex before re-indexing (stale inverses after reload).back_populates— raiseValueErrorwhen the peer class exists but its field lacks reciprocal metadata.MultiLangString— immutableby_langmapping; normalize language tags to lowercase on import.store_flush— skip flush on read-only disk stores (noResourceWarningonclose()).
Changed
Docs — 0.12.x guides, API reference pages (
ontology_registry,terms.lang,terms.typed_literal), ROADMAP matrix; pytest disablespytest_green_lightin project config.
[0.11.0] - 2026-05-20
Breaking
SHACL removed —
triplemodel[shacl]extra,validate_graph, andshacl_shapes=onto_graph/to_dataset/serializeare removed. Usepyshacldirectly if you need SHACL validation.**rdflib_kwargsrenamed to**format_kwargson parse/serialize helpers andTripleModel.parse/serialize.bind_namespaces(..., strategy="rdflib")removed; use"core"(same behavior).Graph.parse/Dataset.parse—publicID=renamed tobase_iri=.
See docs/MIGRATION_0.11.md for migration details.
Added (pyoxigraph surface)
Store ops —
bulk_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 fromtriplemodel/triplemodel.io.stores).Parse — first-class
lenient=,without_named_graphs=,rename_blank_nodes=onparse_into_graph, dataset/model parse helpers;infer_formatusesRdfFormat.from_extension/from_media_typeas fallback.SPARQL —
run_sparql/PreparedModelQuery.executepassthrough foruse_default_graph_as_union,default_graph,named_graphs,base_iri;parse_query_resultsandSparqlResult.serialize.Terms — optional
directiononLangStringandLang(RDF 1.2 base direction).Canonicalize —
canonicalize_quadsfor in-memory blank-node-stable diffs.Example —
examples/stores/bulk_load_backup.py.
Removed
triplemodel.validationpackage and all rdflib imports.Direct
rdflibdependency (previously only in[shacl]extra).
[0.10.1] - 2026-05-20
Fixed
infer_format — reject
hext,longTurtle, andtrixat 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, andStringIOthroughdata=; reject bothsourceanddata.RdfDataset.parse — HTTP(S) and
file://URL sources (parity withRdfGraph.parse).merge_graphs — preserve namespace bindings from input graphs.
fetch_url — raise
HTTPErroron 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-LD —
Rdf.jsonld_contextand unsupported parse/serialize kwargs emitUserWarning(pyoxigraph has no@contextparameter).Docstrings — aligned with pyoxigraph backend (
io/ops,codegen/parse,store/formats,model_to_triples, file I/O guides).Store cleanup —
cleanup_ephemeral_store_pathinio/stores(replaces unused_cleanup_ephemeral_storeinio/files).Tests — removed unused
tests/_rdflib_plugin_fixtures.py; addedtests/test_io_warnings.py.
[0.10.0] - 2026-05-19
Breaking
RDF engine — rdflib replaced by pyoxigraph; import
Storefromtriplemodel(or useRdfGraphinternally). See MIGRATION_0.10.md.Removed —
register_parser,register_serializer,register_store; remoteopen_sparql_graph/load_sparql(raiseNotImplementedError);triplemodel[sqlalchemy]extra.Formats —
hext,longTurtle,trixno longer supported (pyoxigraph limits).
Added
triplemodel.Store— public alias for the pyoxigraph-backed graph type.Disk store —
open_graph("disk", path)for on-diskpyoxigraph.Store;read_onlyandcreate=Falsesupported.RdfGraph.close()— closes the store and removes ephemeral on-disk directories fromparse_into_store_graph.Migration guide — MIGRATION_0.10.md.
Fixed
Streaming / store parse —
parse_into_store_graphcleans up temporary disk directories viagraph.close(); legacysqlalchemy/berkeleydbnames emitDeprecationWarningand map to disk.parse_into_store_graph— on parse failure, callgraph.close()so ephemeral temp directories are not left on disk.Store cleanup —
RdfGraph.close()and_cleanup_ephemeral_storeemitResourceWarningwhen flush ordestroy_storefails (still non-throwing).CI —
storesjob installs[dev,shacl]; removed pytestfilterwarningsentries that required optional modules or referenced rdflib-only deprecations.Examples —
exit_criteria_09.py(0.10 Store smoke),examples/stores/disk_store.py;exit_criteria_08disk store smoke.Makefile
ci— usecompat-pyoxigraphon Python 3.13+; drop removedsqlalchemyextra from install line.Tests — per-module unique
Rdf.type_uriviatests._type_uri.module_type_uri(); removed global pytest filter for duplicate registration warnings.
Changed
SHACL — optional
rdflibonly in[shacl]extra (bridge for pyshacl).Docs / CI — guides, cookbook, README, ECOSYSTEM, ROADMAP matrix updated for pyoxigraph 0.10; GitHub Actions
checkout@v5andsetup-python@v6(Node 24–ready runners).Docs —
docs/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_urlSSRF note in file I/O guide.Packaging — removed unused
berkeleydboptional extra; PyPI keywordspyoxigraph/oxigraph(notrdflib).SPARQL —
load_sparql()fails fast with the sameNotImplementedErrorasopen_sparql_graph()(remote endpoints).
[0.9.0] - 2026-05-18
Added
rdflib plugin passthrough —
register_parser,register_serializer,register_storeintriplemodel.pluginsAPI stability —
docs/API_STABILITY.md; frozentriplemodel.__all__from 0.9 onwardCookbook —
docs/cookbook/(formats, SPARQL/Fuseki, Dataset, SHACL, stores, real-world, plugins)Compatibility docs —
docs/COMPATIBILITY.md; CIcompatjob with minpydantic==2.5.0andrdflib==7.0.0Matrix audit — rdflib coverage matrix marked done / partial / TBD / out of scope
Examples —
examples/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,<2inECOSYSTEM_SPARQLMODEL.mdGuides — 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, bnodefreshpolicy on syncmake examples— runsexit_criteria_03.pyandexit_criteria_04.py;exit_criteria_09.pyplugin 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 withreplace/addsync_to_graph(..., mode="add")— clears incoming inverse triples before appending forward predicates (same reconciliation asreplace/patchforinverse=fields)AddGraphMode— wired throughsync_to_graphformode="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 import —
Rdf.strict_importandRdf.warn_unmapped_fields; enforcement ingraph_to_modelChunked import —
iter_graph_to_models,graph_to_models(..., chunk_size=)Streaming load —
load_models_streaming,parse_into_store_graphfor large N-Triples/N-QuadsStore helpers —
open_graph,graph_store_session,store_commit,store_rollback,destroy_storeOptional extras —
triplemodel[sqlalchemy],triplemodel[berkeleydb]Plugin hooks —
triplemodel.plugins(register_predicate_resolver, re-exports)Codegen (experimental) —
triplemodel-codegenCLI for OWL/RDFS → stub modelsGuide —
docs/guides/15-stores-scale-and-strict.mdExamples —
examples/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
triplemodelpackage rootField resolver —
owned_predicates()delegates to cached maps when using the default resolverCI / dev — test workflow installs
sqlalchemyextra; rootMakefilewithmake ciandmake release-check
Fixed
load_models/load_models_streaming— import kwargs (validate_type,on_duplicate, etc.) no longer forwarded to rdflibparse()(fixesTypeErroron multi-class loads and streaming)iter_graph_to_models— reject non-positivechunk_sizeinstead of silently loading nothingPredicate-map cache — honor
config=overrides ongraph_to_model/owned_predicates_for_classEphemeral SQLAlchemy stores — auto-created temp DB files from
load_models_streamingare removed after loadCodegen —
FileNotFoundErrorfor missing ontology paths;UserWarningwhen duplicate field names are skipped
[0.7.0] - 2026-05-18
Added
Graph comparison —
graphs_equal,graph_diff,model_diff,GraphDiffCBD —
cbd_graph,cbd_model,TripleModel.cbdRDFS helpers —
subject_type_closure,subclass_uris,resolve_model_class_with_rdfs,transitive_objects,transitive_subjectsSubclass dispatch —
resolve_model_classfollowsrdfs:subClassOfwhenRdf.resolve_subclassis true (default)Batch hydration —
hydrate_refs,model_joinfor shared reference URIsVocabulary registry —
VocabularyRegistry(register,bind_vocab,model_for_subject)Transitive import —
Transitive/rdf_field(..., transitive=True)expands multi-hop object URIs on importGuide —
docs/guides/14-graph-algorithms-and-rdfs.mdExample —
examples/exit_criteria_07.py(CBD + RDFS subclass dispatch)
Changed
Wikidata capitals —
examples/realworld/wikidata_capitals.pyuseshydrate_refsfor country labelsPublic exports — graph algorithm and RDFS helpers on the
triplemodelpackage rootBulk dispatch —
all_from_graph_dispatch()discovers subjects viaresolve_model_class(RDFS-aware), not only direct registeredrdf:typetriples
Fixed
Import skolemization — nested
ref_field/ embed imports andhydrate_refspassde_skolemize=Falseafter the outermostde_skolemize; bulk dispatch de-skolemizes once per graphRdf.resolve_subclass—resolve_model_class()honors per-class config whenuse_subclassis omitted (previously always used RDFS closure)Docs — guide 14 real-world example paths;
docs/examples.mdFOAF 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 passthrough —
ask,construct_models,select_models,apply_update,run_sparql,prepare_model_query,PreparedModelQueryRemote endpoints —
load_sparql,open_sparql_graph(SPARQLStore/SPARQLUpdateStore)Helpers —
init_ns_from_model,init_bindings_from_model,detect_query_form,graph_from_construct_resultClass methods —
construct_from_sparql,select_from_sparql,load_sparql,ask_sparqlonTripleModelGuide —
docs/guides/13-sparql-and-endpoints.mdExample —
examples/exit_criteria_06.py
Fixed
select_models— map subject URI bindings toRdf.id_fieldviaid_from_subject_urieven when the subject variable appears infield_mapinit_bindings_from_model— bindURIRef(subject_uri())forid_field(not XSD string literals) so prepared-queryFILTERworks on in-memory graphsload_sparql— infer query form from rdflib preparedQueryobjects (not only raw strings)load_models— single-class path usesparse()instead of deprecatedparse_file()
Changed
Public exports —
run_sparql,init_ns_from_model,graph_from_construct_resulton thetriplemodelpackage root
[0.5.0] - 2026-05-17
Added
Rdf.graph_iri(aliasRdf.graph) — map a model class to a named graph IRI in aDatasetDataset I/O —
parse_into_dataset,load_dataset,dump_dataset,model_to_dataset,models_to_dataset,load_models_from_datasetInstance / class methods —
to_dataset,from_dataset,all_from_dataset,sync_to_datasetConfig helpers —
get_graph_context,resolve_graph_iri,is_quad_formatQuad helpers —
iter_model_quads,quads_in_contextDispatch —
all_from_dataset_dispatch,graph_to_model_dispatch_from_datasetPublic exports —
iter_model_quads,quads_in_context,all_from_dataset,graph_to_model_from_dataset,graph_to_models_from_datasetontriplemodelpackage rootall_from_dataset_dispatch(..., model_classes=...)— optional filter to load only specified model classes from a datasetmodel_class_for_type_uri— resolve registered class for anrdf:typeIRIGuide —
docs/guides/12-datasets-and-named-graphs.mdExample —
examples/exit_criteria_05.py(two named graphs, TriG round-trip)
Fixed
all_from_dataset_dispatch— dedupe subjects with multiple registeredrdf:typevalues; hydrate via subclass dispatch (aligned withall_from_graph_dispatch)graph_to_model_dispatch_from_dataset— unionrdf:typeacross named graphs before resolving class; prefer the named graph matching the resolved model’sRdf.graph_iri; raise when the subject appears in multiple graphs and none matchTripleModel.parse_url— graph path uses inferredresolved_formatconsistently with the dataset pathgraph_to_models— applyde_skolemizeonce per bulk import instead of per instance
Changed
Duplicate
type_uriwarning — documents that dispatch uses the last registration per processparse/parse_file/parse_url— useDatasetwhen format is TriG/N-Quads orRdf.graph_iriis setserialize— useto_dataset+dump_datasetwhen format is TriG/N-Quads orRdf.graph_iriis setload_models— single-parse multi-class load usesDatasetfor quad formats or when any class hasgraph_iri
[0.4.1] - 2026-05-16
Added
load_graph— public alias forparse_into_graphload_models_from_graph— load multipleTripleModelclasses from oneGraphwithout re-parsingload_models(path, *classes)— single file parse returningdict[type, list[Model]]when multiple classes are passedRdf.instance_of/Rdf.instance_type_uri— property-based subject discovery (Wikidatawdt:P31, etc.)ref_field(predicate, model=...)— URI foreign-key fields that hydrate a linked model on importXSD partial dates —
gYear,gMonth,gMonthDayimport via literal registry;rdf_field(..., literal_datatype="xsd:gYear")for exportPredicate mapping validation — invalid
rdf_predicateIRIs fail at class definition; warn when predicate equals a prefix namespace URIGuide —
docs/guides/11-real-world-patterns.md
Changed
examples/realworld/— Nobel/DCAT useload_models; Wikidata usesinstance_of+ref_field;Schema.orguses typedgYearforfoundingDatetests/test_realworld_examples.py— API coverage viatests/test_041_features.py
Fixed
ref_fieldimport — hydrate URI foreign keys correctly when the parent model usesRdf.embed = "bnode"(ref links always use URI semantics, not blank-node embed)
[0.4.0] - 2026-05-17
Fixed
Stale nested IRI + inverse —
replace/patchremove incoming inverse triples when a nested IRI child is dropped from the parentStale nested bnode + inverse — same cleanup when a nested blank-node child is removed
Inverse on sync —
replace/patchclear all incoming inverse triples for inverse fields (including reassignment), not only when the field is cleared; forward predicates remain the export source of truthInverse 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 skolemizationSubject discovery —
all_from_graphwithouttype_urino longer treats inverse-predicate link sources as subjectsDispatch —
graph_to_model_dispatch/all_from_graph_dispatchacceptresolver=; bulk dispatch and type-basedgraph_to_modelsreturn instances in stable subject-URI orderInverse import conflicts — warn or error when both forward and inverse triples exist; forward objects win
Duplicate
Rdf.type_uri—UserWarningwhen a second model class registers the sametype_urifrom_graphlist duplicates — honoron_duplicateforlist/setfields (including multiplerdf:Listheads)Malformed RDF lists — clear
ValueErrorwhen a list head lacksrdf:firstStale error message — nested collection rejection now references 0.4
Added
File I/O —
TripleModel.parse,parse_file,parse_url, andserializedelegate to rdflib;load_models/dump_modelhelpersFormat 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— defaultpublicIDfor resolving relative IRIs on parseRdf.jsonld_context— default JSON-LD@contextfor parse/serialize whenformatis json-ldSubclass dispatch —
parse(..., dispatch=True)andgraph_to_model_dispatchpick the most specific registered class byrdf:typeInverseOf/rdf_field(..., inverse=...)— import from inverse predicates; export uses the canonical forward predicateSHACL (optional) —
triplemodel[shacl]extra;validate_graphandshacl_shapes=onto_graph/serializeexamples/exit_criteria_04.py— Turtle / JSON-LD / graph round-trip exit criteriaReal-world examples —
examples/realworld/(Nobel linked data, DCAT catalog, Wikidata capitals excerpt,Schema.orgNGOs) 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 definitioninverse=on collections —list/setfields withinverse=are rejected at class definitionImport API —
from_graph,all_from_graph, andparse*acceptresolver=andregistry=;all_from_graph/graph_to_modelsacceptde_skolemize=Nested import — parent
on_duplicateis honored when hydrating nested embedsDev tooling — pin
pytest>=8.3,<9for reproducible CI;sphinx-autodoc-typehints>=3for Sphinx 8.2 doc buildsDocs — README limitations (inverse, skolemize graph-wide, dispatch scope); features/API tables; guides for sync, file I/O, and namespaces
parse_urlUser-Agent — usestriplemodel/{version}from package metadataRelease docs —
RELEASING.mdupdated for 0.4.0; README limitations cover dispatch and sync defaultsPyPI trove classifier Development Status :: 4 - Beta (0.1.x–0.3.x were released as alpha)
[0.3.0] - 2026-05-17
Added
RDF lists —
list[T]fields serialize asrdf:Listvia rdflibCollection;set[T]maps to multiple objects per predicate (unordered)Language-tagged literals —
LangString,Annotated[str, Lang("en")], andLangfield metadata for Dublin Core–style@langvaluesResourceRef— validated IRI holder for resource object fields (preferred over barestrwhen the object is always a resource)OpaqueLiteral— preserves unknown or custom XSD/datatype literals on import when no converter is registeredrdf:HTML/rdf:XMLLiteral— round-trip asstrwhen the field type isstrBlank-node embed hardening — stale blank-node subgraphs removed on
replace/patch;Rdf.blank_node_policy("fresh"|"stable") for deterministic nested blank nodesSkolemization —
skolemize/de_skolemizekwargs onto_graph,sync_to_graph,model_to_graph, andfrom_graph;Rdf.skolemize_export/Rdf.skolemize_importdefaultsexamples/exit_criteria_03.py— DCtitlewith language tag, blank-nodeAddressembed, orderednickrdf:ListGuide:
docs/guides/09-rdf-lists-and-lang.mdRunnable 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”; useset[T]for that semantics (seedocs/guides/03-multi-valued-fields.md)README and user guides describe current
list/setcollection semantics (no version-specific migration section)
Fixed
Union fields (
str | int) import using literal datatype to pick the matching member typeExport
rdf:Listfields on nested IRI and blank-node embeds; patch sync clears and updates nested lists and scalarsReplace-mode blank-node cleanup order;
remove_rdf_listclears non-list blank-node subgraphsDocumentation updated for 0.3
list/setsemantics
[0.2.0] - 2026-05-17
Added
Multi-valued fields —
list[T]andset[T]map to multiple objects per predicate on import/exportNested
TripleModel— embed related resources withRdf.embed("iri"or"bnode")Graph sync modes —
sync_to_graph,to_graph(..., mode=), andGraphMode("add","replace","patch") to remove stale owned triplesNamespaces —
Rdf.prefixes,expand_curie,bind_namespaces, CURIE predicates inrdf_field("foaf:name")triplemodel.vocab— re-exports common rdflib namespaces (FOAF, DC, SKOS, …)Literal registry —
register_literal_typewith defaults forDecimal,UUID, andEnumGraph helpers —
merge_graphs,graph_value,graph_set,objects_for_fieldIriIdmetadata and full-IRIid_fieldvalues whenRdf.namespaceis setRdf.graph_mode— whento_graph()/sync_to_graph()/model_to_graph()omitmode=, they use the classRdf.graph_mode(sync_to_graphstill defaults to"replace"whengraph_modeis"add")all_from_graphwithouttype_uri— discovers subjects that have triples for mapped field predicates when no RDF type is configuredgraph_set_many— helper for multi-object predicate updates (used by patch sync)Subpackages —
triplemodel.io,triplemodel.fields,triplemodel.config,triplemodel.terms,triplemodel.embed,triplemodel.metadatawith single-responsibility modules (export, import, discovery, writer, sync modes, embed strategies)triplemodel.protocols— public extension points:RdfResource,PredicateResolver,LiteralRegistry,EmbedStrategy,GraphWriteMode, plusregister_rdf_resource/is_rdf_resource_classfor nested-type detection without importingTripleModelfrom cardinality helpersLiteralRegistryclass —register_literal_typeremains a thin wrapper overdefault_registryAdvanced kwargs —
model_to_graph/sync_to_graphaccept optionalregistry=andresolver=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_duplicatefor multiple objects; collection fields import all valuesto_graphdefaults tomode="add"(0.1 behaviour); usesync_to_graph(..., mode="replace")to drop cleared fieldsPreferred imports — graph I/O via
triplemodel.io; field helpers viatriplemodel.fields; configuration viatriplemodel.config. The package root still re-exports the common developer surfaceobjects_for_field— resolves field predicates with the same prefix/CURIE expansion as export/import (fixes inconsistency withgraph_value/graph_set)freeze_prefixes— public name (was_freeze_prefixes); acceptsdictorlist[tuple[str, str]]forRdf.prefixesInvalid
Rdf.embed/Rdf.graph_modevalues emit aUserWarningand fall back to"iri"/"add"Internal layout: no import cycles between
io,embed,terms,fields, andconfig;model_to_graphdelegates non-addmodes to sync mode handlers instead of cross-importing monolithic modules
Fixed
patchsync for multi-valued fields —sync_to_graph(..., mode="patch")andto_graph(..., mode="patch")now replace all objects per predicate in one step, solist/setfields keep every value instead of only the lastNested IRI embed +
replacesync — clears owned triples on embedded child subjects before re-exportStale nested IRI children —
replaceandpatchremove owned triples for nested child subjects that are no longer linked (includingmbox=None)setexport —Noneelements are skipped on export, matchinglistbehaviourlist[TripleModel]/set[TripleModel]— rejected with a clearValueErroron export and importgraph_to_modelwithURIRefsubjects —id_fieldis derived from the subject URI when the URI is passed as aURIRefreplace/patchextension kwargs — customresolver=andregistry=are honored in all graph write modes (not onlyadd)to_graph(..., mode="patch")on a new graph —PatchGraphModenow honorsbindand bindsRdf.prefixes(same asadd/replace); previously onlysync_to_graphbound prefixes for patch writesNested import with custom
registry—graph_to_model(..., registry=...)now passes the registry throughimport_nested_valueand embed strategies so nested fields use the same literal converters as top-level fields
Removed
Private modules —
triplemodel._graph,_sync,_embed,_config,_fields,_cardinality,_types,_registry,_graph_ops,_namespacesare no longer importable_unwrap_optional— usetriplemodel.metadata.cardinality.unwrap_annotation(also exported fromtriplemodel.metadata)
Documentation
examples/foaf_person_02.pydemonstrates 0.2 exit criteriaTriple 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
TripleModelbase class withto_graph(),from_graph(),all_from_graph(), andsubject_uri()rdf_field()/Predicatefor mapping Pydantic fields to RDF predicatesNested
Rdfconfig (namespace,type_uri,id_field)Low-level helpers:
model_to_graph,model_to_triples,graph_to_model,graph_to_models,models_to_graphPublic 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_TYPEpy.typedPEP 561 marker for type checkers (verified in wheel via CI build)from_graph/all_from_graph/graph_to_modeloptions:validate_type(defaultTrue),on_duplicate("warn"|"ignore"|"error")Export
OnDuplicatefrom the package root for type checkers
Fixed
get_rdf_configwalks the class MRO so subclasses inherit a parent’s nestedRdfconfigfrom_graphchecksrdf:typeagainstRdf.type_uriwhen set (passvalidate_type=Falseto skip)Pydantic validation failures on import are raised as
ValueErrorwith model class and subject URI contextDuplicate 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:stringXSD booleans via
Literal.toPython()when datatype isxsd:booleanBNodevalues rejected when importing intostrfieldsImport errors include field, predicate, and subject context
unwrap_annotationpeelsAnnotated[...]soAnnotated[int, Predicate(...)]imports with correct XSD coercion