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