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.1.tar.gz (48.4 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.1-py3-none-any.whl (8.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: pytest_changed-0.1.1.tar.gz
  • Upload date:
  • Size: 48.4 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.1.tar.gz
Algorithm Hash digest
SHA256 2df8b4ef1e11bb1e1138c35fbf58045680f86ced5ad0c6e83b56f56090134598
MD5 459c4e25afc65a7d5f094e6740539dad
BLAKE2b-256 404c7b3b62b9ecf462e8a5b53a922f904d7afe417e5e099ccb837a9124d8f6d4

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_changed-0.1.1.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.1-py3-none-any.whl.

File metadata

  • Download URL: pytest_changed-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 8.7 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b6cf68ddddc67f13fb33696e36793b42790ef7cdfeb36906c46766757e02c099
MD5 710a7c6b67a66e40d6fb5bba535d8982
BLAKE2b-256 07c597b5fad2feeeb9e0bc971e86301c475f4d90fd742e0c453a6c652c889d6d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_changed-0.1.1-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