shrinkray 26.2.4.0__py3-none-any.whl → 26.2.4.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.
@@ -27,6 +27,7 @@ class SubprocessClient:
27
27
  self._progress_queue: asyncio.Queue[ProgressUpdate] = asyncio.Queue()
28
28
  self._reader_task: asyncio.Task | None = None
29
29
  self._completed = False
30
+ self._closed = False
30
31
  self._error_message: str | None = None
31
32
  self._debug_mode = debug_mode
32
33
  self._stderr_log_file: IO[str] | None = None
@@ -261,6 +262,17 @@ class SubprocessClient:
261
262
 
262
263
  async def close(self) -> None:
263
264
  """Close the subprocess."""
265
+ if self._closed:
266
+ return
267
+ self._closed = True
268
+
269
+ # Cancel all pending futures first so any code awaiting send_command
270
+ # responses (e.g. cancel() in action_quit) is unblocked immediately.
271
+ for future in self._pending_responses.values():
272
+ if not future.done():
273
+ future.cancel()
274
+ self._pending_responses.clear()
275
+
264
276
  if self._reader_task is not None:
265
277
  self._reader_task.cancel()
266
278
  try:
@@ -278,13 +290,21 @@ class SubprocessClient:
278
290
  if self._process.returncode is None:
279
291
  try:
280
292
  self._process.terminate()
281
- await asyncio.wait_for(self._process.wait(), timeout=5.0)
293
+ await asyncio.wait_for(self._process.wait(), timeout=2.0)
282
294
  except TimeoutError:
283
295
  self._process.kill()
284
296
  await self._process.wait()
285
297
  except ProcessLookupError:
286
298
  pass # Process already exited
287
299
 
300
+ # Always await wait() to ensure the asyncio transport is fully
301
+ # finalized before the event loop closes. This prevents the
302
+ # "Event loop is closed" RuntimeError from BaseSubprocessTransport.__del__.
303
+ if self._process.returncode is not None:
304
+ await self._process.wait()
305
+ # Flush pending event loop callbacks (transport cleanup)
306
+ await asyncio.sleep(0)
307
+
288
308
  # Close and remove the stderr log file
289
309
  if self._stderr_log_file is not None:
290
310
  try:
shrinkray/tui.py CHANGED
@@ -1938,11 +1938,14 @@ class ShrinkRayApp(App[None]):
1938
1938
 
1939
1939
  async def action_quit(self) -> None:
1940
1940
  """Quit the application with graceful cancellation."""
1941
+ self.update_status("Shutting down...")
1941
1942
  if self._client and not self._completed:
1942
1943
  try:
1943
- await self._client.cancel()
1944
+ await self._client.close()
1944
1945
  except Exception:
1945
1946
  pass # Process may have already exited
1947
+ # Prevent double-close in run_reduction's finally block
1948
+ self._client = None
1946
1949
  self.exit()
1947
1950
 
1948
1951
  def action_show_pass_stats(self) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shrinkray
3
- Version: 26.2.4.0
3
+ Version: 26.2.4.1
4
4
  Summary: Shrink Ray
5
5
  Author-email: "David R. MacIver" <david@drmaciver.com>
6
6
  License: MIT
@@ -8,7 +8,7 @@ shrinkray/process.py,sha256=-eP8h5X0ESbkcTic8FFEzkd4-vwaZ0YI5tLxUR25L8U,1599
8
8
  shrinkray/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  shrinkray/reducer.py,sha256=8CF_SxkfVMBxiikQKwv-rlrQawTLhQxy6QnVwIWWiws,18601
10
10
  shrinkray/state.py,sha256=gDKz1jov84lEih-7jtYcYoWfIS2k2YtBtWuJmcAx6Is,38478
11
- shrinkray/tui.py,sha256=DSVSPdJjf0iwpOwOs55QlEkAQnt-TRXF3MbP3kz5LvA,73778
11
+ shrinkray/tui.py,sha256=40od9sbfOER-Pg_ZRL1MZ8XhOnH8r5axbtJcb827qPo,73924
12
12
  shrinkray/ui.py,sha256=xuDUwU-MM3AetvwUB7bfzav0P_drUsBrKFPhON_Nr-k,2251
13
13
  shrinkray/validation.py,sha256=piBCO-k9he_id6TWC4EHMK3GfuyPqRcNfkNJPVjxEaU,13366
14
14
  shrinkray/work.py,sha256=GEZ14Kk3bvwUxAnACvY-wom2lVWaGrELMNxrDjv03dk,8110
@@ -23,12 +23,12 @@ shrinkray/passes/python.py,sha256=3WN1lZTf5oVL8FCTGomhrCuE04wIX9ocKcmFV86NMZA,68
23
23
  shrinkray/passes/sat.py,sha256=OboY6jsKf6lph3pAFh535plvhNOVzEF8HJ66WEqsNm4,19483
24
24
  shrinkray/passes/sequences.py,sha256=-5ajmMeHnS7onjjppbxLiP0F6mRSqiFI5DspBTj2x_M,2206
25
25
  shrinkray/subprocess/__init__.py,sha256=qxZ19Nzizbm7H0MkKL38OqfP7U-VuOAvaqBVkmHFivY,375
26
- shrinkray/subprocess/client.py,sha256=c430-qYlLlVDHoaCt2Ho2ZjbIKaGYroyt6qXfp3tvkQ,11336
26
+ shrinkray/subprocess/client.py,sha256=4nMFiHfqGSLQHTZ1jgt4dq2GUCBXwgTqvzPw6Z1N1q4,12199
27
27
  shrinkray/subprocess/protocol.py,sha256=86sSxexQpPpr4W2C1y0V5Ddqoqb-1LfHa2wKjJSzmJA,7340
28
28
  shrinkray/subprocess/worker.py,sha256=Spy2DjzJGHgFmr9PTxkd68q1REoymWcA4Ls4iDD36tE,31373
29
- shrinkray-26.2.4.0.dist-info/licenses/LICENSE,sha256=iMKX79AuokJfIZUnGUARdUp30vVAoIPOJ7ek8TY63kk,1072
30
- shrinkray-26.2.4.0.dist-info/METADATA,sha256=VrGUvggYiw-gHII_STgq2cx0Ngh1oFjjb_bT4n9G300,7836
31
- shrinkray-26.2.4.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
32
- shrinkray-26.2.4.0.dist-info/entry_points.txt,sha256=wIZvnGyOdVeaLTiv2klnSyTe-EKkkwn4SwHh9bmJ7qk,104
33
- shrinkray-26.2.4.0.dist-info/top_level.txt,sha256=fLif8-rFoFOnf5h8-vs3ECkKNWQopTQh3xvl1s7pchQ,10
34
- shrinkray-26.2.4.0.dist-info/RECORD,,
29
+ shrinkray-26.2.4.1.dist-info/licenses/LICENSE,sha256=iMKX79AuokJfIZUnGUARdUp30vVAoIPOJ7ek8TY63kk,1072
30
+ shrinkray-26.2.4.1.dist-info/METADATA,sha256=t4FlQASaioxkzPRRxdjaMxgd3JU_pFeQuHanZ3sOPFE,7836
31
+ shrinkray-26.2.4.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
32
+ shrinkray-26.2.4.1.dist-info/entry_points.txt,sha256=wIZvnGyOdVeaLTiv2klnSyTe-EKkkwn4SwHh9bmJ7qk,104
33
+ shrinkray-26.2.4.1.dist-info/top_level.txt,sha256=fLif8-rFoFOnf5h8-vs3ECkKNWQopTQh3xvl1s7pchQ,10
34
+ shrinkray-26.2.4.1.dist-info/RECORD,,