mq-bridge-py-basic 0.2.19__tar.gz → 0.2.20__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.
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/.github/workflows/release.yml +15 -4
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/Cargo.lock +30 -32
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/Cargo.toml +4 -3
- mq_bridge_py_basic-0.2.20/PKG-INFO +191 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/README.md +6 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/benches/performance_bench.rs +1 -1
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/mq-bridge.schema.json +102 -7
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/mq_bridge/__init__.py +4 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/mq_bridge/__init__.pyi +40 -2
- mq_bridge_py_basic-0.2.20/mq_bridge/config.py +98 -0
- mq_bridge_py_basic-0.2.20/mq_bridge/config.pyi +394 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/pyproject.toml +5 -3
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/Cargo.toml +2 -1
- mq_bridge_py_basic-0.2.20/python/mq-bridge-py/README.md +167 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/example.py +14 -13
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/examples/raw_route.py +0 -1
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/pyproject-basic.toml +5 -3
- mq_bridge_py_basic-0.2.20/python/mq-bridge-py/scripts/gen_config_types.py +248 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/src/lib.rs +216 -41
- mq_bridge_py_basic-0.2.20/python/mq-bridge-py/tests/test_config_types.py +45 -0
- mq_bridge_py_basic-0.2.20/python/mq-bridge-py/tests/test_packaging.py +49 -0
- mq_bridge_py_basic-0.2.20/python/mq-bridge-py/tests/test_routes.py +143 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/uv.lock +100 -1369
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/httparena/frameworks/mq-bridge-py/Dockerfile +1 -1
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/httparena/frameworks/mq-bridge-py/meta.json +7 -2
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/httparena/frameworks/mq-bridge-py/server.py +191 -32
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/grpc.rs +103 -32
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/http.rs +3 -1
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/mod.rs +113 -0
- mq_bridge_py_basic-0.2.20/src/endpoints/websocket.rs +783 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/middleware/buffer.rs +296 -2
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/middleware/mod.rs +2 -5
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/models.rs +77 -3
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/test_utils.rs +45 -11
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/websocket.rs +1 -1
- mq_bridge_py_basic-0.2.20/tests/websocket_test.rs +356 -0
- mq_bridge_py_basic-0.2.19/PKG-INFO +0 -110
- mq_bridge_py_basic-0.2.19/python/mq-bridge-py/README.md +0 -86
- mq_bridge_py_basic-0.2.19/src/endpoints/websocket.rs +0 -459
- mq_bridge_py_basic-0.2.19/tests/websocket_test.rs +0 -118
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/.github/workflows/benchmark.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/.github/workflows/ci.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/.github/workflows/ibm-mq.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/.github/workflows/publish-python.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/.github/workflows/python.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/.github/workflows/test-matrix.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/.gitignore +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/ARCHITECTURE.md +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/CONFIGURATION.md +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/CONTRIBUTING.md +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/LICENSE +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/LICENSE.python +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/benches/router_bench.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/build.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/claude.md +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/examples/ipc_worker_queue.yaml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/mq_bridge/py.typed +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/LICENSE.python +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/analysis/bench_http_native.py +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/examples/bench_memory.py +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/examples/bench_memory.yaml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/examples/json_route.py +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/examples/memory.yaml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/tests/test_performance_smoke.py +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/python/mq-bridge-py/tests/test_public_api.py +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/analysis/HTTP_STREAM_ENDPOINT_PLAN.md +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/analysis/benches/metadata_bench.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/analysis/debug_ibm_mq_tls.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/analysis/http/analyze_xctrace_time_profile.py +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/analysis/http/http_flamegraph.py +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/analysis/http/mq_bridge_http_profile.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/analysis/http/run_http_ladder.sh +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/httparena/README.md +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/techempower/Python/mq-bridge-py/README.md +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/techempower/Python/mq-bridge-py/benchmark_config.json +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/techempower/Python/mq-bridge-py/mq-bridge-py.dockerfile +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/techempower/Python/mq-bridge-py/server.py +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/techempower/README.md +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/techempower/postgres.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/techempower/seed.sql +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/techempower/verify.sh +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/the-benchmarker/README.md +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/the-benchmarker/python/mq-bridge-py/config.yaml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/the-benchmarker/python/mq-bridge-py/pyproject.toml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/scripts/the-benchmarker/python/mq-bridge-py/server.py +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/canonical_message.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/command_handler.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/amqp.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/aws.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/fanout.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/file.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/grpc.proto +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/http_stream.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/ibm_mq.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/kafka.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/memory/endpoint.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/memory/ipc_unix.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/memory/ipc_windows.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/memory/memory_transport.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/memory/mod.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/memory/transport.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/mongodb.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/mqtt.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/nats.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/null.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/reader.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/response.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/sled.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/sqlx.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/static_endpoint.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/stream_buffer.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/switch.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/endpoints/zeromq.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/errors.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/event_handler.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/event_store.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/extensions.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/lib.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/middleware/cookie_jar.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/middleware/deduplication.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/middleware/delay.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/middleware/dlq.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/middleware/limiter.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/middleware/metrics.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/middleware/random_panic.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/middleware/retry.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/middleware/weak_join.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/outcomes.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/publisher.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/response.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/route.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/traits.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/src/type_handler.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/README.md +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/armature_integration.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/amqp.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/aws.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/config.toml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/amqp.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/aws.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/ibm-mq-certs/client.crl +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/ibm-mq-certs/client.kdb +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/ibm-mq-certs/client.p12 +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/ibm-mq-certs/client.rdb +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/ibm-mq-certs/client.sth +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/ibm_mq.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/ibm_mq_tls.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/kafka-tls.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/kafka.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/mariadb.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/mongodb-replica.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/mongodb-tls.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/mongodb.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/mosquitto.conf +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/mosquitto_performance.conf +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/mosquitto_persistence.conf +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/mq_init.mqsc +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/mq_init_tls.mqsc +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/mqtt.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/mqtt_performance.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/mysql.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/nats.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/docker-compose/postgres.yml +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/file.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/grpc.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/grpc_tls.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/http.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/http_tls.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/ibm_mq.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/ibm_mq_tls.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/ipc.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/kafka.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/kafka_tls.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/logic_test.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/mariadb.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/memory.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/mod.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/mongodb.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/mongodb_raw.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/mongodb_tls.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/mqtt.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/mysql.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/nats.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/performance_static.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/postgres.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/route.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/scripts/gen_certs.sh +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/sqlite.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/tls_helpers.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration/zeromq.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/integration_test.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/ref_test.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/sqlite_test.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/tls_example.rs +0 -0
- {mq_bridge_py_basic-0.2.19 → mq_bridge_py_basic-0.2.20}/tests/unit_tests.md +0 -0
|
@@ -50,13 +50,24 @@ jobs:
|
|
|
50
50
|
runs-on: ubuntu-latest
|
|
51
51
|
needs: release
|
|
52
52
|
environment: release
|
|
53
|
+
permissions:
|
|
54
|
+
# Required for crates.io Trusted Publishing (OIDC).
|
|
55
|
+
contents: read
|
|
56
|
+
id-token: write
|
|
53
57
|
steps:
|
|
54
|
-
- uses: actions/checkout@v4
|
|
55
|
-
|
|
58
|
+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
59
|
+
with:
|
|
60
|
+
persist-credentials: false
|
|
61
|
+
|
|
56
62
|
- name: Install Rust toolchain
|
|
57
63
|
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
|
|
58
|
-
|
|
64
|
+
|
|
65
|
+
# Exchange the GitHub OIDC token for a short-lived crates.io token.
|
|
66
|
+
- name: Authenticate to crates.io (Trusted Publishing)
|
|
67
|
+
uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5
|
|
68
|
+
id: auth
|
|
69
|
+
|
|
59
70
|
- name: Publish to crates.io
|
|
60
71
|
run: cargo publish
|
|
61
72
|
env:
|
|
62
|
-
CARGO_REGISTRY_TOKEN: ${{
|
|
73
|
+
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
|
|
@@ -299,7 +299,7 @@ dependencies = [
|
|
|
299
299
|
"tokio-rustls 0.26.4",
|
|
300
300
|
"tokio-stream",
|
|
301
301
|
"tokio-util",
|
|
302
|
-
"tokio-websockets",
|
|
302
|
+
"tokio-websockets 0.10.1",
|
|
303
303
|
"tracing",
|
|
304
304
|
"tryhard",
|
|
305
305
|
"url",
|
|
@@ -3057,7 +3057,7 @@ dependencies = [
|
|
|
3057
3057
|
|
|
3058
3058
|
[[package]]
|
|
3059
3059
|
name = "mq-bridge"
|
|
3060
|
-
version = "0.2.
|
|
3060
|
+
version = "0.2.20"
|
|
3061
3061
|
dependencies = [
|
|
3062
3062
|
"anyhow",
|
|
3063
3063
|
"arc-swap",
|
|
@@ -3109,7 +3109,7 @@ dependencies = [
|
|
|
3109
3109
|
"tokio",
|
|
3110
3110
|
"tokio-rustls 0.26.4",
|
|
3111
3111
|
"tokio-stream",
|
|
3112
|
-
"tokio-
|
|
3112
|
+
"tokio-websockets 0.13.2",
|
|
3113
3113
|
"tonic",
|
|
3114
3114
|
"tonic-prost",
|
|
3115
3115
|
"tonic-prost-build",
|
|
@@ -3124,7 +3124,7 @@ dependencies = [
|
|
|
3124
3124
|
|
|
3125
3125
|
[[package]]
|
|
3126
3126
|
name = "mq-bridge-py"
|
|
3127
|
-
version = "0.2.
|
|
3127
|
+
version = "0.2.20"
|
|
3128
3128
|
dependencies = [
|
|
3129
3129
|
"anyhow",
|
|
3130
3130
|
"async-trait",
|
|
@@ -3132,6 +3132,7 @@ dependencies = [
|
|
|
3132
3132
|
"fast-uuid-v7",
|
|
3133
3133
|
"mq-bridge",
|
|
3134
3134
|
"pyo3",
|
|
3135
|
+
"schemars",
|
|
3135
3136
|
"serde",
|
|
3136
3137
|
"serde_json",
|
|
3137
3138
|
"serde_yaml_ng",
|
|
@@ -4884,6 +4885,12 @@ dependencies = [
|
|
|
4884
4885
|
"digest 0.11.3",
|
|
4885
4886
|
]
|
|
4886
4887
|
|
|
4888
|
+
[[package]]
|
|
4889
|
+
name = "sha1_smol"
|
|
4890
|
+
version = "1.0.1"
|
|
4891
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
4892
|
+
checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
|
|
4893
|
+
|
|
4887
4894
|
[[package]]
|
|
4888
4895
|
name = "sha2"
|
|
4889
4896
|
version = "0.10.9"
|
|
@@ -5550,18 +5557,6 @@ dependencies = [
|
|
|
5550
5557
|
"tokio",
|
|
5551
5558
|
]
|
|
5552
5559
|
|
|
5553
|
-
[[package]]
|
|
5554
|
-
name = "tokio-tungstenite"
|
|
5555
|
-
version = "0.29.0"
|
|
5556
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
5557
|
-
checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c"
|
|
5558
|
-
dependencies = [
|
|
5559
|
-
"futures-util",
|
|
5560
|
-
"log",
|
|
5561
|
-
"tokio",
|
|
5562
|
-
"tungstenite",
|
|
5563
|
-
]
|
|
5564
|
-
|
|
5565
5560
|
[[package]]
|
|
5566
5561
|
name = "tokio-util"
|
|
5567
5562
|
version = "0.7.18"
|
|
@@ -5598,6 +5593,25 @@ dependencies = [
|
|
|
5598
5593
|
"webpki-roots 0.26.11",
|
|
5599
5594
|
]
|
|
5600
5595
|
|
|
5596
|
+
[[package]]
|
|
5597
|
+
name = "tokio-websockets"
|
|
5598
|
+
version = "0.13.2"
|
|
5599
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
5600
|
+
checksum = "dad543404f98bfc969aeb71994105c592acfc6c43323fddcd016bb208d1c65cb"
|
|
5601
|
+
dependencies = [
|
|
5602
|
+
"base64",
|
|
5603
|
+
"bytes",
|
|
5604
|
+
"fastrand",
|
|
5605
|
+
"futures-core",
|
|
5606
|
+
"futures-sink",
|
|
5607
|
+
"http 1.4.2",
|
|
5608
|
+
"httparse",
|
|
5609
|
+
"sha1_smol",
|
|
5610
|
+
"simdutf8",
|
|
5611
|
+
"tokio",
|
|
5612
|
+
"tokio-util",
|
|
5613
|
+
]
|
|
5614
|
+
|
|
5601
5615
|
[[package]]
|
|
5602
5616
|
name = "toml"
|
|
5603
5617
|
version = "1.1.2+spec-1.1.0"
|
|
@@ -5863,22 +5877,6 @@ dependencies = [
|
|
|
5863
5877
|
"tokio",
|
|
5864
5878
|
]
|
|
5865
5879
|
|
|
5866
|
-
[[package]]
|
|
5867
|
-
name = "tungstenite"
|
|
5868
|
-
version = "0.29.0"
|
|
5869
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
5870
|
-
checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8"
|
|
5871
|
-
dependencies = [
|
|
5872
|
-
"bytes",
|
|
5873
|
-
"data-encoding",
|
|
5874
|
-
"http 1.4.2",
|
|
5875
|
-
"httparse",
|
|
5876
|
-
"log",
|
|
5877
|
-
"rand 0.9.4",
|
|
5878
|
-
"sha1 0.10.6",
|
|
5879
|
-
"thiserror 2.0.18",
|
|
5880
|
-
]
|
|
5881
|
-
|
|
5882
5880
|
[[package]]
|
|
5883
5881
|
name = "typed-builder"
|
|
5884
5882
|
version = "0.22.0"
|
|
@@ -16,7 +16,7 @@ members = ["python/mq-bridge-py"]
|
|
|
16
16
|
resolver = "2"
|
|
17
17
|
|
|
18
18
|
[workspace.package]
|
|
19
|
-
version = "0.2.
|
|
19
|
+
version = "0.2.20"
|
|
20
20
|
|
|
21
21
|
[workspace.dependencies]
|
|
22
22
|
anyhow = "1.0"
|
|
@@ -46,6 +46,7 @@ tokio = { workspace = true, features = ["rt-multi-thread", "macros", "sync", "ti
|
|
|
46
46
|
# Note on MSRV for optional features:
|
|
47
47
|
# The base crate keeps MSRV 1.75 with default = [].
|
|
48
48
|
# - "amqp" (lapin 4.10) requires Rust 1.88+; "http" (hyper-rustls 0.27) requires Rust 1.85+.
|
|
49
|
+
# - "websocket" (tokio-websockets 0.13) requires Rust 1.89+.
|
|
49
50
|
# - "aws" (aws-sdk-*) may require a newer stable toolchain than the base MSRV.
|
|
50
51
|
# Use `rustup override set stable` or a `rust-toolchain.toml` file when enabling these features.
|
|
51
52
|
async-trait.workspace = true
|
|
@@ -118,7 +119,7 @@ http-body = { version = "1.0", optional = true }
|
|
|
118
119
|
h2 = { version = "0.4", optional = true }
|
|
119
120
|
flate2 = { version = "1.0", optional = true }
|
|
120
121
|
base64 = { version = "0.22", optional = true }
|
|
121
|
-
tokio-
|
|
122
|
+
tokio-websockets = { version = "0.13", optional = true, default-features = false, features = ["client", "server", "sha1_smol", "fastrand"] }
|
|
122
123
|
|
|
123
124
|
# AWS
|
|
124
125
|
aws-config = { version = "1.8", optional = true, default-features = false, features = ["behavior-version-latest", "rt-tokio", "rustls"] }
|
|
@@ -180,7 +181,7 @@ nats = ["async-nats", "rustls", "rustls-pemfile"]
|
|
|
180
181
|
mongodb = ["dep:mongodb"]
|
|
181
182
|
mqtt = ["rumqttc", "rustls", "tokio-rustls", "rustls-pemfile", "url"]
|
|
182
183
|
http = ["dep:arc-swap", "hyper", "hyper-util", "hyper-rustls", "http-body-util", "dep:http-body", "rustls", "tokio-rustls", "rustls-pemfile", "webpki-roots", "h2", "dep:flate2", "dep:base64"]
|
|
183
|
-
websocket = ["dep:tokio-
|
|
184
|
+
websocket = ["dep:tokio-websockets"]
|
|
184
185
|
aws = ["aws-config", "aws-sdk-sqs", "aws-sdk-sns", "dep:base64"]
|
|
185
186
|
ibm-mq = ["mqi"]
|
|
186
187
|
zeromq = ["dep:zeromq"]
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mq-bridge-py-basic
|
|
3
|
+
Version: 0.2.20
|
|
4
|
+
Classifier: Development Status :: 3 - Alpha
|
|
5
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Rust
|
|
13
|
+
Requires-Dist: typing-extensions>=4.0
|
|
14
|
+
License-File: LICENSE.python
|
|
15
|
+
Summary: Python bindings for mq-bridge (basic: all-platform messaging set)
|
|
16
|
+
Keywords: messaging,tokio,pyo3,maturin,mq-bridge
|
|
17
|
+
License: MIT
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
20
|
+
Project-URL: Homepage, https://github.com/marcomq/mq-bridge
|
|
21
|
+
Project-URL: Issues, https://github.com/marcomq/mq-bridge/issues
|
|
22
|
+
Project-URL: Repository, https://github.com/marcomq/mq-bridge
|
|
23
|
+
|
|
24
|
+
# mq-bridge Python bindings
|
|
25
|
+
|
|
26
|
+
Thin Python bindings for the Rust `mq-bridge` core.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
Pick exactly one distribution. Both install the same import path: `mq_bridge`.
|
|
31
|
+
|
|
32
|
+
| Package | Install | Includes |
|
|
33
|
+
| :--- | :--- | :--- |
|
|
34
|
+
| Full | `pip install mq-bridge-py` | Basic set plus Kafka, AWS, gRPC, MongoDB, SQLx |
|
|
35
|
+
| Basic | `pip install mq-bridge-py-basic` | HTTP, NATS, MQTT, AMQP, WebSocket, ZeroMQ, middleware |
|
|
36
|
+
|
|
37
|
+
Memory and file endpoints are always present in both packages. Use `mq-bridge-py-basic` when you want the lean all-platform wheel set. Use `mq-bridge-py` when you need Kafka or the heavier non-messaging backends.
|
|
38
|
+
|
|
39
|
+
The public API stays close to mq-bridge itself:
|
|
40
|
+
|
|
41
|
+
- `Route.from_yaml(path, name)` loads one named route from a YAML file
|
|
42
|
+
- `Route.from_yaml_str(text, name)` / `Route.from_config(mapping, name)` build a route from an in-memory YAML string or a Python `dict`, no file required
|
|
43
|
+
- `Route.with_handler(...)` attaches a raw `Message` handler, with lazy `json()`/`text()` readers and `with_json()`/`with_payload()` response helpers
|
|
44
|
+
- `Route.add_handler(kind, ...)` uses mq-bridge's `kind` dispatch and delivers decoded JSON
|
|
45
|
+
- `RetryableError` and `NonRetryableError` let Python handlers signal retry intent
|
|
46
|
+
- `Publisher.from_yaml(path, name)` (plus `from_yaml_str` / `from_config`) loads one named publisher
|
|
47
|
+
- `Publisher.send_json(...)` and `Publisher.request_json(...)` serialize Python JSON values in Rust
|
|
48
|
+
|
|
49
|
+
The Python surface is synchronous and blocking. Tokio, broker I/O, routing, and batching all stay in Rust.
|
|
50
|
+
|
|
51
|
+
## Config types and schema
|
|
52
|
+
|
|
53
|
+
`mq-bridge-app` can create and test route and endpoint JSON/YAML through its UI.
|
|
54
|
+
It does not replace your Python code or handlers, but it is useful when you want
|
|
55
|
+
a known-good connection and route shape before pasting the configuration into
|
|
56
|
+
Python. Load the generated config with `Route.from_config`, `Route.from_yaml`,
|
|
57
|
+
`Publisher.from_config`, or `Publisher.from_yaml`.
|
|
58
|
+
|
|
59
|
+
For the `from_config` / `from_yaml_str` mappings, `mq_bridge.config` ships
|
|
60
|
+
`TypedDict` definitions so editors autocomplete the config keys (`input`,
|
|
61
|
+
`output`, `batch_size`, every transport config, middleware, …):
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from mq_bridge import Route
|
|
65
|
+
from mq_bridge.config import ConfigDocument
|
|
66
|
+
|
|
67
|
+
config: ConfigDocument = {
|
|
68
|
+
"routes": {
|
|
69
|
+
"orders": {
|
|
70
|
+
"input": {"memory": {"topic": "orders.in", "capacity": 1600}},
|
|
71
|
+
"output": {"response": {}},
|
|
72
|
+
"batch_size": 128,
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
route = Route.from_config(config, "orders")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
These types are generated from the JSON Schema, which the extension produces on
|
|
80
|
+
demand from the Rust models — there is no checked-in schema copy to drift:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from mq_bridge import config_schema
|
|
84
|
+
|
|
85
|
+
schema = config_schema() # the JSON Schema as a dict
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`config_schema()` is handy for editor validation of YAML configs too — dump it
|
|
89
|
+
to a file and point your `# yaml-language-server: $schema=` line at it. The types
|
|
90
|
+
are regenerated with `uv run python scripts/gen_config_types.py` (a test fails if
|
|
91
|
+
they drift from the schema).
|
|
92
|
+
|
|
93
|
+
## Running a route
|
|
94
|
+
|
|
95
|
+
`Route.run()` **blocks the calling thread** until another thread calls `stop()` —
|
|
96
|
+
it deploys the route and then parks. This is convenient for a process whose only
|
|
97
|
+
job is the route, but it is a common trap: nothing after `route.run()` executes
|
|
98
|
+
until the route stops.
|
|
99
|
+
|
|
100
|
+
To keep running Python code after the route is up, use `start()` (non-blocking)
|
|
101
|
+
or the context-manager form:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
route = Route.from_config(config, "orders_route").with_handler(handle)
|
|
105
|
+
|
|
106
|
+
# Non-blocking: deploys, returns, and runs on a background thread.
|
|
107
|
+
route.start()
|
|
108
|
+
publisher.send_json({"order_id": 42}, {"kind": "order.created"})
|
|
109
|
+
route.stop()
|
|
110
|
+
route.join() # optional: wait for a clean shutdown
|
|
111
|
+
|
|
112
|
+
# Or scope it to a block — starts on enter, stops + joins on exit:
|
|
113
|
+
with Route.from_config(config, "orders_route").with_handler(handle):
|
|
114
|
+
publisher.send_json({"order_id": 42}, {"kind": "order.created"})
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Configuration/connection errors surface from `start()` itself, not from a
|
|
118
|
+
background thread. `run()` remains available for the blocking single-route case.
|
|
119
|
+
|
|
120
|
+
## Tuning (environment variables)
|
|
121
|
+
|
|
122
|
+
These knobs are read from the environment at startup:
|
|
123
|
+
|
|
124
|
+
| Variable | Default | Effect |
|
|
125
|
+
| :--- | :--- | :--- |
|
|
126
|
+
| `MQ_BRIDGE_PY_HANDLER_EXECUTOR` | `worker` | `worker` runs handlers on a dedicated interpreter thread that coalesces queued batches under one GIL acquisition (best under load); `direct` calls the handler inline. |
|
|
127
|
+
| `MQ_BRIDGE_PY_HANDLER_CONCURRENCY` | CPU count | Max in-flight handler batches. `0` disables the limit. |
|
|
128
|
+
| `MQ_BRIDGE_PY_GC_MODE` | `default` | `default` leaves CPython's cyclic GC alone; `count` disables it and runs `gc.collect()` every N messages; `off` disables it entirely (pure refcounting). |
|
|
129
|
+
| `MQ_BRIDGE_PY_GC_THRESHOLD` | `100000` | Messages between collections when `MQ_BRIDGE_PY_GC_MODE=count`. |
|
|
130
|
+
|
|
131
|
+
## Local development
|
|
132
|
+
|
|
133
|
+
`uv` is a good fit here for the Python-side developer workflow, while `maturin` stays the build backend:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
cd python/mq-bridge-py
|
|
137
|
+
uv sync --group dev --no-install-project
|
|
138
|
+
uv run maturin develop
|
|
139
|
+
uv run pytest -q
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Performance smoke tests are skipped by default because they start routes and measure
|
|
143
|
+
local throughput:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
cd python/mq-bridge-py
|
|
147
|
+
MQ_BRIDGE_RUN_PERF_TESTS=1 uv run pytest -q -m performance
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Examples
|
|
151
|
+
|
|
152
|
+
Raw message handler:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
cd python/mq-bridge-py
|
|
156
|
+
uv run python examples/raw_route.py
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Kind-based JSON handler:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
cd python/mq-bridge-py
|
|
163
|
+
uv run python examples/json_route.py
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Memory benchmark:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
cd python/mq-bridge-py
|
|
170
|
+
uv run maturin develop --release
|
|
171
|
+
uv run python examples/bench_memory.py --messages 100000
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Analysis
|
|
175
|
+
|
|
176
|
+
HTTP comparison benchmark, driven by a native load generator (`wrk`) so the
|
|
177
|
+
client is never the bottleneck. It boots each server itself (mq-bridge in worker
|
|
178
|
+
and direct executor modes, plus FastAPI, Starlette, Sanic, aiohttp, and
|
|
179
|
+
FastStream when installed) and drives each with `wrk`:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
cd python/mq-bridge-py
|
|
183
|
+
uv run maturin develop --release
|
|
184
|
+
uv sync --group bench # optional Python HTTP peers
|
|
185
|
+
uv run python analysis/bench_http_native.py --connections 1,8,32 --duration 8
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Requires `wrk` on `PATH` (`brew install wrk`). The FastStream target compares its
|
|
189
|
+
ASGI custom-route path over Uvicorn; it is not a broker-backed subscriber/publisher
|
|
190
|
+
benchmark. The examples use included sample configs or create temporary configs.
|
|
191
|
+
|
|
@@ -157,6 +157,12 @@ The `response` output endpoint sends a reply back to the original requester. Thi
|
|
|
157
157
|
There is a separate repository for running mq-bridge as a standalone app, for example as a Docker container configured via YAML or environment variables:
|
|
158
158
|
https://github.com/marcomq/mq-bridge-app
|
|
159
159
|
|
|
160
|
+
### Configuration-first workflow
|
|
161
|
+
|
|
162
|
+
`mq-bridge-app` can be used to create and test route and endpoint configurations through a UI. The generated JSON/YAML can then be copied into an application and loaded by the Rust library or the Python bindings.
|
|
163
|
+
|
|
164
|
+
This does not replace application code or handlers, but it is useful when you want a known-good connection and route shape before pasting the configuration into code. For Python projects, routes and publishers are commonly loaded from JSON/YAML with `Route.from_config`, `Route.from_yaml`, `Publisher.from_config`, or `Publisher.from_yaml`.
|
|
165
|
+
|
|
160
166
|
### Programmatic Handlers
|
|
161
167
|
|
|
162
168
|
For business logic, `mq-bridge` provides a handler layer separate from transport-level middleware. This is where message-specific code usually belongs.
|
|
@@ -648,7 +648,7 @@ pub mod websocket_helper {
|
|
|
648
648
|
let websocket_config = WebSocketConfig {
|
|
649
649
|
url: "127.0.0.1:0".to_string(),
|
|
650
650
|
path: Some("/bench".to_string()),
|
|
651
|
-
|
|
651
|
+
routed_queue_capacity: Some(super::PERF_TEST_MESSAGE_COUNT * 2),
|
|
652
652
|
..Default::default()
|
|
653
653
|
};
|
|
654
654
|
|
|
@@ -362,7 +362,7 @@
|
|
|
362
362
|
"type": "object",
|
|
363
363
|
"properties": {
|
|
364
364
|
"static": {
|
|
365
|
-
"
|
|
365
|
+
"$ref": "#/$defs/StaticConfig"
|
|
366
366
|
}
|
|
367
367
|
},
|
|
368
368
|
"required": [
|
|
@@ -972,6 +972,11 @@
|
|
|
972
972
|
"format": "uint64",
|
|
973
973
|
"minimum": 0
|
|
974
974
|
},
|
|
975
|
+
"server_protocol": {
|
|
976
|
+
"description": "(Consumer only) Restrict which HTTP protocol versions a server listener accepts.\nDefaults to `auto` (HTTP/1.1 + HTTP/2). On cleartext listeners, `http2_only`\nmeans HTTP/2 prior-knowledge (h2c) only.",
|
|
977
|
+
"$ref": "#/$defs/HttpServerProtocol",
|
|
978
|
+
"default": "auto"
|
|
979
|
+
},
|
|
975
980
|
"stream_response_to": {
|
|
976
981
|
"description": "(Publisher only) Optional endpoint that receives streamed HTTP response items as correlated messages.\n\nUse a `stream_buffer` endpoint here when callers need to read streamed\nresponse items later through a normal mq-bridge consumer. Each streamed\nitem is published with `correlation_id`, `http_stream_id`,\n`http_stream_index`, `http_stream_format`, and `http_stream_end`\nmetadata. If the request message has no `correlation_id`, the HTTP\npublisher uses `format!(\"{:032x}\", request.message_id)` so callers can\nderive the consumer correlation id before calling `send`.",
|
|
977
982
|
"anyOf": [
|
|
@@ -1023,6 +1028,26 @@
|
|
|
1023
1028
|
"url"
|
|
1024
1029
|
]
|
|
1025
1030
|
},
|
|
1031
|
+
"HttpServerProtocol": {
|
|
1032
|
+
"description": "Supported inbound HTTP protocols for server listeners.",
|
|
1033
|
+
"oneOf": [
|
|
1034
|
+
{
|
|
1035
|
+
"description": "Accept both HTTP/1.1 and HTTP/2, matching the current default behavior.",
|
|
1036
|
+
"type": "string",
|
|
1037
|
+
"const": "auto"
|
|
1038
|
+
},
|
|
1039
|
+
{
|
|
1040
|
+
"description": "Accept only HTTP/1.x connections.",
|
|
1041
|
+
"type": "string",
|
|
1042
|
+
"const": "http1_only"
|
|
1043
|
+
},
|
|
1044
|
+
{
|
|
1045
|
+
"description": "Accept only HTTP/2 connections.",
|
|
1046
|
+
"type": "string",
|
|
1047
|
+
"const": "http2_only"
|
|
1048
|
+
}
|
|
1049
|
+
]
|
|
1050
|
+
},
|
|
1026
1051
|
"IbmMqConfig": {
|
|
1027
1052
|
"description": "Connection settings for the IBM MQ Queue Manager.",
|
|
1028
1053
|
"type": "object",
|
|
@@ -1280,11 +1305,13 @@
|
|
|
1280
1305
|
},
|
|
1281
1306
|
"topic": {
|
|
1282
1307
|
"description": "The topic name or transport URL. Can be:\n- Simple name: \"my-topic\" (defaults to memory://my-topic)\n- Memory URL: \"memory://my-topic\"\n- IPC URL: \"ipc://my-queue\" or \"ipc:///path/to/socket\"\n- Unix socket: \"unix:///path/to/socket\" (Unix only)\n- Named pipe: \"pipe://my-pipe\" (Windows only)\n\nEither `topic` or `url` can be specified (they are serde aliases).",
|
|
1283
|
-
"type": "string"
|
|
1308
|
+
"type": "string",
|
|
1309
|
+
"minLength": 1
|
|
1284
1310
|
},
|
|
1285
1311
|
"url": {
|
|
1286
1312
|
"description": "Alias for `topic`. Use either `topic` or `url`.",
|
|
1287
|
-
"type": "string"
|
|
1313
|
+
"type": "string",
|
|
1314
|
+
"minLength": 1
|
|
1288
1315
|
}
|
|
1289
1316
|
},
|
|
1290
1317
|
"additionalProperties": false,
|
|
@@ -1614,7 +1641,7 @@
|
|
|
1614
1641
|
]
|
|
1615
1642
|
},
|
|
1616
1643
|
"delayed_ack": {
|
|
1617
|
-
"description": "(Consumer only) If true, messages are acknowledged immediately upon receipt (auto-ack).\nIf false (default), messages are acknowledged after processing (manual-ack).\nNote:
|
|
1644
|
+
"description": "(Consumer only) If true, messages are acknowledged immediately upon receipt (auto-ack).\nIf false (default), messages are acknowledged after processing (manual-ack).\nNote: For QoS 1/2 the publisher always waits for end-to-end broker\nconfirmation (PUBACK/PUBCOMP) before reporting success, independent of\nthis setting; QoS 0 remains fire-and-forget.",
|
|
1618
1645
|
"type": "boolean",
|
|
1619
1646
|
"default": false
|
|
1620
1647
|
},
|
|
@@ -2157,6 +2184,40 @@
|
|
|
2157
2184
|
"table"
|
|
2158
2185
|
]
|
|
2159
2186
|
},
|
|
2187
|
+
"StaticConfig": {
|
|
2188
|
+
"description": "Configuration for the `static` endpoint. Accepts either a bare string (the response body, JSON-encoded for backward compatibility) or a map where only `body` is required and `raw` / `metadata` are optional.",
|
|
2189
|
+
"oneOf": [
|
|
2190
|
+
{
|
|
2191
|
+
"description": "The response body, JSON-encoded as a string.",
|
|
2192
|
+
"type": "string"
|
|
2193
|
+
},
|
|
2194
|
+
{
|
|
2195
|
+
"type": "object",
|
|
2196
|
+
"properties": {
|
|
2197
|
+
"body": {
|
|
2198
|
+
"description": "The static response body.",
|
|
2199
|
+
"type": "string"
|
|
2200
|
+
},
|
|
2201
|
+
"metadata": {
|
|
2202
|
+
"description": "Extra metadata entries attached to the produced message.",
|
|
2203
|
+
"type": "object",
|
|
2204
|
+
"additionalProperties": {
|
|
2205
|
+
"type": "string"
|
|
2206
|
+
}
|
|
2207
|
+
},
|
|
2208
|
+
"raw": {
|
|
2209
|
+
"description": "Send the body verbatim instead of JSON-encoding it as a string.",
|
|
2210
|
+
"type": "boolean",
|
|
2211
|
+
"default": false
|
|
2212
|
+
}
|
|
2213
|
+
},
|
|
2214
|
+
"additionalProperties": false,
|
|
2215
|
+
"required": [
|
|
2216
|
+
"body"
|
|
2217
|
+
]
|
|
2218
|
+
}
|
|
2219
|
+
]
|
|
2220
|
+
},
|
|
2160
2221
|
"StreamBufferConfig": {
|
|
2161
2222
|
"description": "Configuration for the correlated in-process stream response buffer.",
|
|
2162
2223
|
"type": "object",
|
|
@@ -2297,15 +2358,20 @@
|
|
|
2297
2358
|
"description": "WebSocket connection configuration.",
|
|
2298
2359
|
"type": "object",
|
|
2299
2360
|
"properties": {
|
|
2300
|
-
"
|
|
2301
|
-
"description": "(Consumer only)
|
|
2361
|
+
"backlog": {
|
|
2362
|
+
"description": "(Consumer only) TCP listen backlog (pending-connection queue depth) for the accept socket.\nRaise this if high-concurrency handshake bursts are being dropped/reset before `accept()`\ncan keep up. Defaults to 4096, which is higher than the OS/tokio default of 1024.",
|
|
2302
2363
|
"type": [
|
|
2303
2364
|
"integer",
|
|
2304
2365
|
"null"
|
|
2305
2366
|
],
|
|
2306
|
-
"format": "
|
|
2367
|
+
"format": "uint32",
|
|
2307
2368
|
"minimum": 0
|
|
2308
2369
|
},
|
|
2370
|
+
"execution_mode": {
|
|
2371
|
+
"description": "(Consumer only) Selects whether WebSocket routes run directly or through the routed pipeline.",
|
|
2372
|
+
"$ref": "#/$defs/WebSocketExecutionMode",
|
|
2373
|
+
"default": "auto"
|
|
2374
|
+
},
|
|
2309
2375
|
"message_id_header": {
|
|
2310
2376
|
"description": "(Consumer only) Header key to extract the message ID from the WebSocket handshake. Defaults to \"message-id\".",
|
|
2311
2377
|
"type": [
|
|
@@ -2320,6 +2386,15 @@
|
|
|
2320
2386
|
"null"
|
|
2321
2387
|
]
|
|
2322
2388
|
},
|
|
2389
|
+
"routed_queue_capacity": {
|
|
2390
|
+
"description": "(Consumer only) Queue capacity for the routed adapter. Direct response routes do not use this queue. Defaults to 100.",
|
|
2391
|
+
"type": [
|
|
2392
|
+
"integer",
|
|
2393
|
+
"null"
|
|
2394
|
+
],
|
|
2395
|
+
"format": "uint",
|
|
2396
|
+
"minimum": 0
|
|
2397
|
+
},
|
|
2323
2398
|
"url": {
|
|
2324
2399
|
"description": "For consumers, the listen address (e.g. \"0.0.0.0:9000\"). For publishers, the target URL.",
|
|
2325
2400
|
"type": "string",
|
|
@@ -2331,6 +2406,26 @@
|
|
|
2331
2406
|
"url"
|
|
2332
2407
|
]
|
|
2333
2408
|
},
|
|
2409
|
+
"WebSocketExecutionMode": {
|
|
2410
|
+
"description": "WebSocket route execution strategy.",
|
|
2411
|
+
"oneOf": [
|
|
2412
|
+
{
|
|
2413
|
+
"description": "Use direct per-connection handling for simple `websocket -> response` routes and fall back\nto the routed adapter with a warning when route semantics need the normal pipeline.",
|
|
2414
|
+
"type": "string",
|
|
2415
|
+
"const": "auto"
|
|
2416
|
+
},
|
|
2417
|
+
{
|
|
2418
|
+
"description": "Require direct per-connection handling. Startup fails if the route cannot run directly.",
|
|
2419
|
+
"type": "string",
|
|
2420
|
+
"const": "direct_only"
|
|
2421
|
+
},
|
|
2422
|
+
{
|
|
2423
|
+
"description": "Always use the normal routed consumer/worker/disposition pipeline.",
|
|
2424
|
+
"type": "string",
|
|
2425
|
+
"const": "routed"
|
|
2426
|
+
}
|
|
2427
|
+
]
|
|
2428
|
+
},
|
|
2334
2429
|
"ZeroMqConfig": {
|
|
2335
2430
|
"type": "object",
|
|
2336
2431
|
"properties": {
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
from
|
|
1
|
+
from types import TracebackType
|
|
2
|
+
from typing import Any, Callable, Dict, List, Mapping, Optional, Type, Union
|
|
3
|
+
|
|
4
|
+
__version__: str
|
|
5
|
+
|
|
6
|
+
def config_schema() -> Dict[str, Any]:
|
|
7
|
+
"""Return the route-config JSON Schema, generated from the Rust models."""
|
|
8
|
+
...
|
|
2
9
|
|
|
3
10
|
JsonValue = Any
|
|
4
11
|
HandlerResult = Optional[Union["Message", bytes, str, Dict[str, JsonValue], List[JsonValue], int, float, bool]]
|
|
@@ -48,6 +55,12 @@ class Route:
|
|
|
48
55
|
@classmethod
|
|
49
56
|
def from_yaml(cls, path: str, name: str) -> "Route": ...
|
|
50
57
|
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_yaml_str(cls, text: str, name: str) -> "Route": ...
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def from_config(cls, config: Mapping[str, Any], name: str) -> "Route": ...
|
|
63
|
+
|
|
51
64
|
def with_handler(self, handler: Callable[[Message], HandlerResult]) -> "Route": ...
|
|
52
65
|
|
|
53
66
|
def add_handler(
|
|
@@ -56,10 +69,29 @@ class Route:
|
|
|
56
69
|
handler: Callable[[JsonValue], HandlerResult],
|
|
57
70
|
) -> "Route": ...
|
|
58
71
|
|
|
59
|
-
def run(self) -> None:
|
|
72
|
+
def run(self) -> None:
|
|
73
|
+
"""Deploy and block the calling thread until ``stop()`` is called."""
|
|
74
|
+
...
|
|
75
|
+
|
|
76
|
+
def start(self) -> None:
|
|
77
|
+
"""Deploy on a background thread and return immediately."""
|
|
78
|
+
...
|
|
79
|
+
|
|
80
|
+
def join(self) -> None:
|
|
81
|
+
"""Block until a route started with ``start()`` has stopped."""
|
|
82
|
+
...
|
|
60
83
|
|
|
61
84
|
def stop(self) -> None: ...
|
|
62
85
|
|
|
86
|
+
def __enter__(self) -> "Route": ...
|
|
87
|
+
|
|
88
|
+
def __exit__(
|
|
89
|
+
self,
|
|
90
|
+
exc_type: Optional[Type[BaseException]],
|
|
91
|
+
exc_value: Optional[BaseException],
|
|
92
|
+
traceback: Optional[TracebackType],
|
|
93
|
+
) -> bool: ...
|
|
94
|
+
|
|
63
95
|
|
|
64
96
|
class MemoryDrainer:
|
|
65
97
|
@classmethod
|
|
@@ -81,6 +113,12 @@ class Publisher:
|
|
|
81
113
|
@classmethod
|
|
82
114
|
def from_yaml(cls, path: str, name: str) -> "Publisher": ...
|
|
83
115
|
|
|
116
|
+
@classmethod
|
|
117
|
+
def from_yaml_str(cls, text: str, name: str) -> "Publisher": ...
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def from_config(cls, config: Mapping[str, Any], name: str) -> "Publisher": ...
|
|
121
|
+
|
|
84
122
|
def send(
|
|
85
123
|
self,
|
|
86
124
|
message: Union[Message, bytes],
|