scistudio.tutorials

Canonical import root: from scistudio.tutorials import ...

Self-contained public-API reference — 16 symbols from this module's __all__, with signatures and docstrings inlined (ADR-052 §7). Generated; do not hand-edit.

Actiontype-alias

Stability: unmarked — see the module source / ADR-052

type-alias — see the module source for the value.

Conditionclass

Stability: provisional · Since 0.3.4

class Condition
Condition(term: 'str', args: 'Mapping[str, Any]' = <factory>, operands: 'tuple[Condition, ...]' = ()) -> None

A parsed done_when: one vocabulary term, or an all/any of them.

Build one with parse_condition rather than by hand — that is where a term outside VOCABULARY, or an argument a term does not accept, is rejected with a message naming the offending field. Judge one with evaluate.

A driver only needs this type when it defers part of a step to the core vocabulary; a step whose condition the vocabulary cannot express has no Condition at all, which is why DeclaresConditions.condition may return None.

Members

  • is_combinator(self) -> 'bool'unmarked — see the module source / ADR-052 — True when this node is an all or an any.
  • terms(self) -> 'frozenset[str]'unmarked — see the module source / ADR-052 — Every vocabulary term this condition uses, recursively.

ConditionValidationErrorexception

Stability: provisional · Since 0.3.4

class ConditionValidationError(ValueError)

A done_when was rejected at manifest validation (FR-049).

Raised for an unknown term, a malformed shape, or missing term arguments. scistudio.tutorials.manifest re-raises it as a ManifestValidationError naming the file.

CopyActionclass

Stability: provisional · Since 0.3.4

class CopyAction
CopyAction(source: 'str', destination: 'str') -> None

Copy one asset directory into the tutorial project.

Existing files under the destination are overwritten, for the same reason WriteAction overwrites.

DeclaresConditionsprotocol

Stability: provisional · Since 0.3.4

class DeclaresConditions(Protocol)
DeclaresConditions(*args, **kwargs)

An optional capability: a driver that can name a step's condition.

Not part of TutorialDriver, because FR-038 fixes that interface at four members and a package driver must be able to implement conditions the vocabulary cannot express — which by definition have no scistudio.tutorials.conditions.Condition to return.

A driver that can answer lets the session skip re-evaluating on an event that maps to none of the step's terms (FR-050). A driver that cannot is re-evaluated on every mapped event instead. The two produce identical responses, which is what FR-040 constrains; only the number of evaluations differs, and evaluation is side-effect free (FR-055).

Members

  • condition(self, context: 'DriverContext') -> 'Condition | None'unmarked — see the module source / ADR-052 — Return the condition context.step_id is judged by, if it has one.

DriverContextclass

Stability: provisional · Since 0.3.4

class DriverContext
DriverContext(key: 'TutorialKey', tutorial_dir: 'Path', project_dir: 'Path | None', step_id: 'str | None', satisfied_step_ids: 'tuple[str, ...]' = ()) -> None

Everything a driver is told about the session asking the question.

Deliberately not the session object: a driver reads position and location and nothing else, so it cannot advance the session, end it, or start another one. That is what keeps FR-043's one-session rule and FR-044's containment properties of the runtime rather than of every driver.

ProductStateprotocol

Stability: provisional · Since 0.3.4

class ProductState(Protocol)
ProductState(*args, **kwargs)

The one injected port through which conditions read product truth.

FR-046: judging is a backend concern, evaluated against the registries, the workflow definition, the run records, git, and the filesystem. This module reaches all of that through this protocol rather than by importing the API runtime, which keeps api -> tutorials a one-way edge and keeps the package testable without a FastAPI app (checklist §6.1.2, §6.1.3).

Every member is a pure read (FR-055).

Members

  • workflow(self) -> 'WorkflowDefinition | None'unmarked — see the module source / ADR-052 — The active workflow definition, or None when none is open.
  • block_type_names(self) -> 'frozenset[str]'unmarked — see the module source / ADR-052
  • data_type_names(self) -> 'frozenset[str]'unmarked — see the module source / ADR-052
  • previewer_type_ids(self) -> 'frozenset[str]'unmarked — see the module source / ADR-052
  • plot_bindings(self) -> 'tuple[tuple[str, str, str], ...]'unmarked — see the module source / ADR-052(plot_id, node_id, output_port) for every plot that exists.
  • run_records(self) -> 'tuple[RunSummary, ...]'unmarked — see the module source / ADR-052 — The recent runs, most recent first.
  • port_has_output(self, node_id: 'str', port: 'str') -> 'bool'unmarked — see the module source / ADR-052
  • git_branches(self) -> 'frozenset[str]'unmarked — see the module source / ADR-052
  • git_current_branch(self) -> 'str | None'unmarked — see the module source / ADR-052
  • library_entries(self) -> 'frozenset[tuple[str, str]]'unmarked — see the module source / ADR-052(kind, name) for every entry in the tutorial-scoped library.
  • interactions_completed(self) -> 'frozenset[str]'unmarked — see the module source / ADR-052
  • pages_reached(self) -> 'frozenset[str]'unmarked — see the module source / ADR-052
  • ui_events(self) -> 'frozenset[str]'unmarked — see the module source / ADR-052

ReplayActionclass

Stability: provisional · Since 0.3.4

class ReplayAction
ReplayAction(surface: 'str', segments: 'tuple[ReplaySegment, ...]') -> None

Replay scripted material into one named surface (FR-061, FR-061a, FR-061b).

How a tutorial shows a conversation that would otherwise need a live model: the material is scripted, and the surface renders it as though it had just arrived. A segment may bind to the file actions that must land first, so text claiming a file was written cannot be read before the file exists.

RunSummaryclass

Stability: provisional · Since 0.3.4

class RunSummary
RunSummary(run_id: 'str', workflow_id: 'str', succeeded: 'bool', succeeded_node_ids: 'frozenset[str]' = frozenset()) -> None

The read-only view of one recorded run that run_succeeded needs.

Deliberately not scistudio.core.lineage.record.RunRecord: this module must not depend on the lineage schema, and the only facts a condition can ask about are which workflow the run belonged to, whether it succeeded, and which nodes within it succeeded. The layer that satisfies ProductState projects the lineage rows into this.

StepViewclass

Stability: provisional · Since 0.3.4

class StepView
StepView(id: 'str', index: 'int', total: 'int', title: 'str | None' = None, say: 'str | None' = None, highlight: 'Mapping[str, Any] | None' = None, route_to: 'str | None' = None, prefill: 'tuple[Mapping[str, Any], ...]' = (), awaiting_continue: 'bool' = False, satisfied: 'bool' = False) -> None

What one step looks like, for every driver alike (FR-040, FR-041).

The closed set of fields a driver may influence, and the return type of TutorialDriver.step_view. A driver may return this class, any object carrying these attributes, or a mapping of them; of reduces all three to a plain StepView and drops anything else, which is what makes a package driver and core's ManifestDriver indistinguishable to everything downstream.

Only id, index and total are required. The rest describe what the step shows and what it is waiting for:

  • title, say — the step's heading and body text.
  • highlight — the product surface to point at; the manifest's HIGHLIGHT_TARGETS names what can be addressed.
  • route_to — the surface to open before the step is readable.
  • prefill — values to seed into a form or editor.
  • awaiting_continue — the step ends on the reader acknowledging it rather than on a condition.

satisfied is not a driver's field. A driver reports what a step is; whether it currently holds is the runtime's answer, attached after the driver's view has been reduced.

Members

  • of(cls, raw: 'Any') -> 'StepView'unmarked — see the module source / ADR-052 — Return raw as a plain StepView, dropping anything else.

TutorialDriverprotocol

Stability: provisional · Since 0.3.4

class TutorialDriver(Protocol)
TutorialDriver(*args, **kwargs)

What a package implements to own its tutorial's logic (FR-038, FR-040).

Most tutorials need none of this. A tutorial.yaml written against the published schema is run by core's ManifestDriver, and that is the path to prefer. Implement this protocol only when the tutorial's logic cannot be expressed as a manifest — a step judged by a condition the vocabulary has no term for, or a sequence that depends on what the reader did earlier.

The protocol is four methods and stays four: the runtime asks what the current step looks like, whether it is satisfied, what to do on entering it, and which step comes next. There is no fifth question and no hook to add one.

Name the class from the manifest, and it is imported only when a reader starts that tutorial (FR-021); an import failure ends that one session and leaves every other tutorial listed and startable (FR-044).

Three properties are worth knowing before writing one, because each removes work rather than adding it:

  • Core owns rendering. Whatever step_view returns is normalised through StepView.of at the boundary, so extra attributes and extra mapping keys are dropped rather than reaching a response (FR-041). A driver cannot introduce a display primitive or ship a frontend asset, and correspondingly never has to describe one.
  • The session holds the cursor. A driver is asked about the step a DriverContext names, not about "its" current step, so it persists nothing and survives a backend restart without any state of its own (FR-037).
  • The core evaluator is available. scistudio.tutorials.conditions.evaluate and scistudio.tutorials.conditions.parse_condition are public, so is_satisfied can defer every term the vocabulary already covers and implement only the remainder (FR-042).

A driver that answers most steps from its manifest and one step itself::

from scistudio.tutorials import (
    Condition, DriverContext, ProductState, StepView, evaluate,
)

class MyDriver:
    def __init__(self, manifest, key):
        self._steps = list(manifest.steps)

    def step_view(self, context: DriverContext) -> StepView:
        step = self._step(context.step_id)
        index = self._steps.index(step)
        return StepView(
            id=step.id, index=index, total=len(self._steps),
            title=step.title, say=step.say,
        )

    def is_satisfied(self, context: DriverContext, product: ProductState) -> bool:
        step = self._step(context.step_id)
        if step.id == "calibration-converged":
            return self._converged(product)   # no vocabulary term for this
        return step.done_when is None or evaluate(step.done_when, product)

    def entry_actions(self, context: DriverContext):
        return self._step(context.step_id).actions

    def advance(self, context: DriverContext) -> str | None:
        if context.step_id is None:
            return self._steps[0].id
        nxt = self._steps.index(self._step(context.step_id)) + 1
        return self._steps[nxt].id if nxt < len(self._steps) else None

Optionally also implement DeclaresConditions, which lets the session skip re-evaluating a step on events that cannot affect it. It changes how often is_satisfied is called and never what the reader sees.

Members

  • step_view(self, context: 'DriverContext') -> 'Any'unmarked — see the module source / ADR-052 — Return the view of context.step_id.
  • is_satisfied(self, context: 'DriverContext', product: 'ProductState') -> 'bool'unmarked — see the module source / ADR-052 — Return whether context.step_id is satisfied by current product state.
  • entry_actions(self, context: 'DriverContext') -> 'Sequence[Action]'unmarked — see the module source / ADR-052 — Return the actions to perform on entering context.step_id (FR-056).
  • advance(self, context: 'DriverContext') -> 'str | None'unmarked — see the module source / ADR-052 — Return the id of the step to enter after context.step_id.

TutorialKeyclass

Stability: provisional · Since 0.3.4

class TutorialKey
TutorialKey(source_kind: 'str', source_id: 'str', tutorial_id: 'str') -> None

A tutorial's identity: its source and its id (FR-019, FR-075).

source_id is "" for core, the distribution name for a package, and "user" or "project" for the two local tiers — the same spelling the HTTP contract and the progress groups use, so one value travels from discovery through the project marker to the progress record without being re-encoded at each boundary.

Members

  • core(cls, tutorial_id: 'str') -> 'TutorialKey'unmarked — see the module source / ADR-052 — Return the key of a core tutorial, whose source id is empty.
  • group(self) -> 'tuple[str, str]'unmarked — see the module source / ADR-052 — Return the progress group this tutorial belongs to (FR-076).

VOCABULARYconstant

Stability: unmarked — see the module source / ADR-052

constant — see the module source for the value.

WriteActionclass

Stability: provisional · Since 0.3.4

class WriteAction
WriteAction(source: 'str', destination: 'str') -> None

Write one asset file into the tutorial project.

Available at any step, not only at bootstrap (FR-058). An existing destination is overwritten (spec §2 Edge Cases).

evaluatefunction

Stability: provisional · Since 0.3.4

evaluate(condition: 'Condition', state: 'ProductState') -> 'bool'

Judge condition against state.

Side-effect free (FR-055): no file is created, no registry is mutated, no run is triggered. Called on step entry (FR-054), on a mapped event (FR-050), and on an explicit request (FR-053) — never on a timer (FR-051).

parse_conditionfunction

Stability: provisional · Since 0.3.4

parse_condition(raw: 'Any', *, field_name: 'str' = 'done_when') -> 'Condition'

Parse a done_when mapping, rejecting anything outside the vocabulary.

The accepted shape is a single-key mapping: {term: {args}} for a term, {all: [condition, ...]} or {any: [...]} for a combinator. Called at manifest validation (FR-049), never at evaluation.