ltq 0.2.0__py3-none-any.whl → 0.3.1__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/scheduler.py CHANGED
@@ -4,21 +4,17 @@ import asyncio
4
4
  import time
5
5
  from dataclasses import dataclass, field
6
6
  from datetime import datetime
7
- from typing import TYPE_CHECKING
7
+ from typing import TYPE_CHECKING, Any
8
8
 
9
9
  from .message import Message
10
-
11
- try:
12
- from croniter import croniter # type: ignore[import-not-found]
13
- except ModuleNotFoundError as exc:
14
- raise ModuleNotFoundError(
15
- "Scheduler requires optional dependency 'croniter'. "
16
- "Install with 'ltq[scheduler]'."
17
- ) from exc
18
-
19
10
  from .utils import dispatch
20
11
  from .logger import get_logger
21
12
 
13
+ try:
14
+ from croniter import croniter
15
+ except ImportError:
16
+ croniter = None
17
+
22
18
  if TYPE_CHECKING:
23
19
  from .task import Task
24
20
 
@@ -28,11 +24,11 @@ class ScheduledJob:
28
24
  task: Task
29
25
  msg: Message
30
26
  expr: str
31
- _cron: croniter = field(init=False, repr=False)
27
+ _cron: Any = field(init=False, repr=False) # croniter instance
32
28
  next_run: datetime = field(init=False)
33
29
 
34
30
  def __post_init__(self):
35
- self._cron = croniter(self.expr, datetime.now())
31
+ self._cron = croniter(self.expr, datetime.now()) # type: ignore[misc]
36
32
  self.advance()
37
33
 
38
34
  def advance(self) -> None:
@@ -47,6 +43,11 @@ class Scheduler:
47
43
  self._running = False
48
44
 
49
45
  def cron(self, expr: str, msg: Message) -> None:
46
+ if croniter is None:
47
+ raise ModuleNotFoundError(
48
+ "Scheduler requires optional dependency 'croniter'. "
49
+ "Install with 'ltq[scheduler]'."
50
+ )
50
51
  if msg.task is None:
51
52
  raise ValueError("Message must have a task assigned to use with scheduler")
52
53
  self.jobs.append(ScheduledJob(msg.task, msg, expr))
ltq/worker.py CHANGED
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  from functools import partial
5
- from pathlib import Path
6
5
  from typing import TYPE_CHECKING, Any, Awaitable, Callable, ParamSpec, TypeVar
7
6
 
8
7
  import redis.asyncio as redis
@@ -38,15 +37,13 @@ class Worker:
38
37
  self.concurrency: int = concurrency
39
38
  self.poll_sleep: float = poll_sleep
40
39
 
41
-
42
40
  def task(
43
41
  self,
44
42
  queue_name: str | None = None,
45
43
  ttl: int | None = None,
46
44
  ) -> Callable[[Callable[P, Awaitable[R]]], Task[P, R]]:
47
45
  def decorator(fn: Callable[P, Awaitable[R]]) -> Task[P, R]:
48
- filename = Path(fn.__code__.co_filename).stem
49
- task_name = f"{filename}:{fn.__qualname__}"
46
+ task_name = f"{fn.__module__}:{fn.__qualname__}"
50
47
  queue = Queue(self.client, queue_name or task_name)
51
48
  task = Task(
52
49
  name=task_name,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ltq
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: Add your description here
5
5
  Author: Tom Clesius
6
6
  Author-email: Tom Clesius <tomclesius@gmail.com>
@@ -45,12 +45,12 @@ async def main():
45
45
  # Enqueue a task
46
46
  await send_email.send("user@example.com", "Hello", "World")
47
47
 
48
- # Or enqueue in bulk
48
+ # Or dispatch in bulk
49
49
  messages = [
50
50
  send_email.message("a@example.com", "Hi", "A"),
51
51
  send_email.message("b@example.com", "Hi", "B"),
52
52
  ]
53
- await send_email.send_bulk(messages)
53
+ await ltq.dispatch(messages)
54
54
 
55
55
  asyncio.run(main())
56
56
  ```
@@ -68,13 +68,41 @@ async def send_newsletter(...): ...
68
68
  ## Running Workers
69
69
 
70
70
  ```bash
71
- # Run a worker
71
+ # Run a single worker
72
72
  ltq myapp:worker
73
73
 
74
74
  # With options
75
75
  ltq myapp:worker --concurrency 100 --log-level DEBUG
76
76
  ```
77
77
 
78
+ ## Running an App
79
+
80
+ Register multiple workers into an `App` to run them together:
81
+
82
+ ```python
83
+ import ltq
84
+
85
+ app = ltq.App()
86
+ app.register_worker(emails_worker)
87
+ app.register_worker(notifications_worker)
88
+ ```
89
+
90
+ ```bash
91
+ ltq --app myapp:app
92
+ ```
93
+
94
+ ## Scheduler
95
+
96
+ Run tasks on a cron schedule (requires `ltq[scheduler]`):
97
+
98
+ ```python
99
+ import ltq
100
+
101
+ scheduler = ltq.Scheduler()
102
+ scheduler.cron("*/5 * * * *", send_email.message("admin@example.com", "Report", "..."))
103
+ scheduler.run()
104
+ ```
105
+
78
106
  ## Middleware
79
107
 
80
108
  Add middleware to handle cross-cutting concerns:
@@ -6,11 +6,11 @@ ltq/logger.py,sha256=HPClhDt3ecwZqE0Vq2oYF8Nr9jj-xrsSX9tM6enVgkA,1791
6
6
  ltq/message.py,sha256=hY_oPNMicSICV9-_o8Rma_kJO3DGJu_PFxyt1xZKMJE,871
7
7
  ltq/middleware.py,sha256=xy4VXy8uXp0vBMhdD6aSOQdzwCdbUkcuFw7FwPsqabU,3641
8
8
  ltq/q.py,sha256=3jRlPZKO4Zd5IYnb24jO_oRHtHBbG1la5SCZPI6QLI4,2537
9
- ltq/scheduler.py,sha256=EjXmmcyDv9Bp473Y40cTZAW6UVvkwZ_0MDE1XOjm5nI,2451
9
+ ltq/scheduler.py,sha256=6l53-Hf-2sURxTEpY1MOM5z18slQTl4450f-aLHF1sY,2516
10
10
  ltq/task.py,sha256=ZKKag4u9k9hQ-lNdIWZygmGCc01DRt8a6kMQlQr_yaw,1000
11
11
  ltq/utils.py,sha256=M7EWJ-n9J3MMwofWwtxVkwgHmGeP66eoWuWDhjbx67k,536
12
- ltq/worker.py,sha256=gHA9xfDx1plhrC53nfO2mmapWex1JbFVNxGGSIV_ow8,3423
13
- ltq-0.2.0.dist-info/WHEEL,sha256=e_m4S054HL0hyR3CpOk-b7Q7fDX6BuFkgL5OjAExXas,80
14
- ltq-0.2.0.dist-info/entry_points.txt,sha256=OogYaOJ_RORrWtrLlEL_gTN9Vx5tkgawl8BO7G9FKcg,38
15
- ltq-0.2.0.dist-info/METADATA,sha256=mf5XFhk1D41Oimz_f34fiNh7quyOhXBRQu5qwEt2tnE,2396
16
- ltq-0.2.0.dist-info/RECORD,,
12
+ ltq/worker.py,sha256=WIxLBCZYaVH_KVdexx77J607EEfQm1OKdtjmzCirvPM,3344
13
+ ltq-0.3.1.dist-info/WHEEL,sha256=e_m4S054HL0hyR3CpOk-b7Q7fDX6BuFkgL5OjAExXas,80
14
+ ltq-0.3.1.dist-info/entry_points.txt,sha256=OogYaOJ_RORrWtrLlEL_gTN9Vx5tkgawl8BO7G9FKcg,38
15
+ ltq-0.3.1.dist-info/METADATA,sha256=WJzVlmvsEHVuUl4HovJ9Ip1EU2HERuarIcGpfcpOKAc,2863
16
+ ltq-0.3.1.dist-info/RECORD,,
File without changes