oc_chat
Loading...
Searching...
No Matches
oc_chat.testing.scheduler Namespace Reference

Classes

class  SchedulerUnavailable
class  WorkerPlan
class  ScheduleUnit
class  ChildRequest
class  UnitOutcome

Functions

Any load_parallel_module (Path|None workspace_root=None)
Path manifest_path_for (Path root, Sequence[str] testpaths, Path|None workspace_root=None)
tuple[dict[str, str], tuple[str,...]] load_groups (Path manifest_path, Iterable[str] relpaths, Path|None workspace_root=None)
WorkerPlan resolve_worker_plan (int|str requested, Path workspace_root, str package, *, int|None cpu_count=None)
dict clamp_error (WorkerPlan plan)
tuple[ScheduleUnit,...] build_schedule (Mapping[str, str] groups, Mapping[str, Sequence[str]] nodeids_by_file, *, Iterable[str] whole_file=(), int|None max_free_unit_tests=None)
None child_entry (ChildRequest request, str result_path)
Any _spawn (ScheduleUnit unit, *, Path root, Sequence[str] pytest_args, int|None timeout, Path result_path)
UnitOutcome _read_result (ScheduleUnit unit, Any process, Path result_path, float started, float finished)
tuple[list[UnitOutcome], bool] run_units (Sequence[ScheduleUnit] units, *, int workers, Path root, Sequence[str] pytest_args, int|None timeout, float origin)
dict crash_violation (UnitOutcome outcome)
dict crash_error (UnitOutcome outcome)
dict unclassified_violation (Path root, Sequence[str] missing)

Variables

int DEFAULT_WORKER_CAP = 24
str GROUP_UNCLASSIFIED = "unclassified-serial"
str GROUP_FREE = "free"
str WHOLE_FILE_MARKER = "whole_file"
int MAX_FREE_UNIT_TESTS = 100
str DIST_MODE_PARALLEL = "oct_det_loadscope"
str DIST_MODE_SERIAL = "serial"
str CODE_WORKERS_CLAMPED = "9007"
float _POLL_SECONDS = 0.02

Detailed Description

The §20 stand-in's parallel scheduler (Plan 240; ADR-T-15 slice).

Purpose
-------
Turn ``--workers N | auto`` into real child processes without inventing
a second classifier. The plan-239 mechanism in ``oct.testing.parallel``
owns the resource vocabulary, the committed
``parallel_manifest.json``, the grouping and the worker cap; this
module imports all four and does exactly three things the pytest side
does not need: it resolves the requested worker count against the cap,
it turns the manifest's groups into scheduling units, and it runs those
units in a bounded pool of children, reconciling their results into the
single §11 report.

Responsibilities
----------------
- ``resolve_worker_plan`` — P2: ``auto`` = min(cap, cores); an explicit
  count above the cap CLAMPS and carries the note that says so.
- ``build_schedule`` — P3 as Plan 286 amended it: a group is one unit;
  a ``free`` file is cut, in collection order, into units of at most
  ``MAX_FREE_UNIT_TESTS`` tests, unless it carries the ``whole_file``
  opt-out; units run largest first by collected count; unclassified
  files share one serial unit. Deterministic for any input order.
- ``load_groups`` — the manifest read, delegated to plan 239.
- ``run_units`` — the bounded pool: one spawned child per unit, each
  with its own §20.2 snapshot, reconciled fail-closed.
- ``child_entry`` — the child-side entry point (module level so the
  ``spawn`` start method can import it).

Diagnostics
-----------
Domain: OC_TESTING
Levels:
    L2 — lifecycle events (pool start/stop, unit completion)
    L3 — semantic details (resolved counts, unit shapes)
    L4 — deep tracing

Contracts
---------
- This module NEVER classifies a test file. Every group it uses comes
  from the committed manifest through ``oct.testing.parallel``; a file
  with no entry is :data:`GROUP_UNCLASSIFIED` and is reported, never
  guessed at (P1).
- A child that dies without writing its result is a CRASH: its nodeids
  are reported failed, never passed and never dropped (P3, spec-10
  §8.4 worker-crash semantics).
- The start method is always ``spawn``, so a child inherits the
  parent's ``sys.path`` (the F17 shadow guard) and nothing else of the
  parent's in-process state.
- Ordering of everything this module returns is derived from the
  schedule, not from completion order.

Dependencies
------------
- oc_diagnostics (structured debug logging via _dbg)
- oct.testing.parallel (lazy; Plan 239: manifest, groups, worker cap)
- oc_chat.testing.report (violation/error constructors)
- oc_chat.testing.runner (lazy; the in-process session, imported in the child to keep the module graph acyclic)

Function Documentation

◆ _read_result()

UnitOutcome oc_chat.testing.scheduler._read_result ( ScheduleUnit unit,
Any process,
Path result_path,
float started,
float finished )
protected
Turn a finished child into a :class:`UnitOutcome`, fail-closed.

Definition at line 547 of file scheduler.py.

Here is the caller graph for this function:

◆ _spawn()

Any oc_chat.testing.scheduler._spawn ( ScheduleUnit unit,
* ,
Path root,
Sequence[str] pytest_args,
int | None timeout,
Path result_path )
protected
Start one child for *unit* and return the live process.

Definition at line 519 of file scheduler.py.

Here is the caller graph for this function:

◆ build_schedule()

tuple[ScheduleUnit, ...] oc_chat.testing.scheduler.build_schedule ( Mapping[str, str] groups,
Mapping[str, Sequence[str]] nodeids_by_file,
* ,
Iterable[str] whole_file = (),
int | None max_free_unit_tests = None )
Turn the plan-239 grouping into scheduling units (P3, Plan 286).

A group is the unit of scheduling: every file of a group runs in
ONE child, because that is what the group means, and the
unclassified set shares one serial unit because fail-closed means
serial.

A ``free`` file shares no resource with anything, so its tests may
run anywhere — including in different children (Plan 286 P2). Its
nodeids are therefore cut, in collection order, into units of at
most *max_free_unit_tests* tests, so that one file with hundreds of
tests no longer holds a child for its whole length. A file listed
in *whole_file* is the opt-out: it stays ONE unit however many
tests it has.

Units are scheduled largest first by collected count (longest
processing time first — the classic bound for a bounded pool) and
the result is deterministic for any input order.

Parameters
----------
groups
    Root-relative path -> plan-239 group name.
nodeids_by_file
    Root-relative path -> the parent's selection, in collection
    order.
whole_file
    Root-relative paths whose tests must stay in one unit: the
    files the parent's collect pass saw carrying
    :data:`WHOLE_FILE_MARKER`.
max_free_unit_tests
    Override for :data:`MAX_FREE_UNIT_TESTS`; values below 1 are
    read as 1, so a caller can never ask for an empty unit.

Definition at line 330 of file scheduler.py.

◆ child_entry()

None oc_chat.testing.scheduler.child_entry ( ChildRequest request,
str result_path )
Run one unit and write its result document (the spawn target).

Imports the runner lazily: the runner imports THIS module at module
scope, and a child that re-imported it eagerly would close the
cycle. Any exception is recorded in the result document rather than
raised, so the parent distinguishes "the unit failed" from "the
child died".

Definition at line 465 of file scheduler.py.

◆ clamp_error()

dict oc_chat.testing.scheduler.clamp_error ( WorkerPlan plan)
The §11 ``errors[]`` entry that reports a clamp.

Definition at line 294 of file scheduler.py.

◆ crash_error()

dict oc_chat.testing.scheduler.crash_error ( UnitOutcome outcome)
The §12.4 ``WORKER_CRASHED`` entry a dead child produces.

Definition at line 672 of file scheduler.py.

◆ crash_violation()

dict oc_chat.testing.scheduler.crash_violation ( UnitOutcome outcome)
The §11 ``contract_violations[]`` row a dead child produces.

Definition at line 660 of file scheduler.py.

◆ load_groups()

tuple[dict[str, str], tuple[str, ...]] oc_chat.testing.scheduler.load_groups ( Path manifest_path,
Iterable[str] relpaths,
Path | None workspace_root = None )
Map root-relative files to their groups through plan 239.

Returns
-------
tuple
    ``(path -> group, missing)``. A collected file with no manifest
    entry maps to :data:`GROUP_UNCLASSIFIED` and is listed in
    *missing* — the fail-closed state a stale manifest produces.

Definition at line 191 of file scheduler.py.

Here is the call graph for this function:

◆ load_parallel_module()

Any oc_chat.testing.scheduler.load_parallel_module ( Path | None workspace_root = None)
Import ``oct.testing.parallel``, falling back to the checkout.

In a worktree the editable ``oct`` install may resolve to ANOTHER
checkout that does not carry the module yet; the sibling
subproject in this repository always does. Only that ONE module is
loaded by path — the rest of ``oct`` keeps resolving normally.
This mirrors the root conftest's fallback (Plan 239), because the
stand-in runs in exactly the same trees.

Definition at line 138 of file scheduler.py.

Here is the caller graph for this function:

◆ manifest_path_for()

Path oc_chat.testing.scheduler.manifest_path_for ( Path root,
Sequence[str] testpaths,
Path | None workspace_root = None )
The committed manifest implied by *testpaths* (Plan 239 P4).

Definition at line 180 of file scheduler.py.

Here is the call graph for this function:

◆ resolve_worker_plan()

WorkerPlan oc_chat.testing.scheduler.resolve_worker_plan ( int | str requested,
Path workspace_root,
str package,
* ,
int | None cpu_count = None )
Resolve *requested* against the plan-239 cap (P2).

``auto`` resolves to ``min(cap, cores)``; an explicit count above
the cap is clamped to it and the clamp is reported, never silent.

Definition at line 244 of file scheduler.py.

Here is the call graph for this function:

◆ run_units()

tuple[list[UnitOutcome], bool] oc_chat.testing.scheduler.run_units ( Sequence[ScheduleUnit] units,
* ,
int workers,
Path root,
Sequence[str] pytest_args,
int | None timeout,
float origin )
Run *units* in a bounded pool of children.

Parameters
----------
workers
    Maximum number of children alive at once.
origin
    The run's ``time.monotonic()`` zero, so every window this
    returns is relative to the report's own clock.

Returns
-------
tuple
    ``(outcomes in schedule order, interrupted)``. On interruption
    the still-running children are terminated and the units that
    never started are absent — the caller reports a partial run.

Definition at line 586 of file scheduler.py.

Here is the call graph for this function:

◆ unclassified_violation()

dict oc_chat.testing.scheduler.unclassified_violation ( Path root,
Sequence[str] missing )
The §11 row an absent manifest entry produces (P1).

Definition at line 681 of file scheduler.py.

Variable Documentation

◆ _POLL_SECONDS

float oc_chat.testing.scheduler._POLL_SECONDS = 0.02
protected

Definition at line 127 of file scheduler.py.

◆ CODE_WORKERS_CLAMPED

str oc_chat.testing.scheduler.CODE_WORKERS_CLAMPED = "9007"

Definition at line 124 of file scheduler.py.

◆ DEFAULT_WORKER_CAP

int oc_chat.testing.scheduler.DEFAULT_WORKER_CAP = 24

Definition at line 84 of file scheduler.py.

◆ DIST_MODE_PARALLEL

str oc_chat.testing.scheduler.DIST_MODE_PARALLEL = "oct_det_loadscope"

Definition at line 118 of file scheduler.py.

◆ DIST_MODE_SERIAL

str oc_chat.testing.scheduler.DIST_MODE_SERIAL = "serial"

Definition at line 121 of file scheduler.py.

◆ GROUP_FREE

str oc_chat.testing.scheduler.GROUP_FREE = "free"

Definition at line 90 of file scheduler.py.

◆ GROUP_UNCLASSIFIED

str oc_chat.testing.scheduler.GROUP_UNCLASSIFIED = "unclassified-serial"

Definition at line 87 of file scheduler.py.

◆ MAX_FREE_UNIT_TESTS

int oc_chat.testing.scheduler.MAX_FREE_UNIT_TESTS = 100

Definition at line 114 of file scheduler.py.

◆ WHOLE_FILE_MARKER

str oc_chat.testing.scheduler.WHOLE_FILE_MARKER = "whole_file"

Definition at line 99 of file scheduler.py.