Metadata-Version: 2.4
Name: pushq
Version: 0.1.0b4
Summary: Client for pushq — self-hosted HTTP push task queue (open-source Cloud Tasks alternative)
Project-URL: Homepage, https://github.com/blissfulrays/pushq
Project-URL: Documentation, https://github.com/blissfulrays/pushq/tree/main/sdks/python#readme
Project-URL: Repository, https://github.com/blissfulrays/pushq
Project-URL: Issues, https://github.com/blissfulrays/pushq/issues
Project-URL: Changelog, https://github.com/blissfulrays/pushq/releases
License-Expression: MIT
License-File: LICENSE
Keywords: background-jobs,cloud-tasks,queue,task-queue,webhooks
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Distributed Computing
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.25
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Description-Content-Type: text/markdown

# pushq (Python)

Client for [pushq](https://github.com/blissfulrays/pushq) — a self-hosted HTTP push
task queue (open-source alternative to GCP Cloud Tasks). Tasks are delivered
as HTTP requests to your endpoints with per-queue rate limits, concurrency
caps, and Cloud Tasks-compatible retry semantics.

```bash
pip install pushq
```

## Enqueue

```python
from pushq import Pushq, RateLimits, Retry, Target

pq = Pushq("http://localhost:8080", api_key="pq_...")

# Queues are created/updated at runtime — idempotent, safe to call on every enqueue.
pq.upsert_queue(
    "account-42",
    rate_limits=RateLimits(max_dispatches_per_second=10, max_concurrent_dispatches=5),
    target=Target(base_url="https://api.example.com",
                  signing_secret="whsec_..."),      # omit to keep the stored secret
)

pq.create_task(
    "account-42",
    path="/hooks/send-message",        # resolved against the queue's base_url
    json_body={"user_id": 1},
    delay_seconds=3600,                # or schedule_time=datetime(...)
    task_id="follow_up_abc",           # optional: dedup (409 on collision)
    retry=Retry(max_attempts=3),       # per-task override of the queue config
)

# Operate queues at runtime
pq.pause_queue("account-42")
pq.resume_queue("account-42")
pq.purge_queue("account-42")
stats = pq.queue_stats("account-42", id_prefix="follow_up_")
print(stats.pending, stats.errors_last_1h)
```

`AsyncPushq` mirrors the same surface with `async`/`await`.

## Verify deliveries (FastAPI)

pushq signs deliveries using the [Standard Webhooks](https://www.standardwebhooks.com)
scheme (`webhook-id`, `webhook-timestamp`, `webhook-signature` headers).

```python
from fastapi import FastAPI, HTTPException, Request
from pushq import verify_signature, WebhookVerificationError

app = FastAPI()

@app.post("/hooks/send-message")
async def send_message(request: Request):
    body = await request.body()
    try:
        # Returns "queue/task_id" — stable across retries, use it as an idempotency key.
        msg_id = verify_signature("whsec_...", request.headers, body)
    except WebhookVerificationError:
        raise HTTPException(status_code=401)
    ...
```

Errors raise `PushqError` with `.status`, `.code`
(`already_exists`, `not_found`, `failed_precondition`, …), and `.detail`.
