Skip to content

fields

fields #

Field classification — split a node's dataclass fields into the three roles.

A field's role is decided by its annotation alone (the marker), so an empty substep list or an unset optional input still classifies correctly. The roles:

  • [FieldRole.INPUT][] — an Input[T] data-flow edge.
  • [FieldRole.SUBSTEPS][] — the Substeps control-flow list.
  • [FieldRole.SETTING][] — any Setting[T] baked-in value.
  • [FieldRole.LABEL][] — the inherited label field, serialized on its own.

[partition][] turns an instance into its (inputs, substeps, settings), which is what the serializer's reachability walk and validation all consume. The Procedure.{_inputs, _substeps, _settings} properties are thin wrappers over it.

FieldRole #

Bases: Enum

What a node dataclass field is — fixed by its annotation marker.

Source code in capturegraph-lib/capturegraph/procedures/procedure/type_checking/fields.py
class FieldRole(enum.Enum):
    """What a node dataclass field is — fixed by its annotation marker."""

    INPUT = "input"
    SUBSTEPS = "substeps"
    SETTING = "setting"
    LABEL = "label"

field_roles(cls) #

Every declared field as (name, role, annotation), in field order.

Reads the dataclass fields directly, so private state (_uuid and the _return_type ClassVar) — which are not fields — never appear.

Source code in capturegraph-lib/capturegraph/procedures/procedure/type_checking/fields.py
def field_roles(
    cls: type,
) -> list[tuple[str, FieldRole, Any]]:
    """Every declared field as ``(name, role, annotation)``, in field order.

    Reads the dataclass fields directly, so private state (``_uuid`` and the
    ``_return_type`` ``ClassVar``) — which are *not* fields — never appear.
    """
    return [
        (field.name, role_of(field.name, field.type), field.type) for field in dataclass_fields(cls)
    ]

inputs(node) #

Input fields holding a procedure.

A set optional input is included; an unset one — value None — is not.

Source code in capturegraph-lib/capturegraph/procedures/procedure/type_checking/fields.py
def inputs(
    node: Procedure[Any],
) -> dict[str, Procedure[Any]]:
    """Input fields holding a procedure.

    A set optional input is included; an unset one — value ``None`` — is not.
    """
    result: dict[str, Any] = {}
    for name, role, _ in field_roles(type(node)):
        if role is FieldRole.INPUT:
            value = _value(node, name)
            if value is not None:
                result[name] = value
    return result

role_of(name, annotation) #

The role of a field, from its name and annotation marker.

Source code in capturegraph-lib/capturegraph/procedures/procedure/type_checking/fields.py
def role_of(
    name: str,
    annotation: object,
) -> FieldRole:
    """The role of a field, from its name and annotation marker."""
    if name == "label":
        return FieldRole.LABEL
    if annotations.is_input(annotation):
        return FieldRole.INPUT
    if annotations.is_substeps(annotation):
        return FieldRole.SUBSTEPS
    return FieldRole.SETTING

settings(node) #

Every Setting field with a non-None value, in field order.

Source code in capturegraph-lib/capturegraph/procedures/procedure/type_checking/fields.py
def settings(
    node: Procedure[Any],
) -> dict[str, Any]:
    """Every ``Setting`` field with a non-``None`` value, in field order."""
    result: dict[str, Any] = {}
    for name, role, _ in field_roles(type(node)):
        if role is FieldRole.SETTING:
            value = _value(node, name)
            if value is not None:
                result[name] = value
    return result

substeps(node) #

Every element of every Substeps field, flattened in field order.

Source code in capturegraph-lib/capturegraph/procedures/procedure/type_checking/fields.py
def substeps(
    node: Procedure[Any],
) -> list[Procedure[Any]]:
    """Every element of every ``Substeps`` field, flattened in field order."""
    result: list[Any] = []
    for name, role, _ in field_roles(type(node)):
        if role is FieldRole.SUBSTEPS:
            value = _value(node, name)
            if isinstance(value, list):
                result.extend(value)
    return result