Repository navigation
Let conftest.py skip its directory only with pytest.importorskip - #15143
Open
RonnyPfannschmidt wants to merge 5 commits into
Open
RonnyPfannschmidt wants to merge 5 commits into
RonnyPfannschmidt wants to merge 5 commits into
Conversation
A conftest.py that raised an outcome exception while being imported got different results depending on how pytest was invoked: a skip during collection skipped the directory (even a plain pytest.skip()), while the same conftest loaded as an initial conftest crashed with a raw Skipped traceback, and pytest.fail()/xfail() escaped the conftest import error handling. - Add SkippedModule, an internal Skipped subclass raised by pytest.skip(allow_module_level=True) and pytest.importorskip(). - A conftest raising SkippedModule skips its directory, also for initial conftests; the skip is cached so the conftest runs once. - A plain pytest.skip() in a conftest still skips but issues a PytestRemovedIn10Warning. - pytest.fail()/xfail() in a conftest become a ConftestImportFailure. - A node id below a skipped collector reports the skip instead of "found no collectors". - --import-mode=importlib no longer leaves a module that raised while executing in sys.modules. Closes #15142. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHmGPdXhVMcboqUeE6q62M
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHmGPdXhVMcboqUeE6q62M
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHmGPdXhVMcboqUeE6q62M
RonnyPfannschmidt
marked this pull request as ready for review
October 7, 2026 05:52
Only pytest.importorskip() stays supported for skipping a directory from its conftest.py: it now raises SkippedImport, a SkippedModule subclass, and any other skip at conftest import warns with PytestRemovedIn10Warning. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHmGPdXhVMcboqUeE6q62M
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Ronny · project thread
🤖 Written by Claude Opus 5.5 via Claude Code for the pytest maintainers; I prompted it, it did the work, I read it.
Closes #15142.
Before: a conftest.py with
tables = pytest.importorskip("tables")skipped its directory underpytest, butpytest tests/io/pytablescrashed with a rawSkippedtraceback. A plainpytest.skip()in a conftest silently skipped the whole package, andpytest.fail()crashed the same way.After:
pytest.importorskip()in a conftest skips its directory however pytest is invoked, including node ids below it.pytest.skip()in a conftest, with or withoutallow_module_level=True, still skips but warns (PytestRemovedIn10Warning) and becomes an error in pytest 10.pytest.fail()/pytest.xfail()are reported as a conftest import error.How
SkippedModule(Skipped)in_pytest.outcomes, raised bypytest.skip(allow_module_level=True), and its subclassSkippedImport, raised bypytest.importorskip()(including theminversionskip). Both setallow_module_level=True, sopython.importtestmoduleand anything reading the flag behave as before; their__qualname__isSkipped, so reports are unchanged._importconftest:SkippedImportpropagates; any otherSkippedwarns first, then propagates; any otherOutcomeExceptionbecomesConftestImportFailurelike other exceptions (causeis typedException | OutcomeException). The skip is cached per conftest, so the conftest runs once even when it is both an initial conftest and reached by collection._set_initial_conftestsleaves an anchor alone when a conftest on the way skips; theDircollector re-raises the cached skip and reports it.Session.collect: a skipped collector on the way to an argument counts as matching it, sopytest test_mod.py::test_ainto a module-level skip reports the skip instead ofERROR: found no collectors(applies to test modules too).--import-mode=importlib:_import_module_using_specdrops the module fromsys.moduleswhen executing it raises, like importlib does.allow_module_level/SkippedModuleassessmentScan of 53k test files and conftests in 177 popular repos:
importorskipat module top levelskip(..., allow_module_level=True)at module top level__init__.pyimportorskipat conftest top levelskip(..., allow_module_level=True)in a conftestallow_module_level=False.allow_module_levelon an exceptionSkipped(..., allow_module_level=...)directlySo the flag is only ever used as
pytest.skip(..., allow_module_level=True), and nobody inspects it. That makes it a type in all but name; the subclasses let the conftest loader andimporttestmoduleaskisinstance()instead of reading an attribute. Theallow_module_levelparameter and attribute stay for compatibility.Checklist
TestConftestImportOutcome(test_conftest.py), node-id andSkippedModule/SkippedImporttests (test_skipping.py), importlibsys.modulestest (test_pathlib.py). The new tests fail onmain.15142.bugfix.rst,15142.deprecation.rst.Co-authored-bytrailer.Full test suite passes locally (Python 3.13) apart from
test_pdb_unittest_postmortem, a pexpect timing test that failed once and passed on 3 reruns;pre-commit run --all-filesclean exceptzizmor, which needs GitHub API access not available here.🤖 Generated with Claude Code
https://claude.ai/code/session_01GHmGPdXhVMcboqUeE6q62M