mirror of
https://github.com/python/cpython.git
synced 2026-05-06 12:49:07 -04:00
gh-130080: implement PEP 765 (#130087)
This commit is contained in:
@@ -420,16 +420,16 @@ is executed. If there is a saved exception it is re-raised at the end of the
|
||||
:keyword:`!finally` clause. If the :keyword:`!finally` clause raises another
|
||||
exception, the saved exception is set as the context of the new exception.
|
||||
If the :keyword:`!finally` clause executes a :keyword:`return`, :keyword:`break`
|
||||
or :keyword:`continue` statement, the saved exception is discarded::
|
||||
or :keyword:`continue` statement, the saved exception is discarded. For example,
|
||||
this function returns 42.
|
||||
|
||||
>>> def f():
|
||||
... try:
|
||||
... 1/0
|
||||
... finally:
|
||||
... return 42
|
||||
...
|
||||
>>> f()
|
||||
42
|
||||
.. code-block::
|
||||
|
||||
def f():
|
||||
try:
|
||||
1/0
|
||||
finally:
|
||||
return 42
|
||||
|
||||
The exception information is not available to the program during execution of
|
||||
the :keyword:`!finally` clause.
|
||||
@@ -446,21 +446,25 @@ statement, the :keyword:`!finally` clause is also executed 'on the way out.'
|
||||
The return value of a function is determined by the last :keyword:`return`
|
||||
statement executed. Since the :keyword:`!finally` clause always executes, a
|
||||
:keyword:`!return` statement executed in the :keyword:`!finally` clause will
|
||||
always be the last one executed::
|
||||
always be the last one executed. The following function returns 'finally'.
|
||||
|
||||
>>> def foo():
|
||||
... try:
|
||||
... return 'try'
|
||||
... finally:
|
||||
... return 'finally'
|
||||
...
|
||||
>>> foo()
|
||||
'finally'
|
||||
.. code-block::
|
||||
|
||||
def foo():
|
||||
try:
|
||||
return 'try'
|
||||
finally:
|
||||
return 'finally'
|
||||
|
||||
.. versionchanged:: 3.8
|
||||
Prior to Python 3.8, a :keyword:`continue` statement was illegal in the
|
||||
:keyword:`!finally` clause due to a problem with the implementation.
|
||||
|
||||
.. versionchanged:: next
|
||||
The compiler emits a :exc:`SyntaxWarning` when a :keyword:`return`,
|
||||
:keyword:`break` or :keyword:`continue` appears in a :keyword:`!finally`
|
||||
block (see :pep:`765`).
|
||||
|
||||
|
||||
.. _with:
|
||||
.. _as:
|
||||
|
||||
@@ -418,7 +418,9 @@ points discuss more complex cases when an exception occurs:
|
||||
|
||||
* If the :keyword:`!finally` clause executes a :keyword:`break`,
|
||||
:keyword:`continue` or :keyword:`return` statement, exceptions are not
|
||||
re-raised.
|
||||
re-raised. This can be confusing and is therefore discouraged. From
|
||||
version 3.14 the compiler emits a :exc:`SyntaxWarning` for it
|
||||
(see :pep:`765`).
|
||||
|
||||
* If the :keyword:`!try` statement reaches a :keyword:`break`,
|
||||
:keyword:`continue` or :keyword:`return` statement, the
|
||||
@@ -430,7 +432,9 @@ points discuss more complex cases when an exception occurs:
|
||||
statement, the returned value will be the one from the
|
||||
:keyword:`!finally` clause's :keyword:`!return` statement, not the
|
||||
value from the :keyword:`!try` clause's :keyword:`!return`
|
||||
statement.
|
||||
statement. This can be confusing and is therefore discouraged. From
|
||||
version 3.14 the compiler emits a :exc:`SyntaxWarning` for it
|
||||
(see :pep:`765`).
|
||||
|
||||
For example::
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ Summary -- release highlights
|
||||
* :ref:`PEP 741: Python Configuration C API <whatsnew314-pep741>`
|
||||
* :ref:`PEP 761: Discontinuation of PGP signatures <whatsnew314-pep761>`
|
||||
* :ref:`A new type of interpreter <whatsnew314-tail-call>`
|
||||
* :ref:`PEP 765: Disallow return/break/continue that exit a finally block <whatsnew314-pep765>`
|
||||
|
||||
|
||||
Incompatible changes
|
||||
@@ -370,6 +371,15 @@ Other language changes
|
||||
The testbed can also be used to run the test suite of projects other than
|
||||
CPython itself. (Contributed by Russell Keith-Magee in :gh:`127592`.)
|
||||
|
||||
.. _whatsnew314-pep765:
|
||||
|
||||
PEP 765: Disallow return/break/continue that exit a finally block
|
||||
-----------------------------------------------------------------
|
||||
|
||||
The compiler emits a :exc:`SyntaxWarning` when a :keyword:`return`, :keyword:`break` or
|
||||
:keyword:`continue` statements appears where it exits a :keyword:`finally` block.
|
||||
This change is specified in :pep:`765`.
|
||||
|
||||
New modules
|
||||
===========
|
||||
|
||||
|
||||
@@ -40,13 +40,16 @@ extern int _PyCompile_AstOptimize(
|
||||
PyObject *filename,
|
||||
PyCompilerFlags *flags,
|
||||
int optimize,
|
||||
struct _arena *arena);
|
||||
struct _arena *arena,
|
||||
int syntax_check_only);
|
||||
|
||||
extern int _PyAST_Optimize(
|
||||
struct _mod *,
|
||||
struct _arena *arena,
|
||||
PyObject *filename,
|
||||
int optimize,
|
||||
int ff_features);
|
||||
int ff_features,
|
||||
int syntax_check_only);
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -37,6 +37,7 @@ class AllTest(unittest.TestCase):
|
||||
(".* (module|package)", DeprecationWarning),
|
||||
(".* (module|package)", PendingDeprecationWarning),
|
||||
("", ResourceWarning),
|
||||
("", SyntaxWarning),
|
||||
quiet=True):
|
||||
try:
|
||||
exec("import %s" % modname, names)
|
||||
@@ -52,6 +53,7 @@ class AllTest(unittest.TestCase):
|
||||
with warnings_helper.check_warnings(
|
||||
("", DeprecationWarning),
|
||||
("", ResourceWarning),
|
||||
("", SyntaxWarning),
|
||||
quiet=True):
|
||||
try:
|
||||
exec("from %s import *" % modname, names)
|
||||
|
||||
@@ -820,6 +820,61 @@ class AST_Tests(unittest.TestCase):
|
||||
r"Exceeds the limit \(\d+ digits\)"):
|
||||
repr(ast.Constant(value=eval(source)))
|
||||
|
||||
def test_pep_765_warnings(self):
|
||||
srcs = [
|
||||
textwrap.dedent("""
|
||||
def f():
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
return 42
|
||||
"""),
|
||||
textwrap.dedent("""
|
||||
for x in y:
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
break
|
||||
"""),
|
||||
textwrap.dedent("""
|
||||
for x in y:
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
continue
|
||||
"""),
|
||||
]
|
||||
for src in srcs:
|
||||
with self.assertWarnsRegex(SyntaxWarning, 'finally'):
|
||||
ast.parse(src)
|
||||
|
||||
def test_pep_765_no_warnings(self):
|
||||
srcs = [
|
||||
textwrap.dedent("""
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
def f():
|
||||
return 42
|
||||
"""),
|
||||
textwrap.dedent("""
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
for x in y:
|
||||
break
|
||||
"""),
|
||||
textwrap.dedent("""
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
for x in y:
|
||||
continue
|
||||
"""),
|
||||
]
|
||||
for src in srcs:
|
||||
ast.parse(src)
|
||||
|
||||
|
||||
class CopyTests(unittest.TestCase):
|
||||
"""Test copying and pickling AST nodes."""
|
||||
|
||||
@@ -84,7 +84,8 @@ class TestBreakContinueReturnInExceptStarBlock(unittest.TestCase):
|
||||
if i == 2:
|
||||
break
|
||||
finally:
|
||||
return 0
|
||||
pass
|
||||
return 0
|
||||
""")
|
||||
|
||||
|
||||
@@ -117,7 +118,8 @@ class TestBreakContinueReturnInExceptStarBlock(unittest.TestCase):
|
||||
if i == 2:
|
||||
continue
|
||||
finally:
|
||||
return 0
|
||||
pass
|
||||
return 0
|
||||
""")
|
||||
|
||||
def test_return_in_except_star_block_invalid(self):
|
||||
|
||||
+126
-33
@@ -858,7 +858,7 @@ Traceback (most recent call last):
|
||||
SyntaxError: 'function call' is an illegal expression for augmented assignment
|
||||
|
||||
|
||||
Test continue in finally in weird combinations.
|
||||
Test control flow in finally
|
||||
|
||||
continue in for loop under finally should be ok.
|
||||
|
||||
@@ -872,51 +872,63 @@ continue in for loop under finally should be ok.
|
||||
>>> test()
|
||||
9
|
||||
|
||||
continue in a finally should be ok.
|
||||
break in for loop under finally should be ok.
|
||||
|
||||
>>> def test():
|
||||
... for abc in range(10):
|
||||
... try:
|
||||
... pass
|
||||
... finally:
|
||||
... continue
|
||||
... print(abc)
|
||||
... try:
|
||||
... pass
|
||||
... finally:
|
||||
... for abc in range(10):
|
||||
... break
|
||||
... print(abc)
|
||||
>>> test()
|
||||
9
|
||||
0
|
||||
|
||||
return in function under finally should be ok.
|
||||
|
||||
>>> def test():
|
||||
... for abc in range(10):
|
||||
... try:
|
||||
... pass
|
||||
... finally:
|
||||
... try:
|
||||
... continue
|
||||
... except:
|
||||
... pass
|
||||
... print(abc)
|
||||
... try:
|
||||
... pass
|
||||
... finally:
|
||||
... def f():
|
||||
... return 42
|
||||
... print(f())
|
||||
>>> test()
|
||||
9
|
||||
42
|
||||
|
||||
combine for loop and function def
|
||||
|
||||
return in function under finally should be ok.
|
||||
|
||||
>>> def test():
|
||||
... for abc in range(10):
|
||||
... try:
|
||||
... pass
|
||||
... finally:
|
||||
... try:
|
||||
... pass
|
||||
... except:
|
||||
... continue
|
||||
... print(abc)
|
||||
... try:
|
||||
... pass
|
||||
... finally:
|
||||
... for i in range(10):
|
||||
... def f():
|
||||
... return 42
|
||||
... print(f())
|
||||
>>> test()
|
||||
9
|
||||
42
|
||||
|
||||
>>> def test():
|
||||
... try:
|
||||
... pass
|
||||
... finally:
|
||||
... def f():
|
||||
... for i in range(10):
|
||||
... return 42
|
||||
... print(f())
|
||||
>>> test()
|
||||
42
|
||||
|
||||
A continue outside loop should not be allowed.
|
||||
|
||||
>>> def foo():
|
||||
... try:
|
||||
... pass
|
||||
... finally:
|
||||
... continue
|
||||
... finally:
|
||||
... pass
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
SyntaxError: 'continue' not properly in loop
|
||||
@@ -2393,7 +2405,88 @@ import unittest
|
||||
|
||||
from test import support
|
||||
|
||||
class SyntaxTestCase(unittest.TestCase):
|
||||
class SyntaxWarningTest(unittest.TestCase):
|
||||
def check_warning(self, code, errtext, filename="<testcase>", mode="exec"):
|
||||
"""Check that compiling code raises SyntaxWarning with errtext.
|
||||
|
||||
errtest is a regular expression that must be present in the
|
||||
text of the warning raised.
|
||||
"""
|
||||
with self.assertWarnsRegex(SyntaxWarning, errtext):
|
||||
compile(code, filename, mode)
|
||||
|
||||
def test_return_in_finally(self):
|
||||
source = textwrap.dedent("""
|
||||
def f():
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
return 42
|
||||
""")
|
||||
self.check_warning(source, "'return' in a 'finally' block")
|
||||
|
||||
source = textwrap.dedent("""
|
||||
def f():
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
try:
|
||||
return 42
|
||||
except:
|
||||
pass
|
||||
""")
|
||||
self.check_warning(source, "'return' in a 'finally' block")
|
||||
|
||||
source = textwrap.dedent("""
|
||||
def f():
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
return 42
|
||||
""")
|
||||
self.check_warning(source, "'return' in a 'finally' block")
|
||||
|
||||
def test_break_and_continue_in_finally(self):
|
||||
for kw in ('break', 'continue'):
|
||||
|
||||
source = textwrap.dedent(f"""
|
||||
for abc in range(10):
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
{kw}
|
||||
""")
|
||||
self.check_warning(source, f"'{kw}' in a 'finally' block")
|
||||
|
||||
source = textwrap.dedent(f"""
|
||||
for abc in range(10):
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
try:
|
||||
{kw}
|
||||
except:
|
||||
pass
|
||||
""")
|
||||
self.check_warning(source, f"'{kw}' in a 'finally' block")
|
||||
|
||||
source = textwrap.dedent(f"""
|
||||
for abc in range(10):
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
{kw}
|
||||
""")
|
||||
self.check_warning(source, f"'{kw}' in a 'finally' block")
|
||||
|
||||
|
||||
class SyntaxErrorTestCase(unittest.TestCase):
|
||||
|
||||
def _check_error(self, code, errtext,
|
||||
filename="<testcase>", mode="exec", subclass=None,
|
||||
@@ -2401,7 +2494,7 @@ class SyntaxTestCase(unittest.TestCase):
|
||||
"""Check that compiling code raises SyntaxError with errtext.
|
||||
|
||||
errtest is a regular expression that must be present in the
|
||||
test of the exception raised. If subclass is specified it
|
||||
text of the exception raised. If subclass is specified it
|
||||
is the expected subclass of SyntaxError (e.g. IndentationError).
|
||||
"""
|
||||
try:
|
||||
|
||||
@@ -422,9 +422,11 @@ class UnparseTestCase(ASTTestCase):
|
||||
self.check_ast_roundtrip(f"'''{docstring}'''")
|
||||
|
||||
def test_constant_tuples(self):
|
||||
self.check_src_roundtrip(ast.Module([ast.Constant(value=(1,))]), "(1,)")
|
||||
locs = ast.fix_missing_locations
|
||||
self.check_src_roundtrip(
|
||||
ast.Module([ast.Constant(value=(1, 2, 3))]), "(1, 2, 3)"
|
||||
locs(ast.Module([ast.Expr(ast.Constant(value=(1,)))])), "(1,)")
|
||||
self.check_src_roundtrip(
|
||||
locs(ast.Module([ast.Expr(ast.Constant(value=(1, 2, 3)))])), "(1, 2, 3)"
|
||||
)
|
||||
|
||||
def test_function_type(self):
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Implement PEP 765: Disallow return/break/continue that exit a finally block.
|
||||
+162
-10
@@ -1,15 +1,28 @@
|
||||
/* AST Optimizer */
|
||||
#include "Python.h"
|
||||
#include "pycore_ast.h" // _PyAST_GetDocString()
|
||||
#include "pycore_c_array.h" // _Py_CArray_EnsureCapacity()
|
||||
#include "pycore_format.h" // F_LJUST
|
||||
#include "pycore_runtime.h" // _Py_STR()
|
||||
#include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString()
|
||||
#include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString()
|
||||
|
||||
|
||||
/* See PEP 765 */
|
||||
typedef struct {
|
||||
bool in_finally;
|
||||
bool in_funcdef;
|
||||
bool in_loop;
|
||||
} ControlFlowInFinallyContext;
|
||||
|
||||
typedef struct {
|
||||
PyObject *filename;
|
||||
int optimize;
|
||||
int ff_features;
|
||||
int syntax_check_only;
|
||||
|
||||
_Py_c_array_t cf_finally; /* context for PEP 678 check */
|
||||
int cf_finally_used;
|
||||
} _PyASTOptimizeState;
|
||||
|
||||
#define ENTER_RECURSIVE() \
|
||||
@@ -19,6 +32,102 @@ if (Py_EnterRecursiveCall(" during compilation")) { \
|
||||
|
||||
#define LEAVE_RECURSIVE() Py_LeaveRecursiveCall();
|
||||
|
||||
static ControlFlowInFinallyContext*
|
||||
get_cf_finally_top(_PyASTOptimizeState *state)
|
||||
{
|
||||
int idx = state->cf_finally_used;
|
||||
return ((ControlFlowInFinallyContext*)state->cf_finally.array) + idx;
|
||||
}
|
||||
|
||||
static int
|
||||
push_cf_context(_PyASTOptimizeState *state, stmt_ty node, bool finally, bool funcdef, bool loop)
|
||||
{
|
||||
if (_Py_CArray_EnsureCapacity(&state->cf_finally, state->cf_finally_used+1) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->cf_finally_used++;
|
||||
ControlFlowInFinallyContext *ctx = get_cf_finally_top(state);
|
||||
|
||||
ctx->in_finally = finally;
|
||||
ctx->in_funcdef = funcdef;
|
||||
ctx->in_loop = loop;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
pop_cf_context(_PyASTOptimizeState *state)
|
||||
{
|
||||
assert(state->cf_finally_used > 0);
|
||||
state->cf_finally_used--;
|
||||
}
|
||||
|
||||
static int
|
||||
control_flow_in_finally_warning(const char *kw, stmt_ty n, _PyASTOptimizeState *state)
|
||||
{
|
||||
PyObject *msg = PyUnicode_FromFormat("'%s' in a 'finally' block", kw);
|
||||
if (msg == NULL) {
|
||||
return 0;
|
||||
}
|
||||
int ret = _PyErr_EmitSyntaxWarning(msg, state->filename, n->lineno,
|
||||
n->col_offset + 1, n->end_lineno,
|
||||
n->end_col_offset + 1);
|
||||
Py_DECREF(msg);
|
||||
return ret < 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
static int
|
||||
before_return(_PyASTOptimizeState *state, stmt_ty node_)
|
||||
{
|
||||
if (state->cf_finally_used > 0) {
|
||||
ControlFlowInFinallyContext *ctx = get_cf_finally_top(state);
|
||||
if (ctx->in_finally && ! ctx->in_funcdef) {
|
||||
if (!control_flow_in_finally_warning("return", node_, state)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
before_loop_exit(_PyASTOptimizeState *state, stmt_ty node_, const char *kw)
|
||||
{
|
||||
if (state->cf_finally_used > 0) {
|
||||
ControlFlowInFinallyContext *ctx = get_cf_finally_top(state);
|
||||
if (ctx->in_finally && ! ctx->in_loop) {
|
||||
if (!control_flow_in_finally_warning(kw, node_, state)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define PUSH_CONTEXT(S, N, FINALLY, FUNCDEF, LOOP) \
|
||||
if (!push_cf_context((S), (N), (FINALLY), (FUNCDEF), (LOOP))) { \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
#define POP_CONTEXT(S) pop_cf_context(S)
|
||||
|
||||
#define BEFORE_FINALLY(S, N) PUSH_CONTEXT((S), (N), true, false, false)
|
||||
#define AFTER_FINALLY(S) POP_CONTEXT(S)
|
||||
#define BEFORE_FUNC_BODY(S, N) PUSH_CONTEXT((S), (N), false, true, false)
|
||||
#define AFTER_FUNC_BODY(S) POP_CONTEXT(S)
|
||||
#define BEFORE_LOOP_BODY(S, N) PUSH_CONTEXT((S), (N), false, false, true)
|
||||
#define AFTER_LOOP_BODY(S) POP_CONTEXT(S)
|
||||
|
||||
#define BEFORE_RETURN(S, N) \
|
||||
if (!before_return((S), (N))) { \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
#define BEFORE_LOOP_EXIT(S, N, KW) \
|
||||
if (!before_loop_exit((S), (N), (KW))) { \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
static int
|
||||
make_const(expr_ty node, PyObject *val, PyArena *arena)
|
||||
{
|
||||
@@ -259,6 +368,9 @@ optimize_format(expr_ty node, PyObject *fmt, asdl_expr_seq *elts, PyArena *arena
|
||||
static int
|
||||
fold_binop(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
|
||||
{
|
||||
if (state->syntax_check_only) {
|
||||
return 1;
|
||||
}
|
||||
expr_ty lhs, rhs;
|
||||
lhs = node->v.BinOp.left;
|
||||
rhs = node->v.BinOp.right;
|
||||
@@ -304,6 +416,9 @@ make_const_tuple(asdl_expr_seq *elts)
|
||||
static int
|
||||
fold_tuple(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
|
||||
{
|
||||
if (state->syntax_check_only) {
|
||||
return 1;
|
||||
}
|
||||
PyObject *newval;
|
||||
|
||||
if (node->v.Tuple.ctx != Load)
|
||||
@@ -508,6 +623,9 @@ astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
CALL(fold_tuple, expr_ty, node_);
|
||||
break;
|
||||
case Name_kind:
|
||||
if (state->syntax_check_only) {
|
||||
break;
|
||||
}
|
||||
if (node_->v.Name.ctx == Load &&
|
||||
_PyUnicode_EqualToASCIIString(node_->v.Name.id, "__debug__")) {
|
||||
LEAVE_RECURSIVE();
|
||||
@@ -570,24 +688,30 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
{
|
||||
ENTER_RECURSIVE();
|
||||
switch (node_->kind) {
|
||||
case FunctionDef_kind:
|
||||
case FunctionDef_kind: {
|
||||
CALL_SEQ(astfold_type_param, type_param, node_->v.FunctionDef.type_params);
|
||||
CALL(astfold_arguments, arguments_ty, node_->v.FunctionDef.args);
|
||||
BEFORE_FUNC_BODY(state, node_);
|
||||
CALL(astfold_body, asdl_seq, node_->v.FunctionDef.body);
|
||||
AFTER_FUNC_BODY(state);
|
||||
CALL_SEQ(astfold_expr, expr, node_->v.FunctionDef.decorator_list);
|
||||
if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) {
|
||||
CALL_OPT(astfold_expr, expr_ty, node_->v.FunctionDef.returns);
|
||||
}
|
||||
break;
|
||||
case AsyncFunctionDef_kind:
|
||||
}
|
||||
case AsyncFunctionDef_kind: {
|
||||
CALL_SEQ(astfold_type_param, type_param, node_->v.AsyncFunctionDef.type_params);
|
||||
CALL(astfold_arguments, arguments_ty, node_->v.AsyncFunctionDef.args);
|
||||
BEFORE_FUNC_BODY(state, node_);
|
||||
CALL(astfold_body, asdl_seq, node_->v.AsyncFunctionDef.body);
|
||||
AFTER_FUNC_BODY(state);
|
||||
CALL_SEQ(astfold_expr, expr, node_->v.AsyncFunctionDef.decorator_list);
|
||||
if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) {
|
||||
CALL_OPT(astfold_expr, expr_ty, node_->v.AsyncFunctionDef.returns);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ClassDef_kind:
|
||||
CALL_SEQ(astfold_type_param, type_param, node_->v.ClassDef.type_params);
|
||||
CALL_SEQ(astfold_expr, expr, node_->v.ClassDef.bases);
|
||||
@@ -596,6 +720,7 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
CALL_SEQ(astfold_expr, expr, node_->v.ClassDef.decorator_list);
|
||||
break;
|
||||
case Return_kind:
|
||||
BEFORE_RETURN(state, node_);
|
||||
CALL_OPT(astfold_expr, expr_ty, node_->v.Return.value);
|
||||
break;
|
||||
case Delete_kind:
|
||||
@@ -621,23 +746,32 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
CALL_SEQ(astfold_type_param, type_param, node_->v.TypeAlias.type_params);
|
||||
CALL(astfold_expr, expr_ty, node_->v.TypeAlias.value);
|
||||
break;
|
||||
case For_kind:
|
||||
case For_kind: {
|
||||
CALL(astfold_expr, expr_ty, node_->v.For.target);
|
||||
CALL(astfold_expr, expr_ty, node_->v.For.iter);
|
||||
BEFORE_LOOP_BODY(state, node_);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.For.body);
|
||||
AFTER_LOOP_BODY(state);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.For.orelse);
|
||||
break;
|
||||
case AsyncFor_kind:
|
||||
}
|
||||
case AsyncFor_kind: {
|
||||
CALL(astfold_expr, expr_ty, node_->v.AsyncFor.target);
|
||||
CALL(astfold_expr, expr_ty, node_->v.AsyncFor.iter);
|
||||
BEFORE_LOOP_BODY(state, node_);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.AsyncFor.body);
|
||||
AFTER_LOOP_BODY(state);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.AsyncFor.orelse);
|
||||
break;
|
||||
case While_kind:
|
||||
}
|
||||
case While_kind: {
|
||||
CALL(astfold_expr, expr_ty, node_->v.While.test);
|
||||
BEFORE_LOOP_BODY(state, node_);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.While.body);
|
||||
AFTER_LOOP_BODY(state);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.While.orelse);
|
||||
break;
|
||||
}
|
||||
case If_kind:
|
||||
CALL(astfold_expr, expr_ty, node_->v.If.test);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.If.body);
|
||||
@@ -655,18 +789,24 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
CALL_OPT(astfold_expr, expr_ty, node_->v.Raise.exc);
|
||||
CALL_OPT(astfold_expr, expr_ty, node_->v.Raise.cause);
|
||||
break;
|
||||
case Try_kind:
|
||||
case Try_kind: {
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.Try.body);
|
||||
CALL_SEQ(astfold_excepthandler, excepthandler, node_->v.Try.handlers);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.Try.orelse);
|
||||
BEFORE_FINALLY(state, node_);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.Try.finalbody);
|
||||
AFTER_FINALLY(state);
|
||||
break;
|
||||
case TryStar_kind:
|
||||
}
|
||||
case TryStar_kind: {
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.TryStar.body);
|
||||
CALL_SEQ(astfold_excepthandler, excepthandler, node_->v.TryStar.handlers);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.TryStar.orelse);
|
||||
BEFORE_FINALLY(state, node_);
|
||||
CALL_SEQ(astfold_stmt, stmt, node_->v.TryStar.finalbody);
|
||||
AFTER_FINALLY(state);
|
||||
break;
|
||||
}
|
||||
case Assert_kind:
|
||||
CALL(astfold_expr, expr_ty, node_->v.Assert.test);
|
||||
CALL_OPT(astfold_expr, expr_ty, node_->v.Assert.msg);
|
||||
@@ -678,14 +818,18 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||
CALL(astfold_expr, expr_ty, node_->v.Match.subject);
|
||||
CALL_SEQ(astfold_match_case, match_case, node_->v.Match.cases);
|
||||
break;
|
||||
case Break_kind:
|
||||
BEFORE_LOOP_EXIT(state, node_, "break");
|
||||
break;
|
||||
case Continue_kind:
|
||||
BEFORE_LOOP_EXIT(state, node_, "continue");
|
||||
break;
|
||||
// The following statements don't contain any subexpressions to be folded
|
||||
case Import_kind:
|
||||
case ImportFrom_kind:
|
||||
case Global_kind:
|
||||
case Nonlocal_kind:
|
||||
case Pass_kind:
|
||||
case Break_kind:
|
||||
case Continue_kind:
|
||||
break;
|
||||
// No default case, so the compiler will emit a warning if new statement
|
||||
// kinds are added without being handled here
|
||||
@@ -828,14 +972,22 @@ astfold_type_param(type_param_ty node_, PyArena *ctx_, _PyASTOptimizeState *stat
|
||||
#undef CALL_SEQ
|
||||
|
||||
int
|
||||
_PyAST_Optimize(mod_ty mod, PyArena *arena, int optimize, int ff_features)
|
||||
_PyAST_Optimize(mod_ty mod, PyArena *arena, PyObject *filename, int optimize,
|
||||
int ff_features, int syntax_check_only)
|
||||
{
|
||||
_PyASTOptimizeState state;
|
||||
memset(&state, 0, sizeof(_PyASTOptimizeState));
|
||||
state.filename = filename;
|
||||
state.optimize = optimize;
|
||||
state.ff_features = ff_features;
|
||||
state.syntax_check_only = syntax_check_only;
|
||||
if (_Py_CArray_Init(&state.cf_finally, sizeof(ControlFlowInFinallyContext), 20) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = astfold_mod(mod, arena, &state);
|
||||
assert(ret || PyErr_Occurred());
|
||||
|
||||
_Py_CArray_Fini(&state.cf_finally);
|
||||
return ret;
|
||||
}
|
||||
|
||||
+22
-32
@@ -833,45 +833,35 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename,
|
||||
if (is_ast == -1)
|
||||
goto error;
|
||||
if (is_ast) {
|
||||
if ((flags & PyCF_OPTIMIZED_AST) == PyCF_ONLY_AST) {
|
||||
if (PyAst_CheckMode(source, compile_mode) < 0) {
|
||||
PyArena *arena = _PyArena_New();
|
||||
if (arena == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (flags & PyCF_ONLY_AST) {
|
||||
mod_ty mod = PyAST_obj2mod(source, arena, compile_mode);
|
||||
if (mod == NULL || !_PyAST_Validate(mod)) {
|
||||
_PyArena_Free(arena);
|
||||
goto error;
|
||||
}
|
||||
// return an un-optimized AST
|
||||
result = Py_NewRef(source);
|
||||
int syntax_check_only = ((flags & PyCF_OPTIMIZED_AST) == PyCF_ONLY_AST); /* unoptiomized AST */
|
||||
if (_PyCompile_AstOptimize(mod, filename, &cf, optimize,
|
||||
arena, syntax_check_only) < 0) {
|
||||
_PyArena_Free(arena);
|
||||
goto error;
|
||||
}
|
||||
result = PyAST_mod2obj(mod);
|
||||
}
|
||||
else {
|
||||
// Return an optimized AST or code object
|
||||
|
||||
PyArena *arena = _PyArena_New();
|
||||
if (arena == NULL) {
|
||||
mod_ty mod = PyAST_obj2mod(source, arena, compile_mode);
|
||||
if (mod == NULL || !_PyAST_Validate(mod)) {
|
||||
_PyArena_Free(arena);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (flags & PyCF_ONLY_AST) {
|
||||
mod_ty mod = PyAST_obj2mod(source, arena, compile_mode);
|
||||
if (mod == NULL || !_PyAST_Validate(mod)) {
|
||||
_PyArena_Free(arena);
|
||||
goto error;
|
||||
}
|
||||
if (_PyCompile_AstOptimize(mod, filename, &cf, optimize,
|
||||
arena) < 0) {
|
||||
_PyArena_Free(arena);
|
||||
goto error;
|
||||
}
|
||||
result = PyAST_mod2obj(mod);
|
||||
}
|
||||
else {
|
||||
mod_ty mod = PyAST_obj2mod(source, arena, compile_mode);
|
||||
if (mod == NULL || !_PyAST_Validate(mod)) {
|
||||
_PyArena_Free(arena);
|
||||
goto error;
|
||||
}
|
||||
result = (PyObject*)_PyAST_Compile(mod, filename,
|
||||
&cf, optimize, arena);
|
||||
}
|
||||
_PyArena_Free(arena);
|
||||
result = (PyObject*)_PyAST_Compile(mod, filename,
|
||||
&cf, optimize, arena);
|
||||
}
|
||||
_PyArena_Free(arena);
|
||||
goto finally;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -131,7 +131,7 @@ compiler_setup(compiler *c, mod_ty mod, PyObject *filename,
|
||||
c->c_optimize = (optimize == -1) ? _Py_GetConfig()->optimization_level : optimize;
|
||||
c->c_save_nested_seqs = false;
|
||||
|
||||
if (!_PyAST_Optimize(mod, arena, c->c_optimize, merged)) {
|
||||
if (!_PyAST_Optimize(mod, arena, filename, c->c_optimize, merged, 0)) {
|
||||
return ERROR;
|
||||
}
|
||||
c->c_st = _PySymtable_Build(mod, filename, &c->c_future);
|
||||
@@ -1392,7 +1392,7 @@ _PyAST_Compile(mod_ty mod, PyObject *filename, PyCompilerFlags *pflags,
|
||||
|
||||
int
|
||||
_PyCompile_AstOptimize(mod_ty mod, PyObject *filename, PyCompilerFlags *cf,
|
||||
int optimize, PyArena *arena)
|
||||
int optimize, PyArena *arena, int no_const_folding)
|
||||
{
|
||||
_PyFutureFeatures future;
|
||||
if (!_PyFuture_FromAST(mod, filename, &future)) {
|
||||
@@ -1402,7 +1402,7 @@ _PyCompile_AstOptimize(mod_ty mod, PyObject *filename, PyCompilerFlags *cf,
|
||||
if (optimize == -1) {
|
||||
optimize = _Py_GetConfig()->optimization_level;
|
||||
}
|
||||
if (!_PyAST_Optimize(mod, arena, optimize, flags)) {
|
||||
if (!_PyAST_Optimize(mod, arena, filename, optimize, flags, no_const_folding)) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
+4
-5
@@ -1495,11 +1495,10 @@ Py_CompileStringObject(const char *str, PyObject *filename, int start,
|
||||
return NULL;
|
||||
}
|
||||
if (flags && (flags->cf_flags & PyCF_ONLY_AST)) {
|
||||
if ((flags->cf_flags & PyCF_OPTIMIZED_AST) == PyCF_OPTIMIZED_AST) {
|
||||
if (_PyCompile_AstOptimize(mod, filename, flags, optimize, arena) < 0) {
|
||||
_PyArena_Free(arena);
|
||||
return NULL;
|
||||
}
|
||||
int syntax_check_only = ((flags->cf_flags & PyCF_OPTIMIZED_AST) == PyCF_ONLY_AST); /* unoptiomized AST */
|
||||
if (_PyCompile_AstOptimize(mod, filename, flags, optimize, arena, syntax_check_only) < 0) {
|
||||
_PyArena_Free(arena);
|
||||
return NULL;
|
||||
}
|
||||
PyObject *result = PyAST_mod2obj(mod);
|
||||
_PyArena_Free(arena);
|
||||
|
||||
Reference in New Issue
Block a user