Skip to content

Commit 9b8611e

Browse files
gh-119180: PEP 649 compiler changes (#119361)
1 parent 02c1dff commit 9b8611e

28 files changed

+609
-328
lines changed

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ struct _Py_global_strings {
4545
STRUCT_FOR_STR(dot, ".")
4646
STRUCT_FOR_STR(dot_locals, ".<locals>")
4747
STRUCT_FOR_STR(empty, "")
48+
STRUCT_FOR_STR(format, ".format")
4849
STRUCT_FOR_STR(generic_base, ".generic_base")
4950
STRUCT_FOR_STR(json_decoder, "json.decoder")
5051
STRUCT_FOR_STR(kwdefaults, ".kwdefaults")
@@ -234,7 +235,6 @@ struct _Py_global_strings {
234235
STRUCT_FOR_ID(_abstract_)
235236
STRUCT_FOR_ID(_active)
236237
STRUCT_FOR_ID(_align_)
237-
STRUCT_FOR_ID(_annotation)
238238
STRUCT_FOR_ID(_anonymous_)
239239
STRUCT_FOR_ID(_argtypes_)
240240
STRUCT_FOR_ID(_as_parameter_)

Include/internal/pycore_opcode_utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ extern "C" {
5757
#define MAKE_FUNCTION_KWDEFAULTS 0x02
5858
#define MAKE_FUNCTION_ANNOTATIONS 0x04
5959
#define MAKE_FUNCTION_CLOSURE 0x08
60+
#define MAKE_FUNCTION_ANNOTATE 0x10
6061

6162
/* Values used as the oparg for LOAD_COMMON_CONSTANT */
6263
#define CONSTANT_ASSERTIONERROR 0

Include/internal/pycore_runtime_init_generated.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_symtable.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ struct _mod; // Type defined in pycore_ast.h
1212

1313
typedef enum _block_type {
1414
FunctionBlock, ClassBlock, ModuleBlock,
15-
// Used for annotations if 'from __future__ import annotations' is active.
16-
// Annotation blocks cannot bind names and are not evaluated.
15+
// Used for annotations. If 'from __future__ import annotations' is active,
16+
// annotation blocks cannot bind names and are not evaluated. Otherwise, they
17+
// are lazily evaluated (see PEP 649).
1718
AnnotationBlock,
1819
// Used for generics and type aliases. These work mostly like functions
1920
// (see PEP 695 for details). The three different blocks function identically;
@@ -89,6 +90,7 @@ typedef struct _symtable_entry {
8990
including free refs to globals */
9091
unsigned ste_generator : 1; /* true if namespace is a generator */
9192
unsigned ste_coroutine : 1; /* true if namespace is a coroutine */
93+
unsigned ste_annotations_used : 1; /* true if there are any annotations in this scope */
9294
_Py_comprehension_ty ste_comprehension; /* Kind of comprehension (if any) */
9395
unsigned ste_varargs : 1; /* true if block has varargs */
9496
unsigned ste_varkeywords : 1; /* true if block has varkeywords */
@@ -110,6 +112,7 @@ typedef struct _symtable_entry {
110112
int ste_end_col_offset; /* end offset of first line of block */
111113
int ste_opt_lineno; /* lineno of last exec or import * */
112114
int ste_opt_col_offset; /* offset of last exec or import * */
115+
struct _symtable_entry *ste_annotation_block; /* symbol table entry for this entry's annotations */
113116
struct symtable *ste_table;
114117
} PySTEntryObject;
115118

@@ -126,6 +129,7 @@ extern struct symtable* _PySymtable_Build(
126129
PyObject *filename,
127130
_PyFutureFeatures *future);
128131
extern PySTEntryObject* _PySymtable_Lookup(struct symtable *, void *);
132+
extern int _PySymtable_LookupOptional(struct symtable *, void *, PySTEntryObject **);
129133

130134
extern void _PySymtable_Free(struct symtable *);
131135

Include/internal/pycore_unicodeobject_generated.h

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/inspect.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -220,13 +220,7 @@ def get_annotations(obj, *, globals=None, locals=None, eval_str=False):
220220
"""
221221
if isinstance(obj, type):
222222
# class
223-
obj_dict = getattr(obj, '__dict__', None)
224-
if obj_dict and hasattr(obj_dict, 'get'):
225-
ann = obj_dict.get('__annotations__', None)
226-
if isinstance(ann, types.GetSetDescriptorType):
227-
ann = None
228-
else:
229-
ann = None
223+
ann = obj.__annotations__
230224

231225
obj_globals = None
232226
module_name = getattr(obj, '__module__', None)

Lib/symtable.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ def get_methods(self):
222222
if self.__methods is None:
223223
d = {}
224224
for st in self._table.children:
225+
if st.type == _symtable.TYPE_ANNOTATION:
226+
continue
225227
d[st.name] = 1
226228
self.__methods = tuple(d)
227229
return self.__methods

Lib/test/test_dis.py

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -352,32 +352,21 @@ def wrap_func_w_kwargs():
352352
dis_annot_stmt_str = """\
353353
0 RESUME 0
354354
355-
2 SETUP_ANNOTATIONS
356-
LOAD_CONST 0 (1)
355+
2 LOAD_CONST 0 (1)
357356
STORE_NAME 0 (x)
358-
LOAD_NAME 1 (int)
359-
LOAD_NAME 2 (__annotations__)
360-
LOAD_CONST 1 ('x')
361-
STORE_SUBSCR
362-
363-
3 LOAD_NAME 3 (fun)
364-
PUSH_NULL
365-
LOAD_CONST 0 (1)
366-
CALL 1
367-
LOAD_NAME 2 (__annotations__)
368-
LOAD_CONST 2 ('y')
369-
STORE_SUBSCR
370357
371358
4 LOAD_CONST 0 (1)
372-
LOAD_NAME 4 (lst)
373-
LOAD_NAME 3 (fun)
359+
LOAD_NAME 1 (lst)
360+
LOAD_NAME 2 (fun)
374361
PUSH_NULL
375-
LOAD_CONST 3 (0)
362+
LOAD_CONST 1 (0)
376363
CALL 1
377364
STORE_SUBSCR
378-
LOAD_NAME 1 (int)
379-
POP_TOP
380-
RETURN_CONST 4 (None)
365+
366+
2 LOAD_CONST 2 (<code object __annotate__ at 0x..., file "<dis>", line 2>)
367+
MAKE_FUNCTION
368+
STORE_NAME 3 (__annotate__)
369+
RETURN_CONST 3 (None)
381370
"""
382371

383372
compound_stmt_str = """\

Lib/test/test_grammar.py

Lines changed: 5 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -306,16 +306,6 @@ def test_eof_error(self):
306306

307307
var_annot_global: int # a global annotated is necessary for test_var_annot
308308

309-
# custom namespace for testing __annotations__
310-
311-
class CNS:
312-
def __init__(self):
313-
self._dct = {}
314-
def __setitem__(self, item, value):
315-
self._dct[item.lower()] = value
316-
def __getitem__(self, item):
317-
return self._dct[item]
318-
319309

320310
class GrammarTests(unittest.TestCase):
321311

@@ -446,22 +436,12 @@ class F(C, A):
446436
self.assertEqual(E.__annotations__, {})
447437
self.assertEqual(F.__annotations__, {})
448438

449-
450-
def test_var_annot_metaclass_semantics(self):
451-
class CMeta(type):
452-
@classmethod
453-
def __prepare__(metacls, name, bases, **kwds):
454-
return {'__annotations__': CNS()}
455-
class CC(metaclass=CMeta):
456-
XX: 'ANNOT'
457-
self.assertEqual(CC.__annotations__['xx'], 'ANNOT')
458-
459439
def test_var_annot_module_semantics(self):
460440
self.assertEqual(test.__annotations__, {})
461441
self.assertEqual(ann_module.__annotations__,
462-
{1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int], 'u': int | float})
442+
{'x': int, 'y': str, 'f': typing.Tuple[int, int], 'u': int | float})
463443
self.assertEqual(ann_module.M.__annotations__,
464-
{'123': 123, 'o': type})
444+
{'o': type})
465445
self.assertEqual(ann_module2.__annotations__, {})
466446

467447
def test_var_annot_in_module(self):
@@ -476,51 +456,12 @@ def test_var_annot_in_module(self):
476456
ann_module3.D_bad_ann(5)
477457

478458
def test_var_annot_simple_exec(self):
479-
gns = {}; lns= {}
459+
gns = {}; lns = {}
480460
exec("'docstring'\n"
481-
"__annotations__[1] = 2\n"
482461
"x: int = 5\n", gns, lns)
483-
self.assertEqual(lns["__annotations__"], {1: 2, 'x': int})
484-
with self.assertRaises(KeyError):
485-
gns['__annotations__']
486-
487-
def test_var_annot_custom_maps(self):
488-
# tests with custom locals() and __annotations__
489-
ns = {'__annotations__': CNS()}
490-
exec('X: int; Z: str = "Z"; (w): complex = 1j', ns)
491-
self.assertEqual(ns['__annotations__']['x'], int)
492-
self.assertEqual(ns['__annotations__']['z'], str)
462+
self.assertEqual(lns["__annotate__"](1), {'x': int})
493463
with self.assertRaises(KeyError):
494-
ns['__annotations__']['w']
495-
nonloc_ns = {}
496-
class CNS2:
497-
def __init__(self):
498-
self._dct = {}
499-
def __setitem__(self, item, value):
500-
nonlocal nonloc_ns
501-
self._dct[item] = value
502-
nonloc_ns[item] = value
503-
def __getitem__(self, item):
504-
return self._dct[item]
505-
exec('x: int = 1', {}, CNS2())
506-
self.assertEqual(nonloc_ns['__annotations__']['x'], int)
507-
508-
def test_var_annot_refleak(self):
509-
# complex case: custom locals plus custom __annotations__
510-
# this was causing refleak
511-
cns = CNS()
512-
nonloc_ns = {'__annotations__': cns}
513-
class CNS2:
514-
def __init__(self):
515-
self._dct = {'__annotations__': cns}
516-
def __setitem__(self, item, value):
517-
nonlocal nonloc_ns
518-
self._dct[item] = value
519-
nonloc_ns[item] = value
520-
def __getitem__(self, item):
521-
return self._dct[item]
522-
exec('X: str', {}, CNS2())
523-
self.assertEqual(nonloc_ns['__annotations__']['x'], str)
464+
gns['__annotate__']
524465

525466
def test_var_annot_rhs(self):
526467
ns = {}

0 commit comments

Comments
 (0)