Skip to content

symfonic.kernel.contracts.run_build

run_build

The build the kernel's task factory has in progress, as the new task sees it.

An eager inner factory runs the new task's first step before the factory returns, so the task has no spawn record yet (run_spawn). The factory marks the build where the new task will run -- the current context, which the task copies, or the context the caller passed, marked in place -- and unmarks it once the build returns. It never hands the inner factory a context of its own, so what a host factory sets reaches the task as before.

A mark is readable by more than the new task: a task built mid-step in a copy of its context carries it too. So the build is claimed only from the stack of the coroutine the kernel was handed: its frame must be running, with no other task's coroutine above it. The new task's own coroutine is that coroutine, or a host's wrapper below it (Sentry's patch_asyncio); a task it builds runs above it and falls to the census (run_census), as every task built outside the factory does. An eager host wrapper that is not a native coroutine (a Coroutine subclass, a Cython coroutine) has no frame to find, so its task's first step falls to the census too: wider than exact, never narrower.

A build names a lineage only while it is in progress: once the factory returns it holds neither lineage, spawner nor coroutine, so a context that copied the mark keeps nothing alive and claims nothing.

Build

Build(spawner: Any, lineage: Any, coro: Any)

One factory call in progress: its spawner, lineage, and the coroutine handed in.

Source code in src/symfonic/kernel/contracts/run_build.py
def __init__(self, spawner: Any, lineage: Any, coro: Any) -> None:
    self.spawner, self.lineage, self.coro = spawner, lineage, coro

building

building(task: Any) -> Any

The lineage of the build task runs the first step of; ENDED if none.

Source code in src/symfonic/kernel/contracts/run_build.py
def building(task: Any) -> Any:
    """The lineage of the build ``task`` runs the first step of; ``ENDED`` if none."""
    build = _BUILD.get()
    if build is None or build.spawner is task or not _first_step(build.coro, task):
        return ENDED
    return build.lineage

mark

mark(build: Build, context: Any) -> Any

Mark build where the new task will run; the token :func:unmark takes.

A context passed explicitly is marked in place; one already entered stays unmarked, so an eager first step in it falls to the census.

Source code in src/symfonic/kernel/contracts/run_build.py
def mark(build: Build, context: Any) -> Any:
    """Mark ``build`` where the new task will run; the token :func:`unmark` takes.

    A context passed explicitly is marked in place; one already entered stays
    unmarked, so an eager first step in it falls to the census.
    """
    if context is None:
        return _BUILD.set(build)
    try:
        return context.run(_BUILD.set, build)
    except (RuntimeError, AttributeError, TypeError):
        return None

unmark

unmark(build: Build, context: Any, token: Any) -> None

End build: it names nothing now, wherever a context copied it.

Source code in src/symfonic/kernel/contracts/run_build.py
def unmark(build: Build, context: Any, token: Any) -> None:
    """End ``build``: it names nothing now, wherever a context copied it."""
    build.spawner = build.lineage = build.coro = ENDED
    if token is None:
        return
    if context is None:
        _BUILD.reset(token)
        return
    with contextlib.suppress(RuntimeError, ValueError):
        context.run(_BUILD.reset, token)