Skip to main content

Fast mutation testing for Python

Project description

irradiate

PyPI License: MIT Docs

Fast mutation testing for Python, written in Rust. Built for CI.

Code coverage tells you which lines ran. Mutation testing tells you which lines are actually tested. irradiate makes small changes to your code — flipping < to <=, swapping + with -, replacing True with False — and checks whether your tests catch each one. If they don't, that's a gap.

Quick start

pip install irradiate

# Test only functions changed in your PR
irradiate run --diff main

That's it. irradiate finds your src/ and tests/, generates mutants for the changed code, and reports which ones survived.

Add to CI in 3 lines

- uses: nwyin/irradiate@v0
  with:
    diff: origin/main
    fail-under: "80"

This runs mutation testing on every PR, fails if the score drops below 80%, and posts inline annotations on surviving mutants.

Example output

$ irradiate run --diff main
Generating mutants...
  done in 3ms (14 mutants across 1 files)
Running stats + validation...
  done in 195ms
Running mutation testing (14 mutants, 10 workers)...

Mutation testing complete (14 mutants in 0.1s, 175 mutants/sec)
  Cache hits: 0
  Cache misses: 12
  Killed:    11
  Survived:  1
  No tests:  2
  Score:     91.7%

Survived mutants:

  number_mutation (1):
    simple_lib/__init__.py:6  replaced `0` with `1`  [simple_lib.x_add__irradiate_3]

Why irradiate

  • Fast — pre-warmed pytest workers with fork-per-mutant execution. Pytest starts once. Tests run many times.
  • 38+ mutation operators — arithmetic, comparison, boolean, string methods, return values, exception types, regex patterns, decorator removal, and more.
  • Full decorator support@lru_cache, @app.route, custom decorators all mutated via source-patching. decorator_removal tests whether decorators matter.
  • Type-check filter--type-checker mypy skips mutants caught by static analysis (~35% fewer test runs on typed codebases).
  • Incremental--diff main tests only functions changed since a git ref.
  • Cached — content-addressed results survive rebases, branch switches, and touch. Share across CI with remote cache sync hooks.
  • CI-native--fail-under for gating, GitHub Actions annotations, JSON/HTML reports, composite action.
  • Drop-in — works with any pytest project.

Install

pip install irradiate

Requires Python 3.10+ with pytest installed. See the installation guide for more options.

Usage

# Test functions changed since main (the CI use case)
irradiate run --diff main

# Run on the full codebase
irradiate run

# Sample 10% of mutants for fast feedback
irradiate run --sample 0.1

# Generate reports
irradiate run --report json   # Stryker mutation-testing-report-schema v2
irradiate run --report html   # self-contained HTML report

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

# Explore results
irradiate results
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"]

Source paths can also be passed as positional arguments: irradiate run src/mylib. All settings can be overridden via CLI flags. Run irradiate run --help for the full list.

Features

Mutation operators (38 categories)

Arithmetic, comparison, boolean, augmented assignment, unary, string mutation/emptying, number literals, constant replacement, lambda bodies, return values, assignments, default arguments, argument removal, method swaps, dict kwargs, exception types, match/case removal, condition negation, condition replacement, statement deletion, keyword swap, loop mutation, ternary swap, slice index removal, regex pattern mutations (11 operators: anchor removal, charclass negation, shorthand negation, quantifier removal/change, lookaround negation, alternation removal, and more).

Functions can be excluded with # pragma: no mutate.

Execution model

By default, workers fork after pytest collection. Each mutant runs in an isolated child process with no restart overhead. For projects with complex test infrastructure, --isolate runs each mutant in a fresh subprocess instead. --verify-survivors re-tests survivors in isolate mode after the main run 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

Terminal output groups survived mutants by operator. --report json writes Stryker mutation-testing-report-schema v2, compatible with the Stryker Dashboard. --report html generates a self-contained report using mutation-testing-elements. On GitHub Actions, irradiate auto-emits ::warning annotations on survived mutants and writes a Markdown step summary.

Caching

Content-addressable cache keyed on SHA-256 of function body, test IDs, and operator. Survives rebases, branch switches, and touch (mtime-based caches don't). Use --no-cache to force a full re-run.

Decorator support

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

Sampling (--sample)

Test a random subset of mutants for fast CI feedback. Academic research shows 5-10% random sampling gives 99% R² correlation with the full mutation score.

  • --sample 0.1: test 10% of mutants
  • --sample 100: test exactly 100 mutants
  • --sample-seed 42: override RNG seed (default: 0 for reproducibility)

Sampling is operator-stratified, so every mutation category is proportionally represented.

CI integration

The GitHub Actions composite action (shown above) auto-detects the CI environment, emits inline ::warning annotations on survived mutants, and writes a Markdown step summary. See the CI integration guide for advanced configuration, caching, and non-GitHub setups.

Performance tuning

Parallelism defaults to CPU count (--workers N to override). Workers are recycled when RSS exceeds --max-worker-memory N MB. --covered-only skips mutants with no test coverage. --no-stats skips coverage collection when you want to test all mutants against all tests. Per-mutant timeout defaults to 10x baseline (--timeout-multiplier N).

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 38 categories (incl. 11 regex)
Cache mtime-based Content-addressable (SHA-256)
Orchestration Python multiprocessing Rust + tokio async
Incremental no --diff with merge-base
Reports Terminal only JSON, HTML, GitHub Actions annotations
Decorator support Skip all @property/@classmethod/@staticmethod handled
Sampling no --sample with operator stratification
CI integration Manual --fail-under, GitHub Actions action, annotations, step summary
Isolation Fork only Warm-session + --isolate + --verify-survivors

Guides

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.4.0.tar.gz (344.1 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.4.0-py3-none-manylinux_2_28_x86_64.whl (1.9 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ x86-64

irradiate-0.4.0-py3-none-manylinux_2_28_aarch64.whl (1.8 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ ARM64

irradiate-0.4.0-py3-none-macosx_11_0_arm64.whl (1.7 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

irradiate-0.4.0-py3-none-macosx_10_12_x86_64.whl (1.8 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

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

File metadata

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

File hashes

Hashes for irradiate-0.4.0.tar.gz
Algorithm Hash digest
SHA256 935c1249c26d327c6dfb6d50f59b8f0c703047e34a9319fa8130495713e1b91e
MD5 d4fe0924e5786055073fa7880d565c6c
BLAKE2b-256 016fd996e92aed3fb9258769ad0e70680ec9290a4f545801ff2a3bca9e005399

See more details on using hashes here.

Provenance

The following attestation bundles were made for irradiate-0.4.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.4.0-py3-none-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for irradiate-0.4.0-py3-none-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 8b6ae2ec8b259b7d6129f0caf3fe8d41f4a37f53c2aebc4685cc290af5f6676d
MD5 c8209d69a6fa4dfc5fc5b7ae18b73ec0
BLAKE2b-256 45a8fb372509da1cbc4fa9e93e4234e12fee81312f8e74810f1e4ed12441a3b6

See more details on using hashes here.

Provenance

The following attestation bundles were made for irradiate-0.4.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.4.0-py3-none-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for irradiate-0.4.0-py3-none-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 0ee86ffb4ee0bd322e02eae80e89c58b620367568fd4ec6af2eada9f806237dc
MD5 62c381450ce3c405282b97473b334743
BLAKE2b-256 954c0e8112e8334240452edfd0ad83bf2444435225c2cadf3c334cd8c221ba68

See more details on using hashes here.

Provenance

The following attestation bundles were made for irradiate-0.4.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.4.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for irradiate-0.4.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d3efd990a061afc1ffa7c8573ee5f30c9b4ec842665874599ca5de70501d0aed
MD5 85cd1105357285b3a5183aaba6381263
BLAKE2b-256 af0f1811a16c3a0f35cbc28070a4002d243f849ee1412e9ba3868b812063104d

See more details on using hashes here.

Provenance

The following attestation bundles were made for irradiate-0.4.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.4.0-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for irradiate-0.4.0-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 c2c69c838a95002593cacb3d8e3a49a67c0b36173105d8fee6594c1e6dfeab08
MD5 c7dcffe852e55833932860f682dfa35a
BLAKE2b-256 752b02e4fc1ebd37da697826f8a2233d871df21ce226e60cb0bcf3e49113da79

See more details on using hashes here.

Provenance

The following attestation bundles were made for irradiate-0.4.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