Skip to content

Commit 1d2f0b2

Browse files
committed
Merge branch 'release/0.8.8'
2 parents 1731c4a + 1a3f3d3 commit 1d2f0b2

File tree

4 files changed

+89
-4
lines changed

4 files changed

+89
-4
lines changed

docs/guide/getting-started.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,36 @@ Returned value: 2
217217
```
218218

219219
Continue reading to get more information about taskiq internals.
220+
221+
222+
## Timeouts
223+
224+
If you want to restrict amount of time you want to run task,
225+
just add timeout label to the task.
226+
227+
You can do it either with decorator or when calling the task.
228+
229+
::: tabs
230+
231+
@tab decorator
232+
233+
```python
234+
@broker.task(timeout=0.1)
235+
async def mytask():
236+
await asyncio.sleep(2)
237+
```
238+
239+
@tab when calling
240+
241+
```python
242+
await my_task.kicker().with_labels(timeout=0.3).kiq()
243+
```
244+
245+
:::
246+
247+
::: danger Cool alert
248+
249+
We use [run_in_executor](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor) method to run sync functions. Timeouts will raise a TimeoutException, but
250+
synchronous function may not stop from execution. This is a constraint of python.
251+
252+
:::

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "taskiq"
3-
version = "0.8.7"
3+
version = "0.8.8"
44
description = "Distributed task queue with full async support"
55
authors = ["Pavel Kirilin <[email protected]>"]
66
maintainers = ["Pavel Kirilin <[email protected]>"]

taskiq/receiver/receiver.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,20 +218,27 @@ async def run_task( # noqa: C901, WPS210
218218
kwargs = await dep_ctx.resolve_kwargs()
219219
# We udpate kwargs with kwargs from network.
220220
kwargs.update(message.kwargs)
221-
221+
is_coroutine = True
222222
# If the function is a coroutine, we await it.
223223
if asyncio.iscoroutinefunction(target):
224-
returned = await target(*message.args, **kwargs)
224+
target_future = target(*message.args, **kwargs)
225225
else:
226+
is_coroutine = False
226227
# If this is a synchronous function, we
227228
# run it in executor.
228-
returned = await loop.run_in_executor(
229+
target_future = loop.run_in_executor(
229230
self.executor,
230231
_run_sync,
231232
target,
232233
message.args,
233234
kwargs,
234235
)
236+
timeout = message.labels.get("timeout")
237+
if timeout is not None:
238+
if not is_coroutine:
239+
logger.warning("Timeouts for sync tasks don't work in python well.")
240+
target_future = asyncio.wait_for(target_future, float(timeout))
241+
returned = await target_future
235242
except NoResultError as no_res_exc:
236243
found_exception = no_res_exc
237244
logger.warning(

tests/receiver/test_receiver.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
import time
23
from concurrent.futures import ThreadPoolExecutor
34
from typing import Any, AsyncGenerator, List, Optional, TypeVar
45

@@ -117,6 +118,50 @@ def test_func() -> None:
117118
assert result.is_err
118119

119120

121+
@pytest.mark.anyio
122+
async def test_run_timeouts() -> None:
123+
async def test_func() -> None:
124+
await asyncio.sleep(2)
125+
126+
receiver = get_receiver()
127+
128+
result = await receiver.run_task(
129+
test_func,
130+
TaskiqMessage(
131+
task_id="",
132+
task_name="",
133+
labels={"timeout": "0.3"},
134+
args=[],
135+
kwargs={},
136+
),
137+
)
138+
assert result.return_value is None
139+
assert result.execution_time < 2
140+
assert result.is_err
141+
142+
143+
@pytest.mark.anyio
144+
async def test_run_timeouts_sync() -> None:
145+
def test_func() -> None:
146+
time.sleep(2)
147+
148+
receiver = get_receiver()
149+
150+
result = await receiver.run_task(
151+
test_func,
152+
TaskiqMessage(
153+
task_id="",
154+
task_name="",
155+
labels={"timeout": "0.3"},
156+
args=[],
157+
kwargs={},
158+
),
159+
)
160+
assert result.return_value is None
161+
assert result.execution_time < 2
162+
assert result.is_err
163+
164+
120165
@pytest.mark.anyio
121166
async def test_run_task_exception_middlewares() -> None:
122167
"""Tests that run_task can run sync tasks."""

0 commit comments

Comments
 (0)