Skip to main content

Fast mutation testing for Python

Project description

irradiate

Fast mutation testing for Python, written in Rust.

Why

Mutation testing is slow. The bottleneck isn't generating mutants — it's running the test suite once per mutant. A typical pytest startup costs 200-500ms, and with hundreds of mutants that adds up to minutes of pure overhead.

irradiate eliminates this by maintaining a pool of pre-warmed pytest workers. Pytest starts once, collects tests once, then forks a child process for each mutant. The result: mutation testing at 30-60 mutants/sec on real codebases.

How it works

  1. Parse Python source with tree-sitter (27 mutation operator categories, ~160+ distinct mutations)
  2. Generate trampolined mutants — each function gets an original, N mutated variants, and a runtime dispatcher
  3. Collect test coverage and timing in a single pytest run
  4. Fork a child process per mutant inside pre-warmed workers (no pytest restart)
  5. Report results as terminal output, JSON (Stryker schema v2), HTML, or GitHub Actions annotations

Install

pip install irradiate

Or build from source:

cargo build --release

Requires Python 3.10+ with pytest installed.

Usage

# Run mutation testing (auto-detects src/ and tests/)
irradiate run

# Only test functions changed since main
irradiate run --diff main

# Generate JSON report (Stryker mutation-testing-report-schema v2)
irradiate run --report json

# Generate self-contained HTML report
irradiate run --report html

# Fail CI if mutation score is below threshold
irradiate run --fail-under 80

# See cached results
irradiate results

# Show diff for a specific mutant
irradiate show module.x_func__irradiate_1

Configuration

Configure via [tool.irradiate] in pyproject.toml:

[tool.irradiate]
paths_to_mutate = "src"
tests_dir = "tests"
do_not_mutate = ["**/generated/*", "**/vendor/*"]
pytest_add_cli_args = ["-x", "--tb=short"]

All settings can be overridden via CLI flags. Run irradiate run --help for the full list.

Features

Mutation operators (27 categories)

Arithmetic, comparison, boolean, augmented assignment, unary, string mutation/emptying, number literals, lambda bodies, return values, assignments, default arguments, argument removal, method swaps, dict kwargs, decorator removal (planned), exception types, match/case removal, condition negation, condition replacement, statement deletion, keyword swap, loop mutation, ternary swap, slice index removal.

Functions can be excluded with # pragma: no mutate.

Execution model

  • Fork-per-mutant (default): Workers fork after pytest collection. Each mutant runs in an isolated child process — no state leakage between mutants, no pytest restart overhead.
  • --isolate: Full subprocess isolation. Slower but guaranteed clean for projects with complex test infrastructure.
  • --verify-survivors: After the main run, re-tests survived mutants in isolate mode to catch false negatives from warm-session state leakage.

Incremental mode (--diff)

Only mutate functions touched by a git diff. Uses git merge-base to compare against the divergence point, so --diff main does the right thing on feature branches.

Reporting

Caching

Content-addressable cache keyed on SHA-256 of function body + test IDs + operator. Survives rebases, branch switches, and touch — unlike mtime-based caches.

Decorator support

@property, @classmethod, and @staticmethod are handled natively via a descriptor-aware trampoline. Other decorated functions are skipped (source-patching fallback planned — see #13).

Performance tuning

  • --workers N: control parallelism (defaults to CPU count)
  • --timeout-multiplier N: scale per-mutant timeout (default 10x baseline)
  • --worker-recycle-after N: respawn workers after N mutants (auto-tuned)
  • --max-worker-memory N: recycle workers exceeding N MB RSS
  • --covered-only: skip mutants with no test coverage
  • --no-stats: skip coverage collection, test all mutants against all tests

How it compares to mutmut

mutmut irradiate
Speed pytest.main() per mutant (~200ms each) Fork-per-mutant — pytest starts once
Parser LibCST (Python) tree-sitter (Rust, parallel)
Operators ~20 categories 27 categories
Cache mtime-based Content-addressable (SHA-256)
Orchestration Python multiprocessing Rust + tokio async
Incremental --diff with merge-base
Reports Terminal only JSON, HTML, GitHub Actions annotations
Decorator support Skip all @property/@classmethod/@staticmethod handled
CI integration Manual --fail-under, GitHub annotations, step summary
Isolation Fork only Warm-session + --isolate + --verify-survivors

Acknowledgments

irradiate's trampoline architecture and mutation operator design are informed by mutmut. The naming convention is partially compatible with mutmut to ease migration.

License

MIT

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

irradiate-0.1.0.tar.gz (259.5 kB view details)

Uploaded Source

Built Distributions

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

irradiate-0.1.0-py3-none-manylinux_2_28_x86_64.whl (2.0 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ x86-64

irradiate-0.1.0-py3-none-manylinux_2_28_aarch64.whl (1.9 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ ARM64

irradiate-0.1.0-py3-none-macosx_11_0_arm64.whl (1.8 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

irradiate-0.1.0-py3-none-macosx_10_12_x86_64.whl (1.9 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file irradiate-0.1.0.tar.gz.

File metadata

  • Download URL: irradiate-0.1.0.tar.gz
  • Upload date:
  • Size: 259.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for irradiate-0.1.0.tar.gz
Algorithm Hash digest
SHA256 995795beb9f46ca80ca3b8fec463bd07de96186ed2aa3ba5feafd0d5c414205f
MD5 b0a71b2405eaab340d657ea0aa679029
BLAKE2b-256 a35054b3646aeec6ae0fee497af5f542c41713551634cddbfa5c69e28da657ff

See more details on using hashes here.

Provenance

The following attestation bundles were made for irradiate-0.1.0.tar.gz:

Publisher: release.yml on nwyin/irradiate

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

File details

Details for the file irradiate-0.1.0-py3-none-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for irradiate-0.1.0-py3-none-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 a63ba08e89a971bcc0cd4033ddacb6abc4bc0555d922b6006adbd2e3f16cdd90
MD5 d5ef9f0f84808ca433bd8cf7514cc95f
BLAKE2b-256 ff7e86d152b588444c5345638a738fcda4a33fac7c593d987695093047ad1206

See more details on using hashes here.

Provenance

The following attestation bundles were made for irradiate-0.1.0-py3-none-manylinux_2_28_x86_64.whl:

Publisher: release.yml on nwyin/irradiate

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

File details

Details for the file irradiate-0.1.0-py3-none-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for irradiate-0.1.0-py3-none-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 4b18e5354ce9ed5738b883d8dc2745718aab4f0a595365fd6c137ecea0cb366b
MD5 30da717822475600ea90b138b6a079ec
BLAKE2b-256 74c07a774170daaacf849bf2886e869fbe64377b683daf42ac395748a7b7d673

See more details on using hashes here.

Provenance

The following attestation bundles were made for irradiate-0.1.0-py3-none-manylinux_2_28_aarch64.whl:

Publisher: release.yml on nwyin/irradiate

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

File details

Details for the file irradiate-0.1.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for irradiate-0.1.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 39ff1c95eda5f76a5aab58b7cabcc7f2c7018bdb4b8a0d88e643f0c6977aed9a
MD5 b08fdad35f3ad31a422fba948f149aed
BLAKE2b-256 50c7c70aff444cf59be2fe017afb840f12202f044ecbe5585ba10c9c6bbec471

See more details on using hashes here.

Provenance

The following attestation bundles were made for irradiate-0.1.0-py3-none-macosx_11_0_arm64.whl:

Publisher: release.yml on nwyin/irradiate

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

File details

Details for the file irradiate-0.1.0-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for irradiate-0.1.0-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 ab6dc30dd6487f698b4d501b48f8c9098ac32eabf034d43d542ba04c69f8d4b0
MD5 92f8ceaf24b58c4d81c2986248a7be64
BLAKE2b-256 339aaf4285019645c1d21e7cd46f9d850ab7a0f06e24bb9cb76cc2e664daed60

See more details on using hashes here.

Provenance

The following attestation bundles were made for irradiate-0.1.0-py3-none-macosx_10_12_x86_64.whl:

Publisher: release.yml on nwyin/irradiate

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