Commit b45d290c authored by Paul McCarthy's avatar Paul McCarthy 🚵
Browse files

Merge branch 'enh/taskthread' into 'master'

Enh/taskthread

See merge request fsl/fslpy!283
parents 16056e41 ef0c4878
......@@ -2,6 +2,19 @@ This document contains the ``fslpy`` release history in reverse chronological
order.
3.6.0 (Under development)
-------------------------
Changed
^^^^^^^
* The :class:`.TaskThread` now allows an error handler function to be
specified, which is run on the :mod:`.idle` loop.
3.5.3 (Tuesday 9th February 2021)
---------------------------------
......
......@@ -759,10 +759,11 @@ class Task(object):
"""Container object which encapsulates a task that is run by a
:class:`TaskThread`.
"""
def __init__(self, name, func, onFinish, args, kwargs):
def __init__(self, name, func, onFinish, onError, args, kwargs):
self.name = name
self.func = func
self.onFinish = onFinish
self.onError = onError
self.args = args
self.kwargs = kwargs
self.enabled = True
......@@ -808,9 +809,16 @@ class TaskThread(threading.Thread):
:arg onFinish: An optional function to be called (via :func:`idle`)
when the task funtion has finished. Must be provided as
a keyword argument. If the ``func`` raises a
:class`TaskThreadVeto` error, this function will not
be called.
a keyword argument, and must itself accept no arguments.
If the ``func`` raises a :class`TaskThreadVeto` error,
this function will not be called.
:arg onError: An optional function to be called (via :func:`idle`)
if the task funtion raises an ``Exception``. Must be
provided as a keyword argument, and must itself accept
the raised ``Exception`` object as a single argument.
If the ``func`` raises a :class`TaskThreadVeto` error,
this function will not be called.
All other arguments are passed through to the task function when it is
executed.
......@@ -821,16 +829,18 @@ class TaskThread(threading.Thread):
results.
.. warning:: Make sure that your task function is not expecting keyword
arguments called ``taskName`` or ``onFinish``!
arguments called ``taskName``, ``onFinish``, or
``onError``!
"""
name = kwargs.pop('taskName', None)
onFinish = kwargs.pop('onFinish', None)
onError = kwargs.pop('onError', None)
log.debug('Enqueueing task: {} [{}]'.format(
name, getattr(func, '__name__', '<unknown>')))
t = Task(name, func, onFinish, args, kwargs)
t = Task(name, func, onFinish, onError, args, kwargs)
self.__enqueued[name] = t
self.__q.put(t)
......@@ -951,6 +961,9 @@ class TaskThread(threading.Thread):
type(e).__name__,
str(e)),
exc_info=True)
if task.onError is not None:
idle(task.onError, e)
finally:
self.__q.task_done()
......
......@@ -600,6 +600,37 @@ def test_TaskThread_onFinish():
assert onFinishCalled[0]
def test_TaskThread_onError():
taskCalled = [False]
onFinishCalled = [False]
onErrorCalled = [False]
def task():
taskCalled[0] = True
raise Exception('Task error')
def onFinish():
onFinishCalled[0] = True
def onError(e):
onErrorCalled[0] = str(e)
tt = idle.TaskThread()
tt.start()
tt.enqueue(task, onFinish=onFinish, onError=onError)
time.sleep(0.5)
tt.stop()
tt.join()
assert taskCalled[0]
assert onErrorCalled[0] == 'Task error'
assert not onFinishCalled[0]
def test_TaskThread_isQueued():
called = [False]
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment