bpo-33237: Improve AttributeError message for partially initialized m… · python/cpython@3e429dc
@@ -698,6 +698,27 @@ module_repr(PyModuleObject *m)
698698return PyObject_CallMethod(interp->importlib, "_module_repr", "O", m);
699699}
700700701+/* 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__);
718739mod_name = _PyDict_GetItemId(m->md_dict, &PyId___name__);
719740if (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);
722759return NULL;
723760 }
724761 }