Skip to content

Commit 436b095

Browse files
committed
Add glob support for the scrutineer ignore list
1 parent 70f4cee commit 436b095

File tree

1 file changed

+31
-21
lines changed

1 file changed

+31
-21
lines changed

hypothesis-python/src/hypothesis/internal/scrutineer.py

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import functools
1212
import os
13+
import re
1314
import subprocess
1415
import sys
1516
import types
@@ -107,30 +108,38 @@ def __exit__(self, *args, **kwargs):
107108
# a contextmanager; this is probably after the fault has been triggered.
108109
# Similar reasoning applies to a few other standard-library modules: even
109110
# if the fault was later, these still aren't useful locations to report!
110-
f"{sep}contextlib.py",
111-
f"{sep}inspect.py",
112-
f"{sep}re.py",
113-
f"{sep}re{sep}__init__.py", # refactored in Python 3.11
114-
f"{sep}warnings.py",
111+
# Note: The list is post-processed, so use plain "/" for separator here.
112+
"/contextlib.py",
113+
"/inspect.py",
114+
"/re.py",
115+
"/re/__init__.py", # refactored in Python 3.11
116+
"/warnings.py",
115117
# Quite rarely, the first AFNP line is in Pytest's internals.
116-
f"{sep}_pytest{sep}assertion{sep}__init__.py",
117-
f"{sep}_pytest{sep}assertion{sep}rewrite.py",
118-
f"{sep}_pytest{sep}_io{sep}saferepr.py",
119-
f"{sep}pluggy{sep}_result.py",
120-
# These are triggered by gc callbacks
121-
f"{sep}reprlib.py",
122-
f"{sep}_pytest{sep}assertion{sep}util.py",
123-
f"{sep}_pytest{sep}config{sep}__init__.py",
124-
f"{sep}pluggy{sep}_result.py",
125-
f"{sep}pluggy{sep}_manager.py",
126-
f"{sep}typing.py",
127-
f"{sep}pluggy{sep}_callers.py",
128-
f"{sep}pluggy{sep}_hooks.py",
129-
f"{sep}_pytest{sep}pytester.py",
130-
f"{sep}tests{sep}conftest.py",
118+
"/_pytest/_io/saferepr.py",
119+
"/_pytest/assertion/*.py",
120+
"/_pytest/config/__init__.py",
121+
"/_pytest/pytester.py",
122+
"/pluggy/_*.py",
123+
"/reprlib.py",
124+
"/typing.py",
125+
"/conftest.py",
131126
)
132127

133128

129+
def _glob_to_re(locs):
130+
"""Translate a list of glob patterns to a combined regular expression.
131+
Only * and ** wildcards are supported, and patterns including special
132+
characters will only work by chance."""
133+
# fnmatch.translate is not an option since its "*" consumes path sep
134+
return "|".join(
135+
loc.replace("*", r"[^/]+")
136+
.replace(".", re.escape("."))
137+
.replace("/", re.escape(sep))
138+
+ r"\Z" # right anchored
139+
for loc in locs
140+
)
141+
142+
134143
def get_explaining_locations(traces):
135144
# Traces is a dict[interesting_origin | None, set[frozenset[tuple[str, int]]]]
136145
# Each trace in the set might later become a Counter instead of frozenset.
@@ -173,8 +182,9 @@ def get_explaining_locations(traces):
173182
# The last step is to filter out explanations that we know would be uninformative.
174183
# When this is the first AFNP location, we conclude that Scrutineer missed the
175184
# real divergence (earlier in the trace) and drop that unhelpful explanation.
185+
filter_regex = re.compile(_glob_to_re(UNHELPFUL_LOCATIONS))
176186
return {
177-
origin: {loc for loc in afnp_locs if not loc[0].endswith(UNHELPFUL_LOCATIONS)}
187+
origin: {loc for loc in afnp_locs if not filter_regex.search(loc[0])}
178188
for origin, afnp_locs in explanations.items()
179189
}
180190

0 commit comments

Comments
 (0)