summaryrefslogtreecommitdiffstats
path: root/Lib/unittest
diff options
context:
space:
mode:
authorAndrew Svetlov <andrew.svetlov@gmail.com>2022-03-14 11:54:13 (GMT)
committerGitHub <noreply@github.com>2022-03-14 11:54:13 (GMT)
commit9523c0d84f351a610dc651b234461eb015fa3b82 (patch)
tree5f6f6bed4353eb9c149f65ab2dc95db12d378db3 /Lib/unittest
parent2153daf0a02a598ed5df93f2f224c1ab2a2cca0d (diff)
downloadcpython-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.py55
-rw-r--r--Lib/unittest/test/test_async_case.py18
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):