diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 71cc0c83de567e..949cdf3be8b7e3 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -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: diff --git a/Doc/tutorial/errors.rst b/Doc/tutorial/errors.rst index bfb281c1b7d66a..1c20fa2f0b6ae5 100644 --- a/Doc/tutorial/errors.rst +++ b/Doc/tutorial/errors.rst @@ -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:: diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index b1337190636529..789156974cb0d1 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -68,6 +68,7 @@ Summary -- release highlights * :ref:`PEP 741: Python Configuration C API ` * :ref:`PEP 761: Discontinuation of PGP signatures ` * :ref:`A new type of interpreter ` +* :ref:`PEP 765: Disallow return/break/continue that exit a finally block ` 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 =========== diff --git a/Include/internal/pycore_compile.h b/Include/internal/pycore_compile.h index 5b08bb6f8a7135..467374c705486e 100644 --- a/Include/internal/pycore_compile.h +++ b/Include/internal/pycore_compile.h @@ -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 { diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py index e405056c8ffcb5..f35b1194308262 100644 --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -37,6 +37,7 @@ def check_all(self, modname): (".* (module|package)", DeprecationWarning), (".* (module|package)", PendingDeprecationWarning), ("", ResourceWarning), + ("", SyntaxWarning), quiet=True): try: exec("import %s" % modname, names) @@ -52,6 +53,7 @@ def check_all(self, modname): with warnings_helper.check_warnings( ("", DeprecationWarning), ("", ResourceWarning), + ("", SyntaxWarning), quiet=True): try: exec("from %s import *" % modname, names) diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py index 6e1458facafc30..e63ddb7d1fecc4 100644 --- a/Lib/test/test_ast/test_ast.py +++ b/Lib/test/test_ast/test_ast.py @@ -820,6 +820,61 @@ def test_repr_large_input_crash(self): 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.""" diff --git a/Lib/test/test_except_star.py b/Lib/test/test_except_star.py index 284907f61213f8..47006c6d3a0c36 100644 --- a/Lib/test/test_except_star.py +++ b/Lib/test/test_except_star.py @@ -84,7 +84,8 @@ def test_break_in_except_star(self): if i == 2: break finally: - return 0 + pass + return 0 """) @@ -117,7 +118,8 @@ def test_continue_in_except_star_block_invalid(self): if i == 2: continue finally: - return 0 + pass + return 0 """) def test_return_in_except_star_block_invalid(self): diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 707d4fc6df16ea..d2950cf48abb21 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -858,7 +858,7 @@ 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 @@ >>> 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 @@ def f(x: *b) from test import support -class SyntaxTestCase(unittest.TestCase): +class SyntaxWarningTest(unittest.TestCase): + def check_warning(self, code, errtext, filename="", 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="", mode="exec", subclass=None, @@ -2401,7 +2494,7 @@ def _check_error(self, code, errtext, """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: diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py index f45a651c7ccb5d..9efea1e037f447 100644 --- a/Lib/test/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -422,9 +422,11 @@ def test_docstrings(self): 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): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-13-19-07-54.gh-issue-130080.IoJpuy.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-13-19-07-54.gh-issue-130080.IoJpuy.rst new file mode 100644 index 00000000000000..7c9f30a9f973f6 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-13-19-07-54.gh-issue-130080.IoJpuy.rst @@ -0,0 +1 @@ +Implement PEP 765: Disallow return/break/continue that exit a finally block. diff --git a/Python/ast_opt.c b/Python/ast_opt.c index e261f04361be5f..4a191e919e412c 100644 --- a/Python/ast_opt.c +++ b/Python/ast_opt.c @@ -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; } diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 9f14d1745575d6..3174105fbe32e2 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -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) { - goto error; - } - // return an un-optimized AST - result = Py_NewRef(source); + PyArena *arena = _PyArena_New(); + if (arena == NULL) { + goto error; } - else { - // Return an optimized AST or code object - PyArena *arena = _PyArena_New(); - if (arena == NULL) { + 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 (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); + 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; } - 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); + 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; } - _PyArena_Free(arena); + result = (PyObject*)_PyAST_Compile(mod, filename, + &cf, optimize, arena); } + _PyArena_Free(arena); goto finally; } diff --git a/Python/compile.c b/Python/compile.c index f5e2973436b65b..303d959c9d790c 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -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; diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 41f2174b2d6037..6e24131e5b1113 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -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); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy