Skip to content
Back to top
View this page

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