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