Skip to content

Commit 859ff9e

Browse files
authored
Router feature (and some general improvements) (#8)
* Add endpoint code * Add operation class * Update connection file * Update utils * Add path property to the operation * Rewrite codegen * Update base classes * Start building pubsub example * Add blocking to the client * Fix escaped refs * Add default amqp_uri into devcontainer * Fix codegen types * Fix start/stop cycle * Fix routingKey mode (anonymous queues) * Add missing ack on consumer side * Fix error in template * Make sender do declarations * Update RPC example to match new API * Fix generation of queue names * Drop declared queue if it is exclusive to sender * Add a possibility to stop an application from Router or Endpoint * Implement stop method for both sender and receiver * Drop outdated library tests * Outsource correlation id generation from RpcSender to BaseApplication * Drop unused import * Add operation struct tests * Add endpoint tests * Standardize message generation * Add copyright notice * Fix tests * Update README * Update minor version * Add error_queue_name property to EndpointParams * Move create_message from endpoint params to endpoint * Move errors do separate module * Add rejection logic for receivers * Create codebase for error queue handling * Drop redundant variable * Rename Reject to Rejection * Add debug mode for operation * Add debug mode for operation * Add rejection tests for endpoints * Add copyright notice to files * Update README * Allow refs in Channels (according to spec) * Add support for python 3.13
1 parent 99e9db0 commit 859ff9e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1475
-949
lines changed

.devcontainer/devcontainer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
"forwardPorts": [
77
"rabbitmq:15672"
88
],
9+
"containerEnv": {
10+
"AMQP_URI": "amqp://guest:guest@rabbitmq/"
11+
},
912
"customizations": {
1013
"vscode": {
1114
"extensions": [

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414

1515
strategy:
1616
matrix:
17-
python_version: ["3.9", "3.10", "3.11", "3.12"]
17+
python_version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
1818

1919
services:
2020
rabbitmq:

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# AsyncAPI Python Code Generator
2+
>
23
> [!IMPORTANT]
3-
> Although commits to dev branch might seem infrequent, the project is under active development **as of February 2025**.
4+
> Although commits to dev branch might seem infrequent, the project is under active development **as of March 2025**.
45
>
56
> We currently produce only those changes that are required to satisfy our personal use cases.
6-
>
7+
>
78
> The number of commits will grow as we see increase in popularity of it, so do not hesitate
89
> to star this repo, open issues, and pull requests.
910
@@ -27,7 +28,7 @@ Easily generate type-safe and async Python applications from AsyncAPI 3 specific
2728

2829
## Requirements
2930

30-
- `python>=3.10`
31+
- `python>=3.9`
3132
- `pydantic>=2`
3233
- `pytz`
3334
- For `codegen` extra

examples/amqp-pub-sub/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
publisher/
2+
subscriber/

examples/amqp-pub-sub/Makefile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
VENV_NAME := .venv
2+
PYTHON := $(VENV_NAME)/bin/python
3+
CODEGEN := $(VENV_NAME)/bin/asyncapi-python-codegen
4+
PIP := $(VENV_NAME)/bin/pip
5+
PACKAGE_VERSION := 0.1.0
6+
7+
8+
venv:
9+
python3 -m venv $(VENV_NAME)
10+
11+
install:
12+
$(PIP) install asyncapi-python[amqp,codegen]==$(PACKAGE_VERSION)
13+
14+
generate:
15+
$(CODEGEN) spec/subscriber.asyncapi.yaml subscriber --force
16+
$(CODEGEN) spec/publisher.asyncapi.yaml publisher --force
17+
18+
client:
19+
$(PYTHON) main-subscriber.py
20+
21+
server:
22+
$(PYTHON) main-publisher.py
23+
24+
clean:
25+
rm -rf $(VENV_NAME)
26+
27+
.PHONY: client server

examples/amqp-pub-sub/README.md

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,42 @@
1-
# Asyncapi-Python Pub-Sub Example
1+
# Asyncapi-Python AMQP Pub-Sub Example
22

3-
This example is a work in progress
3+
The following example shows how to use `asyncapi-python` package to create publisher-subscriber communications.
4+
5+
## Requirements
6+
7+
1. A working `amqp` broker
8+
9+
> This example assumes that it will be accessible by `amqp://guest:guest@localhost`.
10+
> If this is not true, set `AMQP_URI` envvar accordingly
11+
12+
## Steps
13+
14+
1. `cd` into this directory
15+
16+
1. Create python virtual environment and activate it.
17+
18+
Alternatively, run `make venv install`.
19+
20+
```bash
21+
python3 -m venv .venv
22+
. .venv/bin/activate
23+
```
24+
25+
1. Generate client and server modules.
26+
27+
Alternatively, run `make generate`.
28+
29+
```bash
30+
asyncapi-python-codegen spec/publisher.asyncapi.yaml publisher --force
31+
asyncapi-python-codegen spec/subscriber.asyncapi.yaml subscriber --force
32+
```
33+
34+
1. Run subscriber, and then publisher. This requires two terminals.
35+
36+
```bash
37+
.venv/bin/python main-subscriber.py
38+
```
39+
40+
```bash
41+
.venv/bin/python main-publisher.py
42+
```
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import asyncio
2+
from os import environ
3+
from publisher import Application
4+
from publisher.messages import Ping
5+
6+
7+
AMQP_URI = environ.get("AMQP_URI", "amqp://guest:guest@localhost")
8+
NUM_REQUESTS = 3
9+
10+
app = Application(AMQP_URI)
11+
12+
13+
async def main() -> None:
14+
await app.start(blocking=False)
15+
for _ in range(NUM_REQUESTS):
16+
req = Ping()
17+
print(f"Sending request: {req}")
18+
await app.producer.application.ping(req)
19+
20+
21+
if __name__ == "__main__":
22+
asyncio.run(main())
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import asyncio
2+
from os import environ
3+
from sys import exit
4+
from subscriber import Application
5+
from subscriber.messages import Ping
6+
7+
8+
AMQP_URI = environ.get("AMQP_URI", "amqp://guest:guest@localhost")
9+
MAX_REQUESTS = 3
10+
request_count = 0
11+
12+
app = Application(AMQP_URI)
13+
14+
15+
@app.consumer.application.ping
16+
async def handle_ping_request(msg: Ping) -> None:
17+
global request_count
18+
print(f"Handling request: {msg}")
19+
request_count += 1
20+
21+
22+
async def termination_handler():
23+
"""A function to terminate the app after all requests are handled"""
24+
while True:
25+
await asyncio.sleep(1)
26+
if request_count >= MAX_REQUESTS:
27+
exit(0)
28+
29+
30+
async def main() -> None:
31+
app_handler = app.start(blocking=True)
32+
term_handler = termination_handler()
33+
await asyncio.gather(app_handler, term_handler)
34+
35+
36+
if __name__ == "__main__":
37+
asyncio.run(main())
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
asyncapi: "3.0.0"
2+
info:
3+
title: Common part of the Ping service example
4+
version: 1.0.0
5+
6+
channels:
7+
/ping/pubsub:
8+
bindings:
9+
amqp:
10+
exchange:
11+
type: fanout
12+
messages:
13+
Ping:
14+
payload:
15+
type: object
16+
properties:
17+
event:
18+
type: string
19+
const: ping
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
asyncapi: "3.0.0"
2+
info:
3+
title: Client part of the Ping service example
4+
version: 1.0.0
5+
6+
operations:
7+
/application/ping:
8+
action: send
9+
channel: { $ref: "./common.asyncapi.yaml#/channels/~1ping~1pubsub" }

0 commit comments

Comments
 (0)