mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-130907: Treat all module-level annotations as conditional (#131550)
This commit is contained in:
parent
5bf0f3666e
commit
922049b613
21 changed files with 221 additions and 53 deletions
|
@ -133,6 +133,8 @@ int _PyCompile_EnterScope(struct _PyCompiler *c, identifier name, int scope_type
|
||||||
void _PyCompile_ExitScope(struct _PyCompiler *c);
|
void _PyCompile_ExitScope(struct _PyCompiler *c);
|
||||||
Py_ssize_t _PyCompile_AddConst(struct _PyCompiler *c, PyObject *o);
|
Py_ssize_t _PyCompile_AddConst(struct _PyCompiler *c, PyObject *o);
|
||||||
_PyInstructionSequence *_PyCompile_InstrSequence(struct _PyCompiler *c);
|
_PyInstructionSequence *_PyCompile_InstrSequence(struct _PyCompiler *c);
|
||||||
|
int _PyCompile_StartAnnotationSetup(struct _PyCompiler *c);
|
||||||
|
int _PyCompile_EndAnnotationSetup(struct _PyCompiler *c);
|
||||||
int _PyCompile_FutureFeatures(struct _PyCompiler *c);
|
int _PyCompile_FutureFeatures(struct _PyCompiler *c);
|
||||||
void _PyCompile_DeferredAnnotations(
|
void _PyCompile_DeferredAnnotations(
|
||||||
struct _PyCompiler *c, PyObject **deferred_annotations,
|
struct _PyCompiler *c, PyObject **deferred_annotations,
|
||||||
|
|
|
@ -45,6 +45,9 @@ typedef struct instruction_sequence {
|
||||||
|
|
||||||
/* PyList of instruction sequences of nested functions */
|
/* PyList of instruction sequences of nested functions */
|
||||||
PyObject *s_nested;
|
PyObject *s_nested;
|
||||||
|
|
||||||
|
/* Code for creating annotations, spliced into the main sequence later */
|
||||||
|
struct instruction_sequence *s_annotations_code;
|
||||||
} _PyInstructionSequence;
|
} _PyInstructionSequence;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -66,6 +69,8 @@ _PyJumpTargetLabel _PyInstructionSequence_NewLabel(_PyInstructionSequence *seq);
|
||||||
int _PyInstructionSequence_ApplyLabelMap(_PyInstructionSequence *seq);
|
int _PyInstructionSequence_ApplyLabelMap(_PyInstructionSequence *seq);
|
||||||
int _PyInstructionSequence_InsertInstruction(_PyInstructionSequence *seq, int pos,
|
int _PyInstructionSequence_InsertInstruction(_PyInstructionSequence *seq, int pos,
|
||||||
int opcode, int oparg, _Py_SourceLocation loc);
|
int opcode, int oparg, _Py_SourceLocation loc);
|
||||||
|
int _PyInstructionSequence_SetAnnotationsCode(_PyInstructionSequence *seq,
|
||||||
|
_PyInstructionSequence *annotations);
|
||||||
int _PyInstructionSequence_AddNested(_PyInstructionSequence *seq, _PyInstructionSequence *nested);
|
int _PyInstructionSequence_AddNested(_PyInstructionSequence *seq, _PyInstructionSequence *nested);
|
||||||
void PyInstructionSequence_Fini(_PyInstructionSequence *seq);
|
void PyInstructionSequence_Fini(_PyInstructionSequence *seq);
|
||||||
|
|
||||||
|
|
24
Include/internal/pycore_opcode_metadata.h
generated
24
Include/internal/pycore_opcode_metadata.h
generated
|
@ -20,6 +20,7 @@ extern "C" {
|
||||||
#define IS_PSEUDO_INSTR(OP) ( \
|
#define IS_PSEUDO_INSTR(OP) ( \
|
||||||
((OP) == LOAD_CLOSURE) || \
|
((OP) == LOAD_CLOSURE) || \
|
||||||
((OP) == STORE_FAST_MAYBE_NULL) || \
|
((OP) == STORE_FAST_MAYBE_NULL) || \
|
||||||
|
((OP) == ANNOTATIONS_PLACEHOLDER) || \
|
||||||
((OP) == JUMP) || \
|
((OP) == JUMP) || \
|
||||||
((OP) == JUMP_NO_INTERRUPT) || \
|
((OP) == JUMP_NO_INTERRUPT) || \
|
||||||
((OP) == JUMP_IF_FALSE) || \
|
((OP) == JUMP_IF_FALSE) || \
|
||||||
|
@ -35,6 +36,8 @@ extern int _PyOpcode_num_popped(int opcode, int oparg);
|
||||||
#ifdef NEED_OPCODE_METADATA
|
#ifdef NEED_OPCODE_METADATA
|
||||||
int _PyOpcode_num_popped(int opcode, int oparg) {
|
int _PyOpcode_num_popped(int opcode, int oparg) {
|
||||||
switch(opcode) {
|
switch(opcode) {
|
||||||
|
case ANNOTATIONS_PLACEHOLDER:
|
||||||
|
return 0;
|
||||||
case BINARY_OP:
|
case BINARY_OP:
|
||||||
return 2;
|
return 2;
|
||||||
case BINARY_OP_ADD_FLOAT:
|
case BINARY_OP_ADD_FLOAT:
|
||||||
|
@ -514,6 +517,8 @@ extern int _PyOpcode_num_pushed(int opcode, int oparg);
|
||||||
#ifdef NEED_OPCODE_METADATA
|
#ifdef NEED_OPCODE_METADATA
|
||||||
int _PyOpcode_num_pushed(int opcode, int oparg) {
|
int _PyOpcode_num_pushed(int opcode, int oparg) {
|
||||||
switch(opcode) {
|
switch(opcode) {
|
||||||
|
case ANNOTATIONS_PLACEHOLDER:
|
||||||
|
return 0;
|
||||||
case BINARY_OP:
|
case BINARY_OP:
|
||||||
return 1;
|
return 1;
|
||||||
case BINARY_OP_ADD_FLOAT:
|
case BINARY_OP_ADD_FLOAT:
|
||||||
|
@ -1004,7 +1009,7 @@ enum InstructionFormat {
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IS_VALID_OPCODE(OP) \
|
#define IS_VALID_OPCODE(OP) \
|
||||||
(((OP) >= 0) && ((OP) < 266) && \
|
(((OP) >= 0) && ((OP) < 267) && \
|
||||||
(_PyOpcode_opcode_metadata[(OP)].valid_entry))
|
(_PyOpcode_opcode_metadata[(OP)].valid_entry))
|
||||||
|
|
||||||
#define HAS_ARG_FLAG (1)
|
#define HAS_ARG_FLAG (1)
|
||||||
|
@ -1058,9 +1063,9 @@ struct opcode_metadata {
|
||||||
uint16_t flags;
|
uint16_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct opcode_metadata _PyOpcode_opcode_metadata[266];
|
extern const struct opcode_metadata _PyOpcode_opcode_metadata[267];
|
||||||
#ifdef NEED_OPCODE_METADATA
|
#ifdef NEED_OPCODE_METADATA
|
||||||
const struct opcode_metadata _PyOpcode_opcode_metadata[266] = {
|
const struct opcode_metadata _PyOpcode_opcode_metadata[267] = {
|
||||||
[BINARY_OP] = { true, INSTR_FMT_IBC0000, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
|
[BINARY_OP] = { true, INSTR_FMT_IBC0000, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
|
||||||
[BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG },
|
[BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG },
|
||||||
[BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
[BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
||||||
|
@ -1285,6 +1290,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = {
|
||||||
[UNPACK_SEQUENCE_TWO_TUPLE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG },
|
[UNPACK_SEQUENCE_TWO_TUPLE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG },
|
||||||
[WITH_EXCEPT_START] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
[WITH_EXCEPT_START] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
||||||
[YIELD_VALUE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG },
|
[YIELD_VALUE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG },
|
||||||
|
[ANNOTATIONS_PLACEHOLDER] = { true, -1, HAS_PURE_FLAG },
|
||||||
[JUMP] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
[JUMP] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
||||||
[JUMP_IF_FALSE] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
[JUMP_IF_FALSE] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
||||||
[JUMP_IF_TRUE] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
[JUMP_IF_TRUE] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
||||||
|
@ -1491,9 +1497,10 @@ _PyOpcode_macro_expansion[256] = {
|
||||||
};
|
};
|
||||||
#endif // NEED_OPCODE_METADATA
|
#endif // NEED_OPCODE_METADATA
|
||||||
|
|
||||||
extern const char *_PyOpcode_OpName[266];
|
extern const char *_PyOpcode_OpName[267];
|
||||||
#ifdef NEED_OPCODE_METADATA
|
#ifdef NEED_OPCODE_METADATA
|
||||||
const char *_PyOpcode_OpName[266] = {
|
const char *_PyOpcode_OpName[267] = {
|
||||||
|
[ANNOTATIONS_PLACEHOLDER] = "ANNOTATIONS_PLACEHOLDER",
|
||||||
[BINARY_OP] = "BINARY_OP",
|
[BINARY_OP] = "BINARY_OP",
|
||||||
[BINARY_OP_ADD_FLOAT] = "BINARY_OP_ADD_FLOAT",
|
[BINARY_OP_ADD_FLOAT] = "BINARY_OP_ADD_FLOAT",
|
||||||
[BINARY_OP_ADD_INT] = "BINARY_OP_ADD_INT",
|
[BINARY_OP_ADD_INT] = "BINARY_OP_ADD_INT",
|
||||||
|
@ -2025,11 +2032,12 @@ struct pseudo_targets {
|
||||||
uint8_t as_sequence;
|
uint8_t as_sequence;
|
||||||
uint8_t targets[4];
|
uint8_t targets[4];
|
||||||
};
|
};
|
||||||
extern const struct pseudo_targets _PyOpcode_PseudoTargets[10];
|
extern const struct pseudo_targets _PyOpcode_PseudoTargets[11];
|
||||||
#ifdef NEED_OPCODE_METADATA
|
#ifdef NEED_OPCODE_METADATA
|
||||||
const struct pseudo_targets _PyOpcode_PseudoTargets[10] = {
|
const struct pseudo_targets _PyOpcode_PseudoTargets[11] = {
|
||||||
[LOAD_CLOSURE-256] = { 0, { LOAD_FAST, 0, 0, 0 } },
|
[LOAD_CLOSURE-256] = { 0, { LOAD_FAST, 0, 0, 0 } },
|
||||||
[STORE_FAST_MAYBE_NULL-256] = { 0, { STORE_FAST, 0, 0, 0 } },
|
[STORE_FAST_MAYBE_NULL-256] = { 0, { STORE_FAST, 0, 0, 0 } },
|
||||||
|
[ANNOTATIONS_PLACEHOLDER-256] = { 0, { NOP, 0, 0, 0 } },
|
||||||
[JUMP-256] = { 0, { JUMP_FORWARD, JUMP_BACKWARD, 0, 0 } },
|
[JUMP-256] = { 0, { JUMP_FORWARD, JUMP_BACKWARD, 0, 0 } },
|
||||||
[JUMP_NO_INTERRUPT-256] = { 0, { JUMP_FORWARD, JUMP_BACKWARD_NO_INTERRUPT, 0, 0 } },
|
[JUMP_NO_INTERRUPT-256] = { 0, { JUMP_FORWARD, JUMP_BACKWARD_NO_INTERRUPT, 0, 0 } },
|
||||||
[JUMP_IF_FALSE-256] = { 1, { COPY, TO_BOOL, POP_JUMP_IF_FALSE, 0 } },
|
[JUMP_IF_FALSE-256] = { 1, { COPY, TO_BOOL, POP_JUMP_IF_FALSE, 0 } },
|
||||||
|
@ -2043,7 +2051,7 @@ const struct pseudo_targets _PyOpcode_PseudoTargets[10] = {
|
||||||
#endif // NEED_OPCODE_METADATA
|
#endif // NEED_OPCODE_METADATA
|
||||||
static inline bool
|
static inline bool
|
||||||
is_pseudo_target(int pseudo, int target) {
|
is_pseudo_target(int pseudo, int target) {
|
||||||
if (pseudo < 256 || pseudo >= 266) {
|
if (pseudo < 256 || pseudo >= 267) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (int i = 0; _PyOpcode_PseudoTargets[pseudo-256].targets[i]; i++) {
|
for (int i = 0; _PyOpcode_PseudoTargets[pseudo-256].targets[i]; i++) {
|
||||||
|
|
21
Include/opcode_ids.h
generated
21
Include/opcode_ids.h
generated
|
@ -234,16 +234,17 @@ extern "C" {
|
||||||
#define INSTRUMENTED_JUMP_BACKWARD 253
|
#define INSTRUMENTED_JUMP_BACKWARD 253
|
||||||
#define INSTRUMENTED_LINE 254
|
#define INSTRUMENTED_LINE 254
|
||||||
#define ENTER_EXECUTOR 255
|
#define ENTER_EXECUTOR 255
|
||||||
#define JUMP 256
|
#define ANNOTATIONS_PLACEHOLDER 256
|
||||||
#define JUMP_IF_FALSE 257
|
#define JUMP 257
|
||||||
#define JUMP_IF_TRUE 258
|
#define JUMP_IF_FALSE 258
|
||||||
#define JUMP_NO_INTERRUPT 259
|
#define JUMP_IF_TRUE 259
|
||||||
#define LOAD_CLOSURE 260
|
#define JUMP_NO_INTERRUPT 260
|
||||||
#define POP_BLOCK 261
|
#define LOAD_CLOSURE 261
|
||||||
#define SETUP_CLEANUP 262
|
#define POP_BLOCK 262
|
||||||
#define SETUP_FINALLY 263
|
#define SETUP_CLEANUP 263
|
||||||
#define SETUP_WITH 264
|
#define SETUP_FINALLY 264
|
||||||
#define STORE_FAST_MAYBE_NULL 265
|
#define SETUP_WITH 265
|
||||||
|
#define STORE_FAST_MAYBE_NULL 266
|
||||||
|
|
||||||
#define HAVE_ARGUMENT 42
|
#define HAVE_ARGUMENT 42
|
||||||
#define MIN_SPECIALIZED_OPCODE 129
|
#define MIN_SPECIALIZED_OPCODE 129
|
||||||
|
|
21
Lib/_opcode_metadata.py
generated
21
Lib/_opcode_metadata.py
generated
|
@ -350,16 +350,17 @@ opmap = {
|
||||||
'INSTRUMENTED_CALL_KW': 251,
|
'INSTRUMENTED_CALL_KW': 251,
|
||||||
'INSTRUMENTED_CALL_FUNCTION_EX': 252,
|
'INSTRUMENTED_CALL_FUNCTION_EX': 252,
|
||||||
'INSTRUMENTED_JUMP_BACKWARD': 253,
|
'INSTRUMENTED_JUMP_BACKWARD': 253,
|
||||||
'JUMP': 256,
|
'ANNOTATIONS_PLACEHOLDER': 256,
|
||||||
'JUMP_IF_FALSE': 257,
|
'JUMP': 257,
|
||||||
'JUMP_IF_TRUE': 258,
|
'JUMP_IF_FALSE': 258,
|
||||||
'JUMP_NO_INTERRUPT': 259,
|
'JUMP_IF_TRUE': 259,
|
||||||
'LOAD_CLOSURE': 260,
|
'JUMP_NO_INTERRUPT': 260,
|
||||||
'POP_BLOCK': 261,
|
'LOAD_CLOSURE': 261,
|
||||||
'SETUP_CLEANUP': 262,
|
'POP_BLOCK': 262,
|
||||||
'SETUP_FINALLY': 263,
|
'SETUP_CLEANUP': 263,
|
||||||
'SETUP_WITH': 264,
|
'SETUP_FINALLY': 264,
|
||||||
'STORE_FAST_MAYBE_NULL': 265,
|
'SETUP_WITH': 265,
|
||||||
|
'STORE_FAST_MAYBE_NULL': 266,
|
||||||
}
|
}
|
||||||
|
|
||||||
HAVE_ARGUMENT = 42
|
HAVE_ARGUMENT = 42
|
||||||
|
|
|
@ -26,6 +26,7 @@ class IsolatedCodeGenTests(CodegenTestCase):
|
||||||
false_lbl = self.Label()
|
false_lbl = self.Label()
|
||||||
expected = [
|
expected = [
|
||||||
('RESUME', 0, 0),
|
('RESUME', 0, 0),
|
||||||
|
('ANNOTATIONS_PLACEHOLDER', None),
|
||||||
('LOAD_CONST', 0, 1),
|
('LOAD_CONST', 0, 1),
|
||||||
('TO_BOOL', 0, 1),
|
('TO_BOOL', 0, 1),
|
||||||
('POP_JUMP_IF_FALSE', false_lbl := self.Label(), 1),
|
('POP_JUMP_IF_FALSE', false_lbl := self.Label(), 1),
|
||||||
|
@ -45,6 +46,7 @@ class IsolatedCodeGenTests(CodegenTestCase):
|
||||||
false_lbl = self.Label()
|
false_lbl = self.Label()
|
||||||
expected = [
|
expected = [
|
||||||
('RESUME', 0, 0),
|
('RESUME', 0, 0),
|
||||||
|
('ANNOTATIONS_PLACEHOLDER', None),
|
||||||
('LOAD_NAME', 0, 1),
|
('LOAD_NAME', 0, 1),
|
||||||
('GET_ITER', None, 1),
|
('GET_ITER', None, 1),
|
||||||
loop_lbl := self.Label(),
|
loop_lbl := self.Label(),
|
||||||
|
@ -73,6 +75,7 @@ class IsolatedCodeGenTests(CodegenTestCase):
|
||||||
expected = [
|
expected = [
|
||||||
# Function definition
|
# Function definition
|
||||||
('RESUME', 0),
|
('RESUME', 0),
|
||||||
|
('ANNOTATIONS_PLACEHOLDER', None),
|
||||||
('LOAD_CONST', 0),
|
('LOAD_CONST', 0),
|
||||||
('MAKE_FUNCTION', None),
|
('MAKE_FUNCTION', None),
|
||||||
('STORE_NAME', 0),
|
('STORE_NAME', 0),
|
||||||
|
@ -106,6 +109,7 @@ class IsolatedCodeGenTests(CodegenTestCase):
|
||||||
expected = [
|
expected = [
|
||||||
# Function definition
|
# Function definition
|
||||||
('RESUME', 0),
|
('RESUME', 0),
|
||||||
|
('ANNOTATIONS_PLACEHOLDER', None),
|
||||||
('LOAD_CONST', 0),
|
('LOAD_CONST', 0),
|
||||||
('MAKE_FUNCTION', None),
|
('MAKE_FUNCTION', None),
|
||||||
('STORE_NAME', 0),
|
('STORE_NAME', 0),
|
||||||
|
|
|
@ -381,24 +381,36 @@ lst[fun(0)]: int = 1
|
||||||
# leading newline is for a reason (tests lineno)
|
# leading newline is for a reason (tests lineno)
|
||||||
|
|
||||||
dis_annot_stmt_str = """\
|
dis_annot_stmt_str = """\
|
||||||
0 RESUME 0
|
-- MAKE_CELL 0 (__conditional_annotations__)
|
||||||
|
|
||||||
2 LOAD_SMALL_INT 1
|
0 RESUME 0
|
||||||
STORE_NAME 0 (x)
|
|
||||||
|
|
||||||
4 LOAD_SMALL_INT 1
|
2 LOAD_CONST 1 (<code object __annotate__ at 0x..., file "<dis>", line 2>)
|
||||||
LOAD_NAME 1 (lst)
|
MAKE_FUNCTION
|
||||||
LOAD_NAME 2 (fun)
|
STORE_NAME 4 (__annotate__)
|
||||||
PUSH_NULL
|
BUILD_SET 0
|
||||||
LOAD_SMALL_INT 0
|
STORE_NAME 0 (__conditional_annotations__)
|
||||||
CALL 1
|
LOAD_SMALL_INT 1
|
||||||
STORE_SUBSCR
|
STORE_NAME 1 (x)
|
||||||
|
LOAD_NAME 0 (__conditional_annotations__)
|
||||||
|
LOAD_SMALL_INT 0
|
||||||
|
SET_ADD 1
|
||||||
|
POP_TOP
|
||||||
|
|
||||||
2 LOAD_CONST 1 (<code object __annotate__ at 0x..., file "<dis>", line 2>)
|
3 LOAD_NAME 0 (__conditional_annotations__)
|
||||||
MAKE_FUNCTION
|
LOAD_SMALL_INT 1
|
||||||
STORE_NAME 3 (__annotate__)
|
SET_ADD 1
|
||||||
LOAD_CONST 2 (None)
|
POP_TOP
|
||||||
RETURN_VALUE
|
|
||||||
|
4 LOAD_SMALL_INT 1
|
||||||
|
LOAD_NAME 2 (lst)
|
||||||
|
LOAD_NAME 3 (fun)
|
||||||
|
PUSH_NULL
|
||||||
|
LOAD_SMALL_INT 0
|
||||||
|
CALL 1
|
||||||
|
STORE_SUBSCR
|
||||||
|
LOAD_CONST 2 (None)
|
||||||
|
RETURN_VALUE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fn_with_annotate_str = """
|
fn_with_annotate_str = """
|
||||||
|
@ -995,7 +1007,8 @@ class DisTests(DisTestBase):
|
||||||
def test_widths(self):
|
def test_widths(self):
|
||||||
long_opcodes = set(['JUMP_BACKWARD_NO_INTERRUPT',
|
long_opcodes = set(['JUMP_BACKWARD_NO_INTERRUPT',
|
||||||
'LOAD_FAST_BORROW_LOAD_FAST_BORROW',
|
'LOAD_FAST_BORROW_LOAD_FAST_BORROW',
|
||||||
'INSTRUMENTED_CALL_FUNCTION_EX'])
|
'INSTRUMENTED_CALL_FUNCTION_EX',
|
||||||
|
'ANNOTATIONS_PLACEHOLDER'])
|
||||||
for op, opname in enumerate(dis.opname):
|
for op, opname in enumerate(dis.opname):
|
||||||
if opname in long_opcodes or opname.startswith("INSTRUMENTED"):
|
if opname in long_opcodes or opname.startswith("INSTRUMENTED"):
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -383,9 +383,10 @@ class GrammarTests(unittest.TestCase):
|
||||||
gns = {}; lns = {}
|
gns = {}; lns = {}
|
||||||
exec("'docstring'\n"
|
exec("'docstring'\n"
|
||||||
"x: int = 5\n", gns, lns)
|
"x: int = 5\n", gns, lns)
|
||||||
|
self.assertNotIn('__annotate__', gns)
|
||||||
|
|
||||||
|
gns.update(lns) # __annotate__ looks at globals
|
||||||
self.assertEqual(lns["__annotate__"](annotationlib.Format.VALUE), {'x': int})
|
self.assertEqual(lns["__annotate__"](annotationlib.Format.VALUE), {'x': int})
|
||||||
with self.assertRaises(KeyError):
|
|
||||||
gns['__annotate__']
|
|
||||||
|
|
||||||
def test_var_annot_rhs(self):
|
def test_var_annot_rhs(self):
|
||||||
ns = {}
|
ns = {}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import inspect
|
||||||
import textwrap
|
import textwrap
|
||||||
import types
|
import types
|
||||||
import unittest
|
import unittest
|
||||||
from test.support import run_code, check_syntax_error, cpython_only
|
from test.support import run_code, check_syntax_error, import_helper, cpython_only
|
||||||
from test.test_inspect import inspect_stringized_annotations
|
from test.test_inspect import inspect_stringized_annotations
|
||||||
|
|
||||||
|
|
||||||
|
@ -151,6 +151,14 @@ class TypeAnnotationTests(unittest.TestCase):
|
||||||
del D.__annotations__
|
del D.__annotations__
|
||||||
self.assertEqual(D.__annotations__, {})
|
self.assertEqual(D.__annotations__, {})
|
||||||
|
|
||||||
|
def test_partially_executed_module(self):
|
||||||
|
partialexe = import_helper.import_fresh_module("test.typinganndata.partialexecution")
|
||||||
|
self.assertEqual(
|
||||||
|
partialexe.a.__annotations__,
|
||||||
|
{"v1": int, "v2": int},
|
||||||
|
)
|
||||||
|
self.assertEqual(partialexe.b.annos, {"v1": int})
|
||||||
|
|
||||||
@cpython_only
|
@cpython_only
|
||||||
def test_no_cell(self):
|
def test_no_cell(self):
|
||||||
# gh-130924: Test that uses of annotations in local scopes do not
|
# gh-130924: Test that uses of annotations in local scopes do not
|
||||||
|
|
1
Lib/test/typinganndata/partialexecution/__init__.py
Normal file
1
Lib/test/typinganndata/partialexecution/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
from . import a
|
5
Lib/test/typinganndata/partialexecution/a.py
Normal file
5
Lib/test/typinganndata/partialexecution/a.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
v1: int
|
||||||
|
|
||||||
|
from . import b
|
||||||
|
|
||||||
|
v2: int
|
3
Lib/test/typinganndata/partialexecution/b.py
Normal file
3
Lib/test/typinganndata/partialexecution/b.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from . import a
|
||||||
|
|
||||||
|
annos = a.__annotations__
|
|
@ -2679,6 +2679,7 @@ TESTSUBDIRS= idlelib/idle_test \
|
||||||
test/translationdata/getopt \
|
test/translationdata/getopt \
|
||||||
test/translationdata/optparse \
|
test/translationdata/optparse \
|
||||||
test/typinganndata \
|
test/typinganndata \
|
||||||
|
test/typinganndata/partialexecution \
|
||||||
test/wheeldata \
|
test/wheeldata \
|
||||||
test/xmltestdata \
|
test/xmltestdata \
|
||||||
test/xmltestdata/c14n-20 \
|
test/xmltestdata/c14n-20 \
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
If the ``__annotations__`` of a module object are accessed while the
|
||||||
|
module is executing, return the annotations that have been defined so far,
|
||||||
|
without caching them.
|
|
@ -1246,6 +1246,25 @@ module_get_annotations(PyObject *self, void *Py_UNUSED(ignored))
|
||||||
|
|
||||||
PyObject *annotations;
|
PyObject *annotations;
|
||||||
if (PyDict_GetItemRef(dict, &_Py_ID(__annotations__), &annotations) == 0) {
|
if (PyDict_GetItemRef(dict, &_Py_ID(__annotations__), &annotations) == 0) {
|
||||||
|
PyObject *spec;
|
||||||
|
if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__spec__), &spec) < 0) {
|
||||||
|
Py_DECREF(dict);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
bool is_initializing = false;
|
||||||
|
if (spec != NULL) {
|
||||||
|
int rc = _PyModuleSpec_IsInitializing(spec);
|
||||||
|
if (rc < 0) {
|
||||||
|
Py_DECREF(spec);
|
||||||
|
Py_DECREF(dict);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_DECREF(spec);
|
||||||
|
if (rc) {
|
||||||
|
is_initializing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PyObject *annotate;
|
PyObject *annotate;
|
||||||
int annotate_result = PyDict_GetItemRef(dict, &_Py_ID(__annotate__), &annotate);
|
int annotate_result = PyDict_GetItemRef(dict, &_Py_ID(__annotate__), &annotate);
|
||||||
if (annotate_result < 0) {
|
if (annotate_result < 0) {
|
||||||
|
@ -1273,7 +1292,8 @@ module_get_annotations(PyObject *self, void *Py_UNUSED(ignored))
|
||||||
annotations = PyDict_New();
|
annotations = PyDict_New();
|
||||||
}
|
}
|
||||||
Py_XDECREF(annotate);
|
Py_XDECREF(annotate);
|
||||||
if (annotations) {
|
// Do not cache annotations if the module is still initializing
|
||||||
|
if (annotations && !is_initializing) {
|
||||||
int result = PyDict_SetItem(
|
int result = PyDict_SetItem(
|
||||||
dict, &_Py_ID(__annotations__), annotations);
|
dict, &_Py_ID(__annotations__), annotations);
|
||||||
if (result) {
|
if (result) {
|
||||||
|
|
|
@ -2026,6 +2026,10 @@ dummy_func(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pseudo(ANNOTATIONS_PLACEHOLDER, (--)) = {
|
||||||
|
NOP,
|
||||||
|
};
|
||||||
|
|
||||||
inst(DICT_UPDATE, (dict, unused[oparg - 1], update -- dict, unused[oparg - 1])) {
|
inst(DICT_UPDATE, (dict, unused[oparg - 1], update -- dict, unused[oparg - 1])) {
|
||||||
PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict);
|
PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict);
|
||||||
PyObject *update_o = PyStackRef_AsPyObjectBorrow(update);
|
PyObject *update_o = PyStackRef_AsPyObjectBorrow(update);
|
||||||
|
|
|
@ -654,6 +654,9 @@ codegen_enter_scope(compiler *c, identifier name, int scope_type,
|
||||||
loc.lineno = 0;
|
loc.lineno = 0;
|
||||||
}
|
}
|
||||||
ADDOP_I(c, loc, RESUME, RESUME_AT_FUNC_START);
|
ADDOP_I(c, loc, RESUME, RESUME_AT_FUNC_START);
|
||||||
|
if (scope_type == COMPILE_SCOPE_MODULE) {
|
||||||
|
ADDOP(c, loc, ANNOTATIONS_PLACEHOLDER);
|
||||||
|
}
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -792,6 +795,14 @@ codegen_process_deferred_annotations(compiler *c, location loc)
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int scope_type = SCOPE_TYPE(c);
|
||||||
|
bool need_separate_block = scope_type == COMPILE_SCOPE_MODULE;
|
||||||
|
if (need_separate_block) {
|
||||||
|
if (_PyCompile_StartAnnotationSetup(c) == ERROR) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// It's possible that ste_annotations_block is set but
|
// It's possible that ste_annotations_block is set but
|
||||||
// u_deferred_annotations is not, because the former is still
|
// u_deferred_annotations is not, because the former is still
|
||||||
// set if there are only non-simple annotations (i.e., annotations
|
// set if there are only non-simple annotations (i.e., annotations
|
||||||
|
@ -800,7 +811,6 @@ codegen_process_deferred_annotations(compiler *c, location loc)
|
||||||
PySTEntryObject *ste = SYMTABLE_ENTRY(c);
|
PySTEntryObject *ste = SYMTABLE_ENTRY(c);
|
||||||
assert(ste->ste_annotation_block != NULL);
|
assert(ste->ste_annotation_block != NULL);
|
||||||
void *key = (void *)((uintptr_t)ste->ste_id + 1);
|
void *key = (void *)((uintptr_t)ste->ste_id + 1);
|
||||||
int scope_type = SCOPE_TYPE(c);
|
|
||||||
if (codegen_setup_annotations_scope(c, loc, key,
|
if (codegen_setup_annotations_scope(c, loc, key,
|
||||||
ste->ste_annotation_block->ste_name) < 0) {
|
ste->ste_annotation_block->ste_name) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -820,6 +830,10 @@ codegen_process_deferred_annotations(compiler *c, location loc)
|
||||||
ste->ste_type == ClassBlock ? &_Py_ID(__annotate_func__) : &_Py_ID(__annotate__),
|
ste->ste_type == ClassBlock ? &_Py_ID(__annotate_func__) : &_Py_ID(__annotate__),
|
||||||
Store));
|
Store));
|
||||||
|
|
||||||
|
if (need_separate_block) {
|
||||||
|
RETURN_IF_ERROR(_PyCompile_EndAnnotationSetup(c));
|
||||||
|
}
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
error:
|
error:
|
||||||
Py_XDECREF(deferred_anno);
|
Py_XDECREF(deferred_anno);
|
||||||
|
|
|
@ -64,6 +64,7 @@ struct compiler_unit {
|
||||||
long u_next_conditional_annotation_index; /* index of the next conditional annotation */
|
long u_next_conditional_annotation_index; /* index of the next conditional annotation */
|
||||||
|
|
||||||
instr_sequence *u_instr_sequence; /* codegen output */
|
instr_sequence *u_instr_sequence; /* codegen output */
|
||||||
|
instr_sequence *u_stashed_instr_sequence; /* temporarily stashed parent instruction sequence */
|
||||||
|
|
||||||
int u_nfblocks;
|
int u_nfblocks;
|
||||||
int u_in_inlined_comp;
|
int u_in_inlined_comp;
|
||||||
|
@ -178,6 +179,7 @@ static void
|
||||||
compiler_unit_free(struct compiler_unit *u)
|
compiler_unit_free(struct compiler_unit *u)
|
||||||
{
|
{
|
||||||
Py_CLEAR(u->u_instr_sequence);
|
Py_CLEAR(u->u_instr_sequence);
|
||||||
|
Py_CLEAR(u->u_stashed_instr_sequence);
|
||||||
Py_CLEAR(u->u_ste);
|
Py_CLEAR(u->u_ste);
|
||||||
Py_CLEAR(u->u_metadata.u_name);
|
Py_CLEAR(u->u_metadata.u_name);
|
||||||
Py_CLEAR(u->u_metadata.u_qualname);
|
Py_CLEAR(u->u_metadata.u_qualname);
|
||||||
|
@ -681,6 +683,7 @@ _PyCompile_EnterScope(compiler *c, identifier name, int scope_type,
|
||||||
compiler_unit_free(u);
|
compiler_unit_free(u);
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
|
u->u_stashed_instr_sequence = NULL;
|
||||||
|
|
||||||
/* Push the old compiler_unit on the stack. */
|
/* Push the old compiler_unit on the stack. */
|
||||||
if (c->u) {
|
if (c->u) {
|
||||||
|
@ -1154,7 +1157,7 @@ _PyCompile_AddDeferredAnnotation(compiler *c, stmt_ty s,
|
||||||
}
|
}
|
||||||
Py_DECREF(ptr);
|
Py_DECREF(ptr);
|
||||||
PyObject *index;
|
PyObject *index;
|
||||||
if (c->u->u_in_conditional_block) {
|
if (c->u->u_scope_type == COMPILE_SCOPE_MODULE || c->u->u_in_conditional_block) {
|
||||||
index = PyLong_FromLong(c->u->u_next_conditional_annotation_index);
|
index = PyLong_FromLong(c->u->u_next_conditional_annotation_index);
|
||||||
if (index == NULL) {
|
if (index == NULL) {
|
||||||
return ERROR;
|
return ERROR;
|
||||||
|
@ -1231,6 +1234,35 @@ _PyCompile_InstrSequence(compiler *c)
|
||||||
return c->u->u_instr_sequence;
|
return c->u->u_instr_sequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyCompile_StartAnnotationSetup(struct _PyCompiler *c)
|
||||||
|
{
|
||||||
|
instr_sequence *new_seq = (instr_sequence *)_PyInstructionSequence_New();
|
||||||
|
if (new_seq == NULL) {
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
assert(c->u->u_stashed_instr_sequence == NULL);
|
||||||
|
c->u->u_stashed_instr_sequence = c->u->u_instr_sequence;
|
||||||
|
c->u->u_instr_sequence = new_seq;
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyCompile_EndAnnotationSetup(struct _PyCompiler *c)
|
||||||
|
{
|
||||||
|
assert(c->u->u_stashed_instr_sequence != NULL);
|
||||||
|
instr_sequence *parent_seq = c->u->u_stashed_instr_sequence;
|
||||||
|
instr_sequence *anno_seq = c->u->u_instr_sequence;
|
||||||
|
c->u->u_stashed_instr_sequence = NULL;
|
||||||
|
c->u->u_instr_sequence = parent_seq;
|
||||||
|
if (_PyInstructionSequence_SetAnnotationsCode(parent_seq, anno_seq) == ERROR) {
|
||||||
|
Py_DECREF(anno_seq);
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyCompile_FutureFeatures(compiler *c)
|
_PyCompile_FutureFeatures(compiler *c)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3847,16 +3847,38 @@ _PyCfg_FromInstructionSequence(_PyInstructionSequence *seq)
|
||||||
seq->s_instrs[instr->i_oparg].i_target = 1;
|
seq->s_instrs[instr->i_oparg].i_target = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
int offset = 0;
|
||||||
for (int i = 0; i < seq->s_used; i++) {
|
for (int i = 0; i < seq->s_used; i++) {
|
||||||
_PyInstruction *instr = &seq->s_instrs[i];
|
_PyInstruction *instr = &seq->s_instrs[i];
|
||||||
|
if (instr->i_opcode == ANNOTATIONS_PLACEHOLDER) {
|
||||||
|
if (seq->s_annotations_code != NULL) {
|
||||||
|
assert(seq->s_annotations_code->s_labelmap_size == 0
|
||||||
|
&& seq->s_annotations_code->s_nested == NULL);
|
||||||
|
for (int j = 0; j < seq->s_annotations_code->s_used; j++) {
|
||||||
|
_PyInstruction *ann_instr = &seq->s_annotations_code->s_instrs[j];
|
||||||
|
assert(!HAS_TARGET(ann_instr->i_opcode));
|
||||||
|
if (_PyCfgBuilder_Addop(g, ann_instr->i_opcode, ann_instr->i_oparg, ann_instr->i_loc) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset += seq->s_annotations_code->s_used - 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
offset -= 1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (instr->i_target) {
|
if (instr->i_target) {
|
||||||
jump_target_label lbl_ = {i};
|
jump_target_label lbl_ = {i + offset};
|
||||||
if (_PyCfgBuilder_UseLabel(g, lbl_) < 0) {
|
if (_PyCfgBuilder_UseLabel(g, lbl_) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int opcode = instr->i_opcode;
|
int opcode = instr->i_opcode;
|
||||||
int oparg = instr->i_oparg;
|
int oparg = instr->i_oparg;
|
||||||
|
if (HAS_TARGET(opcode)) {
|
||||||
|
oparg += offset;
|
||||||
|
}
|
||||||
if (_PyCfgBuilder_Addop(g, opcode, oparg, instr->i_loc) < 0) {
|
if (_PyCfgBuilder_Addop(g, opcode, oparg, instr->i_loc) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,6 +154,15 @@ _PyInstructionSequence_InsertInstruction(instr_sequence *seq, int pos,
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyInstructionSequence_SetAnnotationsCode(instr_sequence *seq,
|
||||||
|
instr_sequence *annotations)
|
||||||
|
{
|
||||||
|
assert(seq->s_annotations_code == NULL);
|
||||||
|
seq->s_annotations_code = annotations;
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyInstructionSequence_AddNested(instr_sequence *seq, instr_sequence *nested)
|
_PyInstructionSequence_AddNested(instr_sequence *seq, instr_sequence *nested)
|
||||||
{
|
{
|
||||||
|
@ -178,6 +187,12 @@ PyInstructionSequence_Fini(instr_sequence *seq) {
|
||||||
|
|
||||||
PyMem_Free(seq->s_instrs);
|
PyMem_Free(seq->s_instrs);
|
||||||
seq->s_instrs = NULL;
|
seq->s_instrs = NULL;
|
||||||
|
|
||||||
|
if (seq->s_annotations_code != NULL) {
|
||||||
|
PyInstructionSequence_Fini(seq->s_annotations_code);
|
||||||
|
Py_CLEAR(seq->s_annotations_code);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
|
@ -200,6 +215,7 @@ inst_seq_create(void)
|
||||||
seq->s_labelmap = NULL;
|
seq->s_labelmap = NULL;
|
||||||
seq->s_labelmap_size = 0;
|
seq->s_labelmap_size = 0;
|
||||||
seq->s_nested = NULL;
|
seq->s_nested = NULL;
|
||||||
|
seq->s_annotations_code = NULL;
|
||||||
|
|
||||||
PyObject_GC_Track(seq);
|
PyObject_GC_Track(seq);
|
||||||
return seq;
|
return seq;
|
||||||
|
@ -414,6 +430,7 @@ inst_seq_traverse(PyObject *op, visitproc visit, void *arg)
|
||||||
{
|
{
|
||||||
_PyInstructionSequence *seq = (_PyInstructionSequence *)op;
|
_PyInstructionSequence *seq = (_PyInstructionSequence *)op;
|
||||||
Py_VISIT(seq->s_nested);
|
Py_VISIT(seq->s_nested);
|
||||||
|
Py_VISIT((PyObject *)seq->s_annotations_code);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,6 +439,7 @@ inst_seq_clear(PyObject *op)
|
||||||
{
|
{
|
||||||
_PyInstructionSequence *seq = (_PyInstructionSequence *)op;
|
_PyInstructionSequence *seq = (_PyInstructionSequence *)op;
|
||||||
Py_CLEAR(seq->s_nested);
|
Py_CLEAR(seq->s_nested);
|
||||||
|
Py_CLEAR(seq->s_annotations_code);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2749,8 +2749,10 @@ symtable_visit_annotation(struct symtable *st, expr_ty annotation, void *key)
|
||||||
// Annotations in local scopes are not executed and should not affect the symtable
|
// Annotations in local scopes are not executed and should not affect the symtable
|
||||||
bool is_unevaluated = st->st_cur->ste_type == FunctionBlock;
|
bool is_unevaluated = st->st_cur->ste_type == FunctionBlock;
|
||||||
|
|
||||||
if ((st->st_cur->ste_type == ClassBlock || st->st_cur->ste_type == ModuleBlock)
|
// Module-level annotations are always considered conditional because the module
|
||||||
&& st->st_cur->ste_in_conditional_block
|
// may be partially executed.
|
||||||
|
if ((((st->st_cur->ste_type == ClassBlock && st->st_cur->ste_in_conditional_block)
|
||||||
|
|| st->st_cur->ste_type == ModuleBlock))
|
||||||
&& !st->st_cur->ste_has_conditional_annotations)
|
&& !st->st_cur->ste_has_conditional_annotations)
|
||||||
{
|
{
|
||||||
st->st_cur->ste_has_conditional_annotations = 1;
|
st->st_cur->ste_has_conditional_annotations = 1;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue