daveruinseverything
daveruinseverything

Reputation: 5167

Make pytest fail on ResourceWarning (unclosed files)

Some of my tests need to ensure open files are closed. Let's use the simplest example:

# test_example.py
import pytest

@pytest.mark.filterwarnings('error::ResourceWarning')
def test_resourcewarning():
    open('/dev/null')

When I run pytest, the warning is thrown and even printed in the test output, but the test still passes:

$ pytest
============================ test session starts =============================
platform darwin -- Python 3.10.2, pytest-7.1.1, pluggy-1.0.0
rootdir: /path/to/project
collected 1 item

test_example.py .                                                      [100%]

============================== warnings summary ==============================
test_example.py::test_resourcewarning
  /path/to/python/site-packages/_pytest/unraisableexception.py:78: PytestUnrai
sableExceptionWarning: Exception ignored in: <_io.FileIO [closed]>

  Traceback (most recent call last):
    File "/path/to/project/test_example.py", line 5, in test_resourcewarning
      open('/dev/null')
  ResourceWarning: unclosed file <_io.TextIOWrapper name='/dev/null' mode='r' 
encoding='UTF-8'>

    warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
======================== 1 passed, 1 warning in 0.01s ========================

Removing @pytest.mark.filterwarnings or changing the warning type to something unrelated (like DeprecationWarning) results in the test passing without printing any warnings at all, so clearly this is catching the warning - but it seems to be subsequently caught by pytest. Pytest then throws pytest.PytestUnraisableExceptionWarning, which doesn't fail because I wasn't filtering for that. If I do filter for pytest.PytestUnraisableExceptionWarning the test also passes, because it isn't looking for the original ResourceWarning.

The only solution I can think of is to filter for both:

@pytest.mark.filterwarnings('error::ResourceWarning')
@pytest.mark.filterwarnings('error::pytest.PytestUnraisableExceptionWarning')

But at this point it doesn't make any sense to me, even after trawling through docs and google search results I don't understand how filtering for warnings is supposed to work if pytest is just going to swallow them and make me capture its own warnings to actually register a test failure, and so the only logical conclusion is that I don't know what I'm doing (or that this is a bug).

What am I missing?

Upvotes: 7

Views: 1989

Answers (2)

Jason R. Coombs
Jason R. Coombs

Reputation: 42659

I recently learned that ResourceWarnings are ignored by default. In jaraco/skeleton (from which I derive my projects), I've added this commit to enable warnings by default, essentially adding the following to pytest.ini:

[pytest]
filterwarnings =
    default::ResourceWarning

Use error instead of default to enable the errors by default across the project.

Upvotes: 2

Baza86
Baza86

Reputation: 2136

Try this:

#test_example.py
import pytest

@pytest.mark.filterwarnings("error")
def test_resourcewarning():
    open('/dev/null')

This makes the test fail for me:

enter image description here

Taken from the pytest docs (https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings)

Upvotes: 2

Related Questions