Commit 82656276 authored by Victor Stinner's avatar Victor Stinner Committed by GitHub

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
parent bb11c3c9
...@@ -128,7 +128,6 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0, ...@@ -128,7 +128,6 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0,
'lineno' -- an integer line number, 0 matches all warnings 'lineno' -- an integer line number, 0 matches all warnings
'append' -- if true, append to the list of filters 'append' -- if true, append to the list of filters
""" """
import re
assert action in ("error", "ignore", "always", "default", "module", assert action in ("error", "ignore", "always", "default", "module",
"once"), "invalid action: %r" % (action,) "once"), "invalid action: %r" % (action,)
assert isinstance(message, str), "message must be a string" assert isinstance(message, str), "message must be a string"
...@@ -137,8 +136,20 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0, ...@@ -137,8 +136,20 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0,
assert isinstance(module, str), "module must be a string" assert isinstance(module, str), "module must be a string"
assert isinstance(lineno, int) and lineno >= 0, \ assert isinstance(lineno, int) and lineno >= 0, \
"lineno must be an int >= 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): def simplefilter(action, category=Warning, lineno=0, append=False):
"""Insert a simple entry into the list of warnings filters (at the front). """Insert a simple entry into the list of warnings filters (at the front).
......
...@@ -35,15 +35,15 @@ check_matched(PyObject *obj, PyObject *arg) ...@@ -35,15 +35,15 @@ check_matched(PyObject *obj, PyObject *arg)
A NULL return value can mean false or an error. A NULL return value can mean false or an error.
*/ */
static PyObject * 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; PyObject *warnings_module, *obj;
_Py_IDENTIFIER(warnings);
warnings_str = _PyUnicode_FromId(&PyId_warnings);
if (warnings_str == NULL) { if (warnings_str == NULL) {
warnings_str = PyUnicode_InternFromString("warnings"); return NULL;
if (warnings_str == NULL)
return NULL;
} }
/* don't try to import after the start of the Python finallization */ /* 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) ...@@ -64,7 +64,7 @@ get_warnings_attr(const char *attr, int try_import)
return NULL; return NULL;
} }
obj = PyObject_GetAttrString(warnings_module, attr); obj = _PyObject_GetAttrId(warnings_module, attr_id);
Py_DECREF(warnings_module); Py_DECREF(warnings_module);
if (obj == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { if (obj == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear(); PyErr_Clear();
...@@ -77,8 +77,9 @@ static PyObject * ...@@ -77,8 +77,9 @@ static PyObject *
get_once_registry(void) get_once_registry(void)
{ {
PyObject *registry; PyObject *registry;
_Py_IDENTIFIER(onceregistry);
registry = get_warnings_attr("onceregistry", 0); registry = get_warnings_attr(&PyId_onceregistry, 0);
if (registry == NULL) { if (registry == NULL) {
if (PyErr_Occurred()) if (PyErr_Occurred())
return NULL; return NULL;
...@@ -102,8 +103,9 @@ static PyObject * ...@@ -102,8 +103,9 @@ static PyObject *
get_default_action(void) get_default_action(void)
{ {
PyObject *default_action; 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 (default_action == NULL) {
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
return NULL; return NULL;
...@@ -132,8 +134,9 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno, ...@@ -132,8 +134,9 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
PyObject *action; PyObject *action;
Py_ssize_t i; Py_ssize_t i;
PyObject *warnings_filters; 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 (warnings_filters == NULL) {
if (PyErr_Occurred()) if (PyErr_Occurred())
return NULL; return NULL;
...@@ -389,11 +392,13 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message, ...@@ -389,11 +392,13 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message,
PyObject *sourceline, PyObject *source) PyObject *sourceline, PyObject *source)
{ {
PyObject *show_fn, *msg, *res, *warnmsg_cls = NULL; 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. /* If the source parameter is set, try to get the Python implementation.
The Python implementation is able to log the traceback where the source The Python implementation is able to log the traceback where the source
was allocated, whereas the C implementation doesn't. */ 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 (show_fn == NULL) {
if (PyErr_Occurred()) if (PyErr_Occurred())
return -1; return -1;
...@@ -407,7 +412,7 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message, ...@@ -407,7 +412,7 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message,
goto error; goto error;
} }
warnmsg_cls = get_warnings_attr("WarningMessage", 0); warnmsg_cls = get_warnings_attr(&PyId_WarningMessage, 0);
if (warnmsg_cls == NULL) { if (warnmsg_cls == NULL) {
if (!PyErr_Occurred()) { if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_RuntimeError, PyErr_SetString(PyExc_RuntimeError,
...@@ -1146,50 +1151,36 @@ static PyMethodDef warnings_functions[] = { ...@@ -1146,50 +1151,36 @@ static PyMethodDef warnings_functions[] = {
static PyObject * static PyObject *
create_filter(PyObject *category, const char *action) create_filter(PyObject *category, const char *action)
{ {
static PyObject *ignore_str = NULL; _Py_IDENTIFIER(ignore);
static PyObject *error_str = NULL; _Py_IDENTIFIER(error);
static PyObject *default_str = NULL; _Py_IDENTIFIER(always);
static PyObject *always_str = NULL; _Py_static_string(PyId_default, "default");
PyObject *action_obj = NULL; _Py_Identifier *id;
if (!strcmp(action, "ignore")) { if (!strcmp(action, "ignore")) {
if (ignore_str == NULL) { id = &PyId_ignore;
ignore_str = PyUnicode_InternFromString("ignore");
if (ignore_str == NULL)
return NULL;
}
action_obj = ignore_str;
} }
else if (!strcmp(action, "error")) { else if (!strcmp(action, "error")) {
if (error_str == NULL) { id = &PyId_error;
error_str = PyUnicode_InternFromString("error");
if (error_str == NULL)
return NULL;
}
action_obj = error_str;
} }
else if (!strcmp(action, "default")) { else if (!strcmp(action, "default")) {
if (default_str == NULL) { id = &PyId_default;
default_str = PyUnicode_InternFromString("default");
if (default_str == NULL)
return NULL;
}
action_obj = default_str;
} }
else if (!strcmp(action, "always")) { else if (!strcmp(action, "always")) {
if (always_str == NULL) { id = &PyId_always;
always_str = PyUnicode_InternFromString("always");
if (always_str == NULL)
return NULL;
}
action_obj = always_str;
} }
else { 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. */ /* 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); category, Py_None, _PyLong_Zero);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment