-
Notifications
You must be signed in to change notification settings - Fork 1
/
background_progress.py
198 lines (157 loc) · 5.99 KB
/
background_progress.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# (C) Copyright 2018-2021 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
"""
Support for a progress-reporting background call.
The code in this module supports an arbitrary callable that accepts a
"progress" named argument, and can use that argument to submit progress
information.
Every progress submission also marks a point where the callable can
be cancelled.
"""
from traits.api import Callable, Dict, Event, HasStrictTraits, Str, Tuple
from traits_futures.base_future import BaseFuture, BaseTask
from traits_futures.i_task_specification import ITaskSpecification
# Message types for messages from ProgressTask
# to ProgressFuture.
#: Task sends progress. Argument is a single object giving progress
#: information. This module does not interpret the contents of the argument.
PROGRESS = "progress"
class ProgressCancelled(Exception):
"""
Exception raised when progress reporting is interrupted by
task cancellation.
"""
class ProgressReporter:
"""
Object used by the target callable to report progress.
"""
def __init__(self, send, cancelled):
self.send = send
self.cancelled = cancelled
def report(self, progress_info):
"""
Send progress information to the linked future.
The ``progress_info`` object will eventually be sent to the
corresponding future's ``progress`` event trait.
Parameters
----------
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")
self.send((PROGRESS, progress_info))
class ProgressTask(BaseTask):
"""
Background portion of a progress background task.
This provides the callable that will be submitted to the worker pool, and
sends messages to communicate with the ProgressFuture.
"""
def __init__(self, callable, args, kwargs):
self.callable = callable
self.args = args
self.kwargs = kwargs
def run(self, send, cancelled):
progress = ProgressReporter(send=send, cancelled=cancelled)
try:
return self.callable(
*self.args,
**self.kwargs,
progress=progress.report,
)
except ProgressCancelled:
return None
class ProgressFuture(BaseFuture):
"""
Object representing the front-end handle to a ProgressTask.
"""
#: Event fired whenever a progress message arrives from the background.
progress = Event()
# Private methods #########################################################
def _process_progress(self, progress_info):
self.progress = progress_info
@ITaskSpecification.register
class BackgroundProgress(HasStrictTraits):
"""
Object representing the background task to be executed.
"""
#: The callable to be executed.
callable = Callable()
#: Positional arguments to be passed to the callable.
args = Tuple()
#: Named arguments to be passed to the callable.
kwargs = Dict(Str())
def future(self, cancel):
"""
Return a Future for the background task.
Parameters
----------
cancel
Zero-argument callable, returning no useful result. The returned
future's ``cancel`` method should call this to request cancellation
of the associated background task.
Returns
-------
future : ProgressFuture
Future object that can be used to monitor the status of the
background task.
"""
return ProgressFuture(_cancel=cancel)
def task(self):
"""
Return a background callable for this task specification.
Returns
-------
task : ProgressTask
Callable accepting arguments ``send`` and ``cancelled``. The
callable can use ``send`` to send messages and ``cancelled`` to
check whether cancellation has been requested.
"""
return ProgressTask(
callable=self.callable,
args=self.args,
kwargs=self.kwargs,
)
def submit_progress(executor, callable, *args, **kwargs):
"""
Submit a progress-reporting task to an executor.
Parameters
----------
executor : TraitsExecutor
Executor to submit the task to. This argument should always be passed
by position rather than by name. Future versions of the library may
enforce this restriction.
callable
Callable that executes the progress-providing function. This callable
must accept a "progress" named argument, in addition to the provided
arguments. The callable may then call the "progress" argument to report
progress. This argument should always be passed by position rather than
by name. Future versions of the library may enforce this restriction.
*args
Positional arguments to pass to the callable.
**kwargs
Named arguments other than "progress" to pass to the callable. These
must not include "progress".
Returns
-------
future : ProgressFuture
Object representing the state of the background task.
"""
if "progress" in kwargs:
raise TypeError("progress may not be passed as a named argument")
task = BackgroundProgress(callable=callable, args=args, kwargs=kwargs)
return executor.submit(task)