def test_wait(self): def gen(): when = yield self.assertAlmostEqual(0.1, when) when = yield 0 self.assertAlmostEqual(0.15, when) yield 0.15 loop = test_utils.TestLoop(gen) self.addCleanup(loop.close) a = tasks.Task(tasks.sleep(0.1, loop=loop), loop=loop) b = tasks.Task(tasks.sleep(0.15, loop=loop), loop=loop) @tasks.coroutine def foo(): done, pending = yield from tasks.wait([b, a], loop=loop) self.assertEqual(done, set([a, b])) self.assertEqual(pending, set()) return 42 res = loop.run_until_complete(tasks.Task(foo(), loop=loop)) self.assertEqual(res, 42) self.assertAlmostEqual(0.15, loop.time()) # Doing it again should take no time and exercise a different path. res = loop.run_until_complete(tasks.Task(foo(), loop=loop)) self.assertAlmostEqual(0.15, loop.time()) self.assertEqual(res, 42)
def test_wait_concurrent_complete(self): def gen(): when = yield self.assertAlmostEqual(0.1, when) when = yield 0 self.assertAlmostEqual(0.15, when) when = yield 0 self.assertAlmostEqual(0.1, when) yield 0.1 loop = test_utils.TestLoop(gen) self.addCleanup(loop.close) a = tasks.Task(tasks.sleep(0.1, loop=loop), loop=loop) b = tasks.Task(tasks.sleep(0.15, loop=loop), loop=loop) done, pending = loop.run_until_complete( tasks.wait([b, a], timeout=0.1, loop=loop)) self.assertEqual(done, set([a])) self.assertEqual(pending, set([b])) self.assertAlmostEqual(0.1, loop.time()) # move forward to close generator loop.advance_time(10) loop.run_until_complete(tasks.wait([a, b], loop=loop))
def test_wait_with_global_loop(self): def gen(): when = yield self.assertAlmostEqual(0.01, when) when = yield 0 self.assertAlmostEqual(0.015, when) yield 0.015 loop = test_utils.TestLoop(gen) self.addCleanup(loop.close) a = tasks.Task(tasks.sleep(0.01, loop=loop), loop=loop) b = tasks.Task(tasks.sleep(0.015, loop=loop), loop=loop) @tasks.coroutine def foo(): done, pending = yield from tasks.wait([b, a]) self.assertEqual(done, set([a, b])) self.assertEqual(pending, set()) return 42 events.set_event_loop(loop) try: res = loop.run_until_complete(tasks.Task(foo(), loop=loop)) finally: events.set_event_loop(None) self.assertEqual(res, 42)
def test_wait_first_completed(self): def gen(): when = yield self.assertAlmostEqual(10.0, when) when = yield 0 self.assertAlmostEqual(0.1, when) yield 0.1 loop = test_utils.TestLoop(gen) self.addCleanup(loop.close) a = tasks.Task(tasks.sleep(10.0, loop=loop), loop=loop) b = tasks.Task(tasks.sleep(0.1, loop=loop), loop=loop) task = tasks.Task(tasks.wait([b, a], return_when=tasks.FIRST_COMPLETED, loop=loop), loop=loop) done, pending = loop.run_until_complete(task) self.assertEqual({b}, done) self.assertEqual({a}, pending) self.assertFalse(a.done()) self.assertTrue(b.done()) self.assertIsNone(b.result()) self.assertAlmostEqual(0.1, loop.time()) # move forward to close generator loop.advance_time(10) loop.run_until_complete(tasks.wait([a, b], loop=loop))
def test_wait_with_global_loop(self): def gen(): when = yield self.assertAlmostEqual(0.01, when) when = yield 0 self.assertAlmostEqual(0.015, when) yield 0.015 loop = test_utils.TestLoop(gen) self.addCleanup(loop.close) a = tasks.Task(tasks.sleep(0.01, loop=loop), loop=loop) b = tasks.Task(tasks.sleep(0.015, loop=loop), loop=loop) @tasks.coroutine def foo(): done, pending = yield from tasks.wait([b, a]) self.assertEqual(done, set([a, b])) self.assertEqual(pending, set()) return 42 events.set_event_loop(loop) try: res = loop.run_until_complete( tasks.Task(foo(), loop=loop)) finally: events.set_event_loop(None) self.assertEqual(res, 42)
def test_wait_first_completed(self): def gen(): when = yield self.assertAlmostEqual(10.0, when) when = yield 0 self.assertAlmostEqual(0.1, when) yield 0.1 loop = test_utils.TestLoop(gen) self.addCleanup(loop.close) a = tasks.Task(tasks.sleep(10.0, loop=loop), loop=loop) b = tasks.Task(tasks.sleep(0.1, loop=loop), loop=loop) task = tasks.Task( tasks.wait([b, a], return_when=tasks.FIRST_COMPLETED, loop=loop), loop=loop) done, pending = loop.run_until_complete(task) self.assertEqual({b}, done) self.assertEqual({a}, pending) self.assertFalse(a.done()) self.assertTrue(b.done()) self.assertIsNone(b.result()) self.assertAlmostEqual(0.1, loop.time()) # move forward to close generator loop.advance_time(10) loop.run_until_complete(tasks.wait([a, b], loop=loop))
def test_wait_concurrent_complete(self): a = tasks.Task(tasks.sleep(0.1)) b = tasks.Task(tasks.sleep(0.15)) done, pending = self.loop.run_until_complete( tasks.wait([b, a], timeout=0.1)) self.assertEqual(done, set([a])) self.assertEqual(pending, set([b]))
def test_as_completed_concurrent(self): a = tasks.sleep(0.05, 'a') b = tasks.sleep(0.05, 'b') fs = {a, b} futs = list(tasks.as_completed(fs)) self.assertEqual(len(futs), 2) waiter = tasks.wait(futs) done, pending = self.loop.run_until_complete(waiter) self.assertEqual(set(f.result() for f in done), {'a', 'b'})
def test_as_completed_concurrent(self): a = tasks.sleep(0.05, 'a', loop=self.loop) b = tasks.sleep(0.05, 'b', loop=self.loop) fs = {a, b} futs = list(tasks.as_completed(fs, loop=self.loop)) self.assertEqual(len(futs), 2) waiter = tasks.wait(futs, loop=self.loop) done, pending = self.loop.run_until_complete(waiter) self.assertEqual(set(f.result() for f in done), {'a', 'b'})
def test_wait_concurrent_complete(self): a = tasks.Task(tasks.sleep(0.1, loop=self.loop), loop=self.loop) b = tasks.Task(tasks.sleep(0.15, loop=self.loop), loop=self.loop) done, pending = self.loop.run_until_complete( tasks.wait([b, a], timeout=0.1, loop=self.loop)) self.assertEqual(done, set([a])) self.assertEqual(pending, set([b]))
def test_as_completed_reverse_wait(self): a = tasks.sleep(0.05, 'a') b = tasks.sleep(0.10, 'b') fs = {a, b} futs = list(tasks.as_completed(fs)) self.assertEqual(len(futs), 2) x = self.loop.run_until_complete(futs[1]) self.assertEqual(x, 'a') y = self.loop.run_until_complete(futs[0]) self.assertEqual(y, 'b')
def test_as_completed_reverse_wait(self): a = tasks.sleep(0.05, 'a', loop=self.loop) b = tasks.sleep(0.10, 'b', loop=self.loop) fs = {a, b} futs = list(tasks.as_completed(fs, loop=self.loop)) self.assertEqual(len(futs), 2) x = self.loop.run_until_complete(futs[1]) self.assertEqual(x, 'a') y = self.loop.run_until_complete(futs[0]) self.assertEqual(y, 'b')
def test_wait_first_completed(self): a = tasks.Task(tasks.sleep(10.0)) b = tasks.Task(tasks.sleep(0.1)) task = tasks.Task(tasks.wait( [b, a], return_when=tasks.FIRST_COMPLETED)) done, pending = self.loop.run_until_complete(task) self.assertEqual({b}, done) self.assertEqual({a}, pending) self.assertFalse(a.done()) self.assertTrue(b.done()) self.assertIsNone(b.result())
def test_wait_first_completed(self): a = tasks.Task(tasks.sleep(10.0, loop=self.loop), loop=self.loop) b = tasks.Task(tasks.sleep(0.1, loop=self.loop), loop=self.loop) task = tasks.Task(tasks.wait([b, a], return_when=tasks.FIRST_COMPLETED, loop=self.loop), loop=self.loop) done, pending = self.loop.run_until_complete(task) self.assertEqual({b}, done) self.assertEqual({a}, pending) self.assertFalse(a.done()) self.assertTrue(b.done()) self.assertIsNone(b.result())
def test_wait_with_timeout(self): a = tasks.Task(tasks.sleep(0.1)) b = tasks.Task(tasks.sleep(0.15)) @tasks.coroutine def foo(): done, pending = yield from tasks.wait([b, a], timeout=0.11) self.assertEqual(done, set([a])) self.assertEqual(pending, set([b])) t0 = time.monotonic() self.loop.run_until_complete(tasks.Task(foo())) t1 = time.monotonic() self.assertTrue(t1-t0 >= 0.1) self.assertTrue(t1-t0 <= 0.13)
def test_wait_with_exception(self): a = tasks.Task(tasks.sleep(0.1)) @tasks.coroutine def sleeper(): yield from tasks.sleep(0.15) raise ZeroDivisionError('really') b = tasks.Task(sleeper()) @tasks.coroutine def foo(): done, pending = yield from tasks.wait([b, a]) self.assertEqual(len(done), 2) self.assertEqual(pending, set()) errors = set(f for f in done if f.exception() is not None) self.assertEqual(len(errors), 1) t0 = time.monotonic() self.loop.run_until_complete(tasks.Task(foo())) t1 = time.monotonic() self.assertTrue(t1-t0 >= 0.14) t0 = time.monotonic() self.loop.run_until_complete(tasks.Task(foo())) t1 = time.monotonic() self.assertTrue(t1-t0 <= 0.01)
def test_wait_with_exception(self): def gen(): when = yield self.assertAlmostEqual(0.1, when) when = yield 0 self.assertAlmostEqual(0.15, when) yield 0.15 loop = test_utils.TestLoop(gen) self.addCleanup(loop.close) a = tasks.Task(tasks.sleep(0.1, loop=loop), loop=loop) @tasks.coroutine def sleeper(): yield from tasks.sleep(0.15, loop=loop) raise ZeroDivisionError('really') b = tasks.Task(sleeper(), loop=loop) @tasks.coroutine def foo(): done, pending = yield from tasks.wait([b, a], loop=loop) self.assertEqual(len(done), 2) self.assertEqual(pending, set()) errors = set(f for f in done if f.exception() is not None) self.assertEqual(len(errors), 1) loop.run_until_complete(tasks.Task(foo(), loop=loop)) self.assertAlmostEqual(0.15, loop.time()) loop.run_until_complete(tasks.Task(foo(), loop=loop)) self.assertAlmostEqual(0.15, loop.time())
def test_wait_first_exception_in_wait(self): def gen(): when = yield self.assertAlmostEqual(10.0, when) when = yield 0 self.assertAlmostEqual(0.01, when) yield 0.01 loop = test_utils.TestLoop(gen) self.addCleanup(loop.close) # first_exception, exception during waiting a = tasks.Task(tasks.sleep(10.0, loop=loop), loop=loop) @tasks.coroutine def exc(): yield from tasks.sleep(0.01, loop=loop) raise ZeroDivisionError('err') b = tasks.Task(exc(), loop=loop) task = tasks.wait([b, a], return_when=tasks.FIRST_EXCEPTION, loop=loop) done, pending = loop.run_until_complete(task) self.assertEqual({b}, done) self.assertEqual({a}, pending) self.assertAlmostEqual(0.01, loop.time()) # move forward to close generator loop.advance_time(10) loop.run_until_complete(tasks.wait([a, b], loop=loop))
def test(): t = tasks.Task(putter(), loop=loop) yield from tasks.sleep(0.01, loop=loop) # The putter is blocked after putting two items. self.assertEqual([0, 1], have_been_put) self.assertEqual(0, q.get_nowait()) # Let the putter resume and put last item. yield from tasks.sleep(0.01, loop=loop) self.assertEqual([0, 1, 2], have_been_put) self.assertEqual(1, q.get_nowait()) self.assertEqual(2, q.get_nowait()) self.assertTrue(t.done()) self.assertTrue(t.result())
def test(): t = tasks.Task(putter(), loop=self.loop) yield from tasks.sleep(0.01, loop=self.loop) # The putter is blocked after putting two items. self.assertEqual([0, 1], have_been_put) self.assertEqual(0, q.get_nowait()) # Let the putter resume and put last item. yield from tasks.sleep(0.01, loop=self.loop) self.assertEqual([0, 1, 2], have_been_put) self.assertEqual(1, q.get_nowait()) self.assertEqual(2, q.get_nowait()) self.assertTrue(t.done()) self.assertTrue(t.result())
def test_sleep_cancel(self): def gen(): when = yield self.assertAlmostEqual(10.0, when) yield 0 loop = test_utils.TestLoop(gen) self.addCleanup(loop.close) t = tasks.Task(tasks.sleep(10.0, 'yeah', loop=loop), loop=loop) handle = None orig_call_later = loop.call_later def call_later(self, delay, callback, *args): nonlocal handle handle = orig_call_later(self, delay, callback, *args) return handle loop.call_later = call_later test_utils.run_briefly(loop) self.assertFalse(handle._cancelled) t.cancel() test_utils.run_briefly(loop) self.assertTrue(handle._cancelled)
def task(): nonlocal x while x < 10: yield from tasks.sleep(0.1, loop=self.loop) x += 1 if x == 2: self.loop.stop()
def add_getter(): q = queues.Queue(loop=self.loop) # Start a task that waits to get. tasks.Task(q.get(), loop=self.loop) # Let it start waiting. yield from tasks.sleep(0.1, loop=self.loop) self.assertTrue('_getters[1]' in fn(q))
def test_wait_with_exception(self): a = tasks.Task(tasks.sleep(0.1, loop=self.loop), loop=self.loop) @tasks.coroutine def sleeper(): yield from tasks.sleep(0.15, loop=self.loop) raise ZeroDivisionError('really') b = tasks.Task(sleeper(), loop=self.loop) @tasks.coroutine def foo(): done, pending = yield from tasks.wait([b, a], loop=self.loop) self.assertEqual(len(done), 2) self.assertEqual(pending, set()) errors = set(f for f in done if f.exception() is not None) self.assertEqual(len(errors), 1) t0 = time.monotonic() self.loop.run_until_complete(tasks.Task(foo(), loop=self.loop)) t1 = time.monotonic() self.assertTrue(t1 - t0 >= 0.14) t0 = time.monotonic() self.loop.run_until_complete(tasks.Task(foo(), loop=self.loop)) t1 = time.monotonic() self.assertTrue(t1 - t0 <= 0.01)
def task(): nonlocal x while x < 10: yield from tasks.sleep(0.1) x += 1 if x == 2: self.loop.stop()
def test_wait_errors(self): self.assertRaises( ValueError, self.loop.run_until_complete, tasks.wait(set())) self.assertRaises( ValueError, self.loop.run_until_complete, tasks.wait([tasks.sleep(10.0)], return_when=-1))
def sleep(dt): nonlocal sleepfut sleepfut = tasks.sleep(dt) try: time.monotonic() yield from sleepfut finally: time.monotonic()
def task(): nonlocal x while x < 10: waiters.append(tasks.sleep(0.1, loop=loop)) yield from waiters[-1] x += 1 if x == 2: loop.stop()
def sleep(dt): nonlocal sleepfut sleepfut = tasks.sleep(dt, loop=self.loop) try: time.monotonic() yield from sleepfut finally: time.monotonic()
def sleeper(dt, x): nonlocal time_shifted yield from tasks.sleep(dt, loop=loop) completed.add(x) if not time_shifted and 'a' in completed and 'b' in completed: time_shifted = True loop.advance_time(0.14) return x
def add_putter(): q = queues.Queue(maxsize=1, loop=self.loop) q.put_nowait(1) # Start a task that waits to put. tasks.Task(q.put(2), loop=self.loop) # Let it start waiting. yield from tasks.sleep(0.1, loop=self.loop) self.assertTrue('_putters[1]' in fn(q))
def test_wait_with_timeout(self): a = tasks.Task(tasks.sleep(0.1, loop=self.loop), loop=self.loop) b = tasks.Task(tasks.sleep(0.15, loop=self.loop), loop=self.loop) @tasks.coroutine def foo(): done, pending = yield from tasks.wait([b, a], timeout=0.11, loop=self.loop) self.assertEqual(done, set([a])) self.assertEqual(pending, set([b])) t0 = time.monotonic() self.loop.run_until_complete(tasks.Task(foo(), loop=self.loop)) t1 = time.monotonic() self.assertTrue(t1 - t0 >= 0.1) self.assertTrue(t1 - t0 <= 0.13)
def add_getter(): q = queues.Queue(loop=loop) # Start a task that waits to get. tasks.Task(q.get(), loop=loop) # Let it start waiting. yield from tasks.sleep(0.1, loop=loop) self.assertTrue('_getters[1]' in fn(q)) # resume q.get coroutine to finish generator q.put_nowait(0)
def test_wait_errors(self): self.assertRaises(ValueError, self.loop.run_until_complete, tasks.wait(set(), loop=self.loop)) self.assertRaises( ValueError, self.loop.run_until_complete, tasks.wait([tasks.sleep(10.0, loop=self.loop)], return_when=-1, loop=self.loop))
def test_as_completed_concurrent(self): def gen(): when = yield self.assertAlmostEqual(0.05, when) when = yield 0 self.assertAlmostEqual(0.05, when) yield 0.05 loop = test_utils.TestLoop(gen) self.addCleanup(loop.close) a = tasks.sleep(0.05, 'a', loop=loop) b = tasks.sleep(0.05, 'b', loop=loop) fs = {a, b} futs = list(tasks.as_completed(fs, loop=loop)) self.assertEqual(len(futs), 2) waiter = tasks.wait(futs, loop=loop) done, pending = loop.run_until_complete(waiter) self.assertEqual(set(f.result() for f in done), {'a', 'b'})
def do_requests(url): for i in range(ATTEMPTS): try: resp = yield from http.request('GET', url) page = yield from resp.read() return page.decode(resp.get_content_charset()) except Exception as e: if i == ATTEMPTS - 1: raise e yield from sleep(ATTEMPT_PAUSE)
def test_run_until_complete_timeout(self): t0 = self.loop.time() task = tasks.async(tasks.sleep(0.020)) self.assertRaises(futures.TimeoutError, self.loop.run_until_complete, task, timeout=0.010) t1 = self.loop.time() self.assertTrue(0.009 <= t1-t0 <= 0.018, t1-t0) self.loop.run_until_complete(task) t2 = self.loop.time() self.assertTrue(0.009 <= t2-t1 <= 0.018, t2-t1)
def test_wait(self): a = tasks.Task(tasks.sleep(0.1)) b = tasks.Task(tasks.sleep(0.15)) @tasks.coroutine def foo(): done, pending = yield from tasks.wait([b, a]) self.assertEqual(done, set([a, b])) self.assertEqual(pending, set()) return 42 t0 = time.monotonic() res = self.loop.run_until_complete(tasks.Task(foo())) t1 = time.monotonic() self.assertTrue(t1-t0 >= 0.14) self.assertEqual(res, 42) # Doing it again should take no time and exercise a different path. t0 = time.monotonic() res = self.loop.run_until_complete(tasks.Task(foo())) t1 = time.monotonic() self.assertTrue(t1-t0 <= 0.01)
def test_wait(self): a = tasks.Task(tasks.sleep(0.1, loop=self.loop), loop=self.loop) b = tasks.Task(tasks.sleep(0.15, loop=self.loop), loop=self.loop) @tasks.coroutine def foo(): done, pending = yield from tasks.wait([b, a], loop=self.loop) self.assertEqual(done, set([a, b])) self.assertEqual(pending, set()) return 42 t0 = time.monotonic() res = self.loop.run_until_complete(tasks.Task(foo(), loop=self.loop)) t1 = time.monotonic() self.assertTrue(t1 - t0 >= 0.14) self.assertEqual(res, 42) # Doing it again should take no time and exercise a different path. t0 = time.monotonic() res = self.loop.run_until_complete(tasks.Task(foo(), loop=self.loop)) t1 = time.monotonic() self.assertTrue(t1 - t0 <= 0.01)
def test_as_completed_with_timeout(self): def gen(): when = yield self.assertAlmostEqual(0.12, when) when = yield 0 self.assertAlmostEqual(0.1, when) when = yield 0 self.assertAlmostEqual(0.15, when) when = yield 0.1 self.assertAlmostEqual(0.12, when) yield 0.02 loop = test_utils.TestLoop(gen) self.addCleanup(loop.close) a = tasks.sleep(0.1, 'a', loop=loop) b = tasks.sleep(0.15, 'b', loop=loop) @tasks.coroutine def foo(): values = [] for f in tasks.as_completed([a, b], timeout=0.12, loop=loop): try: v = yield from f values.append((1, v)) except futures.TimeoutError as exc: values.append((2, exc)) return values res = loop.run_until_complete(tasks.Task(foo(), loop=loop)) self.assertEqual(len(res), 2, res) self.assertEqual(res[0], (1, 'a')) self.assertEqual(res[1][0], 2) self.assertTrue(isinstance(res[1][1], futures.TimeoutError)) self.assertAlmostEqual(0.12, loop.time()) # move forward to close generator loop.advance_time(10) loop.run_until_complete(tasks.wait([a, b], loop=loop))
def test_as_completed_reverse_wait(self): def gen(): yield 0 yield 0.05 yield 0 loop = test_utils.TestLoop(gen) self.addCleanup(loop.close) a = tasks.sleep(0.05, 'a', loop=loop) b = tasks.sleep(0.10, 'b', loop=loop) fs = {a, b} futs = list(tasks.as_completed(fs, loop=loop)) self.assertEqual(len(futs), 2) x = loop.run_until_complete(futs[1]) self.assertEqual(x, 'a') self.assertAlmostEqual(0.05, loop.time()) loop.advance_time(0.05) y = loop.run_until_complete(futs[0]) self.assertEqual(y, 'b') self.assertAlmostEqual(0.10, loop.time())
def test_as_completed_with_timeout(self): a = tasks.sleep(0.1, 'a') b = tasks.sleep(0.15, 'b') @tasks.coroutine def foo(): values = [] for f in tasks.as_completed([a, b], timeout=0.12): try: v = yield from f values.append((1, v)) except futures.TimeoutError as exc: values.append((2, exc)) return values t0 = time.monotonic() res = self.loop.run_until_complete(tasks.Task(foo())) t1 = time.monotonic() self.assertTrue(t1-t0 >= 0.11) self.assertEqual(len(res), 2, res) self.assertEqual(res[0], (1, 'a')) self.assertEqual(res[1][0], 2) self.assertTrue(isinstance(res[1][1], futures.TimeoutError))
def test_as_completed_with_timeout(self): a = tasks.sleep(0.1, 'a', loop=self.loop) b = tasks.sleep(0.15, 'b', loop=self.loop) @tasks.coroutine def foo(): values = [] for f in tasks.as_completed([a, b], timeout=0.12, loop=self.loop): try: v = yield from f values.append((1, v)) except futures.TimeoutError as exc: values.append((2, exc)) return values t0 = time.monotonic() res = self.loop.run_until_complete(tasks.Task(foo(), loop=self.loop)) t1 = time.monotonic() self.assertTrue(t1 - t0 >= 0.11) self.assertEqual(len(res), 2, res) self.assertEqual(res[0], (1, 'a')) self.assertEqual(res[1][0], 2) self.assertTrue(isinstance(res[1][1], futures.TimeoutError))
def queue_put(): with self.assertRaises(queues.Full): return (yield from q.put(1, timeout=0.01)) self.assertEqual(0, q.get_nowait()) # Put works after timeout, with blocking and non-blocking get. get_task = tasks.Task(q.get(), loop=self.loop) # Let the get start waiting. yield from tasks.sleep(0.01, loop=self.loop) q.put_nowait(2) self.assertEqual(2, (yield from get_task)) q.put_nowait(3) self.assertEqual(3, q.get_nowait())
def test_wait_first_exception_in_wait(self): # first_exception, exception during waiting a = tasks.Task(tasks.sleep(10.0, loop=self.loop), loop=self.loop) @tasks.coroutine def exc(): yield from tasks.sleep(0.01, loop=self.loop) raise ZeroDivisionError('err') b = tasks.Task(exc(), loop=self.loop) task = tasks.wait([b, a], return_when=tasks.FIRST_EXCEPTION, loop=self.loop) done, pending = self.loop.run_until_complete(task) self.assertEqual({b}, done) self.assertEqual({a}, pending)
def test_sleep_cancel(self): t = tasks.Task(tasks.sleep(10.0, 'yeah', loop=self.loop), loop=self.loop) handle = None orig_call_later = self.loop.call_later def call_later(self, delay, callback, *args): nonlocal handle handle = orig_call_later(self, delay, callback, *args) return handle self.loop.call_later = call_later test_utils.run_briefly(self.loop) self.assertFalse(handle._cancelled) t.cancel() test_utils.run_briefly(self.loop) self.assertTrue(handle._cancelled)