├── streaq ├── py.typed ├── __main__.py ├── __init__.py ├── constants.py ├── ui │ ├── templates │ │ ├── table.j2 │ │ ├── base.j2 │ │ ├── task.j2 │ │ └── queue.j2 │ ├── deps.py │ ├── __init__.py │ └── tasks.py ├── types.py ├── cli.py ├── utils.py ├── lua │ └── streaq.lua └── task.py ├── tests ├── __init__.py ├── test_utils.py ├── failure.py ├── conftest.py ├── test_web.py ├── test_cli.py ├── test_worker.py └── test_task.py ├── .python-version ├── .github ├── FUNDING.yml ├── pull_request_template.md ├── dependabot.yml └── workflows │ ├── python-publish-test.yml │ ├── python-publish.yml │ └── python-app.yml ├── docs ├── api │ ├── task.rst │ ├── types.rst │ ├── utils.rst │ └── worker.rst ├── Makefile ├── installation.rst ├── make.bat ├── contributing.rst ├── middleware.rst ├── cli.rst ├── getting-started.rst ├── index.rst ├── conf.py ├── integrations.rst ├── worker.rst └── task.rst ├── .readthedocs.yaml ├── Makefile ├── benchmarks ├── bench_streaq.py ├── bench_taskiq.py ├── bench_arq.py ├── bench_saq.py └── README.md ├── example.py ├── coverage.svg ├── LICENSE ├── .gitignore ├── pyproject.toml ├── README.md └── docker-compose.yml /streaq/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.10 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: tastyware 2 | -------------------------------------------------------------------------------- /streaq/__main__.py: -------------------------------------------------------------------------------- 1 | from .cli import cli 2 | 3 | if __name__ == "__main__": 4 | cli() 5 | -------------------------------------------------------------------------------- /docs/api/task.rst: -------------------------------------------------------------------------------- 1 | streaq.task 2 | =========== 3 | 4 | .. automodule:: streaq.task 5 | :members: 6 | :show-inheritance: 7 | -------------------------------------------------------------------------------- /docs/api/types.rst: -------------------------------------------------------------------------------- 1 | streaq.types 2 | ============ 3 | 4 | .. automodule:: streaq.types 5 | :members: 6 | :show-inheritance: 7 | -------------------------------------------------------------------------------- /docs/api/utils.rst: -------------------------------------------------------------------------------- 1 | streaq.utils 2 | ============ 3 | 4 | .. automodule:: streaq.utils 5 | :members: 6 | :show-inheritance: 7 | -------------------------------------------------------------------------------- /docs/api/worker.rst: -------------------------------------------------------------------------------- 1 | streaq.worker 2 | ============= 3 | 4 | .. automodule:: streaq.worker 5 | :members: 6 | :show-inheritance: 7 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | ## Related issue(s) 4 | Fixes ... 5 | 6 | ## Pre-merge checklist 7 | - [ ] Code formatted correctly (check with `make lint`) 8 | - [ ] Passing tests locally (check with `make test`) 9 | - [ ] New tests added (if applicable) 10 | - [ ] Docs updated (if applicable) 11 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: "ubuntu-22.04" 5 | tools: 6 | python: "3.10" 7 | commands: 8 | - asdf plugin add uv 9 | - asdf install uv latest 10 | - asdf global uv latest 11 | - uv sync --dev 12 | - uv run -m sphinx -T -b html -d docs/_build/doctrees -D language=en docs $READTHEDOCS_OUTPUT/html 13 | sphinx: 14 | configuration: docs/conf.py 15 | 16 | formats: all 17 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from streaq.utils import gather, import_string 4 | 5 | pytestmark = pytest.mark.anyio 6 | 7 | 8 | def test_bad_path(): 9 | with pytest.raises(ImportError): 10 | _ = import_string("asdf") 11 | 12 | 13 | def test_bad_worker_name(): 14 | with pytest.raises(ImportError): 15 | _ = import_string("example:asdf") 16 | 17 | 18 | async def test_useless_gather(): 19 | assert not (await gather()) 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install lint test docs 2 | 3 | install: 4 | uv sync --all-extras 5 | 6 | lint: 7 | uv run ruff check --select I --fix 8 | uv run ruff format streaq/ tests/ 9 | uv run ruff check streaq/ tests/ example.py 10 | uv run pyright streaq/ tests/ example.py 11 | 12 | test: 13 | UV_PYTHON=3.10 docker compose run --rm tests uv run --locked --all-extras --dev pytest -n auto --dist=loadgroup --cov=streaq tests/ 14 | 15 | docs: 16 | uv run -m sphinx -T -b html -d docs/_build/doctrees -D language=en docs/ docs/_build/ 17 | 18 | cleanup: 19 | docker compose down --remove-orphans 20 | -------------------------------------------------------------------------------- /streaq/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import coredis 4 | 5 | VERSION = "6.0.0" 6 | __version__ = VERSION 7 | 8 | logger = logging.getLogger(__name__) 9 | logger.addHandler(logging.NullHandler()) 10 | 11 | # disable some runtime checks 12 | coredis.Config.optimized = True 13 | 14 | # ruff: noqa: E402 15 | 16 | from .task import StreaqRetry, TaskStatus 17 | from .types import TaskContext 18 | from .utils import StreaqError 19 | from .worker import Worker 20 | 21 | __all__ = [ 22 | "StreaqError", 23 | "StreaqRetry", 24 | "TaskContext", 25 | "TaskStatus", 26 | "Worker", 27 | ] 28 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "uv" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "weekly" 16 | -------------------------------------------------------------------------------- /streaq/constants.py: -------------------------------------------------------------------------------- 1 | DEFAULT_QUEUE_NAME = "default" 2 | REDIS_ABORT = ":aborted" 3 | REDIS_CHANNEL = ":channels:" 4 | REDIS_CRON = ":cron:" 5 | REDIS_DEPENDENCIES = ":task:dependencies:" 6 | REDIS_DEPENDENTS = ":task:dependents:" 7 | REDIS_GROUP = "workers" 8 | REDIS_HEALTH = ":health" 9 | REDIS_PREFIX = "streaq:" 10 | REDIS_PREVIOUS = ":task:previous:" 11 | REDIS_RESULT = ":task:results:" 12 | REDIS_RETRY = ":task:retry:" 13 | REDIS_RUNNING = ":task:running:" 14 | REDIS_QUEUE = ":queues:delayed:" 15 | REDIS_STREAM = ":queues:" 16 | REDIS_TASK = ":task:data:" 17 | REDIS_TIMEOUT = ":queues:timeout:" 18 | REDIS_UNIQUE = ":task:lock:" 19 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /benchmarks/bench_streaq.py: -------------------------------------------------------------------------------- 1 | import anyio 2 | import typer 3 | 4 | from streaq import Worker 5 | 6 | worker = Worker(concurrency=32) 7 | N_TASKS = 20_000 8 | 9 | 10 | @worker.task() 11 | async def sleeper(time: int) -> None: 12 | if time: 13 | await anyio.sleep(time) 14 | 15 | 16 | async def main(time: int): 17 | start = anyio.current_time() 18 | tasks = [sleeper.enqueue(time) for _ in range(N_TASKS)] 19 | async with worker: 20 | await worker.enqueue_many(tasks) 21 | end = anyio.current_time() 22 | print(f"enqueued {N_TASKS} tasks in {end - start:.2f}s") 23 | 24 | 25 | def run(time: int = 0): 26 | anyio.run(main, time) 27 | 28 | 29 | if __name__ == "__main__": 30 | typer.run(run) 31 | -------------------------------------------------------------------------------- /.github/workflows/python-publish-test.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python distribution to TestPyPI 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build: 8 | name: Build distribution 9 | runs-on: ubuntu-latest 10 | permissions: 11 | id-token: write 12 | 13 | steps: 14 | - uses: actions/checkout@v6 15 | - name: Set up Python 16 | uses: actions/setup-python@v6 17 | with: 18 | python-version-file: ".python-version" 19 | - name: Install uv 20 | uses: astral-sh/setup-uv@v7 21 | with: 22 | enable-cache: true 23 | - name: Build package 24 | run: uv build 25 | - name: Publish to TestPyPI 26 | run: uv publish --publish-url https://test.pypi.org/legacy/ 27 | -------------------------------------------------------------------------------- /streaq/ui/templates/table.j2: -------------------------------------------------------------------------------- 1 |
| Updated time | 5 |Function name | 6 |Status | 7 |Task ID | 8 |
|---|---|---|---|
| {{ task.enqueue_time }} | 14 |{{ task.fn_name }} | 15 |{{ task.status.value }} | 16 |{{ task.task_id }} | 17 |
| Function | 15 |{{ function }} | 16 |
|---|---|
| Status | 19 |20 | {{ status }} 21 | | 22 |
| Created time | 25 |{{ created_time }} | 26 |
| Scheduled time | 30 |{{ scheduled }} | 31 |
| Try count | 34 |{{ task_try }} | 35 |
| Dependencies | 38 |{{ dependencies }} | 39 |
| Dependents | 42 |{{ dependents }} | 43 |
| Success | 47 |{{ success }} | 48 |
| Result | 51 |
52 |
54 | {{- result|trim -}}
55 |
56 | |
57 |
| Enqueued time | 60 |{{ enqueue_time }} | 61 |
| Start time | 64 |{{ start_time }} | 65 |
| End time | 68 |{{ finish_time }} | 69 |
| Worker | 72 |{{ worker_id }} | 73 |
tasks deferred
22 |tasks enqueued
41 |tasks in execution
60 |tasks finished
79 || Updated time | 125 |Function name | 126 |Status | 127 |Task ID | 128 |
|---|---|---|---|
| {{ task.enqueue_time }} | 134 |{{ task.fn_name }} | 135 |{{ task.status.value }} | 136 |{{ task.task_id }} | 137 |