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 CHANGED
@@ -75,12 +75,14 @@ async def run_shrink_ray(
75
75
  )
76
76
  @click.option(
77
77
  "--timeout",
78
- default=1,
78
+ default=None,
79
79
  type=click.FLOAT,
80
80
  help=(
81
- "Time out subprocesses after this many seconds. If set to <= 0 then "
82
- "no timeout will be used. Any commands that time out will be treated "
83
- "as failing the test"
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
- if runtime >= self.timeout and self.first_call:
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
- with trio.move_on_after(
172
- self.timeout * 10 if self.first_call else self.timeout
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
- if runtime >= self.timeout and self.first_call:
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
@@ -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 = 1.0,
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:
@@ -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", 1.0)
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 = 1.0,
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 = 1.0,
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 = 1.0,
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.0
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: pytest-textual-snapshot>=1.0.0; extra == "dev"
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=AL1WNfohkA5Uu7uiCQRhNjzfUc4qKC-UjMTDraPlT8I,10919
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=jqAKlirXjKK6smA2G1BnGjjoH7NgVokQhER2Ax18q64,22381
11
- shrinkray/tui.py,sha256=YiiCDVxGl5g5TlcZN62NMYiD_IqRV68qoRDkNcasvc4,31540
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=i2CJ7xU8_jklqDl-yphpIv-mwD0gG93jVzEDEtsdWvE,9277
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=IyO9FNQ11vWwrc2ikH-Ca3iGoc03MJOIRNUqK3R06aE,19269
28
- shrinkray-25.12.27.0.dist-info/licenses/LICENSE,sha256=iMKX79AuokJfIZUnGUARdUp30vVAoIPOJ7ek8TY63kk,1072
29
- shrinkray-25.12.27.0.dist-info/METADATA,sha256=jdpwOHRfJE7bJnBFBbYlpEw7bINLJaU_v2Pz_aBGsmw,9693
30
- shrinkray-25.12.27.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
31
- shrinkray-25.12.27.0.dist-info/entry_points.txt,sha256=wIZvnGyOdVeaLTiv2klnSyTe-EKkkwn4SwHh9bmJ7qk,104
32
- shrinkray-25.12.27.0.dist-info/top_level.txt,sha256=fLif8-rFoFOnf5h8-vs3ECkKNWQopTQh3xvl1s7pchQ,10
33
- shrinkray-25.12.27.0.dist-info/RECORD,,
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,,