Getting started¶
This tutorial walks you through the whole life cycle of a provenance document with
prov: building it in memory, printing it as PROV-N,
saving it to (and loading it back from) PROV-JSON,
and rendering it as a diagram. Every code block runs as written; paste them into a Python
session in order, or copy them into a script.
If you have not installed the library yet, see Installation. To follow the visualisation section you will also need a local Graphviz install (more on that below).
Build a document¶
A ProvDocument is the top-level container for provenance
statements. We start by declaring the namespaces our identifiers live in — a default
namespace for the things this document is primarily about, and an ex prefix for
everything else.
import prov.model as prov
document = prov.ProvDocument()
document.set_default_namespace("http://anotherexample.org/")
document.add_namespace("ex", "http://example.org/")
Now we add an entity — the file whose provenance we are describing. Attributes are
given as a list (or dict) of (name, value) pairs. Names can be prov:* constants such
as prov.model.PROV_TYPE or any prefixed name like ex:path.
e2 = document.entity("e2", (
(prov.PROV_TYPE, "File"),
("ex:path", "/shared/crime.txt"),
("ex:creator", "Alice"),
("ex:content", "There was a lot of crime in London last month"),
))
Next, the activity that produced the file, an agent responsible for it, and the
relations that tie them together. The factory methods return the record they create, so
you can pass either the record objects (e2, a1) or their string identifiers as
references.
a1 = document.activity("a1", "2024-07-09T16:39:38", None, {prov.PROV_TYPE: "edit"})
# Pass extra attributes with the ``other_attributes`` keyword.
document.wasGeneratedBy(e2, a1, other_attributes={"ex:fct": "save"})
document.wasAssociatedWith("a1", "ag2", None, None, {prov.PROV_ROLE: "author"})
document.agent("ag2", {prov.PROV_TYPE: "prov:Person", "ex:name": "Bob"})
That is a complete provenance document. Print it in PROV-N, the human-readable notation from the PROV specification:
print(document.get_provn())
document
default <http://anotherexample.org/>
prefix ex <http://example.org/>
entity(e2, [prov:type="File", ex:path="/shared/crime.txt", ex:creator="Alice", ex:content="There was a lot of crime in London last month"])
activity(a1, 2024-07-09T16:39:38, -, [prov:type="edit"])
wasGeneratedBy(e2, a1, -, [ex:fct="save"])
wasAssociatedWith(a1, ag2, -, [prov:role="author"])
agent(ag2, [prov:type="prov:Person", ex:name="Bob"])
endDocument
Save it and load it back¶
serialize() writes the document out. With no
destination it returns a string; given a file path it writes the file. The default format
is PROV-JSON.
document.serialize("article-prov.json")
deserialize() is the inverse. It accepts a file path or
an open stream as source, or a string via the content keyword. Because a round trip
through PROV-JSON preserves the model exactly, the loaded document compares equal to the
original:
loaded = prov.ProvDocument.deserialize("article-prov.json")
assert loaded == document
Visualise it¶
The prov.dot module turns a document into a pydot
graph, which you can write straight to an image file:
from prov.dot import prov_to_dot
dot = prov_to_dot(document)
dot.write_png("article-prov.png")
Note
Rendering to PNG/PDF/SVG needs a local Graphviz installation (the dot executable),
not just the pydot Python package. Install it from your package manager (for example
brew install graphviz or apt install graphviz) or from https://graphviz.org/download/.
For styling options — direction, labels, hiding attributes — see the graphics how-to guide.
Bundles¶
A bundle is a named set of statements with its own namespaces, letting you describe
the provenance of provenance. A ProvDocument is the only kind of
bundle that may contain other, named bundles. Note how the same local name e001 refers
to two different entities because each bundle resolves it against a different default
namespace:
d = prov.ProvDocument()
d.set_default_namespace("http://example.org/0/")
d.add_namespace("ex1", "http://example.org/1/")
d.add_namespace("ex2", "http://example.org/2/")
d.entity("e001")
bundle = d.bundle("e001")
bundle.set_default_namespace("http://example.org/2/")
bundle.entity("e001")
print(d.get_provn())
document
default <http://example.org/0/>
prefix ex1 <http://example.org/1/>
prefix ex2 <http://example.org/2/>
entity(e001)
bundle e001
default <http://example.org/2/>
entity(e001)
endBundle
endDocument
Where next¶
How-to guides — task-focused recipes: serialising to the other formats (PROV-XML, PROV-O/RDF, PROV-N), producing graphics, converting to and from a NetworkX graph, and using the command-line tools.
Reference — the full API, generated from the source, under API reference.
The PROV data model — for the concepts behind entities, activities, agents and the relations between them, read the W3C PROV-DM Primer.