mirror of
https://github.com/python/cpython.git
synced 2025-10-19 07:53:46 +00:00
gh-139640: Fix swallowing syntax warnings in different modules (GH-139755)
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.
This commit is contained in:
parent
1ff6d69fbe
commit
279db6bede
7 changed files with 62 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);
|
|
||||||
|
|
|
@ -1679,22 +1679,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
|
||||||
|
@ -1705,16 +1704,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,2 @@
|
||||||
|
Fix swallowing some syntax warnings in different modules if they
|
||||||
|
accidentally have the same message and are emitted from the same line.
|
|
@ -1473,28 +1473,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);
|
||||||
|
|
|
@ -1962,8 +1962,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