Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
f424ef8
Add nodejs and gh commands
Aug 21, 2025
ffe3d24
Drop python version 3.9
Aug 21, 2025
f6bc314
Add more protocol names
Aug 22, 2025
8718972
Create document dataclasses
Aug 22, 2025
af0b477
Add transport abstractions
Aug 22, 2025
2f827a5
Move generator and plugin to python 3.10
Aug 22, 2025
c8b9750
Move abstract transport factory and update its contracts
Aug 22, 2025
b188e4a
rename method from start_recv to recv
Aug 22, 2025
ab0a2c8
Update transport abstractions
Aug 22, 2025
96e4d52
Checkpoint
Aug 22, 2025
03b3808
Refactor module structure
Aug 22, 2025
44cd19a
Make all document parts frozen
WiZeYAR Aug 23, 2025
ddc18af
Update wire
WiZeYAR Aug 25, 2025
45be072
Drop codec defs
WiZeYAR Aug 25, 2025
dcdcbae
Update codec interface definitions
WiZeYAR Aug 26, 2025
91b4989
Implement JSON <-> BaseModel codec
WiZeYAR Aug 26, 2025
ed57afe
Add basic implementation of Application
WiZeYAR Aug 26, 2025
b7588a7
Fix codec protocol
WiZeYAR Aug 26, 2025
7daade2
Set headers to be untyped
WiZeYAR Aug 26, 2025
c532b35
Refactor: create global typevars
WiZeYAR Aug 26, 2025
0432a88
Parametrize handler params and create basic subscriber
WiZeYAR Aug 26, 2025
02375ca
Drop output generics for subscriber
WiZeYAR Aug 26, 2025
3c59712
Drop old tests
WiZeYAR Aug 26, 2025
87ba6db
Update typing
WiZeYAR Aug 26, 2025
ce8ae86
Base implementation for in-memory wire
WiZeYAR Aug 26, 2025
7cd27ce
Drop codegen tests
WiZeYAR Aug 26, 2025
6e386dd
Add json codec + subscriber + tests
WiZeYAR Sep 1, 2025
5c6b180
Update deps
WiZeYAR Sep 1, 2025
25421ea
Fix mypy
WiZeYAR Sep 1, 2025
b7b1121
Add amqp wire
WiZeYAR Sep 1, 2025
99886c7
Add more tests
WiZeYAR Sep 1, 2025
6fda1dc
Add aio-pika dep to amqp group
WiZeYAR Sep 1, 2025
c5b06e5
Drop all unit tests
WiZeYAR Sep 1, 2025
7552fff
Update tests
WiZeYAR Sep 1, 2025
cae9656
Fix json codec
WiZeYAR Sep 1, 2025
2289a54
Refactor wire onto multiple files
WiZeYAR Sep 2, 2025
6542bf6
Update service on actions side
WiZeYAR Sep 2, 2025
7907b94
Update workflow
WiZeYAR Sep 2, 2025
cabb419
Update workflow
WiZeYAR Sep 2, 2025
5359ef7
WIP rpc
WiZeYAR Sep 2, 2025
ad062f7
Add more scenarios
WiZeYAR Sep 2, 2025
da972ff
Lint some stuff
WiZeYAR Sep 2, 2025
a9bab4c
Drop legacy endpoints
WiZeYAR Sep 2, 2025
7c59a93
Fix handler type
WiZeYAR Sep 3, 2025
ffbbd4c
Drop old asyncapi_python_codegen
WiZeYAR Sep 3, 2025
8e619a8
Add parser base code
WiZeYAR Sep 4, 2025
6d154b2
Add some codegen testing
WiZeYAR Sep 4, 2025
4c2aff1
Make pubsub example work
WiZeYAR Sep 4, 2025
4bbafb6
Move to codec registry
WiZeYAR Sep 4, 2025
bb5d7c4
Fix tests; reformat
WiZeYAR Sep 4, 2025
20e4198
Update deps
WiZeYAR Sep 4, 2025
310c873
Move to relative install to keep up with the version
WiZeYAR Sep 4, 2025
f5f2bc2
Update release rules
WiZeYAR Sep 4, 2025
ace72b3
Rename WireFactory to be just Wire
WiZeYAR Sep 4, 2025
8622b2e
Update examples
WiZeYAR Sep 4, 2025
4d27404
Update tests
WiZeYAR Sep 4, 2025
eb552f9
Fix test
WiZeYAR Sep 4, 2025
9e29b2a
Drop example test
WiZeYAR Sep 4, 2025
0048dea
Make more complex routing
WiZeYAR Sep 4, 2025
2d40453
Split generation process
WiZeYAR Sep 4, 2025
6273303
Add complex spec
WiZeYAR Sep 4, 2025
9134065
Refactor codegen with SRP, add AMQP bindings, integrate datamodel-cod…
WiZeYAR Sep 4, 2025
887555c
Drop circular dependency
WiZeYAR Sep 4, 2025
f7b5e61
Fix AMQP binding type safety, add union types, and resolve codegen is…
WiZeYAR Sep 5, 2025
250ca43
Drop comment from template
WiZeYAR Sep 5, 2025
9838c5b
Amqp improvements
WiZeYAR Sep 5, 2025
8a9db8e
Fix pub/sub specs
WiZeYAR Sep 5, 2025
0d600c3
Fix work queue spec
WiZeYAR Sep 5, 2025
d05304a
Drop redundant release tag
WiZeYAR Sep 6, 2025
6c25c23
Make robust connection fail fast
WiZeYAR Sep 6, 2025
c5b03b5
Consumers now have exactly one handler
WiZeYAR Sep 6, 2025
56e0eb1
Refactor tests
WiZeYAR Sep 6, 2025
d30b7a4
Set integration test timeout
WiZeYAR Sep 6, 2025
25c1f0b
Add endpoint parameters interface
WiZeYAR Sep 6, 2025
09e53be
Set start method of Application to be optionally blocking
WiZeYAR Sep 6, 2025
47ad541
Implement interruption on unhandled error
WiZeYAR Sep 6, 2025
53d491a
Add exception handling tests
WiZeYAR Sep 6, 2025
18e65c4
Implement batch processing
WiZeYAR Sep 7, 2025
4aac83e
Update devcontainer to use pyright
WiZeYAR Sep 7, 2025
ea907c9
Update trading example spec
WiZeYAR Sep 7, 2025
e455bfd
Fix pyright in core module
WiZeYAR Sep 7, 2025
64f0e22
Ignore pyright for pants
WiZeYAR Sep 7, 2025
67cfee9
Drop old codegen
WiZeYAR Sep 7, 2025
256b7e9
Fix codegen errors
WiZeYAR Sep 7, 2025
e99f390
Black everything
WiZeYAR Sep 7, 2025
cbecc58
Add more validation
WiZeYAR Sep 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ RUN DEBIAN_FRONTEND=noninteractive \
# Python and uv installation
USER $USER
ARG HOME="/home/$USER"
ARG PYTHON_VERSION=3.9
# ARG PYTHON_VERSION=3.10
ARG PYTHON_VERSION=3.10

ENV PYENV_ROOT="${HOME}/.pyenv"
ENV PATH="${PYENV_ROOT}/shims:${PYENV_ROOT}/bin:${HOME}/.local/bin:$PATH"
Expand Down
29 changes: 27 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
"rabbitmq:15672"
],
"containerEnv": {
"AMQP_URI": "amqp://guest:guest@rabbitmq/"
"AMQP_URI": "amqp://guest:guest@rabbitmq/",
"PYTEST_AMQP_URI": "amqp://guest:guest@rabbitmq/"
},
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-pyright.pyright",
"njpwerner.autodocstring",
"tamasfe.even-better-toml",
"ms-python.mypy-type-checker",
Expand All @@ -26,6 +28,21 @@
],
"settings": {
"python.defaultInterpreterPath": "/workspaces/${localWorkspaceFolderBasename}/.venv/bin/python",
"python.analysis.typeCheckingMode": "strict",
"python.analysis.autoImportCompletions": true,
"python.analysis.diagnosticMode": "workspace",
"python.analysis.autoSearchPaths": true,
"python.analysis.extraPaths": [
"./src"
],
"python.analysis.include": [
"src/**",
"*.py"
],
"python.analysis.stubPath": "./src",
"python.linting.enabled": true,
"python.linting.pylintEnabled": false,
"python.linting.mypyEnabled": false,
"yaml.schemas": {
"https://asyncapi.com/schema-store/3.0.0-without-$id.json": [
"file:///workspaces/asyncapi-python/examples/*.yaml"
Expand All @@ -40,9 +57,17 @@
"amqp",
"venv",
"jsonschema",
"fanout"
"fanout",
"anypointmq",
"googlepubsub",
"ibmmq",
"mqtt"
]
}
}
},
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/node:1": {}
}
}
7 changes: 4 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ permissions:

on:
push:
tags: "v*.*.*"
tags:
- "v*.*.*"

jobs:
test:
Expand All @@ -25,7 +26,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.9"
python-version: "3.10"

- name: Install UV
uses: astral-sh/setup-uv@v3
Expand Down Expand Up @@ -56,7 +57,7 @@ jobs:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
draft: true
prerelease: false
prerelease: ${{ contains(github.ref, 'rc') }}

- name: Build sdist, wheel, and pex
run: >
Expand Down
61 changes: 58 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,67 @@ on:
workflow_call:

jobs:
black:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Install UV
uses: astral-sh/setup-uv@v3
with:
enable-cache: true

- name: Install dependencies
run: uv sync --all-extras

- name: Check code formatting with Black
run: uv run black --check --diff --color src/ tests/

pyright:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Install UV
uses: astral-sh/setup-uv@v3
with:
enable-cache: true

- name: Install dependencies
run: uv sync --all-extras

- name: Run PyRight type checking
run: |
uv run pyright src/asyncapi_python
uv run pyright src/asyncapi_python_codegen

test:
runs-on: ubuntu-latest

strategy:
matrix:
python_version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
python_version: ["3.10", "3.11", "3.12", "3.13"]

services:
rabbitmq:
image: rabbitmq:3.13.6
ports: ["5672"]
ports:
- 5672:5672
options: >-
--health-cmd "rabbitmq-diagnostics -q ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- uses: actions/checkout@v4
Expand All @@ -37,7 +87,12 @@ jobs:
- name: Install dependencies
run: uv sync --all-extras

- name: Wait for RabbitMQ to be ready
run: |
timeout 60s bash -c 'until nc -z localhost 5672; do sleep 1; done'
sleep 5 # Additional wait to ensure RabbitMQ is fully initialized

- name: Run tests
env:
AMQP_URI: amqp://localhost:${{ job.services.rabbitmq.ports[5672] }}
PYTEST_AMQP_URI: amqp://guest:guest@localhost:5672/
run: uv run pytest
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Easily generate type-safe and async Python applications from AsyncAPI 3 specific

## Requirements

- `python>=3.9`
- `python>=3.10`
- `pydantic>=2`
- `pytz`
- For `codegen` extra
Expand Down
4 changes: 4 additions & 0 deletions examples/amqp-pub-sub/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
# Generated code directories
publisher/
subscriber/

# Virtual environment
.venv/
8 changes: 4 additions & 4 deletions examples/amqp-pub-sub/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ venv:
python3 -m venv $(VENV_NAME)

install:
$(PIP) install asyncapi-python[amqp,codegen]==$(PACKAGE_VERSION)
$(PIP) install -e ../../[amqp,codegen]

generate:
$(CODEGEN) spec/subscriber.asyncapi.yaml subscriber --force
$(CODEGEN) spec/publisher.asyncapi.yaml publisher --force

client:
$(PYTHON) main-subscriber.py
$(PYTHON) main-publisher.py

server:
$(PYTHON) main-publisher.py
$(PYTHON) main-subscriber.py

clean:
rm -rf $(VENV_NAME)

.PHONY: client server
.PHONY: client server
10 changes: 6 additions & 4 deletions examples/amqp-pub-sub/main-publisher.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import asyncio
from os import environ
from publisher import Application
from publisher.messages import Ping
from publisher.messages.json import Ping
from asyncapi_python.contrib.wire.amqp import AmqpWire


AMQP_URI = environ.get("AMQP_URI", "amqp://guest:guest@localhost")
NUM_REQUESTS = 3

app = Application(AMQP_URI)
app = Application(AmqpWire(AMQP_URI))


async def main() -> None:
await app.start(blocking=False)
await app.start()
for _ in range(NUM_REQUESTS):
req = Ping()
print(f"Sending request: {req}")
await app.producer.application.ping(req)
await app.producer.application_ping(req)
await app.stop()


if __name__ == "__main__":
Expand Down
14 changes: 8 additions & 6 deletions examples/amqp-pub-sub/main-subscriber.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@
from os import environ
from sys import exit
from subscriber import Application
from subscriber.messages import Ping
from subscriber.messages.json import Ping
from asyncapi_python.contrib.wire.amqp import AmqpWire


AMQP_URI = environ.get("AMQP_URI", "amqp://guest:guest@localhost")
MAX_REQUESTS = 3
request_count = 0

app = Application(AMQP_URI)
app = Application(AmqpWire(AMQP_URI))


@app.consumer.application.ping
@app.consumer.application_ping
async def handle_ping_request(msg: Ping) -> None:
global request_count
print(f"Handling request: {msg}")
Expand All @@ -24,13 +25,14 @@ async def termination_handler():
while True:
await asyncio.sleep(1)
if request_count >= MAX_REQUESTS:
await app.stop()
exit(0)


async def main() -> None:
app_handler = app.start(blocking=True)
term_handler = termination_handler()
await asyncio.gather(app_handler, term_handler)
await app.start()
# Keep running until termination_handler exits
await termination_handler()


if __name__ == "__main__":
Expand Down
4 changes: 4 additions & 0 deletions examples/amqp-rpc/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
# Generated code directories
client/
server/

# Virtual environment
.venv/
4 changes: 2 additions & 2 deletions examples/amqp-rpc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ venv:
python3 -m venv $(VENV_NAME)

install:
$(PIP) install asyncapi-python[amqp,codegen]==$(PACKAGE_VERSION)
$(PIP) install -e ../../[amqp,codegen]

generate:
$(CODEGEN) spec/client.asyncapi.yaml client --force
Expand All @@ -24,4 +24,4 @@ server:
clean:
rm -rf $(VENV_NAME)

.PHONY: client server
.PHONY: client server
10 changes: 6 additions & 4 deletions examples/amqp-rpc/main-client.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import asyncio
from os import environ
from client import Application
from client.messages import Ping, Pong
from client.messages.json import Ping, Pong
from asyncapi_python.contrib.wire.amqp import AmqpWire


AMQP_URI = environ.get("AMQP_URI", "amqp://guest:guest@localhost")
NUM_REQUESTS = 3

app = Application(AMQP_URI)
app = Application(AmqpWire(AMQP_URI))


async def main() -> None:
await app.start(blocking=False)
await app.start()
for _ in range(NUM_REQUESTS):
req = Ping()
print(f"Sending request: {req}")
res: Pong = await app.producer.ping_request(req)
res: Pong = await app.producer.pingrequest(req)
print(f"Got response: {res}")
await app.stop()


if __name__ == "__main__":
Expand Down
14 changes: 8 additions & 6 deletions examples/amqp-rpc/main-server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@
from os import environ
from sys import exit
from server import Application
from server.messages import Ping, Pong
from server.messages.json import Ping, Pong
from asyncapi_python.contrib.wire.amqp import AmqpWire


AMQP_URI = environ.get("AMQP_URI", "amqp://guest:guest@localhost")
MAX_REQUESTS = 3
request_count = 0

app = Application(AMQP_URI)
app = Application(AmqpWire(AMQP_URI))


@app.consumer.on_ping_request
@app.consumer.onpingrequest
async def handle_ping_request(msg: Ping) -> Pong:
global request_count
print(f"Handling request: {msg}")
Expand All @@ -27,13 +28,14 @@ async def termination_handler():
while True:
await asyncio.sleep(1)
if request_count >= MAX_REQUESTS:
await app.stop()
exit(0)


async def main() -> None:
app_handler = app.start(blocking=True)
term_handler = termination_handler()
await asyncio.gather(app_handler, term_handler)
await app.start()
# Keep running until termination_handler exits
await termination_handler()


if __name__ == "__main__":
Expand Down
6 changes: 6 additions & 0 deletions examples/amqp-work-queue/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Generated code directories
producer/
worker/

# Virtual environment
.venv/
Loading
Loading