FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

unittest.assertWarnsRegex swallows warnings that do not match the regex · Issue #143231 · python/cpython · GitHub

Repository navigation

unittest.assertWarnsRegex swallows warnings that do not match the regex #143231

Closed

Description

Bug report

Bug description:

Suppose a bit of code emits two warnings, and you want to check that both are generated. In #69110 rbcollins said "...there's little if any reason to add support for multiple regexes - just nest two context managers." This doesn't work, though, since the inner context manager swallows the warnings and the outer one doesn't see them.

In addition, the fact that non-matching warnings are swallowed means that a test that checks for a warning will hide any other warnings that the code generates, which is also a bug in the sense that the behavior is unexpected and can lead to tests that hide potential issues from the test author.

Here is a minimal reproducer:

from unittest import TestCase, main
from warnings import warn

def foo():
    warn("msg1", DeprecationWarning)
    warn("msg2", UserWarning)

class Test(TestCase):

    def test_foo(self):
        with self.assertWarnsRegex(DeprecationWarning, 'msg1'):
            with self.assertWarnsRegex(UserWarning, 'msg2'):
                foo()

main()

This results in a test failure for 'msg1'. if you remove one of the asserts, the test will pass, but the second warning will not appear on the console.

I'm not sure the warnings machinery has the facilities needed to actually fix the second issue, but the unittest issue should be fixable.

In my actual use case the outer assert is made by a wrapper used by multiple tests, while the inner is made by an individual test, and both are DeprecationWarnings, so using the tuple feature of assertWarnsRegex would not solve my issue for multiple reasons.

CPython versions tested on:

CPython main branch

Operating systems tested on:

No response

Linked PRs

Activity

  1. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Dec 27, 2025
  2. tomasr8 commented on Dec 27, 2025

    Member

    At a first glance, the context manager could simply replay all the caught warnings that do not match using something like warnings.warn_explicit. Here's the basic idea that makes your reproducer pass:

    diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py
    index eba50839cd..bfc00b3c32 100644
    --- a/Lib/unittest/case.py
    +++ b/Lib/unittest/case.py
    @@ -317,11 +317,13 @@ def __exit__(self, exc_type, exc_value, tb):
             for m in self.warnings:
                 w = m.message
                 if not isinstance(w, self.expected):
    +                self._reraise_warning(m)
                     continue
                 if first_matching is None:
                     first_matching = w
                 if (self.expected_regex is not None and
                     not self.expected_regex.search(str(w))):
    +                self._reraise_warning(m)
                     continue
                 # store warning for later retrieval
                 self.warning = w
    @@ -338,6 +340,15 @@ def __exit__(self, exc_type, exc_value, tb):
             else:
                 self._raiseFailure("{} not triggered".format(exc_name))
     
    +    def _reraise_warning(self, w):
    +        warnings.warn_explicit(
    +            message=w.message,
    +            category=w.category,
    +            filename=w.filename,
    +            lineno=w.lineno,
    +            source=w.source,
    +        )
    +
     
     class _AssertNotWarnsContext(_AssertWarnsContext):
     
    

    I'll have to think if there are some edge cases though I can try to put together a PR tomorrow.

  3. added
    type-featureA feature request or enhancement
    3.15bugs and security fixes
    and removed
    type-bugAn unexpected behavior, bug, or error
    on Dec 28, 2025
  4. serhiy-storchaka commented on Dec 28, 2025

    Member

    I think this is rather a new feature.

  5. serhiy-storchaka commented on Dec 28, 2025

    Member

    For search: it is related not only to unittest.assertWarnsRegex(), but also to unittest.assertWarns().

  6. medmunds commented on Apr 30, 2026

    Contributor

    I recently ran into the same behavior. In my actual use case, unrelated deprecation warnings that should have caused test errors were instead suppressed by assertWarnsRegex(DeprecationWarning, r"expected deprecation message").

    I would expect assertWarns(category) to swallow all warnings of that category, but I would not expect assertWarnsRegex(category, regex) to swallow warnings that don't match the regex. (Even if some implementation happens to be shared with assertWarns().)

  7. bitdancer commented on Apr 30, 2026

    MemberAuthor

    For my use case I adopted a variation on the stdlib test.support.warnings_helper._filterwarnings:

    @contextmanager
    def check_all_warnings(*filters):
        """Raise an error if the generated warnings to not exactly match filters."""
    return _filterwarnings(filters)
    

    It seems to me it might be possible to restrict that to checking warnings in just the module under test (or maybe even a list of modules), and thus make it useful outside the stdlib, but I haven't investigated that possibility yet.

  8. medmunds commented on Apr 30, 2026

    Contributor

    That's helpful, thanks.

    The swallow-the-entire-category behavior still feels like a bug in assertWarnsRegex(). (Though there are probably a lot of existing tests that inadvertently depend on it.)

    At a minimum, the docs should probably note this somewhat unexpected behavior.

  9. serhiy-storchaka commented on May 1, 2026

    Member

    See #149229.

    It is not perfect. Since some information (like module name or registry) is not saved in WarningMessage, some external warning filters may not work or work incorrectly with the leaked warnings. This will only affect corner cases, like actions "once" and "module", and compiler warnings emitted by compile(), exec(), import, etc. Fixing these corner cases requires significant effort.

  10. bitdancer commented on May 1, 2026

    MemberAuthor

    I think the improvement is worth it despite the corner cases, but I wonder, should the docs explain that the warnings are re-issued rather than not caught, possibly mentioning the corner cases?

  11. serhiy-storchaka commented on May 1, 2026

    Member

    This is rather an implementation detail which will change in future.

    I am going to do something with corner cases, but it requires larger changes in the warning module, so it may be a different issue.

  12. bitdancer commented on May 1, 2026

    MemberAuthor

    Ok, so it's more like a known remaining bug we are going to fix. Makes sense.

  13. added 2 commits that reference this issue on May 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

3.15bugs and security fixesstdlibStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancement

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions


Back | FazBrowse Home | New Git URL