22
33import asyncio
44import contextlib
5- import contextvars
65import queue
76import signal
87import socket
1110import time
1211import traceback
1312import warnings
13+ import weakref
1414from collections .abc import AsyncGenerator , Awaitable , Callable , Sequence
1515from functools import partial
1616from math import inf
2222)
2323
2424import pytest
25+ import sniffio
2526from outcome import Outcome
2627
2728import trio
@@ -234,7 +235,8 @@ async def trio_main(in_host: InHost) -> str:
234235
235236
236237def test_guest_mode_sniffio_integration () -> None :
237- from sniffio import current_async_library , thread_local as sniffio_library
238+ current_async_library = sniffio .current_async_library
239+ sniffio_library = sniffio .thread_local
238240
239241 async def trio_main (in_host : InHost ) -> str :
240242 async def synchronize () -> None :
@@ -458,9 +460,9 @@ def aiotrio_run(
458460
459461 async def aio_main () -> T :
460462 nonlocal run_sync_soon_not_threadsafe
461- trio_done_fut = loop .create_future ()
463+ trio_done_fut : asyncio . Future [ Outcome [ T ]] = loop .create_future ()
462464
463- def trio_done_callback (main_outcome : Outcome [object ]) -> None :
465+ def trio_done_callback (main_outcome : Outcome [T ]) -> None :
464466 print (f"trio_fn finished: { main_outcome !r} " )
465467 trio_done_fut .set_result (main_outcome )
466468
@@ -479,9 +481,11 @@ def trio_done_callback(main_outcome: Outcome[object]) -> None:
479481 strict_exception_groups = strict_exception_groups ,
480482 )
481483
482- return (await trio_done_fut ).unwrap () # type: ignore[no-any-return]
484+ return (await trio_done_fut ).unwrap ()
483485
484486 try :
487+ # can't use asyncio.run because that fails on Windows (3.8, x64, with
488+ # Komodia LSP) and segfaults on Windows (3.9, x64, with Komodia LSP)
485489 return loop .run_until_complete (aio_main ())
486490 finally :
487491 loop .close ()
@@ -655,8 +659,6 @@ async def trio_main(in_host: InHost) -> None:
655659
656660@restore_unraisablehook ()
657661def test_guest_mode_asyncgens () -> None :
658- import sniffio
659-
660662 record = set ()
661663
662664 async def agen (label : str ) -> AsyncGenerator [int , None ]:
@@ -683,9 +685,49 @@ async def trio_main() -> None:
683685
684686 gc_collect_harder ()
685687
686- # Ensure we don't pollute the thread-level context if run under
687- # an asyncio without contextvars support (3.6)
688- context = contextvars .copy_context ()
689- context .run (aiotrio_run , trio_main , host_uses_signal_set_wakeup_fd = True )
688+ aiotrio_run (trio_main , host_uses_signal_set_wakeup_fd = True )
690689
691690 assert record == {("asyncio" , "asyncio" ), ("trio" , "trio" )}
691+
692+
693+ @restore_unraisablehook ()
694+ def test_guest_mode_asyncgens_garbage_collection () -> None :
695+ record : set [tuple [str , str , bool ]] = set ()
696+
697+ async def agen (label : str ) -> AsyncGenerator [int , None ]:
698+ class A :
699+ pass
700+
701+ a = A ()
702+ a_wr = weakref .ref (a )
703+ assert sniffio .current_async_library () == label
704+ try :
705+ yield 1
706+ finally :
707+ library = sniffio .current_async_library ()
708+ with contextlib .suppress (trio .Cancelled ):
709+ await sys .modules [library ].sleep (0 )
710+
711+ del a
712+ if sys .implementation .name == "pypy" :
713+ gc_collect_harder ()
714+
715+ record .add ((label , library , a_wr () is None ))
716+
717+ async def iterate_in_aio () -> None :
718+ await agen ("asyncio" ).asend (None )
719+
720+ async def trio_main () -> None :
721+ task = asyncio .ensure_future (iterate_in_aio ())
722+ done_evt = trio .Event ()
723+ task .add_done_callback (lambda _ : done_evt .set ())
724+ with trio .fail_after (1 ):
725+ await done_evt .wait ()
726+
727+ await agen ("trio" ).asend (None )
728+
729+ gc_collect_harder ()
730+
731+ aiotrio_run (trio_main , host_uses_signal_set_wakeup_fd = True )
732+
733+ assert record == {("asyncio" , "asyncio" , True ), ("trio" , "trio" , True )}
0 commit comments