gh-130080: implement PEP 765 (#130087)

This commit is contained in:
Irit Katriel
2025-03-17 20:48:54 +00:00
committed by GitHub
parent 468a7aaeb4
commit ffc2f1dd1c
14 changed files with 426 additions and 109 deletions
+22 -18
View File
@@ -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:
+6 -2
View File
@@ -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::
+10
View File
@@ -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
===========
+5 -2
View File
@@ -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 {
+2
View File
@@ -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)
+55
View File
@@ -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."""
+4 -2
View File
@@ -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
View File
@@ -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:
+4 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);