8000 bpo-39220: Do not optimise annotation if 'from __future__ import anno… · python/cpython@d112c60 · GitHub
[go: up one dir, main page]

Skip to content

Commit d112c60

Browse files
authored
bpo-39220: Do not optimise annotation if 'from __future__ import annotations' is used (GH-17866)
Do not apply AST-based optimizations if 'from __future__ import annotations' is used in order to prevent information lost in the final version of the annotations.
1 parent 8849e59 commit d112c60

File tree

5 files changed

+67
-40
lines changed

5 files changed

+67
-40
lines changed

Include/compile.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,12 @@ PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name);
8383
PyAPI_FUNC(int) PyCompile_OpcodeStackEffect(int opcode, int oparg);
8484
PyAPI_FUNC(int) PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int jump);
8585

86-
PyAPI_FUNC(int) _PyAST_Optimize(struct _mod *, PyArena *arena, int optimize);
86+
typedef struct {
87+
int optimize;
88+
int ff_features;
89+
} _PyASTOptimizeState;
90+
91+
PyAPI_FUNC(int) _PyAST_Optimize(struct _mod *, PyArena *arena, _PyASTOptimizeState *state);
8792

8893
#ifdef __cplusplus
8994
}

Lib/test/test_future.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ def f() -> {ann}:
111111
...
112112
def g(arg: {ann}) -> None:
113113
...
114+
async def f2() -> {ann}:
115+
...
116+
async def g2(arg: {ann}) -> None:
117+
...
114118
var: {ann}
115119
var2: {ann} = None
116120
"""
@@ -121,9 +125,13 @@ def getActual(self, annotation):
121125
exec(self.template.format(ann=annotation), {}, scope)
122126
func_ret_ann = scope['f'].__annotations__['return']
123127
func_arg_ann = scope['g'].__annotations__['arg']
128+
async_func_ret_ann = scope['f2'].__annotations__['return']
129+
async_func_arg_ann = scope['g2'].__annotations__['arg']
124130
var_ann1 = scope['__annotations__']['var']
125131
var_ann2 = scope['__annotations__']['var2']
126132
self.assertEqual(func_ret_ann, func_arg_ann)
133+
self.assertEqual(func_ret_ann, async_func_ret_ann)
134+
self.assertEqual(func_ret_ann, async_func_arg_ann)
127135
self.assertEqual(func_ret_ann, var_ann1)
128136
self.assertEqual(func_ret_ann, var_ann2)
129137
return func_ret_ann
@@ -288,6 +296,7 @@ def test_annotations(self):
288296
eq('(((a, b)))', '(a, b)')
289297
eq("(x:=10)")
290298
eq("f'{(x:=10):=10}'")
299+
eq("1 + 2 + 3")
291300

292301
def test_fstring_debug_annotations(self):
293302
# f-strings with '=' don't round trip very well, so set the expected
@@ -299,6 +308,5 @@ def test_fstring_debug_annotations(self):
299308
self.assertAnnotationEqual("f'{x=!a}'", expected="f'x={x!a}'")
300309
self.assertAnnotationEqual("f'{x=!s:*^20}'", expected="f'x={x!s:*^20}'")
301310

302-
303311
if __name__ == "__main__":
304312
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Do not optimize annotations if 'from __future__ import annotations' is used.
2+
Patch by Pablo Galindo.

Python/ast_opt.c

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ unary_not(PyObject *v)
3535
}
3636

3737
static int
38-
fold_unaryop(expr_ty node, PyArena *arena, int optimize)
38+
fold_unaryop(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
3939
{
4040
expr_ty arg = node->v.UnaryOp.operand;
4141

@@ -212,7 +212,7 @@ safe_mod(PyObject *v, PyObject *w)
212212
}
213213

214214
static int
215-
fold_binop(expr_ty node, PyArena *arena, int optimize)
215+
fold_binop(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
216216
{
217217
expr_ty lhs, rhs;
218218
lhs = node->v.BinOp.left;
@@ -294,7 +294,7 @@ make_const_tuple(asdl_seq *elts)
294294
}
295295

296296
static int
297-
fold_tuple(expr_ty node, PyArena *arena, int optimize)
297+
fold_tuple(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
298298
{
299299
PyObject *newval;
300300

@@ -306,7 +306,7 @@ fold_tuple(expr_ty node, PyArena *arena, int optimize)
306306
}
307307

308308
static int
309-
fold_subscr(expr_ty node, PyArena *arena, int optimize)
309+
fold_subscr(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
310310
{
311311
PyObject *newval;
312312
expr_ty arg, idx;
@@ -331,7 +331,7 @@ fold_subscr(expr_ty node, PyArena *arena, int optimize)
331331
in "for" loop and comprehensions.
332332
*/
333333
static int
334-
fold_iter(expr_ty arg, PyArena *arena, int optimize)
334+
fold_iter(expr_ty arg, PyArena *arena, _PyASTOptimizeState *state)
335335
{
336336
PyObject *newval;
337337
if (arg->kind == List_kind) {
@@ -364,7 +364,7 @@ fold_iter(expr_ty arg, PyArena *arena, int optimize)
364364
}
365365

366366
static int
367-
fold_compare(expr_ty node, PyArena *arena, int optimize)
367+
fold_compare(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
368368
{
369369
asdl_int_seq *ops;
370370
asdl_seq *args;
@@ -378,36 +378,36 @@ fold_compare(expr_ty node, PyArena *arena, int optimize)
378378
i = asdl_seq_LEN(ops) - 1;
379379
int op = asdl_seq_GET(ops, i);
380380
if (op == In || op == NotIn) {
381-
if (!fold_iter((expr_ty)asdl_seq_GET(args, i), arena, optimize)) {
381+
if (!fold_iter((expr_ty)asdl_seq_GET(args, i), arena, state)) {
382382
return 0;
383383
}
384384
}
385385
return 1;
386386
}
387387

388-
static int astfold_mod(mod_ty node_, PyArena *ctx_, int optimize_);
389-
static int astfold_stmt(stmt_ty node_, PyArena *ctx_, int optimize_);
390-
static int astfold_expr(expr_ty node_, PyArena *ctx_, int optimize_);
391-
static int astfold_arguments(arguments_ty node_, PyArena *ctx_, int optimize_);
392-
static int astfold_comprehension(comprehension_ty node_, PyArena *ctx_, int optimize_);
393-
static int astfold_keyword(keyword_ty node_, PyArena *ctx_, int optimize_);
394-
static int astfold_arg(arg_ty node_, PyArena *ctx_, int optimize_);
395-
static int astfold_withitem(withitem_ty node_, PyArena *ctx_, int optimize_);
396-
static int astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, int optimize_);
388+
static int astfold_mod(mod_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
389+
static int astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
390+
static int astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
391+
static int astfold_arguments(arguments_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
392+
static int astfold_comprehension(comprehension_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
393+
static int astfold_keyword(keyword_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
394+
static int astfold_arg(arg_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
395+
static int astfold_withitem(withitem_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
396+
static int astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
397397
#define CALL(FUNC, TYPE, ARG) \
398-
if (!FUNC((ARG), ctx_, optimize_)) \
398+
if (!FUNC((ARG), ctx_, state)) \
399399
return 0;
400400

401401
#define CALL_OPT(FUNC, TYPE, ARG) \
402-
if ((ARG) != NULL && !FUNC((ARG), ctx_, optimize_)) \
402+
if ((ARG) != NULL && !FUNC((ARG), ctx_, state)) \
403403
return 0;
404404

405405
#define CALL_SEQ(FUNC, TYPE, ARG) { \
406406
int i; \
407407
asdl_seq *seq = (ARG); /* avoid variable capture */ \
408408
for (i = 0; i < asdl_seq_LEN(seq); i++) { \
409409
TYPE elt = (TYPE)asdl_seq_GET(seq, i); \
410-
if (elt != NULL && !FUNC(elt, ctx_, optimize_)) \
410+
if (elt != NULL && !FUNC(elt, ctx_, state)) \
411411
return 0; \
412412
} \
413413
}
@@ -417,13 +417,13 @@ static int astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, int opti
417417
asdl_int_seq *seq = (ARG); /* avoid variable capture */ \
418418
for (i = 0; i < asdl_seq_LEN(seq); i++) { \
419419
TYPE elt = (TYPE)asdl_seq_GET(seq, i); \
420-
if (!FUNC(elt, ctx_, optimize_)) \
420+
if (!FUNC(elt, ctx_, state)) \
421421
return 0; \
422422
} \
423423
}
424424

425425
static int
426-
astfold_body(asdl_seq *stmts, PyArena *ctx_, int optimize_)
426+
astfold_body(asdl_seq *stmts, PyArena *ctx_, _PyASTOptimizeState *state)
427427
{
428428
int docstring = _PyAST_GetDocString(stmts) != NULL;
429429
CALL_SEQ(astfold_stmt, stmt_ty, stmts);
@@ -445,7 +445,7 @@ astfold_body(asdl_seq *stmts, PyArena *ctx_, int optimize_)
445445
}
446446

447447
static int
448-
astfold_mod(mod_ty node_, PyArena *ctx_, int optimize_)
448+
astfold_mod(mod_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
449449
{
450450
switch (node_->kind) {
451451
case Module_kind:
@@ -464,7 +464,7 @@ astfold_mod(mod_ty node_, PyArena *ctx_, int optimize_)
464464
}
465465

466466
static int
467-
astfold_expr(expr_ty node_, PyArena *ctx_, int optimize_)
467+
astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
468468
{
469469
switch (node_->kind) {
470470
case BoolOp_kind:
@@ -563,7 +563,7 @@ astfold_expr(expr_ty node_, PyArena *ctx_, int optimize_)
563563
break;
564564
case Name_kind:
565565
if (_PyUnicode_EqualToASCIIString(node_->v.Name.id, "__debug__")) {
566-
return make_const(node_, PyBool_FromLong(!optimize_), ctx_);
566+
return make_const(node_, PyBool_FromLong(!state->optimize), ctx_);
567567
}
568568
break;
569569
default:
@@ -573,14 +573,14 @@ astfold_expr(expr_ty node_, PyArena *ctx_, int optimize_)
573573
}
574574

575575
static int
576-
astfold_keyword(keyword_ty node_, PyArena *ctx_, int optimize_)
576+
astfold_keyword(keyword_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
577577
{
578578
CALL(astfold_expr, expr_ty, node_->value);
579579
return 1;
580580
}
581581

582582
static int
583-
astfold_comprehension(comprehension_ty node_, PyArena *ctx_, int optimize_)
583+
astfold_comprehension(comprehension_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
584584
{
585585
CALL(astfold_expr, expr_ty, node_->target);
586586
CALL(astfold_expr, expr_ty, node_->iter);
@@ -591,7 +591,7 @@ astfold_comprehension(comprehension_ty node_, PyArena *ctx_, int optimize_)
591591
}
592592

593593
static int
594-
astfold_arguments(arguments_ty node_, PyArena *ctx_, int optimize_)
594+
astfold_arguments(arguments_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
595595
{
596596
CALL_SEQ(astfold_arg, arg_ty, node_->posonlyargs);
597597
CALL_SEQ(astfold_arg, arg_ty, node_->args);
@@ -604,27 +604,33 @@ astfold_arguments(arguments_ty node_, PyArena *ctx_, int optimize_)
604604
}
605605

606606
static int
607-
astfold_arg(arg_ty node_, PyArena *ctx_, int optimize_)
607+
astfold_arg(arg_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
608608
{
609-
CALL_OPT(astfold_expr, expr_ty, node_->annotation);
609+
if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) {
610+
CALL_OPT(astfold_expr, expr_ty, node_->annotation);
611+
}
610612
return 1;
611613
}
612614

613615
static int
614-
astfold_stmt(stmt_ty node_, PyArena *ctx_, int optimize_)
616+
astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
615617
{
616618
switch (node_->kind) {
617619
case FunctionDef_kind:
618620
CALL(astfold_arguments, arguments_ty, node_->v.FunctionDef.args);
619621
CALL(astfold_body, asdl_seq, node_->v.FunctionDef.body);
620622
CALL_SEQ(astfold_expr, expr_ty, node_->v.FunctionDef.decorator_list);
621-
CALL_OPT(astfold_expr, expr_ty, node_->v.FunctionDef.returns);
623+
if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) {
624+
CALL_OPT(astfold_expr, expr_ty, node_->v.FunctionDef.returns);
625+
}
622626
break;
623627
case AsyncFunctionDef_kind:
624628
CALL(astfold_arguments, arguments_ty, node_->v.AsyncFunctionDef.args);
625629
CALL(astfold_body, asdl_seq, node_->v.AsyncFunctionDef.body);
626630
CALL_SEQ(astfold_expr, expr_ty, node_->v.AsyncFunctionDef.decorator_list);
627-
CALL_OPT(astfold_expr, expr_ty, node_->v.AsyncFunctionDef.returns);
631+
if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) {
632+
CALL_OPT(astfold_expr, expr_ty, node_->v.AsyncFunctionDef.returns);
633+
}
628634
break;
629635
case ClassDef_kind:
630636
CALL_SEQ(astfold_expr, expr_ty, node_->v.ClassDef.bases);
@@ -648,7 +654,9 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, int optimize_)
648654
break;
649655
case AnnAssign_kind:
650656
CALL(astfold_expr, expr_ty, node_->v.AnnAssign.target);
651-
CALL(astfold_expr, expr_ty, node_->v.AnnAssign.annotation);
657+
if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) {
658+
CALL(astfold_expr, expr_ty, node_->v.AnnAssign.annotation);
659+
}
652660
CALL_OPT(astfold_expr, expr_ty, node_->v.AnnAssign.value);
653661
break;
654662
case For_kind:
@@ -707,7 +715,7 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, int optimize_)
707715
}
708716

709717
static int
710-
astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, int optimize_)
718+
astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
711719
{
712720
switch (node_->kind) {
713721
case ExceptHandler_kind:
@@ -721,7 +729,7 @@ astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, int optimize_)
721729
}
722730

723731
static int
724-
astfold_withitem(withitem_ty node_, PyArena *ctx_, int optimize_)
732+
astfold_withitem(withitem_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
725733
{
726734
CALL(astfold_expr, expr_ty, node_->context_expr);
727735
CALL_OPT(astfold_expr, expr_ty, node_->optional_vars);
@@ -734,9 +742,9 @@ astfold_withitem(withitem_ty node_, PyArena *ctx_, int optimize_)
734742
#undef CALL_INT_SEQ
735743

736744
int
737-
_PyAST_Optimize(mod_ty mod, PyArena *arena, int optimize)
745+
_PyAST_Optimize(mod_ty mod, PyArena *arena, _PyASTOptimizeState *state)
738746
{
739-
int ret = astfold_mod(mod, arena, optimize);
747+
int ret = astfold_mod(mod, arena, state);
740748
assert(ret || PyErr_Occurred());
741749
return ret;
742750
}

Python/compile.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,11 @@ PyAST_CompileObject(mod_ty mod, PyObject *filename, PyCompilerFlags *flags,
354354
c.c_nestlevel = 0;
355355
c.c_do_not_emit_bytecode = 0;
356356

357-
if (!_PyAST_Optimize(mod, arena, c.c_optimize)) {
357+
_PyASTOptimizeState state;
358+
state.optimize = c.c_optimize;
359+
state.ff_features = merged;
360+
361+
if (!_PyAST_Optimize(mod, arena, &state)) {
358362
goto finally;
359363
}
360364

0 commit comments

Comments
 (0)
0