A library for W3C Provenance Data Model supporting PROV-JSON, PROV-XML and PROV-O (RDF)
—
System for creating and managing globally unique identifiers using qualified names and namespaces, following URI and W3C standards. This system enables unambiguous identification of PROV elements across different documents and systems.
Base class for all identifiers, representing xsd:anyURI.
class Identifier:
def __init__(self, uri: str):
"""
Create an identifier from a URI string.
Args:
uri (str): URI string for the identifier
"""
@property
def uri(self) -> str:
"""
The URI associated with this identifier.
Returns:
str: The URI representing the resource identifier
"""
def provn_representation(self) -> str:
"""
Get the PROV-N representation of this identifier.
Returns:
str: The PROV-N representation of the URI
"""
def __str__(self) -> str:
"""String representation returns the URI."""
def __eq__(self, other) -> bool:
"""Equality based on URI comparison."""
def __hash__(self) -> int:
"""Hash based on URI and class."""Qualified names combining a namespace with a local part, providing compact representation of URIs.
class QualifiedName(Identifier):
def __init__(self, namespace: Namespace, localpart: str):
"""
Create a qualified name from namespace and local part.
Args:
namespace (Namespace): The namespace object
localpart (str): The local part of the qualified name
"""
@property
def namespace(self) -> Namespace:
"""
The namespace object for this qualified name.
Returns:
Namespace: Associated namespace
"""
@property
def localpart(self) -> str:
"""
The local part of this qualified name.
Returns:
str: Local part string
"""
def provn_representation(self) -> str:
"""
Get the PROV-N representation of this qualified name.
Returns:
str: PROV-N representation (e.g., 'prefix:localpart')
"""
def __str__(self) -> str:
"""String representation in prefix:localpart format."""Namespace management for creating qualified names and managing URI prefixes.
class Namespace:
def __init__(self, prefix: str, uri: str):
"""
Create a namespace with prefix and URI.
Args:
prefix (str): Short prefix for the namespace
uri (str): Full URI for the namespace
"""
@property
def prefix(self) -> str:
"""
The prefix string for this namespace.
Returns:
str: Namespace prefix
"""
@property
def uri(self) -> str:
"""
The URI string for this namespace.
Returns:
str: Full namespace URI
"""
def contains(self, identifier: Identifier) -> bool:
"""
Check if an identifier belongs to this namespace.
Args:
identifier (Identifier): Identifier to check
Returns:
bool: True if identifier is in this namespace
"""
def qname(self, localpart: str) -> QualifiedName:
"""
Create a qualified name in this namespace.
Args:
localpart (str): Local part of the name
Returns:
QualifiedName: New qualified name
"""
def __getitem__(self, localpart: str) -> QualifiedName:
"""
Create qualified name using bracket notation: namespace['localpart'].
Args:
localpart (str): Local part of the name
Returns:
QualifiedName: New qualified name
"""
def __str__(self) -> str:
"""String representation showing prefix and URI."""
def __eq__(self, other) -> bool:
"""Equality based on URI comparison."""
def __hash__(self) -> int:
"""Hash based on URI."""The NamespaceManager class handles namespace registration and qualified name resolution.
class NamespaceManager(dict):
def __init__(self, namespaces=None, default=None, parent=None):
"""
Manage namespaces for a document or bundle.
Args:
namespaces (dict or iterable, optional): Initial namespaces
default (str, optional): Default namespace URI
parent (NamespaceManager, optional): Parent namespace manager
"""
def get_namespace(self, uri: str) -> Namespace | None:
"""
Get namespace by URI.
Args:
uri (str): Namespace URI to find
Returns:
Namespace: Found namespace or None
"""
def get_registered_namespaces(self) -> Iterable[Namespace]:
"""
Get all registered namespaces.
Returns:
Iterable[Namespace]: All registered namespaces
"""
def set_default_namespace(self, uri: str) -> None:
"""
Set the default namespace URI.
Args:
uri (str): Default namespace URI
"""
def get_default_namespace(self) -> Namespace | None:
"""
Get the default namespace.
Returns:
Namespace: Default namespace or None
"""
def add_namespace(self, namespace: Namespace) -> Namespace:
"""
Add a namespace to the manager.
Args:
namespace (Namespace): Namespace to add
Returns:
Namespace: The added namespace (may have modified prefix)
"""
def add_namespaces(self, namespaces):
"""
Add multiple namespaces.
Args:
namespaces (dict or iterable): Namespaces to add
"""
def valid_qualified_name(self, element: Identifier) -> QualifiedName | None:
"""
Convert identifier to valid qualified name if possible.
Args:
element (Identifier): Identifier to convert
Returns:
QualifiedName: Valid qualified name or None
"""
def get_anonymous_identifier(self, local_prefix: str = "id") -> Identifier:
"""
Generate an anonymous identifier.
Args:
local_prefix (str): Prefix for generated identifier
Returns:
Identifier: New anonymous identifier
"""Common namespaces are predefined as constants:
# W3C PROV namespace
PROV: Namespace # prefix='prov', uri='http://www.w3.org/ns/prov#'
# XML Schema namespace
XSD: Namespace # prefix='xsd', uri='http://www.w3.org/2001/XMLSchema#'
# XML Schema Instance namespace
XSI: Namespace # prefix='xsi', uri='http://www.w3.org/2001/XMLSchema-instance'from prov.identifier import Namespace, QualifiedName
from prov.model import ProvDocument
# Create custom namespaces
ex = Namespace('ex', 'http://example.org/')
foaf = Namespace('foaf', 'http://xmlns.com/foaf/0.1/')
# Create qualified names
person_id = ex['person1'] # Using bracket notation
name_attr = foaf.qname('name') # Using qname method
print(person_id) # ex:person1
print(person_id.uri) # http://example.org/person1
print(name_attr.uri) # http://xmlns.com/foaf/0.1/name# Create document and add namespaces
doc = ProvDocument()
doc.add_namespace('ex', 'http://example.org/')
doc.add_namespace(foaf)
# Set default namespace
doc.set_default_namespace('http://example.org/')
# Create elements using different identifier styles
entity1 = doc.entity('ex:entity1') # Qualified name string
entity2 = doc.entity(ex['entity2']) # QualifiedName object
entity3 = doc.entity('entity3') # Uses default namespace
entity4 = doc.entity('http://other.org/entity') # Full URI# Get registered namespaces
for ns in doc.get_registered_namespaces():
print(f"Prefix: {ns.prefix}, URI: {ns.uri}")
# Find namespace by URI
ex_ns = doc.get_namespace('http://example.org/')
if ex_ns:
print(f"Found namespace: {ex_ns.prefix}")
# Get default namespace
default_ns = doc.get_default_namespace()
if default_ns:
print(f"Default namespace: {default_ns.uri}")from prov.constants import PROV
# Use predefined PROV namespace
entity_type = PROV['Entity'] # prov:Entity
person_type = PROV['Person'] # prov:Person
# Create entity with PROV types
person = doc.entity('ex:researcher', {
'prov:type': person_type,
'foaf:name': 'Dr. Jane Smith'
})
# Check namespace membership
if ex.contains(person.identifier):
print("Person is in example namespace")from prov.identifier import Identifier
# Create direct URI identifier
uri_id = Identifier('http://example.org/resource1')
# Convert to qualified name if possible
qname = doc.valid_qualified_name(uri_id)
if qname:
print(f"Converted to: {qname}")
else:
print("Could not convert to qualified name")
# Generate anonymous identifiers
anon_id = doc.get_anonymous_identifier('temp')
print(f"Anonymous ID: {anon_id}")# Get PROV-N representation of identifiers
print(person_id.provn_representation()) # ex:person1
print(uri_id.provn_representation()) # <http://example.org/resource1>
# Different representation styles
entity = doc.entity('ex:myEntity')
print(f"URI: {entity.identifier.uri}")
print(f"PROV-N: {entity.identifier.provn_representation()}")
print(f"String: {str(entity.identifier)}")# Handle namespace prefix conflicts
doc.add_namespace('ex', 'http://example.org/')
doc.add_namespace('ex', 'http://other.org/') # Will get different prefix
# Check actual prefixes assigned
for ns in doc.get_registered_namespaces():
if ns.uri == 'http://other.org/':
print(f"Other namespace got prefix: {ns.prefix}") # Might be 'ex1'
# Use specific namespace objects to avoid conflicts
other_ns = Namespace('other', 'http://other.org/')
doc.add_namespace(other_ns)
entity = doc.entity(other_ns['entity'])Install with Tessl CLI
npx tessl i tessl/pypi-prov