Skip to content

Lightweight, easy-to-use, flexible composite data structures.

Adjacency(contents=dict()) dataclass

Bases: Graph, Dictionary

Base class for adjacency-list graphs.

Parameters:

Name Type Description Default
contents MutableMapping[Hashable, set[Hashable]]

keys are hashable representations of nodes. Values are the nodes to which the key node are connected. In a directed graph, the key node is assumed to come before the value node in order. Nodes that only appear in a value are added as keys with no connections. Defaults to an empty dict.

dict()

contents = dataclasses.field(default_factory=dict) class-attribute instance-attribute

Initialization Methods

nodes property

Returns a set of all nodes in the graph.

Composite(contents=None) dataclass

Bases: ABC

Base class for composite data structures.

A subclass must provide the nodes property and the _add, _delete, _merge, and _subset methods. The public methods of Composite provide validation and error-checking so that subclasses only need to provide the mechanism for each operation on the internal storage format they use.

Every direct subclass of Composite is automatically registered in Forms.

Parameters:

Name Type Description Default
contents Collection[Any] | None

stored nodes or node labels. Subclasses should narrow the type for contents based on the internal storage format used.

None

contents = None class-attribute instance-attribute

Initialization Methods

nodes property

Returns a set of all nodes in the stored composite data structure.

Raises:

Type Description
NotImplementedError

if a subclass does not provide nodes.

add(item, **kwargs)

Adds node to the stored composite data structure.

Parameters:

Name Type Description Default
item Hashable

node to add to the stored composite data structure.

required
**kwargs Any

additional keyword arguments.

{}

Raises:

Type Description
TypeError

if item is not a node type.

ValueError

if item is already in the stored composite data structure.

Source code in src/holden/base.py
def add(self, item: Hashable, **kwargs: Any) -> None:
    """Adds node to the stored composite data structure.

    Args:
        item: node to add to the stored composite data structure.
        **kwargs: additional keyword arguments.

    Raises:
        TypeError: if `item` is not a node type.
        ValueError: if `item` is already in the stored composite data
            structure.

    """
    if not check.is_node(item):
        raise TypeError(f'{item} is not a node type')
    if item in self:
        raise ValueError(
            f'{item} is already in the composite data structure')
    self._add(item, **kwargs)

delete(item, **kwargs)

Deletes node from the stored composite data structure.

Parameters:

Name Type Description Default
item Hashable

node to delete from the stored composite data structure.

required
**kwargs Any

additional keyword arguments.

{}

Raises:

Type Description
KeyError

if item is not in the stored composite data structure.

TypeError

if item is not a node type.

Source code in src/holden/base.py
def delete(self, item: Hashable, **kwargs: Any) -> None:
    """Deletes node from the stored composite data structure.

    Args:
        item: node to delete from the stored composite data structure.
        **kwargs: additional keyword arguments.

    Raises:
        KeyError: if `item` is not in the stored composite data structure.
        TypeError: if `item` is not a node type.

    """
    if not check.is_node(item):
        raise TypeError(f'{item} is not a node type')
    if item not in self:
        raise KeyError(
            f'{item} does not exist in the composite data structure')
    try:
        self._delete(item, **kwargs)
    except KeyError as error:
        message = f'{item} does not exist in the composite data structure'
        raise KeyError(message) from error

merge(item, **kwargs)

Adds item to this Composite.

This method is roughly equivalent to a dict.update, just adding item to the existing stored composite data structure while maintaining its structure.

Parameters:

Name Type Description Default
item Any

another Composite or a raw form of a composite data structure to merge with.

required
**kwargs Any

additional keyword arguments.

{}

Raises:

Type Description
TypeError

if item is not compatible composite data structure type.

Source code in src/holden/base.py
def merge(self, item: Any, **kwargs: Any) -> None:
    """Adds `item` to this Composite.

    This method is roughly equivalent to a `dict.update`, just adding `item`
    to the existing stored composite data structure while maintaining its
    structure.

    Args:
        item: another Composite or a raw form of a composite data structure
            to merge with.
        **kwargs: additional keyword arguments.

    Raises:
        TypeError: if `item` is not compatible composite data structure
            type.

    """
    try:
        classify(item)
    except TypeError as error:
        raise TypeError(f'{item} is not a compatible type') from error
    self._merge(item, **kwargs)

subset(include=None, exclude=None)

Returns a new Composite with a subset of the stored nodes.

All edges will be removed that include any nodes that are not part of the new composite data structure.

Any extra attributes that are part of a Composite (or a subclass) are maintained in the returned composite data structure.

Parameters:

Name Type Description Default
include Hashable | Sequence[Hashable] | None

node(s) which should be included in the new composite data structure. If None, all nodes are included (except those in exclude). Defaults to None.

None
exclude Hashable | Sequence[Hashable] | None

node(s) which should not be included in the new composite data structure. Defaults to None.

None

Raises:

Type Description
ValueError

if include and exclude are both None or if any node in include or exclude is not in the stored composite data structure.

Returns:

Type Description
Composite

Composite with only selected nodes and edges.

Source code in src/holden/base.py
def subset(
    self,
    include: Hashable | Sequence[Hashable] | None = None,
    exclude: Hashable | Sequence[Hashable] | None = None) -> Composite:
    """Returns a new Composite with a subset of the stored nodes.

    All edges will be removed that include any nodes that are not part of
    the new composite data structure.

    Any extra attributes that are part of a Composite (or a subclass) are
    maintained in the returned composite data structure.

    Args:
        include: node(s) which should be included in the new composite data
            structure. If `None`, all nodes are included (except those in
            `exclude`). Defaults to `None`.
        exclude: node(s) which should not be included in the new composite
            data structure. Defaults to `None`.

    Raises:
        ValueError: if `include` and `exclude` are both `None` or if any
            node in `include` or `exclude` is not in the stored composite
            data structure.

    Returns:
       Composite with only selected nodes and edges.

    """
    if include is None and exclude is None:
        raise ValueError('Either include or exclude must not be None')
    included = None if include is None else utilities._listify(include)
    excluded = utilities._listify(exclude)
    for label, values in (('include', included), ('exclude', excluded)):
        missing = [i for i in values or [] if i not in self]
        if missing:
            raise ValueError(
                f'Some values in {label} are not in the composite data '
                f'structure: {missing}')
    return self._subset(included, excluded)

Directed() dataclass

Bases: ABC

Mixin for directed graph data structures.

The methods of Directed work for any form of graph (or composite data structure) that holden recognizes. Subclasses may override the methods and properties to use more efficient approaches for their internal storage formats.

endpoint property

Returns the endpoints of the stored composite.

root property

Returns the roots of the stored composite.

append(item, attachment=None, **kwargs)

Appends item to the endpoint(s) of the stored composite.

Appending merges item into the stored composite and creates an edge from every endpoint of the stored composite to every root of item.

Parameters:

Name Type Description Default
item Any

a node, another composite data structure, or a raw form of a composite data structure to add to the stored composite.

required
attachment Hashable | MutableSequence[Hashable] | None

the endpoint or endpoints to attach item to. If None, all existing endpoints of the stored composite are used.

None
**kwargs Any

additional keyword arguments passed to add.

{}

Raises:

Type Description
TypeError

if item is neither a recognized composite form nor a node.

Source code in src/holden/traits.py
def append(
    self,
    item: Any,
    attachment: Hashable | MutableSequence[Hashable] | None = None,
    **kwargs: Any) -> None:
    """Appends `item` to the endpoint(s) of the stored composite.

    Appending merges `item` into the stored composite and creates an edge
    from every endpoint of the stored composite to every root of `item`.

    Args:
        item: a node, another composite data structure, or a raw form of a
            composite data structure to add to the stored composite.
        attachment: the endpoint or endpoints to attach `item` to.
            If `None`, all existing endpoints of the stored composite are
            used.
        **kwargs: additional keyword arguments passed to `add`.

    Raises:
        TypeError: if `item` is neither a recognized composite form nor a
            node.

    """
    graph: Any = self
    if attachment is None:
        endpoints = self.endpoint
    else:
        endpoints = utilities._listify(attachment)
    other = _to_adjacency(item)
    if other is not None:
        graph.merge(item = item)
        for endpoint in endpoints:
            for root in report.get_roots_adjacency(other):
                if endpoint != root:
                    graph.connect((endpoint, root))
    elif check.is_node(item):
        self._include(item, endpoints, **kwargs)
        for endpoint in endpoints:
            if endpoint != item:
                graph.connect((endpoint, item))
    else:
        raise TypeError('item is not a recognized graph or node type')

prepend(item, **kwargs)

Prepends item to the root(s) of the stored composite.

Prepending merges item into the stored composite and creates an edge from every endpoint of item to every root of the stored composite.

Parameters:

Name Type Description Default
item Any

a node, another composite data structure, or a raw form of a composite data structure to add to the stored composite.

required
**kwargs Any

additional keyword arguments passed to add.

{}

Raises:

Type Description
TypeError

if item is neither a recognized composite form nor a node.

Source code in src/holden/traits.py
def prepend(self, item: Any, **kwargs: Any) -> None:
    """Prepends `item` to the root(s) of the stored composite.

    Prepending merges `item` into the stored composite and creates an edge
    from every endpoint of `item` to every root of the stored composite.

    Args:
        item: a node, another composite data structure, or a raw form of a
            composite data structure to add to the stored composite.
        **kwargs: additional keyword arguments passed to `add`.

    Raises:
        TypeError: if `item` is neither a recognized composite form nor a
            node.

    """
    graph: Any = self
    roots = self.root
    other = _to_adjacency(item)
    if other is not None:
        graph.merge(item = item)
        for root in roots:
            for endpoint in report.get_endpoints_adjacency(other):
                if endpoint != root:
                    graph.connect((endpoint, root))
    elif check.is_node(item):
        self._include(item, roots, **kwargs)
        for root in roots:
            if root != item:
                graph.connect((item, root))
    else:
        raise TypeError('item is not a recognized graph or node type')

walk(start=None, stop=None)

Returns all paths in the stored composite from start to stop.

Parameters:

Name Type Description Default
start Hashable | list[Hashable] | None

node (or list of nodes) to start paths from. If it is None, paths start from every root. Defaults to None.

None
stop Hashable | list[Hashable] | None

node (or list of nodes) to stop paths at. If it is None, paths stop at every endpoint. Defaults to None.

None

Returns:

Type Description
RawParallel

A list of possible paths (each path is a list of nodes) from start to stop.

Source code in src/holden/traits.py
def walk(
    self,
    start: Hashable | list[Hashable] | None = None,
    stop: Hashable | list[Hashable] | None = None) -> kinds.RawParallel:
    """Returns all paths in the stored composite from `start` to `stop`.

    Args:
        start: node (or list of nodes) to start paths from. If it is `None`,
            paths start from every root. Defaults to `None`.
        stop: node (or list of nodes) to stop paths at. If it is `None`,
            paths stop at every endpoint. Defaults to `None`.

    Returns:
        A list of possible paths (each path is a list of nodes) from `start`
            to `stop`.

    """
    return traverse.walk(self, start = start, stop = stop)

Edge(start, stop) dataclass

Bases: Sequence

Base class for an edge in a graph structure.

Edges are not required for most of the base graph classes in holden. But they can be used by subclasses of those base classes for more complex data structures.

An Edge is a drop-in replacement for a tuple of 2 nodes. It can be indexed, iterated over, unpacked, and compared to a tuple.

Parameters:

Name Type Description Default
start Hashable

starting point for the edge.

required
stop Hashable

stopping point for the edge.

required

stop instance-attribute

Dunder Methods

Edges(contents=list()) dataclass

Bases: Graph, Listing

Base class for edge-list graphs.

An edge list can only store nodes that are part of an edge. As a result, a graph is built up by adding edges with connect (which adds the nodes of the edge, if necessary) or with add (which only accepts edges). A node with no edges cannot be added on its own.

Parameters:

Name Type Description Default
contents MutableSequence[Edge | tuple[Hashable, Hashable]]

Listing of edges. Defaults to an empty list.

list()

nodes property

Returns a set of all nodes in the graph.

Exportable() dataclass

Bases: ABC

Mixin for exporting graphs to other formats.

to_dot(path=None, name=None, settings=None)

Converts the stored composite to a dot format.

Parameters:

Name Type Description Default
path str | Path | None

path to export to. Defaults to None.

None
name str | None

name to put in the dot str. Defaults to the snake case name of the class.

None
settings dict[str, Any] | None

any global settings to add to the dot graph. Defaults to None.

None

Returns:

Type Description
str

Composite object in graphviz dot format.

Source code in src/holden/traits.py
def to_dot(
    self,
    path: str | pathlib.Path | None = None,
    name: str | None = None,
    settings: dict[str, Any] | None = None) -> str:
    """Converts the stored composite to a dot format.

    Args:
        path: path to export to. Defaults to `None`.
        name: name to put in the dot `str`. Defaults to the snake case name
            of the class.
        settings: any global settings to add to the dot graph. Defaults to
            `None`.

    Returns:
        Composite object in graphviz dot format.

    """
    name = name or utilities._namify(self)
    return export.to_dot(
        item = self, path = path, name = name or 'holden',
            settings = settings)

to_mermaid(path=None, name=None, settings=None)

Converts the stored composite to a mermaid format.

Parameters:

Name Type Description Default
path str | Path | None

path to export to. Defaults to None.

None
name str | None

name to put in the mermaid str. Defaults to the snake case name of the class.

None
settings dict[str, Any] | None

any global settings to add to the mermaid graph. Defaults to None.

None

Returns:

Type Description
str

Composite object in mermaid format.

Source code in src/holden/traits.py
def to_mermaid(
    self,
    path: str | pathlib.Path | None = None,
    name: str | None = None,
    settings: dict[str, Any] | None = None) -> str:
    """Converts the stored composite to a mermaid format.

    Args:
        path: path to export to. Defaults to `None`.
        name: name to put in the mermaid `str`. Defaults to the snake case
            name of the class.
        settings: any global settings to add to the mermaid graph. Defaults
            to `None`.

    Returns:
        Composite object in mermaid format.

    """
    name = name or utilities._namify(self)
    return export.to_mermaid(
        item = self, path = path, name = name or 'holden',
            settings = settings)

Forms() dataclass

Bases: Registrar

Registry of composite data structures.

Attributes:

Name Type Description
registry GenericDict

stores classes to be used in item construction. Keys are the snake case names of the classes. Defaults to an empty dict.

registry = {} class-attribute

Public Methods

classify(item) classmethod

Determines which form of composite that item is.

There is no difference between this classmethod and the classify function.

Parameters:

Name Type Description Default
item object

object to classify.

required

Returns:

Type Description
str

Name of form that item is.

Source code in src/holden/base.py
@classmethod
def classify(cls, item: object) -> str:
    """Determines which form of composite that `item` is.

    There is no difference between this classmethod and the `classify`
    function.

    Args:
        item: object to classify.

    Returns:
        Name of form that `item` is.

    """
    return classify(item)

register(item, name=None) classmethod

Adds item to registry.

The key assigned for storing item is determined using the _namify function if name is not passed.

Parameters:

Name Type Description Default
item type[Composite]

class to register.

required
name str | None

key to use for storing item. Defaults to None.

None
Source code in src/holden/base.py
@classmethod
def register(cls, item: type[Composite], name: str | None = None) -> None:
    """Adds `item` to `registry`.

    The key assigned for storing `item` is determined using the `_namify`
    function if `name` is not passed.

    Args:
        item: class to register.
        name: key to use for storing `item`. Defaults to `None`.

    """
    name = name or utilities._namify(item)
    cls.registry[name] = item

transform(item, output, *, raise_same_error=True) classmethod

General transform method that will call appropriate transformer.

Unlike the transform function, this method will return a Composite wrapped in the form type stored in the Forms registry (as opposed to the raw type). So, if output is edges, this method will return an edge list in the Edges class. In contrast, the transform function will return the structural type of an edge list without using the Edges class. The rest of the logic is identical between the function and method.

Parameters:

Name Type Description Default
item Any

composite data structure or raw form to transform.

required
output str

name of form to transform item to.

required
raise_same_error bool | None

whether to raise an error if the form of item is the same as output. If True, a ValueError will be raised. If False, item will be returned without any change. Defaults to True.

True

Raises:

Type Description
ValueError

if the form of item is the same as output and raise_same_error is True or if output is not a registered form.

Returns:

Type Description
Any

Transformed composite data structure.

Source code in src/holden/base.py
@classmethod
def transform(
    cls,
    item: Any,
    output: str,
    *,
    raise_same_error: bool | None = True) -> Any:
    """General transform method that will call appropriate transformer.

    Unlike the `transform` function, this method will return a Composite
    wrapped in the form type stored in the `Forms` registry (as opposed to
    the raw type). So, if `output` is `edges`, this method will return an
    edge list in the `Edges` class. In contrast, the `transform` function
    will return the structural type of an edge list without using the
    `Edges` class. The rest of the logic is identical between the function
    and method.

    Args:
        item: composite data structure or raw form to transform.
        output: name of form to transform `item` to.
        raise_same_error: whether to raise an error if the form of `item`
            is the same as `output`. If `True`, a `ValueError` will be
            raised. If `False`, `item` will be returned without any change.
            Defaults to `True`.

    Raises:
        ValueError: if the form of `item` is the same as `output` and
            `raise_same_error` is `True` or if `output` is not a registered
            form.

    Returns:
        Transformed composite data structure.

    """
    form = cls.classify(item)
    if form == output:
        if raise_same_error:
            raise ValueError('The passed item and output are the same type')
        return item
    raw = _to_raw(item, output)
    return _wrap(cls.registry[output], output, raw)

Fungible() dataclass

Bases: ABC

Mixin for composite data structures that can be transformed.

Every form can be accessed as a property. The property returns an instance of the default class for that form (see holden.Forms). If the composite is already that form, the composite itself is returned. Every form can also be used to create a new instance using the from_{form} class methods.

adjacency property

Returns the stored composite as an Adjacency.

edges property

Returns the stored composite as an Edges.

matrix property

Returns the stored composite as a Matrix.

parallel property

Returns the stored composite as a Parallel.

serial property

Returns the stored composite as a Serial.

from_adjacency(item) classmethod

Creates a composite data structure from an adjacency list.

Parameters:

Name Type Description Default
item Adjacency | RawAdjacency

an Adjacency or a raw adjacency list.

required

Returns:

Type Description
Self

New instance of this class derived from item.

Source code in src/holden/traits.py
@classmethod
def from_adjacency(
    cls, item: graphs.Adjacency | kinds.RawAdjacency) -> Self:
    """Creates a composite data structure from an adjacency list.

    Args:
        item: an `Adjacency` or a raw adjacency list.

    Returns:
        New instance of this class derived from `item`.

    """
    return cls._from(item, 'adjacency')

from_edges(item) classmethod

Creates a composite data structure from an edge list.

Parameters:

Name Type Description Default
item Edges | RawEdges

an Edges or a raw edge list.

required

Returns:

Type Description
Self

New instance of this class derived from item.

Source code in src/holden/traits.py
@classmethod
def from_edges(
    cls, item: graphs.Edges | kinds.RawEdges) -> Self:
    """Creates a composite data structure from an edge list.

    Args:
        item: an `Edges` or a raw edge list.

    Returns:
        New instance of this class derived from `item`.

    """
    return cls._from(item, 'edges')

from_matrix(item) classmethod

Creates a composite data structure from an adjacency matrix.

Parameters:

Name Type Description Default
item Matrix | RawMatrix

a Matrix or a raw adjacency matrix (a tuple of the matrix and a list of labels for its nodes).

required

Returns:

Type Description
Self

New instance of this class derived from item.

Source code in src/holden/traits.py
@classmethod
def from_matrix(
    cls, item: graphs.Matrix | kinds.RawMatrix) -> Self:
    """Creates a composite data structure from an adjacency matrix.

    Args:
        item: a `Matrix` or a raw adjacency matrix (a `tuple` of the matrix
            and a list of labels for its nodes).

    Returns:
        New instance of this class derived from `item`.

    """
    return cls._from(item, 'matrix')

from_parallel(item) classmethod

Creates a composite data structure from a parallel structure.

Parameters:

Name Type Description Default
item Parallel | RawParallel

a Parallel or a raw list of lists of nodes.

required

Returns:

Type Description
Self

New instance of this class derived from item.

Source code in src/holden/traits.py
@classmethod
def from_parallel(
    cls, item: composites.Parallel | kinds.RawParallel) -> Self:
    """Creates a composite data structure from a parallel structure.

    Args:
        item: a `Parallel` or a raw list of lists of nodes.

    Returns:
        New instance of this class derived from `item`.

    """
    return cls._from(item, 'parallel')

from_serial(item) classmethod

Creates a composite data structure from a serial structure.

Parameters:

Name Type Description Default
item Serial | RawSerial

a Serial or a raw list of nodes.

required

Returns:

Type Description
Self

New instance of this class derived from item.

Source code in src/holden/traits.py
@classmethod
def from_serial(cls, item: composites.Serial | kinds.RawSerial) -> Self:
    """Creates a composite data structure from a serial structure.

    Args:
        item: a `Serial` or a raw list of nodes.

    Returns:
        New instance of this class derived from `item`.

    """
    return cls._from(item, 'serial')

Graph(contents=None) dataclass

Bases: Composite, ABC

Base class for holden graphs.

Graph adds the requirements of _connect and _disconnect methods in addition to the requirements of Composite.

Parameters:

Name Type Description Default
contents Collection[Any] | None

stored nodes, node labels, edges, or edge labels. Subclasses should narrow the type for contents based on the internal storage format used.

None

connect(item, **kwargs)

Adds edge to the stored graph.

Parameters:

Name Type Description Default
item Edge | tuple[Hashable, Hashable]

edge to add to the stored graph.

required
**kwargs Any

additional keyword arguments.

{}

Raises:

Type Description
TypeError

if item is not an edge type.

ValueError

if the ends of the item are the same or if one of the edge ends does not currently exist in the stored graph.

Source code in src/holden/base.py
def connect(
    self, item: Edge | tuple[Hashable, Hashable], **kwargs: Any) -> None:
    """Adds edge to the stored graph.

    Args:
        item: edge to add to the stored graph.
        **kwargs: additional keyword arguments.

    Raises:
        TypeError: if `item` is not an edge type.
        ValueError: if the ends of the item are the same or if one of the
            edge ends does not currently exist in the stored graph.

    """
    if not check.is_edge(item):
        raise TypeError(f'{item} is not an edge type')
    if item[0] == item[1]:
        raise ValueError(
            'The starting point of an edge cannot be the same as the '
            'ending point')
    if not self._implicit_nodes:
        if item[0] not in self:
            raise ValueError(f'{item[0]} is not in the graph')
        if item[1] not in self:
            raise ValueError(f'{item[1]} is not in the graph')
    self._connect(item, **kwargs)

disconnect(item, **kwargs)

Removes edge from the stored graph.

Parameters:

Name Type Description Default
item Edge | tuple[Hashable, Hashable]

edge to delete from the stored graph.

required
**kwargs Any

additional keyword arguments.

{}

Raises:

Type Description
TypeError

if item is not an edge type.

ValueError

if the edge does not exist in the stored graph.

Source code in src/holden/base.py
def disconnect(
    self, item: Edge | tuple[Hashable, Hashable], **kwargs: Any) -> None:
    """Removes edge from the stored graph.

    Args:
        item: edge to delete from the stored graph.
        **kwargs: additional keyword arguments.

    Raises:
        TypeError: if `item` is not an edge type.
        ValueError: if the edge does not exist in the stored graph.

    """
    if not check.is_edge(item):
        raise TypeError(f'{item} is not an edge type')
    try:
        self._disconnect(item, **kwargs)
    except (KeyError, ValueError) as error:
        message = f'The edge ({item[0]}, {item[1]}) is not in the graph'
        raise ValueError(message) from error

Labeled(name=None, contents=None) dataclass

Bases: ABC

Mixin for labeling a composite object.

Labeled objects are hashed and compared by their name, so that a str equal to the name of a labeled node can be used in place of the node.

Parameters:

Name Type Description Default
name str | None

designates the name of a class instance that is used for internal and external referencing in a composite object. If it is not passed, _namify is used to create a name. Defaults to None.

None
contents Any | None

any stored item(s). Defaults to None.

None

contents = None class-attribute instance-attribute

Initialization Methods

Matrix(contents=list(), labels=list()) dataclass

Bases: Graph, Listing

Base class for adjacency-matrix graphs.

Any non-zero value in the matrix is an edge from the node of the row to the node of the column.

Parameters:

Name Type Description Default
contents MutableSequence[MutableSequence[float]]

a list of list of numbers indicating edges between nodes in the matrix. Defaults to an empty list.

list()
labels MutableSequence[Hashable]

names of nodes in the matrix. The label at each index corresponds to the row and column at that index. Defaults to an empty list.

list()

Raises:

Type Description
ValueError

if the matrix is not square or its size does not match the number of labels.

labels = dataclasses.field(default_factory=list) class-attribute instance-attribute

Initialization Methods

nodes property

Returns a set of all nodes in the graph.

Node(contents=None) dataclass

Bases: Hashable

Vertex wrapper to provide hashability to any object.

Node acts a basic wrapper for any item stored in a graph structure.

Parameters:

Name Type Description Default
contents Any | None

any stored item(s). Defaults to None.

None

contents = None class-attribute instance-attribute

Initialization Methods

Parallel(contents=list()) dataclass

Bases: Composite, Directed, Fungible, Exportable, Listing

Base class for a list of serial composites.

Each path in a Parallel is a list of nodes (or a Serial) that goes from one of its roots to one of its endpoints. A Parallel is Directed, Fungible, and Exportable.

Parameters:

Name Type Description Default
contents MutableSequence[Serial | MutableSequence[Hashable]]

Listing of Serial instances (or lists of nodes). Defaults to an empty list.

list()

contents = dataclasses.field(default_factory=list) class-attribute instance-attribute

Properties

nodes property

Returns a set of all nodes in the stored composite.

add(item, **kwargs)

Adds a path or a node to the stored composite.

Parameters:

Name Type Description Default
item Any

a non-empty list of nodes (or a Serial) to add as a new path or a single node to add as a new path of one node.

required
**kwargs Any

additional keyword arguments.

{}

Raises:

Type Description
ValueError

if item is an empty or otherwise invalid path or a node that is already in the stored composite.

TypeError

if item is not a node.

Source code in src/holden/composites.py
def add(self, item: Any, **kwargs: Any) -> None:
    """Adds a path or a node to the stored composite.

    Args:
        item: a non-empty list of nodes (or a `Serial`) to add as a new
            path or a single node to add as a new path of one node.
        **kwargs: additional keyword arguments.

    Raises:
        ValueError: if `item` is an empty or otherwise invalid path or a
            node that is already in the stored composite.
        TypeError: if `item` is not a node.

    """
    if isinstance(item, MutableSequence):
        if not item or not check.is_serial(item):
            raise ValueError('A path must be a non-empty list of nodes')
        self.contents.append(item)
        return
    super().add(item, **kwargs)

append(item, attachment=None, **kwargs)

Appends item to the end of every path.

If item has more than one path, every existing path is followed by every path of item.

Parameters:

Name Type Description Default
item Any

node, path, or other composite data structure (or raw form).

required
attachment Hashable | MutableSequence[Hashable] | None

unused. It is accepted so that the method matches Directed.append, because paths always attach at their ends.

None
**kwargs Any

additional keyword arguments.

{}
Source code in src/holden/composites.py
def append(
    self,
    item: Any,
    attachment: Hashable | MutableSequence[Hashable] | None = None,  # noqa: ARG002
    **kwargs: Any) -> None:
    """Appends `item` to the end of every path.

    If `item` has more than one path, every existing path is followed by
    every path of `item`.

    Args:
        item: node, path, or other composite data structure (or raw form).
        attachment: unused. It is accepted so that the method matches
            `Directed.append`, because paths always attach at their ends.
        **kwargs: additional keyword arguments.

    """
    other = _to_paths(item)
    if not other:
        return
    if not self.contents:
        self.contents.extend(other)
        return
    self.contents[:] = [
        [*utilities._rawify(path), *new_path]
        for path in self.contents
        for new_path in other]

prepend(item, **kwargs)

Prepends item to the start of every path.

If item has more than one path, every path of item precedes every existing path.

Parameters:

Name Type Description Default
item Any

node, path, or other composite data structure (or raw form).

required
**kwargs Any

additional keyword arguments.

{}
Source code in src/holden/composites.py
def prepend(self, item: Any, **kwargs: Any) -> None:
    """Prepends `item` to the start of every path.

    If `item` has more than one path, every path of `item` precedes every
    existing path.

    Args:
        item: node, path, or other composite data structure (or raw form).
        **kwargs: additional keyword arguments.

    """
    other = _to_paths(item)
    if not other:
        return
    if not self.contents:
        self.contents.extend(other)
        return
    self.contents[:] = [
        [*new_path, *utilities._rawify(path)]
        for new_path in other
        for path in self.contents]

Serial(contents=list()) dataclass

Bases: Composite, Directed, Fungible, Exportable, DictList

Base class for serial composites.

A Serial is a single path of nodes. It is Directed, Fungible, and Exportable.

Parameters:

Name Type Description Default
contents MutableSequence[Hashable]

list of nodes. Defaults to an empty list.

list()

contents = dataclasses.field(default_factory=list) class-attribute instance-attribute

Properties

nodes property

Returns a set of all nodes in the stored composite.

append(item, attachment=None, **kwargs)

Appends item to the end of the stored composite.

Parameters:

Name Type Description Default
item Any

a node, another composite data structure (or raw form) whose nodes are added in order.

required
attachment Hashable | MutableSequence[Hashable] | None

unused. It is accepted so that the method matches Directed.append, because nodes are always added at the end.

None
**kwargs Any

additional keyword arguments.

{}

Raises:

Type Description
TypeError

if item is neither a recognized composite form nor a node.

Source code in src/holden/composites.py
def append(
    self,
    item: Any,
    attachment: Hashable | MutableSequence[Hashable] | None = None,  # noqa: ARG002
    **kwargs: Any) -> None:
    """Appends `item` to the end of the stored composite.

    Args:
        item: a node, another composite data structure (or raw form) whose
            nodes are added in order.
        attachment: unused. It is accepted so that the method matches
            `Directed.append`, because nodes are always added at the end.
        **kwargs: additional keyword arguments.

    Raises:
        TypeError: if `item` is neither a recognized composite form nor a
            node.

    """
    self.contents.extend(_to_nodes(item))

prepend(item, **kwargs)

Prepends item to the start of the stored composite.

Parameters:

Name Type Description Default
item Any

a node, another composite data structure (or raw form) whose nodes are added in order.

required
**kwargs Any

additional keyword arguments.

{}

Raises:

Type Description
TypeError

if item is neither a recognized composite form nor a node.

Source code in src/holden/composites.py
def prepend(self, item: Any, **kwargs: Any) -> None:
    """Prepends `item` to the start of the stored composite.

    Args:
        item: a node, another composite data structure (or raw form) whose
            nodes are added in order.
        **kwargs: additional keyword arguments.

    Raises:
        TypeError: if `item` is neither a recognized composite form nor a
            node.

    """
    self.contents[:0] = _to_nodes(item)

Storage(library=dict()) dataclass

Bases: ABC

Mixin for storing data for nodes separate from the graph structure.

The graph stores lightweight nodes (such as str labels). The data or objects that those labels represent are stored in library, so they can be reused without being part of the graph structure.

Parameters:

Name Type Description Default
library MutableMapping[Hashable, Any]

mapping of nodes to stored data. Defaults to an empty dict.

dict()

library = dataclasses.field(default_factory=dict) class-attribute instance-attribute

Public Methods

discard(node)

Removes the data stored for node, if there is any.

Parameters:

Name Type Description Default
node Hashable

node to remove stored data for.

required
Source code in src/holden/traits.py
def discard(self, node: Hashable) -> None:
    """Removes the data stored for `node`, if there is any.

    Args:
        node: node to remove stored data for.

    """
    self.library.pop(node, None)

retrieve(node)

Returns the data stored for node.

Parameters:

Name Type Description Default
node Hashable

node to return the stored data of.

required

Raises:

Type Description
KeyError

if there is no data stored for node.

Returns:

Type Description
Any

The data stored for node.

Source code in src/holden/traits.py
def retrieve(self, node: Hashable) -> Any:
    """Returns the data stored for `node`.

    Args:
        node: node to return the stored data of.

    Raises:
        KeyError: if there is no data stored for `node`.

    Returns:
        The data stored for `node`.

    """
    try:
        return self.library[node]
    except KeyError as error:
        raise KeyError(f'There is no data stored for {node}') from error

store(node, item)

Stores item as the data for node.

Parameters:

Name Type Description Default
node Hashable

node to store data for. If this instance is a composite data structure, the node must be in it.

required
item Any

data to store.

required

Raises:

Type Description
KeyError

if this instance is a composite data structure and node is not in it.

Source code in src/holden/traits.py
def store(self, node: Hashable, item: Any) -> None:
    """Stores `item` as the data for `node`.

    Args:
        node: node to store data for. If this instance is a composite data
            structure, the node must be in it.
        item: data to store.

    Raises:
        KeyError: if this instance is a composite data structure and `node`
            is not in it.

    """
    if isinstance(self, base.Composite) and node not in self:
        raise KeyError(f'{node} is not in the composite data structure')
    self.library[node] = item

Structural

Bases: ABCMeta

Metaclass that provides structural subtyping for composite forms.

A class with this metaclass treats a raw python object as an instance if the object has the structure of the form that the class represents. For example, isinstance({"a": {"b"}, "b": set()}, holden.Adjacency) is True because the dict is structured as an adjacency list.

System(contents=dict()) dataclass

Bases: Directed, Adjacency, Fungible, Exportable

Directed graph with unweighted edges stored as an adjacency list.

A System combines the Adjacency form with the Directed, Fungible, and Exportable traits. So, it can find roots, endpoints, and paths; be converted to and created from every other form of composite data structure; and be exported to dot and mermaid formats.

Parameters:

Name Type Description Default
contents MutableMapping[Hashable, set[Hashable]]

keys are nodes and values are sets of nodes (or hashable representations of nodes). Defaults to an empty dict.

dict()

Weighted(weight=1.0) dataclass

Bases: ABC

Mixin for weighted nodes and edges.

Parameters:

Name Type Description Default
weight float

the weight of the object. Defaults to 1.0.

1.0

weight = 1.0 class-attribute instance-attribute

Dunder Methods

add_checker(name, item)

Adds a checker to the local namespace.

This allows the function to be found by the holden.classify function and the holden.Forms.classify class method.

Parameters:

Name Type Description Default
name str

name of the checker function. It needs to be in the is_{form} format, where form is the name of the form registered in holden.Forms.

required
item Callable[[Any], bool]

callable checker which should have a single parameter, item, and return whether item is the form.

required

Raises:

Type Description
ValueError

if name does not start with "is_".

Source code in src/holden/check.py
def add_checker(name: str, item: Callable[[Any], bool]) -> None:
    """Adds a checker to the local namespace.

    This allows the function to be found by the `holden.classify` function and
    the `holden.Forms.classify` class method.

    Args:
        name: name of the checker function. It needs to be in the `is_{form}`
            format, where `form` is the name of the form registered in
            `holden.Forms`.
        item: callable checker which should have a single parameter, `item`,
            and return whether `item` is the form.

    Raises:
        ValueError: if `name` does not start with "is_".

    """
    if not name.startswith('is_'):
        raise ValueError("name must be in the 'is_{form}' format")
    globals()[name] = item

add_transformer(name, item)

Adds a transformer to this module's namespace.

This allows the function to be found by the transform function.

Parameters:

Name Type Description Default
name str

name of the transformer function. It needs to be in the {source}_to_{output} format, where source and output are names of forms in holden.Forms.

required
item Callable[[Any], Any]

callable transformer which should have a single parameter, item, which should be a composite data structure or raw form.

required

Raises:

Type Description
ValueError

if name is not in the {source}_to_{output} format.

Source code in src/holden/workshop.py
def add_transformer(name: str, item: Callable[[Any], Any]) -> None:
    """Adds a transformer to this module's namespace.

    This allows the function to be found by the `transform` function.

    Args:
        name: name of the transformer function. It needs to be in the
            `{source}_to_{output}` format, where `source` and `output` are names
            of forms in `holden.Forms`.
        item: callable transformer which should have a single parameter, `item`,
            which should be a composite data structure or raw form.

    Raises:
        ValueError: if `name` is not in the `{source}_to_{output}` format.

    """
    source, separator, output = name.partition('_to_')
    if not (source and separator and output):
        raise ValueError("name must be in the '{source}_to_{output}' format")
    globals()[name] = item

adjacency_to_edges(item)

Converts item to an edge list.

Nodes without any edges cannot be represented in an edge list and are not included in the returned edge list.

Parameters:

Name Type Description Default
item Adjacency | RawAdjacency

adjacency list to convert to an edge list.

required

Returns:

Type Description
RawEdges

Edge list derived from item.

Source code in src/holden/workshop.py
def adjacency_to_edges(
    item: graphs.Adjacency | kinds.RawAdjacency) -> kinds.RawEdges:
    """Converts `item` to an edge list.

    Nodes without any edges cannot be represented in an edge list and are not
    included in the returned edge list.

    Args:
        item: adjacency list to convert to an edge list.

    Returns:
        Edge list derived from `item`.

    """
    adjacency = utilities._rawify(item)
    return [
        (node, connection)
        for node, connections in adjacency.items()
        for connection in utilities._stabilize(connections)]

adjacency_to_matrix(item)

Converts item to an adjacency matrix.

Parameters:

Name Type Description Default
item Adjacency | RawAdjacency

adjacency list to convert to an adjacency matrix.

required

Returns:

Type Description
RawMatrix

Adjacency matrix derived from item. It is a tuple of the matrix (with a 1 for every edge and a 0 otherwise) and a list of the labels of the nodes that correspond to the rows and columns of the matrix.

Source code in src/holden/workshop.py
def adjacency_to_matrix(
    item: graphs.Adjacency | kinds.RawAdjacency) -> kinds.RawMatrix:
    """Converts `item` to an adjacency matrix.

    Args:
        item: adjacency list to convert to an adjacency matrix.

    Returns:
        Adjacency matrix derived from `item`. It is a `tuple` of the matrix
            (with a 1 for every edge and a 0 otherwise) and a list of the labels
            of the nodes that correspond to the rows and columns of the matrix.

    """
    adjacency = utilities._rawify(item)
    names = report._nodes_adjacency(adjacency)
    index = {name: i for i, name in enumerate(names)}
    matrix: list[list[float]] = [[0] * len(names) for _ in names]
    for node, connections in adjacency.items():
        for connection in connections:
            matrix[index[node]][index[connection]] = 1
    return matrix, names

adjacency_to_parallel(item)

Converts item to a parallel structure.

Parameters:

Name Type Description Default
item Adjacency | RawAdjacency

adjacency list to convert to a parallel structure.

required

Returns:

Type Description
RawParallel

Parallel structure derived from item. It has a path for every route from a root to an endpoint of item.

Source code in src/holden/workshop.py
def adjacency_to_parallel(
    item: graphs.Adjacency | kinds.RawAdjacency) -> kinds.RawParallel:
    """Converts `item` to a parallel structure.

    Args:
        item: adjacency list to convert to a parallel structure.

    Returns:
        Parallel structure derived from `item`. It has a path for every route
            from a root to an endpoint of `item`.

    """
    adjacency = utilities._rawify(item)
    paths: kinds.RawParallel = []
    for start in report.get_roots_adjacency(adjacency):
        for stop in report.get_endpoints_adjacency(adjacency):
            paths.extend(
                traverse.walk_adjacency(item = adjacency, start = start,
                    stop = stop))
    return paths

adjacency_to_serial(item)

Converts item to a serial structure.

Parameters:

Name Type Description Default
item Adjacency | RawAdjacency

adjacency list to convert to a serial structure.

required

Returns:

Type Description
RawSerial

Serial structure derived from item. If item has a single path, it is returned. Otherwise, all of the paths are concatenated in order.

Source code in src/holden/workshop.py
def adjacency_to_serial(
    item: graphs.Adjacency | kinds.RawAdjacency) -> kinds.RawSerial:
    """Converts `item` to a serial structure.

    Args:
        item: adjacency list to convert to a serial structure.

    Returns:
        Serial structure derived from `item`. If `item` has a single path, it is
            returned. Otherwise, all of the paths are concatenated in order.

    """
    return parallel_to_serial(item = adjacency_to_parallel(item = item))

classify(item)

Determines which form of composite data structure that item is.

Registered classes are checked first. If item is not an instance (or subclass) of a registered class, the structure of item is checked against each form using the is_{form} functions in the check module.

Parameters:

Name Type Description Default
item object

object to classify.

required

Raises:

Type Description
TypeError

if item is not a recognized form.

Returns:

Type Description
str

Name of form that item is.

Source code in src/holden/base.py
def classify(item: object) -> str:
    """Determines which form of composite data structure that `item` is.

    Registered classes are checked first. If `item` is not an instance (or
    subclass) of a registered class, the structure of `item` is checked against
    each form using the `is_{form}` functions in the `check` module.

    Args:
        item: object to classify.

    Raises:
        TypeError: if `item` is not a recognized form.

    Returns:
        Name of form that `item` is.

    """
    names = _ordered_forms()
    subtype = item if inspect.isclass(item) else item.__class__
    for name in names:
        if issubclass(subtype, Forms.registry[name]):
            return name
    for name in names:
        checker = getattr(check, f'is_{name}', None)
        if checker is not None and checker(item):
            return name
    raise TypeError('The passed item is not a recognized composite form')

edges_to_adjacency(item)

Converts item to an adjacency list.

Parameters:

Name Type Description Default
item Edges | RawEdges

edge list to convert to an adjacency list.

required

Returns:

Type Description
RawAdjacency

Adjacency list derived from item.

Source code in src/holden/workshop.py
def edges_to_adjacency(
    item: graphs.Edges | kinds.RawEdges) -> kinds.RawAdjacency:
    """Converts `item` to an adjacency list.

    Args:
        item: edge list to convert to an adjacency list.

    Returns:
        Adjacency list derived from `item`.

    """
    adjacency: kinds.RawAdjacency = {}
    for start, stop in utilities._rawify(item):
        adjacency.setdefault(start, set()).add(stop)
        adjacency.setdefault(stop, set())
    return adjacency

edges_to_matrix(item)

Converts item to an adjacency matrix.

Parameters:

Name Type Description Default
item Edges | RawEdges

edge list to convert to an adjacency matrix.

required

Returns:

Type Description
RawMatrix

Adjacency matrix derived from item.

Source code in src/holden/workshop.py
def edges_to_matrix(
    item: graphs.Edges | kinds.RawEdges) -> kinds.RawMatrix:
    """Converts `item` to an adjacency matrix.

    Args:
        item: edge list to convert to an adjacency matrix.

    Returns:
        Adjacency matrix derived from `item`.

    """
    return adjacency_to_matrix(item = edges_to_adjacency(item = item))

edges_to_parallel(item)

Converts item to a parallel structure.

Parameters:

Name Type Description Default
item Edges | RawEdges

edge list to convert to a parallel structure.

required

Returns:

Type Description
RawParallel

Parallel structure derived from item.

Source code in src/holden/workshop.py
def edges_to_parallel(
    item: graphs.Edges | kinds.RawEdges) -> kinds.RawParallel:
    """Converts `item` to a parallel structure.

    Args:
        item: edge list to convert to a parallel structure.

    Returns:
        Parallel structure derived from `item`.

    """
    return adjacency_to_parallel(item = edges_to_adjacency(item = item))

edges_to_serial(item)

Converts item to a serial structure.

Parameters:

Name Type Description Default
item Edges | RawEdges

edge list to convert to a serial structure.

required

Returns:

Type Description
RawSerial

Serial structure derived from item.

Source code in src/holden/workshop.py
def edges_to_serial(
    item: graphs.Edges | kinds.RawEdges) -> kinds.RawSerial:
    """Converts `item` to a serial structure.

    Args:
        item: edge list to convert to a serial structure.

    Returns:
        Serial structure derived from `item`.

    """
    return adjacency_to_serial(item = edges_to_adjacency(item = item))

get_base(name)

Returns the default base class for a form of composite data structure.

Parameters:

Name Type Description Default
name str

name of form to get.

required

Raises:

Type Description
KeyError

if name is not a registered form.

Returns:

Type Description
type[Composite]

Class used as the base type for the name form.

Source code in src/holden/options.py
def get_base(name: str) -> type[base.Composite]:
    """Returns the default base class for a form of composite data structure.

    Args:
        name: name of form to get.

    Raises:
        KeyError: if `name` is not a registered form.

    Returns:
        Class used as the base type for the `name` form.

    """
    try:
        return cast('type[base.Composite]', base.Forms.registry[name.lower()])
    except KeyError as error:
        raise KeyError(f'{name} is not a registered form') from error

get_endpoints(item)

Returns the endpoints in any recognized composite form.

Parameters:

Name Type Description Default
item Any

composite data structure or raw form to examine.

required

Raises:

Type Description
NotImplementedError

if there is no endpoint function for the form of item.

Returns:

Type Description
MutableSequence[Hashable]

List of endpoints.

Source code in src/holden/report.py
def get_endpoints(item: Any) -> MutableSequence[Hashable]:
    """Returns the endpoints in any recognized composite form.

    Args:
        item: composite data structure or raw form to examine.

    Raises:
        NotImplementedError: if there is no endpoint function for the form of
            `item`.

    Returns:
        List of endpoints.

    """
    return _dispatch('endpoints', item)

get_endpoints_adjacency(item)

Returns the endpoints in item.

Parameters:

Name Type Description Default
item Adjacency | RawAdjacency

adjacency list object to examine.

required

Returns:

Type Description
MutableSequence[Hashable]

List of endpoints in the order they appear in item.

Source code in src/holden/report.py
def get_endpoints_adjacency(
    item: graphs.Adjacency | kinds.RawAdjacency) -> MutableSequence[Hashable]:
    """Returns the endpoints in `item`.

    Args:
        item: adjacency list object to examine.

    Returns:
        List of endpoints in the order they appear in `item`.

    """
    item = utilities._rawify(item)
    return [n for n in _nodes_adjacency(item) if not item.get(n)]

get_endpoints_edges(item)

Returns the endpoints in item.

Parameters:

Name Type Description Default
item Edges | RawEdges

edge list object to examine.

required

Returns:

Type Description
MutableSequence[Hashable]

List of endpoints.

Source code in src/holden/report.py
def get_endpoints_edges(
    item: graphs.Edges | kinds.RawEdges) -> MutableSequence[Hashable]:
    """Returns the endpoints in `item`.

    Args:
        item: edge list object to examine.

    Returns:
        List of endpoints.

    """
    return get_endpoints_adjacency(
        item = workshop.edges_to_adjacency(item = item))

get_endpoints_matrix(item)

Returns the endpoints in item.

Parameters:

Name Type Description Default
item Matrix | RawMatrix

adjacency matrix object to examine.

required

Returns:

Type Description
MutableSequence[Hashable]

List of endpoints.

Source code in src/holden/report.py
def get_endpoints_matrix(
    item: graphs.Matrix | kinds.RawMatrix) -> MutableSequence[Hashable]:
    """Returns the endpoints in `item`.

    Args:
        item: adjacency matrix object to examine.

    Returns:
        List of endpoints.

    """
    return get_endpoints_adjacency(
        item = workshop.matrix_to_adjacency(item = item))

get_endpoints_parallel(item)

Returns the endpoints in item.

Parameters:

Name Type Description Default
item Parallel | RawParallel

parallel object to examine.

required

Returns:

Type Description
MutableSequence[Hashable]

List of the last node of each path (without duplicates).

Source code in src/holden/report.py
def get_endpoints_parallel(
    item: composites.Parallel | kinds.RawParallel) -> MutableSequence[Hashable]:
    """Returns the endpoints in `item`.

    Args:
        item: parallel object to examine.

    Returns:
        List of the last node of each path (without duplicates).

    """
    item = utilities._rawify(item)
    return list(dict.fromkeys(path[-1] for path in map(_path, item) if path))

get_endpoints_serial(item)

Returns the endpoints in item.

Parameters:

Name Type Description Default
item Serial | RawSerial

serial object to examine.

required

Returns:

Type Description
MutableSequence[Hashable]

List of the last node in item or an empty list if item is empty.

Source code in src/holden/report.py
def get_endpoints_serial(
    item: composites.Serial | kinds.RawSerial) -> MutableSequence[Hashable]:
    """Returns the endpoints in `item`.

    Args:
        item: serial object to examine.

    Returns:
        List of the last node in `item` or an empty list if `item` is empty.

    """
    item = utilities._rawify(item)
    return [item[-1]] if item else []

get_roots(item)

Returns the roots in any recognized composite form.

Parameters:

Name Type Description Default
item Any

composite data structure or raw form to examine.

required

Raises:

Type Description
NotImplementedError

if there is no root function for the form of item.

Returns:

Type Description
MutableSequence[Hashable]

List of roots.

Source code in src/holden/report.py
def get_roots(item: Any) -> MutableSequence[Hashable]:
    """Returns the roots in any recognized composite form.

    Args:
        item: composite data structure or raw form to examine.

    Raises:
        NotImplementedError: if there is no root function for the form of
            `item`.

    Returns:
        List of roots.

    """
    return _dispatch('roots', item)

get_roots_adjacency(item)

Returns the roots in item.

Parameters:

Name Type Description Default
item Adjacency | RawAdjacency

adjacency list object to examine.

required

Returns:

Type Description
MutableSequence[Hashable]

List of roots in the order they appear in item.

Source code in src/holden/report.py
def get_roots_adjacency(
    item: graphs.Adjacency | kinds.RawAdjacency) -> MutableSequence[Hashable]:
    """Returns the roots in `item`.

    Args:
        item: adjacency list object to examine.

    Returns:
        List of roots in the order they appear in `item`.

    """
    item = utilities._rawify(item)
    stops: set[Hashable] = set()
    for connections in item.values():
        stops.update(connections)
    return [n for n in _nodes_adjacency(item) if n not in stops]

get_roots_edges(item)

Returns the roots in item.

Parameters:

Name Type Description Default
item Edges | RawEdges

edge list object to examine.

required

Returns:

Type Description
MutableSequence[Hashable]

List of roots.

Source code in src/holden/report.py
def get_roots_edges(
    item: graphs.Edges | kinds.RawEdges) -> MutableSequence[Hashable]:
    """Returns the roots in `item`.

    Args:
        item: edge list object to examine.

    Returns:
        List of roots.

    """
    return get_roots_adjacency(item = workshop.edges_to_adjacency(item = item))

get_roots_matrix(item)

Returns the roots in item.

Parameters:

Name Type Description Default
item Matrix | RawMatrix

adjacency matrix object to examine.

required

Returns:

Type Description
MutableSequence[Hashable]

List of roots.

Source code in src/holden/report.py
def get_roots_matrix(
    item: graphs.Matrix | kinds.RawMatrix) -> MutableSequence[Hashable]:
    """Returns the roots in `item`.

    Args:
        item: adjacency matrix object to examine.

    Returns:
        List of roots.

    """
    return get_roots_adjacency(item = workshop.matrix_to_adjacency(item = item))

get_roots_parallel(item)

Returns the roots in item.

Parameters:

Name Type Description Default
item Parallel | RawParallel

parallel object to examine.

required

Returns:

Type Description
MutableSequence[Hashable]

List of the first node of each path (without duplicates).

Source code in src/holden/report.py
def get_roots_parallel(
    item: composites.Parallel | kinds.RawParallel) -> MutableSequence[Hashable]:
    """Returns the roots in `item`.

    Args:
        item: parallel object to examine.

    Returns:
        List of the first node of each path (without duplicates).

    """
    item = utilities._rawify(item)
    return list(dict.fromkeys(path[0] for path in map(_path, item) if path))

get_roots_serial(item)

Returns the roots in item.

Parameters:

Name Type Description Default
item Serial | RawSerial

serial object to examine.

required

Returns:

Type Description
MutableSequence[Hashable]

List of the first node in item or an empty list if item is empty.

Source code in src/holden/report.py
def get_roots_serial(
    item: composites.Serial | kinds.RawSerial) -> MutableSequence[Hashable]:
    """Returns the roots in `item`.

    Args:
        item: serial object to examine.

    Returns:
        List of the first node in `item` or an empty list if `item` is empty.

    """
    item = utilities._rawify(item)
    return [item[0]] if item else []

is_adjacency(item)

Returns whether item is an adjacency list.

An adjacency list is a mutable mapping where every value is a set.

Parameters:

Name Type Description Default
item object

instance to test.

required

Returns:

Type Description
bool

Whether item is an adjacency list.

Source code in src/holden/check.py
def is_adjacency(item: object) -> bool:
    """Returns whether `item` is an adjacency list.

    An adjacency list is a mutable mapping where every value is a `set`.

    Args:
        item: instance to test.

    Returns:
        Whether `item` is an adjacency list.

    """
    return isinstance(item, MutableMapping) and all(
        isinstance(connections, set) for connections in item.values())

is_composite(item)

Returns whether item is a composite data structure.

A composite data structure is any object (or class) that has add, delete, merge, and subset methods.

Parameters:

Name Type Description Default
item object

instance or class to test.

required

Returns:

Type Description
bool

Whether item is a composite data structure.

Source code in src/holden/check.py
def is_composite(item: object) -> bool:
    """Returns whether `item` is a composite data structure.

    A composite data structure is any object (or class) that has `add`,
    `delete`, `merge`, and `subset` methods.

    Args:
        item: instance or class to test.

    Returns:
        Whether `item` is a composite data structure.

    """
    return all(
        callable(getattr(item, method, None)) for method in _COMPOSITE_METHODS)

is_edge(item)

Returns whether item is an edge.

An edge is an immutable sequence (such as a tuple) of exactly 2 nodes. Mutable sequences, such as a list, are not edges so that lists can be used unambiguously for serial and parallel paths.

Parameters:

Name Type Description Default
item object

instance to test.

required

Returns:

Type Description
bool

Whether item is an edge.

Source code in src/holden/check.py
def is_edge(item: object) -> bool:
    """Returns whether `item` is an edge.

    An edge is an immutable sequence (such as a `tuple`) of exactly 2 nodes.
    Mutable sequences, such as a `list`, are not edges so that lists can be
    used unambiguously for serial and parallel paths.

    Args:
        item: instance to test.

    Returns:
        Whether `item` is an edge.

    """
    return (
        isinstance(item, Sequence)
        and not isinstance(item, str | bytes | MutableSequence)
        and len(item) == 2  # noqa: PLR2004
        and is_node(item[0])
        and is_node(item[1]))

is_edges(item)

Returns whether item is an edge list.

Parameters:

Name Type Description Default
item object

instance to test.

required

Returns:

Type Description
bool

Whether item is an edge list.

Source code in src/holden/check.py
def is_edges(item: object) -> bool:
    """Returns whether `item` is an edge list.

    Args:
        item: instance to test.

    Returns:
        Whether `item` is an edge list.

    """
    return isinstance(item, MutableSequence) and all(is_edge(i) for i in item)

is_graph(item)

Returns whether item is a graph.

A graph is a composite data structure that also has connect and disconnect methods.

Parameters:

Name Type Description Default
item object

instance or class to test.

required

Returns:

Type Description
bool

Whether item is a graph.

Source code in src/holden/check.py
def is_graph(item: object) -> bool:
    """Returns whether `item` is a graph.

    A graph is a composite data structure that also has `connect` and
    `disconnect` methods.

    Args:
        item: instance or class to test.

    Returns:
        Whether `item` is a graph.

    """
    return is_composite(item) and all(
        callable(getattr(item, method, None)) for method in _GRAPH_METHODS)

is_matrix(item)

Returns whether item is an adjacency matrix.

A raw adjacency matrix is a 2-item sequence. The first item is a mutable sequence of equal-length mutable sequences of numbers (the rows). The second item is a mutable sequence of hashable labels for the nodes. The number of rows and the length of every row must match the number of labels.

Parameters:

Name Type Description Default
item object

instance to test.

required

Returns:

Type Description
bool

Whether item is an adjacency matrix.

Source code in src/holden/check.py
def is_matrix(item: object) -> bool:
    """Returns whether `item` is an adjacency matrix.

    A raw adjacency matrix is a 2-item sequence. The first item is a mutable
    sequence of equal-length mutable sequences of numbers (the rows). The second
    item is a mutable sequence of hashable labels for the nodes. The number of
    rows and the length of every row must match the number of labels.

    Args:
        item: instance to test.

    Returns:
        Whether `item` is an adjacency matrix.

    """
    if not (isinstance(item, Sequence) and len(item) == 2):  # noqa: PLR2004
        return False
    matrix, labels = item
    if not (
        isinstance(matrix, MutableSequence)
        and isinstance(labels, MutableSequence)
        and len(matrix) == len(labels)):
        return False
    return (
        all(isinstance(label, Hashable) for label in labels)
        and all(
            isinstance(row, MutableSequence) and len(row) == len(labels)
            for row in matrix)
        and all(
            isinstance(connection, int | float)
            for row in matrix
            for connection in row))

is_node(item)

Returns whether item is a node.

Any hashable object (or class of hashable objects) can be a node.

Parameters:

Name Type Description Default
item object

instance or class to test.

required

Returns:

Type Description
bool

Whether item is a node.

Source code in src/holden/check.py
def is_node(item: object) -> bool:
    """Returns whether `item` is a node.

    Any hashable object (or class of hashable objects) can be a node.

    Args:
        item: instance or class to test.

    Returns:
        Whether `item` is a node.

    """
    if isinstance(item, type):
        return issubclass(item, Hashable)
    return isinstance(item, Hashable)

is_nodes(item)

Returns whether item is a collection of nodes.

A str or bytes is treated as a single node, not a collection.

Parameters:

Name Type Description Default
item object

instance to test.

required

Returns:

Type Description
bool

Whether item is a collection of nodes.

Source code in src/holden/check.py
def is_nodes(item: object) -> bool:
    """Returns whether `item` is a collection of nodes.

    A `str` or `bytes` is treated as a single node, not a collection.

    Args:
        item: instance to test.

    Returns:
        Whether `item` is a collection of nodes.

    """
    return isinstance(item, list | tuple | set | frozenset) and all(
        is_node(i) for i in item)

is_parallel(item)

Returns whether item is a sequence of serial paths.

Parameters:

Name Type Description Default
item object

instance to test.

required

Returns:

Type Description
bool

Whether item is a sequence of serial paths.

Source code in src/holden/check.py
def is_parallel(item: object) -> bool:
    """Returns whether `item` is a sequence of serial paths.

    Args:
        item: instance to test.

    Returns:
        Whether `item` is a sequence of serial paths.

    """
    return isinstance(item, MutableSequence) and all(is_serial(i) for i in item)

is_serial(item)

Returns whether item is a serial path.

A serial path is a mutable sequence of nodes. Because a tuple is used for edges, a tuple cannot be a node in a serial path.

Parameters:

Name Type Description Default
item object

instance to test.

required

Returns:

Type Description
bool

Whether item is a serial path.

Source code in src/holden/check.py
def is_serial(item: object) -> bool:
    """Returns whether `item` is a serial path.

    A serial path is a mutable sequence of nodes. Because a `tuple` is used for
    edges, a `tuple` cannot be a node in a serial path.

    Args:
        item: instance to test.

    Returns:
        Whether `item` is a serial path.

    """
    return isinstance(item, MutableSequence) and all(
        is_node(i) and not isinstance(i, tuple) for i in item)

matrix_to_adjacency(item)

Converts item to an adjacency list.

Any non-zero value in the matrix is treated as an edge.

Parameters:

Name Type Description Default
item Matrix | RawMatrix

adjacency matrix to convert to an adjacency list.

required

Raises:

Type Description
ValueError

if the matrix is not square or its labels do not match its size.

Returns:

Type Description
RawAdjacency

Adjacency list derived from item.

Source code in src/holden/workshop.py
def matrix_to_adjacency(
    item: graphs.Matrix | kinds.RawMatrix) -> kinds.RawAdjacency:
    """Converts `item` to an adjacency list.

    Any non-zero value in the matrix is treated as an edge.

    Args:
        item: adjacency matrix to convert to an adjacency list.

    Raises:
        ValueError: if the matrix is not square or its labels do not match its
            size.

    Returns:
        Adjacency list derived from `item`.

    """
    matrix, names = utilities._rawify(item)
    if len(matrix) != len(names) or any(
        len(row) != len(names) for row in matrix):
        raise ValueError(
            'The matrix must be square with one label for each row and column')
    adjacency: kinds.RawAdjacency = {name: set() for name in names}
    for name, row in zip(names, matrix, strict = True):
        adjacency[name].update(
            names[j] for j, connection in enumerate(row) if connection)
    return adjacency

matrix_to_edges(item)

Converts item to an edge list.

Parameters:

Name Type Description Default
item Matrix | RawMatrix

adjacency matrix to convert to an edge list.

required

Returns:

Type Description
RawEdges

Edge list derived from item. The edges are in the order of the rows and columns of the matrix.

Source code in src/holden/workshop.py
def matrix_to_edges(
    item: graphs.Matrix | kinds.RawMatrix) -> kinds.RawEdges:
    """Converts `item` to an edge list.

    Args:
        item: adjacency matrix to convert to an edge list.

    Returns:
        Edge list derived from `item`. The edges are in the order of the rows
            and columns of the matrix.

    """
    matrix, names = utilities._rawify(item)
    edges: kinds.RawEdges = []
    for i, row in enumerate(matrix):
        edges.extend(
            (names[i], names[j])
            for j, connection in enumerate(row)
            if connection)
    return edges

matrix_to_parallel(item)

Converts item to a parallel structure.

Parameters:

Name Type Description Default
item Matrix | RawMatrix

adjacency matrix to convert to a parallel structure.

required

Returns:

Type Description
RawParallel

Parallel structure derived from item.

Source code in src/holden/workshop.py
def matrix_to_parallel(
    item: graphs.Matrix | kinds.RawMatrix) -> kinds.RawParallel:
    """Converts `item` to a parallel structure.

    Args:
        item: adjacency matrix to convert to a parallel structure.

    Returns:
        Parallel structure derived from `item`.

    """
    return adjacency_to_parallel(item = matrix_to_adjacency(item = item))

matrix_to_serial(item)

Converts item to a serial structure.

Parameters:

Name Type Description Default
item Matrix | RawMatrix

adjacency matrix to convert to a serial structure.

required

Returns:

Type Description
RawSerial

Serial structure derived from item.

Source code in src/holden/workshop.py
def matrix_to_serial(
    item: graphs.Matrix | kinds.RawMatrix) -> kinds.RawSerial:
    """Converts `item` to a serial structure.

    Args:
        item: adjacency matrix to convert to a serial structure.

    Returns:
        Serial structure derived from `item`.

    """
    return adjacency_to_serial(item = matrix_to_adjacency(item = item))

parallel_to_adjacency(item)

Converts item to an adjacency list.

Parameters:

Name Type Description Default
item Parallel | RawParallel

parallel structure to convert to an adjacency list.

required

Returns:

Type Description
RawAdjacency

Adjacency list derived from item. It is the union of the adjacency lists of each path in item.

Source code in src/holden/workshop.py
def parallel_to_adjacency(
    item: composites.Parallel | kinds.RawParallel) -> kinds.RawAdjacency:
    """Converts `item` to an adjacency list.

    Args:
        item: parallel structure to convert to an adjacency list.

    Returns:
        Adjacency list derived from `item`. It is the union of the adjacency
            lists of each path in `item`.

    """
    adjacency: kinds.RawAdjacency = {}
    for serial in utilities._rawify(item):
        for key, value in serial_to_adjacency(item = serial).items():
            adjacency.setdefault(key, set()).update(value)
    return adjacency

parallel_to_edges(item)

Converts item to an edge list.

Parameters:

Name Type Description Default
item Parallel | RawParallel

parallel structure to convert to an edge list.

required

Returns:

Type Description
RawEdges

Edge list derived from item.

Source code in src/holden/workshop.py
def parallel_to_edges(
    item: composites.Parallel | kinds.RawParallel) -> kinds.RawEdges:
    """Converts `item` to an edge list.

    Args:
        item: parallel structure to convert to an edge list.

    Returns:
        Edge list derived from `item`.

    """
    return adjacency_to_edges(item = parallel_to_adjacency(item = item))

parallel_to_matrix(item)

Converts item to an adjacency matrix.

Parameters:

Name Type Description Default
item Parallel | RawParallel

parallel structure to convert to an adjacency matrix.

required

Returns:

Type Description
RawMatrix

Adjacency matrix derived from item.

Source code in src/holden/workshop.py
def parallel_to_matrix(
    item: composites.Parallel | kinds.RawParallel) -> kinds.RawMatrix:
    """Converts `item` to an adjacency matrix.

    Args:
        item: parallel structure to convert to an adjacency matrix.

    Returns:
        Adjacency matrix derived from `item`.

    """
    return adjacency_to_matrix(item = parallel_to_adjacency(item = item))

parallel_to_serial(item)

Converts item to a serial structure.

Parameters:

Name Type Description Default
item Parallel | RawParallel

parallel structure to convert to a serial structure.

required

Returns:

Type Description
RawSerial

Serial structure derived from item. If item has a single path, it is returned. Otherwise, all of the paths are concatenated in order.

Source code in src/holden/workshop.py
def parallel_to_serial(
    item: composites.Parallel | kinds.RawParallel) -> kinds.RawSerial:
    """Converts `item` to a serial structure.

    Args:
        item: parallel structure to convert to a serial structure.

    Returns:
        Serial structure derived from `item`. If `item` has a single path, it is
            returned. Otherwise, all of the paths are concatenated in order.

    """
    paths = [
        list(utilities._rawify(serial)) for serial in utilities._rawify(item)]
    return list(itertools.chain.from_iterable(paths))

serial_to_adjacency(item)

Converts item to an adjacency list.

Parameters:

Name Type Description Default
item Serial | RawSerial

serial structure to convert to an adjacency list. Each node is connected to the node that follows it. If item is a parallel structure, it is converted using parallel_to_adjacency.

required

Returns:

Type Description
RawAdjacency

Adjacency list derived from item.

Source code in src/holden/workshop.py
def serial_to_adjacency(
    item: composites.Serial | kinds.RawSerial) -> kinds.RawAdjacency:
    """Converts `item` to an adjacency list.

    Args:
        item: serial structure to convert to an adjacency list. Each node is
            connected to the node that follows it. If `item` is a parallel
            structure, it is converted using `parallel_to_adjacency`.

    Returns:
        Adjacency list derived from `item`.

    """
    raw = utilities._rawify(item)
    if raw and check.is_parallel(raw):
        return parallel_to_adjacency(item = raw)
    if not isinstance(raw, Collection) or isinstance(raw, str | bytes):
        raw = [raw]
    adjacency: kinds.RawAdjacency = {}
    for node in raw:
        adjacency.setdefault(node, set())
    for start, stop in itertools.pairwise(raw):
        adjacency[start].add(stop)
    return adjacency

serial_to_edges(item)

Converts item to an edge list.

Parameters:

Name Type Description Default
item Serial | RawSerial

serial structure to convert to an edge list.

required

Returns:

Type Description
RawEdges

Edge list derived from item.

Source code in src/holden/workshop.py
def serial_to_edges(
    item: composites.Serial | kinds.RawSerial) -> kinds.RawEdges:
    """Converts `item` to an edge list.

    Args:
        item: serial structure to convert to an edge list.

    Returns:
        Edge list derived from `item`.

    """
    return adjacency_to_edges(item = serial_to_adjacency(item = item))

serial_to_matrix(item)

Converts item to an adjacency matrix.

Parameters:

Name Type Description Default
item Serial | RawSerial

serial structure to convert to an adjacency matrix.

required

Returns:

Type Description
RawMatrix

Adjacency matrix derived from item.

Source code in src/holden/workshop.py
def serial_to_matrix(
    item: composites.Serial | kinds.RawSerial) -> kinds.RawMatrix:
    """Converts `item` to an adjacency matrix.

    Args:
        item: serial structure to convert to an adjacency matrix.

    Returns:
        Adjacency matrix derived from `item`.

    """
    return adjacency_to_matrix(item = serial_to_adjacency(item = item))

serial_to_parallel(item)

Converts item to a parallel structure.

Parameters:

Name Type Description Default
item Serial | RawSerial

serial structure to convert to a parallel structure.

required

Returns:

Type Description
RawParallel

Parallel structure derived from item with item as its only path or an empty list if item is empty.

Source code in src/holden/workshop.py
def serial_to_parallel(
    item: composites.Serial | kinds.RawSerial) -> kinds.RawParallel:
    """Converts `item` to a parallel structure.

    Args:
        item: serial structure to convert to a parallel structure.

    Returns:
        Parallel structure derived from `item` with `item` as its only path or
            an empty list if `item` is empty.

    """
    nodes = list(utilities._rawify(item))
    return [nodes] if nodes else []

set_base(name, value)

Sets default base class for a form of composite data structure.

Parameters:

Name Type Description Default
name str

name of form to set.

required
value type[Composite]

Composite subclass to use as the base type for the name form.

required

Raises:

Type Description
KeyError

if name is not a registered form.

TypeError

if value is not a Composite subclass.

Source code in src/holden/options.py
def set_base(name: str, value: type[base.Composite]) -> None:
    """Sets default base class for a form of composite data structure.

    Args:
        name: name of form to set.
        value: Composite subclass to use as the base type for the `name` form.

    Raises:
        KeyError: if `name` is not a registered form.
        TypeError: if `value` is not a Composite subclass.

    """
    name = name.lower()
    if name not in base.Forms.registry:
        raise KeyError(f'{name} is not a registered form')
    if not (isinstance(value, type) and issubclass(value, base.Composite)):
        raise TypeError('value must be a Composite subclass')
    base.Forms.registry[name] = value
    globals()[f'_BASE_{name.upper()}'] = value

to_dot(item, path=None, name='holden', settings=None)

Converts item to a dot format.

Parameters:

Name Type Description Default
item Any

composite data structure or raw form to convert to a dot format. If item is a Directed composite data structure, a directed graph is created. Otherwise, an undirected graph is created.

required
path str | Path | None

path to export item to. Defaults to None.

None
name str

name of item to put in the dot str. Defaults to "holden".

'holden'
settings dict[str, Any] | None

any global settings to add to the dot graph. Defaults to None.

None

Returns:

Type Description
str

Composite object in graphviz dot format.

Source code in src/holden/export.py
def to_dot(
    item: Any,
    path: str | pathlib.Path | None = None,
    name: str = 'holden',
    settings: dict[str, Any] | None = None) -> str:
    """Converts `item` to a dot format.

    Args:
        item: composite data structure or raw form to convert to a dot format.
            If `item` is a `Directed` composite data structure, a directed
            graph is created. Otherwise, an undirected graph is created.
        path: path to export `item` to. Defaults to `None`.
        name: name of `item` to put in the dot `str`. Defaults to "holden".
        settings: any global settings to add to the dot graph. Defaults to
            `None`.

    Returns:
        Composite object in graphviz dot format.

    """
    adjacency = base._to_raw(item, 'adjacency')
    if isinstance(item, traits.Directed):
        dot = 'digraph '
        link = _DOT_ARROW
    else:
        dot = 'graph '
        link = _CONNECTOR
    lines = [f'{dot}{_dot_id(name)} {{']
    if settings is not None:
        lines.extend(f'{key}={value};' for key, value in settings.items())
    lines.extend(
        f'{_dot_id(start)} {link} {_dot_id(stop)}'
        for start, stop in _edges(adjacency))
    lines.extend(_dot_id(node) for node in _isolated(adjacency))
    code = _LINE_BREAK.join(lines) + _LINE_BREAK + '}' + _LINE_BREAK
    if path is not None:
        _save_file(code, path)
    return code

to_mermaid(item, path=None, name='holden', settings=None)

Converts item to a mermaid format.

Parameters:

Name Type Description Default
item Any

composite data structure or raw form to convert to a mermaid format. If item is a Directed composite data structure, the edges are arrows. Otherwise, the edges are lines.

required
path str | Path | None

path to export item to. Defaults to None.

None
name str

name of item to put in the mermaid str. Defaults to "holden".

'holden'
settings dict[str, Any] | None

any global settings to add to the mermaid graph. A nested dict is written as nested yaml. Defaults to None.

None

Returns:

Type Description
str

Composite object in mermaid format.

Source code in src/holden/export.py
def to_mermaid(
    item: Any,
    path: str | pathlib.Path | None = None,
    name: str = 'holden',
    settings: dict[str, Any] | None = None) -> str:
    """Converts `item` to a mermaid format.

    Args:
        item: composite data structure or raw form to convert to a mermaid
            format. If `item` is a `Directed` composite data structure, the
            edges are arrows. Otherwise, the edges are lines.
        path: path to export `item` to. Defaults to `None`.
        name: name of `item` to put in the mermaid `str`. Defaults to "holden".
        settings: any global settings to add to the mermaid graph. A nested
            `dict` is written as nested yaml. Defaults to `None`.

    Returns:
        Composite object in mermaid format.

    """
    adjacency = base._to_raw(item, 'adjacency')
    link = _MERMAID_ARROW if isinstance(item, traits.Directed) else _CONNECTOR
    ids: dict[Hashable, str] = {}
    lines = [
        f'{_INDENT}{_mermaid_node(start, ids)} {link} '
        f'{_mermaid_node(stop, ids)}'
        for start, stop in _edges(adjacency)]
    lines.extend(
        f'{_INDENT}{_mermaid_node(node, ids)}' for node in _isolated(adjacency))
    code = _add_mermaid_settings(name, settings)
    code = f'{code}flowchart LR{_LINE_BREAK}'
    code += ''.join(f'{line}{_LINE_BREAK}' for line in lines)
    if path is not None:
        _save_file(code, path)
    return code

transform(item, output, *, raise_same_error=True)

General transform function that will call appropriate transformer.

Parameters:

Name Type Description Default
item Any

composite data structure or raw form to transform.

required
output str

name of form to transform item to.

required
raise_same_error bool | None

whether to raise an error if the form of item is the same as output. If True, a ValueError will be raised. If False, item will be returned without any change. Defaults to True.

True

Raises:

Type Description
ValueError

if the form of item is the same as output and raise_same_error is True or if output is not a registered form.

Returns:

Type Description
Any

Transformed composite data structure in its raw form (for example, a dict for adjacency).

Source code in src/holden/base.py
def transform(
    item: Any,
    output: str,
    *,
    raise_same_error: bool | None = True) -> Any:
    """General transform function that will call appropriate transformer.

    Args:
        item: composite data structure or raw form to transform.
        output: name of form to transform `item` to.
        raise_same_error: whether to raise an error if the form of `item` is
            the same as `output`. If `True`, a `ValueError` will be raised. If
            `False`, `item` will be returned without any change. Defaults to
            `True`.

    Raises:
        ValueError: if the form of `item` is the same as `output` and
            `raise_same_error` is `True` or if `output` is not a registered
            form.

    Returns:
        Transformed composite data structure in its raw form (for example, a
            `dict` for `adjacency`).

    """
    form = classify(item)
    if form == output:
        if raise_same_error:
            raise ValueError('The passed item and output are the same type')
        return item
    return _to_raw(item, output)

walk(item, start=None, stop=None)

Returns all paths in any recognized composite form.

Parameters:

Name Type Description Default
item Any

composite data structure or raw form in which to find paths.

required
start Hashable | Sequence[Hashable] | None

node (or list of nodes) to start paths from. If it is None, paths start from every root of item. Because a tuple is treated as a collection of nodes, a tuple node should be passed inside a list. Defaults to None.

None
stop Hashable | Sequence[Hashable] | None

node (or list of nodes) to stop paths at. If it is None, paths stop at every endpoint of item. Defaults to None.

None

Raises:

Type Description
NotImplementedError

if there is no walk function for the form of item.

Returns:

Type Description
RawParallel

A list of possible paths (each path is a list of nodes) from start to stop.

Source code in src/holden/traverse.py
def walk(
    item: Any,
    start: Hashable | Sequence[Hashable] | None = None,
    stop: Hashable | Sequence[Hashable] | None = None) -> kinds.RawParallel:
    """Returns all paths in any recognized composite form.

    Args:
        item: composite data structure or raw form in which to find paths.
        start: node (or list of nodes) to start paths from. If it is `None`,
            paths start from every root of `item`. Because a `tuple` is treated
            as a collection of nodes, a `tuple` node should be passed inside a
            `list`. Defaults to `None`.
        stop: node (or list of nodes) to stop paths at. If it is `None`, paths
            stop at every endpoint of `item`. Defaults to `None`.

    Raises:
        NotImplementedError: if there is no walk function for the form of
            `item`.

    Returns:
        A list of possible paths (each path is a list of nodes) from `start` to
            `stop`.

    """
    form = base.classify(item)
    function = globals().get(f'walk_{form}')
    if function is None:
        raise NotImplementedError(f'walk does not support {form} forms')
    starts = (
        report.get_roots(item) if start is None else utilities._listify(start))
    stops = (
        report.get_endpoints(item) if stop is None
            else utilities._listify(stop))
    paths: kinds.RawParallel = []
    for first in starts:
        for last in stops:
            paths.extend(function(item, first, last))
    return paths

walk_adjacency(item, start, stop, path=None)

Returns all paths in item from start to stop.

A path never visits the same node twice, so cycles cannot cause the search to run forever. Paths are returned in a deterministic order.

The code here is adapted from: https://www.python.org/doc/essays/graphs/

Parameters:

Name Type Description Default
item Adjacency | RawAdjacency

item in which to find paths.

required
start Hashable

node to start paths from.

required
stop Hashable

node to stop paths at.

required
path Sequence[Hashable] | None

nodes to place in front of every returned path. Defaults to None.

None

Returns:

Type Description
RawParallel

A list of possible paths (each path is a list of nodes) from start to stop. If start and stop are the same node, the only path is that single node.

Source code in src/holden/traverse.py
def walk_adjacency(
    item: graphs.Adjacency | kinds.RawAdjacency,
    start: Hashable,
    stop: Hashable,
    path: Sequence[Hashable] | None = None) -> kinds.RawParallel:
    """Returns all paths in `item` from `start` to `stop`.

    A path never visits the same node twice, so cycles cannot cause the search
    to run forever. Paths are returned in a deterministic order.

    The code here is adapted from: https://www.python.org/doc/essays/graphs/

    Args:
        item: item in which to find paths.
        start: node to start paths from.
        stop: node to stop paths at.
        path: nodes to place in front of every returned path. Defaults to
            `None`.

    Returns:
        A list of possible paths (each path is a list of nodes) from `start` to
            `stop`. If `start` and `stop` are the same node, the only path is
            that single node.

    """
    adjacency = utilities._rawify(item)
    paths: kinds.RawParallel = []
    stack = [[*(path or []), start]]
    while stack:
        current = stack.pop()
        node = current[-1]
        if node == stop:
            paths.append(current)
            continue
        stack.extend(
            [*current, child]
            for child in reversed(utilities._stabilize(adjacency.get(node, ())))
            if child not in current)
    return paths

walk_edges(item, start, stop, path=None)

Returns all paths in item from start to stop.

Parameters:

Name Type Description Default
item Edges | RawEdges

item in which to find paths.

required
start Hashable

node to start paths from.

required
stop Hashable

node to stop paths at.

required
path Sequence[Hashable] | None

nodes to place in front of every returned path. Defaults to None.

None

Returns:

Type Description
RawParallel

A list of possible paths (each path is a list of nodes) from start to stop.

Source code in src/holden/traverse.py
def walk_edges(
    item: graphs.Edges | kinds.RawEdges,
    start: Hashable,
    stop: Hashable,
    path: Sequence[Hashable] | None = None) -> kinds.RawParallel:
    """Returns all paths in `item` from `start` to `stop`.

    Args:
        item: item in which to find paths.
        start: node to start paths from.
        stop: node to stop paths at.
        path: nodes to place in front of every returned path. Defaults to
            `None`.

    Returns:
        A list of possible paths (each path is a list of nodes) from `start` to
            `stop`.

    """
    return walk_adjacency(
        item = workshop.edges_to_adjacency(item = item),
        start = start,
        stop = stop,
        path = path)

walk_matrix(item, start, stop, path=None)

Returns all paths in item from start to stop.

Parameters:

Name Type Description Default
item Matrix | RawMatrix

item in which to find paths.

required
start Hashable

node to start paths from.

required
stop Hashable

node to stop paths at.

required
path Sequence[Hashable] | None

nodes to place in front of every returned path. Defaults to None.

None

Returns:

Type Description
RawParallel

A list of possible paths (each path is a list of nodes) from start to stop.

Source code in src/holden/traverse.py
def walk_matrix(
    item: graphs.Matrix | kinds.RawMatrix,
    start: Hashable,
    stop: Hashable,
    path: Sequence[Hashable] | None = None) -> kinds.RawParallel:
    """Returns all paths in `item` from `start` to `stop`.

    Args:
        item: item in which to find paths.
        start: node to start paths from.
        stop: node to stop paths at.
        path: nodes to place in front of every returned path. Defaults to
            `None`.

    Returns:
        A list of possible paths (each path is a list of nodes) from `start` to
            `stop`.

    """
    return walk_adjacency(
        item = workshop.matrix_to_adjacency(item = item),
        start = start,
        stop = stop,
        path = path)

walk_parallel(item, start, stop)

Returns all paths in item from start to stop.

Parameters:

Name Type Description Default
item Parallel | RawParallel

item in which to find paths.

required
start Hashable

node to start paths from.

required
stop Hashable

node to stop paths at.

required

Returns:

Type Description
RawParallel

A list of possible paths (each path is a list of nodes) from start to stop. A path in item that does not contain start followed by stop does not contribute a path.

Source code in src/holden/traverse.py
def walk_parallel(
    item: composites.Parallel | kinds.RawParallel,
    start: Hashable,
    stop: Hashable) -> kinds.RawParallel:
    """Returns all paths in `item` from `start` to `stop`.

    Args:
        item: item in which to find paths.
        start: node to start paths from.
        stop: node to stop paths at.

    Returns:
        A list of possible paths (each path is a list of nodes) from `start` to
            `stop`. A path in `item` that does not contain `start` followed by
            `stop` does not contribute a path.

    """
    paths: kinds.RawParallel = []
    for serial in utilities._rawify(item):
        paths.extend(walk_serial(item = serial, start = start, stop = stop))
    return paths

walk_serial(item, start, stop)

Returns all paths in item from start to stop.

Parameters:

Name Type Description Default
item Serial | RawSerial

item in which to find paths.

required
start Hashable

node to start paths from.

required
stop Hashable

node to stop paths at.

required

Returns:

Type Description
RawParallel

A list with the single path from the first start in item to the next stop (inclusive of both) or an empty list if item has no such path.

Source code in src/holden/traverse.py
def walk_serial(
    item: composites.Serial | kinds.RawSerial,
    start: Hashable,
    stop: Hashable) -> kinds.RawParallel:
    """Returns all paths in `item` from `start` to `stop`.

    Args:
        item: item in which to find paths.
        start: node to start paths from.
        stop: node to stop paths at.

    Returns:
        A list with the single path from the first `start` in `item` to the
            next `stop` (inclusive of both) or an empty list if `item` has no
            such path.

    """
    nodes = list(utilities._rawify(item))
    try:
        index_start = nodes.index(start)
        index_stop = nodes.index(stop, index_start)
    except ValueError:
        return []
    return [nodes[index_start : index_stop + 1]]