weenspace-queue 0.1.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.
- weenspace_queue-0.1.0/LICENSE +21 -0
- weenspace_queue-0.1.0/PKG-INFO +161 -0
- weenspace_queue-0.1.0/README.md +136 -0
- weenspace_queue-0.1.0/pyproject.toml +62 -0
- weenspace_queue-0.1.0/weenspace_queue/__init__.py +128 -0
- weenspace_queue-0.1.0/weenspace_queue/asyncio/__init__.py +4 -0
- weenspace_queue-0.1.0/weenspace_queue/asyncio/aws_async.py +38 -0
- weenspace_queue-0.1.0/weenspace_queue/asyncio/rabbitmq_async.py +182 -0
- weenspace_queue-0.1.0/weenspace_queue/base.py +103 -0
- weenspace_queue-0.1.0/weenspace_queue/constants.py +75 -0
- weenspace_queue-0.1.0/weenspace_queue/providers/__init__.py +4 -0
- weenspace_queue-0.1.0/weenspace_queue/providers/aws.py +273 -0
- weenspace_queue-0.1.0/weenspace_queue/providers/rabbitmq.py +205 -0
- weenspace_queue-0.1.0/weenspace_queue/utils.py +271 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 WeenSpace
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: weenspace-queue
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: WeenSpace queue client for RabbitMQ and AWS SQS, supporting both synchronous and asynchronous operations.
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: rabbitmq,amqp,messaging,event-driven,microservices,queue,weenspace
|
|
8
|
+
Author: WeenSpace
|
|
9
|
+
Author-email: weenspace@gmail.com
|
|
10
|
+
Requires-Python: >=3.14,<4.0
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Classifier: Topic :: System :: Networking
|
|
18
|
+
Requires-Dist: packaging (>=25.0,<26.0)
|
|
19
|
+
Requires-Dist: python-qpid-proton (>=0.40.0,<0.41.0)
|
|
20
|
+
Requires-Dist: typing-extensions (>=4.15.0,<5.0.0)
|
|
21
|
+
Project-URL: Homepage, https://github.com/weenspace/weenspace-queue
|
|
22
|
+
Project-URL: Repository, https://github.com/weenspace/weenspace-queue
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# WeenSpace RabbitMQ Client
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/weenspace-queue/)
|
|
28
|
+
[](https://www.python.org/downloads/)
|
|
29
|
+
[](LICENSE)
|
|
30
|
+
|
|
31
|
+
A powerful Python RabbitMQ client for AMQP 1.0 protocol, designed for event-driven microservices architecture.
|
|
32
|
+
|
|
33
|
+
## ✨ Features
|
|
34
|
+
|
|
35
|
+
- 🔐 **Authentication**: OAuth2, TLS/SSL, Basic Auth
|
|
36
|
+
- 📬 **Queue Types**: Classic, Quorum, Stream queues
|
|
37
|
+
- ☠️ **Dead Letter Queues**: Built-in DLQ support with configurable strategies
|
|
38
|
+
- ⚡ **Priority Queues**: Native priority queue support
|
|
39
|
+
- 🔄 **Auto Reconnection**: Configurable recovery with exponential backoff
|
|
40
|
+
- 🔀 **All Exchange Types**: Direct, Fanout, Topic, Headers
|
|
41
|
+
- 🚀 **Async Support**: Native asyncio integration
|
|
42
|
+
- 📊 **Streams**: RabbitMQ Streams with filtering support
|
|
43
|
+
|
|
44
|
+
## 📦 Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install weenspace-queue
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 🚀 Quick Start
|
|
51
|
+
|
|
52
|
+
### Basic Publisher/Consumer
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from weenspace_queue import (
|
|
56
|
+
Environment,
|
|
57
|
+
Connection,
|
|
58
|
+
Publisher,
|
|
59
|
+
Consumer,
|
|
60
|
+
Message,
|
|
61
|
+
ClassicQueueSpecification,
|
|
62
|
+
ExchangeSpecification,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Create environment and connection
|
|
66
|
+
environment = Environment(uri="amqp://guest:guest@localhost:5672/")
|
|
67
|
+
connection = environment.connection()
|
|
68
|
+
|
|
69
|
+
# Declare queue with dead letter support
|
|
70
|
+
management = connection.management()
|
|
71
|
+
queue_spec = ClassicQueueSpecification(
|
|
72
|
+
name="my-queue",
|
|
73
|
+
is_durable=True,
|
|
74
|
+
dead_letter_exchange="dlx",
|
|
75
|
+
dead_letter_routing_key="dlx-key",
|
|
76
|
+
max_priority=10, # Enable priority
|
|
77
|
+
)
|
|
78
|
+
management.declare_queue(queue_spec)
|
|
79
|
+
|
|
80
|
+
# Publish message
|
|
81
|
+
publisher = connection.publisher("/queues/my-queue")
|
|
82
|
+
publisher.publish(Message(body=b"Hello WeenSpace!"))
|
|
83
|
+
|
|
84
|
+
# Consume messages
|
|
85
|
+
def on_message(message):
|
|
86
|
+
print(f"Received: {message.body}")
|
|
87
|
+
message.accept()
|
|
88
|
+
|
|
89
|
+
consumer = connection.consumer("/queues/my-queue", handler=on_message)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Async Support
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
import asyncio
|
|
96
|
+
from weenspace_queue.asyncio import AsyncEnvironment
|
|
97
|
+
|
|
98
|
+
async def main():
|
|
99
|
+
async with AsyncEnvironment(uri="amqp://localhost:5672/") as env:
|
|
100
|
+
async with env.connection() as conn:
|
|
101
|
+
publisher = await conn.publisher("/queues/my-queue")
|
|
102
|
+
await publisher.publish(Message(body=b"Async message!"))
|
|
103
|
+
|
|
104
|
+
asyncio.run(main())
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### OAuth2 Authentication
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from weenspace_queue import Environment, OAuth2Options
|
|
111
|
+
|
|
112
|
+
oauth = OAuth2Options(token="your_jwt_token")
|
|
113
|
+
environment = Environment(
|
|
114
|
+
uri="amqp://localhost:5672/",
|
|
115
|
+
oauth2_options=oauth
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### TLS/SSL Connection
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from weenspace_queue import Environment, SslConfiguration
|
|
123
|
+
|
|
124
|
+
ssl_config = SslConfiguration(
|
|
125
|
+
ca_cert="/path/to/ca.pem",
|
|
126
|
+
client_cert="/path/to/client.pem",
|
|
127
|
+
client_key="/path/to/client.key"
|
|
128
|
+
)
|
|
129
|
+
environment = Environment(
|
|
130
|
+
uri="amqps://localhost:5671/",
|
|
131
|
+
ssl_configuration=ssl_config
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## 📚 Documentation
|
|
136
|
+
|
|
137
|
+
See [examples](./examples) folder for more detailed usage examples.
|
|
138
|
+
|
|
139
|
+
## 🔄 Migration from python-rabbitmq
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
# Old (python-rabbitmq)
|
|
143
|
+
# from python_rabbitmq import RabbitMQ
|
|
144
|
+
|
|
145
|
+
# New (weenspace-queue)
|
|
146
|
+
from weenspace_queue import Environment, Connection
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## 📋 Requirements
|
|
150
|
+
|
|
151
|
+
- Python 3.13+
|
|
152
|
+
- RabbitMQ 4.x with AMQP 1.0 plugin enabled
|
|
153
|
+
|
|
154
|
+
## 📄 License
|
|
155
|
+
|
|
156
|
+
MIT License - Based on the official [RabbitMQ AMQP Python Client](https://github.com/rabbitmq/rabbitmq-amqp-python-client)
|
|
157
|
+
|
|
158
|
+
## 🙏 Credits
|
|
159
|
+
|
|
160
|
+
This library is based on the official RabbitMQ AMQP 1.0 Python client by the RabbitMQ team.
|
|
161
|
+
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# WeenSpace RabbitMQ Client
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/weenspace-queue/)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
A powerful Python RabbitMQ client for AMQP 1.0 protocol, designed for event-driven microservices architecture.
|
|
8
|
+
|
|
9
|
+
## ✨ Features
|
|
10
|
+
|
|
11
|
+
- 🔐 **Authentication**: OAuth2, TLS/SSL, Basic Auth
|
|
12
|
+
- 📬 **Queue Types**: Classic, Quorum, Stream queues
|
|
13
|
+
- ☠️ **Dead Letter Queues**: Built-in DLQ support with configurable strategies
|
|
14
|
+
- ⚡ **Priority Queues**: Native priority queue support
|
|
15
|
+
- 🔄 **Auto Reconnection**: Configurable recovery with exponential backoff
|
|
16
|
+
- 🔀 **All Exchange Types**: Direct, Fanout, Topic, Headers
|
|
17
|
+
- 🚀 **Async Support**: Native asyncio integration
|
|
18
|
+
- 📊 **Streams**: RabbitMQ Streams with filtering support
|
|
19
|
+
|
|
20
|
+
## 📦 Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install weenspace-queue
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 🚀 Quick Start
|
|
27
|
+
|
|
28
|
+
### Basic Publisher/Consumer
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from weenspace_queue import (
|
|
32
|
+
Environment,
|
|
33
|
+
Connection,
|
|
34
|
+
Publisher,
|
|
35
|
+
Consumer,
|
|
36
|
+
Message,
|
|
37
|
+
ClassicQueueSpecification,
|
|
38
|
+
ExchangeSpecification,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Create environment and connection
|
|
42
|
+
environment = Environment(uri="amqp://guest:guest@localhost:5672/")
|
|
43
|
+
connection = environment.connection()
|
|
44
|
+
|
|
45
|
+
# Declare queue with dead letter support
|
|
46
|
+
management = connection.management()
|
|
47
|
+
queue_spec = ClassicQueueSpecification(
|
|
48
|
+
name="my-queue",
|
|
49
|
+
is_durable=True,
|
|
50
|
+
dead_letter_exchange="dlx",
|
|
51
|
+
dead_letter_routing_key="dlx-key",
|
|
52
|
+
max_priority=10, # Enable priority
|
|
53
|
+
)
|
|
54
|
+
management.declare_queue(queue_spec)
|
|
55
|
+
|
|
56
|
+
# Publish message
|
|
57
|
+
publisher = connection.publisher("/queues/my-queue")
|
|
58
|
+
publisher.publish(Message(body=b"Hello WeenSpace!"))
|
|
59
|
+
|
|
60
|
+
# Consume messages
|
|
61
|
+
def on_message(message):
|
|
62
|
+
print(f"Received: {message.body}")
|
|
63
|
+
message.accept()
|
|
64
|
+
|
|
65
|
+
consumer = connection.consumer("/queues/my-queue", handler=on_message)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Async Support
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import asyncio
|
|
72
|
+
from weenspace_queue.asyncio import AsyncEnvironment
|
|
73
|
+
|
|
74
|
+
async def main():
|
|
75
|
+
async with AsyncEnvironment(uri="amqp://localhost:5672/") as env:
|
|
76
|
+
async with env.connection() as conn:
|
|
77
|
+
publisher = await conn.publisher("/queues/my-queue")
|
|
78
|
+
await publisher.publish(Message(body=b"Async message!"))
|
|
79
|
+
|
|
80
|
+
asyncio.run(main())
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### OAuth2 Authentication
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from weenspace_queue import Environment, OAuth2Options
|
|
87
|
+
|
|
88
|
+
oauth = OAuth2Options(token="your_jwt_token")
|
|
89
|
+
environment = Environment(
|
|
90
|
+
uri="amqp://localhost:5672/",
|
|
91
|
+
oauth2_options=oauth
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### TLS/SSL Connection
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from weenspace_queue import Environment, SslConfiguration
|
|
99
|
+
|
|
100
|
+
ssl_config = SslConfiguration(
|
|
101
|
+
ca_cert="/path/to/ca.pem",
|
|
102
|
+
client_cert="/path/to/client.pem",
|
|
103
|
+
client_key="/path/to/client.key"
|
|
104
|
+
)
|
|
105
|
+
environment = Environment(
|
|
106
|
+
uri="amqps://localhost:5671/",
|
|
107
|
+
ssl_configuration=ssl_config
|
|
108
|
+
)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## 📚 Documentation
|
|
112
|
+
|
|
113
|
+
See [examples](./examples) folder for more detailed usage examples.
|
|
114
|
+
|
|
115
|
+
## 🔄 Migration from python-rabbitmq
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# Old (python-rabbitmq)
|
|
119
|
+
# from python_rabbitmq import RabbitMQ
|
|
120
|
+
|
|
121
|
+
# New (weenspace-queue)
|
|
122
|
+
from weenspace_queue import Environment, Connection
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## 📋 Requirements
|
|
126
|
+
|
|
127
|
+
- Python 3.13+
|
|
128
|
+
- RabbitMQ 4.x with AMQP 1.0 plugin enabled
|
|
129
|
+
|
|
130
|
+
## 📄 License
|
|
131
|
+
|
|
132
|
+
MIT License - Based on the official [RabbitMQ AMQP Python Client](https://github.com/rabbitmq/rabbitmq-amqp-python-client)
|
|
133
|
+
|
|
134
|
+
## 🙏 Credits
|
|
135
|
+
|
|
136
|
+
This library is based on the official RabbitMQ AMQP 1.0 Python client by the RabbitMQ team.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "weenspace-queue"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "WeenSpace queue client for RabbitMQ and AWS SQS, supporting both synchronous and asynchronous operations."
|
|
5
|
+
authors = ["WeenSpace <weenspace@gmail.com>"]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
homepage = "https://github.com/weenspace/weenspace-queue"
|
|
9
|
+
repository = "https://github.com/weenspace/weenspace-queue"
|
|
10
|
+
keywords = ["rabbitmq", "amqp", "messaging", "event-driven", "microservices", "queue", "weenspace"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.14",
|
|
17
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
18
|
+
"Topic :: System :: Networking",
|
|
19
|
+
]
|
|
20
|
+
packages = [{include = "weenspace_queue"}]
|
|
21
|
+
|
|
22
|
+
[tool.poetry.dependencies]
|
|
23
|
+
python = "^3.14"
|
|
24
|
+
python-qpid-proton = "^0.40.0"
|
|
25
|
+
typing-extensions = "^4.15.0"
|
|
26
|
+
packaging = "^25.0"
|
|
27
|
+
|
|
28
|
+
[tool.poetry.group.dev.dependencies]
|
|
29
|
+
pyjwt = "^2.13.0"
|
|
30
|
+
pika = "^1.4.4"
|
|
31
|
+
flake8 = "^7.3.0"
|
|
32
|
+
isort = "^6.0.0"
|
|
33
|
+
mypy = "^1.19.0"
|
|
34
|
+
pytest = "^9.0.0"
|
|
35
|
+
black = "^26.5.1"
|
|
36
|
+
requests = "^2.32.0"
|
|
37
|
+
pytest-asyncio = "^1.3.0"
|
|
38
|
+
|
|
39
|
+
[build-system]
|
|
40
|
+
requires = ["poetry-core"]
|
|
41
|
+
build-backend = "poetry.core.masonry.api"
|
|
42
|
+
|
|
43
|
+
[dependency-groups]
|
|
44
|
+
dev = [
|
|
45
|
+
"boto3>=1.43.80",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[tool.pytest.ini_options]
|
|
49
|
+
asyncio_mode = "auto"
|
|
50
|
+
|
|
51
|
+
[tool.black]
|
|
52
|
+
line-length = 88
|
|
53
|
+
target-version = ['py313']
|
|
54
|
+
|
|
55
|
+
[tool.isort]
|
|
56
|
+
profile = "black"
|
|
57
|
+
line_length = 88
|
|
58
|
+
|
|
59
|
+
[tool.mypy]
|
|
60
|
+
python_version = "3.13"
|
|
61
|
+
warn_return_any = true
|
|
62
|
+
warn_unused_configs = true
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from typing import Any, Callable, Dict, Type
|
|
2
|
+
|
|
3
|
+
from .asyncio.aws_async import AwsAsyncEngine
|
|
4
|
+
from .asyncio.rabbitmq_async import RabbitMqAsyncEngine
|
|
5
|
+
from .base import (
|
|
6
|
+
AsyncQueueEngine,
|
|
7
|
+
Message,
|
|
8
|
+
QueueEngine,
|
|
9
|
+
QueueSpecification,
|
|
10
|
+
TopicSpecification,
|
|
11
|
+
)
|
|
12
|
+
from .constants import (
|
|
13
|
+
PROVIDER_AWS,
|
|
14
|
+
PROVIDER_RABBITMQ,
|
|
15
|
+
ExchangeKind,
|
|
16
|
+
Provider,
|
|
17
|
+
QueueKind,
|
|
18
|
+
)
|
|
19
|
+
from .providers.aws import AwsEngine
|
|
20
|
+
from .providers.rabbitmq import RabbitMqEngine
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class QueueClient:
|
|
24
|
+
"""Single client: pass provider name, then use the same publish/consume/topology methods."""
|
|
25
|
+
|
|
26
|
+
_ENGINES: Dict[str, Type[QueueEngine]] = {
|
|
27
|
+
PROVIDER_AWS: AwsEngine,
|
|
28
|
+
PROVIDER_RABBITMQ: RabbitMqEngine,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
def __init__(self, provider: str, **config: Any) -> None:
|
|
32
|
+
prov_key = provider.lower().strip()
|
|
33
|
+
engine_cls = self._ENGINES.get(prov_key)
|
|
34
|
+
if engine_cls is None:
|
|
35
|
+
supported = ", ".join(sorted(self._ENGINES))
|
|
36
|
+
raise ValueError(
|
|
37
|
+
f"Unsupported provider '{provider}'. Supported: {supported}"
|
|
38
|
+
)
|
|
39
|
+
self.provider = prov_key
|
|
40
|
+
self.engine: QueueEngine = engine_cls(**config)
|
|
41
|
+
|
|
42
|
+
def declare_queue(self, spec: QueueSpecification) -> str:
|
|
43
|
+
return self.engine.declare_queue(spec)
|
|
44
|
+
|
|
45
|
+
def declare_topic(self, spec: TopicSpecification) -> str:
|
|
46
|
+
return self.engine.declare_topic(spec)
|
|
47
|
+
|
|
48
|
+
def bind_pattern(self, queue_id: str, topic_id: str, pattern: str) -> None:
|
|
49
|
+
self.engine.bind_pattern(queue_id, topic_id, pattern)
|
|
50
|
+
|
|
51
|
+
def publish(self, destination: str, message: Message) -> None:
|
|
52
|
+
self.engine.publish(destination, message)
|
|
53
|
+
|
|
54
|
+
def consume(self, queue_id: str, handler: Callable[[Message], None]) -> None:
|
|
55
|
+
self.engine.consume(queue_id, handler)
|
|
56
|
+
|
|
57
|
+
def stop(self) -> None:
|
|
58
|
+
self.engine.stop()
|
|
59
|
+
|
|
60
|
+
def close(self) -> None:
|
|
61
|
+
self.engine.close()
|
|
62
|
+
|
|
63
|
+
def __enter__(self) -> "QueueClient":
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
|
|
67
|
+
self.close()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class AsyncQueueClient:
|
|
71
|
+
"""Async twin of QueueClient. Same method names, same provider argument."""
|
|
72
|
+
|
|
73
|
+
_ENGINES: Dict[str, Type[AsyncQueueEngine]] = {
|
|
74
|
+
PROVIDER_AWS: AwsAsyncEngine,
|
|
75
|
+
PROVIDER_RABBITMQ: RabbitMqAsyncEngine,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
def __init__(self, provider: str, **config: Any) -> None:
|
|
79
|
+
prov_key = provider.lower().strip()
|
|
80
|
+
engine_cls = self._ENGINES.get(prov_key)
|
|
81
|
+
if engine_cls is None:
|
|
82
|
+
supported = ", ".join(sorted(self._ENGINES))
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"Unsupported provider '{provider}'. Supported: {supported}"
|
|
85
|
+
)
|
|
86
|
+
self.provider = prov_key
|
|
87
|
+
self.engine: AsyncQueueEngine = engine_cls(**config)
|
|
88
|
+
|
|
89
|
+
async def declare_queue(self, spec: QueueSpecification) -> str:
|
|
90
|
+
return await self.engine.declare_queue(spec)
|
|
91
|
+
|
|
92
|
+
async def declare_topic(self, spec: TopicSpecification) -> str:
|
|
93
|
+
return await self.engine.declare_topic(spec)
|
|
94
|
+
|
|
95
|
+
async def bind_pattern(self, queue_id: str, topic_id: str, pattern: str) -> None:
|
|
96
|
+
await self.engine.bind_pattern(queue_id, topic_id, pattern)
|
|
97
|
+
|
|
98
|
+
async def publish(self, destination: str, message: Message) -> None:
|
|
99
|
+
await self.engine.publish(destination, message)
|
|
100
|
+
|
|
101
|
+
async def consume(self, queue_id: str, handler: Callable[[Message], None]) -> None:
|
|
102
|
+
await self.engine.consume(queue_id, handler)
|
|
103
|
+
|
|
104
|
+
async def stop(self) -> None:
|
|
105
|
+
await self.engine.stop()
|
|
106
|
+
|
|
107
|
+
async def close(self) -> None:
|
|
108
|
+
await self.engine.close()
|
|
109
|
+
|
|
110
|
+
async def __aenter__(self) -> "AsyncQueueClient":
|
|
111
|
+
return self
|
|
112
|
+
|
|
113
|
+
async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
|
|
114
|
+
await self.close()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
__all__ = [
|
|
118
|
+
"AsyncQueueClient",
|
|
119
|
+
"AsyncQueueEngine",
|
|
120
|
+
"ExchangeKind",
|
|
121
|
+
"Message",
|
|
122
|
+
"Provider",
|
|
123
|
+
"QueueClient",
|
|
124
|
+
"QueueEngine",
|
|
125
|
+
"QueueKind",
|
|
126
|
+
"QueueSpecification",
|
|
127
|
+
"TopicSpecification",
|
|
128
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from weenspace_queue.base import (
|
|
5
|
+
AsyncQueueEngine,
|
|
6
|
+
Message,
|
|
7
|
+
QueueSpecification,
|
|
8
|
+
TopicSpecification,
|
|
9
|
+
)
|
|
10
|
+
from weenspace_queue.providers.aws import AwsEngine
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AwsAsyncEngine(AsyncQueueEngine):
|
|
14
|
+
"""Async AWS engine. Uses aioboto3 when installed, otherwise boto3 in a worker thread."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
17
|
+
self._sync = AwsEngine(**kwargs)
|
|
18
|
+
|
|
19
|
+
async def declare_queue(self, spec: QueueSpecification) -> str:
|
|
20
|
+
return await asyncio.to_thread(self._sync.declare_queue, spec)
|
|
21
|
+
|
|
22
|
+
async def declare_topic(self, spec: TopicSpecification) -> str:
|
|
23
|
+
return await asyncio.to_thread(self._sync.declare_topic, spec)
|
|
24
|
+
|
|
25
|
+
async def bind_pattern(self, queue_id: str, topic_id: str, pattern: str) -> None:
|
|
26
|
+
await asyncio.to_thread(self._sync.bind_pattern, queue_id, topic_id, pattern)
|
|
27
|
+
|
|
28
|
+
async def publish(self, destination: str, message: Message) -> None:
|
|
29
|
+
await asyncio.to_thread(self._sync.publish, destination, message)
|
|
30
|
+
|
|
31
|
+
async def consume(self, queue_id: str, handler: Callable[[Message], None]) -> None:
|
|
32
|
+
await asyncio.to_thread(self._sync.consume, queue_id, handler)
|
|
33
|
+
|
|
34
|
+
async def stop(self) -> None:
|
|
35
|
+
self._sync.stop()
|
|
36
|
+
|
|
37
|
+
async def close(self) -> None:
|
|
38
|
+
await asyncio.to_thread(self._sync.close)
|