1
1
import asyncio
2
2
import contextlib
3
3
import contextvars
4
- from typing import List , Callable
4
+ from enum import Enum
5
+ from typing import List , Callable , Optional
5
6
6
7
try :
7
- """ Prefer faster event loop implementation. """
8
- from uvloop import run as async_run
8
+ import uvloop
9
9
except ImportError :
10
- from asyncio import run as async_run
10
+ uvloop = None
11
11
12
- _asyncio_debug_enabled = contextvars .ContextVar ("_debug_enabled" , default = False )
12
+
13
+ class EventLoopBackend (Enum ):
14
+ AUTO = "auto"
15
+ UVLOOP = "uvloop"
16
+ ASYNCIO = "asyncio"
17
+
18
+ @property
19
+ def runner (self ) -> Callable :
20
+ if self is EventLoopBackend .AUTO :
21
+ # Prefer UVLoop if available
22
+ return uvloop .run if uvloop is not None else asyncio .run
23
+
24
+ if self is EventLoopBackend .UVLOOP :
25
+ assert uvloop is not None , "uvloop is not installed"
26
+ return uvloop .run
27
+
28
+ return asyncio .run
29
+
30
+
31
+ _loop_debug_enabled = contextvars .ContextVar ("_loop_debug_enabled" , default = False )
32
+ _loop_backend = contextvars .ContextVar ("_loop_backend" , default = EventLoopBackend .AUTO )
13
33
14
34
15
35
@contextlib .contextmanager
16
36
def enable_asyncio_debug ():
17
- token = _asyncio_debug_enabled .set (True )
37
+ token = _loop_debug_enabled .set (True )
18
38
try :
19
39
yield
20
40
finally :
21
- _asyncio_debug_enabled .reset (token )
41
+ _loop_debug_enabled .reset (token )
22
42
23
43
24
44
class EventLoopRunner :
25
45
""" Wrapper around uvloop/asyncio implementations for running the main event loop. """
26
46
debug = False
27
47
context_stack : List [Callable [[], contextlib .AbstractContextManager ]]
48
+ backend : Optional [EventLoopBackend ] = None
28
49
29
- def __init__ (self , debug = False , context_stack = None ):
50
+ def __init__ (self , debug = False , context_stack = None , backend = None ):
30
51
self .debug = debug
31
52
self .context_stack = [] if context_stack is None else context_stack
53
+ self .backend = backend
32
54
33
55
def __enter__ (self ):
34
56
return self
@@ -42,6 +64,9 @@ def run(self, *args, **kwargs) -> None:
42
64
for context_func in self .context_stack :
43
65
stack .enter_context (context_func ())
44
66
45
- return async_run (* args , debug = _asyncio_debug_enabled .get () or self .debug , ** kwargs )
67
+
629A
debug = self .debug or _loop_debug_enabled .get ()
68
+ backend = self .backend or _loop_backend .get ()
69
+
70
+ return backend .runner (* args , debug = debug , ** kwargs )
46
71
except asyncio .CancelledError :
47
72
pass
0 commit comments