|
oct
|
Classes | |
| class | CacheStore |
| class | _ModuleIndex |
| class | CacheKeyBuilder |
| class | CacheDecision |
| class | _CacheRun |
Functions | |
| Path | store_path (Path root) |
| datetime | _now () |
| str | _stamp (datetime moment) |
| datetime|None | _parse_stamp (object text) |
| None | clear_store (Path root) |
| dict[str, Any] | read_last_run (Path root) |
| bytes|None | _read_bytes (Path path) |
| bool | _is_namespace_package (Path path) |
| tuple[Path,...] | _conftest_paths (Path root, Path path) |
| None | _feed (Any digest, bytes label, bytes payload) |
| str | _interpreter_identity () |
| str | _oct_version () |
| bool | is_security_file (str relpath) |
| ast.Module|None | _source_tree (Path path) |
| str|None | _dotted_name (ast.AST node) |
| bool | _imports_hypothesis (ast.Module tree) |
| bool | _pins_derandomize (ast.Module tree) |
| bool | is_unpinned_property_file (Path path, *, Path|None root=None) |
| tuple[str,...] | _unpinned_property_files (ParallelManifest manifest, Path root) |
| tuple[str,...] | eligible_files (ParallelManifest manifest, *, Path|None root=None) |
| CacheDecision | decide (Iterable[str] candidates, CacheKeyBuilder keyer, CacheStore store) |
| list[str] | report_lines (CacheDecision decision, *, bool verbose=False) |
| None | add_cache_options (Any parser) |
| None | install (Any config, *, Path|None manifest_path=None) |
| bool | _is_intentional_skip (Any report) |
| tuple[set[str], set[str]] | _file_outcomes (Any terminalreporter) |
| None | pytest_addoption (Any parser) |
| None | pytest_configure (Any config) |
| None | pytest_sessionstart (Any session) |
| None | pytest_collection_modifyitems (Any config, list items) |
| None | pytest_terminal_summary (Any terminalreporter, Any exitstatus, Any config) |
Variables | |
| str | CACHE_OPTION = "--oct-cache" |
| str | CACHE_CLEAR_OPTION = "--oct-cache-clear" |
| str | CACHE_ENV = "OCT_TEST_CACHE" |
| str | PLUGIN_NAME = "oct.testing.cache" |
| str | STORE_SCHEMA_VERSION = "1.0" |
| tuple | STORE_RELPATH = (".option_c", "cache", "test-cache.json") |
| int | PRUNE_DAYS = 30 |
| str | TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" |
| str | SECURITY_FRAGMENT = "/tests/security/" |
| str | KIND_WORKSPACE = "workspace" |
| str | KIND_EXTERNAL = "external" |
| str | KIND_ABSENT = "absent" |
| str | KIND_MISSING = "missing" |
| bytes | NO_PYTEST_INI = b"<no pytest.ini>" |
| str | _OPTIONS_MARK = "_oct_cache_options_registered" |
| str | _MANIFEST_ATTR = "_oct_cache_manifest_path" |
| str | _RUN_ATTR = "_oct_cache_run" |
| frozenset | _BAD_CATEGORIES |
Purpose
-------
The content-hashed test cache (Plan 249 / TEST-03). uv's principle -
rebuild only when the inputs change - applied to a test suite: a test
FILE whose every input hashes to a value that was already green is not
run again. The cache may only ever skip a file it can PROVE unchanged;
every doubt runs the file.
Responsibilities
----------------
- Pin the key (P1): SHA-256 over the test file's bytes, the bytes of
every workspace module reachable from its imports, the bytes of
every ``conftest.py`` on its path, its plan-239 manifest entry, the
manifest ``schema_version``, the interpreter, the oct version and
the ``pytest.ini`` bytes. An input it cannot resolve yields ``None``,
and a file without a key is never skipped
(:class:`CacheKeyBuilder`).
- Own the store (P2): ``<root>/.option_c/cache/test-cache.json``,
key -> the UTC time of the last run in which every test of that file
passed under that key, emptied on a manifest regeneration and pruned
at :data:`PRUNE_DAYS` (:class:`CacheStore`).
- Pin the skip policy (P3): only a ``free``, keyed, green,
non-``tests/security/`` file whose outcome is deterministic;
a Hypothesis import without ``derandomize=True`` never enters the
candidate set (:func:`eligible_files`, :func:`decide`).
- Be the pytest plugin that applies it (P4), off unless
:data:`CACHE_OPTION` or :data:`CACHE_ENV` says otherwise, and the
seams a root conftest calls (:func:`add_cache_options`,
:func:`install`).
- Record what a run did so ``oct test --cached`` can put it in the
plan-245 ledger (P5, :func:`read_last_run`).
Diagnostics
-----------
Domain: OCT-TESTING
Levels:
L2: lifecycle (store load/save, decision taken)
L3: semantic details (skip counts, unresolved imports)
L4: deep tracing (per-file keys and closures)
Contracts
---------
- FAIL CLOSED everywhere. A missing manifest entry, an unreadable
file, an import that resolves to nothing, a manifest that has been
regenerated since the store was written, a syntax error: each of
them means RUN THE FILE, never "assume unchanged".
- The cache is NEVER on by default and never at a gate: the plugin
does nothing unless the command line carries :data:`CACHE_OPTION` or
the environment carries ``OCT_TEST_CACHE=1``.
- ``tests/security/`` is excluded unconditionally: the M8 witnesses
always run.
- A file importing Hypothesis is never eligible unless that file or a
``conftest.py`` on its path contains an explicit
``derandomize=True`` settings keyword.
- A module OUTSIDE the workspace (standard library, site-packages) is
identified and EXCLUDED from the closure, exactly as P1 requires.
A name that resolves NOWHERE - an optional third-party import behind
a ``try``/``except``, of which the oc_chat closure holds five - is
not a first-party input either, so it cannot fail the key closed
without disabling the cache entirely (measured 2026-09-08: treating
it as a doubt left 0 of 221 eligible oc_chat files keyable). Its
ABSENCE is recorded in the key instead, so installing the package
invalidates every file that reaches it. A FIRST-PARTY input the
resolver cannot follow - an unparsable module, a workspace file it
cannot read, a relative import escaping the workspace - still makes
the key ``None``.
- ``pytest`` is NEVER imported at module scope: it is an optional
(``[test]`` extra) dependency of oct, so the plugin hooks below are
plain functions that pytest discovers by name.
- No ``sys.exit`` anywhere in this module.
Dependencies
------------
- oct.core.diagnostics (_dbg)
- oct.core.workspace (lazy; read-only: the subproject roots the resolver treats as workspace package roots)
- oct.testing.errors (the spec-10 ยง12.4 taxonomy as data)
- oct.testing.parallel (the committed classification and its groups)
- oct.tools.atomic_write (atomic_write_text for saving the test-cache store)
- oct (lazy; the installed oct version recorded in the cache key)
|
protected |
|
protected |
|
protected |
|
protected |
Split the run's reports into green and not-green FILES.
Returns
-------
tuple
``(green, bad)`` sets of nodeid path parts. A file in *bad* is
never in *green*: one failure is enough.
Definition at line 1364 of file cache.py.
|
protected |
|
protected |
|
protected |
True when pytest recorded a REASON for a skip. ``skip``/``skipif``/``pytest.skip`` all produce the standard ``(path, lineno, reason)`` triple. A skip with no readable reason is not evidence that the file passed, so it is not treated as one.
Definition at line 1348 of file cache.py.
|
protected |
True when *path* is an importable directory with no ``__init__``. ``oc_chat/tools/`` is the live example: a directory of modules that six test files import as ``tools.<name>``. The ``.py`` requirement is what keeps ``docs/`` and ``plans/`` from being read as packages merely because they are directories beside one.
Definition at line 362 of file cache.py.
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
| None oct.testing.cache.add_cache_options | ( | Any | parser | ) |
Register the cache options, at most once per run (P4). Safe to call from BOTH a root conftest's ``pytest_addoption`` and this module's own, because pytest hands every implementation the same :class:`~_pytest.config.argparsing.Parser`.
Definition at line 1150 of file cache.py.
| None oct.testing.cache.clear_store | ( | Path | root | ) |
| CacheDecision oct.testing.cache.decide | ( | Iterable[str] | candidates, |
| CacheKeyBuilder | keyer, | ||
| CacheStore | store ) |
| tuple[str, ...] oct.testing.cache.eligible_files | ( | ParallelManifest | manifest, |
| * | , | ||
| Path | None | root = None ) |
Every manifest file a cache may EVER skip (P3). A non-free file can vary with shared state, a security witness must always run, and a Hypothesis file without ``derandomize=True`` can vary with an unkeyed seed. Each is excluded when *root* makes source inspection available; omitting *root* preserves the manifest-only compatibility seam.
Definition at line 1020 of file cache.py.
| None oct.testing.cache.install | ( | Any | config, |
| * | , | ||
| Path | None | manifest_path = None ) |
Attach this plugin to *config* (the root-conftest seam). Pins the classification the cache reads and registers the module as a plugin when ``-p oct.testing.cache`` did not already. The manifest path is read LAZILY, so it does not matter whether this call happens before or after the plugin's own ``pytest_configure``.
| bool oct.testing.cache.is_security_file | ( | str | relpath | ) |
| bool oct.testing.cache.is_unpinned_property_file | ( | Path | path, |
| * | , | ||
| Path | None | root = None ) |
| None oct.testing.cache.pytest_addoption | ( | Any | parser | ) |
| None oct.testing.cache.pytest_collection_modifyitems | ( | Any | config, |
| list | items ) |
| None oct.testing.cache.pytest_configure | ( | Any | config | ) |
| None oct.testing.cache.pytest_sessionstart | ( | Any | session | ) |
Report the decision once, on the process the operator watches. Under xdist the controller does not collect, so the deselection below happens in the workers and their output is never shown; the decision is deterministic, so the controller reports exactly what the workers then apply.
Definition at line 1419 of file cache.py.
| None oct.testing.cache.pytest_terminal_summary | ( | Any | terminalreporter, |
| Any | exitstatus, | ||
| Any | config ) |
| dict[str, Any] oct.testing.cache.read_last_run | ( | Path | root | ) |
What the last cached run under *root* did (P5).
Returns
-------
dict
``{"at": ..., "ran": N, "cached": M}``, or an empty dict when
no store exists or it carries no record. The caller treats an
empty dict as "no measurement", never as zero.
Definition at line 307 of file cache.py.
| list[str] oct.testing.cache.report_lines | ( | CacheDecision | decision, |
| * | , | ||
| bool | verbose = False ) |
The cache and deterministic-property reports for one run (P3). Cached files are reported once by count and, under ``-v``, by name. Every unpinned Hypothesis file is always named once so the refusal is actionable even when no file is skipped.
Definition at line 1102 of file cache.py.
| Path oct.testing.cache.store_path | ( | Path | root | ) |
|
protected |
|
protected |
|
protected |
|
protected |
| str oct.testing.cache.CACHE_CLEAR_OPTION = "--oct-cache-clear" |
| str oct.testing.cache.SECURITY_FRAGMENT = "/tests/security/" |
| tuple oct.testing.cache.STORE_RELPATH = (".option_c", "cache", "test-cache.json") |