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', '', 'eval') + compile('1 is 1', '', 'eval') + + self.assertEqual(len(caught), 1) + + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + compile('1 is 1', '', 'eval') + compile('1 is 1', '', '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="") 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 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy