Skip to content

Commit ebef3c5

Browse files
authored
[3.12] pythongh-116767: fix crash on 'async with' with many context managers (pythonGH-118348) (python#118477)
pythongh-116767: fix crash on 'async with' with many context managers (pythonGH-118348) Account for `add_stopiteration_handler` pushing a block for `async with`. To allow generator functions that previously almost hit the `CO_MAXBLOCKS` limit by nesting non-async blocks, the limit is increased by 1. This increase allows one more block in non-generator functions. (cherry picked from commit c1bf487)
1 parent f5406ef commit ebef3c5

File tree

4 files changed

+50
-7
lines changed

4 files changed

+50
-7
lines changed

Include/cpython/code.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ struct PyCodeObject _PyCode_DEF(1);
207207
*/
208208
#define PY_PARSER_REQUIRES_FUTURE_KEYWORD
209209

210-
#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */
210+
#define CO_MAXBLOCKS 21 /* Max static block nesting within a function */
211211

212212
PyAPI_DATA(PyTypeObject) PyCode_Type;
213213

Lib/test/test_syntax.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,13 +2269,40 @@ def bug():
22692269
code += "): yield a"
22702270
return code
22712271

2272-
CO_MAXBLOCKS = 20 # static nesting limit of the compiler
2272+
CO_MAXBLOCKS = 21 # static nesting limit of the compiler
2273+
MAX_MANAGERS = CO_MAXBLOCKS - 1 # One for the StopIteration block
22732274

2274-
for n in range(CO_MAXBLOCKS):
2275+
for n in range(MAX_MANAGERS):
22752276
with self.subTest(f"within range: {n=}"):
22762277
compile(get_code(n), "<string>", "exec")
22772278

2278-
for n in range(CO_MAXBLOCKS, CO_MAXBLOCKS + 5):
2279+
for n in range(MAX_MANAGERS, MAX_MANAGERS + 5):
2280+
with self.subTest(f"out of range: {n=}"):
2281+
self._check_error(get_code(n), "too many statically nested blocks")
2282+
2283+
@support.cpython_only
2284+
def test_async_with_statement_many_context_managers(self):
2285+
# See gh-116767
2286+
2287+
def get_code(n):
2288+
code = [ textwrap.dedent("""
2289+
async def bug():
2290+
async with (
2291+
a
2292+
""") ]
2293+
for i in range(n):
2294+
code.append(f" as a{i}, a\n")
2295+
code.append("): yield a")
2296+
return "".join(code)
2297+
2298+
CO_MAXBLOCKS = 21 # static nesting limit of the compiler
2299+
MAX_MANAGERS = CO_MAXBLOCKS - 1 # One for the StopIteration block
2300+
2301+
for n in range(MAX_MANAGERS):
2302+
with self.subTest(f"within range: {n=}"):
2303+
compile(get_code(n), "<string>", "exec")
2304+
2305+
for n in range(MAX_MANAGERS, MAX_MANAGERS + 5):
22792306
with self.subTest(f"out of range: {n=}"):
22802307
self._check_error(get_code(n), "too many statically nested blocks")
22812308

@@ -2407,7 +2434,8 @@ def test_syntax_error_on_deeply_nested_blocks(self):
24072434
while 20:
24082435
while 21:
24092436
while 22:
2410-
break
2437+
while 23:
2438+
break
24112439
"""
24122440
self._check_error(source, "too many statically nested blocks")
24132441

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix crash in compiler on 'async with' that has many context managers.

Python/compile.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ compiler IR.
129129

130130
enum fblocktype { WHILE_LOOP, FOR_LOOP, TRY_EXCEPT, FINALLY_TRY, FINALLY_END,
131131
WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE, EXCEPTION_HANDLER,
132-
EXCEPTION_GROUP_HANDLER, ASYNC_COMPREHENSION_GENERATOR };
132+
EXCEPTION_GROUP_HANDLER, ASYNC_COMPREHENSION_GENERATOR,
133+
STOP_ITERATION };
133134

134135
struct fblockinfo {
135136
enum fblocktype fb_type;
@@ -1546,6 +1547,7 @@ compiler_unwind_fblock(struct compiler *c, location *ploc,
15461547
case EXCEPTION_HANDLER:
15471548
case EXCEPTION_GROUP_HANDLER:
15481549
case ASYNC_COMPREHENSION_GENERATOR:
1550+
case STOP_ITERATION:
15491551
return SUCCESS;
15501552

15511553
case FOR_LOOP:
@@ -2235,14 +2237,26 @@ compiler_function_body(struct compiler *c, stmt_ty s, int is_async, Py_ssize_t f
22352237
c->u->u_metadata.u_argcount = asdl_seq_LEN(args->args);
22362238
c->u->u_metadata.u_posonlyargcount = asdl_seq_LEN(args->posonlyargs);
22372239
c->u->u_metadata.u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs);
2240+
2241+
NEW_JUMP_TARGET_LABEL(c, start);
2242+
USE_LABEL(c, start);
2243+
bool add_stopiteration_handler = c->u->u_ste->ste_coroutine || c->u->u_ste->ste_generator;
2244+
if (add_stopiteration_handler) {
2245+
/* wrap_in_stopiteration_handler will push a block, so we need to account for that */
2246+
RETURN_IF_ERROR(
2247+
compiler_push_fblock(c, NO_LOCATION, STOP_ITERATION,
2248+
start, NO_LABEL, NULL));
2249+
}
2250+
22382251
for (Py_ssize_t i = docstring ? 1 : 0; i < asdl_seq_LEN(body); i++) {
22392252
VISIT_IN_SCOPE(c, stmt, (stmt_ty)asdl_seq_GET(body, i));
22402253
}
2241-
if (c->u->u_ste->ste_coroutine || c->u->u_ste->ste_generator) {
2254+
if (add_stopiteration_handler) {
22422255
if (wrap_in_stopiteration_handler(c) < 0) {
22432256
compiler_exit_scope(c);
22442257
return ERROR;
22452258
}
2259+
compiler_pop_fblock(c, STOP_ITERATION, start);
22462260
}
22472261
PyCodeObject *co = optimize_and_assemble(c, 1);
22482262
compiler_exit_scope(c);

0 commit comments

Comments
 (0)