History¶
3.0.0 (2026-07-27)¶
3.0.0 is the compatibility release: the one release the roadmap allows to change behaviour and drop dependencies, closing out the standards-conformance work (PROV-DM, PROV-CONSTRAINTS, PROV-O) that a backward-compatible 2.x series could not fix. See Upgrading to 3.0 for what changed and what to do about it.
Breaking changes¶
ProvBundle.unified()/ProvDocument.unified()implement PROV-CONSTRAINTS key constraints (22/23) with pairwise term unification of formal attributes: records sharing an identifier whose formal attributes hold different concrete values now raise the new documentedprov.model.ProvUnificationErrorinstead of silently unioning; absent formal attributes unify with concrete values; extra attributes keep set-union semantics; bundles unify independently (#253)unified()rejects same-identifier records of incompatible types (entity vs activity, distinct relation kinds — PROV-CONSTRAINTS 53/54/55) withProvUnificationErrorinstead of merging them order-dependently into the first record’s type; spec-permitted overlaps (an agent that is also an entity or activity) are kept as separate records (#253)Attribute values that are Python-equal but differently typed (
2vs2.0,1vsTrue) are all retained on a record instead of collapsing to whichever was asserted first — visible in attribute iteration, record equality and hashing, and serialization. Attributes now iterate in assertion order rather than an arbitrary hash order, which makesargs,formal_attributes,get_startTime()andget_endTime()deterministic.ProvRecord.get_attribute(),get_asserted_types()and thevalueproperty still return a plainset, but it is now built fresh on every call, so mutating it no longer writes through to the record:record.get_asserted_types().add(qn)now silently does nothing (#34)pydotandnetworkxare no longer unconditional runtime dependencies.import prov.dotnow requires thedotextra (pip install "prov[dot]") andimport prov.graphthegraphextra; without them the import raisesModuleNotFoundErrornaming the extra to install. Theplotextra now pulls inpydot/networkxas well, sinceplot()renders throughprov.dot. Seedocs/upgrading-3.0.md.The
rdfextra now requiresrdflib>=7.0.0(previously>=6.0.0); round-trip behaviour is unchangedpython-dateutildropped —provnow has no unconditional runtime dependencies. Datetime strings are parsed asxsd:dateTime(ISO 8601 plus the hour-24 end-of-day form) via the standard library; factorytime=/startTime=/endTime=parameters raiseProvExceptionon invalid input instead of leaking a raw dateutil error, and non-ISO forms previously tolerated by dateutil (e.g."Nov 7, 2011") are no longer accepted (#237)The misspelled
prov.model.GenrationReftype alias is renamedGenerationRef; the old spelling is removed rather than kept as a deprecated alias
Conformance and correctness fixes¶
When an attribute is asserted twice with an equal value of equal type — e.g.
Literal("10", XSD_DECIMAL)andLiteral("10.00", XSD_DECIMAL)(#77), orLiteral("hi", langtag="en")andLiteral("hi", langtag="EN")(#259) — the record retains the first value asserted, as in 2.x (#34)Numeric datatype fidelity:
Literalvalues typedxsd:long(and the rest of the XSD integer family) keep their asserted datatype instead of being silently re-typedxsd:int(#235); PROV-N output types plain integers by magnitude (xsd:int/xsd:long/xsd:integer) so out-of-int32 values are no longer emitted as invalid bare int literals (#249), and plain floats as full-precisionxsd:doubleinstead of%g-truncatedxsd:float(#251)Literal semantics: string literals have one canonical serialized form (RDF output no longer decorates plain strings with
^^xsd:string) (#89);xsd:decimalliterals compare and hash in value space (#77); language tags compare case-insensitively per RDF 1.1 while preserving their original case in output (#259)PROV-XML round trip preserves attributes whose value is the empty string; previously they were silently dropped on deserialization (#224)
PROV-XML serializes attribute names containing characters illegal in an XML NCName using the reversible
_xHHHH_escaping convention instead of raisingValueError; the deserializer applies the inverse, so such names round-trip (#289)The PROV-JSON deserializer raises
ProvJSONExceptionon structurally malformed input instead of leaking rawKeyError/AttributeError(#228)PROV-JSON typed literals always encode
$as a string, and plain integers are typed by magnitude across PROV-JSON/PROV-XML/PROV-O output (xsd:int/xsd:long/xsd:integer), fixing schema-invalid output for out-of-int32 values (#244, #246, #256)PROV-JSON encodes QualifiedName attribute values as
xsd:QNametyped literals per the PROV-JSON submission (previously the non-standardprov:QUALIFIED_NAME, which remains accepted on input) (#168), andprov:QUALIFIED_NAME-typedLiteralvalues are resolved to QualifiedNames at assertion time, restoring round-trip equality (#238)PROV-O: documents containing multiple same-identifier relations with differing formal attributes (“scruffy” statements) are documented as a PROV-O representational limitation — they serialize but do not round-trip through RDF, and deserializing such RDF now raises an error naming the limitation; all other formats are unaffected (#217)
PROV-O: a related gap is recorded, not fixed — an attribute key whose local part ends in a PROV-N metacharacter (#223) can fail to round-trip through RDF when its namespace has not already been registered by another qualified name;
docs/reference/conformance.mddocuments it as an open defect (#341)PROV-O (RDF) round-trip fidelity: mixed XSD-datatype attribute sets survive deserialization with their asserted datatypes intact (#218), and
xsd:doublevalues are emitted at full precision instead of rdflib’s 6-significant-digit canonical form (#225)PROV-O output: anonymous qualified Communication/Attribution/Delegation/ Influence nodes now carry their influencer property (
prov:activity/prov:agent/prov:agent/prov:influencer) as the PROV-O qualification tables require, and the RDF round trip no longer collapses distinct anonymous delegations sharing a (delegate, activity) pair (#250, #226)PROV-O output emits
alternateOfwith the PROV-DM argument order (alt1 prov:alternateOf alt2); previously subject and object were transposed on both write and read (#258)PROV-O (RDF) deserialization returns
xsd:base64Binaryliterals as their base64 text; previously the value was wrapped in a Python bytes repr (b'…'), corrupting the round trip (#288)PROV-O (RDF) deserialization reconciles
prov:startedAtTime/prov:endedAtTimeasserted directly on a qualifiedprov:Start/prov:Endnode into the relation’s formalprov:timeattribute; previously the formal attribute was leftNoneand the value misfiled as an extra attribute (#299)PROV-O (RDF) round trip: an anonymous Communication/Attribution/Influence relation carrying extra attributes now deserializes back into a single record instead of two (#303)
PROV-O (RDF) round trip: a qualified name whose local part ends in a PROV-N metacharacter (
= ' , : ; [ ]) now deserializes instead of raisingValueError: Can't split ...; serialized output is unchanged (#294)PROV-N output escapes qualified-name local parts per the grammar’s
PN_CHARS_ESCproduction, so identifiers containing' ( ) , : ; [ ] =no longer produce invalid PROV-N (#223)PROV-N continues to emit Mention as bare
mentionOf(...)— the de-facto syntax of the reference implementations for the last decade — rather than theprov:mentionOfform derivable from the PROV-Links note; now documented as a deliberate deviation (#248)
Improvements¶
Bundle-local and default namespace prefixes are now bound into RDF serialization alongside document-level ones, so turtle/TriG output uses the declared prefixes instead of auto-minted
ns1:-style fallbacks (#96)Security: PROV-XML parsing no longer resolves DTD entities and never touches the network (
resolve_entities=False,no_network=True), closing an XXE surface on untrusted input (#273)New public type aliases in
prov.model, joining the existingNameValuePair:AttributePair(an attribute name/value pair before qualified-name resolution),InfluencerRef(theinfluencerparameter ofwasInfluencedBy()andinfluence()) andStreamOrPath(the source and destination parameters ofread(),serialize()anddeserialize())
Internal¶
The PROV-O (RDF) serializer’s encode/decode container functions were decomposed into per-record-type dispatch, with byte-identical output (#274)
Functions across the model, the serializers and
prov.dotwere refactored below the project’s complexity threshold, with byte-identical behaviour (#275)Code-quality passes across
src/prov/— type-alias adoption, removal of a permanently-disabled branch in the PROV-O (RDF) encoder, and readability clean-ups — with no change in behaviourTest infrastructure: the RDF fixture-comparison helper
find_diff()now detects single-triple differences, which it previously missed (#304)
2.5.1 (2026-07-13)¶
prov.read()polish following 2.5.0’s #239: seekable streams are now rewound between auto-detection attempts (so e.g. a PROV-XML stream auto-detects instead of being consumed by the first candidate); rdflib’s “does not look like a valid URI” logger noise from swallowed candidate attempts is suppressed during auto-detection; and when astr/bytessource that is not an existing file path fails to parse, the error now carries a hint that it was treated as raw contentDocumentation: the PROV-XML and PROV-JSON how-to pages no longer describe the pre-2.5.0
read()auto-detection behaviour (exception whitelist, “XML never auto-detects”)
2.5.0 (2026-07-13)¶
New record-level chaining convenience methods (#154):
ProvEntity.wasRevisionOf(),.wasQuotedFrom(),.hadPrimarySource(),.mentionOf(), and.wasInfluencedBy()onProvEntity,ProvActivityandProvAgent— mirroring the existinge1.wasDerivedFrom(e2)style for the remaining relation typesThe PROV-XML deserializer now raises
ProvXMLExceptionwhen a record’s child element carries only unrecognised XML attributes, instead of leaking a rawUnboundLocalErroror silently reusing the previous attribute’s value (#254)ProvDocument.serialize()anddeserialize()now accept any writable/ readable file-like object (e.g.tempfile.NamedTemporaryFile), instead of onlyio.IOBaseinstances; previously such destinations were silently treated as file paths, writing to a repr-named file in the working directory. The serializers’ text/binary stream detection now also recognises such wrapper objects as text streams (#240)prov.read()fixes (#239): valid PROV-XML is now auto-detected (the RDF parser’sBadSyntaxno longer aborts detection); astr/bytessource that is not an existing file path is parsed as raw content, as the documentation always advertised; and input that no deserializer can meaningfully parse (e.g. an empty file) now raisesTypeErrorinstead of silently returning an empty documentDocumentation: the agent-subtype idiom now correctly uses qualified names (
PROV["Person"]) forprov:typevalues; the previously documented string form asserted a plain string, which does not denote the pre-defined PROV type (#236)
2.4.0 (2026-07-06)¶
Documentation overhaul: the documentation has been reorganised along the Diátaxis framework (tutorials, how-to guides, reference, explanations), with new furo/MyST/napoleon/intersphinx tooling behind the Sphinx build. Closes #141 (graphics export how-to) and #83 (
prov-convert/prov-compareCLI tools how-to). (#210, #211, #212, #213, #214, #215, #216)Test suite redesigned as pytest-native: shared statement/attribute/qname coverage is now expressed once and parametrized across a document x format matrix (json/xml/rdf/model) instead of being copy-pasted per serializer; Hypothesis property-based round-trip tests generate documents across the full feature set; a malformed-input corpus exercises each deserializer’s error handling. (#219, #220, #221, #222, #227, #229)
``prov.model`` split into a package (
prov.model.records,prov.model.namespaces,prov.model.bundle) for maintainability, with no import-path changes: every historicfrom prov.model import Xstill works identically. (#231)Minor Makefile/CLAUDE.md cleanup for contributors. (#209)
The serializer registry now degrades gracefully when the optional
rdf(rdflib) orxml(lxml) extra is not installed:import provand the JSON/PROV-N serializers work in a minimal install, and requesting therdf/xmlformat raises an informativeDoNotExistnaming the missing extra instead of a bareModuleNotFoundError. (#230)Deprecation warnings signposting planned 3.0 changes: importing
prov.dot/prov.graphnow emits aDeprecationWarningnaming the futureprov[dot]/prov[graph]extras those modules will require, andProvBundle.unified()/ProvDocument.unified()emit aFutureWarningabout the upcoming PROV-CONSTRAINTS unification rework. Both warnings are hidden by default (standardDeprecationWarning/FutureWarningsemantics) and link to the new Upgrading to 3.0 guide, which tables every planned 3.0 change and what to do about it.
2.3.0 (2026-07-05)¶
Dropped Python 3.9 support; minimum is now Python 3.10 (security fixes in transitive dependencies are only released for Python 3.10+) (#189)
Widened ``rdflib`` to ``>=6.0.0,<8`` (was
>=4.2.1,<7): rdflib 7 now supported; the floor rose because 4.2.1 no longer installs on supported Pythons (#207)Diagnostic improvement:
DoNotExist(serializer lookup) and the CLI’sCLIErrornow set__cause__via exception chaining, so tracebacks show the original error; exception types and messages are unchanged, so existingexceptblocks are unaffected (#200)Whole package passes
mypy --strict; ships apy.typedmarker (PEP 561) so downstream type-checkers see inline types (#192, #193, #194)Coverage raised to 97%, enforced in CI; new tests for the CLI scripts,
prov.read()auto-detection, graph interop, and the serializer registry (#201, #202, #203, #204)Internal code quality: ruff rule families I/C4/SIM/RUF/UP045/UP031 enabled and long-standing lint suppressions resolved (#195, #196, #197, #198, #199, #200); dependency audit documented in
docs/dependencies.md; tox removed (useuv run --python 3.X pytestfor local multi-version testing) (#205)Security hygiene:
SECURITY.md, Dependabot version updates, and a documented support policy (#190)Fixed ReadTheDocs build (Sphinx pinned
<9) (#187)
2.2.0 (2026-07-03)¶
Fixed graphical output when a filename is supplied (#164)
Fixed PROV-XML deserialization when prov is the default namespace (#155)
New
plotextra:pip install prov[plot]for matplotlib support (#166)Marked as Production/Stable; added Python 3.14 to the test matrix
Tooling: ruff (lint+format), pytest runner, uv-based CI, automated PyPI releases via Trusted Publishing. No public API changes.
2.1.1 (2025-06-24)¶
No change - fixing the previous botched release
2.1.0 (2025-06-24)¶
Added type annotations and mypy checks
Added support for Python 3.13
2.0.2 (2025-06-07)¶
Removed support for EOL Python 3.8
Using pyproject.toml for project configurations (instead of setup.py)
2.0.1 (2024-06-10)¶
Removed support for EOL Python 3.6 and 3.7
Minor documentation update (#153)
Stopped using deepcopy when duplicating Namespace (#158)
Restricting rdflib package version to “<7” (#156)
Raise an exception when an empty URI is registered as a namespace (#142)
Ensure rdflib 6+ returns bytes when serializing tests (fixed #151)
Removed fancy label output for bundle
2.0.0 (2020-11-01)¶
Removed support for EOL Python 2
Testing against Python 3.6+ and Pypy3
Earlier releases¶
Releases before 2.0.0 are recorded in the changelog archive.