-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbisect.py
More file actions
77 lines (63 loc) · 2.04 KB
/
Copy pathbisect.py
File metadata and controls
77 lines (63 loc) · 2.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
"""Delta-debugging (ddmin) over an ordered list of pytest nodeids."""
from __future__ import annotations
from collections.abc import Callable, Sequence
from typing import TypeVar
T = TypeVar("T")
def ddmin(
items: Sequence[T],
predicate: Callable[[list[T]], bool],
*,
max_calls: int | None = None,
) -> list[T]:
"""Zeller-style delta-debugging.
Returns a minimal sublist of `items` (preserving order) for which `predicate`
returns True. Assumes `predicate(list(items))` already returns True.
Raises `RuntimeError` if `max_calls` is exceeded.
"""
current: list[T] = list(items)
if not current:
return current
calls = 0
cache: dict[tuple, bool] = {}
def test(subset: list[T]) -> bool:
nonlocal calls
key = tuple(subset)
if key in cache:
return cache[key]
if max_calls is not None and calls >= max_calls:
raise RuntimeError(
f"flake-bisect: exceeded max-runs={max_calls} during ddmin"
)
calls += 1
result = predicate(subset)
cache[key] = result
return result
n = 2
while len(current) >= 2:
chunk_size = max(len(current) // n, 1)
chunks = [current[i : i + chunk_size] for i in range(0, len(current), chunk_size)]
# 1) Try each subset alone.
narrowed = False
for chunk in chunks:
if chunk and test(chunk):
current = chunk
n = 2
narrowed = True
break
if narrowed:
continue
# 2) Try each complement.
for chunk in chunks:
chunk_set = set(chunk)
complement = [x for x in current if x not in chunk_set]
if complement and test(complement):
current = complement
n = max(n - 1, 2)
narrowed = True
break
if narrowed:
continue
if n >= len(current):
break
n = min(n * 2, len(current))
return current