Skip to content

StatReload: exclude hidden / cache directories when scanning for .py files - #2995

Open
RachelXiaolan wants to merge 1 commit into
Kludex:mainfrom
RachelXiaolan:ai-2138-round-2-source-audit
Open

StatReload: exclude hidden / cache directories when scanning for .py files#2995
RachelXiaolan wants to merge 1 commit into
Kludex:mainfrom
RachelXiaolan:ai-2138-round-2-source-audit

Conversation

@RachelXiaolan

@RachelXiaolan RachelXiaolan commented Jun 18, 2026

Copy link
Copy Markdown

Summary

StatReload.iter_py_files() does a recursive rglob("*.py") and yields every match, including .py files inside .venv/, .mypy_cache/, .pytest_cache/, .ruff_cache/, and .git/. WatchFilesReload's FileFilter already excludes these (via watch_filter), but the stat-based reloader does not — so users running with watchfiles not installed see spurious reloads on venv installs, type-checker cache writes, linter cache writes, and git operations that touch .git/hooks/*.py.

Fix

Add a small static _DEFAULT_EXCLUDE_DIRS tuple to StatReload and skip any path whose parts intersect it. Mirrors the spirit of WatchFilesReload.FileFilter defaults without taking a runtime dependency on watchfiles.

Reproducer

from pathlib import Path
from uvicorn.config import Config
from uvicorn.supervisors.statreload import StatReload

root = Path("/tmp/project")
(root / "app.py").write_text("# app")
(root / ".venv" / "lib").mkdir(parents=True)
(root / ".venv" / "lib" / "somelib.py").write_text("# venv lib")
(root / ".mypy_cache").mkdir()
(root / ".mypy_cache" / "x.py").write_text("# cache")

config = Config(app="app", reload=True, reload_dirs=[str(root)])
r = StatReload(config, target=lambda **kw: None, sockets=[])
for p in r.iter_py_files():
    print(p)

Before the fix: prints app.py, .venv/lib/somelib.py, .mypy_cache/x.py — the latter two cause spurious reloads.
After the fix: prints only app.py.

Tests

tests/supervisors/test_statreload_filter.py — 2 cases:

  • test_statreload_excludes_hidden_dirs — fails before, passes after
  • test_statreload_includes_app_in_subdir — sanity check that real .py in a non-excluded subdir is still watched

Test run:

  • New tests: 2/2 passing
  • tests/supervisors/: 28 passed (full supervisors suite)
  • tests/test_config.py: 122 passed
  • No regressions

Refs: AI-2138 (bug-hunter Strategy B, source-code audit)

Review in cubic

…anning for .py files

StatReload.iter_py_files() previously did a recursive rglob('*.py') and
yielded every match, including files inside .venv/, .mypy_cache/,
.pytest_cache/, .ruff_cache/ and .git/. WatchFilesReload's FileFilter
already excludes these (via watch_filter), but the stat-based reload
implementation did not — so users running with watchfiles not installed
saw spurious reloads on:

  - venv installs (pip install writes into .venv/*.py)
  - type-checker cache writes (.mypy_cache)
  - test runner cache (.pytest_cache)
  - linter cache (.ruff_cache)
  - git operations that touch .git/hooks/*.py

Fix: add a small static _DEFAULT_EXCLUDE_DIRS tuple to StatReload and
skip any path whose parts intersect it. This matches the spirit of
WatchFilesReload's FileFilter defaults without taking a runtime
dependency on watchfiles.

Tests: tests/supervisors/test_statreload_filter.py covers .venv,
.mypy_cache, .git exclusion plus a sanity test for real .py in a
non-excluded subdir. Before fix: 2/2 failing. After: 2/2 passing.
Full uvicorn test suite (supervisors + config): 150+ passed, no
regressions.

Refs: AI-2138
@codspeed-hq

codspeed-hq Bot commented Jun 18, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 24 untouched benchmarks


Comparing RachelXiaolan:ai-2138-round-2-source-audit (08967ce) with main (e8a31bc)

Open in CodSpeed

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Re-trigger cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants