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