diff --git a/Grammar/python.gram b/Grammar/python.gram index b9ecd2273a5cae..7511abb7382814 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -119,11 +119,12 @@ simple_stmts[asdl_stmt_seq*]: # NOTE: assignment MUST precede expression, else parsing a simple assignment # will throw a SyntaxError. simple_stmt[stmt_ty] (memo): + | &("lazy" | 'import') import_stmt | assignment | &"type" type_alias | e=star_expressions { _PyAST_Expr(e, EXTRA) } | &'return' return_stmt - | &('import' | 'from') import_stmt + | &('import' | 'from' | "lazy" ) import_stmt | &'raise' raise_stmt | &'pass' pass_stmt | &'del' del_stmt @@ -216,21 +217,25 @@ assert_stmt[stmt_ty]: | invalid_assert_stmt | 'assert' a=expression b=[',' z=expression { z }] { _PyAST_Assert(a, b, EXTRA) } -import_stmt[stmt_ty]: - | invalid_import +import_stmt[stmt_ty](memo): | import_name | import_from # Import statements # ----------------- -import_name[stmt_ty]: 'import' a=dotted_as_names { _PyAST_Import(a, EXTRA) } +import_name[stmt_ty]: + | 'import' a=dotted_as_names { _PyAST_Import(a, 0, EXTRA) } + | "lazy" 'import' a=dotted_as_names { _PyAST_Import(a, 1, EXTRA) } + # note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS import_from[stmt_ty]: | 'from' a=('.' | '...')* b=dotted_name 'import' c=import_from_targets { _PyPegen_checked_future_import(p, b->v.Name.id, c, _PyPegen_seq_count_dots(a), EXTRA) } | 'from' a=('.' | '...')+ 'import' b=import_from_targets { - _PyAST_ImportFrom(NULL, b, _PyPegen_seq_count_dots(a), EXTRA) } + _PyAST_ImportFrom(NULL, b, _PyPegen_seq_count_dots(a), 0, EXTRA) } + | 'from' a=('.' | '...')+ "lazy" 'import' b=import_from_targets { + _PyAST_ImportFrom(NULL, b, _PyPegen_seq_count_dots(a), 1, EXTRA) } import_from_targets[asdl_alias_seq*]: | '(' a=import_from_as_names [','] ')' { a } | import_from_as_names !',' diff --git a/Include/internal/pycore_ast.h b/Include/internal/pycore_ast.h index 60367202bab637..b47398669bbe51 100644 --- a/Include/internal/pycore_ast.h +++ b/Include/internal/pycore_ast.h @@ -329,12 +329,14 @@ struct _stmt { struct { asdl_alias_seq *names; + int is_lazy; } Import; struct { identifier module; asdl_alias_seq *names; int level; + int is_lazy; } ImportFrom; struct { @@ -764,11 +766,12 @@ stmt_ty _PyAST_TryStar(asdl_stmt_seq * body, asdl_excepthandler_seq * handlers, end_col_offset, PyArena *arena); stmt_ty _PyAST_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset, int end_lineno, int end_col_offset, PyArena *arena); -stmt_ty _PyAST_Import(asdl_alias_seq * names, int lineno, int col_offset, int - end_lineno, int end_col_offset, PyArena *arena); +stmt_ty _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 _PyAST_ImportFrom(identifier module, asdl_alias_seq * names, int level, - int lineno, int col_offset, int end_lineno, int - end_col_offset, PyArena *arena); + int is_lazy, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); stmt_ty _PyAST_Global(asdl_identifier_seq * names, int lineno, int col_offset, int end_lineno, int end_col_offset, PyArena *arena); stmt_ty _PyAST_Nonlocal(asdl_identifier_seq * names, int lineno, int diff --git a/Include/internal/pycore_ast_state.h b/Include/internal/pycore_ast_state.h index d4ac419f51d6b2..1caf200ee34b2a 100644 --- a/Include/internal/pycore_ast_state.h +++ b/Include/internal/pycore_ast_state.h @@ -205,6 +205,7 @@ struct ast_state { PyObject *id; PyObject *ifs; PyObject *is_async; + PyObject *is_lazy; PyObject *items; PyObject *iter; PyObject *key; diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index a03fe42668f15a..05235be70cd420 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -297,7 +297,11 @@ PyAPI_FUNC(void) _PyEval_FormatExcCheckArg(PyThreadState *tstate, PyObject *exc, PyAPI_FUNC(void) _PyEval_FormatExcUnbound(PyThreadState *tstate, PyCodeObject *co, int oparg); PyAPI_FUNC(void) _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs); PyAPI_FUNC(PyObject *) _PyEval_ImportFrom(PyThreadState *, PyObject *, PyObject *); -PyAPI_FUNC(PyObject *) _PyEval_ImportName(PyThreadState *, _PyInterpreterFrame *, PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) _PyEval_LazyImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals, + PyObject *name, PyObject *fromlist, PyObject *level); +PyAPI_FUNC(PyObject *) _PyEval_LazyImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name); +PyAPI_FUNC(PyObject *) _PyEval_ImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals, PyObject *locals, + PyObject *name, PyObject *fromlist, PyObject *level); PyAPI_FUNC(PyObject *)_PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs); PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys); PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr); diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 13fbff4eb65cb2..4b8ff0e3473e9d 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -31,6 +31,12 @@ extern int _PyImport_FixupBuiltin( PyObject *modules ); +extern PyObject * +_PyImport_ResolveName(PyThreadState *tstate, PyObject *name, PyObject *globals, int level); +extern PyObject * +_PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import); + + #ifdef HAVE_DLOPEN # include // RTLD_NOW, RTLD_LAZY # if HAVE_DECL_RTLD_NOW diff --git a/Include/internal/pycore_lazyimportobject.h b/Include/internal/pycore_lazyimportobject.h new file mode 100644 index 00000000000000..5a2138d3ac7766 --- /dev/null +++ b/Include/internal/pycore_lazyimportobject.h @@ -0,0 +1,29 @@ +/* Copyright (c) Meta, Inc. and its affiliates. All Rights Reserved */ +/* File added for Lazy Imports */ + +/* Lazy object interface */ + +#ifndef Py_LAZYIMPORTOBJECT_H +#define Py_LAZYIMPORTOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PyLazyImport_Type; +#define PyLazyImport_CheckExact(op) Py_IS_TYPE((op), &PyLazyImport_Type) + +typedef struct { + PyObject_HEAD + PyObject *lz_builtins; + PyObject *lz_from; + PyObject *lz_attr; +} PyLazyImportObject; + + +PyAPI_FUNC(PyObject *) _PyLazyImport_GetName(PyObject *lazy_import); +PyAPI_FUNC(PyObject *) _PyLazyImport_New(PyObject *builtins, PyObject *from, PyObject *attr); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_LAZYIMPORTOBJECT_H */ diff --git a/Include/internal/pycore_magic_number.h b/Include/internal/pycore_magic_number.h index 7ec7bd1c695516..ff8b26a055a09a 100644 --- a/Include/internal/pycore_magic_number.h +++ b/Include/internal/pycore_magic_number.h @@ -286,6 +286,7 @@ Known values: Python 3.15a1 3653 (Fix handling of opcodes that may leave operands on the stack when optimizing LOAD_FAST) Python 3.15a1 3654 (Fix missing exception handlers in logical expression) Python 3.15a1 3655 (Fix miscompilation of some module-level annotations) + Python 3.15a1 3656 Lazy imports IMPORT_NAME opcode changes Python 3.16 will start with 3700 @@ -299,7 +300,7 @@ PC/launcher.c must also be updated. */ -#define PYC_MAGIC_NUMBER 3655 +#define PYC_MAGIC_NUMBER 3656 /* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes (little-endian) and then appending b'\r\n'. */ #define PYC_MAGIC_NUMBER_TOKEN \ diff --git a/Lib/dis.py b/Lib/dis.py index d6d2c1386dd785..39d64eb0e7ea10 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -35,6 +35,7 @@ FUNCTION_ATTR_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure', 'annotate') ENTER_EXECUTOR = opmap['ENTER_EXECUTOR'] +IMPORT_NAME = opmap['IMPORT_NAME'] LOAD_GLOBAL = opmap['LOAD_GLOBAL'] LOAD_SMALL_INT = opmap['LOAD_SMALL_INT'] BINARY_OP = opmap['BINARY_OP'] @@ -601,7 +602,14 @@ def get_argval_argrepr(self, op, arg, offset): argval, argrepr = _get_name_info(arg//4, get_name) if (arg & 1) and argrepr: argrepr = f"{argrepr} + NULL|self" + elif deop == IMPORT_NAME: + argval, argrepr = _get_name_info(arg//4, get_name) + if (arg & 1) and argrepr: + argrepr = f"{argrepr} + lazy" + elif (arg & 2) and argrepr: + argrepr = f"{argrepr} + eager" else: + print(deop) argval, argrepr = _get_name_info(arg, get_name) elif deop in hasjump or deop in hasexc: argval = self.offset_from_jump_arg(op, arg, offset) diff --git a/Lib/keyword.py b/Lib/keyword.py index e22c837835e740..e6bc4a164e6a83 100644 --- a/Lib/keyword.py +++ b/Lib/keyword.py @@ -41,6 +41,7 @@ 'in', 'is', 'lambda', + 'lazy', 'nonlocal', 'not', 'or', diff --git a/Makefile.pre.in b/Makefile.pre.in index 34bd4540efb0b8..63f783eed255f1 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -539,6 +539,7 @@ OBJECT_OBJS= \ Objects/funcobject.o \ Objects/interpolationobject.o \ Objects/iterobject.o \ + Objects/lazyimportobject.o \ Objects/listobject.o \ Objects/longobject.o \ Objects/dictobject.o \ @@ -1368,6 +1369,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_interpolation.h \ $(srcdir)/Include/internal/pycore_intrinsics.h \ $(srcdir)/Include/internal/pycore_jit.h \ + $(srcdir)/Include/internal/pycore_lazyimportobject.h \ $(srcdir)/Include/internal/pycore_list.h \ $(srcdir)/Include/internal/pycore_llist.h \ $(srcdir)/Include/internal/pycore_lock.h \ diff --git a/Objects/lazyimportobject.c b/Objects/lazyimportobject.c new file mode 100644 index 00000000000000..973056d5ced254 --- /dev/null +++ b/Objects/lazyimportobject.c @@ -0,0 +1,137 @@ +/* Copyright (c) Meta, Inc. and its affiliates. All Rights Reserved */ +/* File added for Lazy Imports */ + +/* Lazy object implementation */ + +#include "Python.h" +#include "pycore_lazyimportobject.h" + +PyObject * +_PyLazyImport_New(PyObject *builtins, PyObject *from, PyObject *attr) +{ + PyLazyImportObject *m; + if (!from || !PyUnicode_Check(from)) { + PyErr_BadArgument(); + return NULL; + } + if (attr == Py_None) { + attr = NULL; + } + assert(!attr || PyObject_IsTrue(attr)); + m = PyObject_GC_New(PyLazyImportObject, &PyLazyImport_Type); + if (m == NULL) { + return NULL; + } + Py_XINCREF(builtins); + m->lz_builtins = builtins; + Py_INCREF(from); + m->lz_from = from; + Py_XINCREF(attr); + m->lz_attr = attr; + PyObject_GC_Track(m); + return (PyObject *)m; +} + +static void +lazy_import_dealloc(PyLazyImportObject *m) +{ + PyObject_GC_UnTrack(m); + Py_XDECREF(m->lz_builtins); + Py_XDECREF(m->lz_from); + Py_XDECREF(m->lz_attr); + Py_TYPE(m)->tp_free((PyObject *)m); +} + +static PyObject * +lazy_import_name(PyLazyImportObject *m) +{ + if (m->lz_attr != NULL) { + if (PyUnicode_Check(m->lz_attr)) { + return PyUnicode_FromFormat("%U.%U", m->lz_from, m->lz_attr); + } else { + return PyUnicode_FromFormat("%U...", m->lz_from); + } + } + Py_INCREF(m->lz_from); + return m->lz_from; +} + +static PyObject * +lazy_import_repr(PyLazyImportObject *m) +{ + PyObject *name = lazy_import_name(m); + if (name == NULL) { + return NULL; + } + PyObject *res = PyUnicode_FromFormat("", name); + Py_DECREF(name); + return res; +} + +static int +lazy_import_traverse(PyLazyImportObject *m, visitproc visit, void *arg) +{ + Py_VISIT(m->lz_builtins); + Py_VISIT(m->lz_from); + Py_VISIT(m->lz_attr); + return 0; +} + +static int +lazy_import_clear(PyLazyImportObject *m) +{ + Py_CLEAR(m->lz_builtins); + Py_CLEAR(m->lz_from); + Py_CLEAR(m->lz_attr); + return 0; +} + +PyObject * +_PyLazyImport_GetName(PyObject *lazy_import) +{ + assert(PyLazyImport_CheckExact(lazy_import)); + return lazy_import_name((PyLazyImportObject *)lazy_import); +} + +PyTypeObject PyLazyImport_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "lazy_import", /* tp_name */ + sizeof(PyLazyImportObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)lazy_import_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + (reprfunc)lazy_import_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)lazy_import_traverse, /* tp_traverse */ + (inquiry)lazy_import_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; diff --git a/Parser/Python.asdl b/Parser/Python.asdl index 96f3914b029d4c..6579642540e653 100644 --- a/Parser/Python.asdl +++ b/Parser/Python.asdl @@ -45,8 +45,8 @@ module Python | TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody) | Assert(expr test, expr? msg) - | Import(alias* names) - | ImportFrom(identifier? module, alias* names, int? level) + | Import(alias* names, int? is_lazy) + | ImportFrom(identifier? module, alias* names, int? level, int? is_lazy) | Global(identifier* names) | Nonlocal(identifier* names) diff --git a/Parser/action_helpers.c b/Parser/action_helpers.c index 57e46b4399c66d..76a6a0cea984eb 100644 --- a/Parser/action_helpers.c +++ b/Parser/action_helpers.c @@ -1926,7 +1926,7 @@ _PyPegen_checked_future_import(Parser *p, identifier module, asdl_alias_seq * na } } } - return _PyAST_ImportFrom(module, names, level, lineno, col_offset, end_lineno, end_col_offset, arena); + return _PyAST_ImportFrom(module, names, level, 0, lineno, col_offset, end_lineno, end_col_offset, arena); } asdl_stmt_seq* diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 660bc598a4862c..2bb9003fa29406 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -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) { @@ -6218,8 +6253,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" @@ -6348,17 +6383,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)"); @@ -7598,8 +7637,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)); @@ -7607,6 +7646,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; @@ -7616,8 +7656,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)); @@ -7627,6 +7667,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; @@ -9465,6 +9506,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; @@ -9486,6 +9532,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; @@ -13481,6 +13532,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; @@ -13520,7 +13572,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; @@ -13534,6 +13603,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; @@ -13607,8 +13677,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; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 6c3609d293890f..0236c5043f2acb 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1784,6 +1784,11 @@ 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)) { + v_o = _PyImport_LoadLazyImportTstate(tstate, v_o); + ERROR_IF(v_o == NULL); + } + v = PyStackRef_FromPyObjectSteal(v_o); } @@ -1809,7 +1814,16 @@ 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)); + + PyObject *res_o = PyStackRef_AsPyObjectBorrow(*res); + if (PyLazyImport_CheckExact(res_o)) { + res_o = _PyImport_LoadLazyImportTstate(tstate, res_o); + ERROR_IF(res_o == NULL); + //PyStackRef_CLOSE(*res); + *res = PyStackRef_FromPyObjectSteal(res_o); + } } op(_PUSH_NULL_CONDITIONAL, ( -- null[oparg & 1])) { @@ -2914,10 +2928,18 @@ dummy_func( } 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)); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + PyObject *res_o; + if (oparg & 0x01) { + res_o = _PyEval_LazyImportName(tstate, BUILTINS(), GLOBALS(), name, + PyStackRef_AsPyObjectBorrow(fromlist), + PyStackRef_AsPyObjectBorrow(level)); + + } 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); @@ -2925,7 +2947,13 @@ 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, PyStackRef_AsPyObjectBorrow(from), name); + } else { + res_o = _PyEval_ImportFrom(tstate, PyStackRef_AsPyObjectBorrow(from), name); + } + ERROR_IF(res_o == NULL); res = PyStackRef_FromPyObjectSteal(res_o); } diff --git a/Python/ceval.c b/Python/ceval.c index 578c5d2a8b1420..0c8a8a10b9661b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -22,6 +22,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 @@ -2978,11 +2979,11 @@ _PyEval_SliceIndexNotNone(PyObject *v, Py_ssize_t *pi) } PyObject * -_PyEval_ImportName(PyThreadState *tstate, _PyInterpreterFrame *frame, +_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) { @@ -2990,7 +2991,6 @@ _PyEval_ImportName(PyThreadState *tstate, _PyInterpreterFrame *frame, return NULL; } - PyObject *locals = frame->f_locals; if (locals == NULL) { locals = Py_None; } @@ -3004,18 +3004,50 @@ _PyEval_ImportName(PyThreadState *tstate, _PyInterpreterFrame *frame, } 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; } + +PyObject * +_PyEval_LazyImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals, + PyObject *name, PyObject *fromlist, PyObject *level) +{ + PyObject *res = NULL; + PyObject *abs_name = NULL; + int ilevel = PyLong_AsInt(level); + if (ilevel == -1 && PyErr_Occurred()) { + goto error; + } + if (ilevel > 0) { + abs_name = _PyImport_ResolveName(tstate, name, globals, ilevel); + if (abs_name == NULL) { + goto error; + } + } else { /* ilevel == 0 */ + if (PyUnicode_GET_LENGTH(name) == 0) { + PyErr_SetString(PyExc_ValueError, "Empty module name"); + goto error; + } + abs_name = name; + Py_INCREF(abs_name); + } + + // TODO: check sys.modules for module + res = _PyLazyImport_New(builtins, abs_name, fromlist); +error: + Py_XDECREF(abs_name); + return res; +} + PyObject * _PyEval_ImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name) { @@ -3184,6 +3216,33 @@ _PyEval_ImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name) return NULL; } +PyObject * +_PyEval_LazyImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name) +{ + assert(PyLazyImport_CheckExact(v)); + assert(name && PyUnicode_Check(name)); + PyObject *ret; + PyLazyImportObject *d = (PyLazyImportObject *)v; + if (d->lz_attr != NULL) { + if (PyUnicode_Check(d->lz_attr)) { + PyObject *from = PyUnicode_FromFormat("%U.%U", d->lz_from, d->lz_attr); + ret = _PyLazyImport_New(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); + ret = _PyLazyImport_New(d->lz_builtins, from, name); + Py_DECREF(from); + return ret; + } + } + ret = _PyLazyImport_New(d->lz_builtins, d->lz_from, name); + return ret; +} + #define CANNOT_CATCH_MSG "catching classes that do not inherit from "\ "BaseException is not allowed" diff --git a/Python/codegen.c b/Python/codegen.c index c4109fcaa48dbe..bdf894cab8d7c2 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -351,8 +351,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) { @@ -363,40 +363,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) { @@ -2861,7 +2872,13 @@ 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) { + // TODO: SyntaxError when not in module scope + ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 1); + } else { + // TODO: If in try/except, set 2nd bit + ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 0); + } if (alias->asname) { r = codegen_import_as(c, loc, alias->name, alias->asname); @@ -2907,12 +2924,15 @@ 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) { + // TODO: SyntaxError when not in module scope + ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 1); + } else { + ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 0); } for (Py_ssize_t i = 0; i < n; i++) { alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, i); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 1309c1317a6615..480bf5914ef809 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2458,6 +2458,14 @@ if (v_o == NULL) { JUMP_TO_ERROR(); } + if (PyLazyImport_CheckExact(v_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + v_o = _PyImport_LoadLazyImportTstate(tstate, v_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (v_o == NULL) { + JUMP_TO_ERROR(); + } + } v = PyStackRef_FromPyObjectSteal(v_o); stack_pointer[0] = v; stack_pointer += 1; @@ -2476,6 +2484,16 @@ if (PyStackRef_IsNull(*res)) { JUMP_TO_ERROR(); } + PyObject *res_o = PyStackRef_AsPyObjectBorrow(*res); + if (PyLazyImport_CheckExact(res_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyImport_LoadLazyImportTstate(tstate, res_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_ERROR(); + } + *res = PyStackRef_FromPyObjectSteal(res_o); + } stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; @@ -4120,11 +4138,22 @@ oparg = CURRENT_OPARG(); 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 & 0x01) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_LazyImportName(tstate, BUILTINS(), GLOBALS(), name, + PyStackRef_AsPyObjectBorrow(fromlist), + PyStackRef_AsPyObjectBorrow(level)); + 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; @@ -4152,9 +4181,16 @@ oparg = CURRENT_OPARG(); 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, 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_ERROR(); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index c1f6f5c85cdd88..cf5c655f6bac1f 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -6188,9 +6188,16 @@ _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, 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); } @@ -6214,11 +6221,22 @@ _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 & 0x01) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_LazyImportName(tstate, BUILTINS(), GLOBALS(), name, + PyStackRef_AsPyObjectBorrow(fromlist), + PyStackRef_AsPyObjectBorrow(level)); + 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; @@ -9149,6 +9167,16 @@ if (PyStackRef_IsNull(*res)) { JUMP_TO_LABEL(error); } + PyObject *res_o = PyStackRef_AsPyObjectBorrow(*res); + if (PyLazyImport_CheckExact(res_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyImport_LoadLazyImportTstate(tstate, res_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + *res = PyStackRef_FromPyObjectSteal(res_o); + } } // _PUSH_NULL_CONDITIONAL { @@ -9349,6 +9377,14 @@ if (v_o == NULL) { JUMP_TO_LABEL(error); } + if (PyLazyImport_CheckExact(v_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + v_o = _PyImport_LoadLazyImportTstate(tstate, v_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (v_o == NULL) { + JUMP_TO_LABEL(error); + } + } v = PyStackRef_FromPyObjectSteal(v_o); stack_pointer[0] = v; stack_pointer += 1; diff --git a/Python/import.c b/Python/import.c index 9dee20ecb63c91..71684604d1e9c8 100644 --- a/Python/import.c +++ b/Python/import.c @@ -7,6 +7,8 @@ #include "pycore_import.h" // _PyImport_BootstrapImp() #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_interp.h" // struct _import_runtime_state +#include "pycore_long.h" // _PyLong_GetZero +#include "pycore_lazyimportobject.h" #include "pycore_magic_number.h" // PYC_MAGIC_NUMBER_TOKEN #include "pycore_moduleobject.h" // _PyModule_GetDef() #include "pycore_namespace.h" // _PyNamespace_Type @@ -3670,6 +3672,101 @@ 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 = NULL; + assert(lazy_import != NULL); + assert(PyLazyImport_CheckExact(lazy_import)); + PyObject *state_dict = _PyThreadState_GetDict(tstate); + assert(state_dict != NULL); + + PyLazyImportObject *lz = (PyLazyImportObject *)lazy_import; + + 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 (full) { + obj = _PyEval_ImportName(tstate, + lz->lz_builtins, + 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_ImportName(tstate, + lz->lz_builtins, + 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_XDECREF(obj); + obj = NULL; + +ok: + Py_XDECREF(fromlist); + return obj; +} + static PyObject * import_find_and_load(PyThreadState *tstate, PyObject *abs_name) {