2
2
3
3
import asyncio
4
4
import contextlib
5
- import contextvars
6
5
import queue
7
6
import signal
8
7
import socket
11
10
import time
12
11
import traceback
13
12
import warnings
13
+ import weakref
14
14
from collections .abc import AsyncGenerator , Awaitable , Callable
15
15
from functools import partial
16
16
from math import inf
22
22
)
23
23
24
24
import pytest
25
+ import sniffio
25
26
from outcome import Outcome
26
27
27
28
import trio
@@ -221,7 +222,8 @@ async def trio_main(in_host: InHost) -> str:
221
222
222
223
223
224
def 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
225
227
226
228
async def trio_main (in_host : InHost ) -> str :
227
229
async def synchronize () -> None :
@@ -439,9 +441,9 @@ def aiotrio_run(
439
441
loop = asyncio .new_event_loop ()
440
442
441
443
async def aio_main () -> T :
442
- trio_done_fut = loop .create_future ()
444
+ trio_done_fut : asyncio . Future [ Outcome [ T ]] = loop .create_future ()
443
445
444
- def trio_done_callback (main_outcome : Outcome [object ]) -> None :
446
+ def trio_done_callback (main_outcome : Outcome [T ]) -> None :
445
447
print (f"trio_fn finished: { main_outcome !r} " )
446
448
trio_done_fut .set_result (main_outcome )
447
449
@@ -455,9 +457,11 @@ def trio_done_callback(main_outcome: Outcome[object]) -> None:
455
457
** start_guest_run_kwargs ,
456
458
)
457
459
458
- return (await trio_done_fut ).unwrap () # type: ignore[no-any-return]
460
+ return (await trio_done_fut ).unwrap ()
459
461
460
462
try :
463
+ # can't use asyncio.run because that fails on Windows (3.8, x64, with
464
+ # Komodia LSP)
461
465
return loop .run_until_complete (aio_main ())
462
466
finally :
463
467
loop .close ()
@@ -628,8 +632,6 @@ async def trio_main(in_host: InHost) -> None:
628
632
629
633
@restore_unraisablehook ()
630
634
def test_guest_mode_asyncgens () -> None :
631
- import sniffio
632
-
633
635
record = set ()
634
636
635
637
async def agen (label : str ) -> AsyncGenerator [int , None ]:
@@ -656,9 +658,49 @@ async def trio_main() -> None:
656
658
657
659
gc_collect_harder ()
658
660
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 )
663
662
664
663
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