Compatibility¶
This page defines the support surface for benchmatrix. It should agree with
pyproject.toml, CI, nox, release notes, and the security policy.
Supported Python versions¶
benchmatrix supports CPython 3.11 through 3.14. The package metadata declares:
requires-python = ">=3.11,<3.15"
The local nox matrix runs tests on every supported Python version:
make test-matrix
Normal pull request CI verifies every supported Python version on Ubuntu and
runs the test suite on macOS and Windows for the current default Python version.
The primary quality job uses the repository .python-version.
Run make test-matrix locally before changes that may affect Python-version
compatibility.
Supported operating systems¶
The package is intended to be OS-independent Python code and is classified as
Operating System :: OS Independent.
Supported operating systems are:
- Linux;
- macOS;
- Windows.
Pull request CI runs the full supported-Python test matrix on Ubuntu and the current-Python test suite on macOS and Windows. Linux is the primary continuously verified platform. macOS and Windows are supported by design because benchmatrix does not rely on platform-specific APIs, but regressions that only appear on those systems may require a maintainer or contributor with access to the affected platform to confirm and validate the fix.
Supported architectures¶
benchmatrix is pure Python and does not ship compiled extensions. No architecture-specific behavior is part of the public contract.
Supported architectures are any architecture where a supported CPython version
and the runtime dependencies can be installed, including common x86_64 and
aarch64 environments. Architecture-specific issues in pytest, pytest-benchmark,
or optional development tools are handled according to impact and reproducibility.
Stable compatibility surfaces¶
The 1.x compatibility contract covers:
- names exported from the
benchmatrixpackage root; - documented constructors, call signatures, fields, properties, return values, accepted literal values, and exception hierarchies for those names;
- CLI commands, arguments, option meanings, defaults, output routing, and exit status meanings;
- the
[tool.benchmatrix]configuration schema, discovery and precedence rules, and built-in policy defaults; - benchmatrix metadata embedded in pytest-benchmark JSON, run-group manifests, comparison reports, and policy-inspection JSON.
Human-readable wording, whitespace, table layout, object representations, exception message text, private modules, and private names are not stable machine interfaces.
The compatibility rules are:
| Release | Allowed compatibility changes |
|---|---|
| Patch | Correct behavior within the documented contract, improve diagnostics, and reject input that was already invalid. |
| Minor | Add optional API, CLI, configuration, or document-schema capability while retaining every earlier 1.x contract. |
| Major | Remove, rename, reinterpret, or make incompatible changes to a stable surface. |
Urgent security or correctness fixes may require a narrower exception. Such an exception must be prominent in the changelog and release notes.
Public API stability¶
The stable public API is the set of names exported from benchmatrix.__init__
and documented in the generated API reference. Private modules and private names
are not stable extension points, including:
- modules whose names begin with
_, such as_schema.py; - functions, classes, constants, and attributes whose names begin with
_; - incidental implementation details not documented in user-facing docs.
Import stable names from the package root, for example
from benchmatrix import BenchmarkRun. Low-level manifest constants, tuple
aliases, and string-literal aliases are intentionally not re-exported from the
package root. Their concrete values may appear in public record fields and
serialized documents without making the aliases themselves stable import
locations.
Patch releases preserve documented public behavior except for urgent security or correctness fixes. Minor releases add backward-compatible behavior and may introduce deprecations. Incompatible changes to the stable public API require a major release.
For exported functions and methods, parameter names, positional versus keyword-only behavior, defaults, and return-value meaning are stable. A minor release may add an optional keyword-only parameter. Removing or renaming a parameter, adding a required argument, changing a default incompatibly, or changing the meaning of a return value requires a major release.
For exported record and policy classes, documented constructor fields, field order, properties, accepted values, and value semantics are stable. A minor release may add a property or an optional field with a backward-compatible default. Code should use named attributes rather than depend on object representations, hash values, or implementation details of frozen and slotted data classes.
InferencePolicy is the public configuration record for run-level uncertainty,
and BenchmarkInference is the per-cell result record. BenchmarkComparison
retains the earlier observed pairwise range fields and adds optional
inference and paired precision records; BenchmarkRunComparison retains its
earlier policy fields and identifies the independent or paired design plus the
effective inference and precision policies. Formal confidence bounds are
available only through the inference record.
BenchmarkPairedRunGroup, BenchmarkRunPair, and the paired record and
schedule classes expose the manifest-backed AB/BA lifecycle. Use
collect_paired_benchmark_runs and load_paired_benchmark_run_group to create
or restore it; use its complete-pair views or
compare_paired_benchmark_run_groups for paired inference. Pairing is explicit
and positional in the comparison function; optional fixed stratum labels carry
AB/BA orientation into design-preserving resampling. PrecisionPolicy,
PrecisionPlan, and plan_paired_precision describe the optional fresh-design
planning contract, including evidence-minimum and pair-count-multiple
constraints; they do not define power or sequential stopping.
Documented literal values such as metric names, compatibility modes, comparison statuses, regression classifications, and severity values retain their meaning throughout 1.x. Minor releases may add a value to an open set; callers should handle an unfamiliar value explicitly rather than assume exhaustive matching.
| Value domain | Values defined by 1.x |
|---|---|
| Metrics | single_call_latency, batch_throughput, tail_latency |
| Collection status | succeeded, failed |
| Paired variant | baseline, candidate |
| Paired order | AB, BA |
| Compatibility mode | strict, permissive, off |
| Compatibility severity | blocking, warning |
| Comparison direction | lower_is_better, higher_is_better |
| Comparison design | independent, paired |
| Comparison status | matched, missing_baseline, missing_candidate, incompatible |
| Regression classification | improved, unchanged, regressed, inconclusive, not_comparable |
| Threshold scope | cell, case, implementation, metric, default |
| Inference method | bca_bootstrap, legacy_consistency |
| Effective interval method | bca_bootstrap, percentile_bootstrap |
| Multiplicity correction | bonferroni, none |
| Precision method | paired_log_ratio_t |
| Policy selection | defaults, disabled, discovered, explicit |
| Threshold origin | built_in, configuration, cli |
All package-specific exceptions inherit from BenchmatrixError. JSON and policy
validation exceptions also remain ValueError subclasses, while collection
execution failures remain RuntimeError subclasses. Exception classes and base
relationships are stable; exact message wording is not.
CLI compatibility¶
benchmatrix and python -m benchmatrix are equivalent entry points. The root
command supports --version. These commands and options are stable in 1.x:
| Command | Arguments and options |
|---|---|
collect |
--runs, required --output, --resume, --retry-failed, --format with text or json, and the pytest command after -- |
collect-paired |
--pairs, required --output, --random-seed, --baseline-cwd, --candidate-cwd, --resume, --retry-failed, --format with text or json, and baseline/candidate commands after -- separated by ::: |
measure |
one or more pytest targets, the collection options, --inherit-pytest-addopts, and optional pytest arguments after -- |
compare |
baseline and candidate sources for an independent design, or one paired collection source with --paired; repeatable --baseline-run and --candidate-run, --threshold, --compatibility with strict, permissive, or off; --format with text, json, or markdown; text-only --summary; --github-summary, --fail-on-regression, --minimum-runs, --minimum-samples, --inference-method, --confidence-level, --bootstrap-resamples, --random-seed, --multiplicity, --precision-target, and mutually exclusive --config or --no-config |
policy show |
mutually exclusive --config or --no-config, --search-from, and --format with text or json |
policy validate |
the policy show options plus --quiet |
Independent measurement and collection default to five successful runs for a
new group and text output. Paired collection without an explicit target learns
the matrix and selects the smallest complete AB/BA-by-order-row supercycle with
at least five pairs. Measurement invokes pytest through the running Python
interpreter, uses quiet pytest and benchmark output, and ignores configured
pytest addopts unless --inherit-pytest-addopts is supplied.
Comparison defaults to text output and does not fail the process for a reported
regression unless --fail-on-regression is present. Policy commands default to
text output and normal configuration discovery. Adding an optional command or
option is allowed in a minor release; removing or renaming one, changing an
existing option's meaning or default incompatibly, or repurposing an exit code
requires a major release.
Comparison uses deterministic run-level BCa intervals, 95% family confidence,
50,000 resamples, seed zero, and Bonferroni multiplicity by default. A single
run cannot produce that interval and is inconclusive, even when
--minimum-runs 1 relaxes the evidence count.
--inference-method legacy_consistency selects the earlier non-inferential
rule explicitly.
--multiplicity none is an exploratory per-cell analysis without matrix-wide
error control.
Successful text, JSON, and Markdown content is written to standard output.
Diagnostics and usage errors are written to standard error. JSON modes do not
mix progress or diagnostic prose into standard output. --github-summary
appends Markdown to GITHUB_STEP_SUMMARY and does not replace the selected
standard-output format.
| Command outcome | Exit status |
|---|---|
| Successful operation or passing requested gate | 0 |
| Incomplete collection, or a requested comparison gate that regressed, was inconclusive, was incomplete, or was not comparable | 1 |
| Invalid arguments, configuration, environment, or input data | 2 |
Without --fail-on-regression, a completed comparison exits 0 even when the
report's passed field is false. Human-readable text and Markdown layout may
improve within 1.x and must not be parsed as a machine protocol. Comparison JSON
and policy JSON use the versioned schemas below. measure --format json and
collect --format json share a stable 1.x summary whose existing keys and
meanings will not be removed or changed; the manifest is the canonical archival
record.
Configuration compatibility¶
The [tool.benchmatrix] schema is strict. Unknown keys are errors so spelling
mistakes cannot silently weaken a gate.
| Table | Stable keys |
|---|---|
compatibility |
mode (strict, permissive, or off; default permissive) |
evidence |
minimum_runs (5), minimum_samples_per_run (5), minimum_rounds_per_run (5), require_rounds (true), require_iterations (true), require_raw_samples_for_inference (true), minimum_tail_samples_per_run (100), require_tail_iterations_one (true), maximum_cv (unset), maximum_outlier_fraction (unset) |
inference |
method (bca_bootstrap), confidence_level (0.95), resamples (50000), random_seed (0), multiplicity (bonferroni) |
precision |
target_half_width_percent (unset by default; paired design only) |
regression |
default_threshold_percent (5.0), by_metric, by_implementation, by_case, and by_cell |
Each by_cell entry |
implementation, case, metric, and threshold_percent |
Metric selectors are single_call_latency, batch_throughput, and
tail_latency. Implementation and case selectors are non-empty strings.
Thresholds are finite, non-negative percentages.
Policy selection and precedence are stable:
--no-configdisables discovery;--configselects an explicit file; otherwise the nearestpyproject.tomlis selected.- Discovery stops at the nearest project file even if it has no
[tool.benchmatrix]table. - CLI scalar overrides replace only their corresponding configured scalar and retain all selector mappings.
- Regression thresholds resolve from exact cell, case, implementation, metric, then the default.
- Values not configured come from the built-in defaults in the table above.
The stronger evidence defaults and interval-based decision rule are a
statistical-correctness exception to the usual 1.x decision-default stability.
They prevent two-run or single-run evidence from being presented as a
calibrated statistical conclusion. Use method = "legacy_consistency" only for
an explicit, non-inferential migration comparison.
Adding an optional configuration key is allowed in a minor release. Removing or renaming a key, changing its type or meaning, changing selector precedence, or changing a built-in decision default incompatibly requires a major release.
Serialized document compatibility¶
Every canonical JSON document identifies its benchmatrix producer and schema
version. Documents with a kind field must also be dispatched by kind:
| Document | Current writer identity | Versions read by 1.x |
|---|---|---|
Benchmark-row extra_info |
benchmatrix_producer = "benchmatrix", benchmatrix_schema_version = 1 |
1 |
| Run-group manifest | producer = "benchmatrix", kind = "benchmark_run_group", schema_version = 2 |
1, 2 |
| Paired run-group manifest | producer = "benchmatrix", kind = "benchmark_paired_run_group", schema_version = 1 |
1 |
| Comparison report | producer = "benchmatrix", kind = "benchmark_comparison", schema_version = 3 |
1, 2, 3 |
| Policy inspection or validation | producer = "benchmatrix", kind = "benchmark_policy", schema_version = 3 |
Consumer-dispatched; benchmatrix does not load this document |
The pytest-benchmark top-level JSON envelope belongs to pytest-benchmark.
benchmatrix stabilizes its own extra_info keys and validates the upstream
statistics and metadata needed to interpret them. Additional user metadata may
coexist in extra_info.
New run-group manifests use schema version 2. The loader continues to accept schema version 1 manifests produced before resumable collection was added. Version 2 preserves the same core fields but permits attempt records beyond the successful-run target so failed retries remain auditable. Version 1 manifests cannot contain those appended retry records.
Paired manifests have their own schema version 1 and document kind. They record both commands and working directories, the deterministic seed, whether the pair target was automatic, the resolved target, variant-specific commit and environment anchors, the crossed AB/BA and matrix-order schedule, block-attempt identity, and every command outcome. A complete inference pair consists only of two successes from one atomic block; the loader retains but never stitches orphan successes.
Comparison report schema version 2 added the effective inference policy,
per-cell estimand and confidence bounds, nominal and adjusted confidence
levels, family size, multiplicity method, resample count, deterministic seed,
fallback method, warnings, and issues. The current schema version 3 adds the
comparison design, paired collection snapshots, per-cell pair and resampling-
stratum counts, the effective precision policy, constrained and unconstrained
precision pair counts, and their minimum/multiple provenance. The existing
improvement_low_percent and improvement_high_percent fields remain a
descriptive observed effect range; they are not confidence bounds.
Report loading is strict for versions 1, 2, and 3: unsupported versions and
unknown, missing, or inconsistent fields raise BenchmarkJsonError. A version
1 report retains its historical classification and is loaded as an explicit
legacy, non-inferential result. Writing an earlier typed report emits the
current version 3 shape; loading does not recompute historical decisions.
Policy inspection and validation JSON uses document kind benchmark_policy
and schema version 3. Version 2 added the inference policy and stronger
evidence fields; version 3 adds the precision policy. Consumers should check
its producer, kind, schema version, and valid field before reading the
effective policy or validation error.
Writers emit one current version per document kind. Loaders in benchmatrix 1.x continue to read every schema version emitted by a 1.x release, plus the grandfathered version 1 run-group manifest. Dropping one of those read versions requires a benchmatrix major release.
Adding, removing, renaming, or changing the type, requirement status, enum domain, or meaning of a serialized field requires a document schema-version increment. Fixing validation to enforce the existing schema does not. A new document schema may ship in a minor benchmatrix release when the same release retains all earlier 1.x readers. Existing schema versions are never reinterpreted incompatibly.
Supported release branches¶
The active support branch is main.
The project does not currently maintain long-lived release branches. Security and
compatibility fixes are normally released from main in the next patch or minor
release. Temporary release branches may be created for an active release or
security fix, but they are not a standing support channel unless announced in the
release notes.
Security-fix policy¶
Security fixes are provided for the latest minor line in the current major series. Users must run its latest patch to receive fixes. Older minor lines and previous major series do not receive routine backports or long-term support.
A backport to an older release may be considered when all of the following are true:
- the issue is high impact for installed users;
- a safe, minimal patch can be prepared without carrying substantial branch maintenance cost;
- the affected release still has meaningful user adoption;
- the maintainer has capacity to validate and publish the backport.
See SECURITY.md and the security-report runbook for reporting and disclosure
steps.
Deprecation policy¶
Deprecations are used for documented public API or behavior when users need a migration path. Private implementation details do not require deprecation.
Deprecated public API normally remains available until the next major release unless removal is needed for security or correctness.
Each deprecation should include:
- the replacement or migration path;
- a changelog entry;
- removal timing when known;
- tests that preserve the deprecated behavior until removal.
Runtime scope¶
benchmatrix is intended for synchronous Python callables. Async functions, concurrent service load tests, and production latency monitoring are outside the current scope.
pytest-benchmark¶
pytest-benchmark remains the timing engine. benchmatrix relies on its fixture, statistics, and JSON output format, then layers metadata and parsing conventions on top.