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
This commit is contained in:
Victor Stinner 2017-11-22 23:51:42 +01:00 committed by GitHub
parent bb11c3c967
commit 82656276ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 45 deletions

View File

@ -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).

View File

@ -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);
}