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