bpo-33237: Improve AttributeError message for partially initialized m… · python/cpython@3e429dc

@@ -698,6 +698,27 @@ module_repr(PyModuleObject *m)

698698

return 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+701722

static PyObject*

702723

module_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

}