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