History

2.5.3 (2026-08-08)

  • Escaped PROV-N metacharacters (= ' ( ) , : ; [ ]) in the local parts of qualified names, so identifiers containing them serialise to valid PROV-N (#223). Backslashes in string literals are now escaped before quotes.

  • Anonymous prov:qualified* nodes for communication, attribution, delegation and influence now carry their own influencer property, as the PROV-O qualification tables require, so a qualification node is interpretable without its shorthand binary triple (#250). Decoding uses that property to tell several same-kind qualification nodes apart instead of guessing (#226).

  • PROV-XML now round-trips empty-string attribute values, which previously vanished entirely because lxml reports an empty element’s text as None (#224), and escapes attribute-name local parts that are not legal XML NCNames using the _xHHHH_ convention instead of emitting invalid XML (#289).

  • The PROV-JSON deserializer now raises ProvJSONException with a message naming the offending construct when given malformed input, instead of letting KeyError, AttributeError or TypeError escape (#228).

  • prov:startedAtTime and prov:endedAtTime asserted directly on a qualified prov:Start/prov:End node are now decoded into the relation’s formal prov:time attribute, instead of being stripped into extra attributes (#299).

  • Anonymous attribution, communication and influence relations carrying extra attributes now reconcile onto their prov:qualified* node when decoded from RDF, as delegation and association already did, instead of producing a duplicate record (#303).

  • Qualified names whose local part ends in a PROV-N metacharacter now round-trip through PROV-O instead of raising ValueError: Can't split from rdflib during deserialization (#294). Encoded output is unchanged.

  • prov.read() now populates the serializer registry only when it is empty, instead of discarding and rebuilding it on every call. No observable behaviour change (#353).

2.5.2 (2026-08-08)

  • 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. Both etree.parse() call sites previously inherited lxml’s process-global default parser, which any other library in the same process can repoint via etree.set_default_parser() — with a permissive parser installed that way, an external entity resolved and leaked the referenced file’s contents through ProvDocument.deserialize() (#273)

  • Security: CI workflows now pin every third-party action to a commit SHA rather than a mutable tag, and the CI workflow’s GITHUB_TOKEN is restricted to contents: read

  • Test infrastructure: the RDF fixture-comparison helper find_diff() now detects single-triple differences, which it previously missed (#304)

  • Documentation: the support policy on this branch is brought in line with the 3.x line’s — the 2.x release receives security fixes plus bug fixes back-ported from 3.x up to and including 2.6.0, after which it reverts to security fixes only. The previous text still described 2.x as the only maintained line, which predates the 3.0.0 release

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 a str/bytes source that is not an existing file path fails to parse, the error now carries a hint that it was treated as raw content

  • Documentation: 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() on ProvEntity, ProvActivity and ProvAgent — mirroring the existing e1.wasDerivedFrom(e2) style for the remaining relation types

  • The PROV-XML deserializer now raises ProvXMLException when a record’s child element carries only unrecognised XML attributes, instead of leaking a raw UnboundLocalError or silently reusing the previous attribute’s value (#254)

  • ProvDocument.serialize() and deserialize() now accept any writable/ readable file-like object (e.g. tempfile.NamedTemporaryFile), instead of only io.IOBase instances; 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’s BadSyntax no longer aborts detection); a str/bytes source 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 raises TypeError instead of silently returning an empty document

  • Documentation: the agent-subtype idiom now correctly uses qualified names (PROV["Person"]) for prov:type values; 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-compare CLI 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 historic from prov.model import X still works identically. (#231)

  • Minor Makefile/CLAUDE.md cleanup for contributors. (#209)

  • The serializer registry now degrades gracefully when the optional rdf (rdflib) or xml (lxml) extra is not installed: import prov and the JSON/PROV-N serializers work in a minimal install, and requesting the rdf/xml format raises an informative DoNotExist naming the missing extra instead of a bare ModuleNotFoundError. (#230)

  • Deprecation warnings signposting planned 3.0 changes: importing prov.dot/prov.graph now emits a DeprecationWarning naming the future prov[dot]/prov[graph] extras those modules will require, and ProvBundle.unified()/ProvDocument.unified() emit a FutureWarning about the upcoming PROV-CONSTRAINTS unification rework. Both warnings are hidden by default (standard DeprecationWarning/FutureWarning semantics) 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’s CLIError now set __cause__ via exception chaining, so tracebacks show the original error; exception types and messages are unchanged, so existing except blocks are unaffected (#200)

  • Whole package passes mypy --strict; ships a py.typed marker (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 (use uv run --python 3.X pytest for 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 plot extra: 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.