Skip to content

Commit bdeda0e

Browse files
committed
first commit
0 parents  commit bdeda0e

18 files changed

Lines changed: 719 additions & 0 deletions

.github/workflows/ci.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
test:
13+
name: Python ${{ matrix.python }}
14+
runs-on: ubuntu-latest
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
python: ['3.9', '3.10', '3.11', '3.12', '3.13']
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- name: Setup Python
23+
uses: actions/setup-python@v5
24+
with:
25+
python-version: ${{ matrix.python }}
26+
27+
- name: Install
28+
run: |
29+
python -m pip install --upgrade pip
30+
pip install -e ".[dev]"
31+
32+
- name: Run tests
33+
run: pytest

.github/workflows/release.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags: ['v*']
6+
7+
permissions:
8+
contents: read
9+
10+
jobs:
11+
release:
12+
name: Build, test & publish to PyPI
13+
runs-on: ubuntu-latest
14+
# Recommended for PyPI Trusted Publishing — configure a "pypi" environment +
15+
# a trusted publisher on PyPI (repo BabelQueue/babelqueue-python, this workflow).
16+
environment: pypi
17+
permissions:
18+
id-token: write # PyPI Trusted Publishing (OIDC) — no API token needed
19+
contents: write # create the GitHub release
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- name: Setup Python
24+
uses: actions/setup-python@v5
25+
with:
26+
python-version: '3.12'
27+
28+
- name: Install & test
29+
run: |
30+
python -m pip install --upgrade pip build
31+
pip install -e ".[dev]"
32+
pytest
33+
34+
- name: Build distributions
35+
run: python -m build
36+
37+
- name: Publish to PyPI
38+
uses: pypa/gh-action-pypi-publish@release/v1
39+
# Or, with a token instead of trusted publishing:
40+
# with:
41+
# password: ${{ secrets.PYPI_API_TOKEN }}
42+
43+
- name: Create GitHub release
44+
uses: softprops/action-gh-release@v2
45+
with:
46+
generate_release_notes: true

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
__pycache__/
2+
*.py[cod]
3+
*.egg-info/
4+
.eggs/
5+
build/
6+
dist/
7+
.pytest_cache/
8+
.mypy_cache/
9+
.ruff_cache/
10+
.venv/
11+
venv/

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Changelog
2+
3+
All notable changes to `babelqueue` (Python) are documented here.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
The envelope wire format is versioned separately by `meta.schema_version`
8+
(currently **1**) — see the contract at [babelqueue.com](https://babelqueue.com).
9+
10+
## [Unreleased]
11+
12+
### Added
13+
- `EnvelopeCodec` — builds (`make`, `from_message`), encodes and decodes the
14+
canonical `{job, trace_id, data, meta, attempts}` envelope (`schema_version` 1).
15+
The single Python implementation of the wire format.
16+
- Contracts `PolyglotMessage` / `HasTraceId` (typed `Protocol`s).
17+
- `dead_letter.annotate()` — additive `dead_letter` block builder.
18+
- `UnknownUrnStrategy``fail` / `delete` / `release` / `dead_letter`.
19+
- `BabelQueueError` / `UnknownUrnError`.
20+
- Golden conformance fixtures under `tests/fixtures/` (shared cross-SDK set).
21+
- `py.typed` — ships inline type hints (PEP 561).
22+
23+
### Notes
24+
- Pre-1.0: the public API may change before the `1.0.0` tag.
25+
- **Zero runtime dependencies** (standard library only). Requires Python `>=3.9`.
26+
- This is the framework-agnostic **core**. The broker runtime
27+
(`BabelQueue(broker_url=...)` + `@app.handler`, over `redis`/`pika`) and the
28+
Celery/Django adapters are planned next iterations, built on this core.
29+
30+
[Unreleased]: https://github.com/BabelQueue/babelqueue-python/commits/main

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Muhammet Şafak
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.

README.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# BabelQueue for Python
2+
3+
[![CI](https://github.com/BabelQueue/babelqueue-python/actions/workflows/ci.yml/badge.svg)](https://github.com/BabelQueue/babelqueue-python/actions/workflows/ci.yml)
4+
[![PyPI](https://img.shields.io/pypi/v/babelqueue.svg)](https://pypi.org/project/babelqueue/)
5+
[![Python](https://img.shields.io/pypi/pyversions/babelqueue.svg)](https://pypi.org/project/babelqueue/)
6+
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
7+
8+
> **Polyglot Queues, Simplified.** Read and write the canonical BabelQueue message
9+
> envelope from Python — so your Python services (AI/ML, data processing, …)
10+
> exchange messages with Laravel, Symfony, Go, .NET and Node over one strict JSON
11+
> format, on the broker you already run.
12+
13+
This is the framework-agnostic **Python core**: the wire-envelope codec,
14+
contracts, and dead-letter helpers — **zero runtime dependencies** (standard
15+
library only). The full standard is documented at
16+
**[babelqueue.com](https://babelqueue.com)**.
17+
18+
## Installation
19+
20+
```bash
21+
pip install babelqueue
22+
```
23+
24+
Requires Python `>=3.9`.
25+
26+
## Usage
27+
28+
```python
29+
from babelqueue import EnvelopeCodec
30+
31+
# Produce — build the canonical envelope and publish the JSON to your broker.
32+
envelope = EnvelopeCodec.make("urn:babel:orders:created", {"order_id": 1042})
33+
body = EnvelopeCodec.encode(envelope) # -> UTF-8 JSON string
34+
# redis.rpush("queues:orders", body) / channel.basic_publish(body=body, ...)
35+
36+
# Consume — decode a message produced by ANY BabelQueue SDK.
37+
incoming = EnvelopeCodec.decode(body)
38+
urn = incoming["job"] # "urn:babel:orders:created"
39+
data = incoming["data"] # {"order_id": 1042}
40+
trace_id = incoming["trace_id"] # correlate across services
41+
```
42+
43+
The envelope is identical to every other SDK's:
44+
45+
```json
46+
{
47+
"job": "urn:babel:orders:created",
48+
"trace_id": "",
49+
"data": { "order_id": 1042 },
50+
"meta": { "id": "", "queue": "default", "lang": "python", "schema_version": 1, "created_at": 1749132727000 },
51+
"attempts": 0
52+
}
53+
```
54+
55+
### Typed messages (optional)
56+
57+
```python
58+
from babelqueue import EnvelopeCodec, PolyglotMessage
59+
60+
class OrderCreated: # structurally a PolyglotMessage
61+
def __init__(self, order_id: int):
62+
self.order_id = order_id
63+
def get_babel_urn(self) -> str:
64+
return "urn:babel:orders:created"
65+
def to_payload(self) -> dict:
66+
return {"order_id": self.order_id}
67+
68+
envelope = EnvelopeCodec.from_message(OrderCreated(1042), queue="orders")
69+
```
70+
71+
Continue an existing trace by adding `get_babel_trace_id(self) -> str | None`
72+
(see `HasTraceId`), or pass `trace_id=` to `EnvelopeCodec.make`.
73+
74+
### Dead-letter
75+
76+
```python
77+
from babelqueue import dead_letter
78+
79+
dlq = dead_letter.annotate(envelope, "failed", "orders", attempts=3, error="boom")
80+
# publish `EnvelopeCodec.encode(dlq)` to the "orders.dlq" queue
81+
```
82+
83+
## What's here vs. coming
84+
85+
- **Now (this package):** the codec, contracts, dead-letter and unknown-URN
86+
helpers, plus the shared conformance fixtures. Bring your own broker client.
87+
- **Next (planned):** a built-in runtime — `BabelQueue(broker_url=...)` with an
88+
`@app.handler("urn:…")` decorator over `redis`/`pika` — and **Celery** / **Django**
89+
adapters. Install via extras (`babelqueue[redis]`, `babelqueue[celery]`, …).
90+
91+
## Testing
92+
93+
```bash
94+
pip install -e ".[dev]"
95+
pytest
96+
# (or, dependency-free) python -m unittest discover -s tests
97+
```
98+
99+
## License
100+
101+
MIT © Muhammet Şafak. See [LICENSE](LICENSE).

pyproject.toml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
[build-system]
2+
requires = ["hatchling"]
3+
build-backend = "hatchling.build"
4+
5+
[project]
6+
name = "babelqueue"
7+
version = "0.1.0"
8+
description = "Polyglot Queues, Simplified — the Python core: the canonical BabelQueue wire-envelope codec, contracts and dead-letter helpers."
9+
readme = "README.md"
10+
requires-python = ">=3.9"
11+
license = "MIT"
12+
license-files = ["LICENSE"]
13+
authors = [{ name = "Muhammet Şafak", email = "info@muhammetsafak.com.tr" }]
14+
keywords = ["queue", "polyglot", "microservices", "json", "envelope", "messaging"]
15+
classifiers = [
16+
"Development Status :: 4 - Beta",
17+
"Intended Audience :: Developers",
18+
"Operating System :: OS Independent",
19+
"Programming Language :: Python :: 3",
20+
"Programming Language :: Python :: 3.9",
21+
"Programming Language :: Python :: 3.10",
22+
"Programming Language :: Python :: 3.11",
23+
"Programming Language :: Python :: 3.12",
24+
"Programming Language :: Python :: 3.13",
25+
"Topic :: Software Development :: Libraries",
26+
"Typing :: Typed",
27+
]
28+
dependencies = []
29+
30+
[project.optional-dependencies]
31+
# Planned runtime / adapters — standard, zero-heavy-dep drivers.
32+
redis = ["redis>=4"]
33+
amqp = ["pika>=1.3"]
34+
dev = ["pytest>=7"]
35+
36+
[project.urls]
37+
Homepage = "https://babelqueue.com"
38+
Source = "https://github.com/BabelQueue/babelqueue-python"
39+
Issues = "https://github.com/BabelQueue/babelqueue-python/issues"
40+
Changelog = "https://github.com/BabelQueue/babelqueue-python/blob/main/CHANGELOG.md"
41+
42+
[tool.hatch.build.targets.wheel]
43+
packages = ["src/babelqueue"]

src/babelqueue/__init__.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""BabelQueue — Polyglot Queues, Simplified.
2+
3+
The framework-agnostic Python core: the canonical wire-envelope codec, contracts,
4+
and dead-letter helpers. Framework adapters (Celery, Django, ...) build on this.
5+
6+
from babelqueue import EnvelopeCodec
7+
8+
payload = EnvelopeCodec.make("urn:babel:orders:created", {"order_id": 1042})
9+
body = EnvelopeCodec.encode(payload) # send `body` over Redis/RabbitMQ
10+
"""
11+
12+
from __future__ import annotations
13+
14+
from . import dead_letter
15+
from .codec import SCHEMA_VERSION, SOURCE_LANG, EnvelopeCodec
16+
from .contracts import HasTraceId, PolyglotMessage
17+
from .exceptions import BabelQueueError, UnknownUrnError
18+
from .routing import UnknownUrnStrategy
19+
20+
__version__ = "0.1.0"
21+
22+
__all__ = [
23+
"EnvelopeCodec",
24+
"SCHEMA_VERSION",
25+
"SOURCE_LANG",
26+
"PolyglotMessage",
27+
"HasTraceId",
28+
"UnknownUrnStrategy",
29+
"BabelQueueError",
30+
"UnknownUrnError",
31+
"dead_letter",
32+
"__version__",
33+
]

0 commit comments

Comments
 (0)