gh-131927: Prevent emitting optimizer warnings twice in the REPL (#131993)

This commit is contained in:
Tomas R. 2025-04-12 12:34:36 +02:00 committed by GitHub
parent d4e2cdc15b
commit 3d08c8ad20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 74 additions and 2 deletions

View file

@ -18,3 +18,9 @@ PyAPI_FUNC(int) PyErr_WarnExplicitFormat(
// DEPRECATED: Use PyErr_WarnEx() instead.
#define PyErr_Warn(category, msg) PyErr_WarnEx((category), (msg), 1)
int _PyErr_WarnExplicitObjectWithContext(
PyObject *category,
PyObject *message,
PyObject *filename,
int lineno);

View file

@ -1646,6 +1646,24 @@ class TestSpecifics(unittest.TestCase):
self.assertRaises(NameError, ns['foo'])
def test_compile_warnings(self):
# See gh-131927
# Compile warnings originating from the same file and
# line are now only emitted once.
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("default")
compile('1 is 1', '<stdin>', 'eval')
compile('1 is 1', '<stdin>', 'eval')
self.assertEqual(len(caught), 1)
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
compile('1 is 1', '<stdin>', 'eval')
compile('1 is 1', '<stdin>', 'eval')
self.assertEqual(len(caught), 2)
class TestBooleanExpression(unittest.TestCase):
class Value:
def __init__(self):

View file

@ -1,6 +1,7 @@
import contextlib
import io
import unittest
import warnings
from unittest.mock import patch
from textwrap import dedent
@ -273,3 +274,28 @@ class TestMoreLines(unittest.TestCase):
code = "if foo:"
console = InteractiveColoredConsole(namespace, filename="<stdin>")
self.assertTrue(_more_lines(console, code))
class TestWarnings(unittest.TestCase):
def test_pep_765_warning(self):
"""
Test that a SyntaxWarning emitted from the
AST optimizer is only shown once in the REPL.
"""
# gh-131927
console = InteractiveColoredConsole()
code = dedent("""\
def f():
try:
return 1
finally:
return 2
""")
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("default")
console.runsource(code)
count = sum("'return' in a 'finally' block" in str(w.message)
for w in caught)
self.assertEqual(count, 1)

View file

@ -1479,6 +1479,28 @@ PyErr_WarnExplicitObject(PyObject *category, PyObject *message,
return 0;
}
/* Like PyErr_WarnExplicitObject, but automatically sets up context */
int
_PyErr_WarnExplicitObjectWithContext(PyObject *category, PyObject *message,
PyObject *filename, int lineno)
{
PyObject *unused_filename, *module, *registry;
int unused_lineno;
int stack_level = 1;
if (!setup_context(stack_level, NULL, &unused_filename, &unused_lineno,
&module, &registry)) {
return -1;
}
int rc = PyErr_WarnExplicitObject(category, message, filename, lineno,
module, registry);
Py_DECREF(unused_filename);
Py_DECREF(registry);
Py_DECREF(module);
return rc;
}
int
PyErr_WarnExplicit(PyObject *category, const char *text,
const char *filename_str, int lineno,

View file

@ -1906,8 +1906,8 @@ int
_PyErr_EmitSyntaxWarning(PyObject *msg, PyObject *filename, int lineno, int col_offset,
int end_lineno, int end_col_offset)
{
if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg, filename,
lineno, NULL, NULL) < 0)
if (_PyErr_WarnExplicitObjectWithContext(PyExc_SyntaxWarning, msg,
filename, lineno) < 0)
{
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
/* Replace the SyntaxWarning exception with a SyntaxError