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