Skip to content

Commit 3d08c8a

Browse files
authored
gh-131927: Prevent emitting optimizer warnings twice in the REPL (#131993)
1 parent d4e2cdc commit 3d08c8a

File tree

5 files changed

+74
-2
lines changed

5 files changed

+74
-2
lines changed

Diff for: Include/cpython/warnings.h

+6
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,9 @@ PyAPI_FUNC(int) PyErr_WarnExplicitFormat(
1818

1919
// DEPRECATED: Use PyErr_WarnEx() instead.
2020
#define PyErr_Warn(category, msg) PyErr_WarnEx((category), (msg), 1)
21+
22+
int _PyErr_WarnExplicitObjectWithContext(
23+
PyObject *category,
24+
PyObject *message,
25+
PyObject *filename,
26+
int lineno);

Diff for: Lib/test/test_compile.py

+18
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,24 @@ class WeirdDict(dict):
16461646

16471647
self.assertRaises(NameError, ns['foo'])
16481648

1649+
def test_compile_warnings(self):
1650+
# See gh-131927
1651+
# Compile warnings originating from the same file and
1652+
# line are now only emitted once.
1653+
with warnings.catch_warnings(record=True) as caught:
1654+
warnings.simplefilter("default")
1655+
compile('1 is 1', '<stdin>', 'eval')
1656+
compile('1 is 1', '<stdin>', 'eval')
1657+
1658+
self.assertEqual(len(caught), 1)
1659+
1660+
with warnings.catch_warnings(record=True) as caught:
1661+
warnings.simplefilter("always")
1662+
compile('1 is 1', '<stdin>', 'eval')
1663+
compile('1 is 1', '<stdin>', 'eval')
1664+
1665+
self.assertEqual(len(caught), 2)
1666+
16491667
class TestBooleanExpression(unittest.TestCase):
16501668
class Value:
16511669
def __init__(self):

Diff for: Lib/test/test_pyrepl/test_interact.py

+26
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import contextlib
22
import io
33
import unittest
4+
import warnings
45
from unittest.mock import patch
56
from textwrap import dedent
67

@@ -273,3 +274,28 @@ def test_incomplete_statement(self):
273274
code = "if foo:"
274275
console = InteractiveColoredConsole(namespace, filename="<stdin>")
275276
self.assertTrue(_more_lines(console, code))
277+
278+
279+
class TestWarnings(unittest.TestCase):
280+
def test_pep_765_warning(self):
281+
"""
282+
Test that a SyntaxWarning emitted from the
283+
AST optimizer is only shown once in the REPL.
284+
"""
285+
# gh-131927
286+
console = InteractiveColoredConsole()
287+
code = dedent("""\
288+
def f():
289+
try:
290+
return 1
291+
finally:
292+
return 2
293+
""")
294+
295+
with warnings.catch_warnings(record=True) as caught:
296+
warnings.simplefilter("default")
297+
console.runsource(code)
298+
299+
count = sum("'return' in a 'finally' block" in str(w.message)
300+
for w in caught)
301+
self.assertEqual(count, 1)

Diff for: Python/_warnings.c

+22
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,28 @@ PyErr_WarnExplicitObject(PyObject *category, PyObject *message,
14791479
return 0;
14801480
}
14811481

1482+
/* Like PyErr_WarnExplicitObject, but automatically sets up context */
1483+
int
1484+
_PyErr_WarnExplicitObjectWithContext(PyObject *category, PyObject *message,
1485+
PyObject *filename, int lineno)
1486+
{
1487+
PyObject *unused_filename, *module, *registry;
1488+
int unused_lineno;
1489+
int stack_level = 1;
1490+
1491+
if (!setup_context(stack_level, NULL, &unused_filename, &unused_lineno,
1492+
&module, &registry)) {
1493+
return -1;
1494+
}
1495+
1496+
int rc = PyErr_WarnExplicitObject(category, message, filename, lineno,
1497+
module, registry);
1498+
Py_DECREF(unused_filename);
1499+
Py_DECREF(registry);
1500+
Py_DECREF(module);
1501+
return rc;
1502+
}
1503+
14821504
int
14831505
PyErr_WarnExplicit(PyObject *category, const char *text,
14841506
const char *filename_str, int lineno,

Diff for: Python/errors.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1906,8 +1906,8 @@ int
19061906
_PyErr_EmitSyntaxWarning(PyObject *msg, PyObject *filename, int lineno, int col_offset,
19071907
int end_lineno, int end_col_offset)
19081908
{
1909-
if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg, filename,
1910-
lineno, NULL, NULL) < 0)
1909+
if (_PyErr_WarnExplicitObjectWithContext(PyExc_SyntaxWarning, msg,
1910+
filename, lineno) < 0)
19111911
{
19121912
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
19131913
/* Replace the SyntaxWarning exception with a SyntaxError

0 commit comments

Comments
 (0)