amqp-client-python 0.2.1.dev1__tar.gz → 0.2.2.dev1__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.
Files changed (133) hide show
  1. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/.github/workflows/lint.yml +3 -3
  2. amqp_client_python-0.2.2.dev1/.github/workflows/publish.yml +22 -0
  3. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/.github/workflows/test.yml +3 -3
  4. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/PKG-INFO +15 -20
  5. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/README.md +12 -16
  6. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/domain/models/options.py +18 -0
  7. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/rabbitmq/async_channel.py +110 -39
  8. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/rabbitmq/async_connection.py +86 -73
  9. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/rabbitmq/async_eventbus_rabbitmq.py +31 -1
  10. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/rabbitmq/eventbus_wrapper_rabbitmq.py +29 -3
  11. amqp_client_python-0.2.2.dev1/docs/templates/installation.md +11 -0
  12. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/pyproject.toml +8 -10
  13. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/channels/test_async_rpc_client.py +37 -0
  14. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/channels/test_publish.py +29 -0
  15. amqp_client_python-0.2.2.dev1/tests/unit/channels/test_subscribe.py +120 -0
  16. amqp_client_python-0.2.2.dev1/tests/unit/eventbus/test_topology_options.py +310 -0
  17. amqp_client_python-0.2.2.dev1/uv.lock +1659 -0
  18. amqp_client_python-0.2.1.dev1/.github/workflows/publish.yml +0 -24
  19. amqp_client_python-0.2.1.dev1/docs/templates/installation.md +0 -9
  20. amqp_client_python-0.2.1.dev1/tests/unit/channels/test_subscribe.py +0 -30
  21. amqp_client_python-0.2.1.dev1/uv.lock +0 -1624
  22. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/.env.example +0 -0
  23. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/.flake8 +0 -0
  24. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/.github/workflows/type.yml +0 -0
  25. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/.gitignore +0 -0
  26. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/.pre-commit-config.yaml +0 -0
  27. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/.readthedocs.yaml +0 -0
  28. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/.vscode/settings.json +0 -0
  29. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/CODE_OF_CONDUCT.md +0 -0
  30. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/LICENSE +0 -0
  31. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/__init__.py +0 -0
  32. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/domain/models/__init__.py +0 -0
  33. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/domain/models/config.py +0 -0
  34. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/domain/models/ssl_options.py +0 -0
  35. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/domain/utils/__init__.py +0 -0
  36. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/domain/utils/connection_type.py +0 -0
  37. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/domain/utils/types.py +0 -0
  38. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/event/__init__.py +0 -0
  39. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/event/async_provider_handler.py +0 -0
  40. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/event/async_subscriber_handler.py +0 -0
  41. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/event/event.py +0 -0
  42. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/event/integration_event.py +0 -0
  43. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/event/integration_event_handler.py +0 -0
  44. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/event/provider_handler.py +0 -0
  45. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/event/subscriber_handler.py +0 -0
  46. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/exceptions/__init__.py +0 -0
  47. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/exceptions/auto_reconnect_exception.py +0 -0
  48. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/exceptions/blocking_exception.py +0 -0
  49. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/exceptions/eventbus_exception.py +0 -0
  50. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/exceptions/nack_exception.py +0 -0
  51. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/exceptions/publish_timeout_exception.py +0 -0
  52. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/exceptions/response_timeout_exception.py +0 -0
  53. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/exceptions/rpc_provider_exception.py +0 -0
  54. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/exceptions/thread_unsafe_exception.py +0 -0
  55. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/exceptions/timeout_exception.py +0 -0
  56. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/py.typed +0 -0
  57. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/rabbitmq/__init__.py +0 -0
  58. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/rabbitmq/async_channel_factory.py +0 -0
  59. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/rabbitmq/async_connection_factory.py +0 -0
  60. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/rabbitmq/channel_factory_rabbitmq.py +0 -0
  61. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/rabbitmq/channel_rabbitmq.py +0 -0
  62. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/rabbitmq/connection_factory_rabbitmq.py +0 -0
  63. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/rabbitmq/connection_rabbitmq.py +0 -0
  64. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/rabbitmq/eventbus_rabbitmq.py +0 -0
  65. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/rabbitmq/ioloop_factory.py +0 -0
  66. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/amqp_client_python/signals.py +0 -0
  67. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/create-self-signed-certs.sh +0 -0
  68. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/dev_requirements.txt +0 -0
  69. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/docker-compose.yml +0 -0
  70. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/docs/API/domain/config.md +0 -0
  71. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/docs/API/domain/options.md +0 -0
  72. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/docs/API/domain/ssl_options.md +0 -0
  73. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/docs/API/rabbitmq/async_eventbus.md +0 -0
  74. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/docs/contribute.md +0 -0
  75. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/docs/index.md +0 -0
  76. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/docs/stylesheets/extra.css +0 -0
  77. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/docs/templates/cards.html +0 -0
  78. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/docs/templates/sponsors.md +0 -0
  79. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/docs/templates/user_guide.md +0 -0
  80. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/docs/tutorial.md +0 -0
  81. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/examples/__init__.py +0 -0
  82. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/examples/async_case.py +0 -0
  83. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/examples/default.py +0 -0
  84. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/examples/multprocess.py +0 -0
  85. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/examples/publish.py +0 -0
  86. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/examples/rpc_publish.py +0 -0
  87. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/examples/rpc_subscribe.py +0 -0
  88. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/examples/sanic_test.py +0 -0
  89. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/examples/ssl_connection.py +0 -0
  90. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/examples/subscribe.py +0 -0
  91. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/examples/sync_case.py +0 -0
  92. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/examples/sync_wrapper_case.py +0 -0
  93. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/examples/thread.py +0 -0
  94. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/external_config/rabbitmq/rabbitmq.conf +0 -0
  95. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/mkdocs.yml +0 -0
  96. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/mypy.ini +0 -0
  97. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/__init__.py +0 -0
  98. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/__init__.py +0 -0
  99. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/async_eventbus/__init__.py +0 -0
  100. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/async_eventbus/auto_recovery_test.py +0 -0
  101. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/async_eventbus/conftest.py +0 -0
  102. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/async_eventbus/rpc_provider_test.py +0 -0
  103. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/async_eventbus/signals_test.py +0 -0
  104. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/async_eventbus/ssl_test.py +0 -0
  105. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/async_eventbus/subscribe_test.py +0 -0
  106. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/async_eventbus_wrapper/__init__.py +0 -0
  107. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/async_eventbus_wrapper/conftest.py +0 -0
  108. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/async_eventbus_wrapper/rpc_provider_test.py +0 -0
  109. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/async_eventbus_wrapper/signals_test.py +0 -0
  110. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/async_eventbus_wrapper/ssl_test.py +0 -0
  111. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/async_eventbus_wrapper/subscribe_test.py +0 -0
  112. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/eventbus/__init__.py +0 -0
  113. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/eventbus/conftest.py +0 -0
  114. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/eventbus/publish_test.py +0 -0
  115. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/eventbus/rpc_publish_test.py +0 -0
  116. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/eventbus/rpc_subscribe_test.py +0 -0
  117. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/integration/eventbus/subscribe_test.py +0 -0
  118. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/__init__.py +0 -0
  119. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/channels/test_async_channel.py +0 -0
  120. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/channels/test_channel_factory.py +0 -0
  121. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/channels/test_rpc_subscribe.py +0 -0
  122. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/config/test_uri.py +0 -0
  123. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/conftest.py +0 -0
  124. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/eventbus/default.py +0 -0
  125. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/eventbus/test_async_eventbus.py +0 -0
  126. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/eventbus/test_async_eventbus_provide_resource.py +0 -0
  127. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/eventbus/test_async_eventbus_publish.py +0 -0
  128. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/eventbus/test_async_eventbus_rpc_client.py +0 -0
  129. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/eventbus/test_async_eventbus_subscribe.py +0 -0
  130. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/eventbus/test_auto_recovery.py +0 -0
  131. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/utils/ca.pem +0 -0
  132. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/utils/rabbitmq_cert.pem +0 -0
  133. {amqp_client_python-0.2.1.dev1 → amqp_client_python-0.2.2.dev1}/tests/unit/utils/rabbitmq_key.pem +0 -0
@@ -19,10 +19,10 @@ jobs:
19
19
 
20
20
  steps:
21
21
  - uses: actions/checkout@v3
22
- - name: Set up Python 3.8
23
- uses: actions/setup-python@v3
22
+ - name: Set up Python 3.10
23
+ uses: actions/setup-python@v4
24
24
  with:
25
- python-version: "3.8"
25
+ python-version: "3.10"
26
26
  - name: Install dependencies
27
27
  run: |
28
28
  python -m pip install --upgrade pip
@@ -0,0 +1,22 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [created]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ deploy:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Install uv
15
+ uses: astral-sh/setup-uv@v5
16
+
17
+ - name: Build and publish
18
+ env:
19
+ UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
20
+ run: |
21
+ uv build
22
+ uv publish
@@ -42,10 +42,10 @@ jobs:
42
42
  echo "Waiting for RabbitMQ to initialize..."
43
43
  # Give the container time to start up and apply the configuration.
44
44
  sleep 8
45
- - name: Set up Python 3.8
46
- uses: actions/setup-python@v3
45
+ - name: Set up Python 3.10
46
+ uses: actions/setup-python@v4
47
47
  with:
48
- python-version: "3.8"
48
+ python-version: "3.10"
49
49
  - name: Install dependencies
50
50
  run: |
51
51
  python -m pip install --upgrade pip
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: amqp-client-python
3
- Version: 0.2.1.dev1
3
+ Version: 0.2.2.dev1
4
4
  Summary: Python AMQP Client Library
5
5
  Project-URL: Homepage, https://github.com/nutes-uepb/amqp-client-python
6
6
  Project-URL: Repository, https://github.com/nutes-uepb/amqp-client-python
@@ -17,7 +17,6 @@ Classifier: License :: OSI Approved :: Apache Software License
17
17
  Classifier: Natural Language :: English
18
18
  Classifier: Operating System :: OS Independent
19
19
  Classifier: Programming Language :: Python :: 3
20
- Classifier: Programming Language :: Python :: 3.9
21
20
  Classifier: Programming Language :: Python :: 3.10
22
21
  Classifier: Programming Language :: Python :: 3.11
23
22
  Classifier: Programming Language :: Python :: 3.12
@@ -28,7 +27,7 @@ Classifier: Topic :: Internet
28
27
  Classifier: Topic :: Software Development :: Libraries
29
28
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
30
29
  Classifier: Topic :: System :: Networking
31
- Requires-Python: <4.0,>=3.9
30
+ Requires-Python: <4.0,>=3.10
32
31
  Requires-Dist: pika<2.0.0,>=1.3.0
33
32
  Description-Content-Type: text/markdown
34
33
 
@@ -57,16 +56,6 @@ A Python client providing a high-level abstraction for interacting with RabbitMQ
57
56
 
58
57
  **Discord Server**: <a href="https://discord.gg/RkXNeZpNZk" target="_blank">https://discord.gg/RkXNeZpNZk</a>
59
58
 
60
- ### 🦀 Looking for higher performance? Try `amqp-rs`!
61
- If you need higher throughput, thread safety, and built-in compression, check out [**amqp-rs**](https://pypi.org/project/amqp-rs/).
62
-
63
- It is a high-performance Python extension developed in Rust using PyO3 and `tokio`. It shares a similar API and design philosophy with this library, but adds native thread safety, `zstd`/`zlib`/`lz4` compression, robust TLS/SSL support, and graceful shutdowns.
64
-
65
- You can install it via `pip` or `uv`:
66
- ```bash
67
- pip install amqp-rs
68
- ```
69
-
70
59
  ---
71
60
  ### Features:
72
61
  - Automatic creation and management of queues, exchanges and channels;
@@ -80,19 +69,22 @@ pip install amqp-rs
80
69
  ### Table of Compatibility
81
70
  | version | compatible with |
82
71
  | ---- | ---- |
72
+ | 0.2.1 | ~0.2.0 |
83
73
  | 0.2.0 | 0.2.0 |
84
74
  | 0.1.14 | ~0.1.12 |
85
75
 
86
76
  ### Installation
87
77
 
88
- You can install `amqp-client-python` using pip:
78
+ You can install `amqp-client-python` using uv or pip:
89
79
  ```bash
80
+ uv add amqp-client-python
81
+ # or
90
82
  pip install amqp-client-python
91
83
  ```
92
84
 
93
85
  ### Prerequisites
94
86
 
95
- * Python 3.7+
87
+ * Python 3.9+
96
88
  * A running RabbitMQ instance.
97
89
 
98
90
  ### Examples:
@@ -109,15 +101,18 @@ from amqp_client_python import (
109
101
  )
110
102
  config = Config(Options("queue", "rpc_queue", "rpc_exchange"))
111
103
  eventbus = AsyncEventbusRabbitMQ(config)
112
- # publish
104
+ # publish (supports dict/str with auto-JSON, or raw bytes/bytearray without overhead)
105
+ await eventbus.publish("rpc_exchange", "routing.key", {"message": "content"})
106
+ await eventbus.publish("rpc_exchange", "routing.key", b"raw_binary_payload")
113
107
 
114
- eventbus.publish("rpc_exchange", "routing.key", "message_content")
115
- # subscribe
108
+ # subscribe (auto_decode=True delivers parsed JSON dict; auto_decode=False delivers raw bytes)
116
109
  async def subscribe_handler(body) -> None:
117
110
  print(body, type(body), flush=True) # handle messages
118
111
  await eventbus.subscribe("rpc_exchange", "routing.key", subscribe_handler)
112
+
119
113
  # rpc_publish
120
- response = await eventbus.rpc_client("rpc_exchange", "user.find", "message_content")
114
+ response = await eventbus.rpc_client("rpc_exchange", "user.find", {"name": "alice"})
115
+
121
116
  # provider
122
117
  async def rpc_provider_handler(body) -> bytes:
123
118
  print(f"body: {body}")
@@ -23,16 +23,6 @@ A Python client providing a high-level abstraction for interacting with RabbitMQ
23
23
 
24
24
  **Discord Server**: <a href="https://discord.gg/RkXNeZpNZk" target="_blank">https://discord.gg/RkXNeZpNZk</a>
25
25
 
26
- ### 🦀 Looking for higher performance? Try `amqp-rs`!
27
- If you need higher throughput, thread safety, and built-in compression, check out [**amqp-rs**](https://pypi.org/project/amqp-rs/).
28
-
29
- It is a high-performance Python extension developed in Rust using PyO3 and `tokio`. It shares a similar API and design philosophy with this library, but adds native thread safety, `zstd`/`zlib`/`lz4` compression, robust TLS/SSL support, and graceful shutdowns.
30
-
31
- You can install it via `pip` or `uv`:
32
- ```bash
33
- pip install amqp-rs
34
- ```
35
-
36
26
  ---
37
27
  ### Features:
38
28
  - Automatic creation and management of queues, exchanges and channels;
@@ -46,19 +36,22 @@ pip install amqp-rs
46
36
  ### Table of Compatibility
47
37
  | version | compatible with |
48
38
  | ---- | ---- |
39
+ | 0.2.1 | ~0.2.0 |
49
40
  | 0.2.0 | 0.2.0 |
50
41
  | 0.1.14 | ~0.1.12 |
51
42
 
52
43
  ### Installation
53
44
 
54
- You can install `amqp-client-python` using pip:
45
+ You can install `amqp-client-python` using uv or pip:
55
46
  ```bash
47
+ uv add amqp-client-python
48
+ # or
56
49
  pip install amqp-client-python
57
50
  ```
58
51
 
59
52
  ### Prerequisites
60
53
 
61
- * Python 3.7+
54
+ * Python 3.9+
62
55
  * A running RabbitMQ instance.
63
56
 
64
57
  ### Examples:
@@ -75,15 +68,18 @@ from amqp_client_python import (
75
68
  )
76
69
  config = Config(Options("queue", "rpc_queue", "rpc_exchange"))
77
70
  eventbus = AsyncEventbusRabbitMQ(config)
78
- # publish
71
+ # publish (supports dict/str with auto-JSON, or raw bytes/bytearray without overhead)
72
+ await eventbus.publish("rpc_exchange", "routing.key", {"message": "content"})
73
+ await eventbus.publish("rpc_exchange", "routing.key", b"raw_binary_payload")
79
74
 
80
- eventbus.publish("rpc_exchange", "routing.key", "message_content")
81
- # subscribe
75
+ # subscribe (auto_decode=True delivers parsed JSON dict; auto_decode=False delivers raw bytes)
82
76
  async def subscribe_handler(body) -> None:
83
77
  print(body, type(body), flush=True) # handle messages
84
78
  await eventbus.subscribe("rpc_exchange", "routing.key", subscribe_handler)
79
+
85
80
  # rpc_publish
86
- response = await eventbus.rpc_client("rpc_exchange", "user.find", "message_content")
81
+ response = await eventbus.rpc_client("rpc_exchange", "user.find", {"name": "alice"})
82
+
87
83
  # provider
88
84
  async def rpc_provider_handler(body) -> bytes:
89
85
  print(f"body: {body}")
@@ -15,6 +15,12 @@ class Options:
15
15
  vhost: str = "/",
16
16
  heartbeat: Optional[int] = 60,
17
17
  publisher_confirms: bool = False,
18
+ rpc_exchange_type: str = "topic",
19
+ rpc_queue_durable: bool = True,
20
+ rpc_queue_auto_delete: bool = False,
21
+ exchange_type: str = "topic",
22
+ queue_durable: bool = True,
23
+ queue_auto_delete: bool = False,
18
24
  **kwargs: Dict[str, Any]
19
25
  ) -> None:
20
26
  """
@@ -32,6 +38,12 @@ class Options:
32
38
  vhost: vhost information
33
39
  heartbeat: interval between heartbeat
34
40
  publisher_confirms: wait for publisher confirmations
41
+ rpc_exchange_type: exchange type for RPC server (default 'topic')
42
+ rpc_queue_durable: durability for RPC server queue (default True)
43
+ rpc_queue_auto_delete: auto_delete for RPC server queue (default False)
44
+ exchange_type: exchange type for pub/sub subscriptions (default 'topic')
45
+ queue_durable: durability for pub/sub subscriptions queue (default True)
46
+ queue_auto_delete: auto_delete for pub/sub subscriptions queue (default False)
35
47
  **kwargs: pika options
36
48
 
37
49
  Examples:
@@ -56,4 +68,10 @@ class Options:
56
68
  self.vhost = vhost
57
69
  self.heartbeat = heartbeat
58
70
  self.publisher_confirms = publisher_confirms
71
+ self.rpc_exchange_type = kwargs.pop("rpc_exchange_type", rpc_exchange_type)
72
+ self.rpc_queue_durable = kwargs.pop("rpc_queue_durable", rpc_queue_durable)
73
+ self.rpc_queue_auto_delete = kwargs.pop("rpc_queue_auto_delete", rpc_queue_auto_delete)
74
+ self.exchange_type = kwargs.pop("exchange_type", exchange_type)
75
+ self.queue_durable = kwargs.pop("queue_durable", queue_durable)
76
+ self.queue_auto_delete = kwargs.pop("queue_auto_delete", queue_auto_delete)
59
77
  self.kwargs = kwargs
@@ -1,5 +1,5 @@
1
1
  from collections import deque
2
- from typing import MutableMapping, Mapping, Optional, Union, Dict, List
2
+ from typing import MutableMapping, Mapping, Optional, Union, Dict, List, Tuple, Any
3
3
  from .async_channel_factory import AsyncChannelFactoryRabbitMQ
4
4
  from amqp_client_python.exceptions import (
5
5
  NackException,
@@ -54,11 +54,20 @@ class AsyncChannel:
54
54
  self.auto_ack = auto_ack
55
55
  self.subscribes: Dict[str, Dict[str, HandlerType]] = {}
56
56
  self.re_subscribes: List[str] = []
57
+ self._compiled_patterns: Dict[str, List[Tuple[str, Any]]] = {}
58
+ self._queue_exchange_type: Dict[str, str] = {}
57
59
  self.consumers: Dict[str, bool] = {}
58
60
  self.publisher_confirms = False
59
61
  self._message_number = 0
60
62
  self._deliveries: MutableMapping[int, Future] = {}
61
63
  self.response_timeout = 60
64
+ self._declared_exchanges: Dict[str, Tuple[str, bool]] = {}
65
+
66
+ @staticmethod
67
+ def _serialize_payload(body, content_type: str = "application/json"):
68
+ if isinstance(body, (bytes, bytearray, memoryview)):
69
+ return body
70
+ return dumps(body)
62
71
 
63
72
  @property
64
73
  def is_open(self) -> Optional[bool]:
@@ -81,6 +90,7 @@ class AsyncChannel:
81
90
  """
82
91
  LOGGER.info("Channel opened")
83
92
  self._channel = channel
93
+ self._declared_exchanges.clear()
84
94
  self.add_on_channel_close_callback()
85
95
  self.publisher_confirms and self.add_publish_confirms()
86
96
  self._prefetch_count and self.set_qos(self._prefetch_count)
@@ -114,12 +124,14 @@ class AsyncChannel:
114
124
  :param pika.channel.Channel: The closed channel
115
125
  :param Exception reason: why the channel was closed
116
126
  """
117
- LOGGER.info(f"Channel {channel} was closed: {reason}")
127
+ LOGGER.warning(f"Channel {channel} was closed by RabbitMQ: {reason}")
118
128
  self._consuming = False
129
+ if hasattr(self, "_declared_exchanges"):
130
+ self._declared_exchanges.clear()
119
131
  if self._connection.is_closing or self._connection.is_closed:
120
132
  LOGGER.info("Connection is closing or already closed")
121
133
  else:
122
- LOGGER.info("Closing connection")
134
+ LOGGER.info(f"Closing connection due to channel closure: {reason}")
123
135
  self._connection.close()
124
136
 
125
137
  def add_publish_confirms(self):
@@ -154,14 +166,36 @@ class AsyncChannel:
154
166
  be invoked by pika.
155
167
  :param str|unicode exchange_name: The name of the exchange to declare
156
168
  """
157
- LOGGER.info(f"Declaring exchange: {exchange_name}")
169
+ if not exchange_name:
170
+ return
171
+
172
+ exchange_type_str = str(getattr(exchange_type, "value", exchange_type))
173
+ durable_bool = bool(durable)
174
+
175
+ if not hasattr(self, "_declared_exchanges"):
176
+ self._declared_exchanges = {}
177
+
178
+ if exchange_name in self._declared_exchanges:
179
+ prev_type, prev_durable = self._declared_exchanges[exchange_name]
180
+ if (prev_type, prev_durable) == (exchange_type_str, durable_bool):
181
+ return
182
+ LOGGER.warning(
183
+ f"Exchange '{exchange_name}' was already declared as type='{prev_type}', durable={prev_durable}. "
184
+ f"Re-declaring with type='{exchange_type_str}', durable={durable_bool} on the same channel may "
185
+ f"trigger PRECONDITION_FAILED."
186
+ )
187
+
188
+ self._declared_exchanges[exchange_name] = (exchange_type_str, durable_bool)
189
+ LOGGER.info(
190
+ f"Declaring exchange: {exchange_name} (type={exchange_type_str}, durable={durable_bool})"
191
+ )
158
192
  # Note: using functools.partial is not required, it is demonstrating
159
193
  # how arbitrary data can be passed to the callback when it is called
160
194
  cb = partial(self.on_exchange_declareok, userdata=exchange_name)
161
195
  self._channel.exchange_declare(
162
196
  exchange=exchange_name,
163
- exchange_type=exchange_type,
164
- durable=durable,
197
+ exchange_type=exchange_type_str,
198
+ durable=durable_bool,
165
199
  callback=cb,
166
200
  )
167
201
 
@@ -361,7 +395,7 @@ class AsyncChannel:
361
395
  self._channel.basic_publish(
362
396
  exchange_name,
363
397
  routing_key,
364
- dumps(body), # Serialize payload to JSON
398
+ self._serialize_payload(body, content_type),
365
399
  properties=BasicProperties(
366
400
  reply_to=self._callback_queue, # Response queue address
367
401
  correlation_id=corr_id, # Request/response matching ID
@@ -436,7 +470,7 @@ class AsyncChannel:
436
470
  self._channel.basic_publish(
437
471
  exchange_name,
438
472
  routing_key,
439
- dumps(body),
473
+ self._serialize_payload(body, content_type),
440
474
  properties=BasicProperties(
441
475
  reply_to=self._callback_queue,
442
476
  content_type=content_type,
@@ -491,13 +525,14 @@ class AsyncChannel:
491
525
  exchange_type="topic",
492
526
  durable=True,
493
527
  auto_delete=False,
528
+ auto_decode=True,
494
529
  ):
495
530
  await self.start_rpc_publisher()
496
531
  self.register_queue(
497
532
  exchange_name, queue_name, exchange_type, durable, auto_delete
498
533
  )
499
534
  self.register_handler(
500
- queue_name, routing_key, callback, content_type, timeout
535
+ queue_name, routing_key, callback, content_type, timeout, auto_decode=auto_decode
501
536
  )
502
537
 
503
538
  self.queue_bind(queue_name, exchange_name, routing_key)
@@ -518,30 +553,52 @@ class AsyncChannel:
518
553
  self,
519
554
  exchange_name,
520
555
  queue_name: str,
521
- exchange_type="direct",
556
+ exchange_type="topic",
522
557
  durable=True,
523
558
  auto_delete=False,
524
559
  ):
525
560
  if queue_name not in self.subscribes:
526
- if not len(self.subscribes):
527
- self.subscribes[queue_name] = {}
528
- self.setup_exchange(exchange_name, exchange_type)
561
+ self.subscribes[queue_name] = {}
562
+ exchange_type_str = str(getattr(exchange_type, "value", exchange_type))
563
+ if not hasattr(self, "_queue_exchange_type"):
564
+ self._queue_exchange_type = {}
565
+ self._queue_exchange_type[queue_name] = exchange_type_str
566
+
567
+ self.setup_exchange(exchange_name, exchange_type, durable=durable)
529
568
  self.queue_declare(queue_name, durable=durable, auto_delete=auto_delete)
530
569
 
531
570
  if queue_name not in self.consumers:
532
571
  self.consumers[queue_name] = False
533
572
 
573
+ def _compile_routing_pattern(self, queue_name: str, routing_key: str):
574
+ if not hasattr(self, "_compiled_patterns"):
575
+ self._compiled_patterns = {}
576
+ if queue_name not in self._compiled_patterns:
577
+ self._compiled_patterns[queue_name] = []
578
+
579
+ if "*" in routing_key or "#" in routing_key:
580
+ # AMQP topic wildcard pattern (* = single word [^.]+, # = zero or more words .*)
581
+ amqp_regex = "^" + re.escape(routing_key).replace(r"\*", r"[^.]+").replace(r"\#", r".*") + "$"
582
+ try:
583
+ self._compiled_patterns[queue_name].append((routing_key, re.compile(amqp_regex)))
584
+ except re.error:
585
+ pass
586
+
534
587
  def register_handler(
535
- self, queue_name, routing_key, callback, content_type, response_timeout
588
+ self, queue_name, routing_key, callback, content_type, response_timeout, auto_decode=True
536
589
  ):
590
+ if queue_name not in self.subscribes:
591
+ self.subscribes[queue_name] = {}
537
592
  if routing_key not in self.subscribes[queue_name]:
538
593
  self.subscribes[queue_name][routing_key] = {
539
594
  "handle": callback,
540
595
  "content_type": content_type,
541
596
  "response_timeout": response_timeout or self.response_timeout,
597
+ "auto_decode": auto_decode,
542
598
  }
543
599
  if "*" in routing_key and routing_key not in self.re_subscribes:
544
600
  self.re_subscribes.append(routing_key)
601
+ self._compile_routing_pattern(queue_name, routing_key)
545
602
 
546
603
  async def subscribe(
547
604
  self,
@@ -554,12 +611,13 @@ class AsyncChannel:
554
611
  exchange_type: str = "topic",
555
612
  durable: bool = True,
556
613
  auto_delete: bool = False,
614
+ auto_decode: bool = True,
557
615
  ) -> None:
558
616
  self.register_queue(
559
617
  exchange_name, queue_name, exchange_type, durable, auto_delete
560
618
  )
561
619
  self.register_handler(
562
- queue_name, routing_key, callback, content_type, timeout
620
+ queue_name, routing_key, callback, content_type, timeout, auto_decode=auto_decode
563
621
  )
564
622
 
565
623
  self.queue_bind(queue_name, exchange_name, routing_key)
@@ -587,7 +645,11 @@ class AsyncChannel:
587
645
 
588
646
  async def process_message(handler: HandlerType):
589
647
  nonlocal body
590
- body = loads(body)
648
+ if handler.get("auto_decode", True) and handler.get("content_type") == "application/json":
649
+ try:
650
+ body = loads(body)
651
+ except Exception:
652
+ pass
591
653
  response = await wait_for(
592
654
  handler["handle"](body),
593
655
  timeout=handler["response_timeout"],
@@ -609,30 +671,39 @@ class AsyncChannel:
609
671
 
610
672
  async def handle_message(queue_name, basic_deliver, props):
611
673
  try:
612
- if basic_deliver.routing_key in self.subscribes[queue_name]:
613
- await process_message(
614
- self.subscribes[queue_name][basic_deliver.routing_key]
615
- )
616
- else:
617
- for routing_key in self.re_subscribes:
618
- if re.match(routing_key, basic_deliver.routing_key):
619
- return await process_message(
620
- self.subscribes[queue_name][routing_key]
621
- )
622
- if self.rpc_publisher_started and props.reply_to:
623
- self._channel_rpc.basic_publish(
624
- "",
625
- props.reply_to,
626
- "Provider Error: routing_key not binded",
627
- properties=BasicProperties(
628
- correlation_id=props.correlation_id,
629
- content_type="text/plain",
630
- type="error",
631
- ),
632
- )
633
- return not self.auto_ack and self._channel.is_open and self._channel.basic_nack(
634
- basic_deliver.delivery_tag, requeue=False
674
+ handlers = self.subscribes.get(queue_name, {})
675
+ handler = handlers.get(basic_deliver.routing_key)
676
+ if handler:
677
+ return await process_message(handler)
678
+
679
+ queue_type = getattr(self, "_queue_exchange_type", {}).get(queue_name, "topic")
680
+ if queue_type != "direct":
681
+ patterns = getattr(self, "_compiled_patterns", {}).get(queue_name)
682
+ if patterns:
683
+ for pattern_key, pattern in patterns:
684
+ if pattern.match(basic_deliver.routing_key):
685
+ pattern_handler = handlers.get(pattern_key)
686
+ if pattern_handler:
687
+ return await process_message(pattern_handler)
688
+ elif self.re_subscribes:
689
+ for routing_key in self.re_subscribes:
690
+ if routing_key in handlers and re.match(routing_key, basic_deliver.routing_key):
691
+ return await process_message(handlers[routing_key])
692
+
693
+ if self.rpc_publisher_started and props.reply_to:
694
+ self._channel_rpc.basic_publish(
695
+ "",
696
+ props.reply_to,
697
+ "Provider Error: routing_key not binded",
698
+ properties=BasicProperties(
699
+ correlation_id=props.correlation_id,
700
+ content_type="text/plain",
701
+ type="error",
702
+ ),
635
703
  )
704
+ return not self.auto_ack and self._channel.is_open and self._channel.basic_nack(
705
+ basic_deliver.delivery_tag, requeue=False
706
+ )
636
707
 
637
708
  except BaseException as err:
638
709
  if self.rpc_publisher_started and props.reply_to: