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 from test.test_asyncio import utils as test_utils
14 from test import support
15
16
17 def tearDownModule():
18 asyncio.set_event_loop_policy(None)
19
20
21 def _fakefunc(f):
22 return f
23
24
25 def first_cb():
26 pass
27
28
29 def last_cb():
30 pass
31
32
33 class ESC[4;38;5;81mDuckFuture:
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
84 class ESC[4;38;5;81mDuckTests(ESC[4;38;5;149mtest_utilsESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
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
102 class ESC[4;38;5;81mBaseFutureTests:
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 ESC[4;38;5;81mMyFuture:
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 ESC[4;38;5;81mEvil:
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')
626 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):
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')
642 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):
643 try:
644 class ESC[4;38;5;81mCSubFuture(ESC[4;38;5;149mfuturesESC[4;38;5;149m.ESC[4;38;5;149m_CFuture):
645 pass
646
647 cls = CSubFuture
648 except AttributeError:
649 cls = None
650
651
652 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):
653 cls = futures._PyFuture
654
655
656 class ESC[4;38;5;81mBaseFutureDoneCallbackTests():
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 ESC[4;38;5;81mevil:
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 ESC[4;38;5;81mevil:
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 ESC[4;38;5;81mevil:
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')
874 class ESC[4;38;5;81mCFutureDoneCallbackTests(ESC[4;38;5;149mBaseFutureDoneCallbackTests,
875 ESC[4;38;5;149mtest_utilsESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
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')
883 class ESC[4;38;5;81mCSubFutureDoneCallbackTests(ESC[4;38;5;149mBaseFutureDoneCallbackTests,
884 ESC[4;38;5;149mtest_utilsESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
885
886 def _new_future(self):
887 class ESC[4;38;5;81mCSubFuture(ESC[4;38;5;149mfuturesESC[4;38;5;149m.ESC[4;38;5;149m_CFuture):
888 pass
889 return CSubFuture(loop=self.loop)
890
891
892 class ESC[4;38;5;81mPyFutureDoneCallbackTests(ESC[4;38;5;149mBaseFutureDoneCallbackTests,
893 ESC[4;38;5;149mtest_utilsESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
894
895 def _new_future(self):
896 return futures._PyFuture(loop=self.loop)
897
898
899 class ESC[4;38;5;81mBaseFutureInheritanceTests:
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 ESC[4;38;5;81mMyFut(ESC[4;38;5;149mcls):
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
926 class ESC[4;38;5;81mPyFutureInheritanceTests(ESC[4;38;5;149mBaseFutureInheritanceTests,
927 ESC[4;38;5;149mtest_utilsESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
928 def _get_future_cls(self):
929 return futures._PyFuture
930
931
932 @unittest.skipUnless(hasattr(futures, '_CFuture'),
933 'requires the C _asyncio module')
934 class ESC[4;38;5;81mCFutureInheritanceTests(ESC[4;38;5;149mBaseFutureInheritanceTests,
935 ESC[4;38;5;149mtest_utilsESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
936 def _get_future_cls(self):
937 return futures._CFuture
938
939
940 if __name__ == '__main__':
941 unittest.main()