2020#include "pycore_pylifecycle.h"
2121#include "pycore_pymem.h" // _PyMem_DefaultRawFree()
2222#include "pycore_pystate.h" // _PyInterpreterState_GET()
23+ #include "pycore_setobject.h" // _PySet_NextEntry()
2324#include "pycore_sysmodule.h" // _PySys_ClearAttrString()
2425#include "pycore_time.h" // _PyTime_AsMicroseconds()
2526#include "pycore_unicodeobject.h" // _PyUnicode_AsUTF8NoNUL()
@@ -4129,6 +4130,114 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
41294130 return final_mod ;
41304131}
41314132
4133+ static PyObject *
4134+ get_mod_dict (PyObject * module )
4135+ {
4136+ if (PyModule_Check (module )) {
4137+ return Py_XNewRef (PyModule_GetDict (module ));
4138+ }
4139+
4140+ return PyObject_GetAttr (module , & _Py_ID (__dict__ ));
4141+ }
4142+
4143+ static int
4144+ register_lazy_on_parent (PyThreadState * tstate , PyObject * name , PyObject * import_func )
4145+ {
4146+ int ret = -1 ;
4147+ PyObject * parent = NULL ;
4148+ PyObject * child = NULL ;
4149+ PyObject * parent_module = NULL ;
4150+ PyObject * parent_dict = NULL ;
4151+ PyObject * lazy_modules = tstate -> interp -> imports .lazy_modules ;
4152+ if (lazy_modules == NULL ) {
4153+ lazy_modules = tstate -> interp -> imports .lazy_modules = PyDict_New ();
4154+ if (lazy_modules == NULL ) {
4155+ return -1 ;
4156+ }
4157+ }
4158+
4159+ Py_INCREF (name );
4160+ while (true) {
4161+ Py_ssize_t dot = PyUnicode_FindChar (name , '.' , 0 , PyUnicode_GET_LENGTH (name ), -1 );
4162+ if (dot < 0 ) {
4163+ ret = 0 ;
4164+ goto done ;
4165+ }
4166+ parent = PyUnicode_Substring (name , 0 , dot );
4167+ /* If `parent` is NULL then this has hit the end of the import, no more
4168+ * "parent.child" in the import name. The entire import will be resolved
4169+ * lazily. */
4170+ if (parent == NULL ) {
4171+ goto done ;
4172+ }
4173+ Py_XDECREF (child );
4174+ child = PyUnicode_Substring (name , dot + 1 , PyUnicode_GET_LENGTH (name ));
4175+ if (child == NULL ) {
4176+ goto done ;
4177+ }
4178+
4179+ /* Add the lazy import for the child to the parent */
4180+ Py_XDECREF (parent_module );
4181+ parent_module = PyImport_GetModule (parent );
4182+ if (parent_module == NULL ) {
4183+ if (PyErr_Occurred ()) {
4184+ goto done ;
4185+ }
4186+
4187+ // Record the child to be added when the parent is imported.
4188+ PyObject * lazy_submodules ;
4189+ if (PyDict_GetItemRef (lazy_modules , parent , & lazy_submodules ) < 0 ) {
4190+ goto done ;
4191+ }
4192+ if (lazy_submodules == NULL ) {
4193+ lazy_submodules = PySet_New (NULL );
4194+ if (lazy_submodules == NULL ) {
4195+ goto done ;
4196+ }
4197+ if (PyDict_SetItem (lazy_modules , parent , lazy_submodules ) < 0 ) {
4198+ Py_DECREF (lazy_submodules );
4199+ goto done ;
4200+ }
4201+ }
4202+ if (PySet_Add (lazy_submodules , child ) < 0 ) {
4203+ Py_DECREF (lazy_submodules );
4204+ goto done ;
4205+ }
4206+ Py_DECREF (lazy_submodules );
4207+ } else {
4208+ Py_XDECREF (parent_dict );
4209+ parent_dict = get_mod_dict (parent_module );
4210+ if (parent_dict == NULL ) {
4211+ goto done ;
4212+ }
4213+ if (PyDict_CheckExact (parent_dict ) && !PyDict_Contains (parent_dict , child )) {
4214+ printf ("!!! Adding lazy onto %s %s\n" , PyUnicode_AsUTF8 (parent ), PyUnicode_AsUTF8 (child ));
4215+ PyObject * lazy_module_attr = _PyLazyImport_New (import_func , parent , child );
4216+ if (lazy_module_attr == NULL ) {
4217+ goto done ;
4218+ }
4219+ if (PyDict_SetItem (parent_dict , child , lazy_module_attr ) < 0 ) {
4220+ Py_DECREF (lazy_module_attr );
4221+ goto done ;
4222+ }
4223+ Py_DECREF (lazy_module_attr );
4224+ }
4225+ }
4226+
4227+ Py_DECREF (name );
4228+ name = parent ;
4229+ parent = NULL ;
4230+ }
4231+
4232+ done :
4233+ Py_XDECREF (parent_dict );
4234+ Py_XDECREF (parent_module );
4235+ Py_XDECREF (child );
4236+ Py_XDECREF (parent );
4237+ Py_XDECREF (name );
4238+ return ret ;
4239+ }
4240+
41324241PyObject *
41334242_PyImport_LazyImportModuleLevelObject (PyThreadState * tstate ,
41344243 PyObject * name , PyObject * import_func ,
@@ -4144,6 +4253,14 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate,
41444253 _PyInterpreterFrame * frame = _PyEval_GetFrame ();
41454254 assert (frame -> f_globals == frame -> f_locals ); // should only be called in global scope
41464255
4256+ PyObject * mod = PyImport_GetModule (abs_name );
4257+ bool already_exists = mod != NULL ;
4258+ Py_XDECREF (mod );
4259+ //if (mod != NULL) {
4260+ // Py_DECREF(abs_name);
4261+ // return mod;
4262+ //}
4263+
41474264 // Check if the filter disables the lazy import
41484265 PyObject * filter = LAZY_IMPORTS_FILTER (interp );
41494266 if (filter != NULL ) {
@@ -4175,6 +4292,10 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate,
41754292 }
41764293
41774294 PyObject * res = _PyLazyImport_New (import_func , abs_name , fromlist );
4295+ if (!already_exists && register_lazy_on_parent (tstate , abs_name , import_func ) < 0 ) {
4296+ Py_DECREF (res );
4297+ res = NULL ;
4298+ }
41784299 Py_DECREF (abs_name );
41794300 return res ;
41804301}
@@ -5181,6 +5302,75 @@ _imp_set_lazy_imports_impl(PyObject *module, PyObject *enabled,
51815302 Py_RETURN_NONE ;
51825303}
51835304
5305+ /*[clinic input]
5306+ _imp._set_lazy_attributes
5307+ child_module: object
5308+ name: unicode
5309+ /
5310+ Sets attributes to lazy submodules on the module, as side effects.
5311+ [clinic start generated code]*/
5312+
5313+ static PyObject *
5314+ _imp__set_lazy_attributes_impl (PyObject * module , PyObject * child_module ,
5315+ PyObject * name )
5316+ /*[clinic end generated code: output=bd34f2e16f215c29 input=d959fbfa236f4d59]*/
5317+ {
5318+ PyThreadState * tstate = _PyThreadState_GET ();
5319+ PyObject * child_dict = NULL ;
5320+ PyObject * ret = NULL ;
5321+ PyObject * lazy_modules = tstate -> interp -> imports .lazy_modules ;
5322+ if (lazy_modules != NULL ) {
5323+ PyObject * lazy_submodules = PyDict_GetItemWithError (lazy_modules , name );
5324+ if (lazy_submodules == NULL ) {
5325+ if (PyErr_Occurred ()) {
5326+ goto error ;
5327+ }
5328+ goto done ;
5329+ }
5330+
5331+ child_dict = get_mod_dict (child_module );
5332+ if (child_dict == NULL || !PyDict_CheckExact (child_dict )) {
5333+ goto done ;
5334+ }
5335+ PyObject * attr_name ;
5336+ Py_ssize_t pos = 0 ;
5337+ Py_hash_t hash ;
5338+ while (_PySet_NextEntry (lazy_submodules , & pos , & attr_name , & hash )) {
5339+ if (PyDict_Contains (child_dict , attr_name )) {
5340+ printf ("!!!!!!!! Not replacing %s\n" , PyUnicode_AsUTF8 (attr_name ));
5341+ continue ;
5342+ }
5343+ PyObject * builtins = _PyEval_GetBuiltins (tstate );
5344+ PyObject * import_func ;
5345+ if (PyMapping_GetOptionalItem (builtins , & _Py_ID (__import__ ), & import_func ) < 0 ) {
5346+ goto error ;
5347+ } else if (import_func == NULL ) {
5348+ _PyErr_SetString (tstate , PyExc_ImportError , "__import__ not found" );
5349+ goto error ;
5350+ }
5351+
5352+ PyObject * lazy_module_attr = _PyLazyImport_New (import_func , name , attr_name );
5353+ if (lazy_module_attr == NULL ) {
5354+ goto error ;
5355+ }
5356+
5357+ if (PyDict_SetItem (child_dict , attr_name , lazy_module_attr ) < 0 ) {
5358+ Py_DECREF (lazy_module_attr );
5359+ goto error ;
5360+ }
5361+ Py_DECREF (lazy_module_attr );
5362+ }
5363+ if (PyDict_DelItem (lazy_modules , name ) < 0 ) {
5364+ goto error ;
5365+ }
5366+ }
5367+ done :
5368+ ret = Py_NewRef (Py_None );
5369+
5370+ error :
5371+ Py_XDECREF (child_dict );
5372+ return ret ;
5373+ }
51845374
51855375PyDoc_STRVAR (doc_imp ,
51865376"(Extremely) low-level import machinery bits as used by importlib." );
@@ -5206,6 +5396,7 @@ static PyMethodDef imp_methods[] = {
52065396 _IMP__FIX_CO_FILENAME_METHODDEF
52075397 _IMP_SOURCE_HASH_METHODDEF
52085398 _IMP_SET_LAZY_IMPORTS_METHODDEF
5399+ _IMP__SET_LAZY_ATTRIBUTES_METHODDEF
52095400 {NULL , NULL } /* sentinel */
52105401};
52115402
0 commit comments