committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 173 additions and 9 deletions
-
31Doc/library/asyncio-task.rst
-
2Lib/asyncio/__init__.py
-
48Lib/asyncio/runners.py
-
100Lib/test/test_asyncio/test_runners.py
-
1Misc/NEWS.d/next/Library/2017-12-13-16-47-38.bpo-32314.W4_U2j.rst
@ -0,0 +1,48 @@ |
|||
__all__ = 'run', |
|||
|
|||
from . import coroutines |
|||
from . import events |
|||
|
|||
|
|||
def run(main, *, debug=False): |
|||
"""Run a coroutine. |
|||
|
|||
This function runs the passed coroutine, taking care of |
|||
managing the asyncio event loop and finalizing asynchronous |
|||
generators. |
|||
|
|||
This function cannot be called when another asyncio event loop is |
|||
running in the same thread. |
|||
|
|||
If debug is True, the event loop will be run in debug mode. |
|||
|
|||
This function always creates a new event loop and closes it at the end. |
|||
It should be used as a main entry point for asyncio programs, and should |
|||
ideally only be called once. |
|||
|
|||
Example: |
|||
|
|||
async def main(): |
|||
await asyncio.sleep(1) |
|||
print('hello') |
|||
|
|||
asyncio.run(main()) |
|||
""" |
|||
if events._get_running_loop() is not None: |
|||
raise RuntimeError( |
|||
"asyncio.run() cannot be called from a running event loop") |
|||
|
|||
if not coroutines.iscoroutine(main): |
|||
raise ValueError("a coroutine was expected, got {!r}".format(main)) |
|||
|
|||
loop = events.new_event_loop() |
|||
try: |
|||
events.set_event_loop(loop) |
|||
loop.set_debug(debug) |
|||
return loop.run_until_complete(main) |
|||
finally: |
|||
try: |
|||
loop.run_until_complete(loop.shutdown_asyncgens()) |
|||
finally: |
|||
events.set_event_loop(None) |
|||
loop.close() |
|||
@ -0,0 +1,100 @@ |
|||
import asyncio |
|||
import unittest |
|||
|
|||
from unittest import mock |
|||
|
|||
|
|||
class TestPolicy(asyncio.AbstractEventLoopPolicy): |
|||
|
|||
def __init__(self, loop_factory): |
|||
self.loop_factory = loop_factory |
|||
self.loop = None |
|||
|
|||
def get_event_loop(self): |
|||
# shouldn't ever be called by asyncio.run() |
|||
raise RuntimeError |
|||
|
|||
def new_event_loop(self): |
|||
return self.loop_factory() |
|||
|
|||
def set_event_loop(self, loop): |
|||
if loop is not None: |
|||
# we want to check if the loop is closed |
|||
# in BaseTest.tearDown |
|||
self.loop = loop |
|||
|
|||
|
|||
class BaseTest(unittest.TestCase): |
|||
|
|||
def new_loop(self): |
|||
loop = asyncio.BaseEventLoop() |
|||
loop._process_events = mock.Mock() |
|||
loop._selector = mock.Mock() |
|||
loop._selector.select.return_value = () |
|||
loop.shutdown_ag_run = False |
|||
|
|||
async def shutdown_asyncgens(): |
|||
loop.shutdown_ag_run = True |
|||
loop.shutdown_asyncgens = shutdown_asyncgens |
|||
|
|||
return loop |
|||
|
|||
def setUp(self): |
|||
super().setUp() |
|||
|
|||
policy = TestPolicy(self.new_loop) |
|||
asyncio.set_event_loop_policy(policy) |
|||
|
|||
def tearDown(self): |
|||
policy = asyncio.get_event_loop_policy() |
|||
if policy.loop is not None: |
|||
self.assertTrue(policy.loop.is_closed()) |
|||
self.assertTrue(policy.loop.shutdown_ag_run) |
|||
|
|||
asyncio.set_event_loop_policy(None) |
|||
super().tearDown() |
|||
|
|||
|
|||
class RunTests(BaseTest): |
|||
|
|||
def test_asyncio_run_return(self): |
|||
async def main(): |
|||
await asyncio.sleep(0) |
|||
return 42 |
|||
|
|||
self.assertEqual(asyncio.run(main()), 42) |
|||
|
|||
def test_asyncio_run_raises(self): |
|||
async def main(): |
|||
await asyncio.sleep(0) |
|||
raise ValueError('spam') |
|||
|
|||
with self.assertRaisesRegex(ValueError, 'spam'): |
|||
asyncio.run(main()) |
|||
|
|||
def test_asyncio_run_only_coro(self): |
|||
for o in {1, lambda: None}: |
|||
with self.subTest(obj=o), \ |
|||
self.assertRaisesRegex(ValueError, |
|||
'a coroutine was expected'): |
|||
asyncio.run(o) |
|||
|
|||
def test_asyncio_run_debug(self): |
|||
async def main(expected): |
|||
loop = asyncio.get_event_loop() |
|||
self.assertIs(loop.get_debug(), expected) |
|||
|
|||
asyncio.run(main(False)) |
|||
asyncio.run(main(True), debug=True) |
|||
|
|||
def test_asyncio_run_from_running_loop(self): |
|||
async def main(): |
|||
coro = main() |
|||
try: |
|||
asyncio.run(coro) |
|||
finally: |
|||
coro.close() # Suppress ResourceWarning |
|||
|
|||
with self.assertRaisesRegex(RuntimeError, |
|||
'cannot be called from a running'): |
|||
asyncio.run(main()) |
|||
@ -0,0 +1 @@ |
|||
Implement asyncio.run(). |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue