shrinkray 25.12.27.0__py3-none-any.whl → 25.12.27.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shrinkray/__main__.py +8 -6
- shrinkray/state.py +48 -6
- shrinkray/subprocess/client.py +4 -3
- shrinkray/subprocess/worker.py +1 -1
- shrinkray/tui.py +5 -4
- {shrinkray-25.12.27.0.dist-info → shrinkray-25.12.27.1.dist-info}/METADATA +3 -2
- {shrinkray-25.12.27.0.dist-info → shrinkray-25.12.27.1.dist-info}/RECORD +11 -11
- {shrinkray-25.12.27.0.dist-info → shrinkray-25.12.27.1.dist-info}/WHEEL +0 -0
- {shrinkray-25.12.27.0.dist-info → shrinkray-25.12.27.1.dist-info}/entry_points.txt +0 -0
- {shrinkray-25.12.27.0.dist-info → shrinkray-25.12.27.1.dist-info}/licenses/LICENSE +0 -0
- {shrinkray-25.12.27.0.dist-info → shrinkray-25.12.27.1.dist-info}/top_level.txt +0 -0
shrinkray/__main__.py
CHANGED
|
@@ -75,12 +75,14 @@ async def run_shrink_ray(
|
|
|
75
75
|
)
|
|
76
76
|
@click.option(
|
|
77
77
|
"--timeout",
|
|
78
|
-
default=
|
|
78
|
+
default=None,
|
|
79
79
|
type=click.FLOAT,
|
|
80
80
|
help=(
|
|
81
|
-
"Time out subprocesses after this many seconds. If
|
|
82
|
-
"
|
|
83
|
-
"
|
|
81
|
+
"Time out subprocesses after this many seconds. If not specified, "
|
|
82
|
+
"runs the interestingness test once and sets timeout to 10x the "
|
|
83
|
+
"measured time (capped at 5 minutes). If set to <= 0 then no timeout "
|
|
84
|
+
"will be used. Any commands that time out will be treated as failing "
|
|
85
|
+
"the test"
|
|
84
86
|
),
|
|
85
87
|
)
|
|
86
88
|
@click.option(
|
|
@@ -212,7 +214,7 @@ def main(
|
|
|
212
214
|
backup: str,
|
|
213
215
|
filename: str,
|
|
214
216
|
test: list[str],
|
|
215
|
-
timeout: float,
|
|
217
|
+
timeout: float | None,
|
|
216
218
|
in_place: bool,
|
|
217
219
|
parallelism: int,
|
|
218
220
|
seed: int,
|
|
@@ -225,7 +227,7 @@ def main(
|
|
|
225
227
|
ui_type: UIType,
|
|
226
228
|
theme: str,
|
|
227
229
|
) -> None:
|
|
228
|
-
if timeout <= 0:
|
|
230
|
+
if timeout is not None and timeout <= 0:
|
|
229
231
|
timeout = float("inf")
|
|
230
232
|
|
|
231
233
|
if not os.access(test[0], os.X_OK):
|
shrinkray/state.py
CHANGED
|
@@ -36,13 +36,32 @@ class TimeoutExceededOnInitial(InvalidInitialExample):
|
|
|
36
36
|
)
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
# Constants for dynamic timeout
|
|
40
|
+
DYNAMIC_TIMEOUT_CALIBRATION_TIMEOUT = 300.0 # 5 minutes for first call
|
|
41
|
+
DYNAMIC_TIMEOUT_MULTIPLIER = 10
|
|
42
|
+
DYNAMIC_TIMEOUT_MAX = 300.0 # 5 minutes maximum
|
|
43
|
+
DYNAMIC_TIMEOUT_MIN = 1.0 # 1 second minimum to prevent edge cases
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def compute_dynamic_timeout(runtime: float) -> float:
|
|
47
|
+
"""Compute dynamic timeout based on measured runtime.
|
|
48
|
+
|
|
49
|
+
The timeout is set to 10x the measured runtime, clamped between
|
|
50
|
+
DYNAMIC_TIMEOUT_MIN and DYNAMIC_TIMEOUT_MAX.
|
|
51
|
+
"""
|
|
52
|
+
return max(
|
|
53
|
+
DYNAMIC_TIMEOUT_MIN,
|
|
54
|
+
min(runtime * DYNAMIC_TIMEOUT_MULTIPLIER, DYNAMIC_TIMEOUT_MAX),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
39
58
|
@define(slots=False)
|
|
40
59
|
class ShrinkRayState[TestCase](ABC):
|
|
41
60
|
input_type: Any # InputType from __main__
|
|
42
61
|
in_place: bool
|
|
43
62
|
test: list[str]
|
|
44
63
|
filename: str
|
|
45
|
-
timeout: float
|
|
64
|
+
timeout: float | None
|
|
46
65
|
base: str
|
|
47
66
|
parallelism: int
|
|
48
67
|
initial: TestCase
|
|
@@ -127,7 +146,8 @@ class ShrinkRayState[TestCase](ABC):
|
|
|
127
146
|
completed = await trio.run_process(command, **kwargs)
|
|
128
147
|
runtime = time.time() - start_time
|
|
129
148
|
|
|
130
|
-
|
|
149
|
+
# Check for timeout violation (only when timeout is explicitly set)
|
|
150
|
+
if self.timeout is not None and runtime >= self.timeout and self.first_call:
|
|
131
151
|
self.initial_exit_code = completed.returncode
|
|
132
152
|
self.first_call = False
|
|
133
153
|
raise TimeoutExceededOnInitial(
|
|
@@ -137,6 +157,9 @@ class ShrinkRayState[TestCase](ABC):
|
|
|
137
157
|
|
|
138
158
|
if self.first_call:
|
|
139
159
|
self.initial_exit_code = completed.returncode
|
|
160
|
+
# Set dynamic timeout if not explicitly specified
|
|
161
|
+
if self.timeout is None:
|
|
162
|
+
self.timeout = compute_dynamic_timeout(runtime)
|
|
140
163
|
self.first_call = False
|
|
141
164
|
|
|
142
165
|
# Store captured output
|
|
@@ -168,9 +191,19 @@ class ShrinkRayState[TestCase](ABC):
|
|
|
168
191
|
sp = await nursery.start(call_with_kwargs)
|
|
169
192
|
|
|
170
193
|
try:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
194
|
+
# Determine effective timeout for this call
|
|
195
|
+
if self.first_call:
|
|
196
|
+
# For first call: use calibration timeout if dynamic, otherwise 10x explicit timeout
|
|
197
|
+
if self.timeout is None:
|
|
198
|
+
effective_timeout = DYNAMIC_TIMEOUT_CALIBRATION_TIMEOUT
|
|
199
|
+
else:
|
|
200
|
+
effective_timeout = self.timeout * 10
|
|
201
|
+
else:
|
|
202
|
+
# For subsequent calls, timeout must be set (either explicit or computed)
|
|
203
|
+
assert self.timeout is not None
|
|
204
|
+
effective_timeout = self.timeout
|
|
205
|
+
|
|
206
|
+
with trio.move_on_after(effective_timeout):
|
|
174
207
|
await sp.wait()
|
|
175
208
|
|
|
176
209
|
runtime = time.time() - start_time
|
|
@@ -179,7 +212,12 @@ class ShrinkRayState[TestCase](ABC):
|
|
|
179
212
|
# Process didn't terminate before timeout - kill it
|
|
180
213
|
await self._interrupt_wait_and_kill(sp)
|
|
181
214
|
|
|
182
|
-
|
|
215
|
+
# Check for timeout violation (only when timeout is explicitly set)
|
|
216
|
+
if (
|
|
217
|
+
self.timeout is not None
|
|
218
|
+
and runtime >= self.timeout
|
|
219
|
+
and self.first_call
|
|
220
|
+
):
|
|
183
221
|
raise TimeoutExceededOnInitial(
|
|
184
222
|
timeout=self.timeout,
|
|
185
223
|
runtime=runtime,
|
|
@@ -187,6 +225,10 @@ class ShrinkRayState[TestCase](ABC):
|
|
|
187
225
|
finally:
|
|
188
226
|
if self.first_call:
|
|
189
227
|
self.initial_exit_code = sp.returncode
|
|
228
|
+
# Set dynamic timeout if not explicitly specified
|
|
229
|
+
if self.timeout is None:
|
|
230
|
+
runtime = time.time() - start_time
|
|
231
|
+
self.timeout = compute_dynamic_timeout(runtime)
|
|
190
232
|
self.first_call = False
|
|
191
233
|
|
|
192
234
|
result: int | None = sp.returncode
|
shrinkray/subprocess/client.py
CHANGED
|
@@ -127,7 +127,7 @@ class SubprocessClient:
|
|
|
127
127
|
file_path: str,
|
|
128
128
|
test: list[str],
|
|
129
129
|
parallelism: int | None = None,
|
|
130
|
-
timeout: float =
|
|
130
|
+
timeout: float | None = None,
|
|
131
131
|
seed: int = 0,
|
|
132
132
|
input_type: str = "all",
|
|
133
133
|
in_place: bool = False,
|
|
@@ -138,10 +138,9 @@ class SubprocessClient:
|
|
|
138
138
|
trivial_is_error: bool = True,
|
|
139
139
|
) -> Response:
|
|
140
140
|
"""Start the reduction process."""
|
|
141
|
-
params = {
|
|
141
|
+
params: dict[str, Any] = {
|
|
142
142
|
"file_path": file_path,
|
|
143
143
|
"test": test,
|
|
144
|
-
"timeout": timeout,
|
|
145
144
|
"seed": seed,
|
|
146
145
|
"input_type": input_type,
|
|
147
146
|
"in_place": in_place,
|
|
@@ -153,6 +152,8 @@ class SubprocessClient:
|
|
|
153
152
|
}
|
|
154
153
|
if parallelism is not None:
|
|
155
154
|
params["parallelism"] = parallelism
|
|
155
|
+
if timeout is not None:
|
|
156
|
+
params["timeout"] = timeout
|
|
156
157
|
return await self.send_command("start", params)
|
|
157
158
|
|
|
158
159
|
async def get_status(self) -> Response:
|
shrinkray/subprocess/worker.py
CHANGED
|
@@ -169,7 +169,7 @@ class ReducerWorker:
|
|
|
169
169
|
filename = params["file_path"]
|
|
170
170
|
test = params["test"]
|
|
171
171
|
parallelism = params.get("parallelism", os.cpu_count() or 1)
|
|
172
|
-
timeout = params.get("timeout"
|
|
172
|
+
timeout = params.get("timeout") # None means dynamic timeout
|
|
173
173
|
seed = params.get("seed", 0)
|
|
174
174
|
input_type = InputType[params.get("input_type", "all")]
|
|
175
175
|
in_place = params.get("in_place", False)
|
shrinkray/tui.py
CHANGED
|
@@ -98,7 +98,7 @@ class ReductionClientProtocol(Protocol):
|
|
|
98
98
|
file_path: str,
|
|
99
99
|
test: list[str],
|
|
100
100
|
parallelism: int | None = None,
|
|
101
|
-
timeout: float =
|
|
101
|
+
timeout: float | None = None,
|
|
102
102
|
seed: int = 0,
|
|
103
103
|
input_type: str = "all",
|
|
104
104
|
in_place: bool = False,
|
|
@@ -616,7 +616,7 @@ class ShrinkRayApp(App[None]):
|
|
|
616
616
|
file_path: str,
|
|
617
617
|
test: list[str],
|
|
618
618
|
parallelism: int | None = None,
|
|
619
|
-
timeout: float =
|
|
619
|
+
timeout: float | None = None,
|
|
620
620
|
seed: int = 0,
|
|
621
621
|
input_type: str = "all",
|
|
622
622
|
in_place: bool = False,
|
|
@@ -657,6 +657,7 @@ class ShrinkRayApp(App[None]):
|
|
|
657
657
|
yield Label(
|
|
658
658
|
"Shrink Ray - [h] help, [p] passes, [c] skip pass, [q] quit",
|
|
659
659
|
id="status-label",
|
|
660
|
+
markup=False,
|
|
660
661
|
)
|
|
661
662
|
with Vertical(id="stats-container"):
|
|
662
663
|
yield StatsDisplay(id="stats-display")
|
|
@@ -807,7 +808,7 @@ async def _validate_initial_example(
|
|
|
807
808
|
file_path: str,
|
|
808
809
|
test: list[str],
|
|
809
810
|
parallelism: int | None,
|
|
810
|
-
timeout: float,
|
|
811
|
+
timeout: float | None,
|
|
811
812
|
seed: int,
|
|
812
813
|
input_type: str,
|
|
813
814
|
in_place: bool,
|
|
@@ -855,7 +856,7 @@ def run_textual_ui(
|
|
|
855
856
|
file_path: str,
|
|
856
857
|
test: list[str],
|
|
857
858
|
parallelism: int | None = None,
|
|
858
|
-
timeout: float =
|
|
859
|
+
timeout: float | None = None,
|
|
859
860
|
seed: int = 0,
|
|
860
861
|
input_type: str = "all",
|
|
861
862
|
in_place: bool = False,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shrinkray
|
|
3
|
-
Version: 25.12.27.
|
|
3
|
+
Version: 25.12.27.1
|
|
4
4
|
Summary: Shrink Ray
|
|
5
5
|
Author-email: "David R. MacIver" <david@drmaciver.com>
|
|
6
6
|
License: MIT
|
|
@@ -28,7 +28,8 @@ Requires-Dist: hypothesmith>=0.3.1; extra == "dev"
|
|
|
28
28
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
29
29
|
Requires-Dist: pytest-trio>=0.8.0; extra == "dev"
|
|
30
30
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
31
|
-
Requires-Dist:
|
|
31
|
+
Requires-Dist: syrupy>=5.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: jinja2>=3.0.0; extra == "dev"
|
|
32
33
|
Requires-Dist: coverage[toml]>=7.4.0; extra == "dev"
|
|
33
34
|
Requires-Dist: pygments>=2.17.0; extra == "dev"
|
|
34
35
|
Requires-Dist: basedpyright>=1.1.0; extra == "dev"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
shrinkray/__init__.py,sha256=b5MvcvhsEGYya3GRXNbCJAlAL5IZHSsETLK_vtfmXRY,18
|
|
2
|
-
shrinkray/__main__.py,sha256=
|
|
2
|
+
shrinkray/__main__.py,sha256=K3_s96Tyoi7SxNOyoZXkfiEoSxVBL__TJ3o2Cefadmg,11093
|
|
3
3
|
shrinkray/cli.py,sha256=1-qjaIchyCDd-YCdGWtK7q9j9qr6uX6AqtwW8m5QCQg,1697
|
|
4
4
|
shrinkray/display.py,sha256=WYN05uqmUVpZhwi2pxr1U-wLHWZ9KiL0RUlTCBJ1N3E,2430
|
|
5
5
|
shrinkray/formatting.py,sha256=tXCGnhJn-WJGpHMaLHRCAXK8aKJBbrOdiW9QGERrQEk,3121
|
|
@@ -7,8 +7,8 @@ shrinkray/problem.py,sha256=Kp7QN10E4tzjdpoqJve8_RT26VpywzQwY0gX2VkBGCo,17277
|
|
|
7
7
|
shrinkray/process.py,sha256=-eP8h5X0ESbkcTic8FFEzkd4-vwaZ0YI5tLxUR25L8U,1599
|
|
8
8
|
shrinkray/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
shrinkray/reducer.py,sha256=xhLo_GF7qrIVoiLHed6Wt4nxjdE-9jj_7K9F76un89o,19877
|
|
10
|
-
shrinkray/state.py,sha256=
|
|
11
|
-
shrinkray/tui.py,sha256=
|
|
10
|
+
shrinkray/state.py,sha256=HQ7VivCXWiP2L53gB0lh6dHcdru6waWZkbPg6rYO7z4,24290
|
|
11
|
+
shrinkray/tui.py,sha256=3RskLo6JvKdUQIHi40R5ka-F_1GkBXyA_d_SkYbLlCw,31601
|
|
12
12
|
shrinkray/ui.py,sha256=xuDUwU-MM3AetvwUB7bfzav0P_drUsBrKFPhON_Nr-k,2251
|
|
13
13
|
shrinkray/work.py,sha256=GEZ14Kk3bvwUxAnACvY-wom2lVWaGrELMNxrDjv03dk,8110
|
|
14
14
|
shrinkray/passes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -22,12 +22,12 @@ shrinkray/passes/python.py,sha256=3WN1lZTf5oVL8FCTGomhrCuE04wIX9ocKcmFV86NMZA,68
|
|
|
22
22
|
shrinkray/passes/sat.py,sha256=5Zv4IgGfg3SYplMAAaPLkbBNh4fuVpci4F9GdVacayA,19504
|
|
23
23
|
shrinkray/passes/sequences.py,sha256=jCK1fWBxCz79u7JWSps9wf7Yru7W_FAsJwdgg--CLxU,3040
|
|
24
24
|
shrinkray/subprocess/__init__.py,sha256=FyV2y05uwQ1RTZGwREI0aAVaLX1jiwRcWsdsksFmdbM,451
|
|
25
|
-
shrinkray/subprocess/client.py,sha256=
|
|
25
|
+
shrinkray/subprocess/client.py,sha256=erqnPglPO0YNdwEKlmhB3yDo6Mfc00Lxh4T85lZhsDo,9341
|
|
26
26
|
shrinkray/subprocess/protocol.py,sha256=LuHl0IkKpDzYhAGZz_EiTHNqDNq_v1ozg5aUSl7UzE4,6203
|
|
27
|
-
shrinkray/subprocess/worker.py,sha256=
|
|
28
|
-
shrinkray-25.12.27.
|
|
29
|
-
shrinkray-25.12.27.
|
|
30
|
-
shrinkray-25.12.27.
|
|
31
|
-
shrinkray-25.12.27.
|
|
32
|
-
shrinkray-25.12.27.
|
|
33
|
-
shrinkray-25.12.27.
|
|
27
|
+
shrinkray/subprocess/worker.py,sha256=ke-9DYFH117EpJEntkucTrn7ep7pygzmV-VXkRe1o-E,19294
|
|
28
|
+
shrinkray-25.12.27.1.dist-info/licenses/LICENSE,sha256=iMKX79AuokJfIZUnGUARdUp30vVAoIPOJ7ek8TY63kk,1072
|
|
29
|
+
shrinkray-25.12.27.1.dist-info/METADATA,sha256=bB-_7Y4Gk9a-QiXEFKy_iTDy3nWM72YOYXhq0W_PJ7o,9721
|
|
30
|
+
shrinkray-25.12.27.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
31
|
+
shrinkray-25.12.27.1.dist-info/entry_points.txt,sha256=wIZvnGyOdVeaLTiv2klnSyTe-EKkkwn4SwHh9bmJ7qk,104
|
|
32
|
+
shrinkray-25.12.27.1.dist-info/top_level.txt,sha256=fLif8-rFoFOnf5h8-vs3ECkKNWQopTQh3xvl1s7pchQ,10
|
|
33
|
+
shrinkray-25.12.27.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|