11import asyncio as __asyncio
22import typing as _typing
3+ import sys as _sys
4+ import warnings as _warnings
35
46from asyncio .events import BaseDefaultEventLoopPolicy as __BasePolicy
57
@@ -22,9 +24,102 @@ def new_event_loop() -> Loop:
2224
2325def install () -> None :
2426"""A helper function to install uvloop policy."""
27+ if _sys .version_info [:2 ] >= (3 , 12 ):
28+ _warnings .warn (
29+ 'uvloop.install() is deprecated in favor of uvloop.run() '
30+ 'starting with Python 3.12.' ,
31+ DeprecationWarning ,
32+ stacklevel = 1 ,
33+ )
2534__asyncio .set_event_loop_policy (EventLoopPolicy ())
2635
2736
37+ def run (main , * , loop_factory = new_event_loop , debug = None , ** run_kwargs ):
38+ """The preferred way of running a coroutine with uvloop."""
39+
40+ async def wrapper ():
41+ # If `loop_factory` is provided we want it to return
42+ # either uvloop.Loop or a subtype of it, assuming the user
43+ # is using `uvloop.run()` intentionally.
44+ loop = __asyncio ._get_running_loop ()
45+ if not isinstance (loop , Loop ):
46+ raise TypeError ('uvloop.run() uses a non-uvloop event loop' )
47+ return await main
48+
49+ vi = _sys .version_info [:2 ]
50+
51+ if vi <= (3 , 10 ):
52+ # Copied from python/cpython
53+
54+ if __asyncio ._get_running_loop () is not None :
55+ raise RuntimeError (
56+ "asyncio.run() cannot be called from a running event loop" )
57+
58+ if not __asyncio .iscoroutine (main ):
59+ raise ValueError ("a coroutine was expected, got{!r}" .format (main ))
60+
61+ loop = loop_factory ()
62+ try :
63+ __asyncio .set_event_loop (loop )
64+ if debug is not None :
65+ loop .set_debug (debug )
66+ return loop .run_until_complete (wrapper ())
67+ finally :
68+ try :
69+ _cancel_all_tasks (loop )
70+ loop .run_until_complete (loop .shutdown_asyncgens ())
71+ loop .run_until_complete (loop .shutdown_default_executor ())
72+ finally :
73+ __asyncio .set_event_loop (None )
74+ loop .close ()
75+
76+ elif vi == (3 , 11 ):
77+ if __asyncio ._get_running_loop () is not None :
78+ raise RuntimeError (
79+ "asyncio.run() cannot be called from a running event loop" )
80+
81+ with __asyncio .Runner (
82+ loop_factory = loop_factory ,
83+ debug = debug ,
84+ ** run_kwargs
85+ ) as runner :
86+ return runner .run (wrapper ())
87+
88+ else :
89+ assert vi >= (3 , 12 )
90+ return __asyncio .run (
91+ wrapper (),
92+ loop_factory = loop_factory ,
93+ debug = debug ,
94+ ** run_kwargs
95+ )
96+
97+
98+ def _cancel_all_tasks (loop ):
99+ # Copied from python/cpython
100+
101+ to_cancel = __asyncio .all_tasks (loop )
102+ if not to_cancel :
103+ return
104+
105+ for task in to_cancel :
106+ task .cancel ()
107+
108+ loop .run_until_complete (
109+ __asyncio .gather (* to_cancel , return_exceptions = True )
110+ )
111+
112+ for task in to_cancel :
113+ if task .cancelled ():
114+ continue
115+ if task .exception () is not None :
116+ loop .call_exception_handler ({
117+ 'message' : 'unhandled exception during asyncio.run() shutdown' ,
118+ 'exception' : task .exception (),
119+ 'task' : task ,
120+ })
121+
122+
28123class EventLoopPolicy (__BasePolicy ):
29124"""Event loop policy.
30125
0 commit comments