fix: stop reusing shared PytestWarning instances whose tracebacks grow under -W error - #14931
Open
Devansh-awat wants to merge 1 commit into
Open
Conversation
…w under -W error The deprecation constants in src/_pytest/deprecated.py were module-level PytestWarning instances passed directly to warnings.warn(). When a warning is raised as an error, CPython appends to the exception's existing __traceback__ instead of replacing it, so each raise grew the traceback of these process-lifetime singletons (measured 1 -> 2 -> 3 -> 4 frames across four raises). With multiple tests tripping the same deprecation, later failure reports carried stale frames -- and stale frame locals -- from earlier raises. Fix by storing every constant as an UnformattedWarning and calling .format() at the warn sites; format() returns a fresh instance of the underlying category on each call, so no exception object is ever reused. The four constants that were already UnformattedWarning are unchanged. Warning text, category, and all public names are unchanged. Fixes pytest-dev#14912.
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.
Description
src/_pytest/deprecated.pystored most of its deprecation constants as module-levelPytestWarninginstances that call sites passed straight towarnings.warn(). When a warning is filtered as an error (e.g.-W errororfilterwarnings = error), CPython raises that exact object and appends to its existing__traceback__instead of resetting it. Because the instances live for the whole process, every raise permanently grew their traceback — measured 1 → 2 → 3 → 4 frames across four raises ofPRIVATE. With two tests tripping the same deprecation under-W error::pytest.PytestWarning, the second test's failure report carried stale frames (and pinned frame locals) from the first.The fix stores every constant in the module as an
UnformattedWarningand calls.format()at each warn site.UnformattedWarning.format()constructs a fresh instance of the underlying category on every call, so no exception object is ever raised twice. This is the pattern already used by the four constants that needed runtime formatting (HOOK_LEGACY_MARKING,CLASS_FIXTURE_INSTANCE_METHOD, etc.), so it introduces no new mechanism.UnformattedWarningconstants are untouched.testing/deprecated_test.py) that raises the same deprecation twice under an error filter and asserts the second traceback does not grow; it fails on main (3 frames vs 1) and passes with this change.Checklist
changelog/14912.bugfix.rstfile)Fixes #14912