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
1515from functools import partial
1616from math import inf
2222)
2323
2424import pytest
25+ import sniffio
2526from outcome import Outcome
2627
2728import trio
@@ -221,7 +222,8 @@ async def trio_main(in_host: InHost) -> str:
221222
222223
223224def test_guest_mode_sniffio_integration () -> None :
224- from sniffio import current_async_library , thread_local as sniffio_library
225+ current_async_library = sniffio .current_async_library
226+ sniffio_library = sniffio .thread_local
225227
226228 async def trio_main (in_host : InHost ) -> str :
227229 async def synchronize () -> None :
@@ -439,9 +441,9 @@ def aiotrio_run(
439441 loop = asyncio .new_event_loop ()
440442
441443 async def aio_main () -> T :
442- trio_done_fut = loop .create_future ()
444+ trio_done_fut : asyncio . Future [ Outcome [ T ]] = loop .create_future ()
443445
444- def trio_done_callback (main_outcome : Outcome [object ]) -> None :
446+ def trio_done_callback (main_outcome : Outcome [T ]) -> None :
445447 print (f"trio_fn finished: { main_outcome !r} " )
446448 trio_done_fut .set_result (main_outcome )
447449
@@ -455,9 +457,11 @@ def trio_done_callback(main_outcome: Outcome[object]) -> None:
455457 ** start_guest_run_kwargs ,
456458 )
457459
458- return (await trio_done_fut ).unwrap () # type: ignore[no-any-return]
460+ return (await trio_done_fut ).unwrap ()
459461
460462 try :
463+ # can't use asyncio.run because that fails on Windows (3.8, x64, with
464+ # Komodia LSP)
461465 return loop .run_until_complete (aio_main ())
462466 finally :
463467 loop .close ()
@@ -628,8 +632,6 @@ async def trio_main(in_host: InHost) -> None:
628632
629633@restore_unraisablehook ()
630634def test_guest_mode_asyncgens () -> None :
631- import sniffio
632-
633635 record = set ()
634636
635637 async def agen (label : str ) -> AsyncGenerator [int , None ]:
@@ -656,9 +658,49 @@ async def trio_main() -> None:
656658
657659 gc_collect_harder ()
658660
659- # Ensure we don't pollute the thread-level context if run under
660- # an asyncio without contextvars support (3.6)
661- context = contextvars .copy_context ()
662- context .run (aiotrio_run , trio_main , host_uses_signal_set_wakeup_fd = True )
661+ aiotrio_run (trio_main , host_uses_signal_set_wakeup_fd = True )
663662
664663 assert record == {("asyncio" , "asyncio" ), ("trio" , "trio" )}
664+
665+
666+ @restore_unraisablehook ()
667+ def test_guest_mode_asyncgens_garbage_collection () -> None :
668+ record : set [tuple [str , str , bool ]] = set ()
669+
670+ async def agen (label : str ) -> AsyncGenerator [int , None ]:
671+ class A :
672+ pass
673+
674+ a = A ()
675+ a_wr = weakref .ref (a )
676+ assert sniffio .current_async_library () == label
677+ try :
678+ yield 1
679+ finally :
680+ library = sniffio .current_async_library ()
681+ with contextlib .suppress (trio .Cancelled ):
682+ await sys .modules [library ].sleep (0 )
683+
684+ del a
685+ if sys .implementation .name == "pypy" :
686+ gc_collect_harder ()
687+
688+ record .add ((label , library , a_wr () is None ))
689+
690+ async def iterate_in_aio () -> None :
691+ await agen ("asyncio" ).asend (None )
692+
693+ async def trio_main () -> None :
694+ task = asyncio .ensure_future (iterate_in_aio ())
695+ done_evt = trio .Event ()
696+ task .add_done_callback (lambda _ : done_evt .set ())
697+ with trio .fail_after (1 ):
698+ await done_evt .wait ()
699+
700+ await agen ("trio" ).asend (None )
701+
702+ gc_collect_harder ()
703+
704+ aiotrio_run (trio_main , host_uses_signal_set_wakeup_fd = True )
705+
706+ assert record == {("asyncio" , "asyncio" , True ), ("trio" , "trio" , True )}
0 commit comments