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