Data Model Foundations
The pyTooling.DataModel package holds building blocks the classes of a data model share.
Key-Value-Pairs
An element of a data model often carries data its model doesn’t know: the results of an analysis, or what only one
service reports. KeyValuePairsMixin attaches arbitrary key-value-pairs to an element,
accessed like a dictionary. A key is any Hashable object.
element["runner.os"] = "Linux" # create or update a pair
os = element["runner.os"] # read a value; KeyError if the key doesn't exist
os = element.get("runner.os") # read a value; None (or a given default) if the key doesn't exist
del element["runner.os"] # remove a pair; KeyError if the key doesn't exist
if "runner.os" in element: ... # check for a key
count = len(element) # number of pairs
for key in element: ... # the keys, in the order they were added
The constructor takes the initial pairs as a mapping and copies them. Anything but a mapping - e.g. a list of pairs -
is rejected with a TypeError.
The mixin’s field becomes a slot of the first slotted class deriving from it. It is used in one of two ways:
The slotted root class of a data model, whose elements all carry key-value-pairs, derives from the mixin alone.
from pyTooling.DataModel import KeyValuePairsMixin from pyTooling.MetaClasses import ExtendedType class Element(KeyValuePairsMixin, metaclass=ExtendedType, slots=True): _name: str def __init__(self, name: str, keyValuePairs: Nullable[Mapping[Hashable, Any]] = None) -> None: super().__init__(keyValuePairs) self._name = name
A class with a base-class of its own adds the
KeyValuePairsMixinas a further base-class and calls its initializer.from pyTooling.DataModel import KeyValuePairsMixin class Node(Base, KeyValuePairsMixin): def __init__(self, keyValuePairs: Nullable[Mapping[Hashable, Any]] = None) -> None: super().__init__() KeyValuePairsMixin.__init__(self, keyValuePairs)
The elements of pyTooling.CI, pyTooling.ControlFlow and pyTooling.Graph derive from
KeyValuePairsMixin by their root class Base, a Node
directly, and those of pyTooling.Tracing by TraceElement.
Predicate
A method selecting elements - iterating, reversing or removing those a function decides on - takes a predicate:
a function receiving an element and returning a bool. Predicate is its type,
generic in the element type.
from pyTooling.DataModel import Predicate
def IterateVertices(self, predicate: Nullable[Predicate[Vertex]] = None) -> Generator[Vertex, None, None]:
...
Predicate[Vertex] is Callable[[Vertex], bool]. It is written with a type variable, not with the type
statement of Python 3.12, because pyTooling supports Python 3.11.