diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 38a77119769d72..7e34d541d84cbe 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -350,6 +350,16 @@ Standard names are defined for the following types: actually accessed. This type can be used to detect lazy imports programmatically. + .. method:: resolve() + + Resolve the lazy import and return the imported object. Successful + resolutions are cached. + + For a module-level lazy import, ``resolve()`` also updates the original + module global if that name still refers to this proxy. If the name was + deleted or rebound, it is left unchanged; later ``resolve()`` calls return + the cached object without updating that name. + .. versionadded:: 3.15 .. seealso:: :pep:`810` diff --git a/Include/internal/pycore_lazyimportobject.h b/Include/internal/pycore_lazyimportobject.h index b81e4211b08ff3..93617e65c5aed2 100644 --- a/Include/internal/pycore_lazyimportobject.h +++ b/Include/internal/pycore_lazyimportobject.h @@ -19,8 +19,16 @@ typedef struct { PyObject *lz_builtins; PyObject *lz_from; PyObject *lz_attr; + // Protected by the import lock. lz_owner_* records the first module + // global that resolve() may replace. Pending owners are being stored; + // finalized owners cannot be claimed again. + PyObject *lz_owner_globals; + PyObject *lz_owner_key; + PyObject *lz_resolved; // Frame information for the original import location. PyCodeObject *lz_code; // Code object where the lazy import was created. + int lz_owner_pending; + int lz_owner_finalized; int lz_instr_offset; // Instruction offset where the lazy import was created. } PyLazyImportObject; @@ -28,6 +36,27 @@ typedef struct { PyAPI_FUNC(PyObject *) _PyLazyImport_GetName(PyObject *lazy_import); PyAPI_FUNC(PyObject *) _PyLazyImport_New( struct _PyInterpreterFrame *frame, PyObject *import_func, PyObject *from, PyObject *attr); +PyAPI_FUNC(int) _PyLazyImport_BindGlobal( + PyThreadState *tstate, + PyObject *lazy_import, + PyObject *globals, + PyObject *name); +PyAPI_FUNC(void) _PyLazyImport_FinishGlobalBinding( + PyThreadState *tstate, + PyObject *lazy_import, + PyObject *globals, + PyObject *name, + int stored); +PyAPI_FUNC(int) _PyLazyImport_CommitIfCurrent( + PyThreadState *tstate, + PyObject *lazy_import, + PyObject *globals, + PyObject *name, + PyObject *resolved); +PyAPI_FUNC(int) _PyLazyImport_CommitStoredIfCurrent( + PyThreadState *tstate, + PyObject *lazy_import, + PyObject *resolved); #ifdef __cplusplus } diff --git a/Lib/test/test_lazy_import/__init__.py b/Lib/test/test_lazy_import/__init__.py index 4658882243d65f..70cffae4bf9de9 100644 --- a/Lib/test/test_lazy_import/__init__.py +++ b/Lib/test/test_lazy_import/__init__.py @@ -277,6 +277,538 @@ def test_lazy_import_type_attributes_accessible(self): proc = assert_python_ok("-c", code) self.assertIn(b"lz_builtins = Py_XNewRef(builtins); m->lz_from = Py_NewRef(name); m->lz_attr = Py_XNewRef(fromlist); + m->lz_owner_globals = NULL; + m->lz_owner_key = NULL; + m->lz_resolved = NULL; + m->lz_owner_pending = 0; + m->lz_owner_finalized = 0; // Capture frame information for the original import location. m->lz_code = NULL; @@ -58,6 +66,9 @@ lazy_import_traverse(PyObject *op, visitproc visit, void *arg) Py_VISIT(m->lz_builtins); Py_VISIT(m->lz_from); Py_VISIT(m->lz_attr); + Py_VISIT(m->lz_owner_globals); + Py_VISIT(m->lz_owner_key); + Py_VISIT(m->lz_resolved); Py_VISIT(m->lz_code); return 0; } @@ -69,6 +80,11 @@ lazy_import_clear(PyObject *op) Py_CLEAR(m->lz_builtins); Py_CLEAR(m->lz_from); Py_CLEAR(m->lz_attr); + Py_CLEAR(m->lz_owner_globals); + Py_CLEAR(m->lz_owner_key); + Py_CLEAR(m->lz_resolved); + m->lz_owner_pending = 0; + m->lz_owner_finalized = 0; Py_CLEAR(m->lz_code); return 0; } @@ -116,16 +132,248 @@ _PyLazyImport_GetName(PyObject *op) return lazy_import_name(lazy_import); } +int +_PyLazyImport_BindGlobal(PyThreadState *tstate, PyObject *op, + PyObject *globals, PyObject *name) +{ + if (!PyLazyImport_CheckExact(op) || + !PyDict_CheckExact(globals) || + !PyUnicode_CheckExact(name)) + { + return 0; + } + PyLazyImportObject *m = PyLazyImportObject_CAST(op); + int bound = 0; + PyInterpreterState *interp = tstate->interp; + _PyImport_AcquireLock(interp); + // First owner wins: copies of the proxy do not change the original global + // binding that resolve() may replace. + if (m->lz_resolved == NULL && + !m->lz_owner_pending && + !m->lz_owner_finalized && + m->lz_owner_globals == NULL && + m->lz_owner_key == NULL) + { + m->lz_owner_globals = Py_NewRef(globals); + m->lz_owner_key = Py_NewRef(name); + m->lz_owner_pending = 1; + bound = 1; + } + _PyImport_ReleaseLock(interp); + return bound; +} + +static int +owner_matches(PyLazyImportObject *m, PyObject *globals, PyObject *name) +{ + return (m->lz_owner_globals == globals && + m->lz_owner_key != NULL && + _PyUnicode_Equal(m->lz_owner_key, name)); +} + +static void +clear_owner_if_matches(PyThreadState *tstate, PyLazyImportObject *m, + PyObject *globals, PyObject *name) +{ + PyInterpreterState *interp = tstate->interp; + _PyImport_AcquireLock(interp); + if (owner_matches(m, globals, name)) { + m->lz_owner_pending = 0; + // Keep lz_owner_finalized set: this owner has been committed or + // abandoned, so later stores of the same proxy are not canonical. + Py_CLEAR(m->lz_owner_globals); + Py_CLEAR(m->lz_owner_key); + } + _PyImport_ReleaseLock(interp); +} + +void +_PyLazyImport_FinishGlobalBinding(PyThreadState *tstate, PyObject *op, + PyObject *globals, PyObject *name, + int stored) +{ + if (!PyLazyImport_CheckExact(op) || + !PyDict_CheckExact(globals) || + !PyUnicode_CheckExact(name)) + { + return; + } + + PyLazyImportObject *m = PyLazyImportObject_CAST(op); + PyInterpreterState *interp = tstate->interp; + _PyImport_AcquireLock(interp); + if (m->lz_owner_pending && owner_matches(m, globals, name)) { + m->lz_owner_pending = 0; + if (!stored) { + Py_CLEAR(m->lz_owner_globals); + Py_CLEAR(m->lz_owner_key); + } + } + _PyImport_ReleaseLock(interp); +} + +static int +finalize_owner_if_matches(PyThreadState *tstate, PyLazyImportObject *m, + PyObject *globals, PyObject *name) +{ + int finalized = 0; + PyInterpreterState *interp = tstate->interp; + _PyImport_AcquireLock(interp); + if (!m->lz_owner_pending && + !m->lz_owner_finalized && + owner_matches(m, globals, name)) + { + m->lz_owner_finalized = 1; + finalized = 1; + } + _PyImport_ReleaseLock(interp); + return finalized; +} + +static void +restore_owner_if_matches(PyThreadState *tstate, PyLazyImportObject *m, + PyObject *globals, PyObject *name) +{ + PyInterpreterState *interp = tstate->interp; + _PyImport_AcquireLock(interp); + if (m->lz_owner_finalized && owner_matches(m, globals, name)) { + m->lz_owner_finalized = 0; + } + _PyImport_ReleaseLock(interp); +} + +static int +owner_is_pending(PyThreadState *tstate, PyLazyImportObject *m, + PyObject *globals, PyObject *name) +{ + int pending = 0; + PyInterpreterState *interp = tstate->interp; + _PyImport_AcquireLock(interp); + if (m->lz_owner_pending && owner_matches(m, globals, name)) { + pending = 1; + } + _PyImport_ReleaseLock(interp); + return pending; +} + +static int +claim_stored_owner(PyThreadState *tstate, PyLazyImportObject *m, + PyObject **globals, PyObject **name) +{ + int claimed = 0; + PyInterpreterState *interp = tstate->interp; + _PyImport_AcquireLock(interp); + if (!m->lz_owner_pending && + !m->lz_owner_finalized && + m->lz_owner_globals != NULL && + m->lz_owner_key != NULL) + { + m->lz_owner_finalized = 1; + *globals = Py_NewRef(m->lz_owner_globals); + *name = Py_NewRef(m->lz_owner_key); + claimed = 1; + } + _PyImport_ReleaseLock(interp); + return claimed; +} + +static int +commit_if_current(PyObject *op, PyObject *globals, PyObject *name, + PyObject *value) +{ + PyObject *current = NULL; + int err = 0; + Py_BEGIN_CRITICAL_SECTION(globals); + int found = _PyDict_GetItemRef_Unicode_LockHeld( + (PyDictObject *)globals, name, ¤t); + if (found < 0) { + err = -1; + } + else if (found > 0 && current == op) { + err = _PyDict_SetItem_LockHeld((PyDictObject *)globals, name, value); + } + Py_END_CRITICAL_SECTION(); + Py_XDECREF(current); + return err; +} + +int +_PyLazyImport_CommitIfCurrent(PyThreadState *tstate, PyObject *op, + PyObject *globals, PyObject *name, + PyObject *value) +{ + if (!PyLazyImport_CheckExact(op) || + !PyDict_CheckExact(globals) || + !PyUnicode_CheckExact(name)) + { + return 0; + } + + PyLazyImportObject *m = PyLazyImportObject_CAST(op); + if (owner_is_pending(tstate, m, globals, name)) { + return 0; + } + + int finalized_owner = finalize_owner_if_matches(tstate, m, globals, name); + int err = commit_if_current(op, globals, name, value); + if (finalized_owner) { + if (err < 0) { + restore_owner_if_matches(tstate, m, globals, name); + } + else { + clear_owner_if_matches(tstate, m, globals, name); + } + } + return err; +} + +int +_PyLazyImport_CommitStoredIfCurrent(PyThreadState *tstate, PyObject *op, + PyObject *value) +{ + if (!PyLazyImport_CheckExact(op)) { + return 0; + } + + PyLazyImportObject *m = PyLazyImportObject_CAST(op); + PyObject *globals = NULL; + PyObject *name = NULL; + + if (!claim_stored_owner(tstate, m, &globals, &name)) { + return 0; + } + + int err = commit_if_current(op, globals, name, value); + if (err < 0) { + restore_owner_if_matches(tstate, m, globals, name); + } + else { + clear_owner_if_matches(tstate, m, globals, name); + } + Py_DECREF(globals); + Py_DECREF(name); + return err; +} + static PyObject * lazy_import_resolve(PyObject *self, PyObject *args) { - return _PyImport_LoadLazyImportTstate(PyThreadState_GET(), self); + PyThreadState *tstate = PyThreadState_GET(); + PyObject *resolved = _PyImport_LoadLazyImportTstate(tstate, self); + if (resolved == NULL) { + return NULL; + } + if (_PyLazyImport_CommitStoredIfCurrent(tstate, self, resolved) < 0) { + Py_DECREF(resolved); + return NULL; + } + return resolved; } static PyMethodDef lazy_import_methods[] = { { "resolve", lazy_import_resolve, METH_NOARGS, - PyDoc_STR("resolves the lazy import and returns the actual object") + PyDoc_STR("resolve the lazy import and return the actual object") }, {NULL, NULL} }; @@ -137,9 +385,10 @@ PyDoc_STRVAR(lazy_import_doc, "\n" "Represents a lazy import that will be resolved on first use.\n" "\n" -"Instances of this object accessed from the global scope will be\n" -"automatically imported based upon their name and then replaced with\n" -"the imported value."); +"A successful resolution is cached. Instances of this object accessed\n" +"from the global scope will be automatically imported based upon their\n" +"name. The original global is replaced only if it still refers to this\n" +"lazy import object."); PyTypeObject PyLazyImport_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index f447403ef31b43..d4d37b833fd3a3 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -1353,8 +1353,9 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) } PyErr_Clear(); } + PyThreadState *tstate = PyThreadState_GET(); PyObject *new_value = _PyImport_LoadLazyImportTstate( - PyThreadState_GET(), attr); + tstate, attr); if (new_value == NULL) { if (suppress && PyErr_ExceptionMatches(PyExc_ImportCycleError)) { @@ -1368,7 +1369,9 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) return NULL; } - if (PyDict_SetItem(m->md_dict, name, new_value) < 0) { + if (_PyLazyImport_CommitIfCurrent( + tstate, attr, m->md_dict, name, new_value) < 0) + { Py_CLEAR(new_value); } Py_DECREF(attr); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 8afa0be702357d..96546a20574c05 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2005,7 +2005,16 @@ dummy_func( ERROR_IF(true); } if (PyDict_CheckExact(ns)) { - err = PyDict_SetItem(ns, name, PyStackRef_AsPyObjectBorrow(v)); + PyObject *value = PyStackRef_AsPyObjectBorrow(v); + int bound = 0; + if (ns == GLOBALS() && PyLazyImport_CheckExact(value)) { + bound = _PyLazyImport_BindGlobal(tstate, value, ns, name); + } + err = PyDict_SetItem(ns, name, value); + if (bound) { + _PyLazyImport_FinishGlobalBinding( + tstate, value, ns, name, err == 0); + } } else { err = PyObject_SetItem(ns, name, PyStackRef_AsPyObjectBorrow(v)); @@ -2189,7 +2198,16 @@ dummy_func( inst(STORE_GLOBAL, (v --)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyDict_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); + PyObject *value = PyStackRef_AsPyObjectBorrow(v); + int bound = 0; + if (PyLazyImport_CheckExact(value)) { + bound = _PyLazyImport_BindGlobal(tstate, value, GLOBALS(), name); + } + int err = PyDict_SetItem(GLOBALS(), name, value); + if (bound) { + _PyLazyImport_FinishGlobalBinding( + tstate, value, GLOBALS(), name, err == 0); + } PyStackRef_CLOSE(v); ERROR_IF(err); } @@ -2221,7 +2239,9 @@ dummy_func( inst(LOAD_FROM_DICT_OR_GLOBALS, (mod_or_class_dict -- v)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err; - PyObject *v_o = _PyMapping_GetOptionalItem2(PyStackRef_AsPyObjectBorrow(mod_or_class_dict), name, &err); + PyObject *source = PyStackRef_AsPyObjectBorrow(mod_or_class_dict); + PyObject *v_o = _PyMapping_GetOptionalItem2(source, name, &err); + int commit_source = (v_o != NULL && source == GLOBALS()); PyStackRef_CLOSE(mod_or_class_dict); ERROR_IF(err < 0); @@ -2244,8 +2264,18 @@ dummy_func( if (PyLazyImport_CheckExact(v_o)) { PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + if (l_v == NULL) { + Py_DECREF(v_o); + ERROR_IF(true); + } + err = _PyLazyImport_CommitIfCurrent( + tstate, v_o, GLOBALS(), name, l_v); + if (err < 0) { + Py_DECREF(v_o); + Py_DECREF(l_v); + ERROR_IF(true); + } Py_SETREF(v_o, l_v); - ERROR_IF(v_o == NULL); } } else { @@ -2266,11 +2296,36 @@ dummy_func( } if (PyLazyImport_CheckExact(v_o)) { PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + if (l_v == NULL) { + Py_DECREF(v_o); + ERROR_IF(true); + } + err = _PyLazyImport_CommitIfCurrent( + tstate, v_o, GLOBALS(), name, l_v); + if (err < 0) { + Py_DECREF(v_o); + Py_DECREF(l_v); + ERROR_IF(true); + } Py_SETREF(v_o, l_v); - ERROR_IF(v_o == NULL); } } } + else if (commit_source && PyLazyImport_CheckExact(v_o)) { + PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + if (l_v == NULL) { + Py_DECREF(v_o); + ERROR_IF(true); + } + err = _PyLazyImport_CommitIfCurrent( + tstate, v_o, source, 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); } @@ -2285,7 +2340,8 @@ dummy_func( Py_DECREF(v_o); ERROR_IF(true); } - int err = PyDict_SetItem(GLOBALS(), name, l_v); + int err = _PyLazyImport_CommitIfCurrent( + tstate, v_o, GLOBALS(), name, l_v); if (err < 0) { Py_DECREF(v_o); Py_DECREF(l_v); diff --git a/Python/ceval.c b/Python/ceval.c index fbea1f67a36f44..efc82fb4fd6d5a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3651,14 +3651,17 @@ _PyEval_LoadGlobalStackRef(PyObject *globals, PyObject *builtins, PyObject *name 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]); + PyThreadState *tstate = PyThreadState_GET(); + PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, res_o); if (l_v == NULL) { + PyStackRef_CLOSE(writeto[0]); assert(PyErr_Occurred()); *writeto = PyStackRef_NULL; return; } - int err = PyDict_SetItem(globals, name, l_v); + int err = _PyLazyImport_CommitIfCurrent( + tstate, res_o, globals, name, l_v); + PyStackRef_CLOSE(writeto[0]); if (err < 0) { Py_DECREF(l_v); *writeto = PyStackRef_NULL; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 63b77c9e152778..2fa3a91bec8092 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -9587,13 +9587,32 @@ JUMP_TO_ERROR(); } if (PyDict_CheckExact(ns)) { + PyObject *value = PyStackRef_AsPyObjectBorrow(v); + int bound = 0; + if (ns == GLOBALS() && PyLazyImport_CheckExact(value)) { + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyFrame_StackPointerValidate(frame); + bound = _PyLazyImport_BindGlobal(tstate, value, ns, name); + _PyFrame_StackPointerInvalidate(frame); + stack_pointer += -1; + } stack_pointer[0] = v; stack_pointer += 1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); _PyFrame_StackPointerValidate(frame); - err = PyDict_SetItem(ns, name, PyStackRef_AsPyObjectBorrow(v)); + err = PyDict_SetItem(ns, name, value); _PyFrame_StackPointerInvalidate(frame); + if (bound) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + _PyLazyImport_FinishGlobalBinding( + tstate, value, ns, name, err == 0); + _PyFrame_StackPointerInvalidate(frame); + } } else { stack_pointer[0] = v; @@ -10076,13 +10095,32 @@ oparg = CURRENT_OPARG(); v = _stack_item_0; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + PyObject *value = PyStackRef_AsPyObjectBorrow(v); + int bound = 0; + if (PyLazyImport_CheckExact(value)) { + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyFrame_StackPointerValidate(frame); + bound = _PyLazyImport_BindGlobal(tstate, value, GLOBALS(), name); + _PyFrame_StackPointerInvalidate(frame); + stack_pointer += -1; + } stack_pointer[0] = v; stack_pointer += 1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); _PyFrame_StackPointerValidate(frame); - int err = PyDict_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); + int err = PyDict_SetItem(GLOBALS(), name, value); _PyFrame_StackPointerInvalidate(frame); + if (bound) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + _PyLazyImport_FinishGlobalBinding( + tstate, value, GLOBALS(), name, err == 0); + _PyFrame_StackPointerInvalidate(frame); + } stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -10238,7 +10276,8 @@ } assert(stack_pointer == _PyFrame_GetStackPointer(frame)); _PyFrame_StackPointerValidate(frame); - int err = PyDict_SetItem(GLOBALS(), name, l_v); + int err = _PyLazyImport_CommitIfCurrent( + tstate, v_o, GLOBALS(), name, l_v); _PyFrame_StackPointerInvalidate(frame); if (err < 0) { assert(stack_pointer == _PyFrame_GetStackPointer(frame)); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 2cdf48c559a292..b5ee412e4e073e 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -10021,10 +10021,12 @@ mod_or_class_dict = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err; + PyObject *source = PyStackRef_AsPyObjectBorrow(mod_or_class_dict); _PyFrame_SetStackPointer(frame, stack_pointer); _PyFrame_StackPointerValidate(frame); - PyObject *v_o = _PyMapping_GetOptionalItem2(PyStackRef_AsPyObjectBorrow(mod_or_class_dict), name, &err); + PyObject *v_o = _PyMapping_GetOptionalItem2(source, name, &err); _PyFrame_StackPointerInvalidate(frame); + int commit_source = (v_o != NULL && source == GLOBALS()); stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -10059,13 +10061,33 @@ _PyFrame_StackPointerValidate(frame); PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); _PyFrame_StackPointerInvalidate(frame); + if (l_v == NULL) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(v_o); + _PyFrame_StackPointerInvalidate(frame); + JUMP_TO_LABEL(error); + } assert(stack_pointer == _PyFrame_GetStackPointer(frame)); _PyFrame_StackPointerValidate(frame); - Py_SETREF(v_o, l_v); + err = _PyLazyImport_CommitIfCurrent( + tstate, v_o, GLOBALS(), name, l_v); _PyFrame_StackPointerInvalidate(frame); - if (v_o == NULL) { + if (err < 0) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(v_o); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(l_v); + _PyFrame_StackPointerInvalidate(frame); JUMP_TO_LABEL(error); } + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_SETREF(v_o, l_v); + _PyFrame_StackPointerInvalidate(frame); } } else { @@ -10099,16 +10121,69 @@ _PyFrame_StackPointerValidate(frame); PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); _PyFrame_StackPointerInvalidate(frame); + if (l_v == NULL) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(v_o); + _PyFrame_StackPointerInvalidate(frame); + JUMP_TO_LABEL(error); + } assert(stack_pointer == _PyFrame_GetStackPointer(frame)); _PyFrame_StackPointerValidate(frame); - Py_SETREF(v_o, l_v); + err = _PyLazyImport_CommitIfCurrent( + tstate, v_o, GLOBALS(), name, l_v); _PyFrame_StackPointerInvalidate(frame); - if (v_o == NULL) { + if (err < 0) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(v_o); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(l_v); + _PyFrame_StackPointerInvalidate(frame); JUMP_TO_LABEL(error); } + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_SETREF(v_o, l_v); + _PyFrame_StackPointerInvalidate(frame); } } } + else if (commit_source && PyLazyImport_CheckExact(v_o)) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + _PyFrame_StackPointerInvalidate(frame); + if (l_v == NULL) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(v_o); + _PyFrame_StackPointerInvalidate(frame); + JUMP_TO_LABEL(error); + } + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + err = _PyLazyImport_CommitIfCurrent( + tstate, v_o, source, name, l_v); + _PyFrame_StackPointerInvalidate(frame); + if (err < 0) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(v_o); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(l_v); + _PyFrame_StackPointerInvalidate(frame); + JUMP_TO_LABEL(error); + } + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_SETREF(v_o, l_v); + _PyFrame_StackPointerInvalidate(frame); + } v = PyStackRef_FromPyObjectSteal(v_o); stack_pointer[0] = v; stack_pointer += 1; @@ -10377,7 +10452,8 @@ } assert(stack_pointer == _PyFrame_GetStackPointer(frame)); _PyFrame_StackPointerValidate(frame); - int err = PyDict_SetItem(GLOBALS(), name, l_v); + int err = _PyLazyImport_CommitIfCurrent( + tstate, v_o, GLOBALS(), name, l_v); _PyFrame_StackPointerInvalidate(frame); if (err < 0) { assert(stack_pointer == _PyFrame_GetStackPointer(frame)); @@ -12546,10 +12622,25 @@ _PyStackRef v; v = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + PyObject *value = PyStackRef_AsPyObjectBorrow(v); + int bound = 0; + if (PyLazyImport_CheckExact(value)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyFrame_StackPointerValidate(frame); + bound = _PyLazyImport_BindGlobal(tstate, value, GLOBALS(), name); + _PyFrame_StackPointerInvalidate(frame); + } _PyFrame_SetStackPointer(frame, stack_pointer); _PyFrame_StackPointerValidate(frame); - int err = PyDict_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); + int err = PyDict_SetItem(GLOBALS(), name, value); _PyFrame_StackPointerInvalidate(frame); + if (bound) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + _PyLazyImport_FinishGlobalBinding( + tstate, value, GLOBALS(), name, err == 0); + _PyFrame_StackPointerInvalidate(frame); + } stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -12590,10 +12681,25 @@ JUMP_TO_LABEL(error); } if (PyDict_CheckExact(ns)) { + PyObject *value = PyStackRef_AsPyObjectBorrow(v); + int bound = 0; + if (ns == GLOBALS() && PyLazyImport_CheckExact(value)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyFrame_StackPointerValidate(frame); + bound = _PyLazyImport_BindGlobal(tstate, value, ns, name); + _PyFrame_StackPointerInvalidate(frame); + } _PyFrame_SetStackPointer(frame, stack_pointer); _PyFrame_StackPointerValidate(frame); - err = PyDict_SetItem(ns, name, PyStackRef_AsPyObjectBorrow(v)); + err = PyDict_SetItem(ns, name, value); _PyFrame_StackPointerInvalidate(frame); + if (bound) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + _PyLazyImport_FinishGlobalBinding( + tstate, value, ns, name, err == 0); + _PyFrame_StackPointerInvalidate(frame); + } } else { _PyFrame_SetStackPointer(frame, stack_pointer); diff --git a/Python/import.c b/Python/import.c index 6da6faf5f28cc3..6327ec606085de 100644 --- a/Python/import.c +++ b/Python/import.c @@ -3897,6 +3897,12 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import) // Acquire the global import lock to serialize reification _PyImport_AcquireLock(interp); + if (lz->lz_resolved != NULL) { + obj = Py_NewRef(lz->lz_resolved); + _PyImport_ReleaseLock(interp); + return obj; + } + // 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. @@ -4093,6 +4099,9 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import) if (PySet_Discard(importing, lazy_import) < 0) { Py_CLEAR(obj); } + else if (obj != NULL) { + lz->lz_resolved = Py_NewRef(obj); + } // Release the global import lock. _PyImport_ReleaseLock(interp);