mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	asyncio: Increase asyncio.Future test coverage; test both implementations.
Also, add 'isfuture' to 'asyncio.futures.__all__', so that it's exposed as 'asyncio.isfuture'.
This commit is contained in:
		
							parent
							
								
									8985031476
								
							
						
					
					
						commit
						01c521ba7a
					
				
					 2 changed files with 82 additions and 83 deletions
				
			
		|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| __all__ = ['CancelledError', 'TimeoutError', | ||||
|            'InvalidStateError', | ||||
|            'Future', 'wrap_future', | ||||
|            'Future', 'wrap_future', 'isfuture' | ||||
|            ] | ||||
| 
 | ||||
| import concurrent.futures._base | ||||
|  | @ -389,6 +389,10 @@ def __iter__(self): | |||
|         __await__ = __iter__ # make compatible with 'await' expression | ||||
| 
 | ||||
| 
 | ||||
| # Needed for testing purposes. | ||||
| _PyFuture = Future | ||||
| 
 | ||||
| 
 | ||||
| def _set_result_unless_cancelled(fut, result): | ||||
|     """Helper setting the result only if the future was not cancelled.""" | ||||
|     if fut.cancelled(): | ||||
|  | @ -488,4 +492,5 @@ def wrap_future(future, *, loop=None): | |||
| except ImportError: | ||||
|     pass | ||||
| else: | ||||
|     Future = _asyncio.Future | ||||
|     # _CFuture is needed for tests. | ||||
|     Future = _CFuture = _asyncio.Future | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| 
 | ||||
| import asyncio | ||||
| from asyncio import test_utils | ||||
| from asyncio import futures | ||||
| try: | ||||
|     from test import support | ||||
| except ImportError: | ||||
|  | @ -93,14 +94,17 @@ def test_ensure_future(self): | |||
|         assert g is f | ||||
| 
 | ||||
| 
 | ||||
| class FutureTests(test_utils.TestCase): | ||||
| class BaseFutureTests: | ||||
| 
 | ||||
|     def _new_future(self, loop=None): | ||||
|         raise NotImplementedError | ||||
| 
 | ||||
|     def setUp(self): | ||||
|         self.loop = self.new_test_loop() | ||||
|         self.addCleanup(self.loop.close) | ||||
| 
 | ||||
|     def test_initial_state(self): | ||||
|         f = asyncio.Future(loop=self.loop) | ||||
|         f = self._new_future(loop=self.loop) | ||||
|         self.assertFalse(f.cancelled()) | ||||
|         self.assertFalse(f.done()) | ||||
|         f.cancel() | ||||
|  | @ -108,15 +112,15 @@ def test_initial_state(self): | |||
| 
 | ||||
|     def test_init_constructor_default_loop(self): | ||||
|         asyncio.set_event_loop(self.loop) | ||||
|         f = asyncio.Future() | ||||
|         f = self._new_future() | ||||
|         self.assertIs(f._loop, self.loop) | ||||
| 
 | ||||
|     def test_constructor_positional(self): | ||||
|         # Make sure Future doesn't accept a positional argument | ||||
|         self.assertRaises(TypeError, asyncio.Future, 42) | ||||
|         self.assertRaises(TypeError, self._new_future, 42) | ||||
| 
 | ||||
|     def test_cancel(self): | ||||
|         f = asyncio.Future(loop=self.loop) | ||||
|         f = self._new_future(loop=self.loop) | ||||
|         self.assertTrue(f.cancel()) | ||||
|         self.assertTrue(f.cancelled()) | ||||
|         self.assertTrue(f.done()) | ||||
|  | @ -127,7 +131,7 @@ def test_cancel(self): | |||
|         self.assertFalse(f.cancel()) | ||||
| 
 | ||||
|     def test_result(self): | ||||
|         f = asyncio.Future(loop=self.loop) | ||||
|         f = self._new_future(loop=self.loop) | ||||
|         self.assertRaises(asyncio.InvalidStateError, f.result) | ||||
| 
 | ||||
|         f.set_result(42) | ||||
|  | @ -141,7 +145,7 @@ def test_result(self): | |||
| 
 | ||||
|     def test_exception(self): | ||||
|         exc = RuntimeError() | ||||
|         f = asyncio.Future(loop=self.loop) | ||||
|         f = self._new_future(loop=self.loop) | ||||
|         self.assertRaises(asyncio.InvalidStateError, f.exception) | ||||
| 
 | ||||
|         # StopIteration cannot be raised into a Future - CPython issue26221 | ||||
|  | @ -158,12 +162,12 @@ def test_exception(self): | |||
|         self.assertFalse(f.cancel()) | ||||
| 
 | ||||
|     def test_exception_class(self): | ||||
|         f = asyncio.Future(loop=self.loop) | ||||
|         f = self._new_future(loop=self.loop) | ||||
|         f.set_exception(RuntimeError) | ||||
|         self.assertIsInstance(f.exception(), RuntimeError) | ||||
| 
 | ||||
|     def test_yield_from_twice(self): | ||||
|         f = asyncio.Future(loop=self.loop) | ||||
|         f = self._new_future(loop=self.loop) | ||||
| 
 | ||||
|         def fixture(): | ||||
|             yield 'A' | ||||
|  | @ -182,7 +186,7 @@ def fixture(): | |||
| 
 | ||||
|     def test_future_repr(self): | ||||
|         self.loop.set_debug(True) | ||||
|         f_pending_debug = asyncio.Future(loop=self.loop) | ||||
|         f_pending_debug = self._new_future(loop=self.loop) | ||||
|         frame = f_pending_debug._source_traceback[-1] | ||||
|         self.assertEqual(repr(f_pending_debug), | ||||
|                          '<Future pending created at %s:%s>' | ||||
|  | @ -190,21 +194,21 @@ def test_future_repr(self): | |||
|         f_pending_debug.cancel() | ||||
| 
 | ||||
|         self.loop.set_debug(False) | ||||
|         f_pending = asyncio.Future(loop=self.loop) | ||||
|         f_pending = self._new_future(loop=self.loop) | ||||
|         self.assertEqual(repr(f_pending), '<Future pending>') | ||||
|         f_pending.cancel() | ||||
| 
 | ||||
|         f_cancelled = asyncio.Future(loop=self.loop) | ||||
|         f_cancelled = self._new_future(loop=self.loop) | ||||
|         f_cancelled.cancel() | ||||
|         self.assertEqual(repr(f_cancelled), '<Future cancelled>') | ||||
| 
 | ||||
|         f_result = asyncio.Future(loop=self.loop) | ||||
|         f_result = self._new_future(loop=self.loop) | ||||
|         f_result.set_result(4) | ||||
|         self.assertEqual(repr(f_result), '<Future finished result=4>') | ||||
|         self.assertEqual(f_result.result(), 4) | ||||
| 
 | ||||
|         exc = RuntimeError() | ||||
|         f_exception = asyncio.Future(loop=self.loop) | ||||
|         f_exception = self._new_future(loop=self.loop) | ||||
|         f_exception.set_exception(exc) | ||||
|         self.assertEqual(repr(f_exception), | ||||
|                          '<Future finished exception=RuntimeError()>') | ||||
|  | @ -215,7 +219,7 @@ def func_repr(func): | |||
|             text = '%s() at %s:%s' % (func.__qualname__, filename, lineno) | ||||
|             return re.escape(text) | ||||
| 
 | ||||
|         f_one_callbacks = asyncio.Future(loop=self.loop) | ||||
|         f_one_callbacks = self._new_future(loop=self.loop) | ||||
|         f_one_callbacks.add_done_callback(_fakefunc) | ||||
|         fake_repr = func_repr(_fakefunc) | ||||
|         self.assertRegex(repr(f_one_callbacks), | ||||
|  | @ -224,7 +228,7 @@ def func_repr(func): | |||
|         self.assertEqual(repr(f_one_callbacks), | ||||
|                          '<Future cancelled>') | ||||
| 
 | ||||
|         f_two_callbacks = asyncio.Future(loop=self.loop) | ||||
|         f_two_callbacks = self._new_future(loop=self.loop) | ||||
|         f_two_callbacks.add_done_callback(first_cb) | ||||
|         f_two_callbacks.add_done_callback(last_cb) | ||||
|         first_repr = func_repr(first_cb) | ||||
|  | @ -233,7 +237,7 @@ def func_repr(func): | |||
|                          r'<Future pending cb=\[%s, %s\]>' | ||||
|                          % (first_repr, last_repr)) | ||||
| 
 | ||||
|         f_many_callbacks = asyncio.Future(loop=self.loop) | ||||
|         f_many_callbacks = self._new_future(loop=self.loop) | ||||
|         f_many_callbacks.add_done_callback(first_cb) | ||||
|         for i in range(8): | ||||
|             f_many_callbacks.add_done_callback(_fakefunc) | ||||
|  | @ -248,31 +252,31 @@ def func_repr(func): | |||
|     def test_copy_state(self): | ||||
|         from asyncio.futures import _copy_future_state | ||||
| 
 | ||||
|         f = asyncio.Future(loop=self.loop) | ||||
|         f = self._new_future(loop=self.loop) | ||||
|         f.set_result(10) | ||||
| 
 | ||||
|         newf = asyncio.Future(loop=self.loop) | ||||
|         newf = self._new_future(loop=self.loop) | ||||
|         _copy_future_state(f, newf) | ||||
|         self.assertTrue(newf.done()) | ||||
|         self.assertEqual(newf.result(), 10) | ||||
| 
 | ||||
|         f_exception = asyncio.Future(loop=self.loop) | ||||
|         f_exception = self._new_future(loop=self.loop) | ||||
|         f_exception.set_exception(RuntimeError()) | ||||
| 
 | ||||
|         newf_exception = asyncio.Future(loop=self.loop) | ||||
|         newf_exception = self._new_future(loop=self.loop) | ||||
|         _copy_future_state(f_exception, newf_exception) | ||||
|         self.assertTrue(newf_exception.done()) | ||||
|         self.assertRaises(RuntimeError, newf_exception.result) | ||||
| 
 | ||||
|         f_cancelled = asyncio.Future(loop=self.loop) | ||||
|         f_cancelled = self._new_future(loop=self.loop) | ||||
|         f_cancelled.cancel() | ||||
| 
 | ||||
|         newf_cancelled = asyncio.Future(loop=self.loop) | ||||
|         newf_cancelled = self._new_future(loop=self.loop) | ||||
|         _copy_future_state(f_cancelled, newf_cancelled) | ||||
|         self.assertTrue(newf_cancelled.cancelled()) | ||||
| 
 | ||||
|     def test_iter(self): | ||||
|         fut = asyncio.Future(loop=self.loop) | ||||
|         fut = self._new_future(loop=self.loop) | ||||
| 
 | ||||
|         def coro(): | ||||
|             yield from fut | ||||
|  | @ -285,20 +289,20 @@ def test(): | |||
| 
 | ||||
|     @mock.patch('asyncio.base_events.logger') | ||||
|     def test_tb_logger_abandoned(self, m_log): | ||||
|         fut = asyncio.Future(loop=self.loop) | ||||
|         fut = self._new_future(loop=self.loop) | ||||
|         del fut | ||||
|         self.assertFalse(m_log.error.called) | ||||
| 
 | ||||
|     @mock.patch('asyncio.base_events.logger') | ||||
|     def test_tb_logger_result_unretrieved(self, m_log): | ||||
|         fut = asyncio.Future(loop=self.loop) | ||||
|         fut = self._new_future(loop=self.loop) | ||||
|         fut.set_result(42) | ||||
|         del fut | ||||
|         self.assertFalse(m_log.error.called) | ||||
| 
 | ||||
|     @mock.patch('asyncio.base_events.logger') | ||||
|     def test_tb_logger_result_retrieved(self, m_log): | ||||
|         fut = asyncio.Future(loop=self.loop) | ||||
|         fut = self._new_future(loop=self.loop) | ||||
|         fut.set_result(42) | ||||
|         fut.result() | ||||
|         del fut | ||||
|  | @ -306,7 +310,7 @@ def test_tb_logger_result_retrieved(self, m_log): | |||
| 
 | ||||
|     @mock.patch('asyncio.base_events.logger') | ||||
|     def test_tb_logger_exception_unretrieved(self, m_log): | ||||
|         fut = asyncio.Future(loop=self.loop) | ||||
|         fut = self._new_future(loop=self.loop) | ||||
|         fut.set_exception(RuntimeError('boom')) | ||||
|         del fut | ||||
|         test_utils.run_briefly(self.loop) | ||||
|  | @ -315,7 +319,7 @@ def test_tb_logger_exception_unretrieved(self, m_log): | |||
| 
 | ||||
|     @mock.patch('asyncio.base_events.logger') | ||||
|     def test_tb_logger_exception_retrieved(self, m_log): | ||||
|         fut = asyncio.Future(loop=self.loop) | ||||
|         fut = self._new_future(loop=self.loop) | ||||
|         fut.set_exception(RuntimeError('boom')) | ||||
|         fut.exception() | ||||
|         del fut | ||||
|  | @ -323,7 +327,7 @@ def test_tb_logger_exception_retrieved(self, m_log): | |||
| 
 | ||||
|     @mock.patch('asyncio.base_events.logger') | ||||
|     def test_tb_logger_exception_result_retrieved(self, m_log): | ||||
|         fut = asyncio.Future(loop=self.loop) | ||||
|         fut = self._new_future(loop=self.loop) | ||||
|         fut.set_exception(RuntimeError('boom')) | ||||
|         self.assertRaises(RuntimeError, fut.result) | ||||
|         del fut | ||||
|  | @ -337,12 +341,12 @@ def run(arg): | |||
|         f1 = ex.submit(run, 'oi') | ||||
|         f2 = asyncio.wrap_future(f1, loop=self.loop) | ||||
|         res, ident = self.loop.run_until_complete(f2) | ||||
|         self.assertIsInstance(f2, asyncio.Future) | ||||
|         self.assertTrue(asyncio.isfuture(f2)) | ||||
|         self.assertEqual(res, 'oi') | ||||
|         self.assertNotEqual(ident, threading.get_ident()) | ||||
| 
 | ||||
|     def test_wrap_future_future(self): | ||||
|         f1 = asyncio.Future(loop=self.loop) | ||||
|         f1 = self._new_future(loop=self.loop) | ||||
|         f2 = asyncio.wrap_future(f1) | ||||
|         self.assertIs(f1, f2) | ||||
| 
 | ||||
|  | @ -377,10 +381,10 @@ def test_wrap_future_cancel2(self): | |||
|     def test_future_source_traceback(self): | ||||
|         self.loop.set_debug(True) | ||||
| 
 | ||||
|         future = asyncio.Future(loop=self.loop) | ||||
|         future = self._new_future(loop=self.loop) | ||||
|         lineno = sys._getframe().f_lineno - 1 | ||||
|         self.assertIsInstance(future._source_traceback, list) | ||||
|         self.assertEqual(future._source_traceback[-1][:3], | ||||
|         self.assertEqual(future._source_traceback[-2][:3], | ||||
|                          (__file__, | ||||
|                           lineno, | ||||
|                           'test_future_source_traceback')) | ||||
|  | @ -396,57 +400,18 @@ def memory_error(): | |||
|                 return exc | ||||
|         exc = memory_error() | ||||
| 
 | ||||
|         future = asyncio.Future(loop=self.loop) | ||||
|         if debug: | ||||
|             source_traceback = future._source_traceback | ||||
|         future = self._new_future(loop=self.loop) | ||||
|         future.set_exception(exc) | ||||
|         future = None | ||||
|         test_utils.run_briefly(self.loop) | ||||
|         support.gc_collect() | ||||
| 
 | ||||
|         if sys.version_info >= (3, 4): | ||||
|             if debug: | ||||
|                 frame = source_traceback[-1] | ||||
|                 regex = (r'^Future exception was never retrieved\n' | ||||
|                          r'future: <Future finished exception=MemoryError\(\) ' | ||||
|                              r'created at {filename}:{lineno}>\n' | ||||
|                          r'source_traceback: Object ' | ||||
|                             r'created at \(most recent call last\):\n' | ||||
|                          r'  File' | ||||
|                          r'.*\n' | ||||
|                          r'  File "{filename}", line {lineno}, ' | ||||
|                             r'in check_future_exception_never_retrieved\n' | ||||
|                          r'    future = asyncio\.Future\(loop=self\.loop\)$' | ||||
|                          ).format(filename=re.escape(frame[0]), | ||||
|                                   lineno=frame[1]) | ||||
|             else: | ||||
|                 regex = (r'^Future exception was never retrieved\n' | ||||
|                          r'future: ' | ||||
|                             r'<Future finished exception=MemoryError\(\)>$' | ||||
|                          ) | ||||
|             regex = r'^Future exception was never retrieved\n' | ||||
|             exc_info = (type(exc), exc, exc.__traceback__) | ||||
|             m_log.error.assert_called_once_with(mock.ANY, exc_info=exc_info) | ||||
|         else: | ||||
|             if debug: | ||||
|                 frame = source_traceback[-1] | ||||
|                 regex = (r'^Future/Task exception was never retrieved\n' | ||||
|                          r'Future/Task created at \(most recent call last\):\n' | ||||
|                          r'  File' | ||||
|                          r'.*\n' | ||||
|                          r'  File "{filename}", line {lineno}, ' | ||||
|                             r'in check_future_exception_never_retrieved\n' | ||||
|                          r'    future = asyncio\.Future\(loop=self\.loop\)\n' | ||||
|                          r'Traceback \(most recent call last\):\n' | ||||
|                          r'.*\n' | ||||
|                          r'MemoryError$' | ||||
|                          ).format(filename=re.escape(frame[0]), | ||||
|                                   lineno=frame[1]) | ||||
|             else: | ||||
|                 regex = (r'^Future/Task exception was never retrieved\n' | ||||
|                          r'Traceback \(most recent call last\):\n' | ||||
|                          r'.*\n' | ||||
|                          r'MemoryError$' | ||||
|                          ) | ||||
|             regex = r'^Future/Task exception was never retrieved\n' | ||||
|             m_log.error.assert_called_once_with(mock.ANY, exc_info=False) | ||||
|         message = m_log.error.call_args[0][0] | ||||
|         self.assertRegex(message, re.compile(regex, re.DOTALL)) | ||||
|  | @ -458,14 +423,13 @@ def test_future_exception_never_retrieved_debug(self): | |||
|         self.check_future_exception_never_retrieved(True) | ||||
| 
 | ||||
|     def test_set_result_unless_cancelled(self): | ||||
|         from asyncio import futures | ||||
|         fut = asyncio.Future(loop=self.loop) | ||||
|         fut = self._new_future(loop=self.loop) | ||||
|         fut.cancel() | ||||
|         futures._set_result_unless_cancelled(fut, 2) | ||||
|         self.assertTrue(fut.cancelled()) | ||||
| 
 | ||||
|     def test_future_stop_iteration_args(self): | ||||
|         fut = asyncio.Future(loop=self.loop) | ||||
|         fut = self._new_future(loop=self.loop) | ||||
|         fut.set_result((1, 2)) | ||||
|         fi = fut.__iter__() | ||||
|         result = None | ||||
|  | @ -478,7 +442,21 @@ def test_future_stop_iteration_args(self): | |||
|         self.assertEqual(result, (1, 2)) | ||||
| 
 | ||||
| 
 | ||||
| class FutureDoneCallbackTests(test_utils.TestCase): | ||||
| @unittest.skipUnless(hasattr(futures, '_CFuture'), | ||||
|                      'requires the C _asyncio module') | ||||
| class CFutureTests(BaseFutureTests, test_utils.TestCase): | ||||
| 
 | ||||
|     def _new_future(self,  *args, **kwargs): | ||||
|         return futures._CFuture(*args, **kwargs) | ||||
| 
 | ||||
| 
 | ||||
| class PyFutureTests(BaseFutureTests, test_utils.TestCase): | ||||
| 
 | ||||
|     def _new_future(self, *args, **kwargs): | ||||
|         return futures._PyFuture(*args, **kwargs) | ||||
| 
 | ||||
| 
 | ||||
| class BaseFutureDoneCallbackTests(): | ||||
| 
 | ||||
|     def setUp(self): | ||||
|         self.loop = self.new_test_loop() | ||||
|  | @ -493,7 +471,7 @@ def bag_appender(future): | |||
|         return bag_appender | ||||
| 
 | ||||
|     def _new_future(self): | ||||
|         return asyncio.Future(loop=self.loop) | ||||
|         raise NotImplementedError | ||||
| 
 | ||||
|     def test_callbacks_invoked_on_set_result(self): | ||||
|         bag = [] | ||||
|  | @ -557,5 +535,21 @@ def test_remove_done_callback(self): | |||
|         self.assertEqual(f.result(), 'foo') | ||||
| 
 | ||||
| 
 | ||||
| @unittest.skipUnless(hasattr(futures, '_CFuture'), | ||||
|                      'requires the C _asyncio module') | ||||
| class CFutureDoneCallbackTests(BaseFutureDoneCallbackTests, | ||||
|                                test_utils.TestCase): | ||||
| 
 | ||||
|     def _new_future(self): | ||||
|         return futures._CFuture(loop=self.loop) | ||||
| 
 | ||||
| 
 | ||||
| class PyFutureDoneCallbackTests(BaseFutureDoneCallbackTests, | ||||
|                                 test_utils.TestCase): | ||||
| 
 | ||||
|     def _new_future(self): | ||||
|         return futures._PyFuture(loop=self.loop) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     unittest.main() | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Yury Selivanov
						Yury Selivanov