compiler now ignores constant statements
The compile ignores constant statements and emit a SyntaxWarning warning. Don't emit the warning for string statement because triple quoted string is a common syntax for multiline comments. Don't emit the warning on ellipis neither: 'def f(): ...' is a legit syntax for abstract functions. Changes: * test_ast: ignore SyntaxWarning when compiling test statements. Modify test_load_const() to use assignment expressions rather than constant expression. * test_code: add more kinds of constant statements, ignore SyntaxWarning when testing that the compiler removes constant statements. * test_grammar: ignore SyntaxWarning on the statement "1"
This commit is contained in:
parent
51d8c526d5
commit
a2724095cd
|
@ -3,6 +3,7 @@ import dis
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
import warnings
|
||||||
import weakref
|
import weakref
|
||||||
|
|
||||||
from test import support
|
from test import support
|
||||||
|
@ -239,7 +240,9 @@ class AST_Tests(unittest.TestCase):
|
||||||
ast_tree = compile(i, "?", kind, ast.PyCF_ONLY_AST)
|
ast_tree = compile(i, "?", kind, ast.PyCF_ONLY_AST)
|
||||||
self.assertEqual(to_tuple(ast_tree), o)
|
self.assertEqual(to_tuple(ast_tree), o)
|
||||||
self._assertTrueorder(ast_tree, (0, 0))
|
self._assertTrueorder(ast_tree, (0, 0))
|
||||||
with self.subTest(action="compiling", input=i):
|
with warnings.catch_warnings():
|
||||||
|
warnings.filterwarnings('ignore', category=SyntaxWarning)
|
||||||
|
with self.subTest(action="compiling", input=i, kind=kind):
|
||||||
compile(ast_tree, "?", kind)
|
compile(ast_tree, "?", kind)
|
||||||
|
|
||||||
def test_slice(self):
|
def test_slice(self):
|
||||||
|
@ -1020,27 +1023,23 @@ class ConstantTests(unittest.TestCase):
|
||||||
b'bytes',
|
b'bytes',
|
||||||
(1, 2, 3)]
|
(1, 2, 3)]
|
||||||
|
|
||||||
code = '\n'.join(map(repr, consts))
|
code = '\n'.join(['x={!r}'.format(const) for const in consts])
|
||||||
code += '\n...'
|
code += '\nx = ...'
|
||||||
|
consts.extend((Ellipsis, None))
|
||||||
code_consts = [const for const in consts
|
|
||||||
if (not isinstance(const, (str, int, float, complex))
|
|
||||||
or isinstance(const, bool))]
|
|
||||||
code_consts.append(Ellipsis)
|
|
||||||
# the compiler adds a final "LOAD_CONST None"
|
|
||||||
code_consts.append(None)
|
|
||||||
|
|
||||||
tree = ast.parse(code)
|
tree = ast.parse(code)
|
||||||
self.assertEqual(self.get_load_const(tree), code_consts)
|
self.assertEqual(self.get_load_const(tree),
|
||||||
|
consts)
|
||||||
|
|
||||||
# Replace expression nodes with constants
|
# Replace expression nodes with constants
|
||||||
for expr_node, const in zip(tree.body, consts):
|
for assign, const in zip(tree.body, consts):
|
||||||
assert isinstance(expr_node, ast.Expr)
|
assert isinstance(assign, ast.Assign), ast.dump(assign)
|
||||||
new_node = ast.Constant(value=const)
|
new_node = ast.Constant(value=const)
|
||||||
ast.copy_location(new_node, expr_node.value)
|
ast.copy_location(new_node, assign.value)
|
||||||
expr_node.value = new_node
|
assign.value = new_node
|
||||||
|
|
||||||
self.assertEqual(self.get_load_const(tree), code_consts)
|
self.assertEqual(self.get_load_const(tree),
|
||||||
|
consts)
|
||||||
|
|
||||||
def test_literal_eval(self):
|
def test_literal_eval(self):
|
||||||
tree = ast.parse("1 + 2")
|
tree = ast.parse("1 + 2")
|
||||||
|
|
|
@ -66,24 +66,6 @@ nlocals: 1
|
||||||
flags: 67
|
flags: 67
|
||||||
consts: ('None',)
|
consts: ('None',)
|
||||||
|
|
||||||
>>> def optimize_away():
|
|
||||||
... 'doc string'
|
|
||||||
... 'not a docstring'
|
|
||||||
... 53
|
|
||||||
... 0x53
|
|
||||||
|
|
||||||
>>> dump(optimize_away.__code__)
|
|
||||||
name: optimize_away
|
|
||||||
argcount: 0
|
|
||||||
kwonlyargcount: 0
|
|
||||||
names: ()
|
|
||||||
varnames: ()
|
|
||||||
cellvars: ()
|
|
||||||
freevars: ()
|
|
||||||
nlocals: 0
|
|
||||||
flags: 67
|
|
||||||
consts: ("'doc string'", 'None')
|
|
||||||
|
|
||||||
>>> def keywordonly_args(a,b,*,k1):
|
>>> def keywordonly_args(a,b,*,k1):
|
||||||
... return a,b,k1
|
... return a,b,k1
|
||||||
...
|
...
|
||||||
|
@ -102,8 +84,10 @@ consts: ('None',)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import textwrap
|
||||||
import unittest
|
import unittest
|
||||||
import weakref
|
import weakref
|
||||||
|
import warnings
|
||||||
from test.support import run_doctest, run_unittest, cpython_only
|
from test.support import run_doctest, run_unittest, cpython_only
|
||||||
|
|
||||||
|
|
||||||
|
@ -134,6 +118,44 @@ class CodeTest(unittest.TestCase):
|
||||||
self.assertEqual(co.co_name, "funcname")
|
self.assertEqual(co.co_name, "funcname")
|
||||||
self.assertEqual(co.co_firstlineno, 15)
|
self.assertEqual(co.co_firstlineno, 15)
|
||||||
|
|
||||||
|
def dump(self, co):
|
||||||
|
dump = {}
|
||||||
|
for attr in ["name", "argcount", "kwonlyargcount", "names", "varnames",
|
||||||
|
"cellvars", "freevars", "nlocals", "flags"]:
|
||||||
|
dump[attr] = getattr(co, "co_" + attr)
|
||||||
|
dump['consts'] = tuple(consts(co.co_consts))
|
||||||
|
return dump
|
||||||
|
|
||||||
|
def test_optimize_away(self):
|
||||||
|
ns = {}
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.filterwarnings('ignore', category=SyntaxWarning)
|
||||||
|
exec(textwrap.dedent('''
|
||||||
|
def optimize_away():
|
||||||
|
'doc string'
|
||||||
|
'not a docstring'
|
||||||
|
53
|
||||||
|
0x53
|
||||||
|
b'bytes'
|
||||||
|
1.0
|
||||||
|
True
|
||||||
|
False
|
||||||
|
None
|
||||||
|
...
|
||||||
|
'''), ns)
|
||||||
|
|
||||||
|
self.assertEqual(self.dump(ns['optimize_away'].__code__),
|
||||||
|
{'name': 'optimize_away',
|
||||||
|
'argcount': 0,
|
||||||
|
'kwonlyargcount': 0,
|
||||||
|
'names': (),
|
||||||
|
'varnames': (),
|
||||||
|
'cellvars': (),
|
||||||
|
'freevars': (),
|
||||||
|
'nlocals': 0,
|
||||||
|
'flags': 67,
|
||||||
|
'consts': ("'doc string'", 'None')})
|
||||||
|
|
||||||
|
|
||||||
class CodeWeakRefTest(unittest.TestCase):
|
class CodeWeakRefTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import unittest
|
||||||
import sys
|
import sys
|
||||||
# testing import *
|
# testing import *
|
||||||
from sys import *
|
from sys import *
|
||||||
|
from test import support
|
||||||
|
|
||||||
|
|
||||||
class TokenTests(unittest.TestCase):
|
class TokenTests(unittest.TestCase):
|
||||||
|
@ -424,8 +425,11 @@ class GrammarTests(unittest.TestCase):
|
||||||
# Tested below
|
# Tested below
|
||||||
|
|
||||||
def test_expr_stmt(self):
|
def test_expr_stmt(self):
|
||||||
|
msg = 'ignore constant statement'
|
||||||
|
with support.check_warnings((msg, SyntaxWarning)):
|
||||||
|
exec("1")
|
||||||
|
|
||||||
# (exprlist '=')* exprlist
|
# (exprlist '=')* exprlist
|
||||||
1
|
|
||||||
1, 2, 3
|
1, 2, 3
|
||||||
x = 1
|
x = 1
|
||||||
x = 1, 2, 3
|
x = 1, 2, 3
|
||||||
|
|
|
@ -10,6 +10,10 @@ Release date: tba
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #26204: The compiler now ignores constant statements (ex: "def f(): 1")
|
||||||
|
and emit a SyntaxWarning warning. The warning is not emitted for string and
|
||||||
|
ellipsis (...) statements.
|
||||||
|
|
||||||
- Issue #4806: Avoid masking the original TypeError exception when using star
|
- Issue #4806: Avoid masking the original TypeError exception when using star
|
||||||
(*) unpacking in function calls. Based on patch by Hagen Fürstenau and
|
(*) unpacking in function calls. Based on patch by Hagen Fürstenau and
|
||||||
Daniel Urban.
|
Daniel Urban.
|
||||||
|
|
|
@ -2616,20 +2616,39 @@ compiler_visit_stmt_expr(struct compiler *c, expr_ty value)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value->kind == Str_kind || value->kind == Num_kind) {
|
switch (value->kind)
|
||||||
/* ignore strings and numbers */
|
{
|
||||||
|
case Str_kind:
|
||||||
|
case Ellipsis_kind:
|
||||||
|
/* Issue #26204: ignore string statement, but don't emit a
|
||||||
|
* SyntaxWarning. Triple quoted strings is a common syntax for
|
||||||
|
* multiline comments.
|
||||||
|
*
|
||||||
|
* Don't emit warning on "def f(): ..." neither. It's a legit syntax
|
||||||
|
* for abstract function. */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case Bytes_kind:
|
||||||
|
case Num_kind:
|
||||||
|
case NameConstant_kind:
|
||||||
|
case Constant_kind:
|
||||||
|
{
|
||||||
|
PyObject *msg = PyUnicode_FromString("ignore constant statement");
|
||||||
|
if (msg == NULL)
|
||||||
|
return 0;
|
||||||
|
if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning,
|
||||||
|
msg,
|
||||||
|
c->c_filename, c->u->u_lineno,
|
||||||
|
NULL, NULL) == -1) {
|
||||||
|
Py_DECREF(msg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Py_DECREF(msg);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value->kind == Constant_kind) {
|
default:
|
||||||
PyObject *cst = value->v.Constant.value;
|
break;
|
||||||
if (PyUnicode_CheckExact(cst)
|
|
||||||
|| PyLong_CheckExact(cst)
|
|
||||||
|| PyFloat_CheckExact(cst)
|
|
||||||
|| PyComplex_CheckExact(cst)) {
|
|
||||||
/* ignore strings and numbers */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VISIT(c, expr, value);
|
VISIT(c, expr, value);
|
||||||
|
|
Loading…
Reference in New Issue