Skip to content

Commit c140e64

Browse files
authored
Merge pull request #122 from labthings/refactor-tasks-to-actions
Refactor tasks to actions
2 parents a4f3949 + 2e96bba commit c140e64

File tree

20 files changed

+131
-113
lines changed

20 files changed

+131
-113
lines changed

docs/basic_usage/action_tasks.rst

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,24 @@ All Actions will return a serialized representation of the task, when your POST
1717

1818
Most users will not need to create instances of this class. Instead, they will be created automatically when a function is started by an API Action view.
1919

20-
.. autoclass:: labthings.tasks.TaskThread
20+
.. autoclass:: labthings.actions.ActionThread
2121
:members:
2222

23+
Accessing the current action thread
24+
+++++++++++++++++++++++++++++++++++
2325

24-
Accessing the current task
25-
++++++++++++++++++++++++++
26+
A function running inside a :class:`labthings.actions.ActionThread` is able to access the instance it is running in using the :meth:`labthings.current_action` function. This allows the state of the Action to be modified freely.
2627

27-
A function running inside a :class:`labthings.tasks.TaskThread` is able to access the instance it is running in using the :meth:`labthings.current_task` function. This allows the state of the Action to be modified freely.
28-
29-
.. autofunction:: labthings.current_task
28+
.. autofunction:: labthings.current_action
3029
:noindex:
3130

3231

33-
Updating task progress
34-
++++++++++++++++++++++
32+
Updating action progress
33+
++++++++++++++++++++++++
3534

36-
Some client applications may be able to display progress bars showing the progress of an action. Implementing progress updates in your actions is made easy with the :py:meth:`labthings.update_task_progress` function. This function takes a single argument, which is the action progress as an integer percent (0 - 100).
35+
Some client applications may be able to display progress bars showing the progress of an action. Implementing progress updates in your actions is made easy with the :py:meth:`labthings.update_action_progress` function. This function takes a single argument, which is the action progress as an integer percent (0 - 100).
3736

3837
If your long running function was started within a background task, this function will update the state of the corresponding action object. If your function is called outside of a long-running task (e.g. by some internal code, not the web API), then this function will silently do nothing.
3938

40-
.. autofunction:: labthings.update_task_progress
39+
.. autofunction:: labthings.update_action_progress
4140
:noindex:

src/labthings/__init__.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
from .sync import CompositeLock
2020
from .sync import ClientEvent
2121

22-
# Task management functions
23-
from .tasks import current_task
24-
from .tasks import update_task_progress
25-
from .tasks import update_task_data
26-
from .tasks import TaskKillException
22+
# Action threads
23+
from .actions import current_action
24+
from .actions import update_action_progress
25+
from .actions import update_action_data
26+
from .actions import ActionKilledException
2727

2828
# Schema and field
2929
from .schema import Schema
@@ -47,11 +47,10 @@
4747
"StrictLock",
4848
"CompositeLock",
4949
"ClientEvent",
50-
"current_task",
51-
"current_task_stopped"
52-
"update_task_progress"
53-
"update_task_data"
54-
"TaskKillException",
50+
"current_action",
51+
"update_action_progress",
52+
"update_action_data",
53+
"ActionKilledException",
5554
"extensions",
5655
"views",
5756
"fields",

src/labthings/actions/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
__all__ = [
2+
"current_action",
3+
"update_action_progress",
4+
"update_action_data",
5+
"ActionKilledException"
6+
]
7+
8+
from .pool import (
9+
Pool,
10+
current_action,
11+
update_action_progress,
12+
update_action_data,
13+
)
14+
from .thread import ActionThread, ActionKilledException
Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,28 @@
22
from functools import wraps
33

44
import threading
5-
from .thread import TaskThread
5+
from .thread import ActionThread
66

77

8-
# TODO: Handle discarding old tasks. Action views now use deques
8+
# TODO: Handle discarding old actions. Action views now use deques
99
class Pool:
1010
""" """
1111

1212
def __init__(self):
1313
self.threads = set()
1414

15-
def add(self, thread: TaskThread):
15+
def add(self, thread: ActionThread):
1616
"""
1717
18-
:param thread: TaskThread:
18+
:param thread: ActionThread:
1919
2020
"""
2121
self.threads.add(thread)
2222

23-
def start(self, thread: TaskThread):
23+
def start(self, thread: ActionThread):
2424
"""
2525
26-
:param thread: TaskThread:
26+
:param thread: ActionThread:
2727
2828
"""
2929
self.add(thread)
@@ -37,7 +37,7 @@ def spawn(self, function, *args, **kwargs):
3737
:param **kwargs:
3838
3939
"""
40-
thread = TaskThread(target=function, args=args, kwargs=kwargs)
40+
thread = ActionThread(target=function, args=args, kwargs=kwargs)
4141
self.start(thread)
4242
return thread
4343

@@ -55,7 +55,7 @@ def tasks(self):
5555
"""
5656
5757
58-
:returns: List of TaskThread objects.
58+
:returns: List of ActionThread objects.
5959
6060
:rtype: list
6161
@@ -66,7 +66,7 @@ def states(self):
6666
"""
6767
6868
69-
:returns: Dictionary of TaskThread.state dictionaries. Key is TaskThread ID.
69+
:returns: Dictionary of ActionThread.state dictionaries. Key is ActionThread ID.
7070
7171
:rtype: dict
7272
@@ -77,7 +77,7 @@ def to_dict(self):
7777
"""
7878
7979
80-
:returns: Dictionary of TaskThread objects. Key is TaskThread ID.
80+
:returns: Dictionary of ActionThread objects. Key is ActionThread ID.
8181
8282
:rtype: dict
8383
@@ -117,22 +117,22 @@ def join(self):
117117
# Operations on the current task
118118

119119

120-
def current_task():
121-
"""Return the Task instance in which the caller is currently running.
120+
def current_action():
121+
"""Return the ActionThread instance in which the caller is currently running.
122122
123-
If this function is called from outside a Task thread, it will return None.
123+
If this function is called from outside an ActionThread, it will return None.
124124
125125
126-
:returns: TaskThread -- Currently running Task thread.
126+
:returns: ActionThread -- Currently running ActionThread.
127127
128128
"""
129-
current_task_thread = threading.current_thread()
130-
if not isinstance(current_task_thread, TaskThread):
129+
current_action_thread = threading.current_thread()
130+
if not isinstance(current_action_thread, ActionThread):
131131
return None
132-
return current_task_thread
132+
return current_action_thread
133133

134134

135-
def update_task_progress(progress: int):
135+
def update_action_progress(progress: int):
136136
"""Update the progress of the Task in which the caller is currently running.
137137
138138
If this function is called from outside a Task thread, it will do nothing.
@@ -141,13 +141,13 @@ def update_task_progress(progress: int):
141141
:param progress: int:
142142
143143
"""
144-
if current_task():
145-
current_task().update_progress(progress)
144+
if current_action():
145+
current_action().update_progress(progress)
146146
else:
147147
logging.info("Cannot update task progress of __main__ thread. Skipping.")
148148

149149

150-
def update_task_data(data: dict):
150+
def update_action_data(data: dict):
151151
"""Update the data of the Task in which the caller is currently running.
152152
153153
If this function is called from outside a Task thread, it will do nothing.
@@ -156,7 +156,7 @@ def update_task_data(data: dict):
156156
:param data: dict:
157157
158158
"""
159-
if current_task():
160-
current_task().update_data(data)
159+
if current_action():
160+
current_action().update_data(data)
161161
else:
162162
logging.info("Cannot update task data of __main__ thread. Skipping.")
Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
_LOG = logging.getLogger(__name__)
1212

1313

14-
class TaskKillException(SystemExit):
14+
class ActionKilledException(SystemExit):
1515
"""Sibling of SystemExit, but specific to thread termination."""
1616

1717

18-
class TaskThread(threading.Thread):
18+
class ActionThread(threading.Thread):
1919
"""
2020
A native thread with extra functionality for tracking progress and thread termination.
2121
"""
@@ -35,7 +35,7 @@ def __init__(self, target=None, name=None, args=None, kwargs=None, daemon=True):
3535
args = args or ()
3636
kwargs = kwargs or {}
3737

38-
# A UUID for the TaskThread (not the same as the threading.Thread ident)
38+
# A UUID for the ActionThread (not the same as the threading.Thread ident)
3939
self._ID = uuid.uuid4() # Task ID
4040

4141
# Event to track if the task has started
@@ -78,17 +78,34 @@ def __init__(self, target=None, name=None, args=None, kwargs=None, daemon=True):
7878

7979
@property
8080
def id(self):
81-
""" """
81+
"""
82+
UUID for the thread. Note this not the same as the native thread ident.
83+
"""
8284
return self._ID
8385

8486
@property
8587
def output(self):
86-
""" """
88+
"""
89+
Return value of the Action function. If the Action is still running, returns None.
90+
"""
8791
return self._return_value
8892

8993
@property
9094
def status(self):
91-
""" """
95+
"""
96+
Current running status of the thread.
97+
98+
============== =============================================
99+
Status Meaning
100+
============== =============================================
101+
``pending`` Not yet started
102+
``running`` Currently in-progress
103+
``success`` Finished without error
104+
``error`` Exception occured in thread
105+
``stopped`` Thread finished cleanly after a stop request
106+
``terminated`` Thread killed after stop request timed out
107+
============== =============================================
108+
"""
92109
return self._status
93110

94111
@property
@@ -126,7 +143,7 @@ def run(self):
126143
with self._running_lock:
127144
# Don't run if the thread was stopped before starting
128145
if self.stopping.is_set():
129-
raise TaskKillException
146+
raise ActionKilledException
130147
if self._target:
131148
self._thread_proc(self._target)(*self._args, **self._kwargs)
132149
finally:
@@ -161,7 +178,7 @@ def wrapped(*args, **kwargs):
161178
try:
162179
self._return_value = f(*args, **kwargs)
163180
self._status = "success"
164-
except (TaskKillException, SystemExit) as e:
181+
except (ActionKilledException, SystemExit) as e:
165182
logging.error(e)
166183
# Set state to terminated
167184
self._status = "terminated"
@@ -192,7 +209,7 @@ def get(self, block=True, timeout=None):
192209
self.join(timeout=timeout)
193210
return self._return_value
194211

195-
def async_raise(self, exc_type):
212+
def _async_raise(self, exc_type):
196213
"""
197214
198215
:param exc_type:
@@ -242,7 +259,7 @@ def _is_thread_proc_running(self):
242259
return False
243260
return True
244261

245-
def terminate(self, exception=TaskKillException):
262+
def terminate(self, exception=ActionKilledException):
246263
"""
247264
248265
:param exception: (Default value = TaskKillException)
@@ -259,7 +276,7 @@ def terminate(self, exception=TaskKillException):
259276
"will not kill; letting thread exit gracefully."
260277
)
261278
return
262-
self.async_raise(exception)
279+
self._async_raise(exception)
263280

264281
# Wait (block) for the thread to finish closing. If the threaded function has cleanup code in a try-except,
265282
# this pause allows it to finish running before the main thread can continue.
@@ -271,7 +288,7 @@ def terminate(self, exception=TaskKillException):
271288
self.progress = None
272289
return True
273290

274-
def stop(self, timeout=5, exception=TaskKillException):
291+
def stop(self, timeout=5, exception=ActionKilledException):
275292
"""Sets the threads internal stopped event, waits for timeout seconds for the
276293
thread to stop nicely, then forcefully kills the thread.
277294
@@ -346,3 +363,7 @@ def emit(self, record):
346363
# We probably need to check the size of the list...
347364
# TODO: think about whether any of the keys are security flaws
348365
# (this is why I don't dump the whole logrecord)
366+
367+
368+
# Backwards compatibility
369+
ActionThread = ActionThread
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
from ...tasks import *
1+
from ...actions import current_action as current_task
2+
from ...actions import update_action_progress as update_task_progress
3+
from ...actions import update_action_data as update_task_data
4+
from ...actions import ActionKilledException as ThreadTerminationError

src/labthings/core/tasks/pool.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
from ...tasks.pool import *
1+
from ...actions.pool import current_action as current_task
2+
from ...actions.pool import update_action_progress as update_task_progress
3+
from ...actions.pool import update_action_data as update_task_data

src/labthings/core/tasks/thread.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from ...tasks.thread import *
1+
from ...actions.thread import *

src/labthings/default_views/tasks.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88

99

1010
class TaskList(View):
11-
"""List of all background tasks from the session"""
12-
13-
tags = ["tasks"]
11+
"""List of all background actions from the session"""
1412

1513
def get(self):
1614
""" """
@@ -29,8 +27,6 @@ class TaskView(View):
2927
3028
"""
3129

32-
tags = ["tasks"]
33-
3430
def get(self, task_id):
3531
"""Show status of a session task
3632

src/labthings/find.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
"current_app",
99
"url_for",
1010
"current_labthing",
11-
"current_labthing()",
1211
"registered_extensions",
1312
"registered_components",
1413
"find_component",

0 commit comments

Comments
 (0)