def test_cancel_during_iteration(self): # Exercise code path where the cancel event is set during the # iteration. blocker = self._context.event() future = submit_iteration(self.executor, wait_midway, blocker) listener = IterationFutureListener(future=future) self.run_until( listener, "results_items", lambda listener: len(listener.results) > 0, ) # task is prevented from completing until we set the blocker event, # so we can cancel before that happens. self.assertTrue(future.cancellable) future.cancel() blocker.set() self.wait_until_done(future) self.assertNoException(future) self.assertEqual(listener.results, [1729]) self.assertEqual( listener.states, [WAITING, EXECUTING, CANCELLING, CANCELLED], )
def test_prompt_result_deletion(self): # Check that we're not hanging onto result references needlessly in the # background task. test_ready = self._context.event() midpoint = self._context.event() future = submit_iteration(self.executor, ping_pong, test_ready, midpoint) listener = IterationFutureListener(future=future) self.run_until( listener, "results_items", lambda listener: len(listener.results) > 0, ) # Check that there are no other references to this result besides # the one in this test. result = listener.results.pop() ref = weakref.ref(result) del result try: # midpoint won't be set until we next invoke "next(iterable)", # by which time the IterationTask's reference should # have been deleted. self.assertTrue(midpoint.wait(timeout=TIMEOUT)) self.assertIsNone(ref()) finally: # Let the background task complete, even if the test fails. test_ready.set()
def test_generator_closed_on_cancellation(self): resource_acquired = self._context.event() blocker = self._context.event() resource_released = self._context.event() future = submit_iteration( self.executor, resource_acquiring_iteration, resource_acquired, resource_released, blocker, ) listener = IterationFutureListener(future=future) self.run_until( listener, "results_items", lambda listener: len(listener.results) > 0, ) self.assertTrue(resource_acquired.is_set()) self.assertFalse(resource_released.is_set()) future.cancel() blocker.set() self.wait_until_done(future) self.assertTrue(resource_released.is_set())
def test_completed_cancel(self): future = submit_iteration(self.executor, squares, 0, 10) self.wait_until_done(future) self.assertFalse(future.cancellable) cancelled = future.cancel() self.assertFalse(cancelled)
def test_double_cancel(self): future = submit_iteration(self.executor, squares, 0, 10) self.assertTrue(future.cancellable) future.cancel() self.assertFalse(future.cancellable) cancelled = future.cancel() self.assertFalse(cancelled)
def test_general_iterable(self): # Any call that returns an iterable should be accepted future = submit_iteration(self.executor, range, 0, 10, 2) listener = IterationFutureListener(future=future) self.wait_until_done(future) self.assertNoException(future) self.assertEqual(listener.results, [0, 2, 4, 6, 8]) self.assertEqual(listener.states, [WAITING, EXECUTING, COMPLETED])
def test_iteration_with_result(self): future = submit_iteration(self.executor, iteration_with_result) listener = IterationFutureListener(future=future) self.wait_until_done(future) self.assertEqual(listener.states, [WAITING, EXECUTING, COMPLETED]) self.assertEqual(listener.results, [1, 2]) self.assertResult(future, 45) self.assertNoException(future)
def test_failing_iteration(self): # Iteration that eventually fails. future = submit_iteration(self.executor, reciprocals, start=-2, stop=2) listener = IterationFutureListener(future=future) self.wait_until_done(future) self.assertException(future, ZeroDivisionError) self.assertNoResult(future) self.assertEqual(listener.results, [-0.5, -1.0]) self.assertEqual(listener.states, [WAITING, EXECUTING, FAILED])
def test_bad_iteration_setup(self): # Deliberately passing a callable that returns # something non-iterable. future = submit_iteration(self.executor, pow, 2, 5) listener = IterationFutureListener(future=future) self.wait_until_done(future) self.assertException(future, TypeError) self.assertEqual(listener.results, []) self.assertEqual(listener.states, [WAITING, EXECUTING, FAILED])
def test_successful_iteration(self): # A simple case. future = submit_iteration(self.executor, reciprocals, start=1, stop=4) listener = IterationFutureListener(future=future) self.wait_until_done(future) self.assertResult(future, None) self.assertNoException(future) self.assertEqual(listener.results, [1.0, 0.5, 1 / 3.0]) self.assertEqual(listener.states, [WAITING, EXECUTING, COMPLETED])
def test_cancel_bad_job(self): future = submit_iteration(self.executor, pow, 10, 3) listener = IterationFutureListener(future=future) self.assertTrue(future.cancellable) future.cancel() self.wait_until_done(future) self.assertNoException(future) self.assertEqual(listener.results, []) self.assertEqual(listener.states, [WAITING, CANCELLING, CANCELLED])
def test_cancel_before_start(self): with self.block_worker_pool(): future = submit_iteration(self.executor, squares, 0, 10) listener = IterationFutureListener(future=future) self.assertTrue(future.cancellable) future.cancel() self.wait_until_done(future) self.assertNoException(future) self.assertEqual(listener.results, []) self.assertEqual(listener.states, [WAITING, CANCELLING, CANCELLED])
def test_cancel_before_execution(self): # Simulate race condition where we cancel after the background # iteration has checked the cancel event, but before we process # the STARTED message. event = self._context.event() future = submit_iteration(self.executor, set_then_yield, event) listener = IterationFutureListener(future=future) self.assertTrue(event.wait(timeout=TIMEOUT)) self.assertTrue(future.cancellable) future.cancel() self.wait_until_done(future) self.assertNoException(future) self.assertEqual(listener.results, []) self.assertEqual(listener.states, [WAITING, CANCELLING, CANCELLED])
def test_cancel_before_failure(self): blocker = self._context.event() future = submit_iteration(self.executor, wait_then_fail, blocker) listener = IterationFutureListener(future=future) self.wait_for_state(future, EXECUTING) self.assertTrue(future.cancellable) future.cancel() blocker.set() self.wait_until_done(future) self.assertNoException(future) self.assertEqual(listener.results, []) self.assertEqual( listener.states, [WAITING, EXECUTING, CANCELLING, CANCELLED], )
def test_cancel_before_exhausted(self): blocker = self._context.event() future = submit_iteration(self.executor, yield_then_wait, blocker) listener = IterationFutureListener(future=future) # Make sure we've got the single result. self.run_until( listener, "results_items", lambda listener: len(listener.results) > 0, ) self.assertTrue(future.cancellable) future.cancel() blocker.set() self.wait_until_done(future) self.assertNoException(future) self.assertEqual(listener.results, [1]) self.assertEqual( listener.states, [WAITING, EXECUTING, CANCELLING, CANCELLED], )
def test_cancel_after_start(self): blocker = self._context.event() future = submit_iteration(self.executor, wait_midway, blocker) listener = IterationFutureListener(future=future) self.run_until( listener, "results_items", lambda listener: len(listener.results) > 0, ) self.assertTrue(future.cancellable) future.cancel() blocker.set() self.wait_until_done(future) self.assertNoException(future) self.assertEqual(listener.results, [1729]) self.assertEqual( listener.states, [WAITING, EXECUTING, CANCELLING, CANCELLED], )
asyncio_future.set_result(traits_future.result) if sys.version_info < (3, 7): # We want to use get_running_loop, but it's new in Python 3.7. # This branch can be dropped once we can assume a minimum Python # version of 3.7. asyncio_future = asyncio.get_event_loop().create_future() else: asyncio_future = asyncio.get_running_loop().create_future() traits_future.observe(set_result, "done") return await asyncio_future def print_progress(event): """ Progress reporter for the π calculation. """ print(f"π is approximately {event.new:.6f}") if __name__ == "__main__": asyncio_event_loop = asyncio.new_event_loop() traits_executor = TraitsExecutor(event_loop=AsyncioEventLoop( event_loop=asyncio_event_loop)) traits_future = submit_iteration(traits_executor, approximate_pi) traits_future.observe(print_progress, "result_event") asyncio_event_loop.run_until_complete(future_wrapper(traits_future))
def _calculate_pi_approximately(self, event): self.future = submit_iteration(self.traits_executor, pi_iterations, chunk_size=self.chunk_size)
def _submit_calculation(self, event): self.message = "Calculating π" self.future = submit_iteration(self.traits_executor, approximate_pi, self.sample_count)