Skip to content

navigation

navigation #

The fluent navigation and cache-assignment operators bound onto Procedure.

Navigating a procedure descends its return type one step: attribute access reads a struct field, indexing selects an array element (int) or a map entry (key), and .keys() / .values() enumerate a map. The same operators work whether the return type is a Path[…] (each step yields a deeper Path) or a plain value (each step yields the child value) — [.dispatch][] is the table that decides, so every operator here is just classify → guard → build.

Assignment caches: path |= value (skip if exists) and path &= value (overwrite) each record a CacheProcedure wrapped in a RequiredProcedure (so the stored value is a kept Void step) and return the CacheProcedure so the slot reads back as its value type.

These functions are imported into the Procedure class body, so every import here is lazy (the node and dispatch layers import Procedure back). The per-slot state each operator reads and writes lives in [.slots][]; the recorded sequence is owned by the active ProcedureContext.

__getitem__(self, key) #

__getitem__(
    self: Procedure[Array],
    key: int
    | Procedure[CGType]
    | JSONScalar
    | bool
    | float
    | str,
) -> Procedure[CGType]
__getitem__(
    self: Procedure[Map],
    key: int
    | Procedure[CGType]
    | JSONScalar
    | bool
    | float
    | str,
) -> Procedure[CGType]
__getitem__(
    self: Procedure[Path],
    key: int
    | Procedure[CGType]
    | JSONScalar
    | bool
    | float
    | str,
) -> Procedure[Path]

Index a navigable position or a value-access array / map.

On a navigated Path it selects an array element / map entry position (another Path). On a map value a node returns it selects the entry's value (MapKey); on the Array that a map's keys()/values() returns it selects the element value — a key scalar, or a Path[V] to navigate. The runtime dispatches all of these through the same navigable view.

Source code in capturegraph-lib/capturegraph/procedures/procedure/methods/navigation.py
def __getitem__(
    self: Procedure[Path] | Procedure[Array] | Procedure[Map],
    key: int | Procedure[CGType] | JSONScalar | bool | float | str,
) -> Procedure[CGType]:
    """Index a navigable position or a value-access array / map.

    On a navigated ``Path`` it selects an array element / map entry position
    (another ``Path``). On a map *value* a node returns it selects the entry's
    value (``MapKey``); on the ``Array`` that a map's ``keys()``/``values()``
    returns it selects the element *value* — a key scalar, or a ``Path[V]`` to
    navigate. The runtime dispatches all of these through the same ``navigable`` view.
    """
    from capturegraph.procedures.procedure.authoring.dispatch import (
        navigable,
    )

    nav = navigable(self)
    if nav is None or not (nav.is_array or nav.is_map):
        raise TypeError(f"{self} is not indexable (it is neither an array nor a map)")

    if nav.is_path:
        require_no_saved_value(cast("Procedure[Path]", self))
    return subscript_child(cast("Procedure[Path]", self), nav, key)

__iand__(self, value) #

path &= value — cache the value, overwriting any stored value.

Source code in capturegraph-lib/capturegraph/procedures/procedure/methods/navigation.py
def __iand__[T: CGType](
    self: Procedure[Path[T]],
    value: Procedure[T],
) -> CacheProcedure[T]:
    """``path &= value`` — cache the value, overwriting any stored value."""
    return record_cache(self, value, skip_if_exists=False)

__ior__(self, value) #

path |= value — cache the value, skipping if already stored.

Source code in capturegraph-lib/capturegraph/procedures/procedure/methods/navigation.py
def __ior__[T: CGType](
    self: Procedure[Path[T]],
    value: Procedure[T],
) -> CacheProcedure[T]:
    """``path |= value`` — cache the value, skipping if already stored."""
    return record_cache(self, value, skip_if_exists=True)

__setattr__(self, name, value) #

Absorb the path.field |= value rebind; reject any other mutation.

Source code in capturegraph-lib/capturegraph/procedures/procedure/methods/navigation.py
def __setattr__(
    self: Procedure[CGType],
    name: str,
    value: CacheProcedure[CGType],
) -> None:
    """Absorb the ``path.field |= value`` rebind; reject any other mutation."""
    absorb_cache_rebind(self, ("field", name), value, repr(name))

__setitem__(self, key, value) #

Absorb the arr[i] |= value / map[k] &= value rebind.

Source code in capturegraph-lib/capturegraph/procedures/procedure/methods/navigation.py
def __setitem__(
    self: Procedure[Path],
    key: int | Procedure[CGType] | JSONScalar | bool | float | str,
    value: CacheProcedure[CGType],
) -> None:
    """Absorb the ``arr[i] |= value`` / ``map[k] &= value`` rebind."""
    from capturegraph.procedures.procedure.authoring.dispatch import (
        navigable,
    )

    nav = navigable(self)
    memo = subscript_memo(nav, key) if nav is not None else None
    absorb_cache_rebind(self, memo, value, f"[{key!r}]")

exists(self) #

path.exists() — a Bool that is true when a value is stored here.

Reads the position from disk (LoadPath) and reports whether that read completes (ProcedureCompleted), so a missing value yields false instead of aborting the run. Pair it with cg.when(...) to run a block only once the position has been written. The read goes to disk regardless of any value cached at this slot this run, so it tests what was already present before the run.

Source code in capturegraph-lib/capturegraph/procedures/procedure/methods/navigation.py
def exists(self: Procedure[Path]) -> Procedure[Bool]:
    """``path.exists()`` — a ``Bool`` that is true when a value is stored here.

    Reads the position from disk (``LoadPath``) and reports whether that read
    completes (``ProcedureCompleted``), so a missing value yields ``false`` instead
    of aborting the run. Pair it with ``cg.when(...)`` to run a block only once the
    position has been written. The read goes to disk regardless of any value cached
    at this slot this run, so it tests what was already present before the run.
    """
    from capturegraph.procedures.nodes.destination.access import (
        LoadPath,
    )
    from capturegraph.procedures.nodes.process.control import (
        ProcedureCompleted,
    )

    return ProcedureCompleted(procedure=LoadPath(source=self))

keys(self) #

A map position's keys as a read-only Array[K] value (index to select one).

Source code in capturegraph-lib/capturegraph/procedures/procedure/methods/navigation.py
def keys(self: Procedure[Path]) -> Procedure[Array]:
    """A map position's keys as a read-only ``Array[K]`` value (index to select one)."""
    from capturegraph.procedures.procedure.authoring.dispatch import (
        MAP_KEYS,
    )

    require_map_position(self, "keys")
    return cast(
        "Procedure[Array]",
        memoized_child(self, ("keys",), lambda: MAP_KEYS(source=self)),
    )

load(self) #

path.load() — read the value stored at this position.

Loading is explicit: an unwritten position used as a value never lowers to a disk read on its own. The read waits for a stored value (a prior run's, or one an interceptor fills); while nothing is stored it stays pending, so use [load_or][] when an absent value should produce a fallback instead. The read goes to disk regardless of any value cached at this slot this run, so it sees what was already present before the run.

Source code in capturegraph-lib/capturegraph/procedures/procedure/methods/navigation.py
def load[T: CGType](self: Procedure[Path[T]]) -> Procedure[T]:
    """``path.load()`` — read the value stored at this position.

    Loading is explicit: an unwritten position used as a value never lowers to a
    disk read on its own. The read waits for a stored value (a prior run's, or
    one an interceptor fills); while nothing is stored it stays pending, so use
    [load_or][] when an absent value should produce a fallback instead. The
    read goes to disk regardless of any value cached at this slot this run, so it
    sees what was already present before the run.
    """
    from capturegraph.procedures.nodes.destination.access import (
        LoadPath,
    )

    # The compile-time validation pass checks the node against the schema
    # position, so the declared target is enforced before execution.
    return cast("Procedure[T]", LoadPath(source=self))

load_or(self, default) #

load_or(
    self: Procedure[Path[T]], default: Procedure[T]
) -> Procedure[T]
load_or(
    self: Procedure[Path], default: object
) -> Procedure[CGType]

path.load_or(default) — the stored value, or default's value.

Returned when nothing is stored here. The fallback runs only on a definitive "nothing stored" answer — never while the value is still loading — so a slow remote read cannot be mistaken for an absent one. default is a procedure (or raw literal) whose type matches the position's — so a typed default names the result's type, and the compile-time validation pass enforces the match.

Source code in capturegraph-lib/capturegraph/procedures/procedure/methods/navigation.py
def load_or(
    self: Procedure[Path],
    default: object,
) -> object:
    """``path.load_or(default)`` — the stored value, or ``default``'s value.

    Returned when nothing is stored here. The fallback runs only on a definitive
    "nothing stored" answer — never while the value is still loading — so a slow
    remote read cannot be mistaken for an absent one. ``default`` is a procedure (or
    raw literal) whose type matches the position's — so a typed default names the
    result's type, and the compile-time validation pass enforces the match.
    """
    from capturegraph.procedures.nodes.destination.access import (
        LoadPath,
    )
    from capturegraph.procedures.procedure.authoring.bridge import (
        coerce_input,
    )

    return LoadPath(source=self, default=coerce_input(default))

navigate(self, name) #

Read a struct field; bound as Procedure.__getattr__.

E.g. root.reference, CapturePanorama().preview. Only missing attributes reach here, and dunders never navigate.

Source code in capturegraph-lib/capturegraph/procedures/procedure/methods/navigation.py
def navigate(self: Procedure[Path], name: str) -> Procedure[Path]:
    """Read a struct field; bound as ``Procedure.__getattr__``.

    E.g. ``root.reference``, ``CapturePanorama().preview``. Only missing attributes
    reach here, and dunders never navigate.
    """
    if name.startswith("__") and name.endswith("__"):
        raise AttributeError(name)

    from capturegraph.procedures.procedure.authoring.dispatch import (
        navigable,
    )

    nav = navigable(self)
    if nav is None or not nav.has_field(name):
        raise AttributeError(name)

    if nav.is_path:
        require_no_saved_value(self)
    child = memoized_child(self, ("field", name), lambda: nav.nodes.field(source=self, field=name))
    return cast("Procedure[Path]", child)

values(self) #

A map position's entry positions as Array[Path[V]].

Fan out, or index to select one to navigate.

Source code in capturegraph-lib/capturegraph/procedures/procedure/methods/navigation.py
def values(self: Procedure[Path]) -> Procedure[Array]:
    """A map position's entry positions as ``Array[Path[V]]``.

    Fan out, or index to select one to navigate.
    """
    from capturegraph.procedures.procedure.authoring.dispatch import (
        MAP_VALUES,
    )

    require_map_position(self, "values")
    return cast(
        "Procedure[Array]",
        memoized_child(self, ("values",), lambda: MAP_VALUES(source=self)),
    )