diff options
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/test/test_warnings/__init__.py | 30 | ||||
-rw-r--r-- | Lib/warnings.py | 22 |
2 files changed, 48 insertions, 4 deletions
diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 70eae4c..a1b3dba 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -2,7 +2,10 @@ from contextlib import contextmanager import linecache import os from io import StringIO +import re import sys +import tempfile +import textwrap import unittest from test import support from test.support.script_helper import assert_python_ok, assert_python_failure @@ -763,12 +766,39 @@ class WarningsDisplayTests(BaseTest): file_object, expected_file_line) self.assertEqual(expect, file_object.getvalue()) + class CWarningsDisplayTests(WarningsDisplayTests, unittest.TestCase): module = c_warnings class PyWarningsDisplayTests(WarningsDisplayTests, unittest.TestCase): module = py_warnings + def test_tracemalloc(self): + with tempfile.NamedTemporaryFile("w", suffix=".py") as tmpfile: + tmpfile.write(textwrap.dedent(""" + def func(): + f = open(__file__) + # Emit ResourceWarning + f = None + + func() + """)) + tmpfile.flush() + fname = tmpfile.name + res = assert_python_ok('-Wd', '-X', 'tracemalloc=2', fname) + stderr = res.err.decode('ascii', 'replace') + stderr = re.sub('<.*>', '<...>', stderr) + expected = textwrap.dedent(f''' + {fname}:5: ResourceWarning: unclosed file <...> + f = None + Object allocated at (most recent call first): + File "{fname}", lineno 3 + f = open(__file__) + File "{fname}", lineno 7 + func() + ''').strip() + self.assertEqual(stderr, expected) + class CatchWarningTests(BaseTest): diff --git a/Lib/warnings.py b/Lib/warnings.py index f54726a..1566065 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -2,6 +2,7 @@ import sys + __all__ = ["warn", "warn_explicit", "showwarning", "formatwarning", "filterwarnings", "simplefilter", "resetwarnings", "catch_warnings"] @@ -66,6 +67,18 @@ def _formatwarnmsg(msg): if line: line = line.strip() s += " %s\n" % line + if msg.source is not None: + import tracemalloc + tb = tracemalloc.get_object_traceback(msg.source) + if tb is not None: + s += 'Object allocated at (most recent call first):\n' + for frame in tb: + s += (' File "%s", lineno %s\n' + % (frame.filename, frame.lineno)) + line = linecache.getline(frame.filename, frame.lineno) + if line: + line = line.strip() + s += ' %s\n' % line return s def filterwarnings(action, message="", category=Warning, module="", lineno=0, @@ -267,7 +280,8 @@ def warn(message, category=None, stacklevel=1): globals) def warn_explicit(message, category, filename, lineno, - module=None, registry=None, module_globals=None): + module=None, registry=None, module_globals=None, + source=None): lineno = int(lineno) if module is None: module = filename or "<unknown>" @@ -333,17 +347,17 @@ def warn_explicit(message, category, filename, lineno, "Unrecognized action (%r) in warnings.filters:\n %s" % (action, item)) # Print message and context - msg = WarningMessage(message, category, filename, lineno) + msg = WarningMessage(message, category, filename, lineno, source) _showwarnmsg(msg) class WarningMessage(object): _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file", - "line") + "line", "source") def __init__(self, message, category, filename, lineno, file=None, - line=None): + line=None, source=None): local_values = locals() for attr in self._WARNING_DETAILS: setattr(self, attr, local_values[attr]) |