@@ -340,8 +340,9 @@ _PyLazyImport_IsResolving(PyThreadState *tstate, PyObject *op)
340340 return active ;
341341}
342342
343- PyObject *
344- _PyImport_LoadLazyImportTstate (PyThreadState * tstate , PyObject * lazy_import )
343+ static PyObject *
344+ lazy_import_resolve_impl (PyThreadState * tstate , PyObject * lazy_import ,
345+ PyObject * * imported_module )
345346{
346347 PyObject * obj = NULL ;
347348 PyObject * fromlist = NULL ;
@@ -421,6 +422,12 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import)
421422 root -> lz_from , fromlist , _PyLong_GetZero ()
422423 );
423424 obj = lazy_import_resolve_result (tstate , obj );
425+ // The normal importer may publish this module on its parent. Custom
426+ // hooks retain control of their own assignments to the parent.
427+ if (imported_module != NULL && obj != NULL && PyModule_Check (obj ) &&
428+ _PyImport_IsDefaultImportFunc (tstate -> interp , import_func )) {
429+ * imported_module = Py_NewRef (obj );
430+ }
424431 if (obj != NULL && first != NULL ) {
425432 // Keep the hook and root result alive until all attribute lookups finish.
426433 PyObject * from = obj ;
@@ -451,21 +458,81 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import)
451458 return obj ;
452459}
453460
461+ PyObject *
462+ _PyImport_LoadLazyImportTstate (PyThreadState * tstate , PyObject * lazy_import )
463+ {
464+ return lazy_import_resolve_impl (tstate , lazy_import , NULL );
465+ }
466+
467+ // Loading pkg.child can replace a placeholder in pkg.child with the module
468+ // before a from-import retrieves the value that belongs in that binding.
469+ static int
470+ lazy_import_replace_child (PyThreadState * tstate , PyObject * placeholder ,
471+ PyObject * name , PyObject * namespace ,
472+ PyObject * child , PyObject * value )
473+ {
474+ PyLazyImportObject * root = (PyLazyImportObject * )placeholder ;
475+ if (!PyLazyImport_CheckExact (root -> lz_from )) {
476+ return 0 ;
477+ }
478+ while (PyLazyImport_CheckExact (root -> lz_from )) {
479+ root = (PyLazyImportObject * )root -> lz_from ;
480+ }
481+ Py_ssize_t end = PyUnicode_GET_LENGTH (root -> lz_from );
482+ Py_ssize_t dot = PyUnicode_FindChar (root -> lz_from , '.' , 0 , end , -1 );
483+ if (dot < 0 ) {
484+ return dot == -1 ? 0 : -1 ;
485+ }
486+ if (end - dot - 1 != PyUnicode_GET_LENGTH (name )) {
487+ return 0 ;
488+ }
489+ int matches = PyUnicode_Tailmatch (root -> lz_from , name , dot + 1 , end , 1 );
490+ if (matches <= 0 ) {
491+ return matches ;
492+ }
493+ PyObject * parent_name = PyUnicode_Substring (root -> lz_from , 0 , dot );
494+ if (parent_name == NULL ) {
495+ return -1 ;
496+ }
497+ PyObject * modules = Py_XNewRef (_PyImport_GetModules (tstate -> interp ));
498+ PyObject * parent = NULL ;
499+ int rc = 0 ;
500+ if (modules != NULL ) {
501+ rc = PyMapping_GetOptionalItem (modules , parent_name , & parent );
502+ if (rc > 0 && PyModule_Check (parent ) &&
503+ _PyModule_GetDict (parent ) == namespace ) {
504+ rc = _PyDict_ReplaceItemIf (namespace , name , child , value );
505+ }
506+ }
507+ Py_XDECREF (parent );
508+ Py_XDECREF (modules );
509+ Py_DECREF (parent_name );
510+ return rc ;
511+ }
512+
454513PyObject *
455514_PyLazyImport_Reify (PyThreadState * tstate , PyObject * placeholder ,
456515 PyObject * name , PyObject * namespace )
457516{
458- PyObject * value = _PyImport_LoadLazyImportTstate (tstate , placeholder );
517+ PyObject * imported_module = NULL ;
518+ PyObject * value = lazy_import_resolve_impl (
519+ tstate , placeholder , & imported_module );
459520 if (value == NULL ) {
521+ Py_XDECREF (imported_module );
460522 return NULL ;
461523 }
462524 int rc ;
463525 if (PyDict_CheckExact (namespace )) {
464526 rc = _PyDict_ReplaceItemIf (namespace , name , placeholder , value );
527+ if (rc == 0 && imported_module != NULL ) {
528+ rc = lazy_import_replace_child (
529+ tstate , placeholder , name , namespace , imported_module , value );
530+ }
465531 }
466532 else if (Py_TYPE (namespace )-> tp_as_mapping == NULL ||
467533 Py_TYPE (namespace )-> tp_as_mapping -> mp_ass_subscript == NULL ) {
468534 // Read-only namespaces can resolve a value without caching it.
535+ Py_XDECREF (imported_module );
469536 return value ;
470537 }
471538 else {
@@ -483,6 +550,7 @@ _PyLazyImport_Reify(PyThreadState *tstate, PyObject *placeholder,
483550 if (rc < 0 ) {
484551 Py_CLEAR (value );
485552 }
553+ Py_XDECREF (imported_module );
486554 return value ;
487555}
488556
0 commit comments