hawkapi-taskiq 0.1.0__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.
@@ -0,0 +1,52 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ lint:
10
+ name: Lint
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: astral-sh/setup-uv@v4
15
+ with:
16
+ enable-cache: true
17
+ - name: Install dependencies
18
+ run: uv sync --extra dev
19
+ - name: ruff check
20
+ run: uv run ruff check .
21
+ - name: ruff format check
22
+ run: uv run ruff format --check .
23
+
24
+ typecheck:
25
+ name: Typecheck
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+ - uses: astral-sh/setup-uv@v4
30
+ with:
31
+ enable-cache: true
32
+ - name: Install dependencies
33
+ run: uv sync --extra dev
34
+ - name: pyright
35
+ run: uv run pyright src/
36
+
37
+ test:
38
+ name: Test (Python ${{ matrix.python-version }})
39
+ runs-on: ubuntu-latest
40
+ strategy:
41
+ matrix:
42
+ python-version: ["3.12", "3.13"]
43
+ steps:
44
+ - uses: actions/checkout@v4
45
+ - uses: astral-sh/setup-uv@v4
46
+ with:
47
+ enable-cache: true
48
+ python-version: ${{ matrix.python-version }}
49
+ - name: Install dependencies
50
+ run: uv sync --extra dev
51
+ - name: Run tests
52
+ run: uv run pytest tests/ -q
@@ -0,0 +1,25 @@
1
+ name: Release
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ build-and-publish:
9
+ name: Build and publish to PyPI
10
+ runs-on: ubuntu-latest
11
+ environment: release
12
+ permissions:
13
+ id-token: write # required for trusted publishing
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: astral-sh/setup-uv@v4
18
+ with:
19
+ enable-cache: true
20
+ - name: Build package
21
+ run: uv build
22
+ - name: Publish to PyPI
23
+ uses: pypa/gh-action-pypi-publish@release/v1
24
+ with:
25
+ packages-dir: dist/
@@ -0,0 +1,35 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.so
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ *.egg
9
+ .eggs/
10
+ .venv/
11
+ venv/
12
+ env/
13
+ .env
14
+ *.log
15
+ .mypy_cache/
16
+ .pyright/
17
+ .ruff_cache/
18
+ .pytest_cache/
19
+ htmlcov/
20
+ .coverage
21
+ .coverage.*
22
+ coverage.xml
23
+ *.cover
24
+ .hypothesis/
25
+ .tox/
26
+ .nox/
27
+ *.swp
28
+ *.swo
29
+ *~
30
+ .DS_Store
31
+ .idea/
32
+ .vscode/
33
+ .history/
34
+ site/
35
+ .remember/
@@ -0,0 +1,24 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-05-17
4
+
5
+ Initial release.
6
+
7
+ Security review applied before ship:
8
+
9
+ - `task_name` smuggled via `**task_kwargs` no longer bypasses the duplicate-name guard (defense in depth).
10
+ - `extra={"serializer": ...}` is rejected to prevent a future broker release accepting that kwarg from bypassing the JSON-only enforcement (CWE-502 hardening).
11
+ - `result_backend_url` now raises `NotImplementedError` rather than silently dropping the value — a misconfigured result backend can no longer be ignored.
12
+ - Double-init guard on `init_taskiq` — calling it twice on the same app raises rather than double-registering startup/shutdown hooks.
13
+ - `check_broker` actually probes the broker (was a static `True` before) and scrubs Redis/NATS credentials from any error string.
14
+ - `add_scheduled` removed from the public API — it set labels in a format `TaskiqScheduler` does not consume. `Scheduled` remains as a cron-validation value type; README shows the direct `LabelScheduleSource` wiring.
15
+
16
+ Features:
17
+
18
+ - `create_broker(TaskIQConfig(...))` — URL-scheme allowlist (`memory://`, `redis://`, `rediss://`, `nats://`), JSON serializer enforced at construction.
19
+ - `@task(broker, ...)` decorator with duplicate-name detection.
20
+ - `Scheduled` value type with cron-syntax validation via `croniter` (extras `[cron]`).
21
+ - `init_taskiq(app, ...)` wires startup/shutdown into the app lifecycle. `Depends(get_broker)` + `WeakKeyDictionary` registry.
22
+ - `check_broker()` health probe + `HealthReport` with credential scrubbing.
23
+ - `in_memory_broker()` async context manager for tests.
24
+ - Extras: `[redis]` (taskiq-redis), `[nats]` (taskiq-nats), `[cron]` (croniter).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 HawkAPI Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,178 @@
1
+ Metadata-Version: 2.4
2
+ Name: hawkapi-taskiq
3
+ Version: 0.1.0
4
+ Summary: TaskIQ integration for HawkAPI — modern async-native task queue, DI, scheduling, JSON-only formatter
5
+ Project-URL: Homepage, https://pypi.org/project/hawkapi-taskiq/
6
+ Project-URL: Repository, https://github.com/ashimov/hawkapi-taskiq
7
+ Project-URL: Issues, https://github.com/ashimov/hawkapi-taskiq/issues
8
+ Author-email: HawkAPI Contributors <hawkapi@users.noreply.github.com>
9
+ License: MIT License
10
+
11
+ Copyright (c) 2026 HawkAPI Contributors
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: async,hawkapi,queue,scheduling,taskiq,tasks
32
+ Classifier: Development Status :: 4 - Beta
33
+ Classifier: Framework :: AsyncIO
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.12
38
+ Classifier: Programming Language :: Python :: 3.13
39
+ Classifier: Topic :: System :: Distributed Computing
40
+ Classifier: Typing :: Typed
41
+ Requires-Python: >=3.12
42
+ Requires-Dist: hawkapi>=0.1.7
43
+ Requires-Dist: taskiq>=0.11
44
+ Provides-Extra: cron
45
+ Requires-Dist: croniter>=2.0; extra == 'cron'
46
+ Provides-Extra: dev
47
+ Requires-Dist: croniter>=2.0; extra == 'dev'
48
+ Requires-Dist: pyright>=1.1; extra == 'dev'
49
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
50
+ Requires-Dist: pytest>=8.0; extra == 'dev'
51
+ Requires-Dist: ruff>=0.8; extra == 'dev'
52
+ Provides-Extra: nats
53
+ Requires-Dist: taskiq-nats>=0.5; extra == 'nats'
54
+ Provides-Extra: redis
55
+ Requires-Dist: taskiq-redis>=1.0; extra == 'redis'
56
+ Description-Content-Type: text/markdown
57
+
58
+ # hawkapi-taskiq
59
+
60
+ [TaskIQ](https://taskiq-python.github.io/) integration for [HawkAPI](https://github.com/ashimov/HawkAPI). Modern async-native task queue — a lighter, async-first alternative to Celery.
61
+
62
+ ## Install
63
+
64
+ ```bash
65
+ pip install hawkapi-taskiq
66
+ pip install 'hawkapi-taskiq[redis]' # + taskiq-redis
67
+ pip install 'hawkapi-taskiq[nats]' # + taskiq-nats
68
+ pip install 'hawkapi-taskiq[cron]' # + croniter for schedule validation
69
+ ```
70
+
71
+ ## Quickstart
72
+
73
+ ```python
74
+ from hawkapi import Depends, HawkAPI
75
+ from hawkapi_taskiq import TaskIQConfig, get_broker, init_taskiq, task
76
+
77
+ app = HawkAPI()
78
+ broker = init_taskiq(app, config=TaskIQConfig(broker_url="redis://localhost:6379/0"))
79
+
80
+
81
+ @task(broker, name="emails.send")
82
+ async def send_email(to: str, subject: str) -> None:
83
+ ...
84
+
85
+
86
+ @app.post("/notify")
87
+ async def notify(email: str, b = Depends(get_broker)):
88
+ await send_email.kiq(email, "Hello")
89
+ return {"ok": True}
90
+ ```
91
+
92
+ ## Broker selection
93
+
94
+ Choose by URL scheme — all others are rejected:
95
+
96
+ | URL | Broker |
97
+ |---|---|
98
+ | `memory://` | `InMemoryBroker` (tests, single-process) |
99
+ | `redis://host:6379/0` | `ListQueueBroker` (taskiq-redis) |
100
+ | `rediss://...` | same, with TLS |
101
+ | `nats://server:4222` | `NatsBroker` (taskiq-nats) |
102
+
103
+ Any other scheme raises `ValueError` at `create_broker()` — this is a security feature, not a limitation. The allowlist prevents accidentally enabling brokers that use unsafe deserialization formats.
104
+
105
+ ## Scheduling
106
+
107
+ v0.1.0 ships a `Scheduled` value type with cron-syntax validation. Wire it to TaskIQ's native scheduler yourself — we deliberately avoid a "magic" registration helper that doesn't compose cleanly with the upstream `TaskiqScheduler`:
108
+
109
+ ```python
110
+ from hawkapi_taskiq import Scheduled
111
+ from taskiq import TaskiqScheduler
112
+ from taskiq.schedule_sources import LabelScheduleSource
113
+
114
+
115
+ @task(broker, name="myapp.cleanup")
116
+ async def cleanup() -> None:
117
+ ...
118
+
119
+
120
+ # 1. Validate the schedule (cron syntax) up front.
121
+ schedule = Scheduled(cron="0 * * * *") # raises ValueError if malformed
122
+
123
+ # 2. Apply it as a label LabelScheduleSource reads.
124
+ cleanup.labels["schedule"] = [{"cron": schedule.cron, "args": [], "kwargs": {}}]
125
+
126
+
127
+ # 3. Run a scheduler process alongside the worker.
128
+ scheduler = TaskiqScheduler(broker=broker, sources=[LabelScheduleSource(broker)])
129
+ ```
130
+
131
+ `Scheduled(cron="...")` validates via [`croniter`](https://github.com/kiorky/croniter) (install with `[cron]` extra). Both `cron` and `interval_seconds` set are rejected (exactly one is required).
132
+
133
+ ## Health
134
+
135
+ ```python
136
+ from hawkapi_taskiq import check_broker
137
+
138
+ report = await check_broker(broker)
139
+ # HealthReport(broker_ok=True, broker_type="ListQueueBroker", error="")
140
+ ```
141
+
142
+ ## Testing
143
+
144
+ ```python
145
+ from hawkapi_taskiq import in_memory_broker, task
146
+
147
+
148
+ async def test_my_task():
149
+ async with in_memory_broker() as broker:
150
+ @task(broker, name="t.work")
151
+ async def work(x: int) -> int:
152
+ return x * 2
153
+
154
+ await work.kiq(21)
155
+ # Execute pending tasks via TaskIQ's normal flow.
156
+ ```
157
+
158
+ ## Security
159
+
160
+ - **JSON-only serialization** — TaskIQ defaults are fine; we explicitly reject any other serializer via `TaskIQConfig.serializer` to prevent arbitrary-deserialization at consume time (CWE-502).
161
+ - **Broker URL scheme allowlist** — only `memory://`, `redis://`, `rediss://`, `nats://`.
162
+ - **Task name registry** — duplicate `@task(name=...)` raises at registration. TaskIQ silently overrides; we disallow.
163
+ - **Cron expression validation** at registration time — malformed expressions fail fast.
164
+
165
+ ## Development
166
+
167
+ ```bash
168
+ git clone https://github.com/ashimov/hawkapi-taskiq.git
169
+ cd hawkapi-taskiq
170
+ uv sync --extra dev
171
+ uv run pytest -q
172
+ uv run ruff check . && uv run ruff format --check .
173
+ uv run pyright src/
174
+ ```
175
+
176
+ ## License
177
+
178
+ MIT.
@@ -0,0 +1,121 @@
1
+ # hawkapi-taskiq
2
+
3
+ [TaskIQ](https://taskiq-python.github.io/) integration for [HawkAPI](https://github.com/ashimov/HawkAPI). Modern async-native task queue — a lighter, async-first alternative to Celery.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install hawkapi-taskiq
9
+ pip install 'hawkapi-taskiq[redis]' # + taskiq-redis
10
+ pip install 'hawkapi-taskiq[nats]' # + taskiq-nats
11
+ pip install 'hawkapi-taskiq[cron]' # + croniter for schedule validation
12
+ ```
13
+
14
+ ## Quickstart
15
+
16
+ ```python
17
+ from hawkapi import Depends, HawkAPI
18
+ from hawkapi_taskiq import TaskIQConfig, get_broker, init_taskiq, task
19
+
20
+ app = HawkAPI()
21
+ broker = init_taskiq(app, config=TaskIQConfig(broker_url="redis://localhost:6379/0"))
22
+
23
+
24
+ @task(broker, name="emails.send")
25
+ async def send_email(to: str, subject: str) -> None:
26
+ ...
27
+
28
+
29
+ @app.post("/notify")
30
+ async def notify(email: str, b = Depends(get_broker)):
31
+ await send_email.kiq(email, "Hello")
32
+ return {"ok": True}
33
+ ```
34
+
35
+ ## Broker selection
36
+
37
+ Choose by URL scheme — all others are rejected:
38
+
39
+ | URL | Broker |
40
+ |---|---|
41
+ | `memory://` | `InMemoryBroker` (tests, single-process) |
42
+ | `redis://host:6379/0` | `ListQueueBroker` (taskiq-redis) |
43
+ | `rediss://...` | same, with TLS |
44
+ | `nats://server:4222` | `NatsBroker` (taskiq-nats) |
45
+
46
+ Any other scheme raises `ValueError` at `create_broker()` — this is a security feature, not a limitation. The allowlist prevents accidentally enabling brokers that use unsafe deserialization formats.
47
+
48
+ ## Scheduling
49
+
50
+ v0.1.0 ships a `Scheduled` value type with cron-syntax validation. Wire it to TaskIQ's native scheduler yourself — we deliberately avoid a "magic" registration helper that doesn't compose cleanly with the upstream `TaskiqScheduler`:
51
+
52
+ ```python
53
+ from hawkapi_taskiq import Scheduled
54
+ from taskiq import TaskiqScheduler
55
+ from taskiq.schedule_sources import LabelScheduleSource
56
+
57
+
58
+ @task(broker, name="myapp.cleanup")
59
+ async def cleanup() -> None:
60
+ ...
61
+
62
+
63
+ # 1. Validate the schedule (cron syntax) up front.
64
+ schedule = Scheduled(cron="0 * * * *") # raises ValueError if malformed
65
+
66
+ # 2. Apply it as a label LabelScheduleSource reads.
67
+ cleanup.labels["schedule"] = [{"cron": schedule.cron, "args": [], "kwargs": {}}]
68
+
69
+
70
+ # 3. Run a scheduler process alongside the worker.
71
+ scheduler = TaskiqScheduler(broker=broker, sources=[LabelScheduleSource(broker)])
72
+ ```
73
+
74
+ `Scheduled(cron="...")` validates via [`croniter`](https://github.com/kiorky/croniter) (install with `[cron]` extra). Both `cron` and `interval_seconds` set are rejected (exactly one is required).
75
+
76
+ ## Health
77
+
78
+ ```python
79
+ from hawkapi_taskiq import check_broker
80
+
81
+ report = await check_broker(broker)
82
+ # HealthReport(broker_ok=True, broker_type="ListQueueBroker", error="")
83
+ ```
84
+
85
+ ## Testing
86
+
87
+ ```python
88
+ from hawkapi_taskiq import in_memory_broker, task
89
+
90
+
91
+ async def test_my_task():
92
+ async with in_memory_broker() as broker:
93
+ @task(broker, name="t.work")
94
+ async def work(x: int) -> int:
95
+ return x * 2
96
+
97
+ await work.kiq(21)
98
+ # Execute pending tasks via TaskIQ's normal flow.
99
+ ```
100
+
101
+ ## Security
102
+
103
+ - **JSON-only serialization** — TaskIQ defaults are fine; we explicitly reject any other serializer via `TaskIQConfig.serializer` to prevent arbitrary-deserialization at consume time (CWE-502).
104
+ - **Broker URL scheme allowlist** — only `memory://`, `redis://`, `rediss://`, `nats://`.
105
+ - **Task name registry** — duplicate `@task(name=...)` raises at registration. TaskIQ silently overrides; we disallow.
106
+ - **Cron expression validation** at registration time — malformed expressions fail fast.
107
+
108
+ ## Development
109
+
110
+ ```bash
111
+ git clone https://github.com/ashimov/hawkapi-taskiq.git
112
+ cd hawkapi-taskiq
113
+ uv sync --extra dev
114
+ uv run pytest -q
115
+ uv run ruff check . && uv run ruff format --check .
116
+ uv run pyright src/
117
+ ```
118
+
119
+ ## License
120
+
121
+ MIT.
@@ -0,0 +1,76 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "hawkapi-taskiq"
7
+ version = "0.1.0"
8
+ description = "TaskIQ integration for HawkAPI — modern async-native task queue, DI, scheduling, JSON-only formatter"
9
+ readme = "README.md"
10
+ license = { file = "LICENSE" }
11
+ requires-python = ">=3.12"
12
+ authors = [
13
+ { name = "HawkAPI Contributors", email = "hawkapi@users.noreply.github.com" },
14
+ ]
15
+ keywords = ["hawkapi", "taskiq", "tasks", "queue", "async", "scheduling"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Framework :: AsyncIO",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Topic :: System :: Distributed Computing",
25
+ "Typing :: Typed",
26
+ ]
27
+ dependencies = [
28
+ "hawkapi>=0.1.7",
29
+ "taskiq>=0.11",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ redis = ["taskiq-redis>=1.0"]
34
+ nats = ["taskiq-nats>=0.5"]
35
+ cron = ["croniter>=2.0"]
36
+ dev = [
37
+ "pytest>=8.0",
38
+ "pytest-asyncio>=0.24",
39
+ "croniter>=2.0",
40
+ "ruff>=0.8",
41
+ "pyright>=1.1",
42
+ ]
43
+
44
+ [project.urls]
45
+ Homepage = "https://pypi.org/project/hawkapi-taskiq/"
46
+ Repository = "https://github.com/ashimov/hawkapi-taskiq"
47
+ Issues = "https://github.com/ashimov/hawkapi-taskiq/issues"
48
+
49
+ [tool.hatch.build.targets.wheel]
50
+ packages = ["src/hawkapi_taskiq"]
51
+
52
+ [tool.pytest.ini_options]
53
+ testpaths = ["tests"]
54
+ asyncio_mode = "auto"
55
+ filterwarnings = ["ignore::DeprecationWarning"]
56
+
57
+ [tool.ruff]
58
+ target-version = "py312"
59
+ line-length = 100
60
+
61
+ [tool.ruff.lint]
62
+ select = ["E", "F", "I", "UP", "B", "SIM", "S"]
63
+ ignore = ["S101", "S105", "S110", "B008", "SIM105", "SIM108", "SIM113"]
64
+
65
+ [tool.ruff.lint.per-file-ignores]
66
+ "tests/**" = ["S"]
67
+
68
+ [tool.pyright]
69
+ pythonVersion = "3.12"
70
+ typeCheckingMode = "strict"
71
+ reportUnknownVariableType = false
72
+ reportUnknownMemberType = false
73
+ reportUnknownArgumentType = false
74
+ reportMissingTypeStubs = false
75
+ reportUntypedFunctionDecorator = false
76
+ reportGeneralTypeIssues = false
@@ -0,0 +1,35 @@
1
+ """hawkapi-taskiq — TaskIQ integration for HawkAPI.
2
+
3
+ Modern async-native task queue. URL-scheme allowlist enforces JSON-only
4
+ serialization to prevent arbitrary-deserialization vulnerabilities. Broker
5
+ DI via ``init_taskiq(app, ...)`` + ``Depends(get_broker)``. ``Scheduled``
6
+ provides cron-syntax validation; wire it into TaskIQ's own scheduler
7
+ sources (see README).
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from ._broker import create_broker
13
+ from ._config import ALLOWED_BROKER_SCHEMES, TaskIQConfig
14
+ from ._health import HealthReport, check_broker
15
+ from ._plugin import get_broker, init_taskiq, resolve_broker
16
+ from ._schedule import Scheduled
17
+ from ._tasks import task
18
+ from ._testing import in_memory_broker
19
+
20
+ __version__ = "0.1.0"
21
+
22
+ __all__ = [
23
+ "ALLOWED_BROKER_SCHEMES",
24
+ "HealthReport",
25
+ "Scheduled",
26
+ "TaskIQConfig",
27
+ "__version__",
28
+ "check_broker",
29
+ "create_broker",
30
+ "get_broker",
31
+ "in_memory_broker",
32
+ "init_taskiq",
33
+ "resolve_broker",
34
+ "task",
35
+ ]
@@ -0,0 +1,68 @@
1
+ """Broker factory — URL-scheme dispatch with strict allowlist."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from ._config import ALLOWED_BROKER_SCHEMES, TaskIQConfig, _scheme
8
+
9
+
10
+ def create_broker(config: TaskIQConfig | None = None) -> Any:
11
+ """Build an :class:`AsyncBroker` from ``config``. Raises ``ValueError`` for
12
+ any URL scheme not in :data:`ALLOWED_BROKER_SCHEMES`."""
13
+ cfg = config or TaskIQConfig()
14
+
15
+ if cfg.serializer != "json":
16
+ raise ValueError(
17
+ f"hawkapi-taskiq only supports the JSON serializer in v0.1.0; got {cfg.serializer!r}"
18
+ )
19
+
20
+ # Defense in depth — block ``serializer`` from ``extra`` so a future broker
21
+ # release that accepts the kwarg cannot bypass the JSON-only guarantee above.
22
+ if "serializer" in cfg.extra:
23
+ raise ValueError("'serializer' in extra is not permitted; JSON is enforced")
24
+
25
+ broker_url = cfg.broker_url.strip()
26
+ scheme = _scheme(broker_url)
27
+ if scheme not in ALLOWED_BROKER_SCHEMES:
28
+ raise ValueError(
29
+ f"broker_url scheme {scheme!r} is not in the allowlist "
30
+ f"{sorted(ALLOWED_BROKER_SCHEMES)!r}"
31
+ )
32
+
33
+ if cfg.result_backend_url:
34
+ # v0.1.0 does not yet plumb result_backend_url through to the broker —
35
+ # raise early so a misconfigured result backend cannot be silently dropped.
36
+ raise NotImplementedError(
37
+ "result_backend_url is not wired in v0.1.0; configure the result backend "
38
+ "on the broker directly via TaskIQConfig.extra"
39
+ )
40
+
41
+ if scheme == "memory":
42
+ from taskiq import InMemoryBroker
43
+
44
+ return InMemoryBroker()
45
+
46
+ if scheme in ("redis", "rediss"):
47
+ try:
48
+ from taskiq_redis import ListQueueBroker # type: ignore[import-not-found]
49
+ except ImportError as exc: # pragma: no cover
50
+ raise ImportError(
51
+ "taskiq-redis is required for redis brokers; pip install 'hawkapi-taskiq[redis]'"
52
+ ) from exc
53
+ return ListQueueBroker(url=broker_url, **cfg.extra)
54
+
55
+ if scheme == "nats":
56
+ try:
57
+ from taskiq_nats import NatsBroker # type: ignore[import-not-found]
58
+ except ImportError as exc: # pragma: no cover
59
+ raise ImportError(
60
+ "taskiq-nats is required for nats brokers; pip install 'hawkapi-taskiq[nats]'"
61
+ ) from exc
62
+ return NatsBroker(servers=[broker_url], **cfg.extra)
63
+
64
+ # Unreachable — the allowlist check above gates this.
65
+ raise ValueError(f"unsupported scheme {scheme!r}")
66
+
67
+
68
+ __all__ = ["create_broker"]
@@ -0,0 +1,38 @@
1
+ """TaskIQ configuration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Any
7
+
8
+
9
+ @dataclass(slots=True)
10
+ class TaskIQConfig:
11
+ """Builder for a TaskIQ broker via :func:`hawkapi_taskiq.create_broker`."""
12
+
13
+ broker_url: str = "memory://"
14
+ """Broker connection string. Only ``memory://``, ``redis://``, ``rediss://``,
15
+ ``nats://`` are accepted — anything else raises ``ValueError`` to prevent
16
+ accidentally enabling unsafe-deserialization brokers."""
17
+
18
+ result_backend_url: str = ""
19
+ """Optional result-backend URL. Same scheme allowlist as ``broker_url``."""
20
+
21
+ timezone: str = "UTC"
22
+ task_default_queue: str = "default"
23
+ serializer: str = "json"
24
+ """Always ``"json"`` for v0.1.0. Other serializers are rejected at runtime
25
+ to prevent arbitrary-deserialization vulns (CWE-502)."""
26
+
27
+ extra: dict[str, Any] = field(default_factory=dict)
28
+ """Forwarded as keyword args to the broker constructor."""
29
+
30
+
31
+ ALLOWED_BROKER_SCHEMES = frozenset({"memory", "redis", "rediss", "nats"})
32
+
33
+
34
+ def _scheme(url: str) -> str:
35
+ return url.split("://", 1)[0].lower() if "://" in url else ""
36
+
37
+
38
+ __all__ = ["ALLOWED_BROKER_SCHEMES", "TaskIQConfig", "_scheme"]