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