Skip to content

Commit a0a9184

Browse files
authored
Merge pull request #16 from LazyImportsCabal/lazy_globals
Lazy globals
2 parents 2af21a1 + 5166d39 commit a0a9184

File tree

9 files changed

+53
-70
lines changed

9 files changed

+53
-70
lines changed

Include/internal/pycore_compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ int _PyCompile_PushFBlock(struct _PyCompiler *c, _Py_SourceLocation loc,
127127
void _PyCompile_PopFBlock(struct _PyCompiler *c, enum _PyCompile_FBlockType t,
128128
_PyJumpTargetLabel block_label);
129129
_PyCompile_FBlockInfo *_PyCompile_TopFBlock(struct _PyCompiler *c);
130+
bool _PyCompile_InExceptionHandler(struct _PyCompiler *c);
130131

131132
int _PyCompile_EnterScope(struct _PyCompiler *c, identifier name, int scope_type,
132133
void *key, int lineno, PyObject *private,

Include/internal/pycore_symtable.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ typedef struct _symtable_entry {
127127
unsigned ste_has_conditional_annotations : 1; /* true if block has conditionally executed annotations */
128128
unsigned ste_in_conditional_block : 1; /* set while we are inside a conditionally executed block */
129129
unsigned ste_in_try_block : 1; /* set while we are inside a try/except block */
130-
unsigned ste_in_with_block : 1; /* set while we are inside a with block */
131130
unsigned ste_in_unevaluated_annotation : 1; /* set while we are processing an annotation that will not be evaluated */
132131
int ste_comp_iter_expr; /* non-zero if visiting a comprehension range expression */
133132
_Py_SourceLocation ste_loc; /* source location of block */

Lib/test/test_import/__init__.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2700,7 +2700,7 @@ def test_modules_dict(self):
27002700
except ImportError as e:
27012701
self.fail('lazy import failed')
27022702

2703-
self.assertTrue("test.test_import.data.lazy_imports.basic2" in sys.modules)
2703+
self.assertFalse("test.test_import.data.lazy_imports.basic2" in sys.modules)
27042704

27052705
def test_modules_geatattr(self):
27062706
try:
@@ -2756,6 +2756,22 @@ def test_try_except_eager_from(self):
27562756

27572757
self.assertTrue("test.test_import.data.lazy_imports.basic2" in sys.modules)
27582758

2759+
def test_lazy_with(self):
2760+
try:
2761+
import test.test_import.data.lazy_imports.lazy_with
2762+
except ImportError as e:
2763+
self.fail('lazy import failed')
2764+
2765+
self.assertFalse("test.test_import.data.lazy_imports.basic2" in sys.modules)
2766+
2767+
def test_lazy_with_from(self):
2768+
try:
2769+
import test.test_import.data.lazy_imports.lazy_with_from
2770+
except ImportError as e:
2771+
self.fail('lazy import failed')
2772+
2773+
self.assertFalse("test.test_import.data.lazy_imports.basic2" in sys.modules)
2774+
27592775
def test_lazy_import_func(self):
27602776
with self.assertRaises(SyntaxError):
27612777
import test.test_import.data.lazy_imports.lazy_import_func
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import contextlib
2+
with contextlib.nullcontext():
3+
lazy import test.test_import.data.lazy_imports.basic2
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import contextlib
2+
with contextlib.nullcontext():
3+
lazy import test.test_import.data.lazy_imports.basic2 as basic2

Objects/moduleobject.c

Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
(assert(PyModule_Check(op)), _Py_CAST(PyModuleObject*, (op)))
2424

2525

26+
static PyMemberDef module_members[] = {
27+
{"__dict__", _Py_T_OBJECT, offsetof(PyModuleObject, md_dict), Py_READONLY},
28+
{0}
29+
};
30+
2631
PyTypeObject PyModuleDef_Type = {
2732
PyVarObject_HEAD_INIT(&PyType_Type, 0)
2833
"moduledef", /* tp_name */
@@ -1484,52 +1489,9 @@ module_set_annotations(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)
14841489
return ret;
14851490
}
14861491

1487-
static PyObject *
1488-
module_get_dict(PyObject *mod, void *Py_UNUSED(ignored))
1489-
{
1490-
PyModuleObject *self = (PyModuleObject *)mod;
1491-
PyThreadState *tstate = PyThreadState_GET();
1492-
Py_BEGIN_CRITICAL_SECTION(self->md_dict);
1493-
uint32_t version = _PyDict_GetKeysVersionForCurrentState(tstate->interp,
1494-
(PyDictObject *)self->md_dict);
1495-
// Check if the dict has been updated since we last checked to see if
1496-
// it has lazy values.
1497-
if (self->m_dict_version != version || version == 0) {
1498-
// Scan for lazy values...
1499-
bool retry;
1500-
do {
1501-
retry = false;
1502-
Py_ssize_t pos = 0;
1503-
PyObject *key, *value;
1504-
while (PyDict_Next(self->md_dict, &pos, &key, &value)) {
1505-
if (PyLazyImport_CheckExact(value)) {
1506-
PyObject *new_value = _PyImport_LoadLazyImportTstate(tstate, value);
1507-
if (new_value == NULL) {
1508-
return NULL;
1509-
}
1510-
if (_PyModule_ReplaceLazyValue(self->md_dict, key, new_value) < 0) {
1511-
Py_DECREF(new_value);
1512-
return NULL;
1513-
}
1514-
if (!PyLazyImport_CheckExact(value)) {
1515-
// Only force a retry if we actually made forward progress
1516-
retry = true;
1517-
}
1518-
Py_DECREF(new_value);
1519-
}
1520-
}
1521-
} while(retry);
1522-
self->m_dict_version = _PyDict_GetKeysVersionForCurrentState(tstate->interp,
1523-
(PyDictObject *)self->md_dict);
1524-
}
1525-
Py_END_CRITICAL_SECTION();
1526-
return Py_NewRef(self->md_dict);
1527-
}
1528-
15291492
static PyGetSetDef module_getsets[] = {
15301493
{"__annotations__", module_get_annotations, module_set_annotations},
15311494
{"__annotate__", module_get_annotate, module_set_annotate},
1532-
{"__dict__", (getter)module_get_dict, NULL},
15331495
{NULL}
15341496
};
15351497

@@ -1563,7 +1525,7 @@ PyTypeObject PyModule_Type = {
15631525
0, /* tp_iter */
15641526
0, /* tp_iternext */
15651527
module_methods, /* tp_methods */
1566-
0, /* tp_members */
1528+
module_members, /* tp_members */
15671529
module_getsets, /* tp_getset */
15681530
0, /* tp_base */
15691531
0, /* tp_dict */

Python/codegen.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2857,8 +2857,6 @@ codegen_validate_lazy_import(compiler *c, location loc)
28572857
{
28582858
if (_PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE) {
28592859
return _PyCompile_Error(c, loc, "lazy imports only allowed in module scope");
2860-
} else if (_PyCompile_TopFBlock(c)) {
2861-
return _PyCompile_Error(c, loc, "cannot lazy import in a nested scope");
28622860
}
28632861

28642862
return SUCCESS;
@@ -2888,7 +2886,7 @@ codegen_import(compiler *c, stmt_ty s)
28882886
RETURN_IF_ERROR(codegen_validate_lazy_import(c, loc));
28892887
ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 1);
28902888
} else {
2891-
if (_PyCompile_TopFBlock(c) || _PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE) {
2889+
if (_PyCompile_InExceptionHandler(c) || _PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE) {
28922890
// force eager import in try/except block
28932891
ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 2);
28942892
} else {
@@ -2953,7 +2951,8 @@ codegen_from_import(compiler *c, stmt_ty s)
29532951
ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 1);
29542952
} else {
29552953
alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, 0);
2956-
if (_PyCompile_TopFBlock(c) || _PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE ||
2954+
if (_PyCompile_InExceptionHandler(c) ||
2955+
_PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE ||
29572956
PyUnicode_READ_CHAR(alias->name, 0) == '*') {
29582957
// forced non-lazy import due to try/except or import *
29592958
ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 2);

Python/compile.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,26 @@ _PyCompile_TopFBlock(compiler *c)
787787
return &c->u->u_fblock[c->u->u_nfblocks - 1];
788788
}
789789

790+
bool
791+
_PyCompile_InExceptionHandler(compiler *c)
792+
{
793+
for (Py_ssize_t i = c->u->u_nfblocks; i < c->u->u_nfblocks; i++) {
794+
fblockinfo *block = &c->u->u_fblock[i];
795+
switch (block->fb_type) {
796+
case COMPILE_FBLOCK_TRY_EXCEPT:
797+
case COMPILE_FBLOCK_FINALLY_TRY:
798+
case COMPILE_FBLOCK_FINALLY_END:
799+
case COMPILE_FBLOCK_EXCEPTION_HANDLER:
800+
case COMPILE_FBLOCK_EXCEPTION_GROUP_HANDLER:
801+
case COMPILE_FBLOCK_HANDLER_CLEANUP:
802+
return true;
803+
default:
804+
break;
805+
}
806+
}
807+
return false;
808+
}
809+
790810
void
791811
_PyCompile_DeferredAnnotations(compiler *c,
792812
PyObject **deferred_annotations,

Python/symtable.c

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block,
142142
ste->ste_has_conditional_annotations = 0;
143143
ste->ste_in_conditional_block = 0;
144144
ste->ste_in_try_block = 0;
145-
ste->ste_in_with_block = 0;
146145
ste->ste_in_unevaluated_annotation = 0;
147146
ste->ste_annotation_block = NULL;
148147

@@ -1756,13 +1755,6 @@ symtable_enter_type_param_block(struct symtable *st, identifier name,
17561755
#define LEAVE_TRY_BLOCK(ST) \
17571756
(ST)->st_cur->ste_in_try_block = in_try_block;
17581757

1759-
#define ENTER_WITH_BLOCK(ST) \
1760-
int in_with_block = (ST)->st_cur->ste_in_with_block; \
1761-
(ST)->st_cur->ste_in_with_block = 1;
1762-
1763-
#define LEAVE_WITH_BLOCK(ST) \
1764-
(ST)->st_cur->ste_in_with_block = in_with_block;
1765-
17661758
#define ENTER_RECURSIVE() \
17671759
if (Py_EnterRecursiveCall(" during compilation")) { \
17681760
return 0; \
@@ -1835,14 +1827,6 @@ check_lazy_import_context(struct symtable *st, stmt_ty s, const char* import_typ
18351827
return 0;
18361828
}
18371829

1838-
/* Check if inside with block */
1839-
if (st->st_cur->ste_in_with_block) {
1840-
PyErr_Format(PyExc_SyntaxError,
1841-
"lazy %s not allowed inside with blocks", import_type);
1842-
SET_ERROR_LOCATION(st->st_filename, LOCATION(s));
1843-
return 0;
1844-
}
1845-
18461830
/* Check if inside function scope */
18471831
if (st->st_cur->ste_type == FunctionBlock) {
18481832
PyErr_Format(PyExc_SyntaxError,
@@ -2258,10 +2242,8 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
22582242
break;
22592243
case With_kind: {
22602244
ENTER_CONDITIONAL_BLOCK(st);
2261-
ENTER_WITH_BLOCK(st);
22622245
VISIT_SEQ(st, withitem, s->v.With.items);
22632246
VISIT_SEQ(st, stmt, s->v.With.body);
2264-
LEAVE_WITH_BLOCK(st);
22652247
LEAVE_CONDITIONAL_BLOCK(st);
22662248
break;
22672249
}
@@ -2326,10 +2308,8 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
23262308
return 0;
23272309
}
23282310
ENTER_CONDITIONAL_BLOCK(st);
2329-
ENTER_WITH_BLOCK(st);
23302311
VISIT_SEQ(st, withitem, s->v.AsyncWith.items);
23312312
VISIT_SEQ(st, stmt, s->v.AsyncWith.body);
2332-
LEAVE_WITH_BLOCK(st);
23332313
LEAVE_CONDITIONAL_BLOCK(st);
23342314
break;
23352315
}

0 commit comments

Comments
 (0)