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,103 @@ 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+ if hasattr (loop , 'shutdown_default_executor' ):
72+ loop .run_until_complete (loop .shutdown_default_executor ())
73+ finally :
74+ __asyncio .set_event_loop (None )
75+ loop .close ()
76+
77+ elif vi == (3 , 11 ):
78+ if __asyncio ._get_running_loop () is not None :
79+ raise RuntimeError (
80+ "asyncio.run() cannot be called from a running event loop" )
81+
82+ with __asyncio .Runner (
83+ loop_factory = loop_factory ,
84+ debug = debug ,
85+ ** run_kwargs
86+ ) as runner :
87+ return runner .run (wrapper ())
88+
89+ else :
90+ assert vi >= (3 , 12 )
91+ return __asyncio .run (
92+ wrapper (),
93+ loop_factory = loop_factory ,
94+ debug = debug ,
95+ ** run_kwargs
96+ )
97+
98+
99+ def _cancel_all_tasks (loop ):
100+ # Copied from python/cpython
101+
102+ to_cancel = __asyncio .all_tasks (loop )
103+ if not to_cancel :
104+ return
105+
106+ for task in to_cancel :
107+ task .cancel ()
108+
109+ loop .run_until_complete (
110+ __asyncio .gather (* to_cancel , return_exceptions = True )
111+ )
112+
113+ for task in to_cancel :
114+ if task .cancelled ():
115+ continue
116+ if task .exception () is not None :
117+ loop .call_exception_handler ({
118+ 'message' : 'unhandled exception during asyncio.run() shutdown' ,
119+ 'exception' : task .exception (),
120+ 'task' : task ,
121+ })
122+
123+
28124class EventLoopPolicy (__BasePolicy ):
29125 """Event loop policy.
30126
0 commit comments