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