Skip to content

Commit 67336bb

Browse files
committed
docs: add examples with ProgressTracker
1 parent b5d6033 commit 67336bb

5 files changed

Lines changed: 81 additions & 6 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import asyncio
2+
3+
from taskiq_dependencies import Depends
4+
5+
from taskiq.depends.progress_tracker import ProgressTracker, TaskState
6+
7+
8+
async def my_task(progress: ProgressTracker[str] = Depends()) -> None:
9+
for i in range(10):
10+
await asyncio.sleep(1)
11+
await progress.set_progress(TaskState.STARTED, meta=f"{(i + 1) * 10}%")
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import asyncio
2+
from typing import Annotated
3+
4+
from taskiq_dependencies import Depends
5+
6+
from taskiq.depends.progress_tracker import ProgressTracker, TaskState
7+
8+
9+
async def my_task(progress: Annotated[ProgressTracker[str], Depends()]) -> None:
10+
for i in range(10):
11+
await asyncio.sleep(1)
12+
await progress.set_progress(TaskState.STARTED, meta=f"{(i + 1) * 10}%")

‎docs/guide/state-and-deps.md‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,48 @@ taskiq worker my_file:broker --no-propagate-errors
319319

320320
In this case, no exception will ever going to be propagated to any dependency.
321321

322+
## Progress tracking
323+
324+
Sometimes a task runs for a long time and you want to report how far it has progressed, so other parts of your system
325+
(e.g. a web handler polling for status) can display it.
326+
327+
Taskiq provides a `ProgressTracker` dependency for this. It's not a method on `Context`, it's a dependency, just like
328+
anything else built with `taskiq-dependencies`. It grabs the current `task_id` from the context for you and stores
329+
progress using your broker's result backend.
330+
331+
::: tabs
332+
333+
@tab Annotated 3.10+
334+
335+
@[code python](../examples/state/progress_tracker_annot.py)
336+
337+
@tab default values
338+
339+
@[code python](../examples/state/progress_tracker.py)
340+
341+
:::
342+
343+
You can read the progress back from anywhere that has access to the `AsyncTaskiqTask` returned by `kiq`:
344+
345+
```python
346+
task = await my_task.kiq()
347+
348+
progress = await task.get_progress()
349+
if progress is not None:
350+
print(progress.state, progress.meta)
351+
```
352+
353+
`state` can be one of the `TaskState` enum values (`STARTED`, `SUCCESS`, `FAILURE`, `RETRY`) or any custom string.
354+
`meta` is generic (`ProgressTracker[MetaType]`) and can be any value your result backend can serialize, such as a plain
355+
string, a `dict`, or a pydantic model. If you call `set_progress` without `meta`, the previously stored `meta` value is
356+
preserved, which is handy when you only want to update `state`.
357+
358+
::: warning important note
359+
360+
`set_progress`/`get_progress` are no-ops by default on `AsyncResultBackend`. Make sure the result backend you use
361+
actually implements progress storage (`InMemoryBroker`'s built-in backend does), before relying on this in production.
362+
363+
:::
322364

323365
## Generics
324366

‎taskiq/cli/scheduler/run.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ def _is_schedule_ready_to_send(
313313

314314
return is_ready_to_send
315315

316-
async def run(
316+
async def run( # noqa: C901
317317
self,
318318
*,
319319
update_interval: timedelta | None = None,

‎tests/cli/scheduler/test_send_with_timeout.py‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ def __init__(self, hang_seconds: float = 60.0) -> None:
3131
self.on_ready_calls = 0
3232

3333
async def on_ready( # type: ignore[override]
34-
self, source: ScheduleSource, task: ScheduledTask
34+
self,
35+
source: ScheduleSource,
36+
task: ScheduledTask,
3537
) -> None:
3638
self.on_ready_calls += 1
3739
await asyncio.sleep(self.hang_seconds)
@@ -98,7 +100,9 @@ def __init__(self) -> None:
98100
self.calls = 0
99101

100102
async def on_ready( # type: ignore[override]
101-
self, source: ScheduleSource, task: ScheduledTask
103+
self,
104+
source: ScheduleSource,
105+
task: ScheduledTask,
102106
) -> None:
103107
self.calls += 1
104108

@@ -131,7 +135,9 @@ def __init__(self) -> None:
131135
super().__init__(broker=InMemoryBroker(), sources=[_StubSource()])
132136

133137
async def on_ready( # type: ignore[override]
134-
self, source: ScheduleSource, task: ScheduledTask
138+
self,
139+
source: ScheduleSource,
140+
task: ScheduledTask,
135141
) -> None:
136142
raise RuntimeError("boom")
137143

@@ -153,7 +159,9 @@ def __init__(self) -> None:
153159
super().__init__(broker=InMemoryBroker(), sources=[_StubSource()])
154160

155161
async def on_ready( # type: ignore[override]
156-
self, source: ScheduleSource, task: ScheduledTask
162+
self,
163+
source: ScheduleSource,
164+
task: ScheduledTask,
157165
) -> None:
158166
try:
159167
await asyncio.sleep(30.0)
@@ -184,7 +192,9 @@ def __init__(self) -> None:
184192
self.calls: list[Any] = []
185193

186194
async def on_ready( # type: ignore[override]
187-
self, source: ScheduleSource, task: ScheduledTask
195+
self,
196+
source: ScheduleSource,
197+
task: ScheduledTask,
188198
) -> None:
189199
self.calls.append((source, task))
190200

0 commit comments

Comments
 (0)