tasq-client-python 0.1.16__py3-none-any.whl → 0.1.18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tasq_client/client.py +57 -13
- tasq_client_python-0.1.18.dist-info/METADATA +5 -0
- tasq_client_python-0.1.18.dist-info/RECORD +8 -0
- {tasq_client_python-0.1.16.dist-info → tasq_client_python-0.1.18.dist-info}/WHEEL +1 -1
- tasq_client_python-0.1.16.dist-info/METADATA +0 -11
- tasq_client_python-0.1.16.dist-info/RECORD +0 -8
- {tasq_client_python-0.1.16.dist-info → tasq_client_python-0.1.18.dist-info}/top_level.txt +0 -0
tasq_client/client.py
CHANGED
|
@@ -6,13 +6,15 @@ from contextlib import contextmanager
|
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from queue import Empty, Queue
|
|
8
8
|
from threading import Thread
|
|
9
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
9
|
+
from typing import Any, Callable, Dict, Generator, List, Literal, Optional, Tuple
|
|
10
10
|
|
|
11
11
|
import requests
|
|
12
12
|
from requests.adapters import HTTPAdapter, Retry
|
|
13
13
|
|
|
14
14
|
from .check_type import CheckTypeException, OptionalKey, OptionalValue, check_type
|
|
15
15
|
|
|
16
|
+
ExceptionBehavior = Literal["fail", "expire", "ignore"]
|
|
17
|
+
|
|
16
18
|
|
|
17
19
|
@dataclass
|
|
18
20
|
class Task:
|
|
@@ -31,11 +33,15 @@ class QueueCounts:
|
|
|
31
33
|
expired: int
|
|
32
34
|
completed: int
|
|
33
35
|
|
|
36
|
+
# This is a newer field, so legacy servers will not return it.
|
|
37
|
+
failed: int = 0
|
|
38
|
+
|
|
34
39
|
# Won't be set if a time window wasn't specified in the request, or if the
|
|
35
40
|
# server is old enough to not support rate estimation.
|
|
36
41
|
rate: Optional[float] = None
|
|
37
42
|
|
|
38
43
|
modtime: Optional[int] = None
|
|
44
|
+
bytes: Optional[int] = None
|
|
39
45
|
|
|
40
46
|
|
|
41
47
|
class TasqClient:
|
|
@@ -93,7 +99,7 @@ class TasqClient:
|
|
|
93
99
|
full, in which case None is returned.
|
|
94
100
|
"""
|
|
95
101
|
return self._post_form(
|
|
96
|
-
|
|
102
|
+
"/task/push", dict(contents=contents, limit=limit), type_template=OptionalValue(str)
|
|
97
103
|
)
|
|
98
104
|
|
|
99
105
|
def push_batch(self, ids: List[str], limit: int = 0) -> Optional[List[str]]:
|
|
@@ -130,12 +136,12 @@ class TasqClient:
|
|
|
130
136
|
Unlike push_batch(), the ids returned by this method will never be
|
|
131
137
|
None, since all tasks must be pushed.
|
|
132
138
|
"""
|
|
133
|
-
assert isinstance(
|
|
134
|
-
contents, (
|
|
135
|
-
)
|
|
136
|
-
assert (
|
|
137
|
-
init_wait_time
|
|
138
|
-
)
|
|
139
|
+
assert isinstance(contents, (list, tuple)), (
|
|
140
|
+
f"expected a list of task contents, got object of type {type(contents)}"
|
|
141
|
+
)
|
|
142
|
+
assert init_wait_time <= self.max_timeout, (
|
|
143
|
+
f"wait time {init_wait_time=} should not be larger than {self.max_timeout=}"
|
|
144
|
+
)
|
|
139
145
|
assert limit < 0 or limit >= len(contents)
|
|
140
146
|
|
|
141
147
|
cur_wait = init_wait_time
|
|
@@ -225,8 +231,18 @@ class TasqClient:
|
|
|
225
231
|
"""Reset the timeout interval for a still in-progress task."""
|
|
226
232
|
self._post_form("/task/keepalive", dict(id=id), supports_timeout=True)
|
|
227
233
|
|
|
234
|
+
def expire(self, id: str):
|
|
235
|
+
"""Mark the given task as expired."""
|
|
236
|
+
self._post_form("/task/expire", dict(id=id))
|
|
237
|
+
|
|
238
|
+
def failed(self, id: str):
|
|
239
|
+
"""Mark the given task as failed."""
|
|
240
|
+
self._post_form("/task/failed", dict(id=id))
|
|
241
|
+
|
|
228
242
|
@contextmanager
|
|
229
|
-
def pop_running_task(
|
|
243
|
+
def pop_running_task(
|
|
244
|
+
self, on_exception: ExceptionBehavior = "ignore"
|
|
245
|
+
) -> Generator[Optional["RunningTask"], None, None]:
|
|
230
246
|
"""
|
|
231
247
|
Pop a task from the queue and wrap it in a RunningTask, blocking until
|
|
232
248
|
the queue is completely empty or a task is successfully popped.
|
|
@@ -237,6 +253,11 @@ class TasqClient:
|
|
|
237
253
|
This is meant to be used in a `with` clause. When the `with` clause is
|
|
238
254
|
exited, the keepalive loop is stopped, and the task will be marked as
|
|
239
255
|
completed unless the with clause is exited with an exception.
|
|
256
|
+
|
|
257
|
+
In the case of an exception, the on_exception behavior determines what
|
|
258
|
+
is done with the task. Optionally, the task can be marked as expired or
|
|
259
|
+
failed; by default, the task will be left in the running state until
|
|
260
|
+
its timeout expires.
|
|
240
261
|
"""
|
|
241
262
|
while True:
|
|
242
263
|
task, timeout = self.pop()
|
|
@@ -244,9 +265,16 @@ class TasqClient:
|
|
|
244
265
|
rt = RunningTask(self, id=task.id, contents=task.contents)
|
|
245
266
|
try:
|
|
246
267
|
yield rt
|
|
268
|
+
except:
|
|
269
|
+
if on_exception == "fail":
|
|
270
|
+
rt.failed()
|
|
271
|
+
elif on_exception == "expire":
|
|
272
|
+
rt.expire()
|
|
273
|
+
else:
|
|
274
|
+
rt.cancel()
|
|
275
|
+
raise
|
|
276
|
+
else:
|
|
247
277
|
rt.completed()
|
|
248
|
-
finally:
|
|
249
|
-
rt.cancel()
|
|
250
278
|
return
|
|
251
279
|
elif timeout is not None:
|
|
252
280
|
time.sleep(min(timeout, self.max_timeout))
|
|
@@ -257,13 +285,16 @@ class TasqClient:
|
|
|
257
285
|
def counts(self, rate_window: int = 0) -> QueueCounts:
|
|
258
286
|
"""Get the number of tasks in each state within the queue."""
|
|
259
287
|
data = self._get(
|
|
260
|
-
f"/counts?window={rate_window}&includeModtime=1",
|
|
288
|
+
f"/counts?window={rate_window}&includeModtime=1&includeBytes=1",
|
|
261
289
|
{
|
|
262
290
|
"pending": int,
|
|
263
291
|
"running": int,
|
|
264
292
|
"expired": int,
|
|
265
293
|
"completed": int,
|
|
294
|
+
OptionalKey("failed"): int,
|
|
266
295
|
OptionalKey("minute_rate"): float,
|
|
296
|
+
OptionalKey("modtime"): int,
|
|
297
|
+
OptionalKey("bytes"): int,
|
|
267
298
|
},
|
|
268
299
|
)
|
|
269
300
|
return QueueCounts(**data)
|
|
@@ -361,6 +392,7 @@ class RunningTask(Task):
|
|
|
361
392
|
daemon=True,
|
|
362
393
|
)
|
|
363
394
|
self._thread.start()
|
|
395
|
+
self._sent_final_status = False
|
|
364
396
|
|
|
365
397
|
def cancel(self):
|
|
366
398
|
if self._thread is None:
|
|
@@ -370,8 +402,20 @@ class RunningTask(Task):
|
|
|
370
402
|
self._thread = None
|
|
371
403
|
|
|
372
404
|
def completed(self):
|
|
405
|
+
self._send_status(self.client.completed)
|
|
406
|
+
|
|
407
|
+
def failed(self):
|
|
408
|
+
self._send_status(self.client.failed)
|
|
409
|
+
|
|
410
|
+
def expire(self):
|
|
411
|
+
self._send_status(self.client.expire)
|
|
412
|
+
|
|
413
|
+
def _send_status(self, fn: Callable[[str], None]):
|
|
414
|
+
if self._sent_final_status:
|
|
415
|
+
return
|
|
373
416
|
self.cancel()
|
|
374
|
-
|
|
417
|
+
fn(self.id)
|
|
418
|
+
self._sent_final_status = True
|
|
375
419
|
|
|
376
420
|
@staticmethod
|
|
377
421
|
def _keepalive_worker(
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
tasq_client/__init__.py,sha256=I0ik-_c0hcVKUgx7QsE3YnoCQyAVMFKKOzoLt-jNFtE,277
|
|
2
|
+
tasq_client/check_type.py,sha256=t_jreI8rf6QWS9Jf105ZvUVbwFe-uL4rMg4kZk6e4cA,2795
|
|
3
|
+
tasq_client/check_type_test.py,sha256=bvhVaO-Bu18aI3J4Kxnb0H27fzDCKkTHVBWhjJMFMis,1433
|
|
4
|
+
tasq_client/client.py,sha256=3og7zL7TkGmAYIDzt-fyrNfovvghiJL8Iy_TCsiad0w,16730
|
|
5
|
+
tasq_client_python-0.1.18.dist-info/METADATA,sha256=5w7-axKtRfNu28vjLIyRFGCG-r04ABxMBSnvGdtpZjU,110
|
|
6
|
+
tasq_client_python-0.1.18.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
7
|
+
tasq_client_python-0.1.18.dist-info/top_level.txt,sha256=JUs_FTRfs_ggMu8zusU5CSXgAl-JHhrjMXxuZay-B58,12
|
|
8
|
+
tasq_client_python-0.1.18.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
tasq_client/__init__.py,sha256=I0ik-_c0hcVKUgx7QsE3YnoCQyAVMFKKOzoLt-jNFtE,277
|
|
2
|
-
tasq_client/check_type.py,sha256=t_jreI8rf6QWS9Jf105ZvUVbwFe-uL4rMg4kZk6e4cA,2795
|
|
3
|
-
tasq_client/check_type_test.py,sha256=bvhVaO-Bu18aI3J4Kxnb0H27fzDCKkTHVBWhjJMFMis,1433
|
|
4
|
-
tasq_client/client.py,sha256=1f-BBYaiILfziZfzW3J4HOGwAgRbU80pbmlPX2J9wQE,15162
|
|
5
|
-
tasq_client_python-0.1.16.dist-info/METADATA,sha256=Y5auv-WWFODNhED09_NvXXNcVvyq6GDUYGvlzWf-Tx8,168
|
|
6
|
-
tasq_client_python-0.1.16.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
|
|
7
|
-
tasq_client_python-0.1.16.dist-info/top_level.txt,sha256=JUs_FTRfs_ggMu8zusU5CSXgAl-JHhrjMXxuZay-B58,12
|
|
8
|
-
tasq_client_python-0.1.16.dist-info/RECORD,,
|
|
File without changes
|