Skip to content

Commit d1e1761

Browse files
committed
Treat all module-level annotations as conditional
1 parent ef4a375 commit d1e1761

File tree

9 files changed

+76
-25
lines changed

9 files changed

+76
-25
lines changed

Include/internal/pycore_compile.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ int _PyCompile_EnterScope(struct _PyCompiler *c, identifier name, int scope_type
133133
void _PyCompile_ExitScope(struct _PyCompiler *c);
134134
Py_ssize_t _PyCompile_AddConst(struct _PyCompiler *c, PyObject *o);
135135
_PyInstructionSequence *_PyCompile_InstrSequence(struct _PyCompiler *c);
136+
void _PyCompile_SetInstrSequence(struct _PyCompiler *c,
137+
_PyInstructionSequence *instr_sequence);
136138
int _PyCompile_FutureFeatures(struct _PyCompiler *c);
137139
void _PyCompile_DeferredAnnotations(
138140
struct _PyCompiler *c, PyObject **deferred_annotations,

Include/internal/pycore_instruction_sequence.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ _PyJumpTargetLabel _PyInstructionSequence_NewLabel(_PyInstructionSequence *seq);
6666
int _PyInstructionSequence_ApplyLabelMap(_PyInstructionSequence *seq);
6767
int _PyInstructionSequence_InsertInstruction(_PyInstructionSequence *seq, int pos,
6868
int opcode, int oparg, _Py_SourceLocation loc);
69+
int _PyInstructionSequence_PrependSequence(_PyInstructionSequence *seq, _PyInstructionSequence *nested);
6970
int _PyInstructionSequence_AddNested(_PyInstructionSequence *seq, _PyInstructionSequence *nested);
7071
void PyInstructionSequence_Fini(_PyInstructionSequence *seq);
7172

Lib/test/test_type_annotations.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,7 @@ def test_partially_executed_module(self):
115115
partialexe.a.__annotations__,
116116
{"v1": int, "v2": int},
117117
)
118-
self.assertIsInstance(partialexe.b.exc, RuntimeError)
119-
self.assertEqual(
120-
str(partialexe.b.exc),
121-
"cannot access __annotations__ while module is initializing",
122-
)
118+
self.assertEqual(partialexe.b.annos, {"v1": int})
123119

124120
@cpython_only
125121
def test_no_cell(self):
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
11
from . import a
22

3-
try:
4-
a.__annotations__
5-
except Exception as e:
6-
exc = e
7-
else:
8-
exc = None
3+
annos = a.__annotations__

Objects/moduleobject.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,7 @@ module_get_annotations(PyObject *self, void *Py_UNUSED(ignored))
12511251
Py_DECREF(dict);
12521252
return NULL;
12531253
}
1254+
bool is_initializing = false;
12541255
if (spec != NULL) {
12551256
int rc = _PyModuleSpec_IsInitializing(spec);
12561257
if (rc < 0) {
@@ -1260,11 +1261,7 @@ module_get_annotations(PyObject *self, void *Py_UNUSED(ignored))
12601261
}
12611262
Py_DECREF(spec);
12621263
if (rc) {
1263-
PyErr_SetString(PyExc_RuntimeError,
1264-
"cannot access __annotations__ "
1265-
"while module is initializing");
1266-
Py_DECREF(dict);
1267-
return NULL;
1264+
is_initializing = true;
12681265
}
12691266
}
12701267

@@ -1295,7 +1292,8 @@ module_get_annotations(PyObject *self, void *Py_UNUSED(ignored))
12951292
annotations = PyDict_New();
12961293
}
12971294
Py_XDECREF(annotate);
1298-
if (annotations) {
1295+
// Do not cache annotations if the module is still initializing
1296+
if (annotations && !is_initializing) {
12991297
int result = PyDict_SetItem(
13001298
dict, &_Py_ID(__annotations__), annotations);
13011299
if (result) {

Python/codegen.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,17 @@ codegen_process_deferred_annotations(compiler *c, location loc)
792792
return SUCCESS;
793793
}
794794

795+
instr_sequence *old_instr_seq = INSTR_SEQUENCE(c);
796+
instr_sequence *nested_instr_seq = NULL;
797+
int scope_type = SCOPE_TYPE(c);
798+
if (scope_type == COMPILE_SCOPE_MODULE) {
799+
nested_instr_seq = (instr_sequence *)_PyInstructionSequence_New();
800+
if (nested_instr_seq == NULL) {
801+
goto error;
802+
}
803+
_PyCompile_SetInstrSequence(c, nested_instr_seq);
804+
}
805+
795806
// It's possible that ste_annotations_block is set but
796807
// u_deferred_annotations is not, because the former is still
797808
// set if there are only non-simple annotations (i.e., annotations
@@ -800,7 +811,6 @@ codegen_process_deferred_annotations(compiler *c, location loc)
800811
PySTEntryObject *ste = SYMTABLE_ENTRY(c);
801812
assert(ste->ste_annotation_block != NULL);
802813
void *key = (void *)((uintptr_t)ste->ste_id + 1);
803-
int scope_type = SCOPE_TYPE(c);
804814
if (codegen_setup_annotations_scope(c, loc, key,
805815
ste->ste_annotation_block->ste_name) < 0) {
806816
goto error;
@@ -817,8 +827,19 @@ codegen_process_deferred_annotations(compiler *c, location loc)
817827
RETURN_IF_ERROR(codegen_leave_annotations_scope(c, loc));
818828
RETURN_IF_ERROR(codegen_nameop(c, loc, &_Py_ID(__annotate__), Store));
819829

830+
if (nested_instr_seq != NULL) {
831+
RETURN_IF_ERROR(
832+
_PyInstructionSequence_PrependSequence(old_instr_seq, nested_instr_seq));
833+
_PyCompile_SetInstrSequence(c, old_instr_seq);
834+
PyInstructionSequence_Fini(nested_instr_seq);
835+
}
836+
820837
return SUCCESS;
821838
error:
839+
if (nested_instr_seq != NULL) {
840+
PyInstructionSequence_Fini(nested_instr_seq);
841+
_PyCompile_SetInstrSequence(c, old_instr_seq);
842+
}
822843
Py_XDECREF(deferred_anno);
823844
Py_XDECREF(conditional_annotation_indices);
824845
return ERROR;

Python/compile.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1154,7 +1154,7 @@ _PyCompile_AddDeferredAnnotation(compiler *c, stmt_ty s,
11541154
}
11551155
Py_DECREF(ptr);
11561156
PyObject *index;
1157-
if (c->u->u_in_conditional_block) {
1157+
if (c->u->u_scope_type == COMPILE_SCOPE_MODULE || c->u->u_in_conditional_block) {
11581158
index = PyLong_FromLong(c->u->u_next_conditional_annotation_index);
11591159
if (index == NULL) {
11601160
return ERROR;
@@ -1231,6 +1231,12 @@ _PyCompile_InstrSequence(compiler *c)
12311231
return c->u->u_instr_sequence;
12321232
}
12331233

1234+
void
1235+
_PyCompile_SetInstrSequence(compiler *c, instr_sequence *seq)
1236+
{
1237+
c->u->u_instr_sequence = seq;
1238+
}
1239+
12341240
int
12351241
_PyCompile_FutureFeatures(compiler *c)
12361242
{

Python/instruction_sequence.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,29 @@ typedef _Py_SourceLocation location;
3434
}
3535

3636
static int
37-
instr_sequence_next_inst(instr_sequence *seq) {
37+
instr_sequence_grow(instr_sequence *seq, int size) {
3838
assert(seq->s_instrs != NULL || seq->s_used == 0);
3939

40-
4140
_Py_c_array_t array = {
4241
.array = (void*)seq->s_instrs,
4342
.allocated_entries = seq->s_allocated,
4443
.item_size = sizeof(instruction),
4544
.initial_num_entries = INITIAL_INSTR_SEQUENCE_SIZE,
4645
};
4746

48-
RETURN_IF_ERROR(_Py_CArray_EnsureCapacity(&array, seq->s_used + 1));
47+
RETURN_IF_ERROR(_Py_CArray_EnsureCapacity(&array, seq->s_used + size));
4948
seq->s_instrs = array.array;
5049
seq->s_allocated = array.allocated_entries;
5150

5251
assert(seq->s_allocated >= 0);
5352
assert(seq->s_used < seq->s_allocated);
54-
return seq->s_used++;
53+
seq->s_used += size;
54+
return seq->s_used - 1;
55+
}
56+
57+
static int
58+
instr_sequence_next_inst(instr_sequence *seq) {
59+
return instr_sequence_grow(seq, 1);
5560
}
5661

5762
_PyJumpTargetLabel
@@ -154,6 +159,31 @@ _PyInstructionSequence_InsertInstruction(instr_sequence *seq, int pos,
154159
return SUCCESS;
155160
}
156161

162+
int
163+
_PyInstructionSequence_PrependSequence(instr_sequence *seq,
164+
instr_sequence *nested)
165+
{
166+
if (nested->s_used == 0) {
167+
return SUCCESS;
168+
}
169+
// Merging labelmaps is not supported
170+
assert(nested->s_labelmap_size == 0 && nested->s_nested == NULL);
171+
172+
int last_idx = instr_sequence_grow(seq, nested->s_used);
173+
174+
RETURN_IF_ERROR(last_idx);
175+
for (int i = last_idx - nested->s_used; i >= 0; i--) {
176+
seq->s_instrs[i + nested->s_used] = seq->s_instrs[i];
177+
}
178+
for (int i=0; i < nested->s_used; i++) {
179+
seq->s_instrs[i] = nested->s_instrs[i];
180+
}
181+
for(int lbl=0; lbl < seq->s_labelmap_size; lbl++) {
182+
seq->s_labelmap[lbl] += nested->s_used;
183+
}
184+
return SUCCESS;
185+
}
186+
157187
int
158188
_PyInstructionSequence_AddNested(instr_sequence *seq, instr_sequence *nested)
159189
{

Python/symtable.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2749,8 +2749,10 @@ symtable_visit_annotation(struct symtable *st, expr_ty annotation, void *key)
27492749
// Annotations in local scopes are not executed and should not affect the symtable
27502750
bool is_unevaluated = st->st_cur->ste_type == FunctionBlock;
27512751

2752-
if ((st->st_cur->ste_type == ClassBlock || st->st_cur->ste_type == ModuleBlock)
2753-
&& st->st_cur->ste_in_conditional_block
2752+
// Module-level annotations are always considered conditional because the module
2753+
// may be partially executed.
2754+
if ((((st->st_cur->ste_type == ClassBlock && st->st_cur->ste_in_conditional_block)
2755+
|| st->st_cur->ste_type == ModuleBlock))
27542756
&& !st->st_cur->ste_has_conditional_annotations)
27552757
{
27562758
st->st_cur->ste_has_conditional_annotations = 1;

0 commit comments

Comments
 (0)