ltq 0.2.0__tar.gz → 0.3.1__tar.gz

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.
@@ -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:
@@ -31,12 +31,12 @@ async def main():
31
31
  # Enqueue a task
32
32
  await send_email.send("user@example.com", "Hello", "World")
33
33
 
34
- # Or enqueue in bulk
34
+ # Or dispatch in bulk
35
35
  messages = [
36
36
  send_email.message("a@example.com", "Hi", "A"),
37
37
  send_email.message("b@example.com", "Hi", "B"),
38
38
  ]
39
- await send_email.send_bulk(messages)
39
+ await ltq.dispatch(messages)
40
40
 
41
41
  asyncio.run(main())
42
42
  ```
@@ -54,13 +54,41 @@ async def send_newsletter(...): ...
54
54
  ## Running Workers
55
55
 
56
56
  ```bash
57
- # Run a worker
57
+ # Run a single worker
58
58
  ltq myapp:worker
59
59
 
60
60
  # With options
61
61
  ltq myapp:worker --concurrency 100 --log-level DEBUG
62
62
  ```
63
63
 
64
+ ## Running an App
65
+
66
+ Register multiple workers into an `App` to run them together:
67
+
68
+ ```python
69
+ import ltq
70
+
71
+ app = ltq.App()
72
+ app.register_worker(emails_worker)
73
+ app.register_worker(notifications_worker)
74
+ ```
75
+
76
+ ```bash
77
+ ltq --app myapp:app
78
+ ```
79
+
80
+ ## Scheduler
81
+
82
+ Run tasks on a cron schedule (requires `ltq[scheduler]`):
83
+
84
+ ```python
85
+ import ltq
86
+
87
+ scheduler = ltq.Scheduler()
88
+ scheduler.cron("*/5 * * * *", send_email.message("admin@example.com", "Report", "..."))
89
+ scheduler.run()
90
+ ```
91
+
64
92
  ## Middleware
65
93
 
66
94
  Add middleware to handle cross-cutting concerns:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ltq"
3
- version = "0.2.0"
3
+ version = "0.3.1"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "Tom Clesius", email = "tomclesius@gmail.com" }]
@@ -19,7 +19,7 @@ requires = ["uv_build>=0.9.26,<0.10.0"]
19
19
  build-backend = "uv_build"
20
20
 
21
21
  [tool.bumpversion]
22
- current_version = "0.2.0"
22
+ current_version = "0.3.1"
23
23
  commit = true
24
24
  tag = true
25
25
  message = "v{new_version}"
@@ -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))
@@ -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,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes