Skip to content

access

access #

Schema navigation nodes — the small, regular family that walks a Path.

Each node takes a source returning Path[...] and produces the position one step deeper, computing the child type from the source's target schema. GetRoot is the base of every path; LoadPath reads a value at a runtime position (map/array enumeration). Storing a value at a path is [CacheProcedure][] (in destination/cache).

These are deliberately trivial — one operation each — so a cross-platform runtime resolves a path by evaluating the chain step by step.

GetRoot #

Bases: Procedure[Path]

The root position of the target schema (Path[Schema]).

A target schema is required: every path is concretely typed, so there is no untargeted root. The schema is consumed at construction — moved into the return type and cleared — so GetRoot serializes compactly (the schema is carried once at the top of the wire form, not per node).

Attributes:

Name Type Description
schema Setting[type] | None

The target Struct type this roots at. Required, then consumed in __post_init__ and never serialized as a setting.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
@make_procedure
class GetRoot(Procedure[Path]):
    """The root position of the target schema (``Path[Schema]``).

    A target ``schema`` is required: every path is concretely typed, so there is
    no untargeted root. The schema is consumed at construction — moved into the
    return type and cleared — so ``GetRoot`` serializes compactly (the schema is
    carried once at the top of the wire form, not per node).

    Attributes:
        schema: The target ``Struct`` type this roots at. Required, then consumed
            in ``__post_init__`` and never serialized as a setting.
    """

    schema: Setting[type] | None = None

    def __post_init__(self) -> None:
        """Consume ``schema`` into ``_return_type`` and clear the setting."""
        if self.schema is None:
            raise TypeError(
                "GetRoot requires a target schema; there is no untargeted root "
                "(every path carries a concrete target)"
            )
        object.__setattr__(self, "_return_type", Path[self.schema])
        object.__setattr__(self, "schema", None)
        super().__post_init__()

__post_init__() #

Consume schema into _return_type and clear the setting.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
def __post_init__(self) -> None:
    """Consume ``schema`` into ``_return_type`` and clear the setting."""
    if self.schema is None:
        raise TypeError(
            "GetRoot requires a target schema; there is no untargeted root "
            "(every path carries a concrete target)"
        )
    object.__setattr__(self, "_return_type", Path[self.schema])
    object.__setattr__(self, "schema", None)
    super().__post_init__()

LoadPath #

Bases: Procedure[CGType]

Read the value stored at a position, with a fallback for an empty one.

Reads Procedure[Path[T]]Procedure[T], with an explicit fallback for a position holding nothing.

Loading is explicit: the authoring surface builds this only through path.load() / path.load_or(default) (an unassigned position used as a value never lowers to a silent disk read). The read goes to disk regardless of any value cached at the slot this run — it sees what was already present before the run — so reading and then &=-overwriting the same slot is acyclic by construction.

The default runs only on a definitive "nothing stored here" answer, never while the value is still loading, so a slow remote read cannot be mistaken for an absent one. The default NullProcedure produces no value, which leaves an absent read pending — waiting on whatever will write the slot.

Attributes:

Name Type Description
source Procedure[Path]

The Path to the value to read.

default Procedure[CGType] | None

The procedure whose value stands in when nothing is stored at source. Omitted, it becomes a NullProcedure of the target type.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
@make_procedure
class LoadPath(Procedure[CGType]):
    """Read the value stored at a position, with a fallback for an empty one.

    Reads ``Procedure[Path[T]]`` → ``Procedure[T]``, with an explicit fallback
    for a position holding nothing.

    Loading is explicit: the authoring surface builds this only through
    ``path.load()`` / ``path.load_or(default)`` (an unassigned position used as a
    value never lowers to a silent disk read). The read goes to disk regardless of
    any value cached at the slot this run — it sees what was already present
    before the run — so reading and then ``&=``-overwriting the same slot is
    acyclic by construction.

    The ``default`` runs only on a *definitive* "nothing stored here" answer,
    never while the value is still loading, so a slow remote read cannot be
    mistaken for an absent one. The default ``NullProcedure`` produces no value,
    which leaves an absent read pending — waiting on whatever will write the slot.

    Attributes:
        source: The ``Path`` to the value to read.
        default: The procedure whose value stands in when nothing is stored at
            ``source``. Omitted, it becomes a ``NullProcedure`` of the target type.
    """

    source: Procedure[Path]
    default: Procedure[CGType] | None = None

    def __post_init__(self) -> None:
        """Resolve ``default`` to a ``NullProcedure`` when omitted."""
        # The default must be in place before super() runs: that is when the
        # active context catches this node's inputs as consumed.
        target = path_target(self.source)
        if self.default is None:
            from capturegraph.procedures.nodes.process.control import (
                NullProcedure,
            )

            object.__setattr__(self, "default", NullProcedure(output_type=target))
        elif not structural_eq(self.default._return_type, target):
            default_type = getattr(self.default._return_type, "__name__", self.default._return_type)
            raise TypeError(
                f"LoadPath: default returns {default_type}, "
                f"which does not match the position's {target.__name__}"
            )
        super().__post_init__()
        object.__setattr__(self, "_return_type", target)

__post_init__() #

Resolve default to a NullProcedure when omitted.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
def __post_init__(self) -> None:
    """Resolve ``default`` to a ``NullProcedure`` when omitted."""
    # The default must be in place before super() runs: that is when the
    # active context catches this node's inputs as consumed.
    target = path_target(self.source)
    if self.default is None:
        from capturegraph.procedures.nodes.process.control import (
            NullProcedure,
        )

        object.__setattr__(self, "default", NullProcedure(output_type=target))
    elif not structural_eq(self.default._return_type, target):
        default_type = getattr(self.default._return_type, "__name__", self.default._return_type)
        raise TypeError(
            f"LoadPath: default returns {default_type}, "
            f"which does not match the position's {target.__name__}"
        )
    super().__post_init__()
    object.__setattr__(self, "_return_type", target)

PathAppend #

Bases: Procedure[Path]

A fresh element slot at the end of an array (used by the loop).

Returns Path[E].

Attributes:

Name Type Description
source Procedure[Path]

The Path to the Array to append into.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
@make_procedure
class PathAppend(Procedure[Path]):
    """A fresh element slot at the end of an array (used by the loop).

    Returns ``Path[E]``.

    Attributes:
        source: The ``Path`` to the ``Array`` to append into.
    """

    source: Procedure[Path]

    def __post_init__(self) -> None:
        """Resolve ``_return_type`` to the source array's element type."""
        super().__post_init__()
        target = path_target(self.source)
        if not (isinstance(target, type) and issubclass(target, Array)):
            raise TypeError(f"PathAppend: source target {target.__name__} is not an Array")
        element = target._element
        if element is None:
            raise TypeError(
                f"{type(self).__name__}: source target {target.__name__} is schemaless",
            )
        object.__setattr__(self, "_return_type", Path[element])

__post_init__() #

Resolve _return_type to the source array's element type.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
def __post_init__(self) -> None:
    """Resolve ``_return_type`` to the source array's element type."""
    super().__post_init__()
    target = path_target(self.source)
    if not (isinstance(target, type) and issubclass(target, Array)):
        raise TypeError(f"PathAppend: source target {target.__name__} is not an Array")
    element = target._element
    if element is None:
        raise TypeError(
            f"{type(self).__name__}: source target {target.__name__} is schemaless",
        )
    object.__setattr__(self, "_return_type", Path[element])

PathField #

Bases: Procedure[Path]

Descend into a named struct field. Returns Path[FieldType].

Attributes:

Name Type Description
source Procedure[Path]

The Path to the struct.

field Setting[str]

The field name to descend into.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
@make_procedure
class PathField(Procedure[Path]):
    """Descend into a named struct field. Returns ``Path[FieldType]``.

    Attributes:
        source: The ``Path`` to the struct.
        field: The field name to descend into.
    """

    source: Procedure[Path]
    field: Setting[str]

    def __post_init__(self) -> None:
        """Resolve ``_return_type`` to the source's named field type."""
        super().__post_init__()
        target = path_target(self.source)
        fields = getattr(target, "_fields", None)
        if not fields or self.field not in fields:
            raise TypeError(
                f"PathField: {getattr(target, '__name__', target)} has no field {self.field!r}"
            )
        object.__setattr__(self, "_return_type", Path[fields[self.field]])

__post_init__() #

Resolve _return_type to the source's named field type.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
def __post_init__(self) -> None:
    """Resolve ``_return_type`` to the source's named field type."""
    super().__post_init__()
    target = path_target(self.source)
    fields = getattr(target, "_fields", None)
    if not fields or self.field not in fields:
        raise TypeError(
            f"PathField: {getattr(target, '__name__', target)} has no field {self.field!r}"
        )
    object.__setattr__(self, "_return_type", Path[fields[self.field]])

PathIndex #

Bases: Procedure[Path]

Index one array element (negative indexes from the end). Returns Path[E].

Attributes:

Name Type Description
source Procedure[Path]

The Path to the Array.

index Setting[int]

The integer position (e.g. -1 for the last entry).

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
@make_procedure
class PathIndex(Procedure[Path]):
    """Index one array element (negative indexes from the end). Returns ``Path[E]``.

    Attributes:
        source: The ``Path`` to the ``Array``.
        index: The integer position (e.g. ``-1`` for the last entry).
    """

    source: Procedure[Path]
    index: Setting[int]

    def __post_init__(self) -> None:
        """Resolve ``_return_type`` to the source array's element type."""
        super().__post_init__()
        target = path_target(self.source)
        if not (isinstance(target, type) and issubclass(target, Array)):
            raise TypeError(f"PathIndex: source target {target.__name__} is not an Array")
        element = target._element
        if element is None:
            raise TypeError(
                f"{type(self).__name__}: source target {target.__name__} is schemaless",
            )
        object.__setattr__(self, "_return_type", Path[element])

__post_init__() #

Resolve _return_type to the source array's element type.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
def __post_init__(self) -> None:
    """Resolve ``_return_type`` to the source array's element type."""
    super().__post_init__()
    target = path_target(self.source)
    if not (isinstance(target, type) and issubclass(target, Array)):
        raise TypeError(f"PathIndex: source target {target.__name__} is not an Array")
    element = target._element
    if element is None:
        raise TypeError(
            f"{type(self).__name__}: source target {target.__name__} is schemaless",
        )
    object.__setattr__(self, "_return_type", Path[element])

PathKey #

Bases: Procedure[Path]

Select a map entry by a runtime key value. Returns Path[V].

Attributes:

Name Type Description
source Procedure[Path]

The Path to the Map.

key Procedure[CGType]

A procedure producing the key value (a keyable scalar matching the map's key type).

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
@make_procedure
class PathKey(Procedure[Path]):
    """Select a map entry by a runtime key value. Returns ``Path[V]``.

    Attributes:
        source: The ``Path`` to the ``Map``.
        key: A procedure producing the key value (a keyable scalar matching the
            map's key type).
    """

    source: Procedure[Path]
    key: Procedure[CGType]

    def __post_init__(self) -> None:
        """Resolve ``_return_type`` to the source map's element type."""
        super().__post_init__()
        target = path_target(self.source)
        if not (isinstance(target, type) and issubclass(target, Map)):
            raise TypeError(f"PathKey: source target {target.__name__} is not a Map")
        map_key = target._key
        if map_key is None:
            raise TypeError(f"PathKey: source target {target.__name__} is a schemaless Map")
        key_type = self.key._return_type
        if not (is_keyable(key_type) and structural_eq(key_type, map_key)):
            raise TypeError(
                f"PathKey: key {getattr(key_type, '__name__', key_type)} "
                f"does not match the map's key {map_key.__name__}"
            )
        element = target._element
        if element is None:
            raise TypeError(
                f"{type(self).__name__}: source target {target.__name__} is schemaless",
            )
        object.__setattr__(self, "_return_type", Path[element])

__post_init__() #

Resolve _return_type to the source map's element type.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
def __post_init__(self) -> None:
    """Resolve ``_return_type`` to the source map's element type."""
    super().__post_init__()
    target = path_target(self.source)
    if not (isinstance(target, type) and issubclass(target, Map)):
        raise TypeError(f"PathKey: source target {target.__name__} is not a Map")
    map_key = target._key
    if map_key is None:
        raise TypeError(f"PathKey: source target {target.__name__} is a schemaless Map")
    key_type = self.key._return_type
    if not (is_keyable(key_type) and structural_eq(key_type, map_key)):
        raise TypeError(
            f"PathKey: key {getattr(key_type, '__name__', key_type)} "
            f"does not match the map's key {map_key.__name__}"
        )
    element = target._element
    if element is None:
        raise TypeError(
            f"{type(self).__name__}: source target {target.__name__} is schemaless",
        )
    object.__setattr__(self, "_return_type", Path[element])

PathKeys #

Bases: Procedure[Array]

Enumerate a map's keys. Returns Array[KeyScalar].

A value, since keys are read-only (index it with ArrayIndex to select one).

Attributes:

Name Type Description
source Procedure[Path]

The Path to the Map.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
@make_procedure
class PathKeys(Procedure[Array]):
    """Enumerate a map's keys. Returns ``Array[KeyScalar]``.

    A value, since keys are read-only (index it with ``ArrayIndex`` to select
    one).

    Attributes:
        source: The ``Path`` to the ``Map``.
    """

    source: Procedure[Path]

    def __post_init__(self) -> None:
        """Resolve ``_return_type`` to an ``Array`` of the source map's key type."""
        super().__post_init__()
        target = path_target(self.source)
        if not (isinstance(target, type) and issubclass(target, Map)):
            raise TypeError(f"PathKeys: source target {target.__name__} is not a Map")
        object.__setattr__(self, "_return_type", Array[target._key])

__post_init__() #

Resolve _return_type to an Array of the source map's key type.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
def __post_init__(self) -> None:
    """Resolve ``_return_type`` to an ``Array`` of the source map's key type."""
    super().__post_init__()
    target = path_target(self.source)
    if not (isinstance(target, type) and issubclass(target, Map)):
        raise TypeError(f"PathKeys: source target {target.__name__} is not a Map")
    object.__setattr__(self, "_return_type", Array[target._key])

PathValues #

Bases: Procedure[Array]

Enumerate a map's entry positions. Returns Array[Path[V]].

An array of positions to fan out over (or index with ArrayIndex to select one, which stays navigable as Path[V]).

Attributes:

Name Type Description
source Procedure[Path]

The Path to the Map.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
@make_procedure
class PathValues(Procedure[Array]):
    """Enumerate a map's entry positions. Returns ``Array[Path[V]]``.

    An array of positions to fan out over (or index with ``ArrayIndex`` to
    select one, which stays navigable as ``Path[V]``).

    Attributes:
        source: The ``Path`` to the ``Map``.
    """

    source: Procedure[Path]

    def __post_init__(self) -> None:
        """Resolve ``_return_type`` to an ``Array`` of the source map's entry paths."""
        super().__post_init__()
        target = path_target(self.source)
        if not (isinstance(target, type) and issubclass(target, Map)):
            raise TypeError(f"PathValues: source target {target.__name__} is not a Map")
        element = target._element
        if element is None:
            raise TypeError(
                f"{type(self).__name__}: source target {target.__name__} is schemaless",
            )
        object.__setattr__(self, "_return_type", Array[Path[element]])

__post_init__() #

Resolve _return_type to an Array of the source map's entry paths.

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
def __post_init__(self) -> None:
    """Resolve ``_return_type`` to an ``Array`` of the source map's entry paths."""
    super().__post_init__()
    target = path_target(self.source)
    if not (isinstance(target, type) and issubclass(target, Map)):
        raise TypeError(f"PathValues: source target {target.__name__} is not a Map")
    element = target._element
    if element is None:
        raise TypeError(
            f"{type(self).__name__}: source target {target.__name__} is schemaless",
        )
    object.__setattr__(self, "_return_type", Array[Path[element]])

path_target(source) #

The concrete type a Procedure[Path[T]] points at, i.e. T.

Raises:

Type Description
TypeError

If the source's return type is not a Path with a concrete target (e.g. used before @cg.procedure rooted it at a schema).

Source code in capturegraph-lib/capturegraph/procedures/nodes/destination/access.py
def path_target(source: Procedure) -> type[CGType]:
    """The concrete type a ``Procedure[Path[T]]`` points at, i.e. ``T``.

    Raises:
        TypeError: If the source's return type is not a ``Path`` with a concrete
            target (e.g. used before ``@cg.procedure`` rooted it at a schema).
    """
    rt = source._return_type
    target = getattr(rt, "_target", None)
    if not (isinstance(target, type) and issubclass(target, CGType)):
        raise TypeError(
            f"expected a Path with a concrete target, "
            f"got {getattr(rt, '__name__', rt)} from {source}"
        )
    return target