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 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
- f"/task/push", dict(contents=contents, limit=limit), type_template=OptionalValue(str)
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, (list, tuple)
135
- ), f"expected a list of task contents, got object of type {type(contents)}"
136
- assert (
137
- init_wait_time <= self.max_timeout
138
- ), f"wait time {init_wait_time=} should not be larger than {self.max_timeout=}"
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(self) -> Optional["RunningTask"]:
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
- self.client.completed(self.id)
417
+ fn(self.id)
418
+ self._sent_final_status = True
375
419
 
376
420
  @staticmethod
377
421
  def _keepalive_worker(
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: tasq-client-python
3
+ Version: 0.1.18
4
+ Requires-Dist: requests
5
+ Dynamic: requires-dist
@@ -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,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.37.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,11 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: tasq-client-python
3
- Version: 0.1.16
4
- Summary: UNKNOWN
5
- Home-page: UNKNOWN
6
- License: UNKNOWN
7
- Platform: UNKNOWN
8
- Requires-Dist: requests
9
-
10
- UNKNOWN
11
-
@@ -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,,