From d4f8480dfe89447587550a85b61d4e9faf827e98 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 11 Nov 2017 15:19:47 +0200 Subject: [PATCH] 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. --- Python/_warnings.c | 157 +++++++++++++++++++++++++-------------------- 1 file changed, 89 insertions(+), 68 deletions(-) diff --git a/Python/_warnings.c b/Python/_warnings.c index aa80395caa8..d0f3941ba43 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -52,7 +52,9 @@ get_warnings_attr(const char *attr, int try_import) if (warnings_module == NULL) { /* Fallback to the C implementation if we cannot get the Python implementation */ - PyErr_Clear(); + if (PyErr_ExceptionMatches(PyExc_ImportError)) { + PyErr_Clear(); + } return NULL; } } @@ -62,13 +64,11 @@ get_warnings_attr(const char *attr, int try_import) return NULL; } - if (!PyObject_HasAttrString(warnings_module, attr)) { - Py_DECREF(warnings_module); - return NULL; - } - obj = PyObject_GetAttrString(warnings_module, attr); Py_DECREF(warnings_module); + if (obj == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + } return obj; } @@ -82,16 +82,18 @@ get_once_registry(void) if (registry == NULL) { if (PyErr_Occurred()) return NULL; + assert(_PyRuntime.warnings.once_registry); return _PyRuntime.warnings.once_registry; } if (!PyDict_Check(registry)) { - PyErr_SetString(PyExc_TypeError, - "warnings.onceregistry must be a dict"); + PyErr_Format(PyExc_TypeError, + MODULE_NAME ".onceregistry must be a dict, " + "not '%.200s'", + Py_TYPE(registry)->tp_name); Py_DECREF(registry); return NULL; } - Py_DECREF(_PyRuntime.warnings.once_registry); - _PyRuntime.warnings.once_registry = registry; + Py_SETREF(_PyRuntime.warnings.once_registry, registry); return registry; } @@ -106,6 +108,7 @@ get_default_action(void) if (PyErr_Occurred()) { return NULL; } + assert(_PyRuntime.warnings.default_action); return _PyRuntime.warnings.default_action; } if (!PyUnicode_Check(default_action)) { @@ -116,8 +119,7 @@ get_default_action(void) Py_DECREF(default_action); return NULL; } - Py_DECREF(_PyRuntime.warnings.default_action); - _PyRuntime.warnings.default_action = default_action; + Py_SETREF(_PyRuntime.warnings.default_action, default_action); return default_action; } @@ -137,8 +139,7 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno, return NULL; } else { - Py_DECREF(_PyRuntime.warnings.filters); - _PyRuntime.warnings.filters = warnings_filters; + Py_SETREF(_PyRuntime.warnings.filters, warnings_filters); } PyObject *filters = _PyRuntime.warnings.filters; @@ -408,8 +409,10 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message, warnmsg_cls = get_warnings_attr("WarningMessage", 0); if (warnmsg_cls == NULL) { - PyErr_SetString(PyExc_RuntimeError, - "unable to get warnings.WarningMessage"); + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_RuntimeError, + "unable to get warnings.WarningMessage"); + } goto error; } @@ -837,6 +840,68 @@ warnings_warn_impl(PyObject *module, PyObject *message, PyObject *category, return do_warn(message, category, stacklevel, source); } +static PyObject * +get_source_line(PyObject *module_globals, int lineno) +{ + _Py_IDENTIFIER(get_source); + _Py_IDENTIFIER(__loader__); + _Py_IDENTIFIER(__name__); + PyObject *loader; + PyObject *module_name; + PyObject *get_source; + PyObject *source; + PyObject *source_list; + PyObject *source_line; + + /* Check/get the requisite pieces needed for the loader. */ + loader = _PyDict_GetItemIdWithError(module_globals, &PyId___loader__); + if (loader == NULL) { + return NULL; + } + Py_INCREF(loader); + module_name = _PyDict_GetItemIdWithError(module_globals, &PyId___name__); + if (!module_name) { + Py_DECREF(loader); + return NULL; + } + Py_INCREF(module_name); + + /* Make sure the loader implements the optional get_source() method. */ + get_source = _PyObject_GetAttrId(loader, &PyId_get_source); + Py_DECREF(loader); + if (!get_source) { + Py_DECREF(module_name); + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + } + return NULL; + } + /* Call get_source() to get the source code. */ + source = PyObject_CallFunctionObjArgs(get_source, module_name, NULL); + Py_DECREF(get_source); + Py_DECREF(module_name); + if (!source) { + return NULL; + } + if (source == Py_None) { + Py_DECREF(source); + return NULL; + } + + /* Split the source into lines. */ + source_list = PyUnicode_Splitlines(source, 0); + Py_DECREF(source); + if (!source_list) { + return NULL; + } + + /* Get the source line. */ + source_line = PyList_GetItem(source_list, lineno-1); + Py_XINCREF(source_line); + Py_DECREF(source_list); + return source_line; +} + static PyObject * warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds) { @@ -851,6 +916,8 @@ warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds) PyObject *registry = NULL; PyObject *module_globals = NULL; PyObject *sourceobj = NULL; + PyObject *source_line = NULL; + PyObject *returned; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOUi|OOOO:warn_explicit", kwd_list, &message, &category, &filename, &lineno, &module, @@ -858,61 +925,15 @@ warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds) return NULL; if (module_globals) { - _Py_IDENTIFIER(get_source); - PyObject *tmp; - PyObject *loader; - PyObject *module_name; - PyObject *source; - PyObject *source_list; - PyObject *source_line; - PyObject *returned; - - if ((tmp = _PyUnicode_FromId(&PyId_get_source)) == NULL) - return NULL; - - /* Check/get the requisite pieces needed for the loader. */ - loader = PyDict_GetItemString(module_globals, "__loader__"); - module_name = PyDict_GetItemString(module_globals, "__name__"); - - if (loader == NULL || module_name == NULL) - goto standard_call; - - /* Make sure the loader implements the optional get_source() method. */ - if (!_PyObject_HasAttrId(loader, &PyId_get_source)) - goto standard_call; - /* Call get_source() to get the source code. */ - source = PyObject_CallMethodObjArgs(loader, PyId_get_source.object, - module_name, NULL); - if (!source) - return NULL; - else if (source == Py_None) { - Py_DECREF(Py_None); - goto standard_call; - } - - /* Split the source into lines. */ - source_list = PyUnicode_Splitlines(source, 0); - Py_DECREF(source); - if (!source_list) - return NULL; - - /* Get the source line. */ - source_line = PyList_GetItem(source_list, lineno-1); - if (!source_line) { - Py_DECREF(source_list); + source_line = get_source_line(module_globals, lineno); + if (source_line == NULL && PyErr_Occurred()) { return NULL; } - - /* Handle the warning. */ - returned = warn_explicit(category, message, filename, lineno, module, - registry, source_line, sourceobj); - Py_DECREF(source_list); - return returned; } - - standard_call: - return warn_explicit(category, message, filename, lineno, module, - registry, NULL, sourceobj); + returned = warn_explicit(category, message, filename, lineno, module, + registry, source_line, sourceobj); + Py_XDECREF(source_line); + return returned; } static PyObject *