https://github.com/python/cpython/commit/3d08c8ad20dfabd4864be139cd9c2eb5602ccdfe commit: 3d08c8ad20dfabd4864be139cd9c2eb5602ccdfe branch: main author: Tomas R. <tomas.ro...@gmail.com> committer: iritkatriel <1055913+iritkatr...@users.noreply.github.com> date: 2025-04-12T11:34:36+01:00 summary:
gh-131927: Prevent emitting optimizer warnings twice in the REPL (#131993) files: M Include/cpython/warnings.h M Lib/test/test_compile.py M Lib/test/test_pyrepl/test_interact.py M Python/_warnings.c M Python/errors.c diff --git a/Include/cpython/warnings.h b/Include/cpython/warnings.h index 4e3eb88e8ff447..8731fd2e96b716 100644 --- a/Include/cpython/warnings.h +++ b/Include/cpython/warnings.h @@ -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); diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 9cc025d85e168a..0377b3f954f44b 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -1646,6 +1646,24 @@ class WeirdDict(dict): 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): diff --git a/Lib/test/test_pyrepl/test_interact.py b/Lib/test/test_pyrepl/test_interact.py index af5d4d0e67632a..a20719033fc9b7 100644 --- a/Lib/test/test_pyrepl/test_interact.py +++ b/Lib/test/test_pyrepl/test_interact.py @@ -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 @@ def test_incomplete_statement(self): 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) diff --git a/Python/_warnings.c b/Python/_warnings.c index 912468d2a59a95..39bf1b225ccb0c 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -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, ®istry)) { + 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, diff --git a/Python/errors.c b/Python/errors.c index 14999d6dbaf72e..2b088e2f3888a1 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -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 _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com