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