gh-142349: Implement PEP 810 - Explicit lazy imports (#142351)

Co-authored-by: T. Wouters <twouters@meta.com >
Co-authored-by: Brittany Reynoso <breynoso@meta.com>
Co-authored-by: Dino Viehland <dinoviehland@meta.com>
This commit is contained in:
Pablo Galindo Salgado 2026-02-12 00:15:33 +00:00 committed by GitHub
parent cac0c98450
commit 46d5106cfa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
138 changed files with 5126 additions and 197 deletions

113
Python/Python-ast.c generated
View file

@ -222,6 +222,7 @@ void _PyAST_Fini(PyInterpreterState *interp)
Py_CLEAR(state->id);
Py_CLEAR(state->ifs);
Py_CLEAR(state->is_async);
Py_CLEAR(state->is_lazy);
Py_CLEAR(state->items);
Py_CLEAR(state->iter);
Py_CLEAR(state->key);
@ -327,6 +328,7 @@ static int init_identifiers(struct ast_state *state)
if ((state->id = PyUnicode_InternFromString("id")) == NULL) return -1;
if ((state->ifs = PyUnicode_InternFromString("ifs")) == NULL) return -1;
if ((state->is_async = PyUnicode_InternFromString("is_async")) == NULL) return -1;
if ((state->is_lazy = PyUnicode_InternFromString("is_lazy")) == NULL) return -1;
if ((state->items = PyUnicode_InternFromString("items")) == NULL) return -1;
if ((state->iter = PyUnicode_InternFromString("iter")) == NULL) return -1;
if ((state->key = PyUnicode_InternFromString("key")) == NULL) return -1;
@ -527,11 +529,13 @@ static const char * const Assert_fields[]={
};
static const char * const Import_fields[]={
"names",
"is_lazy",
};
static const char * const ImportFrom_fields[]={
"module",
"names",
"level",
"is_lazy",
};
static const char * const Global_fields[]={
"names",
@ -2254,6 +2258,21 @@ add_ast_annotations(struct ast_state *state)
return 0;
}
}
{
PyObject *type = (PyObject *)&PyLong_Type;
type = _Py_union_type_or(type, Py_None);
cond = type != NULL;
if (!cond) {
Py_DECREF(Import_annotations);
return 0;
}
cond = PyDict_SetItemString(Import_annotations, "is_lazy", type) == 0;
Py_DECREF(type);
if (!cond) {
Py_DECREF(Import_annotations);
return 0;
}
}
cond = PyObject_SetAttrString(state->Import_type, "_field_types",
Import_annotations) == 0;
if (!cond) {
@ -2315,6 +2334,22 @@ add_ast_annotations(struct ast_state *state)
return 0;
}
}
{
PyObject *type = (PyObject *)&PyLong_Type;
type = _Py_union_type_or(type, Py_None);
cond = type != NULL;
if (!cond) {
Py_DECREF(ImportFrom_annotations);
return 0;
}
cond = PyDict_SetItemString(ImportFrom_annotations, "is_lazy", type) ==
0;
Py_DECREF(type);
if (!cond) {
Py_DECREF(ImportFrom_annotations);
return 0;
}
}
cond = PyObject_SetAttrString(state->ImportFrom_type, "_field_types",
ImportFrom_annotations) == 0;
if (!cond) {
@ -6223,8 +6258,8 @@ init_types(void *arg)
" | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\n"
" | TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\n"
" | Assert(expr test, expr? msg)\n"
" | Import(alias* names)\n"
" | ImportFrom(identifier? module, alias* names, int? level)\n"
" | Import(alias* names, int? is_lazy)\n"
" | ImportFrom(identifier? module, alias* names, int? level, int? is_lazy)\n"
" | Global(identifier* names)\n"
" | Nonlocal(identifier* names)\n"
" | Expr(expr value)\n"
@ -6353,17 +6388,21 @@ init_types(void *arg)
if (PyObject_SetAttr(state->Assert_type, state->msg, Py_None) == -1)
return -1;
state->Import_type = make_type(state, "Import", state->stmt_type,
Import_fields, 1,
"Import(alias* names)");
Import_fields, 2,
"Import(alias* names, int? is_lazy)");
if (!state->Import_type) return -1;
if (PyObject_SetAttr(state->Import_type, state->is_lazy, Py_None) == -1)
return -1;
state->ImportFrom_type = make_type(state, "ImportFrom", state->stmt_type,
ImportFrom_fields, 3,
"ImportFrom(identifier? module, alias* names, int? level)");
ImportFrom_fields, 4,
"ImportFrom(identifier? module, alias* names, int? level, int? is_lazy)");
if (!state->ImportFrom_type) return -1;
if (PyObject_SetAttr(state->ImportFrom_type, state->module, Py_None) == -1)
return -1;
if (PyObject_SetAttr(state->ImportFrom_type, state->level, Py_None) == -1)
return -1;
if (PyObject_SetAttr(state->ImportFrom_type, state->is_lazy, Py_None) == -1)
return -1;
state->Global_type = make_type(state, "Global", state->stmt_type,
Global_fields, 1,
"Global(identifier* names)");
@ -7605,8 +7644,8 @@ _PyAST_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset, int
}
stmt_ty
_PyAST_Import(asdl_alias_seq * names, int lineno, int col_offset, int
end_lineno, int end_col_offset, PyArena *arena)
_PyAST_Import(asdl_alias_seq * names, int is_lazy, int lineno, int col_offset,
int end_lineno, int end_col_offset, PyArena *arena)
{
stmt_ty p;
p = (stmt_ty)_PyArena_Malloc(arena, sizeof(*p));
@ -7614,6 +7653,7 @@ _PyAST_Import(asdl_alias_seq * names, int lineno, int col_offset, int
return NULL;
p->kind = Import_kind;
p->v.Import.names = names;
p->v.Import.is_lazy = is_lazy;
p->lineno = lineno;
p->col_offset = col_offset;
p->end_lineno = end_lineno;
@ -7623,8 +7663,8 @@ _PyAST_Import(asdl_alias_seq * names, int lineno, int col_offset, int
stmt_ty
_PyAST_ImportFrom(identifier module, asdl_alias_seq * names, int level, int
lineno, int col_offset, int end_lineno, int end_col_offset,
PyArena *arena)
is_lazy, int lineno, int col_offset, int end_lineno, int
end_col_offset, PyArena *arena)
{
stmt_ty p;
p = (stmt_ty)_PyArena_Malloc(arena, sizeof(*p));
@ -7634,6 +7674,7 @@ _PyAST_ImportFrom(identifier module, asdl_alias_seq * names, int level, int
p->v.ImportFrom.module = module;
p->v.ImportFrom.names = names;
p->v.ImportFrom.level = level;
p->v.ImportFrom.is_lazy = is_lazy;
p->lineno = lineno;
p->col_offset = col_offset;
p->end_lineno = end_lineno;
@ -9467,6 +9508,11 @@ ast2obj_stmt(struct ast_state *state, void* _o)
if (PyObject_SetAttr(result, state->names, value) == -1)
goto failed;
Py_DECREF(value);
value = ast2obj_int(state, o->v.Import.is_lazy);
if (!value) goto failed;
if (PyObject_SetAttr(result, state->is_lazy, value) == -1)
goto failed;
Py_DECREF(value);
break;
case ImportFrom_kind:
tp = (PyTypeObject *)state->ImportFrom_type;
@ -9488,6 +9534,11 @@ ast2obj_stmt(struct ast_state *state, void* _o)
if (PyObject_SetAttr(result, state->level, value) == -1)
goto failed;
Py_DECREF(value);
value = ast2obj_int(state, o->v.ImportFrom.is_lazy);
if (!value) goto failed;
if (PyObject_SetAttr(result, state->is_lazy, value) == -1)
goto failed;
Py_DECREF(value);
break;
case Global_kind:
tp = (PyTypeObject *)state->Global_type;
@ -13483,6 +13534,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena*
}
if (isinstance) {
asdl_alias_seq* names;
int is_lazy;
if (PyObject_GetOptionalAttr(obj, state->names, &tmp) < 0) {
return -1;
@ -13522,7 +13574,24 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena*
}
Py_CLEAR(tmp);
}
*out = _PyAST_Import(names, lineno, col_offset, end_lineno,
if (PyObject_GetOptionalAttr(obj, state->is_lazy, &tmp) < 0) {
return -1;
}
if (tmp == NULL || tmp == Py_None) {
Py_CLEAR(tmp);
is_lazy = 0;
}
else {
int res;
if (_Py_EnterRecursiveCall(" while traversing 'Import' node")) {
goto failed;
}
res = obj2ast_int(state, tmp, &is_lazy, arena);
_Py_LeaveRecursiveCall();
if (res != 0) goto failed;
Py_CLEAR(tmp);
}
*out = _PyAST_Import(names, is_lazy, lineno, col_offset, end_lineno,
end_col_offset, arena);
if (*out == NULL) goto failed;
return 0;
@ -13536,6 +13605,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena*
identifier module;
asdl_alias_seq* names;
int level;
int is_lazy;
if (PyObject_GetOptionalAttr(obj, state->module, &tmp) < 0) {
return -1;
@ -13609,8 +13679,25 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena*
if (res != 0) goto failed;
Py_CLEAR(tmp);
}
*out = _PyAST_ImportFrom(module, names, level, lineno, col_offset,
end_lineno, end_col_offset, arena);
if (PyObject_GetOptionalAttr(obj, state->is_lazy, &tmp) < 0) {
return -1;
}
if (tmp == NULL || tmp == Py_None) {
Py_CLEAR(tmp);
is_lazy = 0;
}
else {
int res;
if (_Py_EnterRecursiveCall(" while traversing 'ImportFrom' node")) {
goto failed;
}
res = obj2ast_int(state, tmp, &is_lazy, arena);
_Py_LeaveRecursiveCall();
if (res != 0) goto failed;
Py_CLEAR(tmp);
}
*out = _PyAST_ImportFrom(module, names, level, is_lazy, lineno,
col_offset, end_lineno, end_col_offset, arena);
if (*out == NULL) goto failed;
return 0;
}

View file

@ -9,6 +9,7 @@
#include "pycore_fileutils.h" // _PyFile_Flush
#include "pycore_floatobject.h" // _PyFloat_ExactDealloc()
#include "pycore_interp.h" // _PyInterpreterState_GetConfig()
#include "pycore_import.h" // _PyImport_LazyImportModuleLevelObject ()
#include "pycore_long.h" // _PyLong_CompactValue
#include "pycore_modsupport.h" // _PyArg_NoKwnames()
#include "pycore_object.h" // _Py_AddToAllObjects()
@ -287,6 +288,61 @@ builtin___import___impl(PyObject *module, PyObject *name, PyObject *globals,
}
/*[clinic input]
__lazy_import__ as builtin___lazy_import__
name: object
globals: object(c_default="NULL") = None
locals: object(c_default="NULL") = None
fromlist: object(c_default="NULL") = ()
level: int = 0
Lazily imports a module.
Returns either the module to be imported or a imp.lazy_module object which
indicates the module to be lazily imported.
[clinic start generated code]*/
static PyObject *
builtin___lazy_import___impl(PyObject *module, PyObject *name,
PyObject *globals, PyObject *locals,
PyObject *fromlist, int level)
/*[clinic end generated code: output=300f1771094b9e8c input=9394874f340b2948]*/
{
PyObject *builtins;
PyThreadState *tstate = PyThreadState_GET();
if (globals == NULL) {
globals = PyEval_GetGlobals();
}
if (locals == NULL) {
locals = globals;
}
if (PyDict_GetItemRef(globals, &_Py_ID(__builtins__), &builtins) < 0) {
return NULL;
}
if (builtins == NULL) {
PyErr_SetString(PyExc_ValueError,
"unable to get builtins for lazy import");
return NULL;
}
if (PyModule_Check(builtins)) {
PyObject *builtins_dict = Py_XNewRef(PyModule_GetDict(builtins));
if (builtins_dict == NULL) {
Py_DECREF(builtins);
PyErr_SetString(PyExc_AttributeError,
"builtins module has no dict");
return NULL;
}
Py_SETREF(builtins, builtins_dict);
}
PyObject *res = _PyImport_LazyImportModuleLevelObject(
tstate, name, builtins, globals, locals, fromlist, level);
Py_DECREF(builtins);
return res;
}
/*[clinic input]
abs as builtin_abs
@ -3362,6 +3418,7 @@ static PyMethodDef builtin_methods[] = {
{"__build_class__", _PyCFunction_CAST(builtin___build_class__),
METH_FASTCALL | METH_KEYWORDS, build_class_doc},
BUILTIN___IMPORT___METHODDEF
BUILTIN___LAZY_IMPORT___METHODDEF
BUILTIN_ABS_METHODDEF
BUILTIN_ALL_METHODDEF
BUILTIN_ANY_METHODDEF

View file

@ -11,12 +11,15 @@
#include "pycore_audit.h" // _PySys_Audit()
#include "pycore_backoff.h"
#include "pycore_cell.h" // PyCell_GetRef()
#include "pycore_ceval.h" // _PyEval_LazyImportName(), _PyEval_LazyImportFrom()
#include "pycore_code.h"
#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS
#include "pycore_function.h"
#include "pycore_import.h" // _PyImport_LoadLazyImportTstate()
#include "pycore_instruments.h"
#include "pycore_interpolation.h" // _PyInterpolation_Build()
#include "pycore_intrinsics.h"
#include "pycore_lazyimportobject.h" // PyLazyImport_CheckExact()
#include "pycore_long.h" // _PyLong_ExactDealloc(), _PyLong_GetZero()
#include "pycore_moduleobject.h" // PyModuleObject
#include "pycore_object.h" // _PyObject_GC_TRACK()
@ -1795,6 +1798,12 @@ dummy_func(
}
ERROR_NO_POP();
}
if (PyLazyImport_CheckExact(v_o)) {
PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o);
Py_SETREF(v_o, l_v);
ERROR_IF(v_o == NULL);
}
}
else {
/* Slow-path if globals or builtins is not a dict */
@ -1812,6 +1821,11 @@ dummy_func(
ERROR_IF(true);
}
}
if (PyLazyImport_CheckExact(v_o)) {
PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o);
Py_SETREF(v_o, l_v);
ERROR_IF(v_o == NULL);
}
}
}
v = PyStackRef_FromPyObjectSteal(v_o);
@ -1821,6 +1835,22 @@ dummy_func(
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
PyObject *v_o = _PyEval_LoadName(tstate, frame, name);
ERROR_IF(v_o == NULL);
if (PyLazyImport_CheckExact(v_o)) {
PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o);
// cannot early-decref v_o as it may cause a side-effect on l_v
if (l_v == NULL) {
Py_DECREF(v_o);
ERROR_IF(true);
}
int err = PyDict_SetItem(GLOBALS(), name, l_v);
if (err < 0) {
Py_DECREF(v_o);
Py_DECREF(l_v);
ERROR_IF(true);
}
Py_SETREF(v_o, l_v);
}
v = PyStackRef_FromPyObjectSteal(v_o);
}
@ -1846,6 +1876,7 @@ dummy_func(
op(_LOAD_GLOBAL, ( -- res[1])) {
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
_PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res);
ERROR_IF(PyStackRef_IsNull(*res));
}
@ -2962,11 +2993,23 @@ dummy_func(
b = res ? PyStackRef_True : PyStackRef_False;
}
inst(IMPORT_NAME, (level, fromlist -- res)) {
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
PyObject *res_o = _PyEval_ImportName(tstate, frame, name,
PyStackRef_AsPyObjectBorrow(fromlist),
PyStackRef_AsPyObjectBorrow(level));
inst(IMPORT_NAME, (level, fromlist -- res)) {
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2);
PyObject *res_o;
if (!(oparg & 0x02)) {
res_o = _PyEval_LazyImportName(tstate, BUILTINS(), GLOBALS(),
LOCALS(), name,
PyStackRef_AsPyObjectBorrow(fromlist),
PyStackRef_AsPyObjectBorrow(level),
oparg & 0x01);
}
else {
res_o = _PyEval_ImportName(tstate, BUILTINS(), GLOBALS(),
LOCALS(), name,
PyStackRef_AsPyObjectBorrow(fromlist),
PyStackRef_AsPyObjectBorrow(level));
}
DECREF_INPUTS();
ERROR_IF(res_o == NULL);
res = PyStackRef_FromPyObjectSteal(res_o);
@ -2974,7 +3017,16 @@ dummy_func(
inst(IMPORT_FROM, (from -- from, res)) {
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
PyObject *res_o = _PyEval_ImportFrom(tstate, PyStackRef_AsPyObjectBorrow(from), name);
PyObject *res_o;
if (PyLazyImport_CheckExact(PyStackRef_AsPyObjectBorrow(from))) {
res_o = _PyEval_LazyImportFrom(
tstate, frame, PyStackRef_AsPyObjectBorrow(from), name);
}
else {
res_o = _PyEval_ImportFrom(
tstate, PyStackRef_AsPyObjectBorrow(from), name);
}
ERROR_IF(res_o == NULL);
res = PyStackRef_FromPyObjectSteal(res_o);
}

View file

@ -2916,11 +2916,13 @@ _PyEval_SliceIndexNotNone(PyObject *v, Py_ssize_t *pi)
}
PyObject *
_PyEval_ImportName(PyThreadState *tstate, _PyInterpreterFrame *frame,
PyObject *name, PyObject *fromlist, PyObject *level)
_PyEval_ImportName(PyThreadState *tstate, PyObject *builtins,
PyObject *globals, PyObject *locals, PyObject *name,
PyObject *fromlist, PyObject *level)
{
PyObject *import_func;
if (PyMapping_GetOptionalItem(frame->f_builtins, &_Py_ID(__import__), &import_func) < 0) {
if (PyMapping_GetOptionalItem(builtins, &_Py_ID(__import__),
&import_func) < 0) {
return NULL;
}
if (import_func == NULL) {
@ -2928,29 +2930,143 @@ _PyEval_ImportName(PyThreadState *tstate, _PyInterpreterFrame *frame,
return NULL;
}
PyObject *locals = frame->f_locals;
PyObject *res = _PyEval_ImportNameWithImport(
tstate, import_func, globals, locals, name, fromlist, level);
Py_DECREF(import_func);
return res;
}
PyObject *
_PyEval_ImportNameWithImport(PyThreadState *tstate, PyObject *import_func,
PyObject *globals, PyObject *locals,
PyObject *name, PyObject *fromlist, PyObject *level)
{
if (locals == NULL) {
locals = Py_None;
}
/* Fast path for not overloaded __import__. */
if (_PyImport_IsDefaultImportFunc(tstate->interp, import_func)) {
Py_DECREF(import_func);
int ilevel = PyLong_AsInt(level);
if (ilevel == -1 && _PyErr_Occurred(tstate)) {
return NULL;
}
return PyImport_ImportModuleLevelObject(
name,
frame->f_globals,
globals,
locals,
fromlist,
ilevel);
}
PyObject* args[5] = {name, frame->f_globals, locals, fromlist, level};
PyObject *args[5] = {name, globals, locals, fromlist, level};
PyObject *res = PyObject_Vectorcall(import_func, args, 5, NULL);
Py_DECREF(import_func);
return res;
}
static int
check_lazy_import_compatibility(PyThreadState *tstate, PyObject *globals,
PyObject *name, PyObject *level)
{
// Check if this module should be imported lazily due to
// the compatibility mode support via __lazy_modules__.
PyObject *lazy_modules = NULL;
PyObject *abs_name = NULL;
int res = -1;
if (globals != NULL &&
PyMapping_GetOptionalItem(globals, &_Py_ID(__lazy_modules__),
&lazy_modules) < 0)
{
return -1;
}
if (lazy_modules == NULL) {
assert(!PyErr_Occurred());
return 0;
}
int ilevel = PyLong_AsInt(level);
if (ilevel == -1 && _PyErr_Occurred(tstate)) {
goto error;
}
abs_name = _PyImport_GetAbsName(tstate, name, globals, ilevel);
if (abs_name == NULL) {
goto error;
}
res = PySequence_Contains(lazy_modules, abs_name);
error:
Py_XDECREF(abs_name);
Py_XDECREF(lazy_modules);
return res;
}
PyObject *
_PyEval_LazyImportName(PyThreadState *tstate, PyObject *builtins,
PyObject *globals, PyObject *locals, PyObject *name,
PyObject *fromlist, PyObject *level, int lazy)
{
PyObject *res = NULL;
// Check if global policy overrides the local syntax
switch (PyImport_GetLazyImportsMode()) {
case PyImport_LAZY_NONE:
lazy = 0;
break;
case PyImport_LAZY_ALL:
lazy = 1;
break;
case PyImport_LAZY_NORMAL:
break;
}
if (!lazy) {
// See if __lazy_modules__ forces this to be lazy.
lazy = check_lazy_import_compatibility(tstate, globals, name, level);
if (lazy < 0) {
return NULL;
}
}
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);
}
PyObject *lazy_import_func;
if (PyMapping_GetOptionalItem(builtins, &_Py_ID(__lazy_import__),
&lazy_import_func) < 0) {
goto error;
}
if (lazy_import_func == NULL) {
assert(!PyErr_Occurred());
_PyErr_SetString(tstate, PyExc_ImportError,
"__lazy_import__ not found");
goto error;
}
if (locals == NULL) {
locals = Py_None;
}
if (_PyImport_IsDefaultLazyImportFunc(tstate->interp, lazy_import_func)) {
int ilevel = PyLong_AsInt(level);
if (ilevel == -1 && PyErr_Occurred()) {
goto error;
}
res = _PyImport_LazyImportModuleLevelObject(
tstate, name, builtins, globals, locals, fromlist, ilevel
);
goto error;
}
PyObject *args[6] = {name, globals, locals, fromlist, level, builtins};
res = PyObject_Vectorcall(lazy_import_func, args, 6, NULL);
error:
Py_XDECREF(lazy_import_func);
return res;
}
@ -3122,6 +3238,64 @@ _PyEval_ImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name)
return NULL;
}
PyObject *
_PyEval_LazyImportFrom(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject *v, PyObject *name)
{
assert(PyLazyImport_CheckExact(v));
assert(name);
assert(PyUnicode_Check(name));
PyObject *ret;
PyLazyImportObject *d = (PyLazyImportObject *)v;
PyObject *mod = PyImport_GetModule(d->lz_from);
if (mod != NULL) {
// Check if the module already has the attribute, if so, resolve it
// eagerly.
if (PyModule_Check(mod)) {
PyObject *mod_dict = PyModule_GetDict(mod);
if (mod_dict != NULL) {
if (PyDict_GetItemRef(mod_dict, name, &ret) < 0) {
Py_DECREF(mod);
return NULL;
}
if (ret != NULL) {
Py_DECREF(mod);
return ret;
}
}
}
Py_DECREF(mod);
}
if (d->lz_attr != NULL) {
if (PyUnicode_Check(d->lz_attr)) {
PyObject *from = PyUnicode_FromFormat(
"%U.%U", d->lz_from, d->lz_attr);
if (from == NULL) {
return NULL;
}
ret = _PyLazyImport_New(frame, d->lz_builtins, from, name);
Py_DECREF(from);
return ret;
}
}
else {
Py_ssize_t dot = PyUnicode_FindChar(
d->lz_from, '.', 0, PyUnicode_GET_LENGTH(d->lz_from), 1
);
if (dot >= 0) {
PyObject *from = PyUnicode_Substring(d->lz_from, 0, dot);
if (from == NULL) {
return NULL;
}
ret = _PyLazyImport_New(frame, d->lz_builtins, from, name);
Py_DECREF(from);
return ret;
}
}
ret = _PyLazyImport_New(frame, d->lz_builtins, d->lz_from, name);
return ret;
}
#define CANNOT_CATCH_MSG "catching classes that do not inherit from "\
"BaseException is not allowed"
@ -3410,6 +3584,24 @@ _PyEval_LoadGlobalStackRef(PyObject *globals, PyObject *builtins, PyObject *name
}
*writeto = PyStackRef_FromPyObjectSteal(res);
}
PyObject *res_o = PyStackRef_AsPyObjectBorrow(*writeto);
if (res_o != NULL && PyLazyImport_CheckExact(res_o)) {
PyObject *l_v = _PyImport_LoadLazyImportTstate(PyThreadState_GET(), res_o);
PyStackRef_CLOSE(writeto[0]);
if (l_v == NULL) {
assert(PyErr_Occurred());
*writeto = PyStackRef_NULL;
return;
}
int err = PyDict_SetItem(globals, name, l_v);
if (err < 0) {
Py_DECREF(l_v);
*writeto = PyStackRef_NULL;
return;
}
*writeto = PyStackRef_FromPyObjectSteal(l_v);
}
}
PyObject *

View file

@ -20,6 +20,7 @@
#include "pycore_interpolation.h" // _PyInterpolation_Build()
#include "pycore_intrinsics.h"
#include "pycore_jit.h"
#include "pycore_lazyimportobject.h"
#include "pycore_list.h" // _PyList_GetItemRef()
#include "pycore_long.h" // _PyLong_GetZero()
#include "pycore_moduleobject.h" // PyModuleObject

View file

@ -113,6 +113,101 @@ exit:
return return_value;
}
PyDoc_STRVAR(builtin___lazy_import____doc__,
"__lazy_import__($module, /, name, globals=None, locals=None,\n"
" fromlist=(), level=0)\n"
"--\n"
"\n"
"Lazily imports a module.\n"
"\n"
"Returns either the module to be imported or a imp.lazy_module object which\n"
"indicates the module to be lazily imported.");
#define BUILTIN___LAZY_IMPORT___METHODDEF \
{"__lazy_import__", _PyCFunction_CAST(builtin___lazy_import__), METH_FASTCALL|METH_KEYWORDS, builtin___lazy_import____doc__},
static PyObject *
builtin___lazy_import___impl(PyObject *module, PyObject *name,
PyObject *globals, PyObject *locals,
PyObject *fromlist, int level);
static PyObject *
builtin___lazy_import__(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 5
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
Py_hash_t ob_hash;
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_hash = -1,
.ob_item = { &_Py_ID(name), &_Py_ID(globals), &_Py_ID(locals), &_Py_ID(fromlist), &_Py_ID(level), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"name", "globals", "locals", "fromlist", "level", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "__lazy_import__",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[5];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
PyObject *name;
PyObject *globals = NULL;
PyObject *locals = NULL;
PyObject *fromlist = NULL;
int level = 0;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
/*minpos*/ 1, /*maxpos*/ 5, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
if (!args) {
goto exit;
}
name = args[0];
if (!noptargs) {
goto skip_optional_pos;
}
if (args[1]) {
globals = args[1];
if (!--noptargs) {
goto skip_optional_pos;
}
}
if (args[2]) {
locals = args[2];
if (!--noptargs) {
goto skip_optional_pos;
}
}
if (args[3]) {
fromlist = args[3];
if (!--noptargs) {
goto skip_optional_pos;
}
}
level = PyLong_AsInt(args[4]);
if (level == -1 && PyErr_Occurred()) {
goto exit;
}
skip_optional_pos:
return_value = builtin___lazy_import___impl(module, name, globals, locals, fromlist, level);
exit:
return return_value;
}
PyDoc_STRVAR(builtin_abs__doc__,
"abs($module, number, /)\n"
"--\n"
@ -1285,4 +1380,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
exit:
return return_value;
}
/*[clinic end generated code: output=06500bcc9a341e68 input=a9049054013a1b77]*/
/*[clinic end generated code: output=1c3327da8885bb8e input=a9049054013a1b77]*/

View file

@ -622,6 +622,41 @@ exit:
return return_value;
}
PyDoc_STRVAR(_imp__set_lazy_attributes__doc__,
"_set_lazy_attributes($module, modobj, name, /)\n"
"--\n"
"\n"
"Sets attributes to lazy submodules on the module, as side effects.");
#define _IMP__SET_LAZY_ATTRIBUTES_METHODDEF \
{"_set_lazy_attributes", _PyCFunction_CAST(_imp__set_lazy_attributes), METH_FASTCALL, _imp__set_lazy_attributes__doc__},
static PyObject *
_imp__set_lazy_attributes_impl(PyObject *module, PyObject *modobj,
PyObject *name);
static PyObject *
_imp__set_lazy_attributes(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
PyObject *modobj;
PyObject *name;
if (!_PyArg_CheckPositional("_set_lazy_attributes", nargs, 2, 2)) {
goto exit;
}
modobj = args[0];
if (!PyUnicode_Check(args[1])) {
_PyArg_BadArgument("_set_lazy_attributes", "argument 2", "str", args[1]);
goto exit;
}
name = args[1];
return_value = _imp__set_lazy_attributes_impl(module, modobj, name);
exit:
return return_value;
}
#ifndef _IMP_CREATE_DYNAMIC_METHODDEF
#define _IMP_CREATE_DYNAMIC_METHODDEF
#endif /* !defined(_IMP_CREATE_DYNAMIC_METHODDEF) */
@ -629,4 +664,4 @@ exit:
#ifndef _IMP_EXEC_DYNAMIC_METHODDEF
#define _IMP_EXEC_DYNAMIC_METHODDEF
#endif /* !defined(_IMP_EXEC_DYNAMIC_METHODDEF) */
/*[clinic end generated code: output=24f597d6b0f3feed input=a9049054013a1b77]*/
/*[clinic end generated code: output=5fa42f580441b3fa input=a9049054013a1b77]*/

View file

@ -1820,6 +1820,180 @@ exit:
return return_value;
}
PyDoc_STRVAR(sys_set_lazy_imports_filter__doc__,
"set_lazy_imports_filter($module, /, filter)\n"
"--\n"
"\n"
"Set the lazy imports filter callback.\n"
"\n"
"The filter is a callable which disables lazy imports when they\n"
"would otherwise be enabled. Returns True if the import is still enabled\n"
"or False to disable it. The callable is called with:\n"
"\n"
"(importing_module_name, imported_module_name, [fromlist])\n"
"\n"
"Pass None to clear the filter.");
#define SYS_SET_LAZY_IMPORTS_FILTER_METHODDEF \
{"set_lazy_imports_filter", _PyCFunction_CAST(sys_set_lazy_imports_filter), METH_FASTCALL|METH_KEYWORDS, sys_set_lazy_imports_filter__doc__},
static PyObject *
sys_set_lazy_imports_filter_impl(PyObject *module, PyObject *filter);
static PyObject *
sys_set_lazy_imports_filter(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 1
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
Py_hash_t ob_hash;
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_hash = -1,
.ob_item = { &_Py_ID(filter), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"filter", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "set_lazy_imports_filter",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[1];
PyObject *filter;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
/*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
if (!args) {
goto exit;
}
filter = args[0];
return_value = sys_set_lazy_imports_filter_impl(module, filter);
exit:
return return_value;
}
PyDoc_STRVAR(sys_get_lazy_imports_filter__doc__,
"get_lazy_imports_filter($module, /)\n"
"--\n"
"\n"
"Get the current lazy imports filter callback.\n"
"\n"
"Returns the filter callable or None if no filter is set.");
#define SYS_GET_LAZY_IMPORTS_FILTER_METHODDEF \
{"get_lazy_imports_filter", (PyCFunction)sys_get_lazy_imports_filter, METH_NOARGS, sys_get_lazy_imports_filter__doc__},
static PyObject *
sys_get_lazy_imports_filter_impl(PyObject *module);
static PyObject *
sys_get_lazy_imports_filter(PyObject *module, PyObject *Py_UNUSED(ignored))
{
return sys_get_lazy_imports_filter_impl(module);
}
PyDoc_STRVAR(sys_set_lazy_imports__doc__,
"set_lazy_imports($module, /, mode)\n"
"--\n"
"\n"
"Sets the global lazy imports mode.\n"
"\n"
"The mode parameter must be one of the following strings:\n"
"- \"all\": All top-level imports become potentially lazy\n"
"- \"none\": All lazy imports are suppressed (even explicitly marked ones)\n"
"- \"normal\": Only explicitly marked imports (with \'lazy\' keyword) are lazy\n"
"\n"
"In addition to the mode, lazy imports can be controlled via the filter\n"
"provided to sys.set_lazy_imports_filter");
#define SYS_SET_LAZY_IMPORTS_METHODDEF \
{"set_lazy_imports", _PyCFunction_CAST(sys_set_lazy_imports), METH_FASTCALL|METH_KEYWORDS, sys_set_lazy_imports__doc__},
static PyObject *
sys_set_lazy_imports_impl(PyObject *module, PyObject *mode);
static PyObject *
sys_set_lazy_imports(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 1
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
Py_hash_t ob_hash;
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_hash = -1,
.ob_item = { &_Py_ID(mode), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"mode", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "set_lazy_imports",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[1];
PyObject *mode;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
/*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
if (!args) {
goto exit;
}
mode = args[0];
return_value = sys_set_lazy_imports_impl(module, mode);
exit:
return return_value;
}
PyDoc_STRVAR(sys_get_lazy_imports__doc__,
"get_lazy_imports($module, /)\n"
"--\n"
"\n"
"Gets the global lazy imports mode.\n"
"\n"
"Returns \"all\" if all top level imports are potentially lazy.\n"
"Returns \"none\" if all explicitly marked lazy imports are suppressed.\n"
"Returns \"normal\" if only explicitly marked imports are lazy.");
#define SYS_GET_LAZY_IMPORTS_METHODDEF \
{"get_lazy_imports", (PyCFunction)sys_get_lazy_imports, METH_NOARGS, sys_get_lazy_imports__doc__},
static PyObject *
sys_get_lazy_imports_impl(PyObject *module);
static PyObject *
sys_get_lazy_imports(PyObject *module, PyObject *Py_UNUSED(ignored))
{
return sys_get_lazy_imports_impl(module);
}
PyDoc_STRVAR(_jit_is_available__doc__,
"is_available($module, /)\n"
"--\n"
@ -1947,4 +2121,4 @@ exit:
#ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
#define SYS_GETANDROIDAPILEVEL_METHODDEF
#endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
/*[clinic end generated code: output=5f7d84c5bf00d557 input=a9049054013a1b77]*/
/*[clinic end generated code: output=adbadb629b98eabf input=a9049054013a1b77]*/

View file

@ -357,8 +357,8 @@ codegen_addop_o(compiler *c, location loc,
#define LOAD_ZERO_SUPER_METHOD -4
static int
codegen_addop_name(compiler *c, location loc,
int opcode, PyObject *dict, PyObject *o)
codegen_addop_name_custom(compiler *c, location loc, int opcode,
PyObject *dict, PyObject *o, int shift, int low)
{
PyObject *mangled = _PyCompile_MaybeMangle(c, o);
if (!mangled) {
@ -369,40 +369,51 @@ codegen_addop_name(compiler *c, location loc,
if (arg < 0) {
return ERROR;
}
ADDOP_I(c, loc, opcode, (arg << shift) | low);
return SUCCESS;
}
static int
codegen_addop_name(compiler *c, location loc,
int opcode, PyObject *dict, PyObject *o)
{
int shift = 0, low = 0;
if (opcode == LOAD_ATTR) {
arg <<= 1;
shift = 1;
}
if (opcode == LOAD_METHOD) {
opcode = LOAD_ATTR;
arg <<= 1;
arg |= 1;
shift = 1;
low = 1;
}
if (opcode == LOAD_SUPER_ATTR) {
arg <<= 2;
arg |= 2;
shift = 2;
low = 2;
}
if (opcode == LOAD_SUPER_METHOD) {
opcode = LOAD_SUPER_ATTR;
arg <<= 2;
arg |= 3;
shift = 2;
low = 3;
}
if (opcode == LOAD_ZERO_SUPER_ATTR) {
opcode = LOAD_SUPER_ATTR;
arg <<= 2;
shift = 2;
}
if (opcode == LOAD_ZERO_SUPER_METHOD) {
opcode = LOAD_SUPER_ATTR;
arg <<= 2;
arg |= 1;
shift = 2;
low = 1;
}
ADDOP_I(c, loc, opcode, arg);
return SUCCESS;
return codegen_addop_name_custom(c, loc, opcode, dict, o, shift, low);
}
#define ADDOP_NAME(C, LOC, OP, O, TYPE) \
RETURN_IF_ERROR(codegen_addop_name((C), (LOC), (OP), METADATA(C)->u_ ## TYPE, (O)))
static int
#define ADDOP_NAME_CUSTOM(C, LOC, OP, O, TYPE, SHIFT, LOW) \
RETURN_IF_ERROR(codegen_addop_name_custom((C), (LOC), (OP), METADATA(C)->u_ ## TYPE, (O), SHIFT, LOW))
static int
codegen_addop_j(instr_sequence *seq, location loc,
int opcode, jump_target_label target)
{
@ -2864,6 +2875,17 @@ 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");
}
return SUCCESS;
}
static int
codegen_import(compiler *c, stmt_ty s)
{
@ -2884,7 +2906,18 @@ codegen_import(compiler *c, stmt_ty s)
ADDOP_LOAD_CONST(c, loc, zero);
ADDOP_LOAD_CONST(c, loc, Py_None);
ADDOP_NAME(c, loc, IMPORT_NAME, alias->name, names);
if (s->v.Import.is_lazy) {
RETURN_IF_ERROR(codegen_validate_lazy_import(c, loc));
ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 1);
} else {
if (_PyCompile_InExceptionHandler(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) {
r = codegen_import_as(c, loc, alias->name, alias->asname);
@ -2930,13 +2963,29 @@ codegen_from_import(compiler *c, stmt_ty s)
ADDOP_LOAD_CONST_NEW(c, LOC(s), names);
identifier from = &_Py_STR(empty);
if (s->v.ImportFrom.module) {
ADDOP_NAME(c, LOC(s), IMPORT_NAME, s->v.ImportFrom.module, names);
from = s->v.ImportFrom.module;
}
else {
_Py_DECLARE_STR(empty, "");
ADDOP_NAME(c, LOC(s), IMPORT_NAME, &_Py_STR(empty), names);
if (s->v.ImportFrom.is_lazy) {
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 {
alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, 0);
if (_PyCompile_InExceptionHandler(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

@ -800,6 +800,26 @@ _PyCompile_TopFBlock(compiler *c)
return &c->u->u_fblock[c->u->u_nfblocks - 1];
}
bool
_PyCompile_InExceptionHandler(compiler *c)
{
for (Py_ssize_t i = 0; i < c->u->u_nfblocks; i++) {
fblockinfo *block = &c->u->u_fblock[i];
switch (block->fb_type) {
case COMPILE_FBLOCK_TRY_EXCEPT:
case COMPILE_FBLOCK_FINALLY_TRY:
case COMPILE_FBLOCK_FINALLY_END:
case COMPILE_FBLOCK_EXCEPTION_HANDLER:
case COMPILE_FBLOCK_EXCEPTION_GROUP_HANDLER:
case COMPILE_FBLOCK_HANDLER_CLEANUP:
return true;
default:
break;
}
}
return false;
}
void
_PyCompile_DeferredAnnotations(compiler *c,
PyObject **deferred_annotations,

View file

@ -7447,6 +7447,32 @@
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
if (PyLazyImport_CheckExact(v_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (l_v == NULL) {
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_DECREF(v_o);
stack_pointer = _PyFrame_GetStackPointer(frame);
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = PyDict_SetItem(GLOBALS(), name, l_v);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (err < 0) {
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_DECREF(v_o);
Py_DECREF(l_v);
stack_pointer = _PyFrame_GetStackPointer(frame);
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_SETREF(v_o, l_v);
stack_pointer = _PyFrame_GetStackPointer(frame);
}
v = PyStackRef_FromPyObjectSteal(v_o);
_tos_cache0 = v;
_tos_cache1 = PyStackRef_ZERO_BITS;
@ -10523,15 +10549,34 @@
oparg = CURRENT_OPARG();
fromlist = _stack_item_1;
level = _stack_item_0;
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
stack_pointer[0] = level;
stack_pointer[1] = fromlist;
stack_pointer += 2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2);
PyObject *res_o;
if (!(oparg & 0x02)) {
stack_pointer[0] = level;
stack_pointer[1] = fromlist;
stack_pointer += 2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
res_o = _PyEval_LazyImportName(tstate, BUILTINS(), GLOBALS(),
LOCALS(), name,
PyStackRef_AsPyObjectBorrow(fromlist),
PyStackRef_AsPyObjectBorrow(level),
oparg & 0x01);
stack_pointer = _PyFrame_GetStackPointer(frame);
}
else {
stack_pointer[0] = level;
stack_pointer[1] = fromlist;
stack_pointer += 2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
res_o = _PyEval_ImportName(tstate, BUILTINS(), GLOBALS(),
LOCALS(), name,
PyStackRef_AsPyObjectBorrow(fromlist),
PyStackRef_AsPyObjectBorrow(level));
stack_pointer = _PyFrame_GetStackPointer(frame);
}
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *res_o = _PyEval_ImportName(tstate, frame, name,
PyStackRef_AsPyObjectBorrow(fromlist),
PyStackRef_AsPyObjectBorrow(level));
_PyStackRef tmp = fromlist;
fromlist = PyStackRef_NULL;
stack_pointer[-1] = fromlist;
@ -10565,13 +10610,27 @@
oparg = CURRENT_OPARG();
from = _stack_item_0;
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
stack_pointer[0] = from;
stack_pointer += 1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *res_o = _PyEval_ImportFrom(tstate, PyStackRef_AsPyObjectBorrow(from), name);
stack_pointer = _PyFrame_GetStackPointer(frame);
PyObject *res_o;
if (PyLazyImport_CheckExact(PyStackRef_AsPyObjectBorrow(from))) {
stack_pointer[0] = from;
stack_pointer += 1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
res_o = _PyEval_LazyImportFrom(
tstate, frame, PyStackRef_AsPyObjectBorrow(from), name);
stack_pointer = _PyFrame_GetStackPointer(frame);
}
else {
stack_pointer[0] = from;
stack_pointer += 1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
res_o = _PyEval_ImportFrom(
tstate, PyStackRef_AsPyObjectBorrow(from), name);
stack_pointer = _PyFrame_GetStackPointer(frame);
}
if (res_o == NULL) {
stack_pointer[-1] = from;
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}

View file

@ -6371,9 +6371,19 @@
_PyStackRef res;
from = stack_pointer[-1];
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *res_o = _PyEval_ImportFrom(tstate, PyStackRef_AsPyObjectBorrow(from), name);
stack_pointer = _PyFrame_GetStackPointer(frame);
PyObject *res_o;
if (PyLazyImport_CheckExact(PyStackRef_AsPyObjectBorrow(from))) {
_PyFrame_SetStackPointer(frame, stack_pointer);
res_o = _PyEval_LazyImportFrom(
tstate, frame, PyStackRef_AsPyObjectBorrow(from), name);
stack_pointer = _PyFrame_GetStackPointer(frame);
}
else {
_PyFrame_SetStackPointer(frame, stack_pointer);
res_o = _PyEval_ImportFrom(
tstate, PyStackRef_AsPyObjectBorrow(from), name);
stack_pointer = _PyFrame_GetStackPointer(frame);
}
if (res_o == NULL) {
JUMP_TO_LABEL(error);
}
@ -6397,11 +6407,26 @@
_PyStackRef res;
fromlist = stack_pointer[-1];
level = stack_pointer[-2];
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2);
PyObject *res_o;
if (!(oparg & 0x02)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
res_o = _PyEval_LazyImportName(tstate, BUILTINS(), GLOBALS(),
LOCALS(), name,
PyStackRef_AsPyObjectBorrow(fromlist),
PyStackRef_AsPyObjectBorrow(level),
oparg & 0x01);
stack_pointer = _PyFrame_GetStackPointer(frame);
}
else {
_PyFrame_SetStackPointer(frame, stack_pointer);
res_o = _PyEval_ImportName(tstate, BUILTINS(), GLOBALS(),
LOCALS(), name,
PyStackRef_AsPyObjectBorrow(fromlist),
PyStackRef_AsPyObjectBorrow(level));
stack_pointer = _PyFrame_GetStackPointer(frame);
}
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *res_o = _PyEval_ImportName(tstate, frame, name,
PyStackRef_AsPyObjectBorrow(fromlist),
PyStackRef_AsPyObjectBorrow(level));
_PyStackRef tmp = fromlist;
fromlist = PyStackRef_NULL;
stack_pointer[-1] = fromlist;
@ -9154,6 +9179,15 @@
}
JUMP_TO_LABEL(error);
}
if (PyLazyImport_CheckExact(v_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o);
Py_SETREF(v_o, l_v);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (v_o == NULL) {
JUMP_TO_LABEL(error);
}
}
}
else {
_PyFrame_SetStackPointer(frame, stack_pointer);
@ -9178,6 +9212,15 @@
JUMP_TO_LABEL(error);
}
}
if (PyLazyImport_CheckExact(v_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o);
Py_SETREF(v_o, l_v);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (v_o == NULL) {
JUMP_TO_LABEL(error);
}
}
}
}
v = PyStackRef_FromPyObjectSteal(v_o);
@ -9430,6 +9473,30 @@
if (v_o == NULL) {
JUMP_TO_LABEL(error);
}
if (PyLazyImport_CheckExact(v_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (l_v == NULL) {
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_DECREF(v_o);
stack_pointer = _PyFrame_GetStackPointer(frame);
JUMP_TO_LABEL(error);
}
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = PyDict_SetItem(GLOBALS(), name, l_v);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (err < 0) {
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_DECREF(v_o);
Py_DECREF(l_v);
stack_pointer = _PyFrame_GetStackPointer(frame);
JUMP_TO_LABEL(error);
}
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_SETREF(v_o, l_v);
stack_pointer = _PyFrame_GetStackPointer(frame);
}
v = PyStackRef_FromPyObjectSteal(v_o);
stack_pointer[0] = v;
stack_pointer += 1;

View file

@ -4,21 +4,28 @@
#include "pycore_audit.h" // _PySys_Audit()
#include "pycore_ceval.h"
#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION()
#include "pycore_dict.h" // _PyDict_Contains_KnownHash()
#include "pycore_hashtable.h" // _Py_hashtable_new_full()
#include "pycore_import.h" // _PyImport_BootstrapImp()
#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_interp.h" // struct _import_runtime_state
#include "pycore_interpframe.h"
#include "pycore_lazyimportobject.h"
#include "pycore_long.h" // _PyLong_GetZero
#include "pycore_magic_number.h" // PYC_MAGIC_NUMBER_TOKEN
#include "pycore_moduleobject.h" // _PyModule_GetDef()
#include "pycore_namespace.h" // _PyNamespace_Type
#include "pycore_object.h" // _Py_SetImmortal()
#include "pycore_pyatomic_ft_wrappers.h"
#include "pycore_pyerrors.h" // _PyErr_SetString()
#include "pycore_pyhash.h" // _Py_KeyedHash()
#include "pycore_pylifecycle.h"
#include "pycore_pymem.h" // _PyMem_DefaultRawFree()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_setobject.h" // _PySet_NextEntry()
#include "pycore_sysmodule.h" // _PySys_ClearAttrString()
#include "pycore_time.h" // _PyTime_AsMicroseconds()
#include "pycore_traceback.h"
#include "pycore_unicodeobject.h" // _PyUnicode_AsUTF8NoNUL()
#include "pycore_weakref.h" // _PyWeakref_GET_REF()
@ -85,6 +92,8 @@ static struct _inittab *inittab_copy = NULL;
(interp)->imports.modules
#define MODULES_BY_INDEX(interp) \
(interp)->imports.modules_by_index
#define LAZY_MODULES(interp) \
(interp)->imports.lazy_modules
#define IMPORTLIB(interp) \
(interp)->imports.importlib
#define OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp) \
@ -98,12 +107,30 @@ static struct _inittab *inittab_copy = NULL;
#define IMPORT_FUNC(interp) \
(interp)->imports.import_func
#define LAZY_IMPORT_FUNC(interp) \
(interp)->imports.lazy_import_func
#define IMPORT_LOCK(interp) \
(interp)->imports.lock
#define FIND_AND_LOAD(interp) \
(interp)->imports.find_and_load
#define LAZY_IMPORTS_MODE(interp) \
(interp)->imports.lazy_imports_mode
#define LAZY_IMPORTS_FILTER(interp) \
(interp)->imports.lazy_imports_filter
#ifdef Py_GIL_DISABLED
#define LAZY_IMPORTS_LOCK(interp) PyMutex_Lock(&(interp)->imports.lazy_mutex)
#define LAZY_IMPORTS_UNLOCK(interp) PyMutex_Unlock(&(interp)->imports.lazy_mutex)
#else
#define LAZY_IMPORTS_LOCK(interp)
#define LAZY_IMPORTS_UNLOCK(interp)
#endif
#define _IMPORT_TIME_HEADER(interp) \
do { \
if (FIND_AND_LOAD((interp)).header) { \
@ -241,6 +268,20 @@ import_get_module(PyThreadState *tstate, PyObject *name)
return m;
}
PyObject *
_PyImport_InitLazyModules(PyInterpreterState *interp)
{
assert(LAZY_MODULES(interp) == NULL);
LAZY_MODULES(interp) = PyDict_New();
return LAZY_MODULES(interp);
}
void
_PyImport_ClearLazyModules(PyInterpreterState *interp)
{
Py_CLEAR(LAZY_MODULES(interp));
}
static int
import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *name)
{
@ -2147,7 +2188,8 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
if (filename == NULL) {
return NULL;
}
} else {
}
else {
filename = Py_NewRef(info->filename);
}
// XXX There's a refleak somewhere with the filename.
@ -3539,6 +3581,13 @@ _PyImport_InitDefaultImportFunc(PyInterpreterState *interp)
return -1;
}
IMPORT_FUNC(interp) = import_func;
// Get the __lazy_import__ function
if (PyDict_GetItemStringRef(interp->builtins, "__lazy_import__",
&import_func) <= 0) {
return -1;
}
LAZY_IMPORT_FUNC(interp) = import_func;
return 0;
}
@ -3548,6 +3597,11 @@ _PyImport_IsDefaultImportFunc(PyInterpreterState *interp, PyObject *func)
return func == IMPORT_FUNC(interp);
}
int
_PyImport_IsDefaultLazyImportFunc(PyInterpreterState *interp, PyObject *func)
{
return func == LAZY_IMPORT_FUNC(interp);
}
/* Import a module, either built-in, frozen, or external, and return
its module object WITH INCREMENTED REFERENCE COUNT */
@ -3811,6 +3865,234 @@ resolve_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level
return NULL;
}
PyObject *
_PyImport_ResolveName(PyThreadState *tstate, PyObject *name,
PyObject *globals, int level)
{
return resolve_name(tstate, name, globals, level);
}
PyObject *
_PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import)
{
PyObject *obj = NULL;
PyObject *fromlist = Py_None;
PyObject *import_func = NULL;
assert(lazy_import != NULL);
assert(PyLazyImport_CheckExact(lazy_import));
PyLazyImportObject *lz = (PyLazyImportObject *)lazy_import;
PyInterpreterState *interp = tstate->interp;
// Acquire the global import lock to serialize reification
_PyImport_AcquireLock(interp);
// Check if we are already importing this module, if so, then we want to
// return an error that indicates we've hit a cycle which will indicate
// the value isn't yet available.
PyObject *importing = interp->imports.lazy_importing_modules;
if (importing == NULL) {
importing = interp->imports.lazy_importing_modules = PySet_New(NULL);
if (importing == NULL) {
_PyImport_ReleaseLock(interp);
return NULL;
}
}
assert(PyAnySet_CheckExact(importing));
int is_loading = _PySet_Contains((PySetObject *)importing, lazy_import);
if (is_loading < 0) {
_PyImport_ReleaseLock(interp);
return NULL;
}
else if (is_loading == 1) {
PyObject *name = _PyLazyImport_GetName(lazy_import);
if (name == NULL) {
_PyImport_ReleaseLock(interp);
return NULL;
}
PyObject *errmsg = PyUnicode_FromFormat(
"cannot import name %R (most likely due to a circular import)",
name);
if (errmsg == NULL) {
Py_DECREF(name);
_PyImport_ReleaseLock(interp);
return NULL;
}
PyErr_SetImportErrorSubclass(PyExc_ImportCycleError, errmsg,
lz->lz_from, NULL);
Py_DECREF(errmsg);
Py_DECREF(name);
_PyImport_ReleaseLock(interp);
return NULL;
}
else if (PySet_Add(importing, lazy_import) < 0) {
_PyImport_ReleaseLock(interp);
goto error;
}
Py_ssize_t dot = -1;
int full = 0;
if (lz->lz_attr != NULL) {
full = 1;
}
if (!full) {
dot = PyUnicode_FindChar(lz->lz_from, '.', 0,
PyUnicode_GET_LENGTH(lz->lz_from), 1);
}
if (dot < 0) {
full = 1;
}
if (lz->lz_attr != NULL) {
if (PyUnicode_Check(lz->lz_attr)) {
fromlist = PyTuple_New(1);
if (fromlist == NULL) {
goto error;
}
Py_INCREF(lz->lz_attr);
PyTuple_SET_ITEM(fromlist, 0, lz->lz_attr);
}
else {
Py_INCREF(lz->lz_attr);
fromlist = lz->lz_attr;
}
}
PyObject *globals = PyEval_GetGlobals();
if (PyMapping_GetOptionalItem(lz->lz_builtins, &_Py_ID(__import__),
&import_func) < 0) {
goto error;
}
if (import_func == NULL) {
PyErr_SetString(PyExc_ImportError, "__import__ not found");
goto error;
}
if (full) {
obj = _PyEval_ImportNameWithImport(
tstate, import_func, globals, globals,
lz->lz_from, fromlist, _PyLong_GetZero()
);
}
else {
PyObject *name = PyUnicode_Substring(lz->lz_from, 0, dot);
if (name == NULL) {
goto error;
}
obj = _PyEval_ImportNameWithImport(
tstate, import_func, globals, globals,
name, fromlist, _PyLong_GetZero()
);
Py_DECREF(name);
}
if (obj == NULL) {
goto error;
}
if (lz->lz_attr != NULL && PyUnicode_Check(lz->lz_attr)) {
PyObject *from = obj;
obj = _PyEval_ImportFrom(tstate, from, lz->lz_attr);
Py_DECREF(from);
if (obj == NULL) {
goto error;
}
}
assert(!PyLazyImport_CheckExact(obj));
goto ok;
error:
Py_CLEAR(obj);
// If an error occurred and we have frame information, add it to the
// exception.
if (PyErr_Occurred() && lz->lz_code != NULL && lz->lz_instr_offset >= 0) {
// Get the current exception - this already has the full traceback
// from the access point.
PyObject *exc = _PyErr_GetRaisedException(tstate);
// Get import name - this can fail and set an exception.
PyObject *import_name = _PyLazyImport_GetName(lazy_import);
if (!import_name) {
// Failed to get import name, just restore original exception.
_PyErr_SetRaisedException(tstate, exc);
goto ok;
}
// Resolve line number from instruction offset on demand.
int lineno = PyCode_Addr2Line((PyCodeObject *)lz->lz_code,
lz->lz_instr_offset*2);
// Get strings - these can return NULL on encoding errors.
const char *filename_str = PyUnicode_AsUTF8(lz->lz_code->co_filename);
if (!filename_str) {
// Unicode conversion failed - clear error and restore original
// exception.
PyErr_Clear();
Py_DECREF(import_name);
_PyErr_SetRaisedException(tstate, exc);
goto ok;
}
const char *funcname_str = PyUnicode_AsUTF8(lz->lz_code->co_name);
if (!funcname_str) {
// Unicode conversion failed - clear error and restore original
// exception.
PyErr_Clear();
Py_DECREF(import_name);
_PyErr_SetRaisedException(tstate, exc);
goto ok;
}
// Create a cause exception showing where the lazy import was declared.
PyObject *msg = PyUnicode_FromFormat(
"deferred import of '%U' raised an exception during resolution",
import_name
);
Py_DECREF(import_name); // Done with import_name.
if (!msg) {
// Failed to create message - restore original exception.
_PyErr_SetRaisedException(tstate, exc);
goto ok;
}
PyObject *cause_exc = PyObject_CallOneArg(PyExc_ImportError, msg);
Py_DECREF(msg); // Done with msg.
if (!cause_exc) {
// Failed to create exception - restore original.
_PyErr_SetRaisedException(tstate, exc);
goto ok;
}
// Add traceback entry for the lazy import declaration.
_PyErr_SetRaisedException(tstate, cause_exc);
_PyTraceback_Add(funcname_str, filename_str, lineno);
PyObject *cause_with_tb = _PyErr_GetRaisedException(tstate);
// Set the cause on the original exception.
PyException_SetCause(exc, cause_with_tb); // Steals ref to cause_with_tb.
// Restore the original exception with its full traceback.
_PyErr_SetRaisedException(tstate, exc);
}
ok:
if (PySet_Discard(importing, lazy_import) < 0) {
Py_CLEAR(obj);
}
// Release the global import lock.
_PyImport_ReleaseLock(interp);
Py_XDECREF(fromlist);
Py_XDECREF(import_func);
return obj;
}
static PyObject *
import_find_and_load(PyThreadState *tstate, PyObject *abs_name)
{
@ -3865,6 +4147,28 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name)
#undef accumulated
}
static PyObject *
get_abs_name(PyThreadState *tstate, PyObject *name, PyObject *globals,
int level)
{
if (level > 0) {
return resolve_name(tstate, name, globals, level);
}
if (PyUnicode_GET_LENGTH(name) == 0) {
_PyErr_SetString(tstate, PyExc_ValueError, "Empty module name");
return NULL;
}
return Py_NewRef(name);
}
PyObject *
_PyImport_GetAbsName(PyThreadState *tstate, PyObject *name,
PyObject *globals, int level)
{
return get_abs_name(tstate, name, globals, level);
}
PyObject *
PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
PyObject *locals, PyObject *fromlist,
@ -3895,17 +4199,9 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
goto error;
}
if (level > 0) {
abs_name = resolve_name(tstate, name, globals, level);
if (abs_name == NULL)
goto error;
}
else { /* level == 0 */
if (PyUnicode_GET_LENGTH(name) == 0) {
_PyErr_SetString(tstate, PyExc_ValueError, "Empty module name");
goto error;
}
abs_name = Py_NewRef(name);
abs_name = get_abs_name(tstate, name, globals, level);
if (abs_name == NULL) {
goto error;
}
mod = import_get_module(tstate, abs_name);
@ -4029,6 +4325,243 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
return final_mod;
}
static PyObject *
get_mod_dict(PyObject *module)
{
if (PyModule_Check(module)) {
return Py_NewRef(_PyModule_GetDict(module));
}
return PyObject_GetAttr(module, &_Py_ID(__dict__));
}
// ensure we have the set for the parent module name in sys.lazy_modules.
// Returns a new reference.
static PyObject *
ensure_lazy_submodules(PyDictObject *lazy_modules, PyObject *parent)
{
PyObject *lazy_submodules;
Py_BEGIN_CRITICAL_SECTION(lazy_modules);
int err = _PyDict_GetItemRef_Unicode_LockHeld(lazy_modules, parent,
&lazy_submodules);
if (err == 0) {
// value isn't present
lazy_submodules = PySet_New(NULL);
if (lazy_submodules != NULL &&
_PyDict_SetItem_LockHeld(lazy_modules, parent,
lazy_submodules) < 0) {
Py_CLEAR(lazy_submodules);
}
}
Py_END_CRITICAL_SECTION();
return lazy_submodules;
}
static int
register_lazy_on_parent(PyThreadState *tstate, PyObject *name,
PyObject *builtins)
{
int ret = -1;
PyObject *parent = NULL;
PyObject *child = NULL;
PyObject *parent_module = NULL;
PyObject *parent_dict = NULL;
PyInterpreterState *interp = tstate->interp;
PyObject *lazy_modules = LAZY_MODULES(interp);
assert(lazy_modules != NULL);
Py_INCREF(name);
while (true) {
Py_ssize_t dot = PyUnicode_FindChar(name, '.', 0,
PyUnicode_GET_LENGTH(name), -1);
if (dot < 0) {
ret = 0;
goto done;
}
parent = PyUnicode_Substring(name, 0, dot);
// If `parent` is NULL then this has hit the end of the import, no
// more "parent.child" in the import name. The entire import will be
// resolved lazily.
if (parent == NULL) {
goto done;
}
Py_XDECREF(child);
child = PyUnicode_Substring(name, dot + 1, PyUnicode_GET_LENGTH(name));
if (child == NULL) {
goto done;
}
// Record the child as being lazily imported from the parent.
PyObject *lazy_submodules = ensure_lazy_submodules(
(PyDictObject *)lazy_modules, parent);
if (lazy_submodules == NULL) {
goto done;
}
if (PySet_Add(lazy_submodules, child) < 0) {
Py_DECREF(lazy_submodules);
goto done;
}
Py_DECREF(lazy_submodules);
// Add the lazy import for the child to the parent.
Py_XSETREF(parent_module, PyImport_GetModule(parent));
if (parent_module != NULL) {
Py_XSETREF(parent_dict, get_mod_dict(parent_module));
if (parent_dict == NULL) {
goto done;
}
if (PyDict_CheckExact(parent_dict)) {
int contains = PyDict_Contains(parent_dict, child);
if (contains < 0) {
goto done;
}
if (!contains) {
PyObject *lazy_module_attr = _PyLazyImport_New(
tstate->current_frame, builtins, parent, child
);
if (lazy_module_attr == NULL) {
goto done;
}
if (PyDict_SetItem(parent_dict, child,
lazy_module_attr) < 0) {
Py_DECREF(lazy_module_attr);
goto done;
}
Py_DECREF(lazy_module_attr);
}
}
ret = 0;
goto done;
}
Py_SETREF(name, parent);
parent = NULL;
}
done:
Py_XDECREF(parent_dict);
Py_XDECREF(parent_module);
Py_XDECREF(child);
Py_XDECREF(parent);
Py_XDECREF(name);
return ret;
}
static int
register_from_lazy_on_parent(PyThreadState *tstate, PyObject *abs_name,
PyObject *from, PyObject *builtins)
{
PyObject *fromname = PyUnicode_FromFormat("%U.%U", abs_name, from);
if (fromname == NULL) {
return -1;
}
int res = register_lazy_on_parent(tstate, fromname, builtins);
Py_DECREF(fromname);
return res;
}
PyObject *
_PyImport_LazyImportModuleLevelObject(PyThreadState *tstate,
PyObject *name, PyObject *builtins,
PyObject *globals, PyObject *locals,
PyObject *fromlist, int level)
{
PyObject *abs_name = get_abs_name(tstate, name, globals, level);
if (abs_name == NULL) {
return NULL;
}
PyInterpreterState *interp = tstate->interp;
_PyInterpreterFrame *frame = _PyEval_GetFrame();
if (frame == NULL || frame->f_globals != frame->f_locals) {
Py_DECREF(abs_name);
PyErr_SetString(PyExc_SyntaxError,
"'lazy import' is only allowed at module level");
return NULL;
}
// Check if the filter disables the lazy import.
// We must hold a reference to the filter while calling it to prevent
// use-after-free if another thread replaces it via
// PyImport_SetLazyImportsFilter.
LAZY_IMPORTS_LOCK(interp);
PyObject *filter = Py_XNewRef(LAZY_IMPORTS_FILTER(interp));
LAZY_IMPORTS_UNLOCK(interp);
if (filter != NULL) {
PyObject *modname;
if (PyDict_GetItemRef(globals, &_Py_ID(__name__), &modname) < 0) {
Py_DECREF(filter);
Py_DECREF(abs_name);
return NULL;
}
if (modname == NULL) {
assert(!PyErr_Occurred());
modname = Py_NewRef(Py_None);
}
PyObject *args[] = {modname, name, fromlist};
PyObject *res = PyObject_Vectorcall(filter, args, 3, NULL);
Py_DECREF(modname);
Py_DECREF(filter);
if (res == NULL) {
Py_DECREF(abs_name);
return NULL;
}
int is_true = PyObject_IsTrue(res);
Py_DECREF(res);
if (is_true < 0) {
Py_DECREF(abs_name);
return NULL;
}
if (!is_true) {
Py_DECREF(abs_name);
return PyImport_ImportModuleLevelObject(
name, globals, locals, fromlist, level
);
}
}
// here, 'filter' is either NULL or is equivalent to a borrowed reference
PyObject *res = _PyLazyImport_New(frame, builtins, abs_name, fromlist);
if (res == NULL) {
Py_DECREF(abs_name);
return NULL;
}
if (fromlist && PyUnicode_Check(fromlist)) {
if (register_from_lazy_on_parent(tstate, abs_name, fromlist,
builtins) < 0) {
goto error;
}
}
else if (fromlist && PyTuple_Check(fromlist) &&
PyTuple_GET_SIZE(fromlist)) {
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(fromlist); i++) {
if (register_from_lazy_on_parent(tstate, abs_name,
PyTuple_GET_ITEM(fromlist, i),
builtins) < 0)
{
goto error;
}
}
}
else if (register_lazy_on_parent(tstate, abs_name, builtins) < 0) {
goto error;
}
Py_DECREF(abs_name);
return res;
error:
Py_DECREF(abs_name);
Py_DECREF(res);
return NULL;
}
PyObject *
PyImport_ImportModuleLevel(const char *name, PyObject *globals, PyObject *locals,
PyObject *fromlist, int level)
@ -4235,6 +4768,10 @@ _PyImport_ClearCore(PyInterpreterState *interp)
Py_CLEAR(MODULES_BY_INDEX(interp));
Py_CLEAR(IMPORTLIB(interp));
Py_CLEAR(IMPORT_FUNC(interp));
Py_CLEAR(LAZY_IMPORT_FUNC(interp));
Py_CLEAR(interp->imports.lazy_modules);
Py_CLEAR(interp->imports.lazy_importing_modules);
Py_CLEAR(interp->imports.lazy_imports_filter);
}
void
@ -4370,6 +4907,58 @@ PyImport_ImportModuleAttrString(const char *modname, const char *attrname)
return result;
}
int
PyImport_SetLazyImportsFilter(PyObject *filter)
{
if (filter == Py_None) {
filter = NULL;
}
if (filter != NULL && !PyCallable_Check(filter)) {
PyErr_SetString(PyExc_ValueError,
"filter provided but is not callable");
return -1;
}
PyInterpreterState *interp = _PyInterpreterState_GET();
// Exchange the filter w/ the lock held. We can't use Py_XSETREF
// because we need to release the lock before the decref.
LAZY_IMPORTS_LOCK(interp);
PyObject *old = LAZY_IMPORTS_FILTER(interp);
LAZY_IMPORTS_FILTER(interp) = Py_XNewRef(filter);
LAZY_IMPORTS_UNLOCK(interp);
Py_XDECREF(old);
return 0;
}
/* Return a strong reference to the current lazy imports filter
* or NULL if none exists. This function always succeeds.
*/
PyObject *
PyImport_GetLazyImportsFilter(void)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
LAZY_IMPORTS_LOCK(interp);
PyObject *res = Py_XNewRef(LAZY_IMPORTS_FILTER(interp));
LAZY_IMPORTS_UNLOCK(interp);
return res;
}
int
PyImport_SetLazyImportsMode(PyImport_LazyImportsMode mode)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
FT_ATOMIC_STORE_INT_RELAXED(LAZY_IMPORTS_MODE(interp), mode);
return 0;
}
/* Checks if lazy imports is globally enabled or disabled. Return 1 when
* globally forced on, 0 when globally forced off, or -1 when not set.*/
PyImport_LazyImportsMode
PyImport_GetLazyImportsMode(void)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
return FT_ATOMIC_LOAD_INT_RELAXED(LAZY_IMPORTS_MODE(interp));
}
/**************/
/* the module */
@ -4969,6 +5558,96 @@ _imp_source_hash_impl(PyObject *module, long key, Py_buffer *source)
return PyBytes_FromStringAndSize(hash.data, sizeof(hash.data));
}
static int
publish_lazy_imports_on_module(PyThreadState *tstate,
PyObject *lazy_submodules,
PyObject *name,
PyObject *module_dict)
{
PyObject *builtins = _PyEval_GetBuiltins(tstate);
PyObject *attr_name;
Py_ssize_t pos = 0;
Py_hash_t hash;
// Enumerate the set of lazy submodules which have been imported from the
// parent module.
while (_PySet_NextEntryRef(lazy_submodules, &pos, &attr_name, &hash)) {
if (_PyDict_Contains_KnownHash(module_dict, attr_name, hash)) {
Py_DECREF(attr_name);
continue;
}
// Create a new lazy module attr for the subpackage which was
// previously lazily imported.
PyObject *lazy_module_attr = _PyLazyImport_New(tstate->current_frame, builtins,
name, attr_name);
if (lazy_module_attr == NULL) {
Py_DECREF(attr_name);
return -1;
}
// Publish on the module that was just imported.
if (PyDict_SetItem(module_dict, attr_name,
lazy_module_attr) < 0) {
Py_DECREF(lazy_module_attr);
Py_DECREF(attr_name);
return -1;
}
Py_DECREF(lazy_module_attr);
Py_DECREF(attr_name);
}
return 0;
}
/*[clinic input]
_imp._set_lazy_attributes
modobj: object
name: unicode
/
Sets attributes to lazy submodules on the module, as side effects.
[clinic start generated code]*/
static PyObject *
_imp__set_lazy_attributes_impl(PyObject *module, PyObject *modobj,
PyObject *name)
/*[clinic end generated code: output=3369bb3242b1f043 input=38ea6f30956dd7d6]*/
{
PyThreadState *tstate = _PyThreadState_GET();
PyObject *module_dict = NULL;
PyObject *ret = NULL;
PyObject *lazy_modules = LAZY_MODULES(tstate->interp);
assert(lazy_modules != NULL);
PyObject *lazy_submodules;
if (PyDict_GetItemRef(lazy_modules, name, &lazy_submodules) < 0) {
return NULL;
}
else if (lazy_submodules == NULL) {
Py_RETURN_NONE;
}
module_dict = get_mod_dict(modobj);
if (module_dict == NULL || !PyDict_CheckExact(module_dict)) {
goto done;
}
assert(PyAnySet_CheckExact(lazy_submodules));
Py_BEGIN_CRITICAL_SECTION(lazy_submodules);
publish_lazy_imports_on_module(tstate, lazy_submodules, name, module_dict);
Py_END_CRITICAL_SECTION();
Py_DECREF(lazy_submodules);
// once a module is imported it is removed from sys.lazy_modules
if (PyDict_DelItem(lazy_modules, name) < 0) {
goto error;
}
done:
ret = Py_NewRef(Py_None);
error:
Py_XDECREF(module_dict);
return ret;
}
PyDoc_STRVAR(doc_imp,
"(Extremely) low-level import machinery bits as used by importlib.");
@ -4993,6 +5672,7 @@ static PyMethodDef imp_methods[] = {
_IMP_EXEC_BUILTIN_METHODDEF
_IMP__FIX_CO_FILENAME_METHODDEF
_IMP_SOURCE_HASH_METHODDEF
_IMP__SET_LAZY_ATTRIBUTES_METHODDEF
{NULL, NULL} /* sentinel */
};

View file

@ -111,6 +111,7 @@ static const PyConfigSpec PYCONFIG_SPEC[] = {
SPEC(base_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("base_prefix")),
SPEC(bytes_warning, UINT, PUBLIC, SYS_FLAG(9)),
SPEC(cpu_count, INT, PUBLIC, NO_SYS),
SPEC(lazy_imports, INT, PUBLIC, NO_SYS),
SPEC(exec_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("exec_prefix")),
SPEC(executable, WSTR_OPT, PUBLIC, SYS_ATTR("executable")),
SPEC(inspect, BOOL, PUBLIC, SYS_FLAG(1)),
@ -318,6 +319,8 @@ The following implementation-specific options are available:\n\
"\
-X importtime[=2]: show how long each import takes; use -X importtime=2 to\n\
log imports of already-loaded modules; also PYTHONPROFILEIMPORTTIME\n\
-X lazy_imports=[all|none|normal]: control global lazy imports;\n\
default is normal; also PYTHON_LAZY_IMPORTS\n\
-X int_max_str_digits=N: limit the size of int<->str conversions;\n\
0 disables the limit; also PYTHONINTMAXSTRDIGITS\n\
-X no_debug_ranges: don't include extra location information in code objects;\n\
@ -432,6 +435,7 @@ static const char usage_envvars[] =
"PYTHON_PRESITE: import this module before site (-X presite)\n"
#endif
"PYTHONPROFILEIMPORTTIME: show how long each import takes (-X importtime)\n"
"PYTHON_LAZY_IMPORTS: control global lazy imports (-X lazy_imports)\n"
"PYTHONPYCACHEPREFIX: root directory for bytecode cache (pyc) files\n"
" (-X pycache_prefix)\n"
"PYTHONSAFEPATH : don't prepend a potentially unsafe path to sys.path.\n"
@ -941,6 +945,8 @@ config_check_consistency(const PyConfig *config)
assert(config->int_max_str_digits >= 0);
// cpu_count can be -1 if the user doesn't override it.
assert(config->cpu_count != 0);
// lazy_imports can be -1 (default), 0 (off), or 1 (on).
assert(config->lazy_imports >= -1 && config->lazy_imports <= 1);
// config->use_frozen_modules is initialized later
// by _PyConfig_InitImportConfig().
assert(config->thread_inherit_context >= 0);
@ -1052,6 +1058,7 @@ _PyConfig_InitCompatConfig(PyConfig *config)
config->_is_python_build = 0;
config->code_debug_ranges = 1;
config->cpu_count = -1;
config->lazy_imports = -1;
#ifdef Py_GIL_DISABLED
config->thread_inherit_context = 1;
config->context_aware_warnings = 1;
@ -2300,6 +2307,49 @@ config_init_import_time(PyConfig *config)
return _PyStatus_OK();
}
static PyStatus
config_init_lazy_imports(PyConfig *config)
{
int lazy_imports = -1;
const char *env = config_get_env(config, "PYTHON_LAZY_IMPORTS");
if (env) {
if (strcmp(env, "all") == 0) {
lazy_imports = 1;
}
else if (strcmp(env, "none") == 0) {
lazy_imports = 0;
}
else if (strcmp(env, "normal") == 0) {
lazy_imports = -1;
}
else {
return _PyStatus_ERR("PYTHON_LAZY_IMPORTS: invalid value; "
"expected 'all', 'none', or 'normal'");
}
config->lazy_imports = lazy_imports;
}
const wchar_t *x_value = config_get_xoption_value(config, L"lazy_imports");
if (x_value) {
if (wcscmp(x_value, L"all") == 0) {
lazy_imports = 1;
}
else if (wcscmp(x_value, L"none") == 0) {
lazy_imports = 0;
}
else if (wcscmp(x_value, L"normal") == 0) {
lazy_imports = -1;
}
else {
return _PyStatus_ERR("-X lazy_imports: invalid value; "
"expected 'all', 'none', or 'normal'");
}
config->lazy_imports = lazy_imports;
}
return _PyStatus_OK();
}
static PyStatus
config_read_complex_options(PyConfig *config)
{
@ -2323,6 +2373,13 @@ config_read_complex_options(PyConfig *config)
}
}
if (config->lazy_imports < 0) {
status = config_init_lazy_imports(config);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
}
if (config->tracemalloc < 0) {
status = config_init_tracemalloc(config);
if (_PyStatus_EXCEPTION(status)) {
@ -2712,6 +2769,9 @@ config_read(PyConfig *config, int compute_path_config)
if (config->tracemalloc < 0) {
config->tracemalloc = 0;
}
if (config->lazy_imports < 0) {
config->lazy_imports = -1; // Default is auto/unset
}
if (config->perf_profiling < 0) {
config->perf_profiling = 0;
}

View file

@ -11,9 +11,11 @@
#include "pycore_floatobject.h"
#include "pycore_frame.h"
#include "pycore_function.h"
#include "pycore_import.h"
#include "pycore_interpframe.h"
#include "pycore_interpolation.h"
#include "pycore_intrinsics.h"
#include "pycore_lazyimportobject.h"
#include "pycore_list.h"
#include "pycore_long.h"
#include "pycore_mmap.h"

View file

@ -15,6 +15,7 @@
#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_interpolation.h" // _PyInterpolation_InitTypes()
#include "pycore_long.h" // _PyLong_InitTypes()
#include "pycore_moduleobject.h" // _PyModule_InitModuleDictWatcher()
#include "pycore_object.h" // _PyDebug_PrintTotalRefs()
#include "pycore_obmalloc.h" // _PyMem_init_obmalloc()
#include "pycore_optimizer.h" // _Py_Executors_InvalidateAll
@ -632,6 +633,11 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
_PyInterpreterState_SetWhence(interp, _PyInterpreterState_WHENCE_RUNTIME);
interp->_ready = 1;
/* Initialize the module dict watcher early, before any modules are created */
if (_PyModule_InitModuleDictWatcher(interp) != 0) {
return _PyStatus_ERR("failed to initialize module dict watcher");
}
status = _PyConfig_Copy(&interp->config, src_config);
if (_PyStatus_EXCEPTION(status)) {
return status;
@ -1335,6 +1341,22 @@ init_interp_main(PyThreadState *tstate)
}
}
// Initialize lazy imports based on configuration. Do this after site
// module is imported to avoid circular imports during startup.
if (config->lazy_imports != -1) {
PyImport_LazyImportsMode lazy_mode;
if (config->lazy_imports == 1) {
lazy_mode = PyImport_LAZY_ALL;
}
else {
lazy_mode = PyImport_LAZY_NONE;
}
if (PyImport_SetLazyImportsMode(lazy_mode) < 0) {
return _PyStatus_ERR("failed to set lazy imports mode");
}
}
// If config->lazy_imports == -1, use the default mode, no change needed.
if (is_main_interp) {
#ifndef MS_WINDOWS
emit_stderr_warning_for_legacy_locale(interp->runtime);
@ -1802,6 +1824,9 @@ finalize_modules(PyThreadState *tstate)
// initialization API)
_PyImport_ClearModulesByIndex(interp);
// Clear the dict of lazily loaded module nname to submodule names
_PyImport_ClearLazyModules(interp);
// Clear and delete the modules directory. Actual modules will
// still be there only if imported during the execution of some
// destructor.
@ -2449,6 +2474,11 @@ new_interpreter(PyThreadState **tstate_p,
_PyInterpreterState_SetWhence(interp, whence);
interp->_ready = 1;
/* Initialize the module dict watcher early, before any modules are created */
if (_PyModule_InitModuleDictWatcher(interp) != 0) {
goto error;
}
/* From this point until the init_interp_create_gil() call,
we must not do anything that requires that the GIL be held
(or otherwise exist). That applies whether or not the new

View file

@ -8,6 +8,7 @@
#include "pycore_dict.h" // DICT_KEYS_UNICODE
#include "pycore_function.h" // _PyFunction_GetVersionForCurrentState()
#include "pycore_interpframe.h" // FRAME_SPECIALS_SIZE
#include "pycore_lazyimportobject.h" // PyLazyImport_CheckExact
#include "pycore_list.h" // _PyListIterObject
#include "pycore_long.h" // _PyLong_IsNonNegativeCompact()
#include "pycore_moduleobject.h"
@ -129,6 +130,7 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters
#define SPEC_FAIL_ATTR_BUILTIN_CLASS_METHOD 22
#define SPEC_FAIL_ATTR_CLASS_METHOD_OBJ 23
#define SPEC_FAIL_ATTR_OBJECT_SLOT 24
#define SPEC_FAIL_ATTR_MODULE_LAZY_VALUE 25
#define SPEC_FAIL_ATTR_INSTANCE_ATTRIBUTE 26
#define SPEC_FAIL_ATTR_METACLASS_ATTRIBUTE 27
@ -383,6 +385,10 @@ specialize_module_load_attr_lock_held(PyDictObject *dict, _Py_CODEUNIT *instr, P
}
PyObject *value;
Py_ssize_t index = _PyDict_LookupIndexAndValue(dict, name, &value);
if (value != NULL && PyLazyImport_CheckExact(value)) {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_MODULE_LAZY_VALUE);
return -1;
}
assert(index != DKIX_ERROR);
if (index != (uint16_t)index) {
SPECIALIZATION_FAIL(LOAD_ATTR,
@ -1307,16 +1313,17 @@ specialize_load_global_lock_held(
SPECIALIZATION_FAIL(LOAD_GLOBAL, SPEC_FAIL_LOAD_GLOBAL_NON_STRING_OR_SPLIT);
goto fail;
}
#ifdef Py_GIL_DISABLED
PyObject *value;
Py_ssize_t index = _PyDict_LookupIndexAndValue((PyDictObject *)globals, name, &value);
#else
Py_ssize_t index = _PyDictKeys_StringLookup(globals_keys, name);
#endif
if (index == DKIX_ERROR) {
SPECIALIZATION_FAIL(LOAD_GLOBAL, SPEC_FAIL_EXPECTED_ERROR);
goto fail;
}
if (value != NULL && PyLazyImport_CheckExact(value)) {
SPECIALIZATION_FAIL(LOAD_GLOBAL, SPEC_FAIL_ATTR_MODULE_LAZY_VALUE);
Py_DECREF(value);
goto fail;
}
PyInterpreterState *interp = _PyInterpreterState_GET();
if (index != DKIX_EMPTY) {
if (index != (uint16_t)index) {

View file

@ -141,6 +141,7 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block,
ste->ste_needs_classdict = 0;
ste->ste_has_conditional_annotations = 0;
ste->ste_in_conditional_block = 0;
ste->ste_in_try_block = 0;
ste->ste_in_unevaluated_annotation = 0;
ste->ste_annotation_block = NULL;
@ -1747,6 +1748,13 @@ symtable_enter_type_param_block(struct symtable *st, identifier name,
#define LEAVE_CONDITIONAL_BLOCK(ST) \
(ST)->st_cur->ste_in_conditional_block = in_conditional_block;
#define ENTER_TRY_BLOCK(ST) \
int in_try_block = (ST)->st_cur->ste_in_try_block; \
(ST)->st_cur->ste_in_try_block = 1;
#define LEAVE_TRY_BLOCK(ST) \
(ST)->st_cur->ste_in_try_block = in_try_block;
#define ENTER_RECURSIVE() \
if (Py_EnterRecursiveCall(" during compilation")) { \
return 0; \
@ -1808,6 +1816,38 @@ check_import_from(struct symtable *st, stmt_ty s)
return 1;
}
static int
check_lazy_import_context(struct symtable *st, stmt_ty s,
const char* import_type)
{
// Check if inside try/except block.
if (st->st_cur->ste_in_try_block) {
PyErr_Format(PyExc_SyntaxError,
"lazy %s not allowed inside try/except blocks",
import_type);
SET_ERROR_LOCATION(st->st_filename, LOCATION(s));
return 0;
}
// Check if inside function scope.
if (st->st_cur->ste_type == FunctionBlock) {
PyErr_Format(PyExc_SyntaxError,
"lazy %s not allowed inside functions", import_type);
SET_ERROR_LOCATION(st->st_filename, LOCATION(s));
return 0;
}
// Check if inside class scope.
if (st->st_cur->ste_type == ClassBlock) {
PyErr_Format(PyExc_SyntaxError,
"lazy %s not allowed inside classes", import_type);
SET_ERROR_LOCATION(st->st_filename, LOCATION(s));
return 0;
}
return 1;
}
static bool
allows_top_level_await(struct symtable *st)
{
@ -2076,19 +2116,23 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
break;
case Try_kind: {
ENTER_CONDITIONAL_BLOCK(st);
ENTER_TRY_BLOCK(st);
VISIT_SEQ(st, stmt, s->v.Try.body);
VISIT_SEQ(st, excepthandler, s->v.Try.handlers);
VISIT_SEQ(st, stmt, s->v.Try.orelse);
VISIT_SEQ(st, stmt, s->v.Try.finalbody);
LEAVE_TRY_BLOCK(st);
LEAVE_CONDITIONAL_BLOCK(st);
break;
}
case TryStar_kind: {
ENTER_CONDITIONAL_BLOCK(st);
ENTER_TRY_BLOCK(st);
VISIT_SEQ(st, stmt, s->v.TryStar.body);
VISIT_SEQ(st, excepthandler, s->v.TryStar.handlers);
VISIT_SEQ(st, stmt, s->v.TryStar.orelse);
VISIT_SEQ(st, stmt, s->v.TryStar.finalbody);
LEAVE_TRY_BLOCK(st);
LEAVE_CONDITIONAL_BLOCK(st);
break;
}
@ -2098,9 +2142,33 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
VISIT(st, expr, s->v.Assert.msg);
break;
case Import_kind:
if (s->v.Import.is_lazy) {
if (!check_lazy_import_context(st, s, "import")) {
return 0;
}
}
VISIT_SEQ(st, alias, s->v.Import.names);
break;
case ImportFrom_kind:
if (s->v.ImportFrom.is_lazy) {
if (!check_lazy_import_context(st, s, "from ... import")) {
return 0;
}
// Check for import *
for (Py_ssize_t i = 0; i < asdl_seq_LEN(s->v.ImportFrom.names);
i++) {
alias_ty alias = (alias_ty)asdl_seq_GET(
s->v.ImportFrom.names, i);
if (alias->name &&
_PyUnicode_EqualToASCIIString(alias->name, "*")) {
PyErr_SetString(PyExc_SyntaxError,
"lazy from ... import * is not allowed");
SET_ERROR_LOCATION(st->st_filename, LOCATION(s));
return 0;
}
}
}
VISIT_SEQ(st, alias, s->v.ImportFrom.names);
if (!check_import_from(st, s)) {
return 0;

View file

@ -2785,6 +2785,128 @@ PyAPI_FUNC(int) PyUnstable_CopyPerfMapFile(const char* parent_filename) {
return 0;
}
/*[clinic input]
sys.set_lazy_imports_filter
filter: object
Set the lazy imports filter callback.
The filter is a callable which disables lazy imports when they
would otherwise be enabled. Returns True if the import is still enabled
or False to disable it. The callable is called with:
(importing_module_name, imported_module_name, [fromlist])
Pass None to clear the filter.
[clinic start generated code]*/
static PyObject *
sys_set_lazy_imports_filter_impl(PyObject *module, PyObject *filter)
/*[clinic end generated code: output=10251d49469c278c input=2eb48786bdd4ee42]*/
{
if (PyImport_SetLazyImportsFilter(filter) < 0) {
return NULL;
}
Py_RETURN_NONE;
}
/*[clinic input]
sys.get_lazy_imports_filter
Get the current lazy imports filter callback.
Returns the filter callable or None if no filter is set.
[clinic start generated code]*/
static PyObject *
sys_get_lazy_imports_filter_impl(PyObject *module)
/*[clinic end generated code: output=3bf73022892165af input=cf1e07cb8e203c94]*/
{
PyObject *filter = PyImport_GetLazyImportsFilter();
if (filter == NULL) {
assert(!PyErr_Occurred());
Py_RETURN_NONE;
}
return filter;
}
/*[clinic input]
sys.set_lazy_imports
mode: object
Sets the global lazy imports mode.
The mode parameter must be one of the following strings:
- "all": All top-level imports become potentially lazy
- "none": All lazy imports are suppressed (even explicitly marked ones)
- "normal": Only explicitly marked imports (with 'lazy' keyword) are lazy
In addition to the mode, lazy imports can be controlled via the filter
provided to sys.set_lazy_imports_filter
[clinic start generated code]*/
static PyObject *
sys_set_lazy_imports_impl(PyObject *module, PyObject *mode)
/*[clinic end generated code: output=1ff34ba6c4feaf73 input=f04e70d8bf9fe4f6]*/
{
PyImport_LazyImportsMode lazy_mode;
if (!PyUnicode_Check(mode)) {
PyErr_SetString(PyExc_TypeError,
"mode must be a string: 'normal', 'all', or 'none'");
return NULL;
}
if (PyUnicode_CompareWithASCIIString(mode, "normal") == 0) {
lazy_mode = PyImport_LAZY_NORMAL;
}
else if (PyUnicode_CompareWithASCIIString(mode, "all") == 0) {
lazy_mode = PyImport_LAZY_ALL;
}
else if (PyUnicode_CompareWithASCIIString(mode, "none") == 0) {
lazy_mode = PyImport_LAZY_NONE;
}
else {
PyErr_SetString(PyExc_ValueError,
"mode must be 'normal', 'all', or 'none'");
return NULL;
}
if (PyImport_SetLazyImportsMode(lazy_mode)) {
return NULL;
}
Py_RETURN_NONE;
}
/*[clinic input]
sys.get_lazy_imports
Gets the global lazy imports mode.
Returns "all" if all top level imports are potentially lazy.
Returns "none" if all explicitly marked lazy imports are suppressed.
Returns "normal" if only explicitly marked imports are lazy.
[clinic start generated code]*/
static PyObject *
sys_get_lazy_imports_impl(PyObject *module)
/*[clinic end generated code: output=4147dec48c51ae99 input=8cb574f1e4e3003c]*/
{
switch (PyImport_GetLazyImportsMode()) {
case PyImport_LAZY_NORMAL:
return PyUnicode_FromString("normal");
case PyImport_LAZY_ALL:
return PyUnicode_FromString("all");
case PyImport_LAZY_NONE:
return PyUnicode_FromString("none");
default:
PyErr_SetString(PyExc_RuntimeError, "unknown lazy imports mode");
return NULL;
}
}
static PyMethodDef sys_methods[] = {
/* Might as well keep this in alphabetic order */
@ -2850,6 +2972,10 @@ static PyMethodDef sys_methods[] = {
SYS_UNRAISABLEHOOK_METHODDEF
SYS_GET_INT_MAX_STR_DIGITS_METHODDEF
SYS_SET_INT_MAX_STR_DIGITS_METHODDEF
SYS_GET_LAZY_IMPORTS_METHODDEF
SYS_SET_LAZY_IMPORTS_METHODDEF
SYS_GET_LAZY_IMPORTS_FILTER_METHODDEF
SYS_SET_LAZY_IMPORTS_FILTER_METHODDEF
SYS__BASEREPL_METHODDEF
#ifdef Py_STATS
SYS__STATS_ON_METHODDEF
@ -3374,6 +3500,7 @@ static PyStructSequence_Field flags_fields[] = {
{"gil", "-X gil"},
{"thread_inherit_context", "-X thread_inherit_context"},
{"context_aware_warnings", "-X context_aware_warnings"},
{"lazy_imports", "-X lazy_imports"},
{0}
};
@ -3383,7 +3510,7 @@ static PyStructSequence_Desc flags_desc = {
"sys.flags", /* name */
flags__doc__, /* doc */
flags_fields, /* fields */
18
19
};
static void
@ -3476,6 +3603,7 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags)
#endif
SetFlag(config->thread_inherit_context);
SetFlag(config->context_aware_warnings);
SetFlag(config->lazy_imports);
#undef SetFlagObj
#undef SetFlag
return 0;
@ -4203,6 +4331,15 @@ _PySys_Create(PyThreadState *tstate, PyObject **sysmod_p)
goto error;
}
PyObject *lazy_modules = _PyImport_InitLazyModules(interp); // borrowed reference
if (lazy_modules == NULL) {
goto error;
}
if (PyDict_SetItemString(sysdict, "lazy_modules", lazy_modules) < 0) {
goto error;
}
PyStatus status = _PySys_SetPreliminaryStderr(sysdict);
if (_PyStatus_EXCEPTION(status)) {
return status;