Skip to main content

A fast, drop-in compatible pytest runner written in Rust

Project description

pytest-rs

pytest-rs is a re-implementation of the popular Python testing framework pytest in Rust, focused on speed: a drop-in compatible runner where startup, collection, fixture orchestration, coverage measurement, and reporting are native code, while test bodies run on embedded CPython.

Note: This project is currently in active development (alpha stage). Many features are still under implementation and subject to change. See docs/DESIGN.md for the architecture and roadmap.

pytest-rs is an independent project, not affiliated with or endorsed by the pytest project.

Installation

Prebuilt wheels are published to PyPI for Linux (x86_64 / aarch64) and macOS (arm64) on CPython 3.13 / 3.14:

uv add --dev pytest-rs    # or: pip install pytest-rs

Then run your existing suite, no changes needed:

pytest-rs                       # whole suite, like `pytest`
pytest-rs tests/test_foo.py     # one file
pytest-rs -n 4                  # parallel workers (pytest-xdist compatible)
pytest-rs --cov=mypkg           # native coverage (pytest-cov compatible)

pytest-rs reads the same configuration pytest does (pytest.ini, pyproject.toml [tool.pytest] / [tool.pytest.ini_options], tox.ini, setup.cfg) and understands the familiar flags (-v, -x, -k, -m, --lf, --tb=..., -p no:NAME, ...). It installs alongside pytest without conflict — the pytest command is untouched.

Requirements

  • Linux or macOS (no Windows support yet)
  • CPython 3.13+ built with a shared libpython — true for uv-managed Pythons, python.org installers, Homebrew, conda, and distro packages. Plain pyenv builds need PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install ....

Bundled plugins

The compatibility layers for pytest-asyncio, anyio's pytest plugin, pytest-mock, pytest-cov, pytest-split, pytest-benchmark and pytest-xdist-style -n parallelism are built in — no separate plugin installs (the anyio layer runs tests through the installed anyio library's backends, so anyio itself must be in the environment as usual). Two ways to turn features off:

Per project or per run, like pytest (works with the prebuilt wheel):

[tool.pytest.ini_options]
addopts = "-p no:benchmark -p no:split"

At build time, when installing from source — bundled plugins are Cargo features, all enabled by default:

[tool.uv]
config-settings-package = { pytest-rs = { build-args = "--no-default-features --features asyncio,mock" } }

Third-party plugins (not reimplemented, loaded as-is)

Installed pytest11 entry points load through the pytest API shim — plugins pytest-rs does not reimplement can still work as-is. The supported surface includes fixtures, markers, pytest_addoption (plugin --flags and ini options), config.stash, custom hookspecs (pytest_addhooks), pytest_runtest_protocol/pytest_runtest_call hookwrappers, custom collectors (pytest_collect_fileFile/Item), and terminal-reporter replacement (a plugin that unregisters the terminalreporter plugin and registers its own subclass takes over the output — pytest-rs suppresses its native rendering and drives the replacement through the same hooks pluggy would). Verified status:

evidence plugins
own upstream test suite runs under pytest-rs and gates CI (per-suite pass-rate in the Third-party plugins conformance table below) pytest-timeout, pytest-randomly, pytest-env, pytest-socket, pytest-snapshot, pytest-ruff, pytest-rerunfailures, pytest-order, pytest-repeat, pytest-instafail, pytest-icdiff, pytest-metadata, pytest-subtests, pytest-mypy, pytest-bdd, pytest-django; anyio's own plugin module also loads this way
functional smoke demo gates CI (conformance/plugin_smoke.py) Faker, time-machine, requests-mock, inline-snapshot (snapshot assertions + --inline-snapshot flag), pytest-run-parallel (--parallel-threads really runs each test on N threads)
reporter replacement — terminal output byte-diffed against real pytest 9.0.3 pytest-pretty, pytest-sugar (progress bar, instant failures; activates on a tty or --force-sugar)
not reimplemented yet pytest-html (needs the report data model exposed); syrupy (serializer/extension framework)

A plugin that fails to import (e.g. it reaches into pytest/pluggy internals the shim doesn't provide) warns and is skipped without breaking the run. -p no:NAME and PYTEST_DISABLE_PLUGIN_AUTOLOAD opt out, like pytest.

Performance

Startup, collection, fixture orchestration, coverage measurement, and parallel workers are native Rust code. The main wins:

  • --cov runs — coverage is collected via sys.monitoring (Python 3.12+ low-overhead instrumentation) instead of coverage.py's tracing hooks. Typically 1.5–1.9x faster on medium suites, and the gap widens with suite size.
  • -n parallel runs — workers are forked (not spawned), so each worker inherits a warm interpreter with all imports already done. Startup per worker drops from seconds to milliseconds.
  • Large collections — fixture resolution and parametrize expansion run in Rust; suites with thousands of tests see faster collection.

Benchmarks on open-source projects (macOS arm64, release build, median of 3 warm runs):

suite (tests) mode pytest pytest-rs speedup
pydantic (12 750) --cov 3.64 s 1.90 s 1.9x
pydantic (12 750) --parallel-threads 3 --cov ¹ 3.82 s 1.97 s 1.9x
marshmallow (1 119) --cov 1.65 s 1.07 s 1.5x
marshmallow (1 119) -n 3 --cov 1.79 s 0.81 s 2.2x
click (1 336) --cov 3.46 s 2.19 s 1.6x
click (1 336) -n 3 --cov 3.66 s 2.35 s 1.6x

¹ pydantic's suite runs under pytest-run-parallel (--parallel-threads), so both runners are measured with it. Plain pytest -n 3 (xdist) fails on pydantic with a "different tests collected" error; pytest-rs's fork-based workers avoid this class of issue.

For small, CPU-bound suites without coverage or parallelism the test bodies dominate and both runners perform similarly. Try it on your own suite:

hyperfine -w 1 'pytest -q' 'pytest-rs -q'
hyperfine -w 1 'pytest --cov=mypkg' 'pytest-rs --cov=mypkg'

Known limitations

  • unix only (no Windows)
  • no --pdb / debugger integration yet
  • third-party pytest plugins are loaded via the pytest11 entry point and the pytest API shim; plugins reaching deep into pytest internals may not work (see "Third-party plugins" above for verified examples)

Conformance testing

Compatibility is verified by running the upstream test suites of the libraries pytest-rs reproduces, unchanged, under pytest-rs (conformance/).

Current results (total = passed + failed + errors + skipped; updated automatically by conformance/runner.py, refreshed by CI on every push to main — see conformance/RESULTS.md for per-file detail):

linux (CI-verified)

pytest & plugin ecosystem (the APIs pytest-rs reimplements):

suite tag passed failed errors skipped total conformant % files all-pass files run files excluded
pytest 9.0.3 2349 429 3 52 2833 84.8% 14 52 63
pytest-asyncio v1.4.0 268 0 0 0 268 100.0% 30 30 0
pytest-mock v3.15.1 87 0 0 1 88 100.0% 1 1 0
pytest-cov v7.1.0 185 21 0 3 209 90.0% 0 1 0
pytest-xdist v3.8.0 91 6 0 1 98 93.9% 0 1 6
pytest-split 0.9.0 59 0 0 0 59 100.0% 1 1 3
pytest-benchmark v5.1.0 91 31 0 1 123 74.8% 4 7 6
anyio 4.13.0 3120 0 0 42 3162 100.0% 26 26 0

Third-party plugins (not reimplemented — their own upstream test suites run under pytest-rs, loaded via the pytest11 entry-point shim):

suite tag passed failed errors skipped total conformant % files all-pass files run files excluded
pytest-timeout 2.4.0 40 0 0 1 41 100.0% 1 1 0
pytest-mypy v1.0.1 75 3 0 0 78 96.2% 0 1 0
pytest-ruff v0.5 10 0 0 0 10 100.0% 1 1 0
pytest-subtests v0.14.2 28 4 0 0 32 87.5% 0 1 0
pytest-metadata v2.0.4 10 0 0 0 10 100.0% 1 1 0
pytest-snapshot v0.9.0 101 6 0 0 107 94.4% 0 3 0
pytest-icdiff 0.5 10 2 0 0 12 83.3% 0 1 0
pytest-socket 0.7.0 59 6 0 0 65 90.8% 2 6 0
pytest-order v1.4.0 115 19 0 0 134 85.8% 7 16 0
pytest-repeat v0.9.4 16 0 0 0 16 100.0% 1 1 0
pytest-instafail v0.5.0 63 0 0 0 63 100.0% 1 1 0
pytest-env 1.6.0 67 8 0 0 75 89.3% 2 3 0
pytest-rerunfailures 9.1.1 47 0 0 1 48 100.0% 1 1 0
pytest-randomly 4.1.0 33 4 0 0 37 89.2% 0 1 0
pytest-bdd 8.1.0 112 26 0 1 139 81.3% 27 35 0
pytest-django v4.9.0 141 68 6 1 216 65.7% 2 13 0

Real-world projects (their suites run unchanged, as drop-in evidence):

suite tag passed failed errors skipped total conformant % files all-pass files run files excluded
click 8.3.1 1314 0 0 21 1335 100.0% 20 20 0
jinja 3.1.6 909 0 0 0 909 100.0% 22 22 0
marshmallow 4.1.1 1119 0 0 0 1119 100.0% 12 12 3
rich v14.2.0 855 0 0 25 880 100.0% 60 62 0
httpx 0.28.1 1404 13 0 1 1418 99.1% 26 31 0
httpx2 v2.4.0 1426 0 0 1 1427 100.0% 31 31 0
starlette 0.46.2 902 0 1 0 903 99.9% 27 28 0
attrs 25.3.0 1339 2 0 4 1345 99.9% 20 24 0
more-itertools v10.7.0 670 0 0 1 671 100.0% 2 2 0
werkzeug 3.1.3 897 1 1 0 899 99.8% 21 25 0
fastapi 0.115.12 2324 8 1 130 2463 99.6% 298 310 0
packaging 25.0 26897 50 110 0 27057 99.4% 11 12 0
pandas v3.0.3 160750 39 371 25459 186619 99.8% 863 964 0
scikit-learn-1 1.9.0 8299 16 0 6617 14932 99.9% 75 87 0
scikit-learn-2 1.9.0 4915 0 2 1888 6805 100.0% 50 58 0
scikit-learn-3 1.9.0 9240 7 0 2530 11777 99.9% 101 114 0

The suites are included as shallow git submodules under conformance/suites/ at the pinned release tags. Initialize them once after cloning:

git submodule update --init --depth 1

Then run the full conformance harness:

cargo build
uv run --no-project python conformance/runner.py --local   # uses submodules
uv run --no-project python conformance/runner.py           # re-clones from upstream (CI mode)
Project License Tag
pytest MIT 9.0.3
pytest-asyncio Apache-2.0 v1.4.0
pytest-mock MIT v3.15.1
pytest-cov MIT v7.1.0
pytest-xdist MIT v3.8.0
pytest-split MIT 0.9.0
pytest-benchmark BSD-2-Clause v5.1.0

pytest-rs reimplements the public APIs of these projects, plus anyio's pytest plugin (MIT). Parts of the bundled Python shims are ports of upstream code; see THIRD-PARTY-NOTICES.md. Credit for the API design and the test suites belongs to their respective authors.

License

This project is licensed under the MIT License. See the LICENSE file for more details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_rs-0.0.6.tar.gz (536.1 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pytest_rs-0.0.6-cp314-cp314-manylinux_2_28_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.28+ x86-64

pytest_rs-0.0.6-cp314-cp314-manylinux_2_28_aarch64.whl (3.5 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.28+ ARM64

pytest_rs-0.0.6-cp314-cp314-macosx_11_0_arm64.whl (3.4 MB view details)

Uploaded CPython 3.14macOS 11.0+ ARM64

pytest_rs-0.0.6-cp313-cp313-manylinux_2_28_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.28+ x86-64

pytest_rs-0.0.6-cp313-cp313-manylinux_2_28_aarch64.whl (3.5 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.28+ ARM64

pytest_rs-0.0.6-cp313-cp313-macosx_11_0_arm64.whl (3.4 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

File details

Details for the file pytest_rs-0.0.6.tar.gz.

File metadata

  • Download URL: pytest_rs-0.0.6.tar.gz
  • Upload date:
  • Size: 536.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pytest_rs-0.0.6.tar.gz
Algorithm Hash digest
SHA256 c088d311590c93c726356051b98642854dd378b7e6ff232c810adc83e6d3349b
MD5 46b798d313e58f0fd12db385891f4923
BLAKE2b-256 b1d1a67484ddd8591a8404b73bb9f9d85d129eafddf0c9237505cf1ec36914da

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_rs-0.0.6.tar.gz:

Publisher: release.yml on Yasu-umi/pytest-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_rs-0.0.6-cp314-cp314-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pytest_rs-0.0.6-cp314-cp314-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 dd28f060e1f4435135b03f4f02774236b2183881d3256f707349069aaec13d26
MD5 48e966e2909f71c81807d3168cf6cf39
BLAKE2b-256 471b76d8444e1e8c7cc92872ff2af319ba140dec46986143822f08de87e238d1

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_rs-0.0.6-cp314-cp314-manylinux_2_28_x86_64.whl:

Publisher: release.yml on Yasu-umi/pytest-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_rs-0.0.6-cp314-cp314-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for pytest_rs-0.0.6-cp314-cp314-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 d80a69610956b49f7513f9cd49afa60c8d99e8101e48ca3285024f430c57e551
MD5 a88f1cec16579568ec748592a3507490
BLAKE2b-256 522f37158765095118284ba989f0fb5e9735dfc8948ba4711eaa2d6789e75f9b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_rs-0.0.6-cp314-cp314-manylinux_2_28_aarch64.whl:

Publisher: release.yml on Yasu-umi/pytest-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_rs-0.0.6-cp314-cp314-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pytest_rs-0.0.6-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 019e431cd363e29d92cfd75d443e68d74f8ec70dbe657c444a9c8b0efd0c9c31
MD5 1c4babe8cfe5638ac3c295ad5f31d563
BLAKE2b-256 76b84c02b336ba7b87607751ad7639b812d8f2c484a09a38111eb7fe7fe5028d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_rs-0.0.6-cp314-cp314-macosx_11_0_arm64.whl:

Publisher: release.yml on Yasu-umi/pytest-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_rs-0.0.6-cp313-cp313-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pytest_rs-0.0.6-cp313-cp313-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 9a768cdde4277cda45cbde6976b8ee7a43bc1fdbaf765c99cf5e952c97e1d76e
MD5 e6445b8744db06048dcd60e2d7907443
BLAKE2b-256 54c9f511acbc55326ba384a9ec785e9f00281d7e0bb55daa1d60efe15af05fa6

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_rs-0.0.6-cp313-cp313-manylinux_2_28_x86_64.whl:

Publisher: release.yml on Yasu-umi/pytest-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_rs-0.0.6-cp313-cp313-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for pytest_rs-0.0.6-cp313-cp313-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 cd3b338c9f438bac95a1bddd796869f5752b79662f01d8e0fff1db7edbb3f284
MD5 da2106483c2c4856a9522db6474166c0
BLAKE2b-256 96ce695fce3b4d0bb0e3fe17cc999997c565a66bf5eaa6cd3624090ed7db8f36

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_rs-0.0.6-cp313-cp313-manylinux_2_28_aarch64.whl:

Publisher: release.yml on Yasu-umi/pytest-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_rs-0.0.6-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pytest_rs-0.0.6-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 3816306714928354769000a8826112c7a43d3e440115349e2c61f27f8dea0c79
MD5 3f36dbdcbd739b76df120d3271bd0119
BLAKE2b-256 f832ad6f426bb216e38793e21ec081cf9709c6b955c25fec16938c4e187e86f0

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_rs-0.0.6-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: release.yml on Yasu-umi/pytest-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page