Skip to content

Commit d4f8480

Browse files
bpo-31572: Don't silence unexpected errors in the _warnings module. (#3731)
Get rid of _PyObject_HasAttrId() and PyDict_GetItemString(). Silence only expected AttributeError, KeyError and ImportError when get an attribute, look up in a dict or import a module.
1 parent e2f92de commit d4f8480

File tree

1 file changed

+89
-68
lines changed

1 file changed

+89
-68
lines changed

Python/_warnings.c

Lines changed: 89 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ get_warnings_attr(const char *attr, int try_import)
5252
if (warnings_module == NULL) {
5353
/* Fallback to the C implementation if we cannot get
5454
the Python implementation */
55-
PyErr_Clear();
55+
if (PyErr_ExceptionMatches(PyExc_ImportError)) {
56+
PyErr_Clear();
57+
}
5658
return NULL;
5759
}
5860
}
@@ -62,13 +64,11 @@ get_warnings_attr(const char *attr, int try_import)
6264
return NULL;
6365
}
6466

65-
if (!PyObject_HasAttrString(warnings_module, attr)) {
66-
Py_DECREF(warnings_module);
67-
return NULL;
68-
}
69-
7067
obj = PyObject_GetAttrString(warnings_module, attr);
7168
Py_DECREF(warnings_module);
69+
if (obj == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
70+
PyErr_Clear();
71+
}
7272
return obj;
7373
}
7474

@@ -82,16 +82,18 @@ get_once_registry(void)
8282
if (registry == NULL) {
8383
if (PyErr_Occurred())
8484
return NULL;
85+
assert(_PyRuntime.warnings.once_registry);
8586
return _PyRuntime.warnings.once_registry;
8687
}
8788
if (!PyDict_Check(registry)) {
88-
PyErr_SetString(PyExc_TypeError,
89-
"warnings.onceregistry must be a dict");
89+
PyErr_Format(PyExc_TypeError,
90+
MODULE_NAME ".onceregistry must be a dict, "
91+
"not '%.200s'",
92+
Py_TYPE(registry)->tp_name);
9093
Py_DECREF(registry);
9194
return NULL;
9295
}
93-
Py_DECREF(_PyRuntime.warnings.once_registry);
94-
_PyRuntime.warnings.once_registry = registry;
96+
Py_SETREF(_PyRuntime.warnings.once_registry, registry);
9597
return registry;
9698
}
9799

@@ -106,6 +108,7 @@ get_default_action(void)
106108
if (PyErr_Occurred()) {
107109
return NULL;
108110
}
111+
assert(_PyRuntime.warnings.default_action);
109112
return _PyRuntime.warnings.default_action;
110113
}
111114
if (!PyUnicode_Check(default_action)) {
@@ -116,8 +119,7 @@ get_default_action(void)
116119
Py_DECREF(default_action);
117120
return NULL;
118121
}
119-
Py_DECREF(_PyRuntime.warnings.default_action);
120-
_PyRuntime.warnings.default_action = default_action;
122+
Py_SETREF(_PyRuntime.warnings.default_action, default_action);
121123
return default_action;
122124
}
123125

@@ -137,8 +139,7 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
137139
return NULL;
138140
}
139141
else {
140-
Py_DECREF(_PyRuntime.warnings.filters);
141-
_PyRuntime.warnings.filters = warnings_filters;
142+
Py_SETREF(_PyRuntime.warnings.filters, warnings_filters);
142143
}
143144

144145
PyObject *filters = _PyRuntime.warnings.filters;
@@ -408,8 +409,10 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message,
408409

409410
warnmsg_cls = get_warnings_attr("WarningMessage", 0);
410411
if (warnmsg_cls == NULL) {
411-
PyErr_SetString(PyExc_RuntimeError,
412-
"unable to get warnings.WarningMessage");
412+
if (!PyErr_Occurred()) {
413+
PyErr_SetString(PyExc_RuntimeError,
414+
"unable to get warnings.WarningMessage");
415+
}
413416
goto error;
414417
}
415418

@@ -837,6 +840,68 @@ warnings_warn_impl(PyObject *module, PyObject *message, PyObject *category,
837840
return do_warn(message, category, stacklevel, source);
838841
}
839842

843+
static PyObject *
844+
get_source_line(PyObject *module_globals, int lineno)
845+
{
846+
_Py_IDENTIFIER(get_source);
847+
_Py_IDENTIFIER(__loader__);
848+
_Py_IDENTIFIER(__name__);
849+
PyObject *loader;
850+
PyObject *module_name;
851+
PyObject *get_source;
852+
PyObject *source;
853+
PyObject *source_list;
854+
PyObject *source_line;
855+
856+
/* Check/get the requisite pieces needed for the loader. */
857+
loader = _PyDict_GetItemIdWithError(module_globals, &PyId___loader__);
858+
if (loader == NULL) {
859+
return NULL;
860+
}
861+
Py_INCREF(loader);
862+
module_name = _PyDict_GetItemIdWithError(module_globals, &PyId___name__);
863+
if (!module_name) {
864+
Py_DECREF(loader);
865+
return NULL;
866+
}
867+
Py_INCREF(module_name);
868+
869+
/* Make sure the loader implements the optional get_source() method. */
870+
get_source = _PyObject_GetAttrId(loader, &PyId_get_source);
871+
Py_DECREF(loader);
872+
if (!get_source) {
873+
Py_DECREF(module_name);
874+
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
875+
PyErr_Clear();
876+
}
877+
return NULL;
878+
}
879+
/* Call get_source() to get the source code. */
880+
source = PyObject_CallFunctionObjArgs(get_source, module_name, NULL);
881+
Py_DECREF(get_source);
882+
Py_DECREF(module_name);
883+
if (!source) {
884+
return NULL;
885+
}
886+
if (source == Py_None) {
887+
Py_DECREF(source);
888+
return NULL;
889+
}
890+
891+
/* Split the source into lines. */
892+
source_list = PyUnicode_Splitlines(source, 0);
893+
Py_DECREF(source);
894+
if (!source_list) {
895+
return NULL;
896+
}
897+
898+
/* Get the source line. */
899+
source_line = PyList_GetItem(source_list, lineno-1);
900+
Py_XINCREF(source_line);
901+
Py_DECREF(source_list);
902+
return source_line;
903+
}
904+
840905
static PyObject *
841906
warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds)
842907
{
@@ -851,68 +916,24 @@ warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds)
851916
PyObject *registry = NULL;
852917
PyObject *module_globals = NULL;
853918
PyObject *sourceobj = NULL;
919+
PyObject *source_line = NULL;
920+
PyObject *returned;
854921

855922
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOUi|OOOO:warn_explicit",
856923
kwd_list, &message, &category, &filename, &lineno, &module,
857924
&registry, &module_globals, &sourceobj))
858925
return NULL;
859926

860927
if (module_globals) {
861-
_Py_IDENTIFIER(get_source);
862-
PyObject *tmp;
863-
PyObject *loader;
864-
PyObject *module_name;
865-
PyObject *source;
866-
PyObject *source_list;
867-
PyObject *source_line;
868-
PyObject *returned;
869-
870-
if ((tmp = _PyUnicode_FromId(&PyId_get_source)) == NULL)
871-
return NULL;
872-
873-
/* Check/get the requisite pieces needed for the loader. */
874-
loader = PyDict_GetItemString(module_globals, "__loader__");
875-
module_name = PyDict_GetItemString(module_globals, "__name__");
876-
877-
if (loader == NULL || module_name == NULL)
878-
goto standard_call;
879-
880-
/* Make sure the loader implements the optional get_source() method. */
881-
if (!_PyObject_HasAttrId(loader, &PyId_get_source))
882-
goto standard_call;
883-
/* Call get_source() to get the source code. */
884-
source = PyObject_CallMethodObjArgs(loader, PyId_get_source.object,
885-
module_name, NULL);
886-
if (!source)
928+
source_line = get_source_line(module_globals, lineno);
929+
if (source_line == NULL && PyErr_Occurred()) {
887930
return NULL;
888-
else if (source == Py_None) {
889-
Py_DECREF(Py_None);
890-
goto standard_call;
891931
}
892-
893-
/* Split the source into lines. */
894-
source_list = PyUnicode_Splitlines(source, 0);
895-
Py_DECREF(source);
896-
if (!source_list)
897-
return NULL;
898-
899-
/* Get the source line. */
900-
source_line = PyList_GetItem(source_list, lineno-1);
901-
if (!source_line) {
902-
Py_DECREF(source_list);
903-
return NULL;
904-
}
905-
906-
/* Handle the warning. */
907-
returned = warn_explicit(category, message, filename, lineno, module,
908-
registry, source_line, sourceobj);
909-
Py_DECREF(source_list);
910-
return returned;
911932
}
912-
913-
standard_call:
914-
return warn_explicit(category, message, filename, lineno, module,
915-
registry, NULL, sourceobj);
933+
returned = warn_explicit(category, message, filename, lineno, module,
934+
registry, source_line, sourceobj);
935+
Py_XDECREF(source_line);
936+
return returned;
916937
}
917938

918939
static PyObject *

0 commit comments

Comments
 (0)