Skip to main content

Run pytest for tests related to staged Python files in pre-commit.

Project description

pytest-changed

Run pytest for tests related to staged Python files in pre-commit.

pytest-changed is a small, deterministic selector for commit-time feedback. It takes the Python filenames passed in by pre-commit, turns source-file changes into test-file paths using simple conventions, applies optional explicit overrides, and runs pytest on the resulting test files.

If src/foo/bar.py changes, pytest-changed looks for:

  • tests/foo/test_bar.py
  • tests/test_bar.py

Any changed test file under your configured test roots is always run directly.

What it is for

Use pytest-changed when you want a staged-file-aware pre-commit hook that:

  • runs fast enough to sit on the commit path
  • behaves predictably from filenames alone
  • is easy to debug when selection looks wrong
  • does not rely on stored state, import graphs, or runtime tracing

This tool is intentionally file-level and convention-driven. It is for commit-time test selection, not whole-project impact analysis.

Install

pip install pytest-changed

pytest is installed as a runtime dependency because pytest-changed invokes pytest directly.

Quick start

The default convention assumes source files live under src/ and tests live under tests/.

[tool.pytest-changed]
pytest_args = ["-q"]

With that config:

src/foo/bar.py   -> tests/foo/test_bar.py
src/foo/bar.py   -> tests/test_bar.py

Run manually:

pytest-changed src/foo/bar.py tests/test_other.py

If src/foo/bar.py has no matching tests, pytest-changed exits 0 but warns on stderr by default.

Pre-commit usage

Use this repository as a normal pre-commit hook:

repos:
  - repo: https://github.com/mishmishb/pytest-changed
    rev: v0.1.0
    hooks:
      - id: pytest-changed

For local development before the first release:

repos:
  - repo: local
    hooks:
      - id: pytest-changed
        name: pytest-changed
        entry: pytest-changed
        language: system
        pass_filenames: true
        always_run: false
        stages: [pre-commit]

Configuration

All configuration lives in pyproject.toml under [tool.pytest-changed]:

Option Default Description
pytest_args ["-q"] Extra args passed to pytest
source_roots ["src"] Source roots stripped before convention matching
test_roots ["tests"] Test roots searched for matching tests
warn_on_missing true Warn on stderr when a changed source file has no match
mapping {} Optional explicit overrides for individual source files

Convention matching

For each changed Python source file, pytest-changed tries these paths under each configured test_root:

<test_root>/<relative_dir>/test_<module>.py
<test_root>/test_<module>.py

Example with defaults:

src/core.py           -> tests/test_core.py
src/utils/helpers.py  -> tests/utils/test_helpers.py
src/utils/helpers.py  -> tests/test_helpers.py

If both convention matches exist, both run.

Explicit overrides

Use [tool.pytest-changed.mapping] when a file needs something more specific than the naming convention. Explicit mappings override convention discovery for that source file.

[tool.pytest-changed]
pytest_args = ["-q"]
warn_on_missing = true

[tool.pytest-changed.mapping]
"src/db.py" = ["tests/test_db.py", "tests/test_db_integration.py"]
"src/core.py" = ["tests/custom/test_special_core.py"]

Custom roots

If your project uses different layout conventions, configure them explicitly:

[tool.pytest-changed]
source_roots = ["lib", "pkg"]
test_roots = ["spec", "tests"]
warn_on_missing = true

Environment override

Set PYTEST_CHANGED_CONFIG to point at a TOML config file. This is useful when you cannot or do not want to put configuration in the project pyproject.toml.

PYTEST_CHANGED_CONFIG=pytest-changed.toml pytest-changed src/core.py

The override file uses the same table structure:

[tool.pytest-changed]
pytest_args = ["-q", "--tb=short"]
source_roots = ["src"]
test_roots = ["tests"]
warn_on_missing = true

[tool.pytest-changed.mapping]
"src/core.py" = ["tests/custom/test_special_core.py"]

The canonical table name is hyphenated (pytest-changed) because it matches the package/distribution name. The Python import package remains underscored (pytest_changed), as normal for Python modules. The early underscore table ([tool.pytest_changed]) is accepted as a compatibility alias, but new projects should use [tool.pytest-changed].

How it works

  1. Receives changed filenames from pre-commit or CLI arguments
  2. Filters to Python files only
  3. Auto-includes changed files under configured test roots
  4. Applies an explicit mapping override if one exists for a changed source file
  5. Otherwise tries nested and flat convention matches
  6. Warns about unmatched source files unless disabled
  7. Deduplicates selected tests
  8. Runs python -m pytest <selected tests> <pytest_args>
  9. Returns exit code 0 if no tests match, or if pytest exits with code 5 (no tests collected)

Scope and limits

These are current scope boundaries, not laws of nature:

  • Staged filenames in, test files out — the current contract is commit-time file selection
  • File-level selection — it selects test files, not individual test functions or classes
  • No hidden state — no database, baseline run, or runtime trace cache
  • No dependency graph inference — selection is based on configured conventions and explicit overrides
  • CI still matters — this speeds up local commit feedback; it does not replace the full suite

Design principles

  • Pre-commit first — designed around staged filenames passed by the hook
  • Convention first — useful with minimal config
  • Explicit overrides when needed — config is the escape hatch, not the baseline
  • Changed tests always run — edited tests do not need source mapping
  • Warnings over silent misses — unmatched sources should be visible
  • Deterministic local feedback — selection should be understandable from paths alone

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

pytest_changed-0.1.0.tar.gz (54.9 kB view details)

Uploaded Source

Built Distribution

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

pytest_changed-0.1.0-py3-none-any.whl (8.8 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for pytest_changed-0.1.0.tar.gz
Algorithm Hash digest
SHA256 995a2945f1578322b224e4c8ea079f279c9565a5a0de982b916bb4b0b75c22d4
MD5 bc7728f19c391b6c0d155d6e9035aefb
BLAKE2b-256 b86f157d79becbe9e2b58dac6b9b2e42943510f5bbd66c2be36aa766d8d505ca

See more details on using hashes here.

Provenance

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

Publisher: release.yml on mishmishb/pytest-changed

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_changed-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pytest_changed-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pytest_changed-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 82880d5ecee0dfab90942c6e246b3a00b666e1509f50b43f8860d9d7ec9e23e1
MD5 300200046f1bc1a8b7b398f1348968bc
BLAKE2b-256 d6c596f070ee92869aa5c99e214fc1f81ec203ae1fd7edd7f720958783bb571c

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_changed-0.1.0-py3-none-any.whl:

Publisher: release.yml on mishmishb/pytest-changed

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