Metadata-Version: 2.4
Name: pulq
Version: 0.2.1
Summary: Fair task scheduling with async pull: WDRR-based priority queue for Python
Author: Vadim Schultz
License-Expression: MIT
Project-URL: Homepage, https://github.com/vadim-schultz/pulq
Project-URL: Documentation, https://pulq.readthedocs.io
Project-URL: Repository, https://github.com/vadim-schultz/pulq
Project-URL: Issues, https://github.com/vadim-schultz/pulq/issues
Project-URL: Changelog, https://github.com/vadim-schultz/pulq/blob/main/CHANGELOG.md
Keywords: async,queue,task,scheduling,wdrr,priority,worker
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Framework :: AsyncIO
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Distributed Computing
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pydantic>=2.0
Requires-Dist: typing-extensions>=4.5
Provides-Extra: http
Requires-Dist: httpx>=0.27; extra == "http"
Requires-Dist: truststore>=0.9; extra == "http"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: pytest-cov>=4.1; extra == "dev"
Requires-Dist: ruff>=0.3; extra == "dev"
Requires-Dist: mypy>=1.9; extra == "dev"
Requires-Dist: hypothesis>=6.0; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"
Requires-Dist: truststore>=0.9; extra == "dev"
Provides-Extra: docs
Requires-Dist: sphinx>=7.0; extra == "docs"
Requires-Dist: sphinx-rtd-theme>=2.0; extra == "docs"
Requires-Dist: myst-parser>=2.0; extra == "docs"
Requires-Dist: sphinx-autodoc-typehints>=2.0; extra == "docs"
Dynamic: license-file

# PULQ

**Fair task scheduling with async pull — WDRR-based priority queue for Python**

[![CI](https://github.com/vadim-schultz/pulq/actions/workflows/ci.yml/badge.svg)](https://github.com/vadim-schultz/pulq/actions/workflows/ci.yml)
[![Documentation Status](https://readthedocs.org/projects/pulq/badge/?version=latest)](https://pulq.readthedocs.io/en/latest/?badge=latest)
[![codecov](https://codecov.io/gh/vadim-schultz/pulq/branch/main/graph/badge.svg)](https://codecov.io/gh/vadim-schultz/pulq)
[![PyPI version](https://img.shields.io/pypi/v/pulq)](https://pypi.org/project/pulq/)

[![Python versions](https://img.shields.io/pypi/pyversions/pulq)](https://pypi.org/project/pulq/)
[![License](https://img.shields.io/pypi/l/pulq)](https://github.com/vadim-schultz/pulq/blob/main/LICENSE)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
[![Typing: Strict](https://img.shields.io/badge/typing-strict-blue)](https://github.com/vadim-schultz/pulq/blob/main/pyproject.toml)

PULQ schedules **pull-based** work across named priority buckets using **Weighted Deficit Round Robin (WDRR)** so higher-weight classes get proportionally more CPU time without starving lower classes. Management commands (e.g. **STOP**) are delivered per worker ahead of normal tasks.

## Install

```bash
pip install pulq
```

For HTTP client transport: `pip install pulq[http]` — see [docs/transports.md](docs/transports.md).

## Quick start

`PullQueue` and `Worker` use **sensible defaults** so you can schedule tasks and run a worker with almost no configuration:

```python
import asyncio

from pulq import (
    CommandType,
    InMemoryTaskRepository,
    LocalTransport,
    PullQueue,
    Task,
    Worker,
)

async def handle(task: Task) -> dict:
    return {"ok": True, "echo": task.payload}

async def main() -> None:
    repo = InMemoryTaskRepository()
    queue = PullQueue(repo)  # default: high / medium / low with 3:2:1 weights, quantum 1
    await queue.schedule(Task(priority="high", handler_name="default", payload={"job": "a"}))
    await queue.schedule(Task(priority="low", handler_name="default", payload={"job": "b"}))

    transport = LocalTransport(queue)
    worker = Worker(transport, "worker-1", handle)  # default short backoff when idle

    async def stop_later() -> None:
        await asyncio.sleep(0.05)
        queue.send_command("worker-1", CommandType.STOP)

    await asyncio.gather(worker.run(), stop_later())

asyncio.run(main())
```

### Customizing with Pydantic config

For different priorities, weights, quantum, or worker lifecycle hooks on the **handler registry**, pass validated config objects:

```python
from pulq import DeficitSchedulerConfig, HandlerRegistry, PullQueueConfig, WorkerConfig

queue = PullQueue(
    repo,
    config=PullQueueConfig(
        scheduler=DeficitSchedulerConfig(
            priority_order=("critical", "high", "low"),
            weights={"critical": 5, "high": 3, "low": 1},
            quantum=2,
        ),
    ),
)

registry = HandlerRegistry(default=handle, startup=my_startup, shutdown=my_shutdown)
worker = Worker(
    transport,
    "worker-1",
    registry,
    config=WorkerConfig(no_work_delay_seconds=0.05),
)
```

More on **HTTP vs local** and **repositories**: [docs/transports.md](docs/transports.md).

## Documentation

Full docs: **[pulq.readthedocs.io](https://pulq.readthedocs.io/)**

## Development

```bash
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
ruff check src tests
mypy src
pytest
```

**Releases (PyPI + Read the Docs):** after bumping the version in `pyproject.toml` and updating `CHANGELOG.md`, merge to `main` — see [docs/releasing.md](docs/releasing.md) for trusted publishing setup on PyPI and manual options.

## License

MIT — see [LICENSE](LICENSE).
