Skip to content
Back to top
View this page

# 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.