@@ -698,6 +698,27 @@ module_repr(PyModuleObject *m)
698698 return PyObject_CallMethod (interp -> importlib , "_module_repr" , "O" , m );
699699}
700700
701+ /* Check if the "_initializing" attribute of the module spec is set to true.
702+ Clear the exception and return 0 if spec is NULL.
703+ */
704+ int
705+ _PyModuleSpec_IsInitializing (PyObject * spec )
706+ {
707+ if (spec != NULL ) {
708+ _Py_IDENTIFIER (_initializing );
709+ PyObject * value = _PyObject_GetAttrId (spec , & PyId__initializing );
710+ if (value != NULL ) {
711+ int initializing = PyObject_IsTrue (value );
712+ Py_DECREF (value );
713+ if (initializing >= 0 ) {
714+ return initializing ;
715+ }
716+ }
717+ }
718+ PyErr_Clear ();
719+ return 0 ;
720+ }
721+
701722static PyObject *
702723module_getattro (PyModuleObject * m , PyObject * name )
703724{
@@ -717,8 +738,24 @@ module_getattro(PyModuleObject *m, PyObject *name)
717738 _Py_IDENTIFIER (__name__ );
718739 mod_name = _PyDict_GetItemId (m -> md_dict , & PyId___name__ );
719740 if (mod_name && PyUnicode_Check (mod_name )) {
720- PyErr_Format (PyExc_AttributeError ,
721- "module '%U' has no attribute '%U'" , mod_name , name );
741+ _Py_IDENTIFIER (__spec__ );
742+ Py_INCREF (mod_name );
743+ PyObject * spec = _PyDict_GetItemId (m -> md_dict , & PyId___spec__ );
744+ Py_XINCREF (spec );
745+ if (_PyModuleSpec_IsInitializing (spec )) {
746+ PyErr_Format (PyExc_AttributeError ,
747+ "partially initialized "
748+ "module '%U' has no attribute '%U' "
749+ "(most likely due to a circular import)" ,
750+ mod_name , name );
751+ }
752+ else {
753+ PyErr_Format (PyExc_AttributeError ,
754+ "module '%U' has no attribute '%U'" ,
755+ mod_name , name );
756+ }
757+ Py_XDECREF (spec );
758+ Py_DECREF (mod_name );
722759 return NULL ;
723760 }
724761 }
0 commit comments