... | ... |
@@ -253,6 +253,74 @@ class AsyncRunner(BaseRunner): |
253 | 253 |
end_time = self.end_time if self.task.done() else time.time() |
254 | 254 |
return end_time - self.start_time |
255 | 255 |
|
256 |
+ def status(self): |
|
257 |
+ """Return the runner status as a string. |
|
258 |
+ |
|
259 |
+ The possible statuses are: running, cancelled, failed, and finished. |
|
260 |
+ """ |
|
261 |
+ try: |
|
262 |
+ self.task.result() |
|
263 |
+ except asyncio.CancelledError: |
|
264 |
+ return 'cancelled' |
|
265 |
+ except asyncio.InvalidStateError: |
|
266 |
+ return 'running' |
|
267 |
+ except Exception: |
|
268 |
+ return 'failed' |
|
269 |
+ else: |
|
270 |
+ return 'finished' |
|
271 |
+ |
|
272 |
+ def live_info(self, *, update_interval=0.5): |
|
273 |
+ """Display live information about the runner. |
|
274 |
+ |
|
275 |
+ Returns an interactive ipywidget that can be |
|
276 |
+ visualized in a Jupyter notebook. |
|
277 |
+ """ |
|
278 |
+ import ipywidgets as widgets |
|
279 |
+ from IPython.display import display |
|
280 |
+ |
|
281 |
+ status = widgets.HTML(value=self._info_html()) |
|
282 |
+ |
|
283 |
+ cancel = widgets.Button(description='cancel runner', |
|
284 |
+ layout=widgets.Layout(width='100px')) |
|
285 |
+ cancel.on_click(lambda _: self.cancel()) |
|
286 |
+ |
|
287 |
+ async def update(): |
|
288 |
+ while not self.task.done(): |
|
289 |
+ await asyncio.sleep(update_interval) |
|
290 |
+ status.value = self._info_html() |
|
291 |
+ status.value = self._info_html() |
|
292 |
+ |
|
293 |
+ self.ioloop.create_task(update()) |
|
294 |
+ |
|
295 |
+ hbox = widgets.HBox( |
|
296 |
+ (status, cancel), |
|
297 |
+ description='Runner stats', |
|
298 |
+ layout=widgets.Layout(border='solid 1px', |
|
299 |
+ width='200px', |
|
300 |
+ align_items='center'), |
|
301 |
+ ) |
|
302 |
+ return display(hbox) |
|
303 |
+ |
|
304 |
+ def _info_html(self): |
|
305 |
+ info = [ |
|
306 |
+ ('status', self.status()), |
|
307 |
+ ('elapsed time', datetime.timedelta(seconds=self.elapsed_time())), |
|
308 |
+ ] |
|
309 |
+ |
|
310 |
+ try: |
|
311 |
+ info.append(('# of points', self.learner.n)) |
|
312 |
+ except Exception: |
|
313 |
+ pass |
|
314 |
+ |
|
315 |
+ template = '<dt>{}</dt><dd>{}</dd>' |
|
316 |
+ table = '\n'.join(template.format(k, v) for k, v in info) |
|
317 |
+ |
|
318 |
+ return f''' |
|
319 |
+ <dl> |
|
320 |
+ {table} |
|
321 |
+ </dl> |
|
322 |
+ ''' |
|
323 |
+ |
|
256 | 324 |
def cancel(self): |
257 | 325 |
"""Cancel the runner. |
258 | 326 |
|
... | ... |
@@ -12,9 +12,13 @@ install_requires = [ |
12 | 12 |
'jupyter_client>=5.2.2', # because https://github.com/jupyter/jupyter_client/pull/314 |
13 | 13 |
] |
14 | 14 |
|
15 |
-extras_require = {'recommended': ['holoviews>=1.9.1', |
|
16 |
- 'ipyparallel', |
|
17 |
- 'distributed']} |
|
15 |
+extras_require = { |
|
16 |
+ 'recommended': [ |
|
17 |
+ 'holoviews>=1.9.1', |
|
18 |
+ 'ipyparallel', |
|
19 |
+ 'distributed', |
|
20 |
+ 'ipywidgets', |
|
21 |
+ ], |
|
18 | 22 |
|
19 | 23 |
setup( |
20 | 24 |
name='adaptive', |