@@ -698,6 +698,27 @@ module_repr(PyModuleObject *m)
698
698
return PyObject_CallMethod (interp -> importlib , "_module_repr" , "O" , m );
699
699
}
700
700
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
+
701
722
static PyObject *
702
723
module_getattro (PyModuleObject * m , PyObject * name )
703
724
{
@@ -717,8 +738,24 @@ module_getattro(PyModuleObject *m, PyObject *name)
717
738
_Py_IDENTIFIER (__name__ );
718
739
mod_name = _PyDict_GetItemId (m -> md_dict , & PyId___name__ );
719
740
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 );
722
759
return NULL ;
723
760
}
724
761
}
0 commit comments