mirror of
https://github.com/python/cpython.git
synced 2026-02-21 22:50:55 +00:00
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:
parent
cac0c98450
commit
46d5106cfa
138 changed files with 5126 additions and 197 deletions
113
Python/Python-ast.c
generated
113
Python/Python-ast.c
generated
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
208
Python/ceval.c
208
Python/ceval.c
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
97
Python/clinic/bltinmodule.c.h
generated
97
Python/clinic/bltinmodule.c.h
generated
|
|
@ -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]*/
|
||||
|
|
|
|||
37
Python/clinic/import.c.h
generated
37
Python/clinic/import.c.h
generated
|
|
@ -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]*/
|
||||
|
|
|
|||
176
Python/clinic/sysmodule.c.h
generated
176
Python/clinic/sysmodule.c.h
generated
|
|
@ -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]*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
87
Python/executor_cases.c.h
generated
87
Python/executor_cases.c.h
generated
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
81
Python/generated_cases.c.h
generated
81
Python/generated_cases.c.h
generated
|
|
@ -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;
|
||||
|
|
|
|||
704
Python/import.c
704
Python/import.c
|
|
@ -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 */
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue