Skip to content

Commit 8660fb7

Browse files
gh-112660: Do not clear arbitrary errors on import (GH-112661)
Previously arbitrary errors could be cleared during formatting error messages for ImportError or AttributeError for modules. Now all unexpected errors are reported.
1 parent 953ee62 commit 8660fb7

File tree

4 files changed

+72
-58
lines changed

4 files changed

+72
-58
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Do not clear unexpected errors during formatting error messages for
2+
ImportError and AttributeError for modules.

Objects/moduleobject.c

+25-32
Original file line numberDiff line numberDiff line change
@@ -749,27 +749,20 @@ module_repr(PyModuleObject *m)
749749
}
750750

751751
/* Check if the "_initializing" attribute of the module spec is set to true.
752-
Clear the exception and return 0 if spec is NULL.
753752
*/
754753
int
755754
_PyModuleSpec_IsInitializing(PyObject *spec)
756755
{
757-
if (spec != NULL) {
758-
PyObject *value;
759-
int ok = PyObject_GetOptionalAttr(spec, &_Py_ID(_initializing), &value);
760-
if (ok == 0) {
761-
return 0;
762-
}
763-
if (value != NULL) {
764-
int initializing = PyObject_IsTrue(value);
765-
Py_DECREF(value);
766-
if (initializing >= 0) {
767-
return initializing;
768-
}
769-
}
756+
if (spec == NULL) {
757+
return 0;
770758
}
771-
PyErr_Clear();
772-
return 0;
759+
PyObject *value;
760+
int rc = PyObject_GetOptionalAttr(spec, &_Py_ID(_initializing), &value);
761+
if (rc > 0) {
762+
rc = PyObject_IsTrue(value);
763+
Py_DECREF(value);
764+
}
765+
return rc;
773766
}
774767

775768
/* Check if the submodule name is in the "_uninitialized_submodules" attribute
@@ -782,17 +775,13 @@ _PyModuleSpec_IsUninitializedSubmodule(PyObject *spec, PyObject *name)
782775
return 0;
783776
}
784777

785-
PyObject *value = PyObject_GetAttr(spec, &_Py_ID(_uninitialized_submodules));
786-
if (value == NULL) {
787-
return 0;
778+
PyObject *value;
779+
int rc = PyObject_GetOptionalAttr(spec, &_Py_ID(_uninitialized_submodules), &value);
780+
if (rc > 0) {
781+
rc = PySequence_Contains(value, name);
782+
Py_DECREF(value);
788783
}
789-
790-
int is_uninitialized = PySequence_Contains(value, name);
791-
Py_DECREF(value);
792-
if (is_uninitialized == -1) {
793-
return 0;
794-
}
795-
return is_uninitialized;
784+
return rc;
796785
}
797786

798787
PyObject*
@@ -840,23 +829,27 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress)
840829
return NULL;
841830
}
842831
if (suppress != 1) {
843-
if (_PyModuleSpec_IsInitializing(spec)) {
832+
int rc = _PyModuleSpec_IsInitializing(spec);
833+
if (rc > 0) {
844834
PyErr_Format(PyExc_AttributeError,
845835
"partially initialized "
846836
"module '%U' has no attribute '%U' "
847837
"(most likely due to a circular import)",
848838
mod_name, name);
849839
}
850-
else if (_PyModuleSpec_IsUninitializedSubmodule(spec, name)) {
851-
PyErr_Format(PyExc_AttributeError,
840+
else if (rc == 0) {
841+
rc = _PyModuleSpec_IsUninitializedSubmodule(spec, name);
842+
if (rc > 0) {
843+
PyErr_Format(PyExc_AttributeError,
852844
"cannot access submodule '%U' of module '%U' "
853845
"(most likely due to a circular import)",
854846
name, mod_name);
855-
}
856-
else {
857-
PyErr_Format(PyExc_AttributeError,
847+
}
848+
else if (rc == 0) {
849+
PyErr_Format(PyExc_AttributeError,
858850
"module '%U' has no attribute '%U'",
859851
mod_name, name);
852+
}
860853
}
861854
}
862855
Py_XDECREF(spec);

Python/ceval.c

+31-15
Original file line numberDiff line numberDiff line change
@@ -2614,11 +2614,10 @@ import_from(PyThreadState *tstate, PyObject *v, PyObject *name)
26142614
/* Issue #17636: in case this failed because of a circular relative
26152615
import, try to fallback on reading the module directly from
26162616
sys.modules. */
2617-
pkgname = PyObject_GetAttr(v, &_Py_ID(__name__));
2618-
if (pkgname == NULL) {
2619-
goto error;
2617+
if (PyObject_GetOptionalAttr(v, &_Py_ID(__name__), &pkgname) < 0) {
2618+
return NULL;
26202619
}
2621-
if (!PyUnicode_Check(pkgname)) {
2620+
if (pkgname == NULL || !PyUnicode_Check(pkgname)) {
26222621
Py_CLEAR(pkgname);
26232622
goto error;
26242623
}
@@ -2635,42 +2634,59 @@ import_from(PyThreadState *tstate, PyObject *v, PyObject *name)
26352634
Py_DECREF(pkgname);
26362635
return x;
26372636
error:
2638-
pkgpath = PyModule_GetFilenameObject(v);
26392637
if (pkgname == NULL) {
26402638
pkgname_or_unknown = PyUnicode_FromString("<unknown module name>");
26412639
if (pkgname_or_unknown == NULL) {
2642-
Py_XDECREF(pkgpath);
26432640
return NULL;
26442641
}
26452642
} else {
26462643
pkgname_or_unknown = pkgname;
26472644
}
26482645

2646+
pkgpath = NULL;
2647+
if (PyModule_Check(v)) {
2648+
pkgpath = PyModule_GetFilenameObject(v);
2649+
if (pkgpath == NULL) {
2650+
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
2651+
Py_DECREF(pkgname_or_unknown);
2652+
return NULL;
2653+
}
2654+
// module filename missing
2655+
_PyErr_Clear(tstate);
2656+
}
2657+
}
26492658
if (pkgpath == NULL || !PyUnicode_Check(pkgpath)) {
2650-
_PyErr_Clear(tstate);
2659+
Py_CLEAR(pkgpath);
26512660
errmsg = PyUnicode_FromFormat(
26522661
"cannot import name %R from %R (unknown location)",
26532662
name, pkgname_or_unknown
26542663
);
2655-
/* NULL checks for errmsg and pkgname done by PyErr_SetImportError. */
2656-
_PyErr_SetImportErrorWithNameFrom(errmsg, pkgname, NULL, name);
26572664
}
26582665
else {
2659-
PyObject *spec = PyObject_GetAttr(v, &_Py_ID(__spec__));
2666+
PyObject *spec;
2667+
int rc = PyObject_GetOptionalAttr(v, &_Py_ID(__spec__), &spec);
2668+
if (rc > 0) {
2669+
rc = _PyModuleSpec_IsInitializing(spec);
2670+
Py_DECREF(spec);
2671+
}
2672+
if (rc < 0) {
2673+
Py_DECREF(pkgname_or_unknown);
2674+
Py_DECREF(pkgpath);
2675+
return NULL;
2676+
}
26602677
const char *fmt =
2661-
_PyModuleSpec_IsInitializing(spec) ?
2678+
rc ?
26622679
"cannot import name %R from partially initialized module %R "
26632680
"(most likely due to a circular import) (%S)" :
26642681
"cannot import name %R from %R (%S)";
2665-
Py_XDECREF(spec);
26662682

26672683
errmsg = PyUnicode_FromFormat(fmt, name, pkgname_or_unknown, pkgpath);
2668-
/* NULL checks for errmsg and pkgname done by PyErr_SetImportError. */
2669-
_PyErr_SetImportErrorWithNameFrom(errmsg, pkgname, pkgpath, name);
26702684
}
2685+
/* NULL checks for errmsg and pkgname done by PyErr_SetImportError. */
2686+
_PyErr_SetImportErrorWithNameFrom(errmsg, pkgname, pkgpath, name);
26712687

26722688
Py_XDECREF(errmsg);
2673-
Py_XDECREF(pkgname_or_unknown);
2689+
Py_DECREF(pkgname_or_unknown);
26742690
Py_XDECREF(pkgpath);
26752691
return NULL;
26762692
}

Python/import.c

+14-11
Original file line numberDiff line numberDiff line change
@@ -252,18 +252,21 @@ import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *n
252252
NOTE: because of this, initializing must be set *before*
253253
stuffing the new module in sys.modules.
254254
*/
255-
spec = PyObject_GetAttr(mod, &_Py_ID(__spec__));
256-
int busy = _PyModuleSpec_IsInitializing(spec);
257-
Py_XDECREF(spec);
258-
if (busy) {
259-
/* Wait until module is done importing. */
260-
PyObject *value = PyObject_CallMethodOneArg(
261-
IMPORTLIB(interp), &_Py_ID(_lock_unlock_module), name);
262-
if (value == NULL) {
263-
return -1;
264-
}
265-
Py_DECREF(value);
255+
int rc = PyObject_GetOptionalAttr(mod, &_Py_ID(__spec__), &spec);
256+
if (rc > 0) {
257+
rc = _PyModuleSpec_IsInitializing(spec);
258+
Py_DECREF(spec);
259+
}
260+
if (rc <= 0) {
261+
return rc;
266262
}
263+
/* Wait until module is done importing. */
264+
PyObject *value = PyObject_CallMethodOneArg(
265+
IMPORTLIB(interp), &_Py_ID(_lock_unlock_module), name);
266+
if (value == NULL) {
267+
return -1;
268+
}
269+
Py_DECREF(value);
267270
return 0;
268271
}
269272

0 commit comments

Comments
 (0)