1"""Tests for futures.py.""" 2 3import concurrent.futures 4import gc 5import re 6import sys 7import threading 8import unittest 9from unittest import mock 10from types import GenericAlias 11import asyncio 12from asyncio import futures 13from test.test_asyncio import utils as test_utils 14from test import support 15 16 17def tearDownModule(): 18 asyncio.set_event_loop_policy(None) 19 20 21def _fakefunc(f): 22 return f 23 24 25def first_cb(): 26 pass 27 28 29def last_cb(): 30 pass 31 32 33class DuckFuture: 34 # Class that does not inherit from Future but aims to be duck-type 35 # compatible with it. 36 37 _asyncio_future_blocking = False 38 __cancelled = False 39 __result = None 40 __exception = None 41 42 def cancel(self): 43 if self.done(): 44 return False 45 self.__cancelled = True 46 return True 47 48 def cancelled(self): 49 return self.__cancelled 50 51 def done(self): 52 return (self.__cancelled 53 or self.__result is not None 54 or self.__exception is not None) 55 56 def result(self): 57 self.assertFalse(self.cancelled()) 58 if self.__exception is not None: 59 raise self.__exception 60 return self.__result 61 62 def exception(self): 63 self.assertFalse(self.cancelled()) 64 return self.__exception 65 66 def set_result(self, result): 67 self.assertFalse(self.done()) 68 self.assertIsNotNone(result) 69 self.__result = result 70 71 def set_exception(self, exception): 72 self.assertFalse(self.done()) 73 self.assertIsNotNone(exception) 74 self.__exception = exception 75 76 def __iter__(self): 77 if not self.done(): 78 self._asyncio_future_blocking = True 79 yield self 80 self.assertTrue(self.done()) 81 return self.result() 82 83 84class DuckTests(test_utils.TestCase): 85 86 def setUp(self): 87 super().setUp() 88 self.loop = self.new_test_loop() 89 self.addCleanup(self.loop.close) 90 91 def test_wrap_future(self): 92 f = DuckFuture() 93 g = asyncio.wrap_future(f) 94 self.assertIs(g, f) 95 96 def test_ensure_future(self): 97 f = DuckFuture() 98 g = asyncio.ensure_future(f) 99 self.assertIs(g, f) 100 101 102class BaseFutureTests: 103 104 def _new_future(self, *args, **kwargs): 105 return self.cls(*args, **kwargs) 106 107 def setUp(self): 108 super().setUp() 109 self.loop = self.new_test_loop() 110 self.addCleanup(self.loop.close) 111 112 def test_generic_alias(self): 113 future = self.cls[str] 114 self.assertEqual(future.__args__, (str,)) 115 self.assertIsInstance(future, GenericAlias) 116 117 def test_isfuture(self): 118 class MyFuture: 119 _asyncio_future_blocking = None 120 121 def __init__(self): 122 self._asyncio_future_blocking = False 123 124 self.assertFalse(asyncio.isfuture(MyFuture)) 125 self.assertTrue(asyncio.isfuture(MyFuture())) 126 self.assertFalse(asyncio.isfuture(1)) 127 128 # As `isinstance(Mock(), Future)` returns `False` 129 self.assertFalse(asyncio.isfuture(mock.Mock())) 130 131 f = self._new_future(loop=self.loop) 132 self.assertTrue(asyncio.isfuture(f)) 133 self.assertFalse(asyncio.isfuture(type(f))) 134 135 # As `isinstance(Mock(Future), Future)` returns `True` 136 self.assertTrue(asyncio.isfuture(mock.Mock(type(f)))) 137 138 f.cancel() 139 140 def test_initial_state(self): 141 f = self._new_future(loop=self.loop) 142 self.assertFalse(f.cancelled()) 143 self.assertFalse(f.done()) 144 f.cancel() 145 self.assertTrue(f.cancelled()) 146 147 def test_constructor_without_loop(self): 148 with self.assertWarns(DeprecationWarning) as cm: 149 with self.assertRaisesRegex(RuntimeError, 'There is no current event loop'): 150 self._new_future() 151 self.assertEqual(cm.warnings[0].filename, __file__) 152 153 def test_constructor_use_running_loop(self): 154 async def test(): 155 return self._new_future() 156 f = self.loop.run_until_complete(test()) 157 self.assertIs(f._loop, self.loop) 158 self.assertIs(f.get_loop(), self.loop) 159 160 def test_constructor_use_global_loop(self): 161 # Deprecated in 3.10 162 asyncio.set_event_loop(self.loop) 163 self.addCleanup(asyncio.set_event_loop, None) 164 with self.assertWarns(DeprecationWarning) as cm: 165 f = self._new_future() 166 self.assertEqual(cm.warnings[0].filename, __file__) 167 self.assertIs(f._loop, self.loop) 168 self.assertIs(f.get_loop(), self.loop) 169 170 def test_constructor_positional(self): 171 # Make sure Future doesn't accept a positional argument 172 self.assertRaises(TypeError, self._new_future, 42) 173 174 def test_uninitialized(self): 175 # Test that C Future doesn't crash when Future.__init__() 176 # call was skipped. 177 178 fut = self.cls.__new__(self.cls, loop=self.loop) 179 self.assertRaises(asyncio.InvalidStateError, fut.result) 180 181 fut = self.cls.__new__(self.cls, loop=self.loop) 182 self.assertRaises(asyncio.InvalidStateError, fut.exception) 183 184 fut = self.cls.__new__(self.cls, loop=self.loop) 185 with self.assertRaises((RuntimeError, AttributeError)): 186 fut.set_result(None) 187 188 fut = self.cls.__new__(self.cls, loop=self.loop) 189 with self.assertRaises((RuntimeError, AttributeError)): 190 fut.set_exception(Exception) 191 192 fut = self.cls.__new__(self.cls, loop=self.loop) 193 with self.assertRaises((RuntimeError, AttributeError)): 194 fut.cancel() 195 196 fut = self.cls.__new__(self.cls, loop=self.loop) 197 with self.assertRaises((RuntimeError, AttributeError)): 198 fut.add_done_callback(lambda f: None) 199 200 fut = self.cls.__new__(self.cls, loop=self.loop) 201 with self.assertRaises((RuntimeError, AttributeError)): 202 fut.remove_done_callback(lambda f: None) 203 204 fut = self.cls.__new__(self.cls, loop=self.loop) 205 try: 206 repr(fut) 207 except (RuntimeError, AttributeError): 208 pass 209 210 fut = self.cls.__new__(self.cls, loop=self.loop) 211 try: 212 fut.__await__() 213 except RuntimeError: 214 pass 215 216 fut = self.cls.__new__(self.cls, loop=self.loop) 217 try: 218 iter(fut) 219 except RuntimeError: 220 pass 221 222 fut = self.cls.__new__(self.cls, loop=self.loop) 223 self.assertFalse(fut.cancelled()) 224 self.assertFalse(fut.done()) 225 226 def test_future_cancel_message_getter(self): 227 f = self._new_future(loop=self.loop) 228 self.assertTrue(hasattr(f, '_cancel_message')) 229 self.assertEqual(f._cancel_message, None) 230 231 f.cancel('my message') 232 with self.assertRaises(asyncio.CancelledError): 233 self.loop.run_until_complete(f) 234 self.assertEqual(f._cancel_message, 'my message') 235 236 def test_future_cancel_message_setter(self): 237 f = self._new_future(loop=self.loop) 238 f.cancel('my message') 239 f._cancel_message = 'my new message' 240 self.assertEqual(f._cancel_message, 'my new message') 241 242 # Also check that the value is used for cancel(). 243 with self.assertRaises(asyncio.CancelledError): 244 self.loop.run_until_complete(f) 245 self.assertEqual(f._cancel_message, 'my new message') 246 247 def test_cancel(self): 248 f = self._new_future(loop=self.loop) 249 self.assertTrue(f.cancel()) 250 self.assertTrue(f.cancelled()) 251 self.assertTrue(f.done()) 252 self.assertRaises(asyncio.CancelledError, f.result) 253 self.assertRaises(asyncio.CancelledError, f.exception) 254 self.assertRaises(asyncio.InvalidStateError, f.set_result, None) 255 self.assertRaises(asyncio.InvalidStateError, f.set_exception, None) 256 self.assertFalse(f.cancel()) 257 258 def test_result(self): 259 f = self._new_future(loop=self.loop) 260 self.assertRaises(asyncio.InvalidStateError, f.result) 261 262 f.set_result(42) 263 self.assertFalse(f.cancelled()) 264 self.assertTrue(f.done()) 265 self.assertEqual(f.result(), 42) 266 self.assertEqual(f.exception(), None) 267 self.assertRaises(asyncio.InvalidStateError, f.set_result, None) 268 self.assertRaises(asyncio.InvalidStateError, f.set_exception, None) 269 self.assertFalse(f.cancel()) 270 271 def test_exception(self): 272 exc = RuntimeError() 273 f = self._new_future(loop=self.loop) 274 self.assertRaises(asyncio.InvalidStateError, f.exception) 275 276 # StopIteration cannot be raised into a Future - CPython issue26221 277 self.assertRaisesRegex(TypeError, "StopIteration .* cannot be raised", 278 f.set_exception, StopIteration) 279 280 f.set_exception(exc) 281 self.assertFalse(f.cancelled()) 282 self.assertTrue(f.done()) 283 self.assertRaises(RuntimeError, f.result) 284 self.assertEqual(f.exception(), exc) 285 self.assertRaises(asyncio.InvalidStateError, f.set_result, None) 286 self.assertRaises(asyncio.InvalidStateError, f.set_exception, None) 287 self.assertFalse(f.cancel()) 288 289 def test_exception_class(self): 290 f = self._new_future(loop=self.loop) 291 f.set_exception(RuntimeError) 292 self.assertIsInstance(f.exception(), RuntimeError) 293 294 def test_yield_from_twice(self): 295 f = self._new_future(loop=self.loop) 296 297 def fixture(): 298 yield 'A' 299 x = yield from f 300 yield 'B', x 301 y = yield from f 302 yield 'C', y 303 304 g = fixture() 305 self.assertEqual(next(g), 'A') # yield 'A'. 306 self.assertEqual(next(g), f) # First yield from f. 307 f.set_result(42) 308 self.assertEqual(next(g), ('B', 42)) # yield 'B', x. 309 # The second "yield from f" does not yield f. 310 self.assertEqual(next(g), ('C', 42)) # yield 'C', y. 311 312 def test_future_repr(self): 313 self.loop.set_debug(True) 314 f_pending_debug = self._new_future(loop=self.loop) 315 frame = f_pending_debug._source_traceback[-1] 316 self.assertEqual( 317 repr(f_pending_debug), 318 f'<{self.cls.__name__} pending created at {frame[0]}:{frame[1]}>') 319 f_pending_debug.cancel() 320 321 self.loop.set_debug(False) 322 f_pending = self._new_future(loop=self.loop) 323 self.assertEqual(repr(f_pending), f'<{self.cls.__name__} pending>') 324 f_pending.cancel() 325 326 f_cancelled = self._new_future(loop=self.loop) 327 f_cancelled.cancel() 328 self.assertEqual(repr(f_cancelled), f'<{self.cls.__name__} cancelled>') 329 330 f_result = self._new_future(loop=self.loop) 331 f_result.set_result(4) 332 self.assertEqual( 333 repr(f_result), f'<{self.cls.__name__} finished result=4>') 334 self.assertEqual(f_result.result(), 4) 335 336 exc = RuntimeError() 337 f_exception = self._new_future(loop=self.loop) 338 f_exception.set_exception(exc) 339 self.assertEqual( 340 repr(f_exception), 341 f'<{self.cls.__name__} finished exception=RuntimeError()>') 342 self.assertIs(f_exception.exception(), exc) 343 344 def func_repr(func): 345 filename, lineno = test_utils.get_function_source(func) 346 text = '%s() at %s:%s' % (func.__qualname__, filename, lineno) 347 return re.escape(text) 348 349 f_one_callbacks = self._new_future(loop=self.loop) 350 f_one_callbacks.add_done_callback(_fakefunc) 351 fake_repr = func_repr(_fakefunc) 352 self.assertRegex( 353 repr(f_one_callbacks), 354 r'<' + self.cls.__name__ + r' pending cb=\[%s\]>' % fake_repr) 355 f_one_callbacks.cancel() 356 self.assertEqual(repr(f_one_callbacks), 357 f'<{self.cls.__name__} cancelled>') 358 359 f_two_callbacks = self._new_future(loop=self.loop) 360 f_two_callbacks.add_done_callback(first_cb) 361 f_two_callbacks.add_done_callback(last_cb) 362 first_repr = func_repr(first_cb) 363 last_repr = func_repr(last_cb) 364 self.assertRegex(repr(f_two_callbacks), 365 r'<' + self.cls.__name__ + r' pending cb=\[%s, %s\]>' 366 % (first_repr, last_repr)) 367 368 f_many_callbacks = self._new_future(loop=self.loop) 369 f_many_callbacks.add_done_callback(first_cb) 370 for i in range(8): 371 f_many_callbacks.add_done_callback(_fakefunc) 372 f_many_callbacks.add_done_callback(last_cb) 373 cb_regex = r'%s, <8 more>, %s' % (first_repr, last_repr) 374 self.assertRegex( 375 repr(f_many_callbacks), 376 r'<' + self.cls.__name__ + r' pending cb=\[%s\]>' % cb_regex) 377 f_many_callbacks.cancel() 378 self.assertEqual(repr(f_many_callbacks), 379 f'<{self.cls.__name__} cancelled>') 380 381 def test_copy_state(self): 382 from asyncio.futures import _copy_future_state 383 384 f = self._new_future(loop=self.loop) 385 f.set_result(10) 386 387 newf = self._new_future(loop=self.loop) 388 _copy_future_state(f, newf) 389 self.assertTrue(newf.done()) 390 self.assertEqual(newf.result(), 10) 391 392 f_exception = self._new_future(loop=self.loop) 393 f_exception.set_exception(RuntimeError()) 394 395 newf_exception = self._new_future(loop=self.loop) 396 _copy_future_state(f_exception, newf_exception) 397 self.assertTrue(newf_exception.done()) 398 self.assertRaises(RuntimeError, newf_exception.result) 399 400 f_cancelled = self._new_future(loop=self.loop) 401 f_cancelled.cancel() 402 403 newf_cancelled = self._new_future(loop=self.loop) 404 _copy_future_state(f_cancelled, newf_cancelled) 405 self.assertTrue(newf_cancelled.cancelled()) 406 407 def test_iter(self): 408 fut = self._new_future(loop=self.loop) 409 410 def coro(): 411 yield from fut 412 413 def test(): 414 arg1, arg2 = coro() 415 416 with self.assertRaisesRegex(RuntimeError, "await wasn't used"): 417 test() 418 fut.cancel() 419 420 def test_log_traceback(self): 421 fut = self._new_future(loop=self.loop) 422 with self.assertRaisesRegex(ValueError, 'can only be set to False'): 423 fut._log_traceback = True 424 425 @mock.patch('asyncio.base_events.logger') 426 def test_tb_logger_abandoned(self, m_log): 427 fut = self._new_future(loop=self.loop) 428 del fut 429 self.assertFalse(m_log.error.called) 430 431 @mock.patch('asyncio.base_events.logger') 432 def test_tb_logger_not_called_after_cancel(self, m_log): 433 fut = self._new_future(loop=self.loop) 434 fut.set_exception(Exception()) 435 fut.cancel() 436 del fut 437 self.assertFalse(m_log.error.called) 438 439 @mock.patch('asyncio.base_events.logger') 440 def test_tb_logger_result_unretrieved(self, m_log): 441 fut = self._new_future(loop=self.loop) 442 fut.set_result(42) 443 del fut 444 self.assertFalse(m_log.error.called) 445 446 @mock.patch('asyncio.base_events.logger') 447 def test_tb_logger_result_retrieved(self, m_log): 448 fut = self._new_future(loop=self.loop) 449 fut.set_result(42) 450 fut.result() 451 del fut 452 self.assertFalse(m_log.error.called) 453 454 @mock.patch('asyncio.base_events.logger') 455 def test_tb_logger_exception_unretrieved(self, m_log): 456 fut = self._new_future(loop=self.loop) 457 fut.set_exception(RuntimeError('boom')) 458 del fut 459 test_utils.run_briefly(self.loop) 460 support.gc_collect() 461 self.assertTrue(m_log.error.called) 462 463 @mock.patch('asyncio.base_events.logger') 464 def test_tb_logger_exception_retrieved(self, m_log): 465 fut = self._new_future(loop=self.loop) 466 fut.set_exception(RuntimeError('boom')) 467 fut.exception() 468 del fut 469 self.assertFalse(m_log.error.called) 470 471 @mock.patch('asyncio.base_events.logger') 472 def test_tb_logger_exception_result_retrieved(self, m_log): 473 fut = self._new_future(loop=self.loop) 474 fut.set_exception(RuntimeError('boom')) 475 self.assertRaises(RuntimeError, fut.result) 476 del fut 477 self.assertFalse(m_log.error.called) 478 479 def test_wrap_future(self): 480 481 def run(arg): 482 return (arg, threading.get_ident()) 483 ex = concurrent.futures.ThreadPoolExecutor(1) 484 f1 = ex.submit(run, 'oi') 485 f2 = asyncio.wrap_future(f1, loop=self.loop) 486 res, ident = self.loop.run_until_complete(f2) 487 self.assertTrue(asyncio.isfuture(f2)) 488 self.assertEqual(res, 'oi') 489 self.assertNotEqual(ident, threading.get_ident()) 490 ex.shutdown(wait=True) 491 492 def test_wrap_future_future(self): 493 f1 = self._new_future(loop=self.loop) 494 f2 = asyncio.wrap_future(f1) 495 self.assertIs(f1, f2) 496 497 def test_wrap_future_without_loop(self): 498 def run(arg): 499 return (arg, threading.get_ident()) 500 ex = concurrent.futures.ThreadPoolExecutor(1) 501 f1 = ex.submit(run, 'oi') 502 with self.assertWarns(DeprecationWarning) as cm: 503 with self.assertRaises(RuntimeError): 504 asyncio.wrap_future(f1) 505 self.assertEqual(cm.warnings[0].filename, __file__) 506 ex.shutdown(wait=True) 507 508 def test_wrap_future_use_running_loop(self): 509 def run(arg): 510 return (arg, threading.get_ident()) 511 ex = concurrent.futures.ThreadPoolExecutor(1) 512 f1 = ex.submit(run, 'oi') 513 async def test(): 514 return asyncio.wrap_future(f1) 515 f2 = self.loop.run_until_complete(test()) 516 self.assertIs(self.loop, f2._loop) 517 ex.shutdown(wait=True) 518 519 def test_wrap_future_use_global_loop(self): 520 # Deprecated in 3.10 521 asyncio.set_event_loop(self.loop) 522 self.addCleanup(asyncio.set_event_loop, None) 523 def run(arg): 524 return (arg, threading.get_ident()) 525 ex = concurrent.futures.ThreadPoolExecutor(1) 526 f1 = ex.submit(run, 'oi') 527 with self.assertWarns(DeprecationWarning) as cm: 528 f2 = asyncio.wrap_future(f1) 529 self.assertEqual(cm.warnings[0].filename, __file__) 530 self.assertIs(self.loop, f2._loop) 531 ex.shutdown(wait=True) 532 533 def test_wrap_future_cancel(self): 534 f1 = concurrent.futures.Future() 535 f2 = asyncio.wrap_future(f1, loop=self.loop) 536 f2.cancel() 537 test_utils.run_briefly(self.loop) 538 self.assertTrue(f1.cancelled()) 539 self.assertTrue(f2.cancelled()) 540 541 def test_wrap_future_cancel2(self): 542 f1 = concurrent.futures.Future() 543 f2 = asyncio.wrap_future(f1, loop=self.loop) 544 f1.set_result(42) 545 f2.cancel() 546 test_utils.run_briefly(self.loop) 547 self.assertFalse(f1.cancelled()) 548 self.assertEqual(f1.result(), 42) 549 self.assertTrue(f2.cancelled()) 550 551 def test_future_source_traceback(self): 552 self.loop.set_debug(True) 553 554 future = self._new_future(loop=self.loop) 555 lineno = sys._getframe().f_lineno - 1 556 self.assertIsInstance(future._source_traceback, list) 557 self.assertEqual(future._source_traceback[-2][:3], 558 (__file__, 559 lineno, 560 'test_future_source_traceback')) 561 562 @mock.patch('asyncio.base_events.logger') 563 def check_future_exception_never_retrieved(self, debug, m_log): 564 self.loop.set_debug(debug) 565 566 def memory_error(): 567 try: 568 raise MemoryError() 569 except BaseException as exc: 570 return exc 571 exc = memory_error() 572 573 future = self._new_future(loop=self.loop) 574 future.set_exception(exc) 575 future = None 576 test_utils.run_briefly(self.loop) 577 support.gc_collect() 578 579 if sys.version_info >= (3, 4): 580 regex = f'^{self.cls.__name__} exception was never retrieved\n' 581 exc_info = (type(exc), exc, exc.__traceback__) 582 m_log.error.assert_called_once_with(mock.ANY, exc_info=exc_info) 583 else: 584 regex = r'^Future/Task exception was never retrieved\n' 585 m_log.error.assert_called_once_with(mock.ANY, exc_info=False) 586 message = m_log.error.call_args[0][0] 587 self.assertRegex(message, re.compile(regex, re.DOTALL)) 588 589 def test_future_exception_never_retrieved(self): 590 self.check_future_exception_never_retrieved(False) 591 592 def test_future_exception_never_retrieved_debug(self): 593 self.check_future_exception_never_retrieved(True) 594 595 def test_set_result_unless_cancelled(self): 596 fut = self._new_future(loop=self.loop) 597 fut.cancel() 598 futures._set_result_unless_cancelled(fut, 2) 599 self.assertTrue(fut.cancelled()) 600 601 def test_future_stop_iteration_args(self): 602 fut = self._new_future(loop=self.loop) 603 fut.set_result((1, 2)) 604 fi = fut.__iter__() 605 result = None 606 try: 607 fi.send(None) 608 except StopIteration as ex: 609 result = ex.args[0] 610 else: 611 self.fail('StopIteration was expected') 612 self.assertEqual(result, (1, 2)) 613 614 def test_future_iter_throw(self): 615 fut = self._new_future(loop=self.loop) 616 fi = iter(fut) 617 self.assertRaises(TypeError, fi.throw, 618 Exception, Exception("elephant"), 32) 619 self.assertRaises(TypeError, fi.throw, 620 Exception("elephant"), Exception("elephant")) 621 self.assertRaises(TypeError, fi.throw, list) 622 623 def test_future_del_collect(self): 624 class Evil: 625 def __del__(self): 626 gc.collect() 627 628 for i in range(100): 629 fut = self._new_future(loop=self.loop) 630 fut.set_result(Evil()) 631 632 633@unittest.skipUnless(hasattr(futures, '_CFuture'), 634 'requires the C _asyncio module') 635class CFutureTests(BaseFutureTests, test_utils.TestCase): 636 try: 637 cls = futures._CFuture 638 except AttributeError: 639 cls = None 640 641 def test_future_del_segfault(self): 642 fut = self._new_future(loop=self.loop) 643 with self.assertRaises(AttributeError): 644 del fut._asyncio_future_blocking 645 with self.assertRaises(AttributeError): 646 del fut._log_traceback 647 648 649@unittest.skipUnless(hasattr(futures, '_CFuture'), 650 'requires the C _asyncio module') 651class CSubFutureTests(BaseFutureTests, test_utils.TestCase): 652 try: 653 class CSubFuture(futures._CFuture): 654 pass 655 656 cls = CSubFuture 657 except AttributeError: 658 cls = None 659 660 661class PyFutureTests(BaseFutureTests, test_utils.TestCase): 662 cls = futures._PyFuture 663 664 665class BaseFutureDoneCallbackTests(): 666 667 def setUp(self): 668 super().setUp() 669 self.loop = self.new_test_loop() 670 671 def run_briefly(self): 672 test_utils.run_briefly(self.loop) 673 674 def _make_callback(self, bag, thing): 675 # Create a callback function that appends thing to bag. 676 def bag_appender(future): 677 bag.append(thing) 678 return bag_appender 679 680 def _new_future(self): 681 raise NotImplementedError 682 683 def test_callbacks_remove_first_callback(self): 684 bag = [] 685 f = self._new_future() 686 687 cb1 = self._make_callback(bag, 42) 688 cb2 = self._make_callback(bag, 17) 689 cb3 = self._make_callback(bag, 100) 690 691 f.add_done_callback(cb1) 692 f.add_done_callback(cb2) 693 f.add_done_callback(cb3) 694 695 f.remove_done_callback(cb1) 696 f.remove_done_callback(cb1) 697 698 self.assertEqual(bag, []) 699 f.set_result('foo') 700 701 self.run_briefly() 702 703 self.assertEqual(bag, [17, 100]) 704 self.assertEqual(f.result(), 'foo') 705 706 def test_callbacks_remove_first_and_second_callback(self): 707 bag = [] 708 f = self._new_future() 709 710 cb1 = self._make_callback(bag, 42) 711 cb2 = self._make_callback(bag, 17) 712 cb3 = self._make_callback(bag, 100) 713 714 f.add_done_callback(cb1) 715 f.add_done_callback(cb2) 716 f.add_done_callback(cb3) 717 718 f.remove_done_callback(cb1) 719 f.remove_done_callback(cb2) 720 f.remove_done_callback(cb1) 721 722 self.assertEqual(bag, []) 723 f.set_result('foo') 724 725 self.run_briefly() 726 727 self.assertEqual(bag, [100]) 728 self.assertEqual(f.result(), 'foo') 729 730 def test_callbacks_remove_third_callback(self): 731 bag = [] 732 f = self._new_future() 733 734 cb1 = self._make_callback(bag, 42) 735 cb2 = self._make_callback(bag, 17) 736 cb3 = self._make_callback(bag, 100) 737 738 f.add_done_callback(cb1) 739 f.add_done_callback(cb2) 740 f.add_done_callback(cb3) 741 742 f.remove_done_callback(cb3) 743 f.remove_done_callback(cb3) 744 745 self.assertEqual(bag, []) 746 f.set_result('foo') 747 748 self.run_briefly() 749 750 self.assertEqual(bag, [42, 17]) 751 self.assertEqual(f.result(), 'foo') 752 753 def test_callbacks_invoked_on_set_result(self): 754 bag = [] 755 f = self._new_future() 756 f.add_done_callback(self._make_callback(bag, 42)) 757 f.add_done_callback(self._make_callback(bag, 17)) 758 759 self.assertEqual(bag, []) 760 f.set_result('foo') 761 762 self.run_briefly() 763 764 self.assertEqual(bag, [42, 17]) 765 self.assertEqual(f.result(), 'foo') 766 767 def test_callbacks_invoked_on_set_exception(self): 768 bag = [] 769 f = self._new_future() 770 f.add_done_callback(self._make_callback(bag, 100)) 771 772 self.assertEqual(bag, []) 773 exc = RuntimeError() 774 f.set_exception(exc) 775 776 self.run_briefly() 777 778 self.assertEqual(bag, [100]) 779 self.assertEqual(f.exception(), exc) 780 781 def test_remove_done_callback(self): 782 bag = [] 783 f = self._new_future() 784 cb1 = self._make_callback(bag, 1) 785 cb2 = self._make_callback(bag, 2) 786 cb3 = self._make_callback(bag, 3) 787 788 # Add one cb1 and one cb2. 789 f.add_done_callback(cb1) 790 f.add_done_callback(cb2) 791 792 # One instance of cb2 removed. Now there's only one cb1. 793 self.assertEqual(f.remove_done_callback(cb2), 1) 794 795 # Never had any cb3 in there. 796 self.assertEqual(f.remove_done_callback(cb3), 0) 797 798 # After this there will be 6 instances of cb1 and one of cb2. 799 f.add_done_callback(cb2) 800 for i in range(5): 801 f.add_done_callback(cb1) 802 803 # Remove all instances of cb1. One cb2 remains. 804 self.assertEqual(f.remove_done_callback(cb1), 6) 805 806 self.assertEqual(bag, []) 807 f.set_result('foo') 808 809 self.run_briefly() 810 811 self.assertEqual(bag, [2]) 812 self.assertEqual(f.result(), 'foo') 813 814 def test_remove_done_callbacks_list_mutation(self): 815 # see http://bugs.python.org/issue28963 for details 816 817 fut = self._new_future() 818 fut.add_done_callback(str) 819 820 for _ in range(63): 821 fut.add_done_callback(id) 822 823 class evil: 824 def __eq__(self, other): 825 fut.remove_done_callback(id) 826 return False 827 828 fut.remove_done_callback(evil()) 829 830 def test_schedule_callbacks_list_mutation_1(self): 831 # see http://bugs.python.org/issue28963 for details 832 833 def mut(f): 834 f.remove_done_callback(str) 835 836 fut = self._new_future() 837 fut.add_done_callback(mut) 838 fut.add_done_callback(str) 839 fut.add_done_callback(str) 840 fut.set_result(1) 841 test_utils.run_briefly(self.loop) 842 843 def test_schedule_callbacks_list_mutation_2(self): 844 # see http://bugs.python.org/issue30828 for details 845 846 fut = self._new_future() 847 fut.add_done_callback(str) 848 849 for _ in range(63): 850 fut.add_done_callback(id) 851 852 max_extra_cbs = 100 853 extra_cbs = 0 854 855 class evil: 856 def __eq__(self, other): 857 nonlocal extra_cbs 858 extra_cbs += 1 859 if extra_cbs < max_extra_cbs: 860 fut.add_done_callback(id) 861 return False 862 863 fut.remove_done_callback(evil()) 864 865 866@unittest.skipUnless(hasattr(futures, '_CFuture'), 867 'requires the C _asyncio module') 868class CFutureDoneCallbackTests(BaseFutureDoneCallbackTests, 869 test_utils.TestCase): 870 871 def _new_future(self): 872 return futures._CFuture(loop=self.loop) 873 874 875@unittest.skipUnless(hasattr(futures, '_CFuture'), 876 'requires the C _asyncio module') 877class CSubFutureDoneCallbackTests(BaseFutureDoneCallbackTests, 878 test_utils.TestCase): 879 880 def _new_future(self): 881 class CSubFuture(futures._CFuture): 882 pass 883 return CSubFuture(loop=self.loop) 884 885 886class PyFutureDoneCallbackTests(BaseFutureDoneCallbackTests, 887 test_utils.TestCase): 888 889 def _new_future(self): 890 return futures._PyFuture(loop=self.loop) 891 892 893class BaseFutureInheritanceTests: 894 895 def _get_future_cls(self): 896 raise NotImplementedError 897 898 def setUp(self): 899 super().setUp() 900 self.loop = self.new_test_loop() 901 self.addCleanup(self.loop.close) 902 903 def test_inherit_without_calling_super_init(self): 904 # See https://bugs.python.org/issue38785 for the context 905 cls = self._get_future_cls() 906 907 class MyFut(cls): 908 def __init__(self, *args, **kwargs): 909 # don't call super().__init__() 910 pass 911 912 fut = MyFut(loop=self.loop) 913 with self.assertRaisesRegex( 914 RuntimeError, 915 "Future object is not initialized." 916 ): 917 fut.get_loop() 918 919 920class PyFutureInheritanceTests(BaseFutureInheritanceTests, 921 test_utils.TestCase): 922 def _get_future_cls(self): 923 return futures._PyFuture 924 925 926@unittest.skipUnless(hasattr(futures, '_CFuture'), 927 'requires the C _asyncio module') 928class CFutureInheritanceTests(BaseFutureInheritanceTests, 929 test_utils.TestCase): 930 def _get_future_cls(self): 931 return futures._CFuture 932 933 934if __name__ == '__main__': 935 unittest.main() 936