Skip to content

Commit 54526b9

Browse files
committed
More robust async event shielding
1 parent cc431df commit 54526b9

File tree

2 files changed

+17
-4
lines changed

2 files changed

+17
-4
lines changed

src/reactpy/core/hooks.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,13 @@ def decorator(func: _AsyncEffectFunc) -> None:
231231
async def effect(stop: asyncio.Event) -> None:
232232
# Make sure we always clean up the previous effect's resources
233233
if pending_task.current:
234-
pending_task.current.cancel()
234+
previous_task = pending_task.current
235+
if not shield:
236+
previous_task.cancel()
235237
with contextlib.suppress(asyncio.CancelledError):
236-
await pending_task.current
238+
await previous_task
239+
if pending_task.current is previous_task:
240+
pending_task.current = None
237241

238242
run_effect_cleanup(cleanup_func)
239243

@@ -267,7 +271,9 @@ async def effect(stop: asyncio.Event) -> None:
267271
if not shield:
268272
task.cancel()
269273
with contextlib.suppress(asyncio.CancelledError):
270-
await task
274+
cleanup_func.current = await task
275+
if pending_task.current is task:
276+
pending_task.current = None
271277

272278
# Run the clean-up function when the effect is stopped,
273279
# if it hasn't been run already by a new effect

tests/test_core/test_hooks.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@ async def effect():
609609
async def test_use_async_effect_shield():
610610
component_hook = HookCatcher()
611611
effect_ran = asyncio.Event()
612+
effect_was_cancelled = asyncio.Event()
612613
effect_finished = asyncio.Event()
613614
stop_waiting = asyncio.Event()
614615

@@ -618,7 +619,11 @@ def ComponentWithShieldedEffect():
618619
@reactpy.hooks.use_async_effect(dependencies=None, shield=True)
619620
async def effect():
620621
effect_ran.set()
621-
await stop_waiting.wait()
622+
try:
623+
await stop_waiting.wait()
624+
except asyncio.CancelledError:
625+
effect_was_cancelled.set()
626+
raise
622627
effect_finished.set()
623628

624629
return reactpy.html.div()
@@ -636,6 +641,7 @@ async def effect():
636641

637642
# Verify effect hasn't finished yet but also wasn't cancelled
638643
assert not effect_finished.is_set()
644+
assert not effect_was_cancelled.is_set()
639645

640646
# Now allow the effect to finish
641647
stop_waiting.set()
@@ -644,6 +650,7 @@ async def effect():
644650
await layout.render()
645651

646652
await asyncio.wait_for(effect_finished.wait(), 1)
653+
assert not effect_was_cancelled.is_set()
647654

648655

649656
async def test_async_effect_sleep_is_cancelled_on_re_render():

0 commit comments

Comments
 (0)