Repository: enthought/traits-futures

Issue:
_ProgressCancelled should be in the api module
The `_ProgressCancelled` exception type should be made public, and should be available in `traits_futures.api`.

Target functions for `submit_progress` might legitimately want to access the exception that `progress` raises, and other runners that want to emulate the Traits Futures behaviour (for example in tests for such a target function) may want to raise that exception.

If we wanted to go one step further: ideally, it would be possible to write a function intended as a target for `submit_progress` without even importing Traits Futures. That would involve making the exception class available from the `progress` argument passed to the function. That's not impossible, but it's not clear whether it's really a win.

 

Comments:

Patch:
diff --git a/docs/source/guide/intro.rst b/docs/source/guide/intro.rst
index 13c6274..2cff470 100644
--- a/docs/source/guide/intro.rst
+++ b/docs/source/guide/intro.rst
@@ -54,7 +54,9 @@ from ``traits_futures.api``:
   *must* have a parameter called "progress".  A value for this parameter will
   be passed (by name) by the executor machinery. The value passed for the
   "progress" parameter can then be called to send progress reports to the
-  associated |ProgressFuture| object.
+  associated |ProgressFuture| object. If the future has been cancelled, the
+  next call to ``progress`` in the background task will raise a
+  |ProgressCancelled| exception.
 
   For example, your callable might look like this::
 
@@ -313,6 +315,7 @@ needed.
 .. |IterationFuture| replace:: :class:`~traits_futures.background_iteration.IterationFuture`
 .. |submit_iteration| replace:: :func:`~traits_futures.background_iteration.submit_iteration`
 
+.. |ProgressCancelled| replace:: :exc:`~traits_futures.background_progress.ProgressCancelled`
 .. |ProgressFuture| replace:: :class:`~traits_futures.background_progress.ProgressFuture`
 .. |submit_progress| replace:: :func:`~traits_futures.background_progress.submit_progress`
 
diff --git a/traits_futures/api.py b/traits_futures/api.py
index f5ec6ea..b147f9b 100644
--- a/traits_futures/api.py
+++ b/traits_futures/api.py
@@ -28,6 +28,7 @@ Task submission functions
 - :func:`~.submit_call`
 - :func:`~.submit_iteration`
 - :func:`~.submit_progress`
+- :exc:`~.ProgressCancelled`
 
 Types of futures
 ----------------
@@ -75,7 +76,11 @@ from traits_futures.background_iteration import (
     IterationFuture,
     submit_iteration,
 )
-from traits_futures.background_progress import ProgressFuture, submit_progress
+from traits_futures.background_progress import (
+    ProgressCancelled,
+    ProgressFuture,
+    submit_progress,
+)
 from traits_futures.base_future import BaseFuture
 from traits_futures.future_states import (
     CANCELLED,
@@ -105,6 +110,7 @@ __all__ = [
     "CallFuture",
     "IterationFuture",
     "ProgressFuture",
+    "ProgressCancelled",
     # Future states
     "FutureState",
     "CANCELLED",
diff --git a/traits_futures/background_progress.py b/traits_futures/background_progress.py
index c79d6e6..84f5f66 100644
--- a/traits_futures/background_progress.py
+++ b/traits_futures/background_progress.py
@@ -32,7 +32,7 @@ from traits_futures.i_task_specification import ITaskSpecification
 PROGRESS = "progress"
 
 
-class _ProgressCancelled(Exception):
+class ProgressCancelled(Exception):
     """
     Exception raised when progress reporting is interrupted by
     task cancellation.
@@ -60,9 +60,16 @@ class ProgressReporter:
         progress_info : object
             An arbitrary object representing progress. Ideally, this
             should be immutable and pickleable.
+
+        Raises
+        ------
+        ProgressCancelled
+            If a cancellation request for this task has already been made.
+            In this case, the exception will be raised before any progress
+            information is sent.
         """
         if self.cancelled():
-            raise _ProgressCancelled("Task was cancelled")
+            raise ProgressCancelled("Task was cancelled")
         self.send(PROGRESS, progress_info)
 
 
@@ -85,7 +92,7 @@ class ProgressBackgroundTask:
 
         try:
             return self.callable(*self.args, **self.kwargs)
-        except _ProgressCancelled:
+        except ProgressCancelled:
             return None
 
 


Test patch:
diff --git a/traits_futures/tests/background_progress_tests.py b/traits_futures/tests/background_progress_tests.py
index 6683862..3dcd648 100644
--- a/traits_futures/tests/background_progress_tests.py
+++ b/traits_futures/tests/background_progress_tests.py
@@ -17,6 +17,7 @@ from traits_futures.api import (
     EXECUTING,
     FAILED,
     FutureState,
+    ProgressCancelled,
     ProgressFuture,
     submit_progress,
     WAITING,
@@ -80,7 +81,7 @@ def syncing_progress(test_ready, raised, progress):
     # so that we never get to the following code.
     try:
         progress("second")
-    except BaseException:
+    except ProgressCancelled:
         raised.set()
         raise
 
diff --git a/traits_futures/tests/test_api.py b/traits_futures/tests/test_api.py
index 7fc2c83..9e7a1b4 100644
--- a/traits_futures/tests/test_api.py
+++ b/traits_futures/tests/test_api.py
@@ -29,6 +29,7 @@ class TestApi(unittest.TestCase):
             IterationFuture,
             MultiprocessingContext,
             MultithreadingContext,
+            ProgressCancelled,
             ProgressFuture,
             RUNNING,
             STOPPED,