1111timeout = 60
1212
1313
14- @pytest_asyncio .fixture (scope = "function" )
14+ # Override the event loop so it never closes during test execution
15+ # This helps with pytest-xdist and prevents "Event loop is closed" errors
16+ @pytest .fixture (scope = "session" )
1517def event_loop ():
16- """Create an instance of the default event loop for each test case."""
17- policy = asyncio .get_event_loop_policy ()
18- loop = policy .new_event_loop ()
19- yield loop
20- # Clean up any remaining tasks
21- try :
22- pending = asyncio .all_tasks (loop )
23- for task in pending :
24- task .cancel ()
25- if pending :
26- loop .run_until_complete (asyncio .gather (* pending , return_exceptions = True ))
27- except Exception :
28- pass
29- # Close the loop gracefully
18+ """Create a session-scoped event loop for all async tests."""
3019 try :
31- loop .close ()
32- except Exception :
33- pass
20+ loop = asyncio .get_running_loop ()
21+ except RuntimeError :
22+ loop = asyncio .new_event_loop ()
23+ yield loop
24+ loop .close ()
3425
3526
3627@pytest .fixture ()
@@ -54,24 +45,38 @@ def sandbox(template, debug):
5445 )
5546
5647
57- @pytest_asyncio .fixture (loop_scope = "function" )
58- async def async_sandbox (template , debug ):
59- async_sandbox = await AsyncSandbox .create (template , timeout = timeout , debug = debug )
48+ @pytest .fixture
49+ def async_sandbox_factory (request , template , debug , event_loop ):
50+ """Factory for creating async sandboxes with proper cleanup."""
51+ async def factory (template_override = None , ** kwargs ):
52+ template_name = template_override or template
53+ kwargs .setdefault ("timeout" , timeout )
54+ kwargs .setdefault ("debug" , debug )
55+
56+ sandbox = await AsyncSandbox .create (template_name , ** kwargs )
6057
61- try :
62- yield async_sandbox
63- finally :
64- try :
65- await async_sandbox .kill ()
66- except RuntimeError as e :
67- # Ignore "Event loop is closed" errors during cleanup in pytest-xdist
68- if "Event loop is closed" not in str (e ):
69- raise
70- except : # noqa: E722
71- if not debug :
72- warning (
73- "Failed to kill sandbox — this is expected if the test runs with local envd."
74- )
58+ def kill ():
59+ async def _kill ():
60+ try :
61+ await sandbox .kill ()
62+ except : # noqa: E722
63+ if not debug :
64+ warning (
65+ "Failed to kill sandbox — this is expected if the test runs with local envd."
66+ )
67+
68+ event_loop .run_until_complete (_kill ())
69+
70+ request .addfinalizer (kill )
71+ return sandbox
72+
73+ return factory
74+
75+
76+ @pytest .fixture
77+ async def async_sandbox (async_sandbox_factory ):
78+ """Default async sandbox fixture."""
79+ return await async_sandbox_factory ()
7580
7681
7782@pytest .fixture
0 commit comments