mirror of
https://github.com/python/cpython.git
synced 2025-11-01 14:11:41 +00:00
[3.14] gh-139640: Fix swallowing syntax warnings in different modules (GH-139755) (GH-140117)
Revert GH-131993.
Fix swallowing some syntax warnings in different modules if they accidentally
have the same message and are emitted from the same line.
Fix duplicated warnings in the "finally" block.
(cherry picked from commit 279db6bede)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
* Update 2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst
---------
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
99e42ab49c
commit
af28efd8b4
7 changed files with 63 additions and 74 deletions
|
|
@ -18,9 +18,3 @@ PyAPI_FUNC(int) PyErr_WarnExplicitFormat(
|
||||||
|
|
||||||
// DEPRECATED: Use PyErr_WarnEx() instead.
|
// DEPRECATED: Use PyErr_WarnEx() instead.
|
||||||
#define PyErr_Warn(category, msg) PyErr_WarnEx((category), (msg), 1)
|
#define PyErr_Warn(category, msg) PyErr_WarnEx((category), (msg), 1)
|
||||||
|
|
||||||
int _PyErr_WarnExplicitObjectWithContext(
|
|
||||||
PyObject *category,
|
|
||||||
PyObject *message,
|
|
||||||
PyObject *filename,
|
|
||||||
int lineno);
|
|
||||||
|
|
|
||||||
|
|
@ -1662,22 +1662,21 @@ class WeirdDict(dict):
|
||||||
self.assertRaises(NameError, ns['foo'])
|
self.assertRaises(NameError, ns['foo'])
|
||||||
|
|
||||||
def test_compile_warnings(self):
|
def test_compile_warnings(self):
|
||||||
# See gh-131927
|
# Each invocation of compile() emits compiler warnings, even if they
|
||||||
# Compile warnings originating from the same file and
|
# have the same message and line number.
|
||||||
# line are now only emitted once.
|
source = textwrap.dedent(r"""
|
||||||
|
# tokenizer
|
||||||
|
1or 0 # line 3
|
||||||
|
# code generator
|
||||||
|
1 is 1 # line 5
|
||||||
|
""")
|
||||||
with warnings.catch_warnings(record=True) as caught:
|
with warnings.catch_warnings(record=True) as caught:
|
||||||
warnings.simplefilter("default")
|
warnings.simplefilter("default")
|
||||||
compile('1 is 1', '<stdin>', 'eval')
|
for i in range(2):
|
||||||
compile('1 is 1', '<stdin>', 'eval')
|
# Even if compile() is at the same line.
|
||||||
|
compile(source, '<stdin>', 'exec')
|
||||||
|
|
||||||
self.assertEqual(len(caught), 1)
|
self.assertEqual([wm.lineno for wm in caught], [3, 5] * 2)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def test_compile_warning_in_finally(self):
|
def test_compile_warning_in_finally(self):
|
||||||
# Ensure that warnings inside finally blocks are
|
# Ensure that warnings inside finally blocks are
|
||||||
|
|
@ -1688,16 +1687,47 @@ def test_compile_warning_in_finally(self):
|
||||||
try:
|
try:
|
||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
1 is 1
|
1 is 1 # line 5
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
finally: # nested
|
||||||
|
1 is 1 # line 9
|
||||||
""")
|
""")
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as caught:
|
with warnings.catch_warnings(record=True) as caught:
|
||||||
warnings.simplefilter("default")
|
warnings.simplefilter("always")
|
||||||
compile(source, '<stdin>', 'exec')
|
compile(source, '<stdin>', 'exec')
|
||||||
|
|
||||||
self.assertEqual(len(caught), 1)
|
self.assertEqual(sorted(wm.lineno for wm in caught), [5, 9])
|
||||||
self.assertEqual(caught[0].category, SyntaxWarning)
|
for wm in caught:
|
||||||
self.assertIn("\"is\" with 'int' literal", str(caught[0].message))
|
self.assertEqual(wm.category, SyntaxWarning)
|
||||||
|
self.assertIn("\"is\" with 'int' literal", str(wm.message))
|
||||||
|
|
||||||
|
# Other code path is used for "try" with "except*".
|
||||||
|
source = textwrap.dedent("""
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
except *Exception:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
1 is 1 # line 7
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
except *Exception:
|
||||||
|
pass
|
||||||
|
finally: # nested
|
||||||
|
1 is 1 # line 13
|
||||||
|
""")
|
||||||
|
|
||||||
|
with warnings.catch_warnings(record=True) as caught:
|
||||||
|
warnings.simplefilter("always")
|
||||||
|
compile(source, '<stdin>', 'exec')
|
||||||
|
|
||||||
|
self.assertEqual(sorted(wm.lineno for wm in caught), [7, 13])
|
||||||
|
for wm in caught:
|
||||||
|
self.assertEqual(wm.category, SyntaxWarning)
|
||||||
|
self.assertIn("\"is\" with 'int' literal", str(wm.message))
|
||||||
|
|
||||||
|
|
||||||
class TestBooleanExpression(unittest.TestCase):
|
class TestBooleanExpression(unittest.TestCase):
|
||||||
class Value:
|
class Value:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import contextlib
|
import contextlib
|
||||||
import io
|
import io
|
||||||
import unittest
|
import unittest
|
||||||
import warnings
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
|
|
@ -274,28 +273,3 @@ def test_incomplete_statement(self):
|
||||||
code = "if foo:"
|
code = "if foo:"
|
||||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||||
self.assertTrue(_more_lines(console, code))
|
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)
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fix swallowing some syntax warnings in different modules if they
|
||||||
|
accidentally have the same message and are emitted from the same line.
|
||||||
|
Fix duplicated warnings in the ``finally`` block.
|
||||||
|
|
@ -1479,28 +1479,6 @@ PyErr_WarnExplicitObject(PyObject *category, PyObject *message,
|
||||||
return 0;
|
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
|
int
|
||||||
PyErr_WarnExplicit(PyObject *category, const char *text,
|
PyErr_WarnExplicit(PyObject *category, const char *text,
|
||||||
const char *filename_str, int lineno,
|
const char *filename_str, int lineno,
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ typedef struct _PyCompiler {
|
||||||
bool c_save_nested_seqs; /* if true, construct recursive instruction sequences
|
bool c_save_nested_seqs; /* if true, construct recursive instruction sequences
|
||||||
* (including instructions for nested code objects)
|
* (including instructions for nested code objects)
|
||||||
*/
|
*/
|
||||||
|
int c_disable_warning;
|
||||||
} compiler;
|
} compiler;
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
@ -765,6 +766,9 @@ _PyCompile_PushFBlock(compiler *c, location loc,
|
||||||
f->fb_loc = loc;
|
f->fb_loc = loc;
|
||||||
f->fb_exit = exit;
|
f->fb_exit = exit;
|
||||||
f->fb_datum = datum;
|
f->fb_datum = datum;
|
||||||
|
if (t == COMPILE_FBLOCK_FINALLY_END) {
|
||||||
|
c->c_disable_warning++;
|
||||||
|
}
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -776,6 +780,9 @@ _PyCompile_PopFBlock(compiler *c, fblocktype t, jump_target_label block_label)
|
||||||
u->u_nfblocks--;
|
u->u_nfblocks--;
|
||||||
assert(u->u_fblock[u->u_nfblocks].fb_type == t);
|
assert(u->u_fblock[u->u_nfblocks].fb_type == t);
|
||||||
assert(SAME_JUMP_TARGET_LABEL(u->u_fblock[u->u_nfblocks].fb_block, block_label));
|
assert(SAME_JUMP_TARGET_LABEL(u->u_fblock[u->u_nfblocks].fb_block, block_label));
|
||||||
|
if (t == COMPILE_FBLOCK_FINALLY_END) {
|
||||||
|
c->c_disable_warning--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fblockinfo *
|
fblockinfo *
|
||||||
|
|
@ -1203,6 +1210,9 @@ _PyCompile_Error(compiler *c, location loc, const char *format, ...)
|
||||||
int
|
int
|
||||||
_PyCompile_Warn(compiler *c, location loc, const char *format, ...)
|
_PyCompile_Warn(compiler *c, location loc, const char *format, ...)
|
||||||
{
|
{
|
||||||
|
if (c->c_disable_warning) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
va_list vargs;
|
va_list vargs;
|
||||||
va_start(vargs, format);
|
va_start(vargs, format);
|
||||||
PyObject *msg = PyUnicode_FromFormatV(format, vargs);
|
PyObject *msg = PyUnicode_FromFormatV(format, vargs);
|
||||||
|
|
|
||||||
|
|
@ -1938,8 +1938,8 @@ int
|
||||||
_PyErr_EmitSyntaxWarning(PyObject *msg, PyObject *filename, int lineno, int col_offset,
|
_PyErr_EmitSyntaxWarning(PyObject *msg, PyObject *filename, int lineno, int col_offset,
|
||||||
int end_lineno, int end_col_offset)
|
int end_lineno, int end_col_offset)
|
||||||
{
|
{
|
||||||
if (_PyErr_WarnExplicitObjectWithContext(PyExc_SyntaxWarning, msg,
|
if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg,
|
||||||
filename, lineno) < 0)
|
filename, lineno, NULL, NULL) < 0)
|
||||||
{
|
{
|
||||||
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
|
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
|
||||||
/* Replace the SyntaxWarning exception with a SyntaxError
|
/* Replace the SyntaxWarning exception with a SyntaxError
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue