diff options
author | Andrew Svetlov <andrew.svetlov@gmail.com> | 2022-03-14 11:54:13 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-14 11:54:13 (GMT) |
commit | 9523c0d84f351a610dc651b234461eb015fa3b82 (patch) | |
tree | 5f6f6bed4353eb9c149f65ab2dc95db12d378db3 /Lib/unittest | |
parent | 2153daf0a02a598ed5df93f2f224c1ab2a2cca0d (diff) | |
download | cpython-9523c0d84f351a610dc651b234461eb015fa3b82.zip cpython-9523c0d84f351a610dc651b234461eb015fa3b82.tar.gz cpython-9523c0d84f351a610dc651b234461eb015fa3b82.tar.bz2 |
bpo-46994: Accept explicit contextvars.Context in asyncio create_task() API (GH-31837)
Diffstat (limited to 'Lib/unittest')
-rw-r--r-- | Lib/unittest/async_case.py | 55 | ||||
-rw-r--r-- | Lib/unittest/test/test_async_case.py | 18 |
2 files changed, 35 insertions, 38 deletions
diff --git a/Lib/unittest/async_case.py b/Lib/unittest/async_case.py index 3c57bb5..25adc3d 100644 --- a/Lib/unittest/async_case.py +++ b/Lib/unittest/async_case.py @@ -1,4 +1,5 @@ import asyncio +import contextvars import inspect import warnings @@ -34,7 +35,7 @@ class IsolatedAsyncioTestCase(TestCase): def __init__(self, methodName='runTest'): super().__init__(methodName) self._asyncioTestLoop = None - self._asyncioCallsQueue = None + self._asyncioTestContext = contextvars.copy_context() async def asyncSetUp(self): pass @@ -58,7 +59,7 @@ class IsolatedAsyncioTestCase(TestCase): self.addCleanup(*(func, *args), **kwargs) def _callSetUp(self): - self.setUp() + self._asyncioTestContext.run(self.setUp) self._callAsync(self.asyncSetUp) def _callTestMethod(self, method): @@ -68,47 +69,30 @@ class IsolatedAsyncioTestCase(TestCase): def _callTearDown(self): self._callAsync(self.asyncTearDown) - self.tearDown() + self._asyncioTestContext.run(self.tearDown) def _callCleanup(self, function, *args, **kwargs): self._callMaybeAsync(function, *args, **kwargs) def _callAsync(self, func, /, *args, **kwargs): assert self._asyncioTestLoop is not None, 'asyncio test loop is not initialized' - ret = func(*args, **kwargs) - assert inspect.isawaitable(ret), f'{func!r} returned non-awaitable' - fut = self._asyncioTestLoop.create_future() - self._asyncioCallsQueue.put_nowait((fut, ret)) - return self._asyncioTestLoop.run_until_complete(fut) + assert inspect.iscoroutinefunction(func), f'{func!r} is not an async function' + task = self._asyncioTestLoop.create_task( + func(*args, **kwargs), + context=self._asyncioTestContext, + ) + return self._asyncioTestLoop.run_until_complete(task) def _callMaybeAsync(self, func, /, *args, **kwargs): assert self._asyncioTestLoop is not None, 'asyncio test loop is not initialized' - ret = func(*args, **kwargs) - if inspect.isawaitable(ret): - fut = self._asyncioTestLoop.create_future() - self._asyncioCallsQueue.put_nowait((fut, ret)) - return self._asyncioTestLoop.run_until_complete(fut) + if inspect.iscoroutinefunction(func): + task = self._asyncioTestLoop.create_task( + func(*args, **kwargs), + context=self._asyncioTestContext, + ) + return self._asyncioTestLoop.run_until_complete(task) else: - return ret - - async def _asyncioLoopRunner(self, fut): - self._asyncioCallsQueue = queue = asyncio.Queue() - fut.set_result(None) - while True: - query = await queue.get() - queue.task_done() - if query is None: - return - fut, awaitable = query - try: - ret = await awaitable - if not fut.cancelled(): - fut.set_result(ret) - except (SystemExit, KeyboardInterrupt): - raise - except (BaseException, asyncio.CancelledError) as ex: - if not fut.cancelled(): - fut.set_exception(ex) + return self._asyncioTestContext.run(func, *args, **kwargs) def _setupAsyncioLoop(self): assert self._asyncioTestLoop is None, 'asyncio test loop already initialized' @@ -116,16 +100,11 @@ class IsolatedAsyncioTestCase(TestCase): asyncio.set_event_loop(loop) loop.set_debug(True) self._asyncioTestLoop = loop - fut = loop.create_future() - self._asyncioCallsTask = loop.create_task(self._asyncioLoopRunner(fut)) - loop.run_until_complete(fut) def _tearDownAsyncioLoop(self): assert self._asyncioTestLoop is not None, 'asyncio test loop is not initialized' loop = self._asyncioTestLoop self._asyncioTestLoop = None - self._asyncioCallsQueue.put_nowait(None) - loop.run_until_complete(self._asyncioCallsQueue.join()) try: # cancel all tasks diff --git a/Lib/unittest/test/test_async_case.py b/Lib/unittest/test/test_async_case.py index 3717486..7dc8a6b 100644 --- a/Lib/unittest/test/test_async_case.py +++ b/Lib/unittest/test/test_async_case.py @@ -1,4 +1,5 @@ import asyncio +import contextvars import unittest from test import support @@ -11,6 +12,9 @@ def tearDownModule(): asyncio.set_event_loop_policy(None) +VAR = contextvars.ContextVar('VAR', default=()) + + class TestAsyncCase(unittest.TestCase): maxDiff = None @@ -24,22 +28,26 @@ class TestAsyncCase(unittest.TestCase): def setUp(self): self.assertEqual(events, []) events.append('setUp') + VAR.set(VAR.get() + ('setUp',)) async def asyncSetUp(self): self.assertEqual(events, ['setUp']) events.append('asyncSetUp') + VAR.set(VAR.get() + ('asyncSetUp',)) self.addAsyncCleanup(self.on_cleanup1) async def test_func(self): self.assertEqual(events, ['setUp', 'asyncSetUp']) events.append('test') + VAR.set(VAR.get() + ('test',)) self.addAsyncCleanup(self.on_cleanup2) async def asyncTearDown(self): self.assertEqual(events, ['setUp', 'asyncSetUp', 'test']) + VAR.set(VAR.get() + ('asyncTearDown',)) events.append('asyncTearDown') def tearDown(self): @@ -48,6 +56,7 @@ class TestAsyncCase(unittest.TestCase): 'test', 'asyncTearDown']) events.append('tearDown') + VAR.set(VAR.get() + ('tearDown',)) async def on_cleanup1(self): self.assertEqual(events, ['setUp', @@ -57,6 +66,9 @@ class TestAsyncCase(unittest.TestCase): 'tearDown', 'cleanup2']) events.append('cleanup1') + VAR.set(VAR.get() + ('cleanup1',)) + nonlocal cvar + cvar = VAR.get() async def on_cleanup2(self): self.assertEqual(events, ['setUp', @@ -65,8 +77,10 @@ class TestAsyncCase(unittest.TestCase): 'asyncTearDown', 'tearDown']) events.append('cleanup2') + VAR.set(VAR.get() + ('cleanup2',)) events = [] + cvar = () test = Test("test_func") result = test.run() self.assertEqual(result.errors, []) @@ -74,13 +88,17 @@ class TestAsyncCase(unittest.TestCase): expected = ['setUp', 'asyncSetUp', 'test', 'asyncTearDown', 'tearDown', 'cleanup2', 'cleanup1'] self.assertEqual(events, expected) + self.assertEqual(cvar, tuple(expected)) events = [] + cvar = () test = Test("test_func") test.debug() self.assertEqual(events, expected) + self.assertEqual(cvar, tuple(expected)) test.doCleanups() self.assertEqual(events, expected) + self.assertEqual(cvar, tuple(expected)) def test_exception_in_setup(self): class Test(unittest.IsolatedAsyncioTestCase): |