From 82656276caf4cb889193572d2d14dbc5f3d2bdff Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 22 Nov 2017 23:51:42 +0100 Subject: bpo-27535: Optimize warnings.warn() (#4508) * Optimize warnings.filterwarnings(). Replace re.compile('') with None to avoid the cost of calling a regex.match() method, whereas it always matchs. * Optimize get_warnings_attr(): replace PyObject_GetAttrString() with _PyObject_GetAttrId(). Cleanup also create_filter(): * Use _Py_IDENTIFIER() to allow to cleanup strings at Python finalization * Replace Py_FatalError() with a regular exceptions --- Lib/warnings.py | 17 ++++++++++--- Python/_warnings.c | 75 ++++++++++++++++++++++++------------------------------ 2 files changed, 47 insertions(+), 45 deletions(-) diff --git a/Lib/warnings.py b/Lib/warnings.py index b2605f8..5b62569 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -128,7 +128,6 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0, 'lineno' -- an integer line number, 0 matches all warnings 'append' -- if true, append to the list of filters """ - import re assert action in ("error", "ignore", "always", "default", "module", "once"), "invalid action: %r" % (action,) assert isinstance(message, str), "message must be a string" @@ -137,8 +136,20 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0, assert isinstance(module, str), "module must be a string" assert isinstance(lineno, int) and lineno >= 0, \ "lineno must be an int >= 0" - _add_filter(action, re.compile(message, re.I), category, - re.compile(module), lineno, append=append) + + if message or module: + import re + + if message: + message = re.compile(message, re.I) + else: + message = None + if module: + module = re.compile(module) + else: + module = None + + _add_filter(action, message, category, module, lineno, append=append) def simplefilter(action, category=Warning, lineno=0, append=False): """Insert a simple entry into the list of warnings filters (at the front). diff --git a/Python/_warnings.c b/Python/_warnings.c index f2110ed..d865f0a 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -35,15 +35,15 @@ check_matched(PyObject *obj, PyObject *arg) A NULL return value can mean false or an error. */ static PyObject * -get_warnings_attr(const char *attr, int try_import) +get_warnings_attr(_Py_Identifier *attr_id, int try_import) { - static PyObject *warnings_str = NULL; + PyObject *warnings_str; PyObject *warnings_module, *obj; + _Py_IDENTIFIER(warnings); + warnings_str = _PyUnicode_FromId(&PyId_warnings); if (warnings_str == NULL) { - warnings_str = PyUnicode_InternFromString("warnings"); - if (warnings_str == NULL) - return NULL; + return NULL; } /* don't try to import after the start of the Python finallization */ @@ -64,7 +64,7 @@ get_warnings_attr(const char *attr, int try_import) return NULL; } - obj = PyObject_GetAttrString(warnings_module, attr); + obj = _PyObject_GetAttrId(warnings_module, attr_id); Py_DECREF(warnings_module); if (obj == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { PyErr_Clear(); @@ -77,8 +77,9 @@ static PyObject * get_once_registry(void) { PyObject *registry; + _Py_IDENTIFIER(onceregistry); - registry = get_warnings_attr("onceregistry", 0); + registry = get_warnings_attr(&PyId_onceregistry, 0); if (registry == NULL) { if (PyErr_Occurred()) return NULL; @@ -102,8 +103,9 @@ static PyObject * get_default_action(void) { PyObject *default_action; + _Py_IDENTIFIER(defaultaction); - default_action = get_warnings_attr("defaultaction", 0); + default_action = get_warnings_attr(&PyId_defaultaction, 0); if (default_action == NULL) { if (PyErr_Occurred()) { return NULL; @@ -132,8 +134,9 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno, PyObject *action; Py_ssize_t i; PyObject *warnings_filters; + _Py_IDENTIFIER(filters); - warnings_filters = get_warnings_attr("filters", 0); + warnings_filters = get_warnings_attr(&PyId_filters, 0); if (warnings_filters == NULL) { if (PyErr_Occurred()) return NULL; @@ -389,11 +392,13 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message, PyObject *sourceline, PyObject *source) { PyObject *show_fn, *msg, *res, *warnmsg_cls = NULL; + _Py_IDENTIFIER(_showwarnmsg); + _Py_IDENTIFIER(WarningMessage); /* If the source parameter is set, try to get the Python implementation. The Python implementation is able to log the traceback where the source was allocated, whereas the C implementation doesn't. */ - show_fn = get_warnings_attr("_showwarnmsg", source != NULL); + show_fn = get_warnings_attr(&PyId__showwarnmsg, source != NULL); if (show_fn == NULL) { if (PyErr_Occurred()) return -1; @@ -407,7 +412,7 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message, goto error; } - warnmsg_cls = get_warnings_attr("WarningMessage", 0); + warnmsg_cls = get_warnings_attr(&PyId_WarningMessage, 0); if (warnmsg_cls == NULL) { if (!PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, @@ -1146,50 +1151,36 @@ static PyMethodDef warnings_functions[] = { static PyObject * create_filter(PyObject *category, const char *action) { - static PyObject *ignore_str = NULL; - static PyObject *error_str = NULL; - static PyObject *default_str = NULL; - static PyObject *always_str = NULL; - PyObject *action_obj = NULL; + _Py_IDENTIFIER(ignore); + _Py_IDENTIFIER(error); + _Py_IDENTIFIER(always); + _Py_static_string(PyId_default, "default"); + _Py_Identifier *id; if (!strcmp(action, "ignore")) { - if (ignore_str == NULL) { - ignore_str = PyUnicode_InternFromString("ignore"); - if (ignore_str == NULL) - return NULL; - } - action_obj = ignore_str; + id = &PyId_ignore; } else if (!strcmp(action, "error")) { - if (error_str == NULL) { - error_str = PyUnicode_InternFromString("error"); - if (error_str == NULL) - return NULL; - } - action_obj = error_str; + id = &PyId_error; } else if (!strcmp(action, "default")) { - if (default_str == NULL) { - default_str = PyUnicode_InternFromString("default"); - if (default_str == NULL) - return NULL; - } - action_obj = default_str; + id = &PyId_default; } else if (!strcmp(action, "always")) { - if (always_str == NULL) { - always_str = PyUnicode_InternFromString("always"); - if (always_str == NULL) - return NULL; - } - action_obj = always_str; + id = &PyId_always; } else { - Py_FatalError("unknown action"); + PyErr_SetString(PyExc_ValueError, "unknown action"); + return NULL; + } + + PyObject *action_str = _PyUnicode_FromId(id); + if (action_str == NULL) { + return NULL; } /* This assumes the line number is zero for now. */ - return PyTuple_Pack(5, action_obj, Py_None, + return PyTuple_Pack(5, action_str, Py_None, category, Py_None, _PyLong_Zero); } -- cgit v0.12