Skip to content

Commit c175af6

Browse files
committed
fix coverage
1 parent d5d8cc7 commit c175af6

File tree

2 files changed

+48
-13
lines changed

2 files changed

+48
-13
lines changed

Diff for: src/py/reactpy/reactpy/core/hooks.py

+22-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import inspect
44
import sys
55
import warnings
6-
from asyncio import CancelledError, Event, create_task
6+
from asyncio import FIRST_COMPLETED, CancelledError, Event, create_task, wait
77
from collections.abc import Awaitable, Coroutine, Sequence
88
from logging import getLogger
99
from types import FunctionType
@@ -240,20 +240,36 @@ async def __aexit__(self, exc_type: type[BaseException], *exc: Any) -> Any:
240240
# propagate non-cancellation exceptions
241241
return None
242242

243+
if not self._maybe_uncancel_task():
244+
return None
245+
246+
wait_for_stop = create_task(self._stop.wait())
243247
try:
244-
await self._stop.wait()
248+
await wait([wait_for_stop, self.task], return_when=FIRST_COMPLETED)
245249
except CancelledError:
246-
if self.task.cancelling() > self._cancel_count:
247-
# Task has been cancelled by something else - propagate it
248-
return None
249-
self.task.uncancel()
250+
if not self._maybe_uncancel_task():
251+
raise
250252

251253
return True
252254

253255
def _cancel_task(self) -> None:
254256
self.task.cancel()
255257
self._cancel_count += 1
256258

259+
def _maybe_uncancel_task(self) -> bool:
260+
"""Return if task was uncancelled
261+
262+
If task was not cancelled by this effect then returns. Otherwise, if the task
263+
was cancelled at all, uncancell it and return True
264+
"""
265+
if self.task.cancelling() > self._cancel_count:
266+
# Task has been cancelled by something else - propagate it
267+
return False
268+
elif self._cancel_count:
269+
for _ in range(self._cancel_count):
270+
self.task.uncancel()
271+
return True
272+
257273

258274
def _cast_async_effect(function: Callable[..., Any]) -> _AsyncEffectFunc:
259275
if inspect.iscoroutinefunction(function):

Diff for: src/py/reactpy/tests/test_core/test_hooks.py

+26-7
Original file line numberDiff line numberDiff line change
@@ -587,27 +587,46 @@ async def effect_func(e):
587587
await did_cleanup.wait()
588588

589589

590-
async def test_effect_external_cancellation_is_propagated():
590+
async def test_effect_internal_cancellation_is_propagated():
591591
did_start = WaitForEvent()
592592
did_cleanup = Ref(False)
593+
never_happens = asyncio.Event()
593594

594595
async def effect_func(e):
595596
async with e:
596597
did_start.set()
597-
# We don't use e.task or asyncio.current_task() because there seems to
598-
# be some platform dependence on whether the task is cancelled properly
599-
outer_task.cancel()
600-
# Allow cancellation to propagate
601-
await asyncio.sleep(0)
598+
asyncio.current_task().cancel()
599+
await never_happens.wait()
602600
did_cleanup.current = True
603601

604602
async def main():
605603
effect = Effect(effect_func)
606604
await did_start.wait()
607-
await effect.stop()
605+
await effect.task
606+
607+
with pytest.raises(asyncio.CancelledError):
608+
await main()
609+
610+
assert not did_cleanup.current
611+
612+
613+
async def test_effect_external_cancellation_is_propagated():
614+
did_start = WaitForEvent()
615+
did_cleanup = Ref(False)
616+
617+
async def effect_func(e):
618+
async with e:
619+
did_start.set()
620+
did_cleanup.current = True
621+
622+
async def main():
623+
effect = Effect(effect_func)
624+
await effect.task
608625

609626
with pytest.raises(asyncio.CancelledError):
610627
outer_task = asyncio.create_task(main())
628+
await did_start.wait()
629+
outer_task.cancel()
611630
await outer_task
612631

613632
assert not did_cleanup.current

0 commit comments

Comments
 (0)