Skip to content

Commit e1c1e69

Browse files
committed
Implement uvloop.run()
1 parent 178df25 commit e1c1e69

File tree

6 files changed

+174
-5
lines changed

6 files changed

+174
-5
lines changed

‎README.rst‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,27 @@ uvloop with::
5353
Using uvloop
5454
------------
5555

56+
As of uvloop 0.18, the preferred way of using it is via the
57+
``uvloop.run()`` helper function:
58+
59+
60+
.. code:: python
61+
62+
import uvloop
63+
64+
asyncdefmain():
65+
# Main entry-point.
66+
...
67+
68+
uvloop.run(main())
69+
70+
``uvloop.run()`` works by simply configuring ``asyncio.run()``
71+
to use uvloop, passing all of the arguments to it, such as ``debug``,
72+
e.g. ``uvloop.run(main(), debug=True)``.
73+
74+
With Python 3.11 and earlier the following alternative
75+
snippet can be used:
76+
5677
.. code:: python
5778
5879
import asyncio

‎tests/test_dealloc.py‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,12 @@ def test_dealloc_1(self):
3030
asyncdeftest():
3131
prog='''\
3232
import uvloop
33-
import asyncio
3433
3534
async def foo():
3635
return 42
3736
3837
def main():
39-
uvloop.install()
40-
loop = asyncio.get_event_loop()
38+
loop = uvloop.new_event_loop()
4139
loop.set_debug(True)
4240
loop.run_until_complete(foo())
4341
# Do not close the loop on purpose: let __dealloc__ methods run.

‎tests/test_process.py‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -851,8 +851,7 @@ async def test():
851851
)
852852
await proc.communicate()
853853
854-
uvloop.install()
855-
asyncio.run(test())
854+
uvloop.run(test())
856855
857856
stdin, stdout, stderr = dups
858857
(r, w), = pipes

‎tests/test_runner.py‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
importasyncio
2+
importunittest
3+
importuvloop
4+
5+
6+
classTestSourceCode(unittest.TestCase):
7+
8+
deftest_uvloop_run_1(self):
9+
CNT=0
10+
11+
asyncdefmain():
12+
nonlocalCNT
13+
CNT+=1
14+
15+
loop=asyncio.get_running_loop()
16+
17+
self.assertTrue(isinstance(loop, uvloop.Loop))
18+
self.assertTrue(loop.get_debug())
19+
20+
return'done'
21+
22+
result=uvloop.run(main(), debug=True)
23+
24+
self.assertEqual(result, 'done')
25+
self.assertEqual(CNT, 1)
26+
27+
deftest_uvloop_run_2(self):
28+
29+
asyncdefmain():
30+
pass
31+
32+
coro=main()
33+
withself.assertRaisesRegex(TypeError, ' a non-uvloop event loop'):
34+
uvloop.run(
35+
coro,
36+
loop_factory=asyncio.DefaultEventLoopPolicy().new_event_loop,
37+
)
38+
39+
coro.close()

‎uvloop/__init__.py‎

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
importasyncioas__asyncio
22
importtypingas_typing
3+
importsysas_sys
4+
importwarningsas_warnings
35

46
fromasyncio.eventsimportBaseDefaultEventLoopPolicyas__BasePolicy
57

@@ -22,9 +24,102 @@ def new_event_loop() -> Loop:
2224

2325
definstall() ->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+
defrun(main, *, loop_factory=new_event_loop, debug=None, **run_kwargs):
38+
"""The preferred way of running a coroutine with uvloop."""
39+
40+
asyncdefwrapper():
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+
ifnotisinstance(loop, Loop):
46+
raiseTypeError('uvloop.run() uses a non-uvloop event loop')
47+
returnawaitmain
48+
49+
vi=_sys.version_info[:2]
50+
51+
ifvi<= (3, 10):
52+
# Copied from python/cpython
53+
54+
if__asyncio._get_running_loop() isnotNone:
55+
raiseRuntimeError(
56+
"asyncio.run() cannot be called from a running event loop")
57+
58+
ifnot__asyncio.iscoroutine(main):
59+
raiseValueError("a coroutine was expected, got{!r}".format(main))
60+
61+
loop=loop_factory()
62+
try:
63+
__asyncio.set_event_loop(loop)
64+
ifdebugisnotNone:
65+
loop.set_debug(debug)
66+
returnloop.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+
elifvi== (3, 11):
77+
if__asyncio._get_running_loop() isnotNone:
78+
raiseRuntimeError(
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+
) asrunner:
86+
returnrunner.run(wrapper())
87+
88+
else:
89+
assertvi>= (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+
ifnotto_cancel:
103+
return
104+
105+
fortaskinto_cancel:
106+
task.cancel()
107+
108+
loop.run_until_complete(
109+
__asyncio.gather(*to_cancel, return_exceptions=True)
110+
)
111+
112+
fortaskinto_cancel:
113+
iftask.cancelled():
114+
continue
115+
iftask.exception() isnotNone:
116+
loop.call_exception_handler({
117+
'message': 'unhandled exception during asyncio.run() shutdown',
118+
'exception': task.exception(),
119+
'task': task,
120+
})
121+
122+
28123
classEventLoopPolicy(__BasePolicy):
29124
"""Event loop policy.
30125

‎uvloop/__init__.pyi‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
importsys
2+
3+
fromasyncioimportAbstractEventLoop
4+
fromcollections.abcimportCallable, Coroutine
5+
fromcontextvarsimportContext
6+
fromtypingimportAny, TypeVar
7+
8+
9+
_T=TypeVar('_T')
10+
11+
12+
defrun(
13+
main: Coroutine[Any, Any, _T],
14+
*,
15+
debug: bool|None= ...,
16+
loop_factory: Callable[[], AbstractEventLoop] |None= ...,
17+
) ->_T: ...

0 commit comments

Comments
(0)