rabbitmq-amqp-python-client 0.2.0__tar.gz → 0.3.0__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 (53) hide show
  1. rabbitmq_amqp_python_client-0.3.0/PKG-INFO +104 -0
  2. rabbitmq_amqp_python_client-0.3.0/README.md +82 -0
  3. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/pyproject.toml +5 -1
  4. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/__init__.py +12 -0
  5. rabbitmq_amqp_python_client-0.3.0/rabbitmq_amqp_python_client/asyncio/__init__.py +13 -0
  6. rabbitmq_amqp_python_client-0.3.0/rabbitmq_amqp_python_client/asyncio/connection.py +317 -0
  7. rabbitmq_amqp_python_client-0.3.0/rabbitmq_amqp_python_client/asyncio/consumer.py +339 -0
  8. rabbitmq_amqp_python_client-0.3.0/rabbitmq_amqp_python_client/asyncio/environment.py +158 -0
  9. rabbitmq_amqp_python_client-0.3.0/rabbitmq_amqp_python_client/asyncio/management.py +375 -0
  10. rabbitmq_amqp_python_client-0.3.0/rabbitmq_amqp_python_client/asyncio/publisher.py +157 -0
  11. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/management.py +16 -4
  12. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/publisher.py +12 -1
  13. rabbitmq_amqp_python_client-0.2.0/PKG-INFO +0 -65
  14. rabbitmq_amqp_python_client-0.2.0/README.md +0 -43
  15. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/LICENSE +0 -0
  16. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/address_helper.py +0 -0
  17. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/amqp_consumer_handler.py +0 -0
  18. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/common.py +0 -0
  19. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/connection.py +0 -0
  20. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/consumer.py +0 -0
  21. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/delivery_context.py +0 -0
  22. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/entities.py +0 -0
  23. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/environment.py +0 -0
  24. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/exceptions.py +0 -0
  25. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/options.py +0 -0
  26. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/py.typed +0 -0
  27. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/__init__.py +0 -0
  28. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_common.py +0 -0
  29. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_condition.py +0 -0
  30. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_data.py +0 -0
  31. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_delivery.py +0 -0
  32. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_endpoints.py +0 -0
  33. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_events.py +0 -0
  34. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_exceptions.py +0 -0
  35. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_handler.py +0 -0
  36. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_handlers.py +0 -0
  37. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_io.py +0 -0
  38. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_message.py +0 -0
  39. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_reactor.py +0 -0
  40. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_selectable.py +0 -0
  41. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_tracing.py +0 -0
  42. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_transport.py +0 -0
  43. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_url.py +0 -0
  44. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_utils.py +0 -0
  45. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/_wrapper.py +0 -0
  46. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/handlers.py +0 -0
  47. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/py.typed +0 -0
  48. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/reactor.py +0 -0
  49. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/tracing.py +0 -0
  50. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/qpid/proton/utils.py +0 -0
  51. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/queues.py +0 -0
  52. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/ssl_configuration.py +0 -0
  53. {rabbitmq_amqp_python_client-0.2.0 → rabbitmq_amqp_python_client-0.3.0}/rabbitmq_amqp_python_client/utils.py +0 -0
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: rabbitmq-amqp-python-client
3
+ Version: 0.3.0
4
+ Summary: Python RabbitMQ client for AMQP 1.0 protocol
5
+ License: Apache-2.0 license
6
+ License-File: LICENSE
7
+ Author: RabbitMQ team
8
+ Requires-Python: >=3.9,<4.0
9
+ Classifier: License :: Other/Proprietary License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Requires-Dist: packaging (>=23.0,<24.0)
18
+ Requires-Dist: python-qpid-proton (>=0.39.0,<0.40.0)
19
+ Requires-Dist: typing-extensions (>=4.13.0,<5.0.0)
20
+ Description-Content-Type: text/markdown
21
+
22
+ ## RabbitMQ AMQP 1.0 Python Client
23
+ This library is meant to be used with RabbitMQ `4.x`. Suitable for testing in pre-production environments.
24
+
25
+ The client is distributed via [`PIP`](https://pypi.org/project/rabbitmq-amqp-python-client/):
26
+ ```bash
27
+ pip install rabbitmq-amqp-python-client
28
+ ```
29
+
30
+ ### Getting Started
31
+
32
+ Inside the [examples](./examples) folder you can find a set of examples that show how to use the client.
33
+
34
+
35
+ ### Documentation
36
+
37
+ [Client Guide](https://www.rabbitmq.com/client-libraries/amqp-client-libraries) select the python section.
38
+
39
+
40
+ ### Build
41
+
42
+ - `make rabbitmq-server`: run the RabbitMQ server in a docker container
43
+ - `poetry build`: build the source project
44
+ - `poetry install`: resolves and install dependencies
45
+ - `make test`: run the tests
46
+
47
+ ### Note for MAC users:
48
+
49
+ To run TLS you need to:
50
+ ``` bash
51
+ - pip uninstall python-qpid-proton
52
+ - brew install swig
53
+ - brew install pkg-config
54
+ - export CFLAGS="-I/usr/local/opt/openssl/include"; pip install python-qpid-proton --verbose --no-cache-dir
55
+ ```
56
+
57
+ Read more about the issue [here](https://stackoverflow.com/questions/44979947/python-qpid-proton-for-mac-using-amqps)
58
+
59
+ ### SSL Problems in local environment
60
+
61
+ If when running tests, this exceptions is raised by the proton library: `SSLUnavailable`:
62
+ ``` bash
63
+ pip uninstall python-qpid-proton -y
64
+
65
+ sudo apt-get update
66
+ sudo apt-get install -y swig cmake build-essential libssl-dev pkg-config
67
+
68
+ export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
69
+ export CFLAGS="-I/usr/include/openssl"
70
+ export LDFLAGS="-L/usr/lib/x86_64-linux-gnu"
71
+
72
+ pip install "python-qpid-proton>=0.39.0,<0.40.0" --no-binary python-qpid-proton --verbose --no-cache-dir
73
+ ```
74
+
75
+ ### Async Interface (Experimental)
76
+
77
+ The client provides an async interface via the `rabbitmq_amqp_python_client.asyncio` module. The async classes act as facades that:
78
+
79
+ - Wrap the corresponding synchronous classes
80
+ - Execute blocking operations in a thread pool executor using `run_in_executor`
81
+ - Coordinate concurrent access using `asyncio.Lock`
82
+ - Implement proper async context managers (`async with`) for resource management
83
+ - Maintain API compatibility with the synchronous version
84
+
85
+ **Key differences from the synchronous interface:**
86
+
87
+ 1. Use `AsyncEnvironment` instead of `Environment`
88
+ 2. All operations must be awaited with `await`
89
+ 3. Use `async with` for resource management (connections, publishers, consumers, management)
90
+ 4. Consumer signal handling uses `asyncio.Event` and `loop.add_signal_handler`
91
+
92
+ For a complete example showing proper consumer termination and signal handling, refer to:
93
+
94
+ - [examples/getting_started/getting_started_async.py](./examples/getting_started/getting_started_async.py)
95
+
96
+ Additional async examples are available in the [examples](./examples) folder:
97
+
98
+ - OAuth2: [examples/oauth/oAuth2_async.py](./examples/oauth/oAuth2_async.py)
99
+ - Reconnection: [examples/reconnection/reconnection_example_async.py](./examples/reconnection/reconnection_example_async.py)
100
+ - Streams: [examples/streams/example_with_streams_async.py](./examples/streams/example_with_streams_async.py)
101
+ - TLS: [examples/tls/tls_example_async.py](./examples/tls/tls_example_async.py)
102
+
103
+
104
+
@@ -0,0 +1,82 @@
1
+ ## RabbitMQ AMQP 1.0 Python Client
2
+ This library is meant to be used with RabbitMQ `4.x`. Suitable for testing in pre-production environments.
3
+
4
+ The client is distributed via [`PIP`](https://pypi.org/project/rabbitmq-amqp-python-client/):
5
+ ```bash
6
+ pip install rabbitmq-amqp-python-client
7
+ ```
8
+
9
+ ### Getting Started
10
+
11
+ Inside the [examples](./examples) folder you can find a set of examples that show how to use the client.
12
+
13
+
14
+ ### Documentation
15
+
16
+ [Client Guide](https://www.rabbitmq.com/client-libraries/amqp-client-libraries) select the python section.
17
+
18
+
19
+ ### Build
20
+
21
+ - `make rabbitmq-server`: run the RabbitMQ server in a docker container
22
+ - `poetry build`: build the source project
23
+ - `poetry install`: resolves and install dependencies
24
+ - `make test`: run the tests
25
+
26
+ ### Note for MAC users:
27
+
28
+ To run TLS you need to:
29
+ ``` bash
30
+ - pip uninstall python-qpid-proton
31
+ - brew install swig
32
+ - brew install pkg-config
33
+ - export CFLAGS="-I/usr/local/opt/openssl/include"; pip install python-qpid-proton --verbose --no-cache-dir
34
+ ```
35
+
36
+ Read more about the issue [here](https://stackoverflow.com/questions/44979947/python-qpid-proton-for-mac-using-amqps)
37
+
38
+ ### SSL Problems in local environment
39
+
40
+ If when running tests, this exceptions is raised by the proton library: `SSLUnavailable`:
41
+ ``` bash
42
+ pip uninstall python-qpid-proton -y
43
+
44
+ sudo apt-get update
45
+ sudo apt-get install -y swig cmake build-essential libssl-dev pkg-config
46
+
47
+ export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
48
+ export CFLAGS="-I/usr/include/openssl"
49
+ export LDFLAGS="-L/usr/lib/x86_64-linux-gnu"
50
+
51
+ pip install "python-qpid-proton>=0.39.0,<0.40.0" --no-binary python-qpid-proton --verbose --no-cache-dir
52
+ ```
53
+
54
+ ### Async Interface (Experimental)
55
+
56
+ The client provides an async interface via the `rabbitmq_amqp_python_client.asyncio` module. The async classes act as facades that:
57
+
58
+ - Wrap the corresponding synchronous classes
59
+ - Execute blocking operations in a thread pool executor using `run_in_executor`
60
+ - Coordinate concurrent access using `asyncio.Lock`
61
+ - Implement proper async context managers (`async with`) for resource management
62
+ - Maintain API compatibility with the synchronous version
63
+
64
+ **Key differences from the synchronous interface:**
65
+
66
+ 1. Use `AsyncEnvironment` instead of `Environment`
67
+ 2. All operations must be awaited with `await`
68
+ 3. Use `async with` for resource management (connections, publishers, consumers, management)
69
+ 4. Consumer signal handling uses `asyncio.Event` and `loop.add_signal_handler`
70
+
71
+ For a complete example showing proper consumer termination and signal handling, refer to:
72
+
73
+ - [examples/getting_started/getting_started_async.py](./examples/getting_started/getting_started_async.py)
74
+
75
+ Additional async examples are available in the [examples](./examples) folder:
76
+
77
+ - OAuth2: [examples/oauth/oAuth2_async.py](./examples/oauth/oAuth2_async.py)
78
+ - Reconnection: [examples/reconnection/reconnection_example_async.py](./examples/reconnection/reconnection_example_async.py)
79
+ - Streams: [examples/streams/example_with_streams_async.py](./examples/streams/example_with_streams_async.py)
80
+ - TLS: [examples/tls/tls_example_async.py](./examples/tls/tls_example_async.py)
81
+
82
+
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "rabbitmq-amqp-python-client"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  description = "Python RabbitMQ client for AMQP 1.0 protocol"
5
5
  authors = ["RabbitMQ team"]
6
6
  license = "Apache-2.0 license"
@@ -23,7 +23,11 @@ pytest = "^8.3.4"
23
23
  black = "^24.3.0"
24
24
  python-qpid-proton = "^0.39.0"
25
25
  requests = "^2.31.0"
26
+ pytest-asyncio = "^1.2.0"
26
27
 
27
28
  [build-system]
28
29
  requires = ["poetry-core"]
29
30
  build-backend = "poetry.core.masonry.api"
31
+
32
+ [tool.pytest.ini_options]
33
+ asyncio_mode = "auto"
@@ -2,6 +2,13 @@ from importlib import metadata
2
2
 
3
3
  from .address_helper import AddressHelper
4
4
  from .amqp_consumer_handler import AMQPMessagingHandler
5
+ from .asyncio import (
6
+ AsyncConnection,
7
+ AsyncConsumer,
8
+ AsyncEnvironment,
9
+ AsyncManagement,
10
+ AsyncPublisher,
11
+ )
5
12
  from .common import ExchangeType, QueueType
6
13
  from .connection import Connection
7
14
  from .consumer import Consumer
@@ -99,4 +106,9 @@ __all__ = [
99
106
  "RecoveryConfiguration",
100
107
  "OAuth2Options",
101
108
  "Converter",
109
+ "AsyncConnection",
110
+ "AsyncConsumer",
111
+ "AsyncPublisher",
112
+ "AsyncManagement",
113
+ "AsyncEnvironment",
102
114
  ]
@@ -0,0 +1,13 @@
1
+ from .connection import AsyncConnection
2
+ from .consumer import AsyncConsumer
3
+ from .environment import AsyncEnvironment
4
+ from .management import AsyncManagement
5
+ from .publisher import AsyncPublisher
6
+
7
+ __all__ = [
8
+ "AsyncConnection",
9
+ "AsyncConsumer",
10
+ "AsyncManagement",
11
+ "AsyncPublisher",
12
+ "AsyncEnvironment",
13
+ ]
@@ -0,0 +1,317 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import logging
5
+ from typing import Callable, Optional, Union
6
+
7
+ from ..address_helper import validate_address
8
+ from ..connection import Connection
9
+ from ..consumer import Consumer
10
+ from ..entities import (
11
+ ConsumerOptions,
12
+ OAuth2Options,
13
+ RecoveryConfiguration,
14
+ )
15
+ from ..exceptions import ArgumentOutOfRangeException
16
+ from ..management import Management
17
+ from ..publisher import Publisher
18
+ from ..qpid.proton._handlers import MessagingHandler
19
+ from ..ssl_configuration import (
20
+ PosixSslConfigurationContext,
21
+ WinSslConfigurationContext,
22
+ )
23
+ from .consumer import AsyncConsumer
24
+ from .management import AsyncManagement
25
+ from .publisher import AsyncPublisher
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ class AsyncConnection:
31
+ """
32
+ Asyncio-compatible facade around Connection.
33
+
34
+ This class manages the connection to RabbitMQ and provides factory methods for
35
+ creating publishers, consumers, and management interfaces. It supports both
36
+ single-node and multi-node configurations, as well as SSL/TLS connections.
37
+
38
+ Note:
39
+ The underlying Proton BlockingConnection is NOT thread-safe. A lock is used
40
+ to serialize all operations.
41
+
42
+ Attributes:
43
+ _connection (Connection): The underlying synchronous Connection
44
+ _connection_lock (asyncio.Lock): Lock for coordinating access to the shared connection
45
+ _async_publishers (list[AsyncPublisher]): List of active async publishers
46
+ _async_consumers (list[AsyncConsumer]): List of active async consumers
47
+ _async_managements (list[AsyncManagement]): List of active async management interfaces
48
+ _remove_callback (Optional[Callable[[AsyncConnection], None]]): Callback on close
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ uri: Optional[str] = None,
54
+ uris: Optional[list[str]] = None,
55
+ ssl_context: Union[
56
+ PosixSslConfigurationContext, WinSslConfigurationContext, None
57
+ ] = None,
58
+ oauth2_options: Optional[OAuth2Options] = None,
59
+ recovery_configuration: RecoveryConfiguration = RecoveryConfiguration(),
60
+ ):
61
+ """
62
+ Initialize AsyncConnection.
63
+
64
+ Args:
65
+ uri: Optional single-node connection URI
66
+ uris: Optional multi-node connection URIs
67
+ ssl_context: Optional SSL/TLS configuration
68
+ oauth2_options: Optional OAuth2 configuration
69
+ recovery_configuration: Configuration for automatic recovery
70
+
71
+ Raises:
72
+ ValidationCodeException: If recovery configuration is invalid
73
+ """
74
+ self._connection = Connection(
75
+ uri=uri,
76
+ uris=uris,
77
+ ssl_context=ssl_context,
78
+ oauth2_options=oauth2_options,
79
+ recovery_configuration=recovery_configuration,
80
+ )
81
+ self._connection_lock = asyncio.Lock()
82
+ self._async_publishers: list[AsyncPublisher] = []
83
+ self._async_consumers: list[AsyncConsumer] = []
84
+ self._async_managements: list[AsyncManagement] = []
85
+ self._remove_callback: Optional[Callable[[AsyncConnection], None]] = None
86
+
87
+ async def dial(self) -> None:
88
+ """
89
+ Establish a connection to the AMQP server.
90
+
91
+ Configures SSL if specified and establishes the connection using the
92
+ provided URI(s). Also initializes the management interface.
93
+ """
94
+ async with self._connection_lock:
95
+ await asyncio.to_thread(self._connection.dial)
96
+
97
+ def _set_remove_callback(
98
+ self, callback: Optional[Callable[[AsyncConnection], None]]
99
+ ) -> None:
100
+ """Set callback to be called when connection is closed."""
101
+ self._remove_callback = callback
102
+
103
+ def _remove_publisher(self, publisher: AsyncPublisher) -> None:
104
+ """Remove a publisher from the active list."""
105
+ if publisher in self._async_publishers:
106
+ self._async_publishers.remove(publisher)
107
+
108
+ def _remove_consumer(self, consumer: AsyncConsumer) -> None:
109
+ """Remove a consumer from the active list."""
110
+ if consumer in self._async_consumers:
111
+ self._async_consumers.remove(consumer)
112
+
113
+ def _remove_management(self, management: AsyncManagement) -> None:
114
+ """Remove a management interface from the active list."""
115
+ if management in self._async_managements:
116
+ self._async_managements.remove(management)
117
+
118
+ async def close(self) -> None:
119
+ """
120
+ Close the connection to the AMQP 1.0 server.
121
+
122
+ Closes the underlying connection and removes it from the connection list.
123
+ """
124
+ logger.debug("Closing async connection")
125
+ try:
126
+ for async_publisher in self._async_publishers[:]:
127
+ await async_publisher.close()
128
+ for async_consumer in self._async_consumers[:]:
129
+ await async_consumer.close()
130
+ for async_management in self._async_managements[:]:
131
+ await async_management.close()
132
+
133
+ async with self._connection_lock:
134
+ await asyncio.to_thread(self._connection.close)
135
+ except Exception as e:
136
+ logger.error(f"Error closing async connections: {e}")
137
+ raise e
138
+ finally:
139
+ if self._remove_callback is not None:
140
+ self._remove_callback(self)
141
+
142
+ def _set_connection_managements(self, management: Management) -> None:
143
+ if len(self._connection._managements) == 0:
144
+ self._connection._managements = [management]
145
+
146
+ async def management(
147
+ self,
148
+ ) -> AsyncManagement:
149
+ """
150
+ Get the management interface for this connection.
151
+
152
+ Returns:
153
+ AsyncManagement: The management interface for performing administrative tasks
154
+ """
155
+ if len(self._async_managements) > 0:
156
+ return self._async_managements[0]
157
+
158
+ async_management: Optional[AsyncManagement] = None
159
+ async with self._connection_lock:
160
+ if len(self._async_managements) == 0:
161
+ async_management = AsyncManagement(
162
+ self._connection._conn,
163
+ connection_lock=self._connection_lock,
164
+ )
165
+
166
+ self._set_connection_managements(
167
+ async_management._management
168
+ ) # TODO: check this
169
+ self._async_managements.append(async_management)
170
+
171
+ async_management._set_remove_callback(self._remove_management)
172
+
173
+ if async_management is not None:
174
+ await async_management.open()
175
+
176
+ return self._async_managements[0]
177
+
178
+ def _set_connection_publishers(self, publisher: Publisher) -> None:
179
+ """Set the list of publishers in the underlying connection."""
180
+ publisher._set_publishers_list(
181
+ [async_publisher._publisher for async_publisher in self._async_publishers]
182
+ )
183
+ self._connection._publishers.append(publisher)
184
+
185
+ async def publisher(self, destination: str = "") -> AsyncPublisher:
186
+ """
187
+ Create a new publisher instance.
188
+
189
+ Args:
190
+ destination: Optional default destination for published messages
191
+
192
+ Returns:
193
+ AsyncPublisher: A new publisher instance
194
+
195
+ Raises:
196
+ RuntimeError: If publisher creation fails
197
+ ArgumentOutOfRangeException: If destination address format is invalid
198
+ """
199
+ if destination != "":
200
+ if not validate_address(destination):
201
+ raise ArgumentOutOfRangeException(
202
+ "destination address must start with /queues or /exchanges"
203
+ )
204
+
205
+ async with self._connection_lock:
206
+ async_publisher = AsyncPublisher(
207
+ self._connection._conn,
208
+ destination,
209
+ connection_lock=self._connection_lock,
210
+ )
211
+ await async_publisher.open()
212
+ if async_publisher._publisher is None:
213
+ raise RuntimeError("Failed to create publisher")
214
+ self._set_connection_publishers(
215
+ async_publisher._publisher
216
+ ) # TODO: check this
217
+ self._async_publishers.append(async_publisher)
218
+
219
+ async_publisher._set_remove_callback(self._remove_publisher)
220
+
221
+ return async_publisher
222
+
223
+ def _set_connection_consumers(self, consumer: Consumer) -> None:
224
+ """Set the list of consumers in the underlying connection."""
225
+ self._connection._consumers.append(consumer)
226
+
227
+ async def consumer(
228
+ self,
229
+ destination: str,
230
+ message_handler: Optional[MessagingHandler] = None,
231
+ consumer_options: Optional[ConsumerOptions] = None,
232
+ credit: Optional[int] = None,
233
+ ) -> AsyncConsumer:
234
+ """
235
+ Create a new consumer instance.
236
+
237
+ Args:
238
+ destination: The address to consume from
239
+ message_handler: Optional handler for processing messages
240
+ consumer_options: Optional configuration for queue consumption. Each queue has its own consumer options.
241
+ credit: Optional credit value for flow control
242
+
243
+ Returns:
244
+ AsyncConsumer: A new consumer instance
245
+
246
+ Raises:
247
+ RuntimeError: If consumer creation fails
248
+ ArgumentOutOfRangeException: If destination address format is invalid
249
+ """
250
+ if not validate_address(destination):
251
+ raise ArgumentOutOfRangeException(
252
+ "destination address must start with /queues or /exchanges"
253
+ )
254
+ if consumer_options is not None:
255
+ consumer_options.validate(
256
+ {
257
+ "4.0.0": self._connection._is_server_version_gte("4.0.0"),
258
+ "4.1.0": self._connection._is_server_version_gte("4.1.0"),
259
+ "4.2.0": self._connection._is_server_version_gte("4.2.0"),
260
+ }
261
+ )
262
+
263
+ async with self._connection_lock:
264
+ async_consumer = AsyncConsumer(
265
+ self._connection._conn,
266
+ destination,
267
+ message_handler, # pyright: ignore[reportArgumentType]
268
+ consumer_options,
269
+ credit,
270
+ connection_lock=self._connection_lock,
271
+ )
272
+ await async_consumer.open()
273
+ if async_consumer._consumer is None:
274
+ raise RuntimeError("Failed to create consumer")
275
+ self._set_connection_consumers(async_consumer._consumer) # TODO: check this
276
+ self._async_consumers.append(async_consumer)
277
+
278
+ async_consumer._set_remove_callback(self._remove_consumer)
279
+
280
+ return async_consumer
281
+
282
+ async def refresh_token(self, token: str) -> None:
283
+ """
284
+ Refresh the oauth token
285
+
286
+ Args:
287
+ token: the oauth token to refresh
288
+
289
+ Raises:
290
+ ValidationCodeException: If oauth is not enabled
291
+ """
292
+ async with self._connection_lock:
293
+ await asyncio.to_thread(self._connection.refresh_token, token)
294
+
295
+ async def __aenter__(self) -> AsyncConnection:
296
+ """ "Async context manager entry."""
297
+ await self.dial()
298
+ return self
299
+
300
+ async def __aexit__(
301
+ self,
302
+ exc_type: Optional[type[BaseException]],
303
+ exc: Optional[BaseException],
304
+ tb: Optional[object],
305
+ ) -> None:
306
+ """Async context manager exit."""
307
+ await self.close()
308
+
309
+ @property
310
+ def active_producers(self) -> int:
311
+ """Get the number of active producers of the connection."""
312
+ return len(self._async_publishers)
313
+
314
+ @property
315
+ def active_consumers(self) -> int:
316
+ """Get the number of active consumers of the connection."""
317
+ return len(self._async_consumers)