Skip to content

Commit 8265627

Browse files
authored
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
1 parent bb11c3c commit 8265627

File tree

2 files changed

+47
-45
lines changed

2 files changed

+47
-45
lines changed

Lib/warnings.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0,
128128
'lineno' -- an integer line number, 0 matches all warnings
129129
'append' -- if true, append to the list of filters
130130
"""
131-
import re
132131
assert action in ("error", "ignore", "always", "default", "module",
133132
"once"), "invalid action: %r" % (action,)
134133
assert isinstance(message, str), "message must be a string"
@@ -137,8 +136,20 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0,
137136
assert isinstance(module, str), "module must be a string"
138137
assert isinstance(lineno, int) and lineno >= 0, \
139138
"lineno must be an int >= 0"
140-
_add_filter(action, re.compile(message, re.I), category,
141-
re.compile(module), lineno, append=append)
139+
140+
if message or module:
141+
import re
142+
143+
if message:
144+
message = re.compile(message, re.I)
145+
else:
146+
message = None
147+
if module:
148+
module = re.compile(module)
149+
else:
150+
module = None
151+
152+
_add_filter(action, message, category, module, lineno, append=append)
142153

143154
def simplefilter(action, category=Warning, lineno=0, append=False):
144155
"""Insert a simple entry into the list of warnings filters (at the front).

Python/_warnings.c

Lines changed: 33 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ check_matched(PyObject *obj, PyObject *arg)
3535
A NULL return value can mean false or an error.
3636
*/
3737
static PyObject *
38-
get_warnings_attr(const char *attr, int try_import)
38+
get_warnings_attr(_Py_Identifier *attr_id, int try_import)
3939
{
40-
static PyObject *warnings_str = NULL;
40+
PyObject *warnings_str;
4141
PyObject *warnings_module, *obj;
42+
_Py_IDENTIFIER(warnings);
4243

44+
warnings_str = _PyUnicode_FromId(&PyId_warnings);
4345
if (warnings_str == NULL) {
44-
warnings_str = PyUnicode_InternFromString("warnings");
45-
if (warnings_str == NULL)
46-
return NULL;
46+
return NULL;
4747
}
4848

4949
/* 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)
6464
return NULL;
6565
}
6666

67-
obj = PyObject_GetAttrString(warnings_module, attr);
67+
obj = _PyObject_GetAttrId(warnings_module, attr_id);
6868
Py_DECREF(warnings_module);
6969
if (obj == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
7070
PyErr_Clear();
@@ -77,8 +77,9 @@ static PyObject *
7777
get_once_registry(void)
7878
{
7979
PyObject *registry;
80+
_Py_IDENTIFIER(onceregistry);
8081

81-
registry = get_warnings_attr("onceregistry", 0);
82+
registry = get_warnings_attr(&PyId_onceregistry, 0);
8283
if (registry == NULL) {
8384
if (PyErr_Occurred())
8485
return NULL;
@@ -102,8 +103,9 @@ static PyObject *
102103
get_default_action(void)
103104
{
104105
PyObject *default_action;
106+
_Py_IDENTIFIER(defaultaction);
105107

106-
default_action = get_warnings_attr("defaultaction", 0);
108+
default_action = get_warnings_attr(&PyId_defaultaction, 0);
107109
if (default_action == NULL) {
108110
if (PyErr_Occurred()) {
109111
return NULL;
@@ -132,8 +134,9 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
132134
PyObject *action;
133135
Py_ssize_t i;
134136
PyObject *warnings_filters;
137+
_Py_IDENTIFIER(filters);
135138

136-
warnings_filters = get_warnings_attr("filters", 0);
139+
warnings_filters = get_warnings_attr(&PyId_filters, 0);
137140
if (warnings_filters == NULL) {
138141
if (PyErr_Occurred())
139142
return NULL;
@@ -389,11 +392,13 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message,
389392
PyObject *sourceline, PyObject *source)
390393
{
391394
PyObject *show_fn, *msg, *res, *warnmsg_cls = NULL;
395+
_Py_IDENTIFIER(_showwarnmsg);
396+
_Py_IDENTIFIER(WarningMessage);
392397

393398
/* If the source parameter is set, try to get the Python implementation.
394399
The Python implementation is able to log the traceback where the source
395400
was allocated, whereas the C implementation doesn't. */
396-
show_fn = get_warnings_attr("_showwarnmsg", source != NULL);
401+
show_fn = get_warnings_attr(&PyId__showwarnmsg, source != NULL);
397402
if (show_fn == NULL) {
398403
if (PyErr_Occurred())
399404
return -1;
@@ -407,7 +412,7 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message,
407412
goto error;
408413
}
409414

410-
warnmsg_cls = get_warnings_attr("WarningMessage", 0);
415+
warnmsg_cls = get_warnings_attr(&PyId_WarningMessage, 0);
411416
if (warnmsg_cls == NULL) {
412417
if (!PyErr_Occurred()) {
413418
PyErr_SetString(PyExc_RuntimeError,
@@ -1146,50 +1151,36 @@ static PyMethodDef warnings_functions[] = {
11461151
static PyObject *
11471152
create_filter(PyObject *category, const char *action)
11481153
{
1149-
static PyObject *ignore_str = NULL;
1150-
static PyObject *error_str = NULL;
1151-
static PyObject *default_str = NULL;
1152-
static PyObject *always_str = NULL;
1153-
PyObject *action_obj = NULL;
1154+
_Py_IDENTIFIER(ignore);
1155+
_Py_IDENTIFIER(error);
1156+
_Py_IDENTIFIER(always);
1157+
_Py_static_string(PyId_default, "default");
1158+
_Py_Identifier *id;
11541159

11551160
if (!strcmp(action, "ignore")) {
1156-
if (ignore_str == NULL) {
1157-
ignore_str = PyUnicode_InternFromString("ignore");
1158-
if (ignore_str == NULL)
1159-
return NULL;
1160-
}
1161-
action_obj = ignore_str;
1161+
id = &PyId_ignore;
11621162
}
11631163
else if (!strcmp(action, "error")) {
1164-
if (error_str == NULL) {
1165-
error_str = PyUnicode_InternFromString("error");
1166-
if (error_str == NULL)
1167-
return NULL;
1168-
}
1169-
action_obj = error_str;
1164+
id = &PyId_error;
11701165
}
11711166
else if (!strcmp(action, "default")) {
1172-
if (default_str == NULL) {
1173-
default_str = PyUnicode_InternFromString("default");
1174-
if (default_str == NULL)
1175-
return NULL;
1176-
}
1177-
action_obj = default_str;
1167+
id = &PyId_default;
11781168
}
11791169
else if (!strcmp(action, "always")) {
1180-
if (always_str == NULL) {
1181-
always_str = PyUnicode_InternFromString("always");
1182-
if (always_str == NULL)
1183-
return NULL;
1184-
}
1185-
action_obj = always_str;
1170+
id = &PyId_always;
11861171
}
11871172
else {
1188-
Py_FatalError("unknown action");
1173+
PyErr_SetString(PyExc_ValueError, "unknown action");
1174+
return NULL;
1175+
}
1176+
1177+
PyObject *action_str = _PyUnicode_FromId(id);
1178+
if (action_str == NULL) {
1179+
return NULL;
11891180
}
11901181

11911182
/* This assumes the line number is zero for now. */
1192-
return PyTuple_Pack(5, action_obj, Py_None,
1183+
return PyTuple_Pack(5, action_str, Py_None,
11931184
category, Py_None, _PyLong_Zero);
11941185
}
11951186

0 commit comments

Comments
 (0)