def test_cancel_task_ignoring(self): fut1 = futures.Future(loop=self.loop) fut2 = futures.Future(loop=self.loop) fut3 = futures.Future(loop=self.loop) @tasks.coroutine def task(): yield from fut1 try: yield from fut2 except futures.CancelledError: pass res = yield from fut3 return res t = tasks.Task(task(), loop=self.loop) test_utils.run_briefly(self.loop) self.assertIs(t._fut_waiter, fut1) # White-box test. fut1.set_result(None) test_utils.run_briefly(self.loop) self.assertIs(t._fut_waiter, fut2) # White-box test. t.cancel() self.assertTrue(fut2.cancelled()) test_utils.run_briefly(self.loop) self.assertIs(t._fut_waiter, fut3) # White-box test. fut3.set_result(42) res = self.loop.run_until_complete(t) self.assertEqual(res, 42) self.assertFalse(fut3.cancelled()) self.assertFalse(t.cancelled())
def test_constructor_heterogenous_futures(self): fut1 = futures.Future(loop=self.one_loop) fut2 = futures.Future(loop=self.other_loop) with self.assertRaises(ValueError): tasks.gather(fut1, fut2) with self.assertRaises(ValueError): tasks.gather(fut1, loop=self.other_loop)
def test_cancel_done_future(self): fut1 = futures.Future(loop=self.loop) fut2 = futures.Future(loop=self.loop) fut3 = futures.Future(loop=self.loop) @tasks.coroutine def task(): yield from fut1 try: yield from fut2 except futures.CancelledError: pass yield from fut3 t = tasks.Task(task(), loop=self.loop) test_utils.run_briefly(self.loop) fut1.set_result(None) t.cancel() test_utils.run_once(self.loop) # process fut1 result, delay cancel self.assertFalse(t.done()) test_utils.run_once(self.loop) # cancel fut2, but coro still alive self.assertFalse(t.done()) test_utils.run_briefly(self.loop) # cancel fut3 self.assertTrue(t.done()) self.assertEqual(fut1.result(), None) self.assertTrue(fut2.cancelled()) self.assertTrue(fut3.cancelled()) self.assertTrue(t.cancelled())
def test_shield_gather(self): child1 = futures.Future(loop=self.loop) child2 = futures.Future(loop=self.loop) parent = tasks.gather(child1, child2, loop=self.loop) outer = tasks.shield(parent, loop=self.loop) test_utils.run_briefly(self.loop) outer.cancel() test_utils.run_briefly(self.loop) self.assertTrue(outer.cancelled()) child1.set_result(1) child2.set_result(2) test_utils.run_briefly(self.loop) self.assertEqual(parent.result(), [1, 2])
def test_ctor_with_waiter(self): fut = futures.Future(loop=self.loop) tr = unix_events._UnixWritePipeTransport(self.loop, self.pipe, self.protocol, fut) self.loop.assert_reader(5, tr._read_ready) test_utils.run_briefly(self.loop) self.assertEqual(None, fut.result())
def test_gather_shield(self): child1 = futures.Future(loop=self.loop) child2 = futures.Future(loop=self.loop) inner1 = tasks.shield(child1, loop=self.loop) inner2 = tasks.shield(child2, loop=self.loop) parent = tasks.gather(inner1, inner2, loop=self.loop) test_utils.run_briefly(self.loop) parent.cancel() # This should cancel inner1 and inner2 but bot child1 and child2. test_utils.run_briefly(self.loop) self.assertIsInstance(parent.exception(), futures.CancelledError) self.assertTrue(inner1.cancelled()) self.assertTrue(inner2.cancelled()) child1.set_result(1) child2.set_result(2) test_utils.run_briefly(self.loop)
def test_shield_effect(self): # Cancelling outer() does not affect inner(). proof = 0 waiter = futures.Future(loop=self.loop) @tasks.coroutine def inner(): nonlocal proof yield from waiter proof += 1 @tasks.coroutine def outer(): nonlocal proof yield from tasks.shield(inner(), loop=self.loop) proof += 100 f = tasks. async (outer(), loop=self.loop) test_utils.run_briefly(self.loop) f.cancel() with self.assertRaises(futures.CancelledError): self.loop.run_until_complete(f) waiter.set_result(None) test_utils.run_briefly(self.loop) self.assertEqual(proof, 1)
def test_shield_cancel(self): inner = futures.Future(loop=self.loop) outer = tasks.shield(inner) test_utils.run_briefly(self.loop) inner.cancel() test_utils.run_briefly(self.loop) self.assertTrue(outer.cancelled())
def test_yield_wait_does_not_shield_cancel(self): # Cancelling outer() makes wait() return early, leaves inner() # running. proof = 0 waiter = futures.Future(loop=self.loop) @tasks.coroutine def inner(): nonlocal proof yield from waiter proof += 1 @tasks.coroutine def outer(): nonlocal proof d, p = yield from tasks.wait([inner()], loop=self.loop) proof += 100 f = tasks. async (outer(), loop=self.loop) test_utils.run_briefly(self.loop) f.cancel() self.assertRaises(futures.CancelledError, self.loop.run_until_complete, f) waiter.set_result(None) test_utils.run_briefly(self.loop) self.assertEqual(proof, 1)
def test_yield_future_passes_cancel(self): # Cancelling outer() cancels inner() cancels waiter. proof = 0 waiter = futures.Future(loop=self.loop) @tasks.coroutine def inner(): nonlocal proof try: yield from waiter except futures.CancelledError: proof += 1 raise else: self.fail('got past sleep() in inner()') @tasks.coroutine def outer(): nonlocal proof try: yield from inner() except futures.CancelledError: proof += 100 # Expect this path. else: proof += 10 f = tasks. async (outer(), loop=self.loop) test_utils.run_briefly(self.loop) f.cancel() self.loop.run_until_complete(f) self.assertEqual(proof, 101) self.assertTrue(waiter.cancelled())
def test_cancellation_broadcast(self): # Cancelling outer() cancels all children. proof = 0 waiter = futures.Future(loop=self.one_loop) @tasks.coroutine def inner(): nonlocal proof yield from waiter proof += 1 child1 = tasks. async (inner(), loop=self.one_loop) child2 = tasks. async (inner(), loop=self.one_loop) gatherer = None @tasks.coroutine def outer(): nonlocal proof, gatherer gatherer = tasks.gather(child1, child2, loop=self.one_loop) yield from gatherer proof += 100 f = tasks. async (outer(), loop=self.one_loop) test_utils.run_briefly(self.one_loop) self.assertTrue(f.cancel()) with self.assertRaises(futures.CancelledError): self.one_loop.run_until_complete(f) self.assertFalse(gatherer.cancel()) self.assertTrue(waiter.cancelled()) self.assertTrue(child1.cancelled()) self.assertTrue(child2.cancelled()) test_utils.run_briefly(self.one_loop) self.assertEqual(proof, 0)
def test_shield_exception(self): inner = futures.Future(loop=self.loop) outer = tasks.shield(inner) test_utils.run_briefly(self.loop) exc = RuntimeError('expected') inner.set_exception(exc) test_utils.run_briefly(self.loop) self.assertIs(outer.exception(), exc)
def test_ctor_with_waiter(self): fut = futures.Future(loop=self.loop) tr = unix_events._UnixWritePipeTransport(self.loop, self.pipe, self.protocol, fut) self.loop.call_soon.assert_called_with(fut.set_result, None) self.loop.add_reader.assert_called_with(5, tr._read_ready) self.assertTrue(tr._enable_read_hack) fut.cancel()
def wrap_future(self, future): """XXX""" if isinstance(future, futures.Future): return future # Don't wrap our own type of Future. new_future = futures.Future() future.add_done_callback(lambda future: self.call_soon_threadsafe( new_future._copy_state, future)) return new_future
def test_constructor_homogenous_futures(self): children = [futures.Future(loop=self.other_loop) for i in range(3)] fut = tasks.gather(*children) self.assertIs(fut._loop, self.other_loop) self._run_loop(self.other_loop) self.assertFalse(fut.done()) fut = tasks.gather(*children, loop=self.other_loop) self.assertIs(fut._loop, self.other_loop) self._run_loop(self.other_loop) self.assertFalse(fut.done())
def test_get_with_putters(self): q = queues.Queue(1, loop=self.loop) q.put_nowait(1) waiter = futures.Future(loop=self.loop) q._putters.append((2, waiter)) res = self.loop.run_until_complete(q.get()) self.assertEqual(1, res) self.assertTrue(waiter.done()) self.assertIsNone(waiter.result())
def test_yield_vs_yield_from(self): fut = futures.Future(loop=self.loop) @tasks.coroutine def wait_for_future(): yield fut task = wait_for_future() with self.assertRaises(RuntimeError): self.loop.run_until_complete(task) self.assertFalse(fut.done())
def test_yield_vs_yield_from(self): fut = futures.Future(loop=self.loop) @tasks.coroutine def wait_for_future(): yield fut task = wait_for_future() with self.assertRaises(RuntimeError) as cm: self.loop.run_until_complete(task) self.assertTrue(fut.done()) self.assertIs(fut.exception(), cm.exception)
def test_exception_marking(self): # Test for the first line marked "Mark exception retrieved." @tasks.coroutine def inner(f): yield from f raise RuntimeError('should not be ignored') a = futures.Future(loop=self.one_loop) b = futures.Future(loop=self.one_loop) @tasks.coroutine def outer(): yield from tasks.gather(inner(a), inner(b), loop=self.one_loop) f = tasks. async (outer(), loop=self.one_loop) test_utils.run_briefly(self.one_loop) a.set_result(None) test_utils.run_briefly(self.one_loop) b.set_result(None) test_utils.run_briefly(self.one_loop) self.assertIsInstance(f.exception(), RuntimeError)
def _check_success(self, **kwargs): a, b, c = [futures.Future(loop=self.one_loop) for i in range(3)] fut = tasks.gather(*self.wrap_futures(a, b, c), **kwargs) cb = Mock() fut.add_done_callback(cb) b.set_result(1) a.set_result(2) self._run_loop(self.one_loop) self.assertEqual(cb.called, False) self.assertFalse(fut.done()) c.set_result(3) self._run_loop(self.one_loop) cb.assert_called_once_with(fut) self.assertEqual(fut.result(), [2, 1, 3])
def test_async_future(self): f_orig = futures.Future(loop=self.loop) f_orig.set_result('ko') f = tasks. async (f_orig) self.loop.run_until_complete(f) self.assertTrue(f.done()) self.assertEqual(f.result(), 'ko') self.assertIs(f, f_orig) with self.assertRaises(ValueError): loop = events.new_event_loop() f = tasks. async (f_orig, loop=loop) f = tasks. async (f_orig, loop=self.loop) self.assertIs(f, f_orig)
def test_cancel_inner_future(self): f = futures.Future(loop=self.loop) @tasks.coroutine def task(): yield from f return 12 t = tasks.Task(task(), loop=self.loop) test_utils.run_briefly(self.loop) # start task f.cancel() with self.assertRaises(futures.CancelledError): self.loop.run_until_complete(t) self.assertTrue(f.cancelled()) self.assertTrue(t.cancelled())
def run_in_executor(self, executor, callback, *args): if isinstance(callback, events.Handler): assert not args assert not isinstance(callback, events.Timer) if callback.cancelled: f = futures.Future() f.set_result(None) return f callback, args = callback.callback, callback.args if executor is None: executor = self._default_executor if executor is None: executor = concurrent.futures.ThreadPoolExecutor(_MAX_WORKERS) self._default_executor = executor return self.wrap_future(executor.submit(callback, *args))
def test_coroutine_non_gen_function_return_future(self): fut = futures.Future(loop=self.loop) @tasks.coroutine def func(): return fut @tasks.coroutine def coro(): fut.set_result('test') t1 = tasks.Task(func(), loop=self.loop) t2 = tasks.Task(coro(), loop=self.loop) res = self.loop.run_until_complete(t1) self.assertEqual(res, 'test') self.assertIsNone(t2.result())
def test_one_cancellation(self): a, b, c, d, e = [futures.Future(loop=self.one_loop) for i in range(5)] fut = tasks.gather(a, b, c, d, e) cb = Mock() fut.add_done_callback(cb) a.set_result(1) b.cancel() self._run_loop(self.one_loop) self.assertTrue(fut.done()) cb.assert_called_once_with(fut) self.assertFalse(fut.cancelled()) self.assertIsInstance(fut.exception(), futures.CancelledError) # Does nothing c.set_result(3) d.cancel() e.set_exception(RuntimeError())
def test_one_exception(self): a, b, c, d, e = [futures.Future(loop=self.one_loop) for i in range(5)] fut = tasks.gather(*self.wrap_futures(a, b, c, d, e)) cb = Mock() fut.add_done_callback(cb) exc = ZeroDivisionError() a.set_result(1) b.set_exception(exc) self._run_loop(self.one_loop) self.assertTrue(fut.done()) cb.assert_called_once_with(fut) self.assertIs(fut.exception(), exc) # Does nothing c.set_result(3) d.cancel() e.set_exception(RuntimeError())
def test_cancel_race(self): # Several tasks: # - A acquires the lock # - B is blocked in aqcuire() # - C is blocked in aqcuire() # # Now, concurrently: # - B is cancelled # - A releases the lock # # If B's waiter is marked cancelled but not yet removed from # _waiters, A's release() call will crash when trying to set # B's waiter; instead, it should move on to C's waiter. # Setup: A has the lock, b and c are waiting. lock = locks.Lock(loop=self.loop) @tasks.coroutine def lockit(name, blocker): yield from lock.acquire() try: if blocker is not None: yield from blocker finally: lock.release() fa = futures.Future(loop=self.loop) ta = tasks.Task(lockit('A', fa), loop=self.loop) test_utils.run_briefly(self.loop) self.assertTrue(lock.locked()) tb = tasks.Task(lockit('B', None), loop=self.loop) test_utils.run_briefly(self.loop) self.assertEqual(len(lock._waiters), 1) tc = tasks.Task(lockit('C', None), loop=self.loop) test_utils.run_briefly(self.loop) self.assertEqual(len(lock._waiters), 2) # Create the race and check. # Without the fix this failed at the last assert. fa.set_result(None) tb.cancel() self.assertTrue(lock._waiters[0].cancelled()) test_utils.run_briefly(self.loop) self.assertFalse(lock.locked()) self.assertTrue(ta.done()) self.assertTrue(tb.cancelled()) self.assertTrue(tc.done())
def test_task_cancel_waiter_future(self): fut = futures.Future(loop=self.loop) @tasks.coroutine def coro(): yield from fut task = tasks.Task(coro(), loop=self.loop) test_utils.run_briefly(self.loop) self.assertIs(task._fut_waiter, fut) task.cancel() test_utils.run_briefly(self.loop) self.assertRaises(futures.CancelledError, self.loop.run_until_complete, task) self.assertIsNone(task._fut_waiter) self.assertTrue(fut.cancelled())
def test_return_exceptions(self): a, b, c, d = [futures.Future(loop=self.one_loop) for i in range(4)] fut = tasks.gather(*self.wrap_futures(a, b, c, d), return_exceptions=True) cb = Mock() fut.add_done_callback(cb) exc = ZeroDivisionError() exc2 = RuntimeError() b.set_result(1) c.set_exception(exc) a.set_result(3) self._run_loop(self.one_loop) self.assertFalse(fut.done()) d.set_exception(exc2) self._run_loop(self.one_loop) self.assertTrue(fut.done()) cb.assert_called_once_with(fut) self.assertEqual(fut.result(), [3, 1, exc, exc2])
def test_run_once_in_executor_plain(self): def cb(): pass h = events.Handle(cb, ()) f = futures.Future(loop=self.loop) executor = unittest.mock.Mock() executor.submit.return_value = f self.loop.set_default_executor(executor) res = self.loop.run_in_executor(None, h) self.assertIs(f, res) executor = unittest.mock.Mock() executor.submit.return_value = f res = self.loop.run_in_executor(executor, h) self.assertIs(f, res) self.assertTrue(executor.submit.called) f.cancel() # Don't complain about abandoned Future.