Skip to content

Commit

Permalink
Expand warnings output for ResourceWarning
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoddemus committed Feb 13, 2022
1 parent c01a5c1 commit db8dd8e
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 0 deletions.
4 changes: 4 additions & 0 deletions changelog/9644.improvement.rst
@@ -0,0 +1,4 @@
More information about the location of resources that led Python to raise :class:`ResourceWarning` can now
be obtained by enabling :mod:`tracemalloc`.

See :ref:`resource-warnings` for more information.
15 changes: 15 additions & 0 deletions doc/en/how-to/capture-warnings.rst
Expand Up @@ -441,3 +441,18 @@ Please read our :ref:`backwards-compatibility` to learn how we proceed about dep
features.

The full list of warnings is listed in :ref:`the reference documentation <warnings ref>`.


.. _`resource-warnings`:

Resource Warnings
-----------------

Additional information of the source of a :class:`ResourceWarning` can be obtained when captured by pytest if
:mod:`tracemalloc` module is enabled.

One convenient way to enable :mod:`tracemalloc` when running tests is to set the :envvar:`PYTHONTRACEMALLOC` to a large
enough number of frames (say ``20``, but that number is application dependent).

For more information, consult the `Python Development Mode <https://docs.python.org/3/library/devmode.html>`__
section in the Python documentation.
15 changes: 15 additions & 0 deletions src/_pytest/warnings.py
@@ -1,4 +1,5 @@
import sys
import tracemalloc
import warnings
from contextlib import contextmanager
from typing import Generator
Expand Down Expand Up @@ -81,6 +82,20 @@ def warning_record_to_str(warning_message: warnings.WarningMessage) -> str:
warning_message.lineno,
warning_message.line,
)
if warning_message.source is not None:
tb = tracemalloc.get_object_traceback(warning_message.source)
if tb is not None:
formatted_tb = "\n".join(tb.format())
# Use a leading new line to better separate the (large) output
# from the traceback to the previous warning text.
msg += f"\nObject allocated at (most recent call first):\n{formatted_tb}"
else:
# No need for a leading new line.
url = "https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings"
msg += (
"Enable tracemalloc to get traceback where the object was allocated.\n"
)
msg += f"See {url} for more info."
return msg


Expand Down
34 changes: 34 additions & 0 deletions testing/test_warnings.py
@@ -1,4 +1,5 @@
import os
import sys
import warnings
from typing import List
from typing import Optional
Expand Down Expand Up @@ -774,3 +775,36 @@ def test_it():
"*Unknown pytest.mark.unknown*",
]
)


def test_resource_warning(pytester: Pytester, monkeypatch: pytest.MonkeyPatch) -> None:
pytester.makepyfile(
"""
def open_file(p):
f = p.open("r")
assert p.read_text() == "hello"
def test_resource_warning(tmp_path):
p = tmp_path.joinpath("foo.txt")
p.write_text("hello")
open_file(p)
"""
)
result = pytester.run(sys.executable, "-Xdev", "-m", "pytest")
result.stdout.fnmatch_lines(
[
"*ResourceWarning* unclosed file*",
"*Enable tracemalloc to get traceback where the object was allocated*",
"*See https* for more info.",
]
)

monkeypatch.setenv("PYTHONTRACEMALLOC", "20")

result = pytester.run(sys.executable, "-Xdev", "-m", "pytest")
result.stdout.fnmatch_lines(
[
"*ResourceWarning* unclosed file*",
"*Object allocated at (most recent call first)*",
]
)

0 comments on commit db8dd8e

Please sign in to comment.