rocketmq-sdk 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,19 @@
1
+ .venv/
2
+ .git/
3
+ .github/
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .mypy_cache/
7
+ __pycache__/
8
+ *.pyc
9
+ *.egg-info/
10
+ dist/
11
+ build/
12
+ *.egg
13
+ tests/
14
+ examples/
15
+ docs/
16
+ *.md
17
+ Makefile
18
+ .gitignore
19
+ .dockerignore
@@ -0,0 +1,40 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ check:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.11", "3.12", "3.13"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v6
21
+ with:
22
+ enable-cache: true
23
+
24
+ - name: Set up Python ${{ matrix.python-version }}
25
+ run: uv python install ${{ matrix.python-version }}
26
+
27
+ - name: Install dependencies
28
+ run: uv sync
29
+
30
+ - name: Format check (ruff)
31
+ run: uv run ruff format --check src/ tests/ examples/
32
+
33
+ - name: Lint (ruff)
34
+ run: uv run ruff check src/ tests/ examples/
35
+
36
+ - name: Type check (pyright)
37
+ run: uv run pyright src/ tests/
38
+
39
+ - name: Test (pytest)
40
+ run: uv run pytest tests/ -v
@@ -0,0 +1,36 @@
1
+ name: Publish to PyPI
2
+
3
+ # Trigger on GitHub releases (recommended over tag-push for PyPI)
4
+ on:
5
+ release:
6
+ types: [published]
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ environment: rocketmq.py
12
+ permissions:
13
+ contents: read # Required for actions/checkout to clone the repo
14
+ id-token: write # Required for trusted publishing (OIDC)
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v6
21
+ with:
22
+ enable-cache: true
23
+
24
+ - name: Set up Python
25
+ run: uv python install 3.12
26
+
27
+ - name: Install dependencies
28
+ run: uv sync --no-dev --frozen
29
+
30
+ - name: Build package
31
+ run: uv build
32
+
33
+ - name: Publish to PyPI
34
+ uses: pypa/gh-action-pypi-publish@release/v1
35
+ # Uses trusted publishing (OIDC) — no API token needed.
36
+ # Configure at: https://pypi.org/manage/project/rocketmq/settings/publishing/
@@ -0,0 +1,10 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ *.egg
10
+ .mypy_cache/
@@ -0,0 +1,18 @@
1
+ FROM python:3.12-slim AS base
2
+
3
+ WORKDIR /app
4
+
5
+ # Install uv for fast dependency resolution
6
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
7
+
8
+ # Copy dependency files first for layer caching
9
+ COPY pyproject.toml uv.lock ./
10
+
11
+ # Install production deps only (no dev group)
12
+ RUN uv sync --no-dev --frozen
13
+
14
+ # Copy source code
15
+ COPY src/ src/
16
+
17
+ # Default command: Python shell with rocketmq importable
18
+ CMD ["uv", "run", "python", "-c", "import rocketmq; print(f'rocketmq {rocketmq.__version__}')"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Edilson Pateguana
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,32 @@
1
+ .DEFAULT_GOAL := help
2
+ .PHONY: help install fmt lint typecheck test check all clean
3
+
4
+ help: ## Show this help
5
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
6
+ awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-12s\033[0m %s\n", $$1, $$2}'
7
+
8
+ install: ## Install deps + editable package
9
+ uv sync
10
+
11
+ fmt: ## Format code (ruff)
12
+ uv run ruff format src/ tests/ examples/
13
+
14
+ lint: ## Lint code (ruff)
15
+ uv run ruff check src/ tests/ examples/
16
+
17
+ lint-fix: ## Lint + auto-fix (ruff)
18
+ uv run ruff check --fix src/ tests/ examples/
19
+
20
+ typecheck: ## Type-check (pyright)
21
+ uv run pyright src/ tests/
22
+
23
+ test: ## Run tests (pytest)
24
+ uv run pytest tests/ -v
25
+
26
+ check: lint typecheck test ## Run lint + typecheck + test
27
+
28
+ all: fmt lint-fix typecheck test ## Format + fix + typecheck + test
29
+
30
+ clean: ## Remove caches and build artifacts
31
+ rm -rf .venv .pytest_cache .ruff_cache dist build __pycache__
32
+ find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: rocketmq-sdk
3
+ Version: 0.1.0
4
+ Summary: Schema-aware AMQP client for the RocketMQ broker — Pydantic + aio-pika.
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: aio-pika<10.0,>=9.0
9
+ Requires-Dist: pydantic<3.0,>=2.0
10
+ Description-Content-Type: text/markdown
11
+
12
+ # rocketmq.py
13
+
14
+ Schema-aware AMQP client for the RocketMQ broker - **Pydantic v2 + aio-pika**.
15
+
16
+ Python port of the [`rocketmq.js`](../rocketmq.js) TypeScript SDK.
17
+
18
+ ## Setup
19
+
20
+ Requires [uv](https://docs.astral.sh/uv/).
21
+
22
+ ```bash
23
+ uv sync # install deps + editable package
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ```python
29
+ import asyncio
30
+ from rocketmq import connect
31
+ from rocketmq.schema import BaseSchema
32
+
33
+
34
+ class Order(BaseSchema):
35
+ id: str
36
+ customer_id: str
37
+ qty: int
38
+
39
+
40
+ async def main():
41
+ mq = await connect(url="amqp://localhost")
42
+ orders = await mq.queue("orders", Order)
43
+
44
+ await orders.send(Order(id="1", customer_id="c1", qty=5))
45
+ await orders.consume(lambda msg: print(msg))
46
+
47
+ await asyncio.sleep(2)
48
+ await mq.close()
49
+
50
+
51
+ asyncio.run(main())
52
+ ```
53
+
54
+ ## Development
55
+
56
+ ```bash
57
+ make help # show all commands
58
+ make fmt # format (ruff)
59
+ make lint # lint (ruff)
60
+ make typecheck # type-check (pyright)
61
+ make test # run tests (pytest)
62
+ make check # lint + typecheck + test
63
+ make all # format + fix + typecheck + test
64
+ ```
65
+
66
+ ## Architecture
67
+
68
+ ```
69
+ src/rocketmq/
70
+ ├── __init__.py # Public re-exports
71
+ ├── py.typed # PEP 561 marker
72
+ ├── amqp.py # Thin aio-pika wrapper
73
+ ├── client.py # RocketMQ + connect() + QueueHandle
74
+ ├── schema.py # BaseSchema + Proto annotation
75
+ ├── proto.py # Pydantic model -> proto3 string
76
+ ├── serializer.py # Serializer Protocol + JsonSerializer
77
+ ├── errors.py # Error hierarchy
78
+ ├── error_codes.py # BrokerErrorCode enum
79
+ └── error_parser.py # Broker JSON error parsing
80
+ ```
81
+
82
+ ## Cross-Language Type Compatibility
83
+
84
+ Python types map to proto3 types that match the JS SDK:
85
+
86
+ | Python | TypeScript (Zod) | Proto3 |
87
+ |---|---|---|
88
+ | `str` | `z.string()` | `string` |
89
+ | `int` | `z.number()` | `double` |
90
+ | `float` | `z.number()` | `double` |
91
+ | `bool` | `z.boolean()` | `bool` |
92
+ | `bytes` | `z.object(...)` | `bytes` |
93
+ | `datetime` | `z.date()` | `Timestamp` |
94
+ | `Annotated[int, Proto("int32")]` | `z.number().int()` | `int32` |
@@ -0,0 +1,83 @@
1
+ # rocketmq.py
2
+
3
+ Schema-aware AMQP client for the RocketMQ broker - **Pydantic v2 + aio-pika**.
4
+
5
+ Python port of the [`rocketmq.js`](../rocketmq.js) TypeScript SDK.
6
+
7
+ ## Setup
8
+
9
+ Requires [uv](https://docs.astral.sh/uv/).
10
+
11
+ ```bash
12
+ uv sync # install deps + editable package
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```python
18
+ import asyncio
19
+ from rocketmq import connect
20
+ from rocketmq.schema import BaseSchema
21
+
22
+
23
+ class Order(BaseSchema):
24
+ id: str
25
+ customer_id: str
26
+ qty: int
27
+
28
+
29
+ async def main():
30
+ mq = await connect(url="amqp://localhost")
31
+ orders = await mq.queue("orders", Order)
32
+
33
+ await orders.send(Order(id="1", customer_id="c1", qty=5))
34
+ await orders.consume(lambda msg: print(msg))
35
+
36
+ await asyncio.sleep(2)
37
+ await mq.close()
38
+
39
+
40
+ asyncio.run(main())
41
+ ```
42
+
43
+ ## Development
44
+
45
+ ```bash
46
+ make help # show all commands
47
+ make fmt # format (ruff)
48
+ make lint # lint (ruff)
49
+ make typecheck # type-check (pyright)
50
+ make test # run tests (pytest)
51
+ make check # lint + typecheck + test
52
+ make all # format + fix + typecheck + test
53
+ ```
54
+
55
+ ## Architecture
56
+
57
+ ```
58
+ src/rocketmq/
59
+ ├── __init__.py # Public re-exports
60
+ ├── py.typed # PEP 561 marker
61
+ ├── amqp.py # Thin aio-pika wrapper
62
+ ├── client.py # RocketMQ + connect() + QueueHandle
63
+ ├── schema.py # BaseSchema + Proto annotation
64
+ ├── proto.py # Pydantic model -> proto3 string
65
+ ├── serializer.py # Serializer Protocol + JsonSerializer
66
+ ├── errors.py # Error hierarchy
67
+ ├── error_codes.py # BrokerErrorCode enum
68
+ └── error_parser.py # Broker JSON error parsing
69
+ ```
70
+
71
+ ## Cross-Language Type Compatibility
72
+
73
+ Python types map to proto3 types that match the JS SDK:
74
+
75
+ | Python | TypeScript (Zod) | Proto3 |
76
+ |---|---|---|
77
+ | `str` | `z.string()` | `string` |
78
+ | `int` | `z.number()` | `double` |
79
+ | `float` | `z.number()` | `double` |
80
+ | `bool` | `z.boolean()` | `bool` |
81
+ | `bytes` | `z.object(...)` | `bytes` |
82
+ | `datetime` | `z.date()` | `Timestamp` |
83
+ | `Annotated[int, Proto("int32")]` | `z.number().int()` | `int32` |
@@ -0,0 +1,71 @@
1
+ """
2
+ Example: Custom serializer.
3
+
4
+ Demonstrates plugging in a custom serializer (MessagePack in this case)
5
+ while keeping the same schema-aware API.
6
+
7
+ Run::
8
+
9
+ pip install msgpack
10
+ python examples/custom_serializer.py
11
+ """
12
+
13
+ import asyncio
14
+
15
+ from rocketmq import connect
16
+ from rocketmq.schema import BaseSchema
17
+
18
+
19
+ class MsgpackSerializer:
20
+ """MessagePack serializer — drop-in replacement for JsonSerializer.
21
+
22
+ Usage::
23
+
24
+ mq = await connect(url="amqp://localhost", serializer=MsgpackSerializer())
25
+ """
26
+
27
+ @property
28
+ def content_type(self) -> str:
29
+ return "application/x-msgpack"
30
+
31
+ def serialize(self, value: object) -> bytes:
32
+ from typing import cast
33
+
34
+ import msgpack
35
+
36
+ # WHY: packb is typed as -> bytes | None in stubs, but never
37
+ # returns None for dict/list inputs.
38
+ return cast("bytes", msgpack.packb(value, use_bin_type=True))
39
+
40
+ def deserialize(self, data: bytes) -> object:
41
+ import msgpack
42
+
43
+ return msgpack.unpackb(data, raw=False)
44
+
45
+
46
+ class SensorReading(BaseSchema):
47
+ sensor_id: str
48
+ temperature: float
49
+ humidity: float
50
+
51
+
52
+ async def main() -> None:
53
+ mq = await connect(url="amqp://localhost", serializer=MsgpackSerializer())
54
+ readings = await mq.queue("sensor-readings", SensorReading)
55
+
56
+ await readings.send(
57
+ SensorReading(
58
+ sensor_id="s-42",
59
+ temperature=23.5,
60
+ humidity=61.2,
61
+ )
62
+ )
63
+
64
+ _ = await readings.consume(lambda msg: print(f"Sensor {msg.sensor_id}: {msg.temperature}°C"))
65
+
66
+ await asyncio.sleep(2)
67
+ await mq.close()
68
+
69
+
70
+ if __name__ == "__main__":
71
+ asyncio.run(main())
@@ -0,0 +1,64 @@
1
+ """
2
+ Example: Exchange routing with Pydantic schemas.
3
+
4
+ Demonstrates declaring an exchange, binding queues with routing keys,
5
+ and publishing messages that route to specific consumers.
6
+
7
+ Run::
8
+
9
+ python examples/exchange_routing.py
10
+ """
11
+
12
+ import asyncio
13
+
14
+ from rocketmq import connect
15
+ from rocketmq.schema import BaseSchema
16
+
17
+
18
+ class OrderEvent(BaseSchema):
19
+ order_id: str
20
+ action: str
21
+ amount: float
22
+
23
+
24
+ async def main() -> None:
25
+ mq = await connect(url="amqp://localhost")
26
+
27
+ # Declare exchange + queues
28
+ await mq.assert_exchange("orders.topic", "direct")
29
+ await mq.assert_queue("orders.created", OrderEvent)
30
+ await mq.assert_queue("orders.cancelled", OrderEvent)
31
+ await mq.bind_queue("orders.created", "orders.topic", "created")
32
+ await mq.bind_queue("orders.cancelled", "orders.topic", "cancelled")
33
+
34
+ # Publish to different routing keys
35
+ await mq.publish(
36
+ "orders.topic",
37
+ "created",
38
+ {
39
+ "order_id": "ord-001",
40
+ "action": "created",
41
+ "amount": 99.90,
42
+ },
43
+ )
44
+
45
+ await mq.publish(
46
+ "orders.topic",
47
+ "cancelled",
48
+ {
49
+ "order_id": "ord-002",
50
+ "action": "cancelled",
51
+ "amount": 45.00,
52
+ },
53
+ )
54
+
55
+ # Consume from specific queues
56
+ _ = await mq.consume("orders.created", OrderEvent, lambda msg: print(f"NEW: {msg}"))
57
+ _ = await mq.consume("orders.cancelled", OrderEvent, lambda msg: print(f"CANCEL: {msg}"))
58
+
59
+ await asyncio.sleep(2)
60
+ await mq.close()
61
+
62
+
63
+ if __name__ == "__main__":
64
+ asyncio.run(main())
@@ -0,0 +1,51 @@
1
+ """
2
+ Example: Pydantic model schema — publish + consume.
3
+
4
+ Demonstrates defining a message schema as a Pydantic model, declaring
5
+ a queue with schema metadata, publishing a typed message, and consuming
6
+ with automatic deserialization.
7
+
8
+ Run::
9
+
10
+ python examples/publisher.py
11
+ """
12
+
13
+ import asyncio
14
+ from typing import Annotated
15
+
16
+ from rocketmq import connect
17
+ from rocketmq.schema import BaseSchema, Proto
18
+
19
+
20
+ class Notification(BaseSchema):
21
+ id: Annotated[int, Proto("int32")]
22
+ content: str
23
+ timestamp: float
24
+
25
+
26
+ async def main() -> None:
27
+ mq = await connect(url="amqp://localhost")
28
+ queue = await mq.queue("pending-notifications", Notification)
29
+
30
+ message = Notification(
31
+ id=1,
32
+ content="Hello from Python SDK",
33
+ timestamp=1717520000.0,
34
+ )
35
+
36
+ # Publish
37
+ await queue.send(message)
38
+
39
+ # Consume
40
+ async def on_message(msg: Notification) -> None:
41
+ print(f"Received: id={msg.id} content={msg.content}")
42
+
43
+ _ = await queue.consume(on_message)
44
+
45
+ # Keep alive to let messages arrive, then close.
46
+ await asyncio.sleep(2)
47
+ await mq.close()
48
+
49
+
50
+ if __name__ == "__main__":
51
+ asyncio.run(main())
@@ -0,0 +1,85 @@
1
+ [project]
2
+ name = "rocketmq-sdk"
3
+ version = "0.1.0"
4
+ description = "Schema-aware AMQP client for the RocketMQ broker — Pydantic + aio-pika."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ dependencies = [
9
+ "aio-pika>=9.0,<10.0",
10
+ "pydantic>=2.0,<3.0",
11
+ ]
12
+
13
+ [build-system]
14
+ requires = ["hatchling"]
15
+ build-backend = "hatchling.build"
16
+
17
+ [tool.hatch.build.targets.wheel]
18
+ packages = ["src/rocketmq"]
19
+
20
+ # --------------------------------------------------------------------------
21
+ # ruff — lint + format (replaces flake8, isort, black, pyupgrade)
22
+ # --------------------------------------------------------------------------
23
+ [tool.ruff]
24
+ target-version = "py311"
25
+ line-length = 100
26
+ src = ["src", "tests"]
27
+
28
+ [tool.ruff.lint]
29
+ select = [
30
+ "E", # pycodestyle errors
31
+ "W", # pycodestyle warnings
32
+ "F", # pyflakes
33
+ "I", # isort
34
+ "UP", # pyupgrade
35
+ "B", # flake8-bugbear
36
+ "SIM", # flake8-simplify
37
+ "RUF", # ruff-specific rules
38
+ "TCH", # type-checking imports
39
+ "PT", # flake8-pytest-style
40
+ "PIE", # flake8-pie
41
+ "C4", # flake8-comprehensions
42
+ "T20", # flake8-print (catch stray prints)
43
+ ]
44
+ ignore = [
45
+ "T201", # allow print() in examples
46
+ "UP007", # allow Union[X, Y] alongside X | Y in runtime contexts
47
+ ]
48
+
49
+ [tool.ruff.lint.isort]
50
+ known-first-party = ["rocketmq"]
51
+ force-single-line = false
52
+ lines-after-imports = 2
53
+
54
+ [tool.ruff.lint.per-file-ignores]
55
+ "tests/**" = ["T201"] # allow print() in tests
56
+ "examples/**" = ["T201"] # allow print() in examples
57
+
58
+ # --------------------------------------------------------------------------
59
+ # pyright — static type checking
60
+ # --------------------------------------------------------------------------
61
+ [tool.pyright]
62
+ pythonVersion = "3.11"
63
+ venvPath = "."
64
+ venv = ".venv"
65
+ typeCheckingMode = "standard"
66
+ include = ["src", "tests", "examples"]
67
+ reportMissingTypeStubs = false
68
+
69
+ # --------------------------------------------------------------------------
70
+ # pytest
71
+ # --------------------------------------------------------------------------
72
+ [tool.pytest.ini_options]
73
+ asyncio_mode = "auto"
74
+ testpaths = ["tests"]
75
+
76
+ [dependency-groups]
77
+ dev = [
78
+ "pytest>=8.0",
79
+ "pytest-asyncio>=0.24",
80
+ "pyright>=1.1.410",
81
+ "ruff>=0.15.16",
82
+ ]
83
+ examples = [
84
+ "msgpack>=1.0,<2.0",
85
+ ]