Fix unittest.addModuleCleanup having no effect under pytest - #14973
Fix unittest.addModuleCleanup having no effect under pytest#14973ShamikOfficial wants to merge 4 commits into
Conversation
Wire mark-and-drain module cleanups into the xunit module fixture and add a session-end backstop for import-time registrations (closes pytest-dev#14958). Co-authored-by: Cursor <cursoragent@cursor.com>
|
@claude investigate in detail the differences this creates compared to unittest as well as investigate the failure modes we would see when test reordering is in effect Post the report as reply in the pr so we can decide what to document and what needs warnings/ errors Prepare a table wirh suggestions/ tradeoffs to support that |
CI was treating EncodingWarning from open()/read_text() as errors. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Pushed a small fix for the CI failures. The new event-log tests were opening and reading files without an encoding argument, which raised EncodingWarning under the CI warning settings. They now pass encoding="utf-8". Still happy to follow up on the unittest vs reordering tradeoffs once that report is in. |
Co-authored-by: Cursor <cursoragent@cursor.com>
|
please check the ci again |
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Checked CI again. Status after the latest push
On the unittest vs pytest / reordering report you asked for (Claude did not post one, so here is a concise version): Behavior vs stdlib unittest
Reordering / re-entry failure modes
Suggestions / tradeoffs
I can add a short docs note for (F) in a follow-up commit on this PR if you want that before merge. |
|
This comment was written and posted by an AI agent (Claude Fable 5.1 via Claude Code) on @RonnyPfannschmidt's instruction. He prompted the investigation (his Comparison of PR head 084c895 against 1. Behaviour differences vs. unittest
Repro for the nested-session defect (this is exactly the shape of a plugin test suite, and of pytest's own import unittest, unittest.case as uc
pytest_plugins = ["pytester"]
def setUpModule():
unittest.addModuleCleanup(print, "outer cleanup")
def test_inner_run(pytester):
print("pending before:", len(uc._module_cleanups)) # 1
pytester.makepyfile(test_inner="import unittest\nclass T(unittest.TestCase):\n def test(self): pass")
pytester.runpytest_inprocess().assert_outcomes(passed=1) # prints "outer cleanup" here
print("pending after:", len(uc._module_cleanups)) # 02. Failure modes under test reorderingReordering here means anything that breaks unittest's "all tests of a module are contiguous" invariant: explicit node ids,
3. Suggestions and trade-offs
4. CI status
|
closes #14958
Summary
unittest.addModuleCleanup/enterModuleContexthad no effect under pytest because we wiredsetUpModule/tearDownModulebut never ran module cleanups. A prior attempt (#14959) drained the process-global_module_cleanupslist at each module boundary; that is only correct under unittest's contiguous scheduling and is unsafe when pytest interleaves or re-enters modules.This follows the approach discussed on the issue:
_register_setup_module_fixture): recordlen(unittest.case._module_cleanups)at setup, LIFO-drain down to that mark aftertearDownModule(in afinally, and also whensetUpModulefails). Cleanups registered during a module visit therefore stay attributed to that visit.pytest_runtest_setuponce the session is on the SetupState stack.ExceptionGroup, matching class-cleanup handling (stdlib keeps only the first).Deliberate deviations from stdlib (also noted on the issue): import-time registrations run at session end rather than at an arbitrary first module boundary; package/
__init__.pyparity and unconditional fixture registration for modules withoutsetUpModule/tearDownModuleare left for follow-ups (session backstop already covers the no-setup case).AI disclosure
Implementation was assisted by Cursor. I reviewed the approach against the issue discussion and the closed #14959 feedback, own the change, and will handle review feedback.
Test plan
testing/test_unittest.pycovering import-time + setup-time cleanups,enterModuleContext, setup/teardown failure paths, cross-module isolation, ExceptionGroup on multiple cleanup failures, private-API contract, and modules withoutsetUpModulesetUpModuletests pass locally