Skip to content

Fix: local plugins are importable for erase and combine - #2253

Open
Eljees wants to merge 2 commits into
coveragepy:mainfrom
Eljees:fix/1726-plugins-erase-and-combine
Open

Eljees wants to merge 2 commits into
coveragepy:mainfrom
Eljees:fix/1726-plugins-erase-and-combine

Conversation

@Eljees

@Eljees Eljees commented Aug 6, 2026

Copy link
Copy Markdown

Closes #1726.

cmdline.py inserts "" into sys.path with the comment that plugins may need to import from the current directory, but the insert sits below the erase and combine branches, both of which return before reaching it. Both commands construct Coverage, which loads configured plugins, so a plugin living in the project directory raises ModuleNotFoundError.

The report in issue 1726 is about erase; combine is broken by the same defect, which matters more - it is the core of parallel mode.

Only the console script is affected: python -m coverage puts the CWD on sys.path itself.

Move the block above the action dispatch, keeping run excluded: there sys.path[0] is owned by PyRunner.prepare(), and run never reached the old line anyway, so its behaviour is unchanged.

cmdline.py inserts "" into sys.path with the comment that plugins may
need to import from the current directory, but the insert sits below the
erase and combine branches, both of which return before reaching it.
Both commands construct Coverage, which loads configured plugins, so a
plugin living in the project directory raises ModuleNotFoundError.

The report in issue 1726 is about erase; combine is broken by the same
defect, which matters more - it is the core of parallel mode.

Only the console script is affected: python -m coverage puts the CWD on
sys.path itself.

Move the block above the action dispatch, keeping run excluded: there
sys.path[0] is owned by PyRunner.prepare(), and run never reached the
old line anyway, so its behaviour is unchanged.
@Eljees

Eljees commented Aug 6, 2026

Copy link
Copy Markdown
Author

Closes #1726.

Repro on master, straight from the issue — a plugin that is only importable from the current directory works under run but not under combine or erase:

$ python -m coverage combine
...
ModuleNotFoundError: No module named 'importing_plugin'

cmdline.py already had a sys.path.insert(0, ""), but it sat below the branch that dispatches erase and combine, so those two commands returned before ever reaching it — the insert was effectively dead code for them. This moves it above the dispatch and skips it for run, where PyRunner.prepare() owns sys.path[0] and inserting again would be wrong.

Test: test_local_files_are_importable_for_erase_and_combine in tests/test_plugins.py. It fails on master with exactly the ModuleNotFoundError above and passes with the fix; the rest of tests/test_plugins.py is green either way.

AI-assisted (LLM used for drafting the patch and the test); the analysis and the runs are mine.

@Eljees

Eljees commented Aug 14, 2026

Copy link
Copy Markdown
Author

Ping — this has been open since 6 August with no review yet.

cmdline.py inserts "" into sys.path with a comment saying plugins may need to import from the current directory, but the insert sits below the erase and combine branches, both of which return before reaching it. Both construct Coverage, which loads configured plugins, so a plugin living in the project directory raises ModuleNotFoundError. #1726 reports this for erase; combine is broken by the same defect, which matters more since it is the core of parallel mode. Only the console script is affected — python -m coverage puts the CWD on sys.path itself.

The fix moves the block above the action dispatch and keeps run excluded, where sys.path[0] is owned by PyRunner.prepare() and the old line was never reached anyway.

Branch is mergeable with no conflicts. Happy to close it if you would rather handle #1726 another way.

@Eljees

Eljees commented Sep 18, 2026

Copy link
Copy Markdown
Author

This PR had no CI at all for its whole life, which I only noticed while auditing my open work. For the head commit 8383cc8 there were zero workflow runs and zero check suites — not a pending approval, simply nothing. The only status on it was Read the Docs.

It was specific to this PR, not the repository. Checking every open PR here:

#2252  created 2026-08-06  fork  5 workflow runs      <- opened the same day as mine
#2257  created 2026-08-11  fork  5
#2258  created 2026-08-12  fork  5
#2259  created 2026-08-14  fork  5
#2253  created 2026-08-06  fork  0                    <- this one

Every PR opened since Aug 2025 has 4–6 runs; the only other zeros are a handful from 2019–2025 whose last push predates the current workflows. testsuite.yml, quality.yml and coverage.yml all trigger on a bare pull_request: with no path filters, and this PR touches coverage/cmdline.py and tests/test_plugins.py, so nothing in the configuration explains it.

I have merged current main into the branch, which gave the PR a new head and the events it never got: CI is running now.

While I was there I re-verified the change against main at f108ca89, since the branch was 55 commits behind:

  • on pristine main with only my test file applied, the new test fails with ModuleNotFoundError: No module named 'importing_plugin' — the bug this fixes;
  • with the change, that test and the existing test_local_files_are_importable both pass;
  • the whole tests/test_plugins.py is green on the merged branch (41 passed, 1 skipped) against 40 passed, 1 skipped on main — the difference being the added test.

The change itself is unchanged: move the sys.path.insert(0, "") so it also covers erase and combine, and exclude run, where PyRunner.prepare() owns sys.path[0].

🤖 Generated with Claude Code

https://claude.ai/code/session_015CsUTRwsaFCwavDJTPwEpW

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.

Erase fails with local plugins due to incorrect path

1 participant