Skip to content

Jobs

Portable progress, cancellation, and shell-job contracts.

Shell jobs are deliberately smaller than consumer domain queues. A job here is work that this TUI process started and can present, gate, or cancel. Durable processing queues, leases, retries, and run records stay with the consuming application.

BlockDecision dataclass

BlockDecision(allowed: bool, reason: str | None = None)

Policy answer for whether a candidate job can start.

allowed instance-attribute

allowed: bool

reason class-attribute instance-attribute

reason: str | None = None

allow classmethod

allow() -> BlockDecision

block classmethod

block(reason: str) -> BlockDecision

CancellationMode

Bases: StrEnum

How the shell should present and route cancellation for an action.

COOPERATIVE class-attribute instance-attribute

COOPERATIVE = 'cooperative'

IMMEDIATE class-attribute instance-attribute

IMMEDIATE = 'immediate'

UNSUPPORTED class-attribute instance-attribute

UNSUPPORTED = 'unsupported'

CancellationRequested

Bases: RuntimeError

Raised by cooperative runners when the operator has requested cancellation.

CancellationToken

Bases: Protocol

Structural cancellation contract passed into consumer runners.

requested property

requested: bool

raise_if_requested

raise_if_requested() -> None

JobManager

JobManager(policy: JobPolicy | None = None)

Multi-job-capable manager with a conservative default policy.

The manager does not execute work itself. Textual workers, threads, or synchronous test harnesses own execution and report lifecycle changes back here.

active property

active: tuple[JobSnapshot, ...]

foreground property

foreground: JobSnapshot | None

policy instance-attribute

policy = policy or SingleForegroundJobPolicy()

can_start

can_start(spec: JobSpec) -> BlockDecision

complete

complete(job_id: str) -> JobSnapshot

request_cancel

request_cancel(job_id: str) -> JobSnapshot

start

start(
    spec: JobSpec,
    *,
    job_id: str | None = None,
    metadata: Mapping[str, object] | None = None,
) -> tuple[JobSnapshot, ThreadCancellationToken]

update

update(job_id: str, progress: ProgressEvent) -> JobSnapshot

JobPolicy

Bases: Protocol

In-process gate for shell jobs.

can_start

can_start(
    candidate: JobSpec, active: Sequence[JobSnapshot]
) -> BlockDecision

JobSnapshot dataclass

JobSnapshot(
    job_id: str,
    spec: JobSpec,
    started_at: float,
    progress: ProgressEvent | None = None,
    cancelling: bool = False,
    metadata: Mapping[str, object] = dict(),
)

Immutable view of a shell job for policy, bars, and tests.

cancellable property

cancellable: bool

cancelling class-attribute instance-attribute

cancelling: bool = False

elapsed property

elapsed: float

fraction property

fraction: float | None

job_id instance-attribute

job_id: str

metadata class-attribute instance-attribute

metadata: Mapping[str, object] = field(default_factory=dict)

progress class-attribute instance-attribute

progress: ProgressEvent | None = None

spec instance-attribute

spec: JobSpec

started_at instance-attribute

started_at: float

describe

describe() -> str

JobSpec dataclass

JobSpec(
    key: str,
    label: str,
    resources: frozenset[str] = frozenset(),
    effects: frozenset[str] = frozenset(),
    cancellation: CancellationMode = CancellationMode.UNSUPPORTED,
    foreground: bool = True,
)

What the shell needs to decide whether work may start.

cancellation class-attribute instance-attribute

cancellation: CancellationMode = (
    CancellationMode.UNSUPPORTED
)

effects class-attribute instance-attribute

effects: frozenset[str] = frozenset()

foreground class-attribute instance-attribute

foreground: bool = True

key instance-attribute

key: str

label instance-attribute

label: str

resources class-attribute instance-attribute

resources: frozenset[str] = frozenset()

ProgressEvent dataclass

ProgressEvent(
    event: str,
    phase: str | None = None,
    completed: int = 0,
    total: int | None = None,
    unit: str | None = None,
    message: str | None = None,
    timestamp: datetime = (lambda: datetime.now(UTC))(),
)

One portable progress update emitted by a runner.

completed class-attribute instance-attribute

completed: int = 0

event instance-attribute

event: str

fraction property

fraction: float | None

message class-attribute instance-attribute

message: str | None = None

phase class-attribute instance-attribute

phase: str | None = None

timestamp class-attribute instance-attribute

timestamp: datetime = field(
    default_factory=lambda: datetime.now(UTC)
)

total class-attribute instance-attribute

total: int | None = None

unit class-attribute instance-attribute

unit: str | None = None

ProgressSink

Bases: Protocol

Consumer runners call this; widgets never need to know the runner type.

emit

emit(event: ProgressEvent) -> None

RecordingProgressSink

RecordingProgressSink()

Small sink used by tests, demos, and synchronous action execution.

events instance-attribute

events: list[ProgressEvent] = []

emit

emit(event: ProgressEvent) -> None

SingleForegroundJobPolicy

Default policy: one foreground job, plus no overlapping explicit resources.

can_start

can_start(
    candidate: JobSpec, active: Sequence[JobSnapshot]
) -> BlockDecision

ThreadCancellationToken

ThreadCancellationToken()

Thread-safe default cancellation token for ordinary shell workers.

requested property

requested: bool

raise_if_requested

raise_if_requested() -> None

request

request() -> None