Skip to content

Commit 32c00db

Browse files
authored
Merge pull request #7176 from bluetech/warnings-optimize-parse
warnings: speed up work done in catch_warnings_for_item()
2 parents 81da5da + 65963d2 commit 32c00db

File tree

1 file changed

+32
-14
lines changed

1 file changed

+32
-14
lines changed

src/_pytest/warnings.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,52 @@
1+
import re
12
import sys
23
import warnings
34
from contextlib import contextmanager
5+
from functools import lru_cache
46
from typing import Generator
7+
from typing import Tuple
58

69
import pytest
10+
from _pytest.compat import TYPE_CHECKING
711
from _pytest.main import Session
812

13+
if TYPE_CHECKING:
14+
from typing_extensions import Type
915

10-
def _setoption(wmod, arg):
11-
"""
12-
Copy of the warning._setoption function but does not escape arguments.
16+
17+
@lru_cache(maxsize=50)
18+
def _parse_filter(
19+
arg: str, *, escape: bool
20+
) -> "Tuple[str, str, Type[Warning], str, int]":
21+
"""Parse a warnings filter string.
22+
23+
This is copied from warnings._setoption, but does not apply the filter,
24+
only parses it, and makes the escaping optional.
1325
"""
1426
parts = arg.split(":")
1527
if len(parts) > 5:
16-
raise wmod._OptionError("too many fields (max 5): {!r}".format(arg))
28+
raise warnings._OptionError("too many fields (max 5): {!r}".format(arg))
1729
while len(parts) < 5:
1830
parts.append("")
19-
action, message, category, module, lineno = [s.strip() for s in parts]
20-
action = wmod._getaction(action)
21-
category = wmod._getcategory(category)
22-
if lineno:
31+
action_, message, category_, module, lineno_ = [s.strip() for s in parts]
32+
action = warnings._getaction(action_) # type: str # type: ignore[attr-defined]
33+
category = warnings._getcategory(
34+
category_
35+
) # type: Type[Warning] # type: ignore[attr-defined]
36+
if message and escape:
37+
message = re.escape(message)
38+
if module and escape:
39+
module = re.escape(module) + r"\Z"
40+
if lineno_:
2341
try:
24-
lineno = int(lineno)
42+
lineno = int(lineno_)
2543
if lineno < 0:
2644
raise ValueError
2745
except (ValueError, OverflowError):
28-
raise wmod._OptionError("invalid lineno {!r}".format(lineno))
46+
raise warnings._OptionError("invalid lineno {!r}".format(lineno_))
2947
else:
3048
lineno = 0
31-
wmod.filterwarnings(action, message, category, module, lineno)
49+
return (action, message, category, module, lineno)
3250

3351

3452
def pytest_addoption(parser):
@@ -79,15 +97,15 @@ def catch_warnings_for_item(config, ihook, when, item):
7997
# filters should have this precedence: mark, cmdline options, ini
8098
# filters should be applied in the inverse order of precedence
8199
for arg in inifilters:
82-
_setoption(warnings, arg)
100+
warnings.filterwarnings(*_parse_filter(arg, escape=False))
83101

84102
for arg in cmdline_filters:
85-
warnings._setoption(arg)
103+
warnings.filterwarnings(*_parse_filter(arg, escape=True))
86104

87105
if item is not None:
88106
for mark in item.iter_markers(name="filterwarnings"):
89107
for arg in mark.args:
90-
_setoption(warnings, arg)
108+
warnings.filterwarnings(*_parse_filter(arg, escape=False))
91109

92110
yield
93111

0 commit comments

Comments
 (0)