History¶
Unreleased¶
Project¶
Python 3.15 is supported and tested in CI, so 3.2.x covers CPython 3.10 to 3.15 and PyPy 3.11. No code changes were needed (#542)
3.2.2 (2026-09-18)¶
3.2.2 is a bug-fix release. No dependency or Python-floor changes; one additive type
alias, prov.model.AttributeValue.
Fixes¶
Attribute arguments type-check for callers under
mypy --strictand pyright without acast(#474).RecordAttributesArgrejecteddict[QualifiedName, str]and similar dicts becausedictis invariant in its key type, and itsIterablealternative stopped mypy inferring an inline dict literal. The alias is now covariant in its key type and names the values a dict may hold asAttributeValue:str,int,float,bool,datetime,Identifier,QualifiedName,Literal,ProvRecord, andNone, which is skipped. The values in a list, tuple, set or iterator of pairs stayAny, as in 3.2.1. One kind of call that type-checked under 3.2.1 is now reported by type checkers, with no change at runtime. A value whose static type isIterable[...]orCollection[...], whether a variable, a parameter or a function’s return, is no longer accepted; type it as aSequence, aSetor anIterator, or wrap it inlist(). One case still needs an annotation under mypy. A dict that is built before the call and holds values of more than one type, such as{"ex:a": 1, "ex:b": "x"}, is inferred asdict[str, object]and reported; annotate the variable asdict[str, AttributeValue]. Pyright accepts the call without the annotationProvRecord.add_attributesaccepts a generator. It iterated its argument twice, so a generator was consumed by the first pass and the record silently gained no attributes. The bundle factory methods were unaffectedA mapping that is not a
dict, such astypes.MappingProxyType, is read through itsitems()wherever attributes are accepted. It was iterated as bare keys and failed
3.2.1 (2026-09-13)¶
3.2.1 is a bug-fix release. No dependency or Python-floor changes; one additive class
attribute, Serializer.deserialize_options.
Fixes¶
The
defaultPROV-N profile no longer accepts the typed-element and derivation shorthand keywordsperson,organization,softwareAgent,collection,emptyCollection,plan,wasRevisionOf,wasQuotedFromandhadPrimarySource. 3.2.0 described them as keywordsprovand ProvToolbox write; neither does, and ProvToolbox’s reader rejects them.defaultis now the Recommendation grammar plus the barementionOfkeyword, which both tools write. A shorthand keyword raisesProvNSyntaxErrorunderstrictanddefaultand is skipped with aProvWarningunderlenientUnder the
strictPROV-N profile, the error for a barementionOfsays thatprov:mentionOfis the strict spelling and that thedefaultprofile accepts the bare keywordThe
lenientPROV-N profile warns once for every skipped statement. Consecutive unparsable statements, such as a run ofprovext:extensibility expressions, were skipped as one with a singleProvWarningnaming only the first; resynchronisation now stops at any name followed by(, not only at a keyword the parser knowsThe PROV-XML deserializer no longer turns a reference element that has no
prov:refinto an identifier made of its whitespace. ProvToolbox writes ahadMembermember as<entity><entity prov:ref="..."/></entity>; the nested reference is now used, with aProvWarning, and a reference element with neither aprov:refnor text raisesProvXMLExceptionThe PROV-JSONLD deserializer accepts an array-valued
entityon aMembershipstatement, which the submission (section 4.18) allows and ProvToolbox always writes, decoding onehadMemberper member; an empty array raisesProvJSONLDExceptionPROV-N output spells
xsd:booleanvalues"true"and"false"instead of"1"and"0". Both are legal lexical forms and the parser reads all four, but every other producer writes the wordsprov-convertandprov-compareopen their files after parsing arguments instead of throughargparse.FileType, which Python 3.14 deprecates; standard input and output are used when no file or-is given and are no longer closed by the tool,--versionand--helpno longer need a binary stdout, andprov-comparereports a missing file argument as a usage error. The own-warnings CI guard runs on Python 3.14 and treatsPendingDeprecationWarningas an error (#441)PROV-XML reader warnings (
<prov:other>skipped, unrepresentable attribute, nested reference) are attributed to the caller ofdeserialize()orprov.read()rather than to a frame insideprov, as the PROV-N reader’s already areIdentifier,QualifiedName,NamespaceandLiteralpickle under every protocol, load pickles written by 3.1.1 and by 3.2.0, and recompute their hash when loaded, so an object unpickled in another process (amultiprocessingworker, a disk cache) matches equal objects in sets and dicts again;weakref.refworks on them. 3.2.0’s__slots__had pickled the stored hash and broken protocols 0 and 1An
Identifierhashes by its URI alone, as aQualifiedNamealready did, so two identifiers that compare equal always hash equaladd_namespace()clears the qualified-name resolution cache when the URI was already registered under another prefix, so resolving the same text before and after the call gives the same result; 3.2.0’s cache had made it order-dependentA URI equal to the default namespace itself is no longer compacted to a qualified name with an empty local part, and the PROV-N writer raises instead of writing an empty identifier for one;
prefix:with an empty local part is still written and readPROV-N parser: a prefix declared twice in one scope is a
ProvNSyntaxError(the Recommendation forbids it); a bare qualified-name literal with an escaped colon and no default namespace is an error instead of resolving in the wrong namespace; a duplicate bundle identifier is a positionedProvNSyntaxError, andlenientskips that bundle whole; an all-digit local name is accepted as an attribute name and as a datatype; a leading byte order mark is skipped; a carriage return ends a short string like a line feed; abytearraystream is decodedThe
lenientPROV-N profile no longer swallows abundleheader orendDocumentthat follows a statement missing its closing bracketPROV-N output names a bundle with a prefixed identifier when the bundle declares a default namespace different from the one its identifier lives in, adding the prefix declaration inside the bundle, so the identifier’s IRI survives re-reading; a bare name was resolved against the bundle’s own default
The PROV-N writer warns with
ProvWarningwhen it percent-encodes a character a local part cannot express, encodes a lone surrogate instead of raisingUnicodeEncodeError, percent-encodes a leading character PN_LOCAL forbids first, and raisesProvExceptionfor a namespace URI that is not an IRIprov.read()forwards each deserializer the options it declares (profilefor PROV-N;rdf_format,relation_mapperandpredicate_mapperfor PROV-O), so auto-detection can read non-TriG RDF withrdf_format, and an option a format does not accept raises aTypeErrornaming the format and its options instead of a JSON or rdflib errorprov.read()forwards thejson.loadoptions (object_hook,object_pairs_hook,parse_float,parse_int,parse_constant) to the PROV-JSON and PROV-JSONLD deserializers, which document themThe PROV-O deserializer warns with
ProvWarningwhen it mints a prefix for an attribute predicate under a namespace declared neither in the document nor in the graph; 3.2.0 minted it silently where 3.1.1 raisedA PROV-JSONLD
Membershipwith an@idand several members decodes to unidentifiedhadMemberrecords with aProvWarning, since records sharing one identifier cannot be unified; with one member the@idis keptprov-convertandprov-compareaccept a text-only standard stream for-Every serializer converts to UTC a datetime whose offset is not a whole number of minutes, since
xsd:dateTimeallows no seconds in an offset
3.2.0 (2026-09-12)¶
PROV-N¶
PROV-N can now be read as well as written:
ProvDocument.deserialize(format="provn"),prov.read()(including auto-detection) andprov-compareaccept PROV-N text, parsed by a hand-written recursive-descent parser with no new dependency (#122)Three parsing profiles, selected with
profile=:strictaccepts the W3C grammar only;defaultalso accepts the barementionOfkeyword and the shorthand keywords (person,organization,softwareAgent,collection,emptyCollection,plan,wasRevisionOf,wasQuotedFrom,hadPrimarySource) thatprovand ProvToolbox write;lenientskips any statement that fails to parse with aProvWarningnaming its line and column and resumes at the next statementprov.serializers.provn.ProvNSyntaxError(aProvException) reports the line, column and expectation of every syntax errorget_provn(strict=True)andserialize(format="provn", strict=True)writeprov:mentionOfinstead of the barementionOfkeyword, so the output parses under the strict profile; the default output is unchangedprov-convertgains-i/--input-format, so it reads PROV-N (or any other registered format), not only PROV-JSONPROV-N output percent-encodes every local part character
PN_LOCALcannot express, not just the earlier fixed set; a tab, a non-ASCII letter or any other codepoint outsidePN_CHARS,PN_CHARS_OTHERand the backslash-escaped metacharacters is now percent-encoded instead of written raw, which previously produced PROV-N the parser could not read back
Performance¶
NamespaceManagercaches string-to-QualifiedNameresolutions per manager, cleared whenever a namespace is added or the default namespace changes, so deserialising a document no longer re-resolves the same identifier text for every record that mentions it (PROV-O deserialisation about 9% faster on the benchmark suite; construction through the API is unchanged, as its cost lies elsewhere)Identifier,QualifiedName,NamespaceandLiteraldeclare__slots__andQualifiedNamestores its hash at construction, cutting the cost of the many hash lookups a record’s attributes require; the__slots__memory saving is offset by the stored hash value eachIdentifier/QualifiedNamenow retains, so overall memory per record is essentially unchanged
Fixes¶
PROV-N output escapes a leading
-or.and a trailing.in a local part, as the grammar requires; other occurrences of-and.stay unescapedPROV-N output percent-encodes a local part character that
PN_LOCALcannot express even escaped (a space,<,>,",{,},|,^,`,\, or a bare%not followed by two hex digits); the local part changes on such a round trip (a bbecomesa%20b), a documented limitation rather than a bugPROV-N output writes a
Literalwith an empty language tag as a plain string, the same as one with no language tag at all; the model and the other serializers are unaffected, so aLiteral("hi", langtag="")still writes PROV-XML’sxml:lang=""PROV-N output writes an underscore-separated language tag (e.g.
en_US) with a hyphen (en-US), the separator the grammar and BCP 47 both usePROV-N output escapes a carriage return in a short string as
\rinstead of writing it raw, which the grammar forbidsPROV-N input accepts a bare, unprefixed local name that starts with a digit (for example
entity(4567)), asPN_LOCALallows; the lexer previously read it as an integer literal and the parser rejected it as an identifierprov.dotwarns withProvWarningwhen a relation’s endpoint is unset (it is drawn to a blank node, as before), instead of staying silent, matching whatprov_to_graph()does since 3.1.1; the warning is attributed to the caller’s own frame even for a relation inside a nested bundlePROV-O decoding resolves attribute keys against the namespaces declared in the RDF source, so a key whose local part ends in a PROV-N metacharacter (
= ' , : ; [ ]) under a namespace no identifier uses now reads back; the last generation-time exclusion in the round-trip property test is liftedProvDocument.deserialize()reads a path source as UTF-8 in binary mode, matching howserialize()writes one, instead of the text mode locale encoding, which could raiseUnicodeDecodeErroror read non-ASCII content back wrongly under a non-UTF-8 locale
Tests¶
The shared round-trip suite and the Hypothesis round-trip property now cover PROV-N, so every statement form, attribute datatype and qualified-name shape is written and read back through the new parser
A PROV-N conformance corpus: every example in the PROV-N and PROV-DM Recommendations parses under the strict profile and round-trips through the writer, ProvToolbox’s own PROV-N test documents parse, and the PROV-N that ProvToolbox writes for the shared test corpus reads back equal to the PROV-JSON fixture of the same name
Documentation¶
The PROV-N how-to covers reading, the three profiles, syntax errors and strict output; the conformance page treats PROV-N as a round-trip format and lists the corpus findings; the What’s new page describes 3.2.0
Project¶
A benchmark suite under
benchmarks/with a non-blocking CI job that fails on a 20% regression against a committed baselinepre-commit run --all-filesnow passes on a clean checkout. The vendored W3C schemas are excluded from the whitespace hooks and the Codacy configuration file ends with a newlineThe citation tests skip from an installed wheel when either repository metadata file is absent
Identifier,QualifiedName,NamespaceandLiteraldeclare their identity fieldsFinal, so type checkers reject reassignment of a field that equality and hashing depend on
3.1.1 (2026-09-10)¶
3.1.1 is a point release: bug fixes, hardening of the graphical export, test coverage for long-open issues, a “What’s new in 3” page, and community and citation files for the repository. No API, dependency or Python-floor changes.
Fixes¶
ProvBundle.__eq__(and thereforeProvDocument.__eq__) compares equal bundles in linear time by testing record-set equality first, falling back to the record-by-record search only when the sets differNamespaceManager.get_namespace()looks a URI up by index instead of scanning every known namespace; where a URI is both registered and the default namespace, the registered one is now returned consistentlyPROV-XML and PROV-O serializers declare a bundle’s namespaces in registration order instead of hash-seed-dependent set order, and
ProvDocument.add_bundle()registers a copied document’s namespaces in the same order; PROV-XML byte output changes for bundles carrying two or more of their own namespaces (#337)prov.graph.prov_to_graph()warns withprov.model.ProvWarningwhen it drops a relation (an unset endpoint, or an undeclared endpoint whose type cannot be inferred, which only awasInfluencedBycan trigger) instead of skipping it silentlyThe PROV-O deserializer no longer warns “attributes were not converted” for documents that decode cleanly; the warning fires only when a subject is left with unconverted attributes and names only those subjects
Security¶
prov.dotno longer writes identifier URIs into GraphvizURL/hreflink attributes unless the scheme ishttp,https,mailto,urnor absent, and escapes labels and identifiers in both HTML-like and quoted node labels; a hostile document could previously plantjavascript:links or break out of a label in rendered SVG. The scheme is checked on the decoded form of the URI, because Graphviz passes HTML character references such as:through to SVG unchanged and every consumer decodes them, so an undecoded check could not catch a namespace URI that smuggled ajavascript:link this way. Annotation-rowhrefvalues are now HTML-escaped too. Ordinary documents render byte-identically, except that identifier URIs containing&now change bytes in the escapedhref
Tests¶
Regression test pinning insertion-ordered attribute output in PROV-N and PROV-JSON; the non-determinism reported in #130 was removed by the 3.0.0 attribute-storage rework and the guarantee is now documented in the PROV-N how-to
The PROV-XML serializer’s
force_typesparameter is now covered in both states (#338)
Documentation¶
README.mdandSECURITY.mdnow state the 2.x support policy as it stands: security fixes only, with 2.5.3 the last back-port releaseNew What’s new in 3 page summarising the 3.x line for projects still on 2.x, linked from the README
New architecture overview under Explanation
Project¶
A non-blocking CI job fails if
provraises aDeprecationWarningorFutureWarningof its own (#340)Design specs, implementation plans and the test-gap checklist moved from
docs/to a top-levelplanning/directory; the published documentation is unaffectedContributor Covenant 2.1 code of conduct, issue forms, a pull-request template,
CITATION.cff(kept in step withprov.__version__by a test) and.zenodo.json; contributions are accepted under the project’s MIT licence on an inbound-equals-outbound basis and the contributor licence agreements incla/are removed
3.1.0 (2026-08-07)¶
New PROV-JSONLD serializer and deserializer, selected with
format="jsonld"(src/prov/serializers/provjsonld.py): implements the W3C member submission “A JSON-LD Representation for the PROV Data Model”, the canonical §4 compacted shape only, with no runtime dependency beyond the standard library. Registered inprov.serializers.Registryand included inprov.read()’s format auto-detection. See Work with PROV-JSONLDserialize(format="jsonld", ...)takes acontextkeyword:"url"(the default) references the submission’s context by URL;"embed"inlines the vendored context object for fully self-contained output; any other value raisesValueErrorThe deserializer additionally tolerates ProvToolbox’s
prov:-prefixed spellings of the type and special terms, and is exercised against both the submission’s own example and a vendored ProvToolbox fixture (src/prov/tests/jsonld/)jsonldjoinsjson/xml/rdfin the shared round-trip test matrix (SHARED_TARGETSinsrc/prov/tests/conftest.py); output is additionally validated against the submission’s vendored JSON Schema in both context modes (context="embed"after substituting the canonical context URL for the embedded object, since the schema’sContextproduction cannot describe an embedded context) and, forcontext="embed", additionally proven valid JSON-LD viapyldflattenKnown limitation: the PROV-JSONLD submission defines no term for
mentionOf(PROV-DM’s Mention relation);serialize()raisesprov.serializers.provjsonld.ProvJSONLDExceptionfor any document containing aProvMentionrecord, and the deserializer raises the same exception on an input statement typed"Mention". This is a permanent limitation of the submission, not aprovdefect. Seedocs/reference/conformance.md
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. This is 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.mdThe
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.modelsplit 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
rdflibto>=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.