shrinkray 25.12.27.3__py3-none-any.whl → 25.12.29.0__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/passes/bytes.py +0 -125
- shrinkray/passes/clangdelta.py +0 -24
- shrinkray/passes/genericlanguages.py +0 -20
- shrinkray/passes/json.py +0 -8
- shrinkray/passes/patching.py +0 -63
- shrinkray/passes/sequences.py +0 -25
- shrinkray/reducer.py +0 -50
- shrinkray/state.py +43 -51
- shrinkray/subprocess/__init__.py +0 -4
- shrinkray/subprocess/protocol.py +10 -12
- shrinkray/subprocess/worker.py +59 -21
- shrinkray/tui.py +605 -32
- {shrinkray-25.12.27.3.dist-info → shrinkray-25.12.29.0.dist-info}/METADATA +10 -2
- shrinkray-25.12.29.0.dist-info/RECORD +33 -0
- shrinkray/display.py +0 -75
- shrinkray-25.12.27.3.dist-info/RECORD +0 -34
- {shrinkray-25.12.27.3.dist-info → shrinkray-25.12.29.0.dist-info}/WHEEL +0 -0
- {shrinkray-25.12.27.3.dist-info → shrinkray-25.12.29.0.dist-info}/entry_points.txt +0 -0
- {shrinkray-25.12.27.3.dist-info → shrinkray-25.12.29.0.dist-info}/licenses/LICENSE +0 -0
- {shrinkray-25.12.27.3.dist-info → shrinkray-25.12.29.0.dist-info}/top_level.txt +0 -0
shrinkray/subprocess/worker.py
CHANGED
|
@@ -37,14 +37,6 @@ class OutputStream(Protocol):
|
|
|
37
37
|
async def send(self, data: bytes) -> None: ...
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
class StdoutStream:
|
|
41
|
-
"""Wrapper around sys.stdout for the OutputStream protocol."""
|
|
42
|
-
|
|
43
|
-
async def send(self, data: bytes) -> None:
|
|
44
|
-
sys.stdout.write(data.decode("utf-8"))
|
|
45
|
-
sys.stdout.flush()
|
|
46
|
-
|
|
47
|
-
|
|
48
40
|
class ReducerWorker:
|
|
49
41
|
"""Runs the reducer in a subprocess with JSON protocol communication."""
|
|
50
42
|
|
|
@@ -66,6 +58,11 @@ class ReducerWorker:
|
|
|
66
58
|
self._output_stream = output_stream
|
|
67
59
|
# Output directory for test output capture (cleaned up on shutdown)
|
|
68
60
|
self._output_dir: str | None = None
|
|
61
|
+
# Size history for graphing: list of (runtime_seconds, size) tuples
|
|
62
|
+
self._size_history: list[tuple[float, int]] = []
|
|
63
|
+
self._last_sent_history_index: int = 0
|
|
64
|
+
self._last_recorded_size: int = 0
|
|
65
|
+
self._last_history_time: float = 0.0
|
|
69
66
|
|
|
70
67
|
async def emit(self, msg: Response | ProgressUpdate) -> None:
|
|
71
68
|
"""Write a message to the output stream."""
|
|
@@ -165,9 +162,9 @@ class ReducerWorker:
|
|
|
165
162
|
find_clang_delta,
|
|
166
163
|
)
|
|
167
164
|
from shrinkray.state import (
|
|
165
|
+
OutputCaptureManager,
|
|
168
166
|
ShrinkRayDirectoryState,
|
|
169
167
|
ShrinkRayStateSingleFile,
|
|
170
|
-
TestOutputManager,
|
|
171
168
|
)
|
|
172
169
|
from shrinkray.work import Volume
|
|
173
170
|
|
|
@@ -221,7 +218,7 @@ class ReducerWorker:
|
|
|
221
218
|
|
|
222
219
|
# Create output manager for test output capture (always enabled for TUI)
|
|
223
220
|
self._output_dir = tempfile.mkdtemp(prefix="shrinkray-output-")
|
|
224
|
-
self.state.output_manager =
|
|
221
|
+
self.state.output_manager = OutputCaptureManager(output_dir=self._output_dir)
|
|
225
222
|
|
|
226
223
|
self.problem = self.state.problem
|
|
227
224
|
self.reducer = self.state.reducer
|
|
@@ -312,17 +309,23 @@ class ReducerWorker:
|
|
|
312
309
|
return Response(id=request_id, result={"status": "skipped"})
|
|
313
310
|
return Response(id=request_id, error="Reducer does not support pass control")
|
|
314
311
|
|
|
315
|
-
def _get_test_output_preview(self) -> tuple[str, int | None]:
|
|
316
|
-
"""Get preview of current test output and
|
|
312
|
+
def _get_test_output_preview(self) -> tuple[str, int | None, int | None]:
|
|
313
|
+
"""Get preview of current test output, test ID, and return code.
|
|
314
|
+
|
|
315
|
+
Returns (content, test_id, return_code) where:
|
|
316
|
+
- content: the last 4KB of the output file
|
|
317
|
+
- test_id: the test ID being displayed
|
|
318
|
+
- return_code: None if test is still running, otherwise the exit code
|
|
319
|
+
"""
|
|
317
320
|
if self.state is None or self.state.output_manager is None:
|
|
318
|
-
return "", None
|
|
321
|
+
return "", None, None
|
|
319
322
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
+
output_path, test_id, return_code = (
|
|
324
|
+
self.state.output_manager.get_current_output()
|
|
325
|
+
)
|
|
323
326
|
|
|
324
327
|
if output_path is None:
|
|
325
|
-
return "",
|
|
328
|
+
return "", None, None
|
|
326
329
|
|
|
327
330
|
# Read last 4KB of file
|
|
328
331
|
try:
|
|
@@ -334,9 +337,13 @@ class ReducerWorker:
|
|
|
334
337
|
else:
|
|
335
338
|
f.seek(0)
|
|
336
339
|
data = f.read()
|
|
337
|
-
return
|
|
340
|
+
return (
|
|
341
|
+
data.decode("utf-8", errors="replace"),
|
|
342
|
+
test_id,
|
|
343
|
+
return_code,
|
|
344
|
+
)
|
|
338
345
|
except OSError:
|
|
339
|
-
return "",
|
|
346
|
+
return "", test_id, return_code
|
|
340
347
|
|
|
341
348
|
def _get_content_preview(self) -> tuple[str, bool]:
|
|
342
349
|
"""Get a preview of the current test case content."""
|
|
@@ -387,6 +394,29 @@ class ReducerWorker:
|
|
|
387
394
|
return None
|
|
388
395
|
|
|
389
396
|
stats = self.problem.stats
|
|
397
|
+
runtime = time.time() - stats.start_time
|
|
398
|
+
current_size = stats.current_test_case_size
|
|
399
|
+
|
|
400
|
+
# Record size history when size changes or periodically
|
|
401
|
+
# Use 200ms interval for first 5 minutes, then 1s (ticks are at 1-minute intervals)
|
|
402
|
+
history_interval = 1.0 if runtime >= 300 else 0.2
|
|
403
|
+
|
|
404
|
+
if not self._size_history:
|
|
405
|
+
# First sample: record initial size at time 0
|
|
406
|
+
self._size_history.append((0.0, stats.initial_test_case_size))
|
|
407
|
+
self._last_recorded_size = stats.initial_test_case_size
|
|
408
|
+
self._last_history_time = 0.0
|
|
409
|
+
|
|
410
|
+
if current_size != self._last_recorded_size:
|
|
411
|
+
# Size changed - always record
|
|
412
|
+
self._size_history.append((runtime, current_size))
|
|
413
|
+
self._last_recorded_size = current_size
|
|
414
|
+
self._last_history_time = runtime
|
|
415
|
+
elif runtime - self._last_history_time >= history_interval:
|
|
416
|
+
# No size change but interval passed - record periodic update
|
|
417
|
+
self._size_history.append((runtime, current_size))
|
|
418
|
+
self._last_history_time = runtime
|
|
419
|
+
|
|
390
420
|
content_preview, hex_mode = self._get_content_preview()
|
|
391
421
|
|
|
392
422
|
# Get parallel workers count and track average
|
|
@@ -441,7 +471,13 @@ class ReducerWorker:
|
|
|
441
471
|
disabled_passes = []
|
|
442
472
|
|
|
443
473
|
# Get test output preview
|
|
444
|
-
test_output_preview, active_test_id =
|
|
474
|
+
test_output_preview, active_test_id, last_return_code = (
|
|
475
|
+
self._get_test_output_preview()
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
# Get new size history entries since last update
|
|
479
|
+
new_entries = self._size_history[self._last_sent_history_index :]
|
|
480
|
+
self._last_sent_history_index = len(self._size_history)
|
|
445
481
|
|
|
446
482
|
return ProgressUpdate(
|
|
447
483
|
status=self.reducer.status if self.reducer else "",
|
|
@@ -451,7 +487,7 @@ class ReducerWorker:
|
|
|
451
487
|
reductions=stats.reductions,
|
|
452
488
|
interesting_calls=stats.interesting_calls,
|
|
453
489
|
wasted_calls=stats.wasted_interesting_calls,
|
|
454
|
-
runtime=
|
|
490
|
+
runtime=runtime,
|
|
455
491
|
parallel_workers=parallel_workers,
|
|
456
492
|
average_parallelism=average_parallelism,
|
|
457
493
|
effective_parallelism=effective_parallelism,
|
|
@@ -463,6 +499,8 @@ class ReducerWorker:
|
|
|
463
499
|
disabled_passes=disabled_passes,
|
|
464
500
|
test_output_preview=test_output_preview,
|
|
465
501
|
active_test_id=active_test_id,
|
|
502
|
+
last_test_return_code=last_return_code,
|
|
503
|
+
new_size_history=new_entries,
|
|
466
504
|
)
|
|
467
505
|
|
|
468
506
|
async def emit_progress_updates(self) -> None:
|