Skip to content

sift_client.pytest_plugin

Sift pytest plugin: records each test as a step in a Sift test report.

Load it from a project's conftest.py::

pytest_plugins = ["sift_client.pytest_plugin"]

This module holds only the plugin's public surface: the catchable warnings, the session-state globals a conftest may read, the fixtures a project can request or override, and pytest's hook entry points. The implementation (settings registry, step stacks, report construction, terminal formatting) lives under sift_client._internal.pytest_plugin.

CLASS DESCRIPTION
SiftPytestPluginWarning

Base warning for issues raised by the Sift pytest plugin.

SiftPytestStepDrainError

Raised when mid-session drain fails, signaling a likely upstream invariant break.

SiftPytestStepDrainWarning

A step's __exit__ raised while the plugin was draining its stack.

FUNCTION DESCRIPTION
client_has_connection

Verify the SiftClient can reach Sift via /ping.

report_context

Lazy session-scoped Sift ReportContext.

sift_client

Default SiftClient resolved from environment variables and ini keys.

step

Create an outer step for the function when the Sift gate is on.

ATTRIBUTE DESCRIPTION
REPORT_CONTEXT

TYPE: Any

SIFT_REPORT_ID_STASH_KEY

SIFT_REPORT_URL_STASH_KEY

REPORT_CONTEXT module-attribute

REPORT_CONTEXT: Any = None

SIFT_REPORT_ID_STASH_KEY module-attribute

SIFT_REPORT_ID_STASH_KEY = StashKey[str]()

SIFT_REPORT_URL_STASH_KEY module-attribute

SIFT_REPORT_URL_STASH_KEY = StashKey[str]()

SiftPytestPluginWarning

Bases: SiftWarning

Base warning for issues raised by the Sift pytest plugin.

SiftPytestStepDrainError

Bases: RuntimeError

Raised when mid-session drain fails, signaling a likely upstream invariant break.

SiftPytestStepDrainWarning

Bases: SiftPytestPluginWarning

A step's __exit__ raised while the plugin was draining its stack.

Surfaced at module-teardown or session-end so the drain can continue and pytest test outcomes stay unaffected; the underlying exception is included in the message for debugging.

client_has_connection

client_has_connection(
    pytestconfig: Config, request: FixtureRequest
) -> bool

Verify the SiftClient can reach Sift via /ping.

Consulted at session start by report_context in online mode. A failed ping aborts the session via pytest.exit. Override this fixture in your conftest to use a different reachability signal (e.g. a cached auth token) for environments where pinging is the wrong check. Returns False in --sift-disabled mode without constructing a client.

pytest_addoption

pytest_addoption(parser: Parser) -> None

Register every CLI flag and pytest ini key declared in PLUGIN_OPTIONS.

pytest_collection_modifyitems

pytest_collection_modifyitems(
    config: Config, items: list[Item]
) -> None

Stash each item's class chain + parametrize path and cluster siblings.

Sorts by (file_path, hierarchy_chain, parametrize_path) so sibling items under a shared parent (package, module, class, or parametrize axis) stay contiguous; otherwise a free function sorting between two class methods would tear down + re-open the class step, producing duplicate parents in the report tree.

pytest_configure

pytest_configure(config: Config) -> None

Register the Sift gate markers and warn on unknown SIFT_* settings.

pytest_report_header

pytest_report_header(config: Config) -> str | None

Emit a session-start header with the SDK version and active mode.

Suppressed under -q (negative verbosity), matching how pytest hides its own platform/plugin header.

pytest_runtest_makereport

pytest_runtest_makereport(item: Item, call: CallInfo[Any])

Capture per-phase reports and finalize step status after teardown.

Stashes both rep_<when> (the CallInfo, kept for pytest plugins that expect that conventional attribute) and _sift_phase_<when> (a SimpleNamespace(call, report) used by resolve_initial_status). The collection-time skip path is strictly gated on _sift_step being unset so it does not duplicate steps the fixture already created.

pytest_sessionfinish

pytest_sessionfinish(
    session: Session, exitstatus: int
) -> None

Drain any parent steps still open at session end (innermost first).

Wrapped so a failure in the inner drain does not prevent the outer one from running. With module_substep removed, this is the sole place where hierarchy parents close; they persist across all tests and only drain when the session ends.

pytest_terminal_summary

pytest_terminal_summary(
    terminalreporter: Any, exitstatus: int, config: Config
) -> None

Emit a session-end Sift report summary, adapting per mode.

The printed panel is suppressed under -q, but programmatic side effects (stashing the report ref for conftest.py, --sift-open-report) still run so other plugins and CI steps can consume the result. The panel itself is rendered by write_report_summary; this hook handles the side effects.

report_context

report_context(
    request: FixtureRequest, pytestconfig: Config
) -> Generator[ReportContext, None, None]

Lazy session-scoped Sift ReportContext.

The fixture is no longer autouse; it's instantiated on the first call to request.getfixturevalue("report_context"), which today happens inside the gated step, _hierarchy_parents, and _parametrize_parents fixtures. If every test in the session is excluded via the marker gate, this fixture is never resolved and no ReportContext (or teardown subprocess) is created.

What gets yielded depends on the mode:

  • --sift-disabled: a real ReportContext against a placeholder SiftClient with _simulate=True. Every test-results write returns a synthesized response without contacting Sift; no log file is written; the replay subprocess never spawns. Test code that calls step.measure(...) keeps working because bounds are evaluated as usual and routed through the simulate path.
  • --sift-offline: a real ReportContext, but the session-start ping is skipped, all create/update calls go to the JSONL log file, and the import-test-result-log replay subprocess is not spawned at session end.
  • default (online): verify connectivity via client_has_connection before constructing the context. A failed ping aborts the session with pytest.exit and points at --sift-offline and --sift-disabled as escape hatches.

The log-file destination is controlled by --sift-log-file; defaults to a temp file when unset.

sift_client

sift_client(pytestconfig: Config) -> SiftClient

Default SiftClient resolved from environment variables and ini keys.

Each credential is read from its environment variable first. The URIs (SIFT_GRPC_URI, SIFT_REST_URI) additionally fall back to the sift_grpc_uri / sift_rest_uri ini keys, since they are stable per-org values that are safe to commit. SIFT_API_KEY is intentionally env-only; use pytest-dotenv (already a project dependency) to load it from a .env file kept out of version control.

Projects that need custom construction (TLS toggles, custom timeouts, etc.) can override this fixture by defining their own sift_client in their conftest.py; pytest fixture resolution prefers the local definition.

In --sift-offline mode the missing-credential check is relaxed: real env vars and ini values still win when set (so the client is constructible against a real backend even though no calls are made), but anything still missing is filled with a placeholder. In --sift-disabled mode the credential resolution is skipped entirely and placeholders are always used.

step

step(
    request: FixtureRequest,
    pytestconfig: Config,
    _parametrize_parents: None,
) -> Generator[NewStep | None, None, None]

Create an outer step for the function when the Sift gate is on.

Resolves the gate via gate_enabled: the sift_exclude marker forces off, sift_include forces on, otherwise the sift_autouse ini default applies. When on, requests the session report_context lazily; the first gated test in the session triggers its creation, subsequent gated tests reuse it. In --sift-disabled mode the report context is backed by a SiftClient(_simulate=True) placeholder, so every write returns a synthesized response without contacting Sift.