Skip to content

Can't configure warnings module in pytest_configure #13284

@tlandschoff-scale

Description

@tlandschoff-scale

(This is basically #2430 again, for warnings configured in pytest_configure)

I ran into a nasty problem with pytest-based testing today. We are using pytest since version 2.5 and it usually works great, thanks for enabling that!

But today for a migration (to newer SQLAlchemy), I tried to elevate a specific warning class to be handled as error (instead of giving me thousands of lines as "warnings summary"). My goal was to get the traceback of the deprecated usage.

Our code has a specific warnings configuration for testing already, which we use to note deprecations early, something like this:

def pytest_configure(config):
    ...
    # Find deprecated calls from our code
    warnings.filterwarnings("error", category=DeprecationWarning, module=r"myapp\.")

    # Our own deprecations should error out.
    warnings.filterwarnings("error", category=MyAppDeprecationWarning)

I added some extra filters to elevate the SQLAlchemy warnings to error. To my surprise, the tests were still running and flooding me with the warnings summary of warnings that should have caused exceptions.

After a long debugging session, it turned out that warning configuration in pytest_configure is ignored. This also effected the original warnings (above), so enabling it globally by force turned quite a few test failures.

Full code example (clone gist + use docker for easier analysis)

To illustrate, here is some example code that shows how we used warnings in pytest 3.0, which is now broken:

https://gist.github.com/tlandschoff-scale/d20fe4a6dc7a4b595dcd42562be9fd20

  • conftest.py
import warnings

from app import MyDeprecationWarning


def pytest_configure():
    # Configure app specific warning as error.
    warnings.filterwarnings("error", category=MyDeprecationWarning)
  • test_depwarn.py
import warnings

import pytest

from app import MyDeprecationWarning


def test_deprecated():
    with pytest.raises(MyDeprecationWarning):
        warnings.warn("Stuff deprecated", category=MyDeprecationWarning)
  • app.py
class MyDeprecationWarning(DeprecationWarning):
    pass

Output of code example

$ uv init --python 3.7 && uv add 'pytest>3,<3.1'
Initialized project `warnings`
Using CPython 3.7.9
Creating virtual environment at: .venv
Resolved 5 packages in 135ms
Installed 3 packages in 7ms
 + py==1.11.0
 + pytest==3.0.7
 + setuptools==68.0.0

$ uv run pytest
==================================================== test session starts =====================================================
platform linux -- Python 3.7.9, pytest-3.0.7, py-1.11.0, pluggy-0.4.0
rootdir: /home/torsten.landschoff/tmp/2025-03-10/warnings, inifile:
collected 1 items 

test_depwarn.py .

================================================== 1 passed in 0.01 seconds ==================================================

$ uv python pin 3.13 && uv add 'pytest>7'
Updated `.python-version` from `3.7` -> `3.13`
Using CPython 3.13.1
Removed virtual environment at: .venv
Creating virtual environment at: .venv
Resolved 11 packages in 123ms
Installed 4 packages in 7ms
 + iniconfig==2.0.0
 + packaging==24.0
 + pluggy==1.2.0
 + pytest==7.4.4

$ uv run pytest
==================================================== test session starts =====================================================
platform linux -- Python 3.13.1, pytest-7.4.4, pluggy-1.2.0
rootdir: /home/torsten.landschoff/tmp/2025-03-10/warnings
collected 1 item                                                                                                             

test_depwarn.py F                                                                                                      [100%]

========================================================== FAILURES ==========================================================
______________________________________________________ test_deprecated _______________________________________________________

    def test_deprecated():
>       with pytest.raises(MyDeprecationWarning):
E       Failed: DID NOT RAISE <class 'app.MyDeprecationWarning'>

test_depwarn.py:9: Failed
====================================================== warnings summary ======================================================
test_depwarn.py::test_deprecated
  /home/torsten.landschoff/tmp/2025-03-10/warnings/test_depwarn.py:10: MyDeprecationWarning: Stuff deprecated
    warnings.warn("Stuff deprecated", category=MyDeprecationWarning)

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================================== short test summary info ===================================================
FAILED test_depwarn.py::test_deprecated - Failed: DID NOT RAISE <class 'app.MyDeprecationWarning'>
================================================ 1 failed, 1 warning in 0.02s ================================================

Analysis

It appears that it is impossible to configure warnings in pytest_configure now, as it is wrapped with warnings.catch_warnings() (currently in _pytest/warnings.py).

I would prefer to configure warnings the usual way, for example in pytest.ini by setting filterwarnings, for example by setting

[pytest]
filterwarnings =
    error::app.MyAppDeprecationWarning

But this is broken consistently with built-in Python warning control:

ERROR: while parsing the following warning configuration:

  error::app.MyAppDeprecationWarning

This error occurred:

Traceback (most recent call last):
  File "/home/torsten.landschoff/tmp/2025-03-10/warnings/.venv/lib/python3.13/site-packages/_pytest/config/__init__.py", line 1761, in parse_warning_filter
    category: Type[Warning] = _resolve_warning_category(category_)
                              ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/home/torsten.landschoff/tmp/2025-03-10/warnings/.venv/lib/python3.13/site-packages/_pytest/config/__init__.py", line 1799, in _resolve_warning_category
    m = __import__(module, None, None, [klass])
ModuleNotFoundError: No module named 'app'

So to catch a specific warning, the current implementation is usable (just put the right regex into the filter, together with some built-in base class of the warning). In my case, I don't know what message to exect, I just want to flag all warnings of a given class.

Summary

I think

  • warning filters set up in the pytest_configure hook should take effect IMHO (they currently don't)
  • failing that, there should at least be a way to set up warning filters for application/library warning classes

Metadata

Metadata

Assignees

No one assigned

    Labels

    plugin: warningsrelated to the warnings builtin plugintopic: configrelated to config handling, argument parsing and config file

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions