rabbitmq-amqp-python-client 2.0.0a3__tar.gz → 2.0.0b2__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 (114) hide show
  1. rabbitmq_amqp_python_client-2.0.0b2/.dockerignore +15 -0
  2. rabbitmq_amqp_python_client-2.0.0b2/AGENTS.md +46 -0
  3. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/PKG-INFO +16 -3
  4. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/README.md +14 -2
  5. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/README.md +2 -1
  6. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/console_application.py +158 -26
  7. rabbitmq_amqp_python_client-2.0.0b2/docs/examples/docker/reliable/Dockerfile +45 -0
  8. rabbitmq_amqp_python_client-2.0.0b2/docs/examples/docker/reliable/README.md +60 -0
  9. rabbitmq_amqp_python_client-2.0.0b2/docs/examples/reliable.py +619 -0
  10. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/pyproject.toml +2 -1
  11. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/consumer.py +51 -11
  12. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/link.py +60 -20
  13. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/session.py +15 -3
  14. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/wire/encoding.py +50 -7
  15. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/integration/test_consumer_integration.py +72 -0
  16. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/fake_broker.py +3 -1
  17. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_consumer.py +209 -0
  18. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_link.py +1 -1
  19. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_session.py +82 -1
  20. rabbitmq_amqp_python_client-2.0.0b2/tests/unit/test_wire_fuzz.py +188 -0
  21. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/certs/ca.p12 +0 -0
  22. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/certs/ca_certificate.pem +0 -0
  23. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/certs/ca_key.pem +0 -0
  24. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/certs/client_localhost.p12 +0 -0
  25. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/certs/client_localhost_certificate.pem +0 -0
  26. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/certs/client_localhost_key.pem +0 -0
  27. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/certs/server_localhost.p12 +0 -0
  28. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/certs/server_localhost_certificate.pem +0 -0
  29. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/certs/server_localhost_key.pem +0 -0
  30. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/ubuntu/advanced.config +0 -0
  31. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/ubuntu/enabled_plugins +0 -0
  32. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/ubuntu/gha-log-check.sh +0 -0
  33. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/ubuntu/gha-setup.sh +0 -0
  34. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/ubuntu/log/.gitkeep +0 -0
  35. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.ci/ubuntu/rabbitmq.conf +0 -0
  36. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.github/release.yml +0 -0
  37. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.github/workflows/build-test.yaml +0 -0
  38. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.github/workflows/publish-pypi.yaml +0 -0
  39. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.github/workflows/publish_release.yaml +0 -0
  40. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/.gitignore +0 -0
  41. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/Makefile +0 -0
  42. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/create_tag.sh +0 -0
  43. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/auto_reconnection.py +0 -0
  44. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/basic_connection.py +0 -0
  45. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/consumer_example.py +0 -0
  46. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/consumer_timeout_example.py +0 -0
  47. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/delayed_retry_context_example.py +0 -0
  48. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/delayed_retry_example.py +0 -0
  49. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/direct_reply_to_example.py +0 -0
  50. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/management_example.py +0 -0
  51. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/performance_test.py +0 -0
  52. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/presettled_consumer_example.py +0 -0
  53. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/publisher_example.py +0 -0
  54. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/quorum_single_active_consumer.py +0 -0
  55. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/rejection_reason_example.py +0 -0
  56. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/stream_filtering.py +0 -0
  57. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/docs/examples/tls_connection.py +0 -0
  58. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/__init__.py +0 -0
  59. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/connection.py +0 -0
  60. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/constants.py +0 -0
  61. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/exceptions.py +0 -0
  62. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/logging_utils.py +0 -0
  63. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/management.py +0 -0
  64. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/publisher.py +0 -0
  65. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/reconnection.py +0 -0
  66. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/wire/__init__.py +0 -0
  67. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/wire/delivery_state.py +0 -0
  68. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/wire/frames.py +0 -0
  69. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/wire/message.py +0 -0
  70. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/wire/performatives.py +0 -0
  71. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/rabbitmq_amqp_python_client/wire/sasl.py +0 -0
  72. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/__init__.py +0 -0
  73. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/integration/__init__.py +0 -0
  74. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/integration/conftest.py +0 -0
  75. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/integration/test_client_interpolation_integration.py +0 -0
  76. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/integration/test_client_stream_interpolation_integration.py +0 -0
  77. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/integration/test_connection_integration.py +0 -0
  78. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/integration/test_console_application_integration.py +0 -0
  79. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/integration/test_management_integration.py +0 -0
  80. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/integration/test_performance_test_integration.py +0 -0
  81. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/integration/test_publisher_integration.py +0 -0
  82. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/integration/test_reconnection_integration.py +0 -0
  83. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/integration/test_session_link_integration.py +0 -0
  84. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/integration/test_tls_integration.py +0 -0
  85. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/__init__.py +0 -0
  86. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/conftest.py +0 -0
  87. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/resources/amqp10_validation/empty_message.amqp +0 -0
  88. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/resources/amqp10_validation/header_amqpvalue_message.amqp +0 -0
  89. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/resources/amqp10_validation/message_body_250.amqp +0 -0
  90. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/resources/amqp10_validation/message_body_700.amqp +0 -0
  91. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/resources/amqp10_validation/message_from_version_1_0_0.amqp +0 -0
  92. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/resources/amqp10_validation/message_random_application_properties_300.amqp +0 -0
  93. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/resources/amqp10_validation/message_random_application_properties_500.amqp +0 -0
  94. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/resources/amqp10_validation/message_random_application_properties_properties_900.amqp +0 -0
  95. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/resources/amqp10_validation/message_unicode_message.amqp +0 -0
  96. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/resources/amqp10_validation/nil_and_types.amqp +0 -0
  97. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/resources/amqp10_validation/shovel_annotations.amqp +0 -0
  98. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/resources/amqp10_validation/static_test_message_compare.amqp +0 -0
  99. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/resources/amqp10_validation/uuid_message.amqp +0 -0
  100. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_amqp10_validation.py +0 -0
  101. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_connection.py +0 -0
  102. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_console_application.py +0 -0
  103. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_delivery_state.py +0 -0
  104. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_encoding.py +0 -0
  105. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_frames.py +0 -0
  106. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_management.py +0 -0
  107. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_management_builders.py +0 -0
  108. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_message.py +0 -0
  109. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_package_exports.py +0 -0
  110. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_performance_test.py +0 -0
  111. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_performatives.py +0 -0
  112. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_publisher.py +0 -0
  113. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_reconnection.py +0 -0
  114. {rabbitmq_amqp_python_client-2.0.0a3 → rabbitmq_amqp_python_client-2.0.0b2}/tests/unit/test_sasl.py +0 -0
@@ -0,0 +1,15 @@
1
+ .git
2
+ .github
3
+ .venv
4
+ .mypy_cache
5
+ .pytest_cache
6
+ .ruff_cache
7
+ .idea
8
+ .claude
9
+ __pycache__
10
+ *.pyc
11
+ dist
12
+ build
13
+ *.egg-info
14
+ tests
15
+ .ci
@@ -0,0 +1,46 @@
1
+ # AGENTS.md
2
+
3
+ Guidance for AI coding agents working in this repository.
4
+
5
+ ## Project
6
+
7
+ RabbitMQ AMQP 1.0 Python client (version 2.x), a native Python implementation
8
+ with no dependency on `qpid-proton`. See `README.md` for install instructions
9
+ and a quick-start example, and `README.md#project-layout` for how the code is
10
+ organized (`wire/` is the protocol layer; `connection.py`, `session.py`,
11
+ `link.py`, `management.py`, `publisher.py`, `consumer.py`, `reconnection.py`
12
+ build the public API on top of it).
13
+
14
+ ## Setup
15
+
16
+ ```sh
17
+ make install
18
+ ```
19
+
20
+ ## Common commands
21
+
22
+ ```sh
23
+ make format # ruff format, then ruff check --fix
24
+ make lint # ruff check and ruff format --check
25
+ make typecheck # mypy rabbitmq_amqp_python_client, in strict mode
26
+ make test-unit # the unit suite; no broker needed
27
+ make test-integration # the integration suite; needs a broker on localhost:5672
28
+ make test # both suites
29
+ ```
30
+
31
+ Run `make lint` and `make typecheck` before considering a change done.
32
+ `make test-unit` needs no broker; `make test-integration` needs one running
33
+ on `localhost:5672`.
34
+
35
+ ## Contributing
36
+
37
+ Every contribution — bug fix or feature — must include one of the following:
38
+
39
+ - A **failing test case** that reproduces the issue and passes once the fix
40
+ is applied (add it under `tests/unit` or `tests/integration`, matching the
41
+ existing style in that directory).
42
+ - A **small, runnable example** that clearly reproduces the issue, in the
43
+ style of the scripts under `docs/examples/`.
44
+
45
+ Do not open a change without one of these — it's how reviewers (human or
46
+ agent) confirm the problem is real and that the fix actually addresses it.
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: rabbitmq-amqp-python-client
3
- Version: 2.0.0a3
3
+ Version: 2.0.0-beta.2
4
4
  Summary: A from-scratch AMQP 1.0 client for RabbitMQ, built directly against the wire protocol.
5
5
  Author: RabbitMQ Team
6
6
  License: Apache-2.0
7
7
  Requires-Python: >=3.10
8
8
  Provides-Extra: dev
9
+ Requires-Dist: hypothesis>=6.100; extra == 'dev'
9
10
  Requires-Dist: mypy>=2.3.1; extra == 'dev'
10
11
  Requires-Dist: pika>=1.4.2; extra == 'dev'
11
12
  Requires-Dist: pytest-cov>=5.0; extra == 'dev'
@@ -18,13 +19,25 @@ Description-Content-Type: text/markdown
18
19
 
19
20
  RabbitMQ AMQP 1.0 Python client version 2.x for RabbitMQ 4.x.
20
21
 
21
- This client version built from scratch. The primary reason for this rewrite is to remove the dependency on the `qpid-proton` C library present in version 1, providing a fully native Python client.
22
+ Client is tested and validated against RabbitMQ 4.3.0
23
+
24
+ This client version built from scratch.</br>
25
+ The primary reason for this rewrite is to remove the dependency on the `qpid-proton` C library present in version 1.0, providing a fully native Python client, see [here](https://github.com/rabbitmq/rabbitmq-amqp-python-client/discussions/123).
22
26
  This client is inspired by the Java and .NET AMQP 1.0 clients, aiming to deliver the same behavior and user experience across ecosystems.
23
27
 
24
28
  The client is different from 1.0 and not compatible, so read the documentation before updating the client.
25
29
 
26
30
 
27
- ## Installation
31
+ ## Install
32
+
33
+ The client is distributed via [`PIP`](https://pypi.org/project/rabbitmq-amqp-python-client/):
34
+ ```bash
35
+ pip install rabbitmq-amqp-python-client==2.0.0ax
36
+ ```
37
+
38
+
39
+
40
+ ## Build from source
28
41
 
29
42
  ```sh
30
43
  python3 -m venv .venv
@@ -2,13 +2,25 @@
2
2
 
3
3
  RabbitMQ AMQP 1.0 Python client version 2.x for RabbitMQ 4.x.
4
4
 
5
- This client version built from scratch. The primary reason for this rewrite is to remove the dependency on the `qpid-proton` C library present in version 1, providing a fully native Python client.
5
+ Client is tested and validated against RabbitMQ 4.3.0
6
+
7
+ This client version built from scratch.</br>
8
+ The primary reason for this rewrite is to remove the dependency on the `qpid-proton` C library present in version 1.0, providing a fully native Python client, see [here](https://github.com/rabbitmq/rabbitmq-amqp-python-client/discussions/123).
6
9
  This client is inspired by the Java and .NET AMQP 1.0 clients, aiming to deliver the same behavior and user experience across ecosystems.
7
10
 
8
11
  The client is different from 1.0 and not compatible, so read the documentation before updating the client.
9
12
 
10
13
 
11
- ## Installation
14
+ ## Install
15
+
16
+ The client is distributed via [`PIP`](https://pypi.org/project/rabbitmq-amqp-python-client/):
17
+ ```bash
18
+ pip install rabbitmq-amqp-python-client==2.0.0ax
19
+ ```
20
+
21
+
22
+
23
+ ## Build from source
12
24
 
13
25
  ```sh
14
26
  python3 -m venv .venv
@@ -18,7 +18,8 @@ one layer at a time, and the rest build on them.
18
18
  | [`publisher_example.py`](publisher_example.py) | The three targets a publisher can have — a queue, an exchange plus a routing key, and none at all. The anonymous publisher sends to two different addresses over one link by setting `properties.to` per message, and a publish to an unbound routing key shows a `RELEASED` outcome, which is not an error. |
19
19
  | [`consumer_example.py`](consumer_example.py) | A `message_handler` that accepts and counts every delivery, `unsettled_message_count` while it does, and `pause()`/`unpause()` as flow control: messages published while paused stay queued and arrive the moment credit is restored. |
20
20
  | [`auto_reconnection.py`](auto_reconnection.py) | A connection surviving a forced socket drop: publishers and consumers built before the drop keep working afterwards, and `RecoveryConfiguration(topology=True)` re-declares the topology the broker lost. |
21
- | [`console_application.py`](console_application.py) | The whole client end to end — declare, publish, consume, report — as a scriptable smoke test with meaningful exit codes. `--help` lists its options. |
21
+ | [`console_application.py`](console_application.py) | The whole client end to end — declare, publish, consume, report — as a scriptable smoke test with meaningful exit codes. `--help` lists its options, every one of which also has an `AMQP_*` environment-variable override; see [`docker/`](docker/) for running it as a container configured entirely that way. |
22
+ | [`reliable.py`](reliable.py) | Modeled on [rabbitmq-amqp-go-client's own `reliable` example](https://github.com/rabbitmq/rabbitmq-amqp-go-client/tree/main/docs/examples/reliable): a publisher and a consumer built once on a quorum queue and left running while a broker disconnect/reconnect can happen at any point without either being rebuilt, with every publish outcome classified and periodic stats logged throughout. Configured entirely through environment variables, no command-line flags; see [`docker/reliable/`](docker/reliable/) for running it as a container. |
22
23
  | [`performance_test.py`](performance_test.py) | A throughput/latency generator: many publishers and consumers on one or more connections, with periodic rate and latency reporting. |
23
24
  | [`presettled_consumer_example.py`](presettled_consumer_example.py) | At-most-once consumption: `settle_strategy(ConsumerSettleStrategy.PRESETTLED)` attaches with `snd-settle-mode = settled`, so the broker settles every delivery itself, the handler never touches its `Context`, and `unsettled_message_count` stays at `0` for the consumer's whole life — including across a forced disconnect that auto-reconnection recovers from. |
24
25
  | [`direct_reply_to_example.py`](direct_reply_to_example.py) | Request/reply without a dedicated reply queue: `settle_strategy(ConsumerSettleStrategy.DIRECT_REPLY_TO)` attaches to no queue at all and reads back a broker-generated pseudo-queue address from `consumer.queue`; a request naming it as `properties.reply_to` gets answered by an ordinary queue-bound consumer publishing to that address. The pseudo-queue is session-scoped, so a forced disconnect and auto-reconnection leave the requester with a brand new address, which the script re-reads before sending its second request. |
@@ -10,6 +10,15 @@ both confirmed and consumed, so this doubles as a scriptable smoke test::
10
10
  python3 docs/examples/console_application.py --messages 500 --queue-type quorum
11
11
  python3 docs/examples/console_application.py --help
12
12
 
13
+ Every flag also has an ``AMQP_*`` environment variable that sets its default
14
+ (a flag given on the command line always wins); see each flag's ``--help``
15
+ text for its variable's name. That is what lets docs/examples/docker/Dockerfile
16
+ run this script as a container configured entirely through ``docker run -e``,
17
+ with nothing broker-specific baked into the image::
18
+
19
+ docker build -f docs/examples/docker/Dockerfile -t console-application .
20
+ docker run --rm -e AMQP_HOST=rabbitmq -e AMQP_MESSAGES=500 console-application
21
+
13
22
  This program adds nothing to the client's public surface: it is built entirely
14
23
  out of :class:`~rabbitmq_amqp_python_client.Connection`,
15
24
  :class:`~rabbitmq_amqp_python_client.Management`,
@@ -33,6 +42,7 @@ from __future__ import annotations
33
42
 
34
43
  import argparse
35
44
  import logging
45
+ import os
36
46
  import ssl
37
47
  import sys
38
48
  import threading
@@ -44,9 +54,12 @@ from pathlib import Path
44
54
  from typing import TYPE_CHECKING, TextIO
45
55
 
46
56
  # Make the client importable when this example is run straight from a checkout,
47
- # with neither the package installed nor PYTHONPATH set.
48
- _SOURCE_ROOT = Path(__file__).resolve().parents[2]
49
- if _SOURCE_ROOT.is_dir() and str(_SOURCE_ROOT) not in sys.path:
57
+ # with neither the package installed nor PYTHONPATH set. Guarded by a length
58
+ # check because a containerized copy of this script (see docs/examples/docker/)
59
+ # lives too shallow in the image's filesystem for a third parent to exist.
60
+ _RESOLVED_PARENTS = Path(__file__).resolve().parents
61
+ _SOURCE_ROOT = _RESOLVED_PARENTS[2] if len(_RESOLVED_PARENTS) > 2 else None
62
+ if _SOURCE_ROOT is not None and _SOURCE_ROOT.is_dir() and str(_SOURCE_ROOT) not in sys.path:
50
63
  sys.path.insert(0, str(_SOURCE_ROOT))
51
64
 
52
65
  from rabbitmq_amqp_python_client import ( # noqa: E402 - after the sys.path bootstrap above
@@ -93,6 +106,79 @@ QUEUE_TYPES = (QUEUE_TYPE_CLASSIC, QUEUE_TYPE_QUORUM, QUEUE_TYPE_STREAM)
93
106
  #: Prefix of the queue name generated when ``--queue`` is not given.
94
107
  GENERATED_QUEUE_PREFIX = "console-app-"
95
108
 
109
+ # --- environment overrides (§2) -----------------------------------------
110
+ #
111
+ # Every flag below also reads an ``AMQP_*`` environment variable as its
112
+ # default, before argparse's own default applies; a flag given on the command
113
+ # line always wins. This is what lets docs/examples/docker/Dockerfile
114
+ # configure a run entirely through ``docker run -e``, with no broker detail
115
+ # baked into the image.
116
+
117
+ _ENV_TRUE_VALUES = frozenset({"1", "true", "yes", "on"})
118
+ _ENV_FALSE_VALUES = frozenset({"0", "false", "no", "off"})
119
+
120
+
121
+ def _env_str(name: str, default: str) -> str:
122
+ """Return the string environment variable ``name``, or ``default`` if unset."""
123
+ return os.environ.get(name, default)
124
+
125
+
126
+ def _env_optional_int(name: str) -> int | None:
127
+ """Return ``name`` parsed as an int, or ``None`` if unset.
128
+
129
+ Raises:
130
+ OptionsError: If ``name`` is set but is not a valid integer.
131
+ """
132
+ value = os.environ.get(name)
133
+ if value is None:
134
+ return None
135
+ try:
136
+ return int(value)
137
+ except ValueError:
138
+ raise OptionsError(f"{name}={value!r} is not a valid integer") from None
139
+
140
+
141
+ def _env_int(name: str, default: int) -> int:
142
+ """Return ``name`` parsed as an int, or ``default`` if unset."""
143
+ value = _env_optional_int(name)
144
+ return default if value is None else value
145
+
146
+
147
+ def _env_float(name: str, default: float) -> float:
148
+ """Return ``name`` parsed as a float, or ``default`` if unset.
149
+
150
+ Raises:
151
+ OptionsError: If ``name`` is set but is not a valid float.
152
+ """
153
+ value = os.environ.get(name)
154
+ if value is None:
155
+ return default
156
+ try:
157
+ return float(value)
158
+ except ValueError:
159
+ raise OptionsError(f"{name}={value!r} is not a valid float") from None
160
+
161
+
162
+ def _env_bool(name: str, default: bool) -> bool:
163
+ """Return the boolean ``name`` is set to, or ``default`` if unset.
164
+
165
+ Accepts ``1``/``true``/``yes``/``on`` and ``0``/``false``/``no``/``off``,
166
+ case-insensitively.
167
+
168
+ Raises:
169
+ OptionsError: If ``name`` is set to anything else.
170
+ """
171
+ value = os.environ.get(name)
172
+ if value is None:
173
+ return default
174
+ normalized = value.strip().lower()
175
+ if normalized in _ENV_TRUE_VALUES:
176
+ return True
177
+ if normalized in _ENV_FALSE_VALUES:
178
+ return False
179
+ raise OptionsError(f"{name}={value!r} is not a valid boolean (true/false, yes/no, on/off, 1/0)")
180
+
181
+
96
182
  # --- timing -------------------------------------------------------------
97
183
 
98
184
  #: How long the drain wait blocks per poll before re-reading the counters.
@@ -169,8 +255,19 @@ def build_parser() -> argparse.ArgumentParser:
169
255
 
170
256
  Returns:
171
257
  A parser whose defaults match :class:`Options`', except for ``--queue``,
172
- which is generated per run by :func:`parse_args`.
258
+ which is generated per run by :func:`parse_args`. Every default is first
259
+ read from its ``AMQP_*`` environment variable, falling back to
260
+ :class:`Options`' own default when that variable is unset.
261
+
262
+ Raises:
263
+ OptionsError: If an ``AMQP_*`` environment variable is set to a value
264
+ its flag cannot accept — the same rejection :func:`parse_args`
265
+ raises for a bad command-line value, just discovered earlier.
173
266
  """
267
+ queue_type_default = _env_str("AMQP_QUEUE_TYPE", QUEUE_TYPE_CLASSIC)
268
+ if queue_type_default not in QUEUE_TYPES:
269
+ raise OptionsError(f"AMQP_QUEUE_TYPE={queue_type_default!r} must be one of {', '.join(QUEUE_TYPES)}")
270
+
174
271
  parser = argparse.ArgumentParser(
175
272
  prog="console_application.py",
176
273
  description="Publish and consume messages against one RabbitMQ queue, then report what happened.",
@@ -179,52 +276,87 @@ def build_parser() -> argparse.ArgumentParser:
179
276
  "-n",
180
277
  "--messages",
181
278
  type=int,
182
- default=DEFAULT_MESSAGE_COUNT,
183
- help="how many messages to publish (default: %(default)s); 0 is rejected",
279
+ default=_env_int("AMQP_MESSAGES", DEFAULT_MESSAGE_COUNT),
280
+ help="how many messages to publish (default: %(default)s, env: AMQP_MESSAGES); 0 is rejected",
184
281
  )
185
282
  parser.add_argument(
186
283
  "--queue-type",
187
284
  choices=QUEUE_TYPES,
188
- default=QUEUE_TYPE_CLASSIC,
189
- help="which queue sub-builder declares the queue (default: %(default)s)",
285
+ default=queue_type_default,
286
+ help="which queue sub-builder declares the queue (default: %(default)s, env: AMQP_QUEUE_TYPE)",
287
+ )
288
+ parser.add_argument(
289
+ "--queue",
290
+ default=_env_str("AMQP_QUEUE", ""),
291
+ help="queue name (default: a generated console-app-* name, env: AMQP_QUEUE)",
292
+ )
293
+ parser.add_argument(
294
+ "--keep-queue",
295
+ action="store_true",
296
+ default=_env_bool("AMQP_KEEP_QUEUE", False),
297
+ help="do not delete the queue during teardown (env: AMQP_KEEP_QUEUE)",
190
298
  )
191
- parser.add_argument("--queue", default="", help="queue name (default: a generated console-app-* name)")
192
- parser.add_argument("--keep-queue", action="store_true", help="do not delete the queue during teardown")
193
299
  parser.add_argument(
194
300
  "--consume-timeout",
195
301
  type=float,
196
- default=DEFAULT_CONSUME_TIMEOUT_SECONDS,
197
- help="seconds to wait for consumption to catch up (default: %(default)s)",
302
+ default=_env_float("AMQP_CONSUME_TIMEOUT", DEFAULT_CONSUME_TIMEOUT_SECONDS),
303
+ help="seconds to wait for consumption to catch up (default: %(default)s, env: AMQP_CONSUME_TIMEOUT)",
198
304
  )
199
305
  parser.add_argument(
200
306
  "--publish-timeout",
201
307
  type=float,
202
- default=DEFAULT_PUBLISH_TIMEOUT_SECONDS,
203
- help="per-call publish timeout, in seconds (default: %(default)s)",
308
+ default=_env_float("AMQP_PUBLISH_TIMEOUT", DEFAULT_PUBLISH_TIMEOUT_SECONDS),
309
+ help="per-call publish timeout, in seconds (default: %(default)s, env: AMQP_PUBLISH_TIMEOUT)",
204
310
  )
205
311
  parser.add_argument(
206
312
  "--stats-interval",
207
313
  type=float,
208
- default=DEFAULT_STATS_INTERVAL_SECONDS,
209
- help="seconds between periodic stats blocks (default: %(default)s)",
314
+ default=_env_float("AMQP_STATS_INTERVAL", DEFAULT_STATS_INTERVAL_SECONDS),
315
+ help="seconds between periodic stats blocks (default: %(default)s, env: AMQP_STATS_INTERVAL)",
316
+ )
317
+ parser.add_argument(
318
+ "--host",
319
+ default=_env_str("AMQP_HOST", "localhost"),
320
+ help="broker host (default: %(default)s, env: AMQP_HOST)",
321
+ )
322
+ parser.add_argument(
323
+ "--port",
324
+ type=int,
325
+ default=_env_optional_int("AMQP_PORT"),
326
+ help="broker port (default: 5672, or 5671 with --tls; env: AMQP_PORT)",
327
+ )
328
+ parser.add_argument(
329
+ "--user",
330
+ default=_env_str("AMQP_USER", "guest"),
331
+ help="SASL PLAIN username (default: %(default)s, env: AMQP_USER)",
332
+ )
333
+ parser.add_argument(
334
+ "--password",
335
+ default=_env_str("AMQP_PASSWORD", "guest"),
336
+ help="SASL PLAIN password (default: %(default)s, env: AMQP_PASSWORD)",
337
+ )
338
+ parser.add_argument(
339
+ "--vhost",
340
+ default=_env_str("AMQP_VHOST", "/"),
341
+ help="virtual host (default: %(default)s, env: AMQP_VHOST)",
342
+ )
343
+ parser.add_argument(
344
+ "--tls",
345
+ action="store_true",
346
+ default=_env_bool("AMQP_TLS", False),
347
+ help="wrap the connection in TLS (env: AMQP_TLS)",
210
348
  )
211
- parser.add_argument("--host", default="localhost", help="broker host (default: %(default)s)")
212
- parser.add_argument("--port", type=int, default=None, help="broker port (default: 5672, or 5671 with --tls)")
213
- parser.add_argument("--user", default="guest", help="SASL PLAIN username (default: %(default)s)")
214
- parser.add_argument("--password", default="guest", help="SASL PLAIN password (default: %(default)s)")
215
- parser.add_argument("--vhost", default="/", help="virtual host (default: %(default)s)")
216
- parser.add_argument("--tls", action="store_true", help="wrap the connection in TLS")
217
349
  parser.add_argument(
218
350
  "--recovery",
219
351
  action=argparse.BooleanOptionalAction,
220
- default=True,
221
- help="auto-reconnect after an unexpected disconnect (default: enabled)",
352
+ default=_env_bool("AMQP_RECOVERY", True),
353
+ help="auto-reconnect after an unexpected disconnect (default: enabled, env: AMQP_RECOVERY)",
222
354
  )
223
355
  parser.add_argument(
224
356
  "--recovery-topology",
225
357
  action=argparse.BooleanOptionalAction,
226
- default=False,
227
- help="redeclare recorded topology after a reconnect (default: disabled)",
358
+ default=_env_bool("AMQP_RECOVERY_TOPOLOGY", False),
359
+ help="redeclare recorded topology after a reconnect (default: disabled, env: AMQP_RECOVERY_TOPOLOGY)",
228
360
  )
229
361
  return parser
230
362
 
@@ -0,0 +1,45 @@
1
+ # Runs docs/examples/reliable.py as a container. Every runtime parameter is
2
+ # external — set with `docker run -e` — following the same pattern as
3
+ # rabbitmq-amqp-go-client's docs/examples/reliable/Dockerfile, which this
4
+ # example is itself modeled on.
5
+ #
6
+ # Build context must be the repository root, since the image needs both the
7
+ # installable package and the example script:
8
+ #
9
+ # docker build -f docs/examples/docker/reliable/Dockerfile -t reliable-example .
10
+ #
11
+ # Run against a broker reachable as `rabbitmq`, publishing 1000 messages
12
+ # non-interactively:
13
+ #
14
+ # docker run --rm -e AMQP_HOST=rabbitmq -e MESSAGES_TO_SEND=1000 reliable-example
15
+ #
16
+ # See docs/examples/docker/reliable/README.md for the full variable table.
17
+
18
+ FROM python:3.12-slim
19
+
20
+ WORKDIR /app
21
+
22
+ # The client (pyproject.toml's `dependencies = []`) is pure Python with no
23
+ # third-party dependencies, so it needs no `pip install` step — copying the
24
+ # package onto PYTHONPATH is enough, and keeps the build itself needing no
25
+ # network access beyond pulling this base image.
26
+ COPY rabbitmq_amqp_python_client ./rabbitmq_amqp_python_client
27
+ COPY docs/examples/reliable.py ./reliable.py
28
+ ENV PYTHONPATH=/app
29
+
30
+ # Matches reliable.py's own defaults, except QUEUE_NAME and IS_SILENT: a
31
+ # container has no interactive stdin to press a key on, and
32
+ # "clients-integration" is the queue name shared across the RabbitMQ clients'
33
+ # own Dockerfiles. AMQP_PASSWORD's default is RabbitMQ's own documented
34
+ # local-broker default, not a real secret.
35
+ ENV AMQP_HOST=localhost \
36
+ AMQP_USER=guest \
37
+ AMQP_PASSWORD=guest \
38
+ AMQP_VHOST=/ \
39
+ AMQP_TLS=false \
40
+ QUEUE_NAME=clients-integration \
41
+ MESSAGES_TO_SEND=500000 \
42
+ IS_SILENT=true \
43
+ DELAY_MESSAGE=false
44
+
45
+ ENTRYPOINT ["python", "reliable.py"]
@@ -0,0 +1,60 @@
1
+ # Reliable messaging, containerized
2
+
3
+ [`Dockerfile`](Dockerfile) packages [`reliable.py`](../../reliable.py) — a
4
+ long-running publish/consume demo that keeps running through a broker
5
+ disconnect and reconnect — as a container image, following the same pattern
6
+ as [rabbitmq-amqp-go-client's own reliable
7
+ example](https://github.com/rabbitmq/rabbitmq-amqp-go-client/tree/main/docs/examples/reliable).
8
+ Every runtime parameter is external: nothing broker-specific is baked into
9
+ the image, and every environment variable `reliable.py` reads can be set at
10
+ `docker run` time instead.
11
+
12
+ ## Build
13
+
14
+ Run from the repository root, since the build needs both the installable
15
+ package and the example script:
16
+
17
+ ```sh
18
+ docker build -f docs/examples/docker/reliable/Dockerfile -t reliable-example .
19
+ ```
20
+
21
+ ## Run
22
+
23
+ ```sh
24
+ docker run --rm \
25
+ -e AMQP_HOST=rabbitmq \
26
+ -e MESSAGES_TO_SEND=1000 \
27
+ reliable-example
28
+ ```
29
+
30
+ Against a broker on the Docker host rather than another container, use
31
+ `--network host` (Linux) or `-e AMQP_HOST=host.docker.internal` (macOS/Windows).
32
+
33
+ The image defaults `IS_SILENT=true`, since a container has no interactive
34
+ stdin to press a key on: the process keeps publishing, consuming and logging
35
+ periodic stats until the container is stopped (`docker stop`), rather than
36
+ waiting for input like an un-containerized run does by default.
37
+
38
+ ## Environment variables
39
+
40
+ Each variable sets that setting's value directly — there are no command-line
41
+ flags, matching the Go example this is modeled on. Defaults below are the
42
+ image's own (set in the `Dockerfile`), which match `reliable.py`'s
43
+ un-containerized defaults except where noted.
44
+
45
+ | Variable | Default | Purpose |
46
+ |---|---|---|
47
+ | `QUEUE_NAME` | `clients-integration` | Quorum queue to declare, publish to and consume from. |
48
+ | `MESSAGES_TO_SEND` | `500000` | How many messages the publish loop attempts; must be > 0. |
49
+ | `IS_SILENT` | `true` | Run forever instead of waiting on stdin before tearing down. |
50
+ | `DELAY_MESSAGE` | `false` | Pace the publish loop with a small sleep between messages. |
51
+ | `AMQP_HOST` | `localhost` | Broker host. |
52
+ | `AMQP_PORT` | unset (5672, or 5671 with `AMQP_TLS=true`) | Broker port. |
53
+ | `AMQP_USER` | `guest` | SASL PLAIN username. |
54
+ | `AMQP_PASSWORD` | `guest` | SASL PLAIN password. |
55
+ | `AMQP_VHOST` | `/` | Virtual host. |
56
+ | `AMQP_TLS` | `false` | Wrap the connection in TLS. |
57
+
58
+ Boolean variables accept `true`/`false`, `yes`/`no`, `on`/`off` or `1`/`0`,
59
+ case-insensitively; any other value is rejected before the container connects
60
+ to anything.