Skip to content

Commit e5a4d6d

Browse files
committed
Add tests for run_trio_task, fix code to satisfy them
1 parent 111dd16 commit e5a4d6d

File tree

2 files changed

+74
-14
lines changed

2 files changed

+74
-14
lines changed

tests/test_misc.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,75 @@ async def trio_task():
396396
assert fut.cancelled()
397397

398398

399+
@pytest.mark.trio
400+
async def test_trio_as_fut_throws_after_cancelled():
401+
"""If a trio_as_future() future is cancelled, any exception
402+
thrown by the Trio task as it unwinds is ignored. (This is
403+
somewhat infelicitous, but the asyncio Future API doesn't allow
404+
a future to go from cancelled to some other outcome.)
405+
"""
406+
407+
async def trio_task():
408+
try:
409+
await trio.sleep_forever()
410+
finally:
411+
raise ValueError("hi")
412+
413+
async with trio_asyncio.open_loop() as loop:
414+
fut = loop.trio_as_future(trio_task)
415+
await trio.testing.wait_all_tasks_blocked()
416+
fut.cancel()
417+
with pytest.raises(asyncio.CancelledError):
418+
await fut
419+
420+
421+
@pytest.mark.trio
422+
async def test_run_trio_task_errors(monkeypatch):
423+
async with trio_asyncio.open_loop() as loop:
424+
# Test never getting to start the task
425+
handle = loop.run_trio_task(trio.sleep_forever)
426+
handle.cancel()
427+
428+
# Test cancelling the task
429+
handle = loop.run_trio_task(trio.sleep_forever)
430+
await trio.testing.wait_all_tasks_blocked()
431+
handle.cancel()
432+
433+
# Helper for the rest of this test, which covers cases where
434+
# the Trio task raises an exception
435+
async def raise_in_aio_loop(exc):
436+
async def raise_it():
437+
raise exc
438+
439+
async with trio_asyncio.open_loop() as loop:
440+
loop.run_trio_task(raise_it)
441+
442+
# We temporarily modify the default exception handler to collect
443+
# the exceptions instead of logging or raising them
444+
445+
exceptions = []
446+
447+
def collect_exceptions(loop, context):
448+
if context.get("exception"):
449+
exceptions.append(context["exception"])
450+
else:
451+
exceptions.append(RuntimeError(context.get("message") or "unknown"))
452+
453+
monkeypatch.setattr(
454+
trio_asyncio.TrioEventLoop, "default_exception_handler", collect_exceptions
455+
)
456+
expected = [
457+
ValueError("hi"), ValueError("lo"), KeyError(), IndexError()
458+
]
459+
await raise_in_aio_loop(expected[0])
460+
with pytest.raises(SystemExit):
461+
await raise_in_aio_loop(SystemExit(0))
462+
with pytest.raises(SystemExit):
463+
await raise_in_aio_loop(trio.MultiError([expected[1], SystemExit()]))
464+
await raise_in_aio_loop(trio.MultiError(expected[2:]))
465+
assert exceptions == expected
466+
467+
399468
@pytest.mark.trio
400469
@pytest.mark.skipif(sys.version_info < (3, 7), reason="needs asyncio contextvars")
401470
async def test_contextvars():

trio_asyncio/_handles.py

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,10 @@ def report_exception(exc):
8888
if not isinstance(exc, Exception):
8989
# Let BaseExceptions such as Cancelled escape without being noted.
9090
return exc
91-
try:
92-
orig_tb = exc.__traceback__
93-
self._raise(exc)
94-
# If self._raise() just logged something, suppress the exception.
95-
return None
96-
except BaseException as other_exc:
97-
if other_exc is exc:
98-
# If _raise() reraised its argument, remove the _raise and
99-
# default_exception_handler frames that it added to the traceback.
100-
return exc.with_traceback(orig_tb)
101-
# If _raise() raised a different exception, don't mess with
102-
# the traceback.
103-
return other_exc
91+
# Otherwise defer to the asyncio exception handler. (In an async loop
92+
# this will still raise the exception out of the loop, terminating it.)
93+
self._raise(exc)
94+
return None
10495

10596
def remove_cancelled(exc):
10697
if isinstance(exc, trio.Cancelled):
@@ -129,7 +120,7 @@ def only_cancelled(exc):
129120
# Pass Exceptions through the fallback exception handler since
130121
# they have nowhere better to go. Let BaseExceptions escape so
131122
# that Cancelled and SystemExit work reasonably.
132-
with trio.MultiError.catch(handle_exc):
123+
with trio.MultiError.catch(report_exception):
133124
raise
134125
else:
135126
# The result future gets all the non-Cancelled

0 commit comments

Comments
 (0)