ltq 0.3.2__py3-none-any.whl → 0.4.0__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.
ltq/app.py CHANGED
@@ -37,4 +37,5 @@ class App:
37
37
  while any(t.is_alive() for t in threads):
38
38
  await asyncio.sleep(0.2)
39
39
  except asyncio.CancelledError:
40
+ # Allow graceful shutdown when the run coroutine is cancelled.
40
41
  pass
ltq/broker.py CHANGED
@@ -41,6 +41,14 @@ class RedisBroker(Broker):
41
41
  self.url = url
42
42
  self._client = aioredis.from_url(url)
43
43
  self._id = uuid.uuid4().hex[:8]
44
+ self._consume = self._client.register_script("""
45
+ local ready = redis.call('zrangebyscore', KEYS[1], 0, ARGV[1], 'LIMIT', 0, 1)
46
+ if #ready == 0 then return nil end
47
+ local msg = ready[1]
48
+ redis.call('zadd', KEYS[2], ARGV[1], msg)
49
+ redis.call('zrem', KEYS[1], msg)
50
+ return msg
51
+ """)
44
52
 
45
53
  async def close(self) -> None:
46
54
  await self._client.aclose()
@@ -60,14 +68,11 @@ class RedisBroker(Broker):
60
68
 
61
69
  async def consume(self, queue: str) -> Message:
62
70
  while True:
63
- now = time.time()
64
- ready = await self._client.zrangebyscore(
65
- f"queue:{queue}", 0, now, start=0, num=1
66
- ) # type: ignore
67
- if ready:
68
- msg = ready[0]
69
- await self._client.zadd(f"processing:{queue}:{self._id}", {msg: now,}) # type: ignore
70
- await self._client.zrem(f"queue:{queue}", msg) # type: ignore
71
+ msg = await self._consume(
72
+ keys=[f"queue:{queue}", f"processing:{queue}:{self._id}"],
73
+ args=[time.time()],
74
+ )
75
+ if msg:
71
76
  return Message.from_json(msg)
72
77
  await asyncio.sleep(0.1)
73
78
 
ltq/middleware.py CHANGED
@@ -100,6 +100,7 @@ class Sentry(Middleware):
100
100
  sentry_sdk.init(dsn=dsn)
101
101
  self.sentry = sentry_sdk
102
102
  except ImportError:
103
+ # Sentry SDK is optional; if it's not installed, disable Sentry integration silently.
103
104
  pass
104
105
 
105
106
  @asynccontextmanager
ltq/scheduler.py CHANGED
@@ -96,6 +96,7 @@ class Scheduler:
96
96
  try:
97
97
  await self.task
98
98
  except asyncio.CancelledError:
99
+ # Task cancellation is expected during normal scheduler shutdown.
99
100
  pass
100
101
  self.task = None
101
102
  self.logger.info("Scheduler stopped")
ltq/worker.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import copy
4
5
  from contextlib import AsyncExitStack
5
6
  from typing import Awaitable, Callable, ParamSpec, TypeVar
6
7
 
@@ -26,7 +27,7 @@ class Worker:
26
27
  self.name = name
27
28
  self.broker = Broker.from_url(broker_url)
28
29
  self.tasks: list[Task] = []
29
- self.middlewares: list[Middleware] = middlewares or list(DEFAULT)
30
+ self.middlewares: list[Middleware] = middlewares or copy.deepcopy(DEFAULT)
30
31
  self.concurrency: int = concurrency
31
32
  self.logger = get_logger(name)
32
33
 
@@ -55,6 +56,7 @@ class Worker:
55
56
 
56
57
  async def _poll(self, task: Task, broker: Broker) -> None:
57
58
  sem = asyncio.Semaphore(self.concurrency)
59
+ pending: set[asyncio.Task] = set()
58
60
  self.logger.info(f"Polling for Task {task.name}")
59
61
 
60
62
  try:
@@ -62,9 +64,13 @@ class Worker:
62
64
  message = await broker.consume(task.name)
63
65
  # concurrency limiter, without, queue would be drained in one go.
64
66
  await sem.acquire()
65
- asyncio.create_task(self._process(task, broker, sem, message))
67
+ t = asyncio.create_task(self._process(task, broker, sem, message))
68
+ pending.add(t)
69
+ t.add_done_callback(pending.discard)
66
70
  except asyncio.CancelledError:
67
71
  self.logger.info(f"Worker {task.name} cancelled...")
72
+ if pending:
73
+ await asyncio.gather(*pending, return_exceptions=True)
68
74
  raise
69
75
 
70
76
  async def _process(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ltq
3
- Version: 0.3.2
3
+ Version: 0.4.0
4
4
  Summary: Add your description here
5
5
  Author: Tom Clesius
6
6
  Author-email: Tom Clesius <tomclesius@gmail.com>
@@ -0,0 +1,16 @@
1
+ ltq/__init__.py,sha256=dMoQtB1-odVRCDWDijNft-DlCverh8y83iQd-hDJxhw,491
2
+ ltq/app.py,sha256=MHKzbemukVG9aXUcy5TAayxn0D7kW6Zq7gxffIkvuvI,1410
3
+ ltq/broker.py,sha256=TtOOSVvaZ9jWBd6HiX4UfkznHzSyGgS9agzgiVuRqV4,4202
4
+ ltq/cli.py,sha256=8pt6_aP6RFi0Xi2garzefOlsBuPRXuJjpu_-ohG-__o,5282
5
+ ltq/errors.py,sha256=Oo6fVTFoNhO7xlXJLj4NwBBD4UsrYU_f4msoeS2GsM0,320
6
+ ltq/logger.py,sha256=XEcjvuZz_drfVyKAQj4r1Dz-gRwZcffkN_xTLg_h4hk,2088
7
+ ltq/message.py,sha256=vkF17yfTE7sWjqsn5PE9LpGh6FOIlwOJLS4OZ4iGD5k,829
8
+ ltq/middleware.py,sha256=JnTcWyUw6_VcMThQjWnKOzWDgakbIgs_rmWevwK_A4o,3573
9
+ ltq/scheduler.py,sha256=gOF5jvj9R1z83-QtYzfshq6dh3auqFToiQfHgyiDZgc,3189
10
+ ltq/task.py,sha256=0JUY55JezhrtUvAiZLtRPMap77nua09-D3S5VdOA9uY,944
11
+ ltq/utils.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
12
+ ltq/worker.py,sha256=S8tEn-tnNPEFbVGC0F9ohwI53jA4zwCQPqy6pGMg3RE,4168
13
+ ltq-0.4.0.dist-info/WHEEL,sha256=iHtWm8nRfs0VRdCYVXocAWFW8ppjHL-uTJkAdZJKOBM,80
14
+ ltq-0.4.0.dist-info/entry_points.txt,sha256=OogYaOJ_RORrWtrLlEL_gTN9Vx5tkgawl8BO7G9FKcg,38
15
+ ltq-0.4.0.dist-info/METADATA,sha256=JvU4mJm3z6T3qB-Y7ya4ZjXrItrZj8g0QzQbtktT5E4,5326
16
+ ltq-0.4.0.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- ltq/__init__.py,sha256=dMoQtB1-odVRCDWDijNft-DlCverh8y83iQd-hDJxhw,491
2
- ltq/app.py,sha256=l045sTXKHQo-pv5JDi8pE90wHGoCX7vsxrTQZIx7o5s,1335
3
- ltq/broker.py,sha256=oNZwmugFHB2PbdDiLt4db4MYUxwNOhdOIDto4vLb8uU,4062
4
- ltq/cli.py,sha256=8pt6_aP6RFi0Xi2garzefOlsBuPRXuJjpu_-ohG-__o,5282
5
- ltq/errors.py,sha256=Oo6fVTFoNhO7xlXJLj4NwBBD4UsrYU_f4msoeS2GsM0,320
6
- ltq/logger.py,sha256=XEcjvuZz_drfVyKAQj4r1Dz-gRwZcffkN_xTLg_h4hk,2088
7
- ltq/message.py,sha256=vkF17yfTE7sWjqsn5PE9LpGh6FOIlwOJLS4OZ4iGD5k,829
8
- ltq/middleware.py,sha256=uHNCJoohgHnPV2cPbaKOL0i-wU9D8WIhfoc07c78ve4,3475
9
- ltq/scheduler.py,sha256=FauNClAN-N7A6hUCBI0oAGe8at1fohDLzXuFI1hF1-I,3111
10
- ltq/task.py,sha256=0JUY55JezhrtUvAiZLtRPMap77nua09-D3S5VdOA9uY,944
11
- ltq/utils.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
12
- ltq/worker.py,sha256=6RPiNgPiJmdX38p8Iw_xpbExhYvE5VKqV_g-oC_nctc,3921
13
- ltq-0.3.2.dist-info/WHEEL,sha256=iHtWm8nRfs0VRdCYVXocAWFW8ppjHL-uTJkAdZJKOBM,80
14
- ltq-0.3.2.dist-info/entry_points.txt,sha256=OogYaOJ_RORrWtrLlEL_gTN9Vx5tkgawl8BO7G9FKcg,38
15
- ltq-0.3.2.dist-info/METADATA,sha256=8PubjxCurEHvtFF4L7enJM7D9OF65Tg0exeUd_Tt2rY,5326
16
- ltq-0.3.2.dist-info/RECORD,,
File without changes