Skip to content

Race condition with warnings issued once directly and once inside pytest.warns #169

@lesteve

Description

@lesteve

I bumped into a slighty surprising behaviour in scikit-learn and it took me a bit of time to realize that the root cause is that the same warning is issued twice, once directly in the test function and once inside a pytest.warns context. There appears to be some race condition that makes the test fail when run with enough iterations. Not sure pytest-run-parallel can do much about it but I thought I would report it here first.

Wild-guessing, maybe per-module __warningregistry__ is still a global (in contrary the per-thread warnings.filters since Python 3.14 and python/cpython#130010)? My understanding is that per-module __warningregistry__ is used on top of the warnings.filter to decide whether to issue repeated warnings.

Here is a reproducer:

# test.py
import warnings
import pytest

import my_module


def test_pytest_warns():
    my_module.function_that_warns()

    with pytest.warns(UserWarning):
        my_module.function_that_warns()
# my_module.py
import warnings

def function_that_warns():
    warnings.warn('bla')
pytest test.py --parallel-threads 4 --iterations 100

Output:

============================================================================================================= test session starts ==============================================================================================================
platform linux -- Python 3.14.2, pytest-9.0.1, pluggy-1.6.0
rootdir: /tmp
plugins: run-parallel-0.8.0, cov-7.0.0, repeat-0.9.4, xdist-3.8.0
collected 1 item                                                                                                                                                                                                                               
Collected 1 items to run in parallel

test.py e                                                                                                                                                                                                                                [100%]

==================================================================================================================== ERRORS ====================================================================================================================
______________________________________________________________________________________________________ ERROR at call of test_pytest_warns ______________________________________________________________________________________________________

    def test_pytest_warns():
        my_module.function_that_warns()
    
>       with pytest.warns(UserWarning):
             ^^^^^^^^^^^^^^^^^^^^^^^^^
E       Failed: DID NOT WARN. No warnings of type (<class 'UserWarning'>,) were emitted.
E        Emitted warnings: [].

test.py:10: Failed
=============================================================================================================== warnings summary ===============================================================================================================
test.py: 286 warnings
  /tmp/my_module.py:4: UserWarning: bla
    warnings.warn('bla')

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
********************************************************************************************************** pytest-run-parallel report **********************************************************************************************************
All tests were run in parallel! 🎉
=========================================================================================================== short test summary info ============================================================================================================
PARALLEL FAILED test.py::test_pytest_warns - Failed: DID NOT WARN. No warnings of type (<class 'UserWarning'>,) were emitted.
======================================================================================================== 286 warnings, 1 error in 0.07s ========================================================================================================

Possible work-arounds:

  • make sure to use with pytest.warns to capture the first warning as well
  • mark test as thread-unsafe
  • use --mark-warnings-as-unsafe

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions