Skip to content

Commit 21f4096

Browse files
committed
Fix hang-on-close tests
We do this by * running the queue until there are no more queued tasks. * excavating the task from the handle it's queued in and cancelling that when the handle is cancelled.
1 parent 52e455d commit 21f4096

4 files changed

Lines changed: 36 additions & 14 deletions

File tree

tests/test_misc.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -282,36 +282,39 @@ async def cancel_sleep():
282282

283283
@pytest.mark.trio
284284
async def test_wrong_context_manager_order():
285+
take_down = trio.Event()
285286
async def work_in_asyncio():
286287
await asyncio.sleep(0)
287288

288289
async def runner(*, task_status=trio.TASK_STATUS_IGNORED):
289-
await trio_asyncio.run_asyncio(work_in_asyncio)
290+
await trio_asyncio.aio_as_trio(work_in_asyncio)()
290291
try:
291292
task_status.started()
292-
await trio.sleep_forever()
293+
await take_down.wait()
293294
finally:
294-
await trio_asyncio.run_asyncio(work_in_asyncio)
295+
await trio_asyncio.aio_as_trio(work_in_asyncio)()
295296

296297
async with trio.open_nursery() as nursery:
297298
async with trio_asyncio.open_loop():
298299
await nursery.start(runner)
300+
take_down.set()
299301

300302

301303
@pytest.mark.trio
302304
async def test_keyboard_interrupt_teardown():
303305
asyncio_loop_closed = trio.Event()
304306

305307
async def work_in_trio_no_matter_what(*, task_status=trio.TASK_STATUS_IGNORED):
306-
await trio_asyncio.run_asyncio(work_in_asyncio)
308+
await trio_asyncio.aio_as_trio(work_in_asyncio)()
307309
try:
308310
# KeyboardInterrupt won't cancel this coroutine thanks to the shield
309311
with trio.open_cancel_scope(shield=True):
310312
task_status.started()
311313
await asyncio_loop_closed.wait()
312314
finally:
313315
# Hence this call will be exceuted after run_asyncio_loop is cancelled
314-
await trio_asyncio.run_asyncio(work_in_asyncio)
316+
with pytest.raises(RuntimeError):
317+
await trio_asyncio.aio_as_trio(work_in_asyncio)()
315318

316319
async def work_in_asyncio():
317320
await asyncio.sleep(0)
@@ -331,7 +334,8 @@ async def run_asyncio_loop(nursery, *, task_status=trio.TASK_STATUS_IGNORED):
331334
import signal
332335
import threading
333336
with pytest.raises(KeyboardInterrupt):
334-
async with trio.open_nursery() as nursery:
335-
await nursery.start(run_asyncio_loop, nursery)
336-
# Trigger KeyboardInterrupt that should propagate accross the coroutines
337-
signal.pthread_kill(threading.get_ident(), signal.SIGINT)
337+
with pytest.warns(RuntimeWarning):
338+
async with trio.open_nursery() as nursery:
339+
await nursery.start(run_asyncio_loop, nursery)
340+
# Trigger KeyboardInterrupt that should propagate accross the coroutines
341+
signal.pthread_kill(threading.get_ident(), signal.SIGINT)

trio_asyncio/async_.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,11 @@ def _main_loop_exit(self):
123123
try:
124124
await loop.stop().wait()
125125
finally:
126-
await loop._main_loop_exit()
127-
loop.close()
128-
nursery.cancel_scope.cancel()
129-
current_loop.reset(old_loop)
130-
current_policy.reset(old_policy)
126+
try:
127+
await loop._main_loop_exit()
128+
finally:
129+
loop.close()
130+
nursery.cancel_scope.cancel()
131+
current_loop.reset(old_loop)
132+
current_policy.reset(old_policy)
133+

trio_asyncio/base.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,12 @@ async def _main_loop_exit(self):
782782
self.stop()
783783
await self.wait_stopped()
784784

785+
while True:
786+
try:
787+
await self._main_loop_one(no_wait=True)
788+
except trio.WouldBlock:
789+
break
790+
785791
# Kill off unprocessed work
786792
self._cancel_fds()
787793
self._cancel_timers()

trio_asyncio/handles.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,18 @@ def _init(self, is_sync):
4646
self._scope = None
4747

4848
def cancel(self):
49+
try:
50+
task = self._callback.__self__
51+
except AttributeError:
52+
task = None
53+
else:
54+
if not isinstance(task, asyncio.Task):
55+
task = None
4956
super().cancel()
5057
if self._scope is not None:
5158
self._scope.cancel()
59+
elif task is not None:
60+
task.cancel()
5261

5362
def _cb_future_cancel(self, f):
5463
"""If a Trio task completes an asyncio Future,

0 commit comments

Comments
 (0)