Implement disabling imports in try/except and * imports, report errors on bad usage of lazy

This commit is contained in:
Dino Viehland 2025-09-29 09:01:41 -07:00
parent 164423b42b
commit 00e7800e4c
11 changed files with 99 additions and 27 deletions

View file

@ -3049,17 +3049,6 @@ _PyEval_LazyImportName(PyThreadState *tstate, PyObject *builtins, PyObject *glob
break;
}
// Always make star imports eager regardless of lazy setting
if (fromlist && PyTuple_Check(fromlist) && PyTuple_GET_SIZE(fromlist) == 1) {
PyObject *item = PyTuple_GET_ITEM(fromlist, 0);
if (PyUnicode_Check(item)) {
const char *item_str = PyUnicode_AsUTF8(item);
if (item_str && strcmp(item_str, "*") == 0) {
lazy = 0; // Force star imports to be eager
}
}
}
if (!lazy) {
// Not a lazy import or lazy imports are disabled, fallback to the regular import
return _PyEval_ImportName(tstate, builtins, globals, locals, name, fromlist, level);

View file

@ -2852,6 +2852,18 @@ codegen_import_as(compiler *c, location loc,
return codegen_nameop(c, loc, asname, Store);
}
static int
codegen_validate_lazy_import(compiler *c, location loc)
{
if (_PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE) {
return _PyCompile_Error(c, loc, "lazy imports only allowed in module scope");
} else if (_PyCompile_TopFBlock(c)) {
return _PyCompile_Error(c, loc, "cannot lazy import in a nested scope");
}
return SUCCESS;
}
static int
codegen_import(compiler *c, stmt_ty s)
{
@ -2873,11 +2885,15 @@ codegen_import(compiler *c, stmt_ty s)
ADDOP_LOAD_CONST(c, loc, zero);
ADDOP_LOAD_CONST(c, loc, Py_None);
if (s->v.Import.is_lazy) {
// TODO: SyntaxError when not in module scope
RETURN_IF_ERROR(codegen_validate_lazy_import(c, loc));
ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 1);
} else {
// TODO: If in try/except, set 2nd bit
ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 0);
if (_PyCompile_TopFBlock(c) || _PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE) {
// force eager import in try/except block
ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 2);
} else {
ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 0);
}
}
if (alias->asname) {
@ -2929,11 +2945,23 @@ codegen_from_import(compiler *c, stmt_ty s)
from = s->v.ImportFrom.module;
}
if (s->v.ImportFrom.is_lazy) {
// TODO: SyntaxError when not in module scope
alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, 0);
if (PyUnicode_READ_CHAR(alias->name, 0) == '*') {
return _PyCompile_Error(c, LOC(s), "cannot lazy import *");
}
RETURN_IF_ERROR(codegen_validate_lazy_import(c, LOC(s)));
ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 1);
} else {
ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 0);
alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, 0);
if (_PyCompile_TopFBlock(c) || _PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE ||
PyUnicode_READ_CHAR(alias->name, 0) == '*') {
// forced non-lazy import due to try/except or import *
ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 2);
} else {
ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 0);
}
}
for (Py_ssize_t i = 0; i < n; i++) {
alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, i);
identifier store_name;

View file

@ -4118,16 +4118,8 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate,
}
PyInterpreterState *interp = tstate->interp;
// Don't return early if we have a fromlist - we need to handle the import properly
// to ensure submodules are loaded
if (fromlist == NULL || fromlist == Py_None) {
PyObject *mod = PyImport_GetModule(abs_name);
if (mod != NULL) {
Py_DECREF(abs_name);
return mod;
}
}
_PyInterpreterFrame *frame = _PyEval_GetFrame();
assert(frame->f_globals == frame->f_locals); // should only be called in global scope
// Check if the filter disables the lazy import
PyObject *filter = LAZY_IMPORTS_FILTER(interp);