Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Include/internal/pycore_symtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ typedef struct _symtable_entry {
unsigned ste_method : 1; /* true if block is a function block defined in class scope */
unsigned ste_has_conditional_annotations : 1; /* true if block has conditionally executed annotations */
unsigned ste_in_conditional_block : 1; /* set while we are inside a conditionally executed block */
unsigned ste_in_unevaluated_annotation : 1; /* set while we are processing an annotation that will not be evaluated */
int ste_comp_iter_expr; /* non-zero if visiting a comprehension range expression */
_Py_SourceLocation ste_loc; /* source location of block */
struct _symtable_entry *ste_annotation_block; /* symbol table entry for this entry's annotations */
Expand Down
13 changes: 12 additions & 1 deletion Lib/test/test_type_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import textwrap
import types
import unittest
from test.support import run_code, check_syntax_error
from test.support import run_code, check_syntax_error, cpython_only


class TypeAnnotationTests(unittest.TestCase):
Expand Down Expand Up @@ -109,6 +109,16 @@ class D(metaclass=C):
del D.__annotations__
self.assertEqual(D.__annotations__, {})

@cpython_only
def test_no_cell(self):
# gh-130924: Test that uses of annotations in local scopes do not
# create cell variables.
def f(x):
a: x
return x

self.assertEqual(f.__code__.co_cellvars, ())


def build_module(code: str, name: str = "top") -> types.ModuleType:
ns = run_code(code)
Expand Down Expand Up @@ -352,6 +362,7 @@ def test_no_exotic_expressions_in_unevaluated_annotations(self):
check_syntax_error(self, prelude + "(x): (yield)", "yield expression cannot be used within an annotation")
check_syntax_error(self, prelude + "(x): (yield from x)", "yield expression cannot be used within an annotation")
check_syntax_error(self, prelude + "(x): (y := 3)", "named expression cannot be used within an annotation")
check_syntax_error(self, prelude + "(x): (__debug__ := 3)", "named expression cannot be used within an annotation")
check_syntax_error(self, prelude + "(x): (await 42)", "await expression cannot be used within an annotation")

def test_ignore_non_simple_annotations(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Usage of a name in a function-scope annotation no longer triggers creation
of a cell for that variable. This fixes a regression in earlier alphas of
Python 3.14.
36 changes: 24 additions & 12 deletions Python/symtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block,
ste->ste_needs_classdict = 0;
ste->ste_has_conditional_annotations = 0;
ste->ste_in_conditional_block = 0;
ste->ste_in_unevaluated_annotation = 0;
ste->ste_annotation_block = NULL;

ste->ste_has_docstring = 0;
Expand Down Expand Up @@ -2538,17 +2539,19 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
VISIT(st, expr, e->v.Slice.step);
break;
case Name_kind:
if (!symtable_add_def_ctx(st, e->v.Name.id,
e->v.Name.ctx == Load ? USE : DEF_LOCAL,
LOCATION(e), e->v.Name.ctx)) {
return 0;
}
/* Special-case super: it counts as a use of __class__ */
if (e->v.Name.ctx == Load &&
_PyST_IsFunctionLike(st->st_cur) &&
_PyUnicode_EqualToASCIIString(e->v.Name.id, "super")) {
if (!symtable_add_def(st, &_Py_ID(__class__), USE, LOCATION(e)))
if (!st->st_cur->ste_in_unevaluated_annotation) {
if (!symtable_add_def_ctx(st, e->v.Name.id,
e->v.Name.ctx == Load ? USE : DEF_LOCAL,
LOCATION(e), e->v.Name.ctx)) {
return 0;
}
/* Special-case super: it counts as a use of __class__ */
if (e->v.Name.ctx == Load &&
_PyST_IsFunctionLike(st->st_cur) &&
_PyUnicode_EqualToASCIIString(e->v.Name.id, "super")) {
if (!symtable_add_def(st, &_Py_ID(__class__), USE, LOCATION(e)))
return 0;
}
}
break;
/* child nodes of List and Tuple will have expr_context set */
Expand Down Expand Up @@ -2733,6 +2736,9 @@ symtable_visit_params(struct symtable *st, asdl_arg_seq *args)
static int
symtable_visit_annotation(struct symtable *st, expr_ty annotation, void *key)
{
// Annotations in local scopes are not executed and should not affect the symtable
bool is_unevaluated = st->st_cur->ste_type == FunctionBlock;

if ((st->st_cur->ste_type == ClassBlock || st->st_cur->ste_type == ModuleBlock)
&& st->st_cur->ste_in_conditional_block
&& !st->st_cur->ste_has_conditional_annotations)
Expand Down Expand Up @@ -2764,11 +2770,17 @@ symtable_visit_annotation(struct symtable *st, expr_ty annotation, void *key)
return 0;
}
}
VISIT(st, expr, annotation);
if (is_unevaluated) {
st->st_cur->ste_in_unevaluated_annotation = 1;
}
int rc = symtable_visit_expr(st, annotation);
if (is_unevaluated) {
st->st_cur->ste_in_unevaluated_annotation = 0;
}
if (!symtable_exit_block(st)) {
return 0;
}
return 1;
return rc;
}

static int
Expand Down
Loading