DefStream-RabbitMQ 0.1.1__py3-none-any.whl
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.
- DefStream/Broker/Implements/RabbitMQ/Configuration.py +48 -0
- DefStream/Broker/Implements/RabbitMQ/Connection.py +449 -0
- DefStream/Broker/Implements/RabbitMQ/Consumer.py +244 -0
- DefStream/Broker/Implements/RabbitMQ/Event.py +106 -0
- DefStream/Broker/Implements/RabbitMQ/Exchange.py +222 -0
- DefStream/Broker/Implements/RabbitMQ/Policies.py +9 -0
- DefStream/Broker/Implements/RabbitMQ/Queue.py +248 -0
- DefStream/Broker/Implements/RabbitMQ/Serializer.py +61 -0
- DefStream/Broker/Implements/RabbitMQ/Stream.py +119 -0
- DefStream/Broker/Implements/RabbitMQ/Subscription.py +47 -0
- DefStream/Broker/Implements/RabbitMQ/__init__.py +47 -0
- defstream_rabbitmq-0.1.1.dist-info/METADATA +143 -0
- defstream_rabbitmq-0.1.1.dist-info/RECORD +16 -0
- defstream_rabbitmq-0.1.1.dist-info/WHEEL +5 -0
- defstream_rabbitmq-0.1.1.dist-info/licenses/LICENSE +674 -0
- defstream_rabbitmq-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from ...Event import (
|
|
4
|
+
Event as BaseEvent,
|
|
5
|
+
EventHandler as BaseEventHandler,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from abc import abstractmethod
|
|
9
|
+
|
|
10
|
+
__all__ = (
|
|
11
|
+
'Event',
|
|
12
|
+
'EventHandler',
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Event(BaseEvent):
|
|
17
|
+
"""Event of Event Broker for RabbitMQ"""
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
channel,
|
|
21
|
+
queue,
|
|
22
|
+
consumer,
|
|
23
|
+
transaction,
|
|
24
|
+
properties,
|
|
25
|
+
body,
|
|
26
|
+
):
|
|
27
|
+
self.channel = channel
|
|
28
|
+
self.queue = queue
|
|
29
|
+
self.consumer = consumer
|
|
30
|
+
self.transaction = transaction
|
|
31
|
+
self.properties = properties
|
|
32
|
+
self.props = properties.headers
|
|
33
|
+
self.payload = body
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
def __repr__(self):
|
|
37
|
+
return 'Event(id={}, body={}, type={}, headers={})'.format(
|
|
38
|
+
self.properties.message_id,
|
|
39
|
+
self.payload,
|
|
40
|
+
self.properties.type,
|
|
41
|
+
self.props,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def ack(self):
|
|
45
|
+
self.channel.basic_ack(self.transaction)
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
def nack(self):
|
|
49
|
+
self.channel.basic_nack(
|
|
50
|
+
self.transaction,
|
|
51
|
+
requeue=True,
|
|
52
|
+
)
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
def reject(self):
|
|
56
|
+
self.channel.basic_reject(
|
|
57
|
+
self.transaction,
|
|
58
|
+
requeue=False,
|
|
59
|
+
)
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
def headers(self):
|
|
63
|
+
return self.props
|
|
64
|
+
|
|
65
|
+
def header(
|
|
66
|
+
self,
|
|
67
|
+
key,
|
|
68
|
+
):
|
|
69
|
+
if key not in self.props:
|
|
70
|
+
return None
|
|
71
|
+
return self.props[key]
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def src(self):
|
|
75
|
+
return self.queue
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def id(self):
|
|
79
|
+
return self.properties.message_id
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def type(self):
|
|
83
|
+
return self.properties.type
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def body(self):
|
|
87
|
+
return self.payload
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def priority(self):
|
|
91
|
+
if not hasattr(
|
|
92
|
+
self.properties,
|
|
93
|
+
'priority',
|
|
94
|
+
):
|
|
95
|
+
return None
|
|
96
|
+
return self.properties.priority
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class EventHandler(BaseEventHandler):
|
|
100
|
+
"""Event Handler Interface of Event Broker for RabbitMQ"""
|
|
101
|
+
@abstractmethod
|
|
102
|
+
def __call__(
|
|
103
|
+
self,
|
|
104
|
+
event: Event,
|
|
105
|
+
):
|
|
106
|
+
raise NotImplementedError('{} must be implemented __call__'.format(self.__class__.__name__))
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from ... import (
|
|
4
|
+
Sendable,
|
|
5
|
+
Callable,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from .Serializer import (
|
|
9
|
+
Encoder,
|
|
10
|
+
Decoder,
|
|
11
|
+
)
|
|
12
|
+
from ...Errors import RemoteError
|
|
13
|
+
from copy import deepcopy
|
|
14
|
+
from math import (
|
|
15
|
+
ceil,
|
|
16
|
+
isfinite,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from pika import (
|
|
20
|
+
BlockingConnection,
|
|
21
|
+
BasicProperties,
|
|
22
|
+
ConnectionParameters,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from time import (
|
|
26
|
+
time,
|
|
27
|
+
monotonic,
|
|
28
|
+
)
|
|
29
|
+
from enum import Enum
|
|
30
|
+
from uuid import uuid4
|
|
31
|
+
|
|
32
|
+
from typing import (
|
|
33
|
+
Any,
|
|
34
|
+
Dict,
|
|
35
|
+
Optional,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
__all__ = (
|
|
39
|
+
'Exchange',
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Exchange(
|
|
44
|
+
Sendable,
|
|
45
|
+
Callable,
|
|
46
|
+
):
|
|
47
|
+
"""Exchange of Event Broker for RabbitMQ"""
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
connection: BlockingConnection,
|
|
51
|
+
encode: Encoder,
|
|
52
|
+
name: str = None,
|
|
53
|
+
decode: Optional[Decoder] = None,
|
|
54
|
+
rpc_parameters: Optional[ConnectionParameters] = None,
|
|
55
|
+
):
|
|
56
|
+
self.connection = connection
|
|
57
|
+
self.encode = encode
|
|
58
|
+
self.channel = self.connection.channel()
|
|
59
|
+
self.channel.auto_decode = False
|
|
60
|
+
self.exchange = name
|
|
61
|
+
self.decode = decode
|
|
62
|
+
self.rpc_parameters = rpc_parameters
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
def __del__(self):
|
|
66
|
+
if self.channel and self.channel.is_open:
|
|
67
|
+
self.channel.close()
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
def __str__(self): return self.exchange
|
|
71
|
+
|
|
72
|
+
def send(
|
|
73
|
+
self,
|
|
74
|
+
body,
|
|
75
|
+
event: str = None,
|
|
76
|
+
headers: Dict = {},
|
|
77
|
+
priority: int = None,
|
|
78
|
+
expiration: int = None,
|
|
79
|
+
timestamp: int = None,
|
|
80
|
+
persistent: bool = True,
|
|
81
|
+
id: str = None,
|
|
82
|
+
):
|
|
83
|
+
if not timestamp: timestamp = int(time())
|
|
84
|
+
if not id: id = uuid4().hex
|
|
85
|
+
properties = BasicProperties(
|
|
86
|
+
type=event if event else '',
|
|
87
|
+
headers=headers,
|
|
88
|
+
content_type=self.encode.format,
|
|
89
|
+
content_encoding=self.encode.charset,
|
|
90
|
+
priority=priority,
|
|
91
|
+
timestamp=timestamp,
|
|
92
|
+
expiration=expiration,
|
|
93
|
+
message_id=id,
|
|
94
|
+
delivery_mode=2 if persistent else 1,
|
|
95
|
+
)
|
|
96
|
+
self.channel.basic_publish(
|
|
97
|
+
exchange=self.exchange,
|
|
98
|
+
routing_key=event if event else '',
|
|
99
|
+
properties=properties,
|
|
100
|
+
body=self.encode(body),
|
|
101
|
+
)
|
|
102
|
+
return id
|
|
103
|
+
|
|
104
|
+
def call(
|
|
105
|
+
self,
|
|
106
|
+
body: Any,
|
|
107
|
+
timeout: float = 5,
|
|
108
|
+
*,
|
|
109
|
+
event: Optional[str] = None,
|
|
110
|
+
) -> Any:
|
|
111
|
+
if self.rpc_parameters is None or self.decode is None:
|
|
112
|
+
raise RuntimeError('RPC requires decoder and connection parameters; obtain the exchange through Connection.exchange')
|
|
113
|
+
if isinstance(
|
|
114
|
+
timeout,
|
|
115
|
+
bool,
|
|
116
|
+
) or not isinstance(
|
|
117
|
+
timeout,
|
|
118
|
+
(
|
|
119
|
+
int,
|
|
120
|
+
float,
|
|
121
|
+
),
|
|
122
|
+
) or not isfinite(timeout) or timeout <= 0:
|
|
123
|
+
raise ValueError('RPC timeout must be a finite positive number of seconds')
|
|
124
|
+
deadline = monotonic() + timeout
|
|
125
|
+
settings = deepcopy(self.rpc_parameters)
|
|
126
|
+
settings.connection_attempts = 1
|
|
127
|
+
settings.socket_timeout = min(
|
|
128
|
+
settings.socket_timeout,
|
|
129
|
+
timeout,
|
|
130
|
+
)
|
|
131
|
+
settings.stack_timeout = timeout
|
|
132
|
+
settings.blocked_connection_timeout = timeout
|
|
133
|
+
connection = BlockingConnection(settings)
|
|
134
|
+
try:
|
|
135
|
+
channel = connection.channel()
|
|
136
|
+
reply_queue = channel.queue_declare(
|
|
137
|
+
queue='',
|
|
138
|
+
exclusive=True,
|
|
139
|
+
auto_delete=True,
|
|
140
|
+
).method.queue
|
|
141
|
+
channel.confirm_delivery()
|
|
142
|
+
correlation_id = uuid4().hex
|
|
143
|
+
responses = []
|
|
144
|
+
|
|
145
|
+
def receive(
|
|
146
|
+
channel,
|
|
147
|
+
method,
|
|
148
|
+
properties,
|
|
149
|
+
payload,
|
|
150
|
+
):
|
|
151
|
+
if properties.correlation_id == correlation_id:
|
|
152
|
+
responses.append((
|
|
153
|
+
properties,
|
|
154
|
+
payload,
|
|
155
|
+
))
|
|
156
|
+
|
|
157
|
+
channel.basic_consume(
|
|
158
|
+
reply_queue,
|
|
159
|
+
on_message_callback=receive,
|
|
160
|
+
auto_ack=True,
|
|
161
|
+
)
|
|
162
|
+
remaining = deadline - monotonic()
|
|
163
|
+
if remaining <= 0:
|
|
164
|
+
raise TimeoutError('RPC timed out before request publication')
|
|
165
|
+
channel.basic_publish(
|
|
166
|
+
exchange=self.exchange,
|
|
167
|
+
routing_key=event if event is not None else '',
|
|
168
|
+
body=self.encode(body),
|
|
169
|
+
properties=BasicProperties(
|
|
170
|
+
reply_to=reply_queue,
|
|
171
|
+
correlation_id=correlation_id,
|
|
172
|
+
content_type=self.encode.format,
|
|
173
|
+
content_encoding=self.encode.charset,
|
|
174
|
+
expiration=str(max(
|
|
175
|
+
1,
|
|
176
|
+
ceil(remaining * 1000),
|
|
177
|
+
)),
|
|
178
|
+
),
|
|
179
|
+
mandatory=True,
|
|
180
|
+
)
|
|
181
|
+
while not responses:
|
|
182
|
+
remaining = deadline - monotonic()
|
|
183
|
+
if remaining <= 0:
|
|
184
|
+
raise TimeoutError(f'RPC response timed out for exchange {self.exchange!r} routing_key={event!r}')
|
|
185
|
+
connection.process_data_events(time_limit=min(
|
|
186
|
+
remaining,
|
|
187
|
+
0.1,
|
|
188
|
+
))
|
|
189
|
+
properties, payload = responses[0]
|
|
190
|
+
response = self.decode(
|
|
191
|
+
payload,
|
|
192
|
+
properties.content_type,
|
|
193
|
+
properties.content_encoding,
|
|
194
|
+
)
|
|
195
|
+
if not isinstance(
|
|
196
|
+
response,
|
|
197
|
+
dict,
|
|
198
|
+
) or type(response.get('ok')) is not bool:
|
|
199
|
+
raise ValueError('Invalid RPC response envelope')
|
|
200
|
+
if response['ok']:
|
|
201
|
+
if 'result' not in response:
|
|
202
|
+
raise ValueError('RPC response has no result')
|
|
203
|
+
return response['result']
|
|
204
|
+
error = response.get('error')
|
|
205
|
+
if not isinstance(
|
|
206
|
+
error,
|
|
207
|
+
dict,
|
|
208
|
+
) or not isinstance(
|
|
209
|
+
error.get('code'),
|
|
210
|
+
str,
|
|
211
|
+
) or not isinstance(
|
|
212
|
+
error.get('message'),
|
|
213
|
+
str,
|
|
214
|
+
):
|
|
215
|
+
raise ValueError('Invalid RPC error response')
|
|
216
|
+
raise RemoteError(
|
|
217
|
+
error['code'],
|
|
218
|
+
error['message'],
|
|
219
|
+
)
|
|
220
|
+
finally:
|
|
221
|
+
if connection.is_open:
|
|
222
|
+
connection.close()
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from ... import (
|
|
4
|
+
Sendable,
|
|
5
|
+
Callable,
|
|
6
|
+
Poppable,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from .Serializer import (
|
|
10
|
+
Encoder,
|
|
11
|
+
Decoder,
|
|
12
|
+
)
|
|
13
|
+
from .Event import Event
|
|
14
|
+
from ...Errors import RemoteError
|
|
15
|
+
from copy import deepcopy
|
|
16
|
+
from math import (
|
|
17
|
+
ceil,
|
|
18
|
+
isfinite,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from pika import (
|
|
22
|
+
BlockingConnection,
|
|
23
|
+
BasicProperties,
|
|
24
|
+
ConnectionParameters,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
from time import (
|
|
28
|
+
time,
|
|
29
|
+
sleep,
|
|
30
|
+
monotonic,
|
|
31
|
+
)
|
|
32
|
+
from uuid import uuid4
|
|
33
|
+
|
|
34
|
+
from typing import (
|
|
35
|
+
Any,
|
|
36
|
+
Optional,
|
|
37
|
+
Dict,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
__all__ = (
|
|
41
|
+
'Queue'
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Queue(
|
|
46
|
+
Sendable,
|
|
47
|
+
Callable,
|
|
48
|
+
Poppable,
|
|
49
|
+
):
|
|
50
|
+
"""Queue of Event Broker for RabbitMQ"""
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
connection: BlockingConnection,
|
|
54
|
+
encode: Encoder,
|
|
55
|
+
decode: Decoder,
|
|
56
|
+
name: str = None,
|
|
57
|
+
rpc_parameters: Optional[ConnectionParameters] = None,
|
|
58
|
+
):
|
|
59
|
+
self.connection = connection
|
|
60
|
+
self.encode = encode
|
|
61
|
+
self.decode = decode
|
|
62
|
+
self.channel = self.connection.channel()
|
|
63
|
+
self.channel.auto_decode = False
|
|
64
|
+
self.queue = name
|
|
65
|
+
self.event = None
|
|
66
|
+
self.rpc_parameters = rpc_parameters
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
def __del__(self):
|
|
70
|
+
if self.channel and self.channel.is_open:
|
|
71
|
+
self.channel.close()
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
def __str__(self): return self.queue
|
|
75
|
+
|
|
76
|
+
def send(
|
|
77
|
+
self,
|
|
78
|
+
body,
|
|
79
|
+
event: str = None,
|
|
80
|
+
headers: Dict = {},
|
|
81
|
+
priority: int = None,
|
|
82
|
+
expiration: int = None,
|
|
83
|
+
timestamp: int = None,
|
|
84
|
+
persistent: bool = True,
|
|
85
|
+
id: str = None,
|
|
86
|
+
reply_to: str = None,
|
|
87
|
+
correlation_id: str = None,
|
|
88
|
+
):
|
|
89
|
+
if not timestamp: timestamp = int(time())
|
|
90
|
+
if not id: id = uuid4().hex
|
|
91
|
+
properties = BasicProperties(
|
|
92
|
+
type=event if event else '',
|
|
93
|
+
headers=headers,
|
|
94
|
+
content_type=self.encode.format,
|
|
95
|
+
content_encoding=self.encode.charset,
|
|
96
|
+
priority=priority,
|
|
97
|
+
timestamp=timestamp,
|
|
98
|
+
expiration=expiration,
|
|
99
|
+
message_id=id,
|
|
100
|
+
delivery_mode=2 if persistent else 1,
|
|
101
|
+
reply_to=reply_to,
|
|
102
|
+
correlation_id=correlation_id,
|
|
103
|
+
)
|
|
104
|
+
self.channel.basic_publish(
|
|
105
|
+
exchange='',
|
|
106
|
+
routing_key=self.queue,
|
|
107
|
+
properties=properties,
|
|
108
|
+
body=self.encode(body)
|
|
109
|
+
)
|
|
110
|
+
return id
|
|
111
|
+
|
|
112
|
+
def call(
|
|
113
|
+
self,
|
|
114
|
+
body: Any,
|
|
115
|
+
timeout: float = 5,
|
|
116
|
+
) -> Any:
|
|
117
|
+
if self.rpc_parameters is None:
|
|
118
|
+
raise RuntimeError('RPC requires connection parameters; obtain the queue through Connection.queue')
|
|
119
|
+
if isinstance(
|
|
120
|
+
timeout,
|
|
121
|
+
bool,
|
|
122
|
+
) or not isinstance(
|
|
123
|
+
timeout,
|
|
124
|
+
(
|
|
125
|
+
int,
|
|
126
|
+
float,
|
|
127
|
+
),
|
|
128
|
+
) or not isfinite(timeout) or timeout <= 0:
|
|
129
|
+
raise ValueError('RPC timeout must be a finite positive number of seconds')
|
|
130
|
+
deadline = monotonic() + timeout
|
|
131
|
+
settings = deepcopy(self.rpc_parameters)
|
|
132
|
+
settings.connection_attempts = 1
|
|
133
|
+
settings.socket_timeout = min(
|
|
134
|
+
settings.socket_timeout,
|
|
135
|
+
timeout,
|
|
136
|
+
)
|
|
137
|
+
settings.stack_timeout = timeout
|
|
138
|
+
settings.blocked_connection_timeout = timeout
|
|
139
|
+
connection = BlockingConnection(settings)
|
|
140
|
+
try:
|
|
141
|
+
channel = connection.channel()
|
|
142
|
+
reply_queue = channel.queue_declare(
|
|
143
|
+
queue='',
|
|
144
|
+
exclusive=True,
|
|
145
|
+
auto_delete=True,
|
|
146
|
+
).method.queue
|
|
147
|
+
channel.confirm_delivery()
|
|
148
|
+
correlation_id = uuid4().hex
|
|
149
|
+
responses = []
|
|
150
|
+
|
|
151
|
+
def receive(
|
|
152
|
+
channel,
|
|
153
|
+
method,
|
|
154
|
+
properties,
|
|
155
|
+
payload,
|
|
156
|
+
):
|
|
157
|
+
if properties.correlation_id == correlation_id:
|
|
158
|
+
responses.append((
|
|
159
|
+
properties,
|
|
160
|
+
payload,
|
|
161
|
+
))
|
|
162
|
+
|
|
163
|
+
channel.basic_consume(
|
|
164
|
+
reply_queue,
|
|
165
|
+
on_message_callback=receive,
|
|
166
|
+
auto_ack=True,
|
|
167
|
+
)
|
|
168
|
+
remaining = deadline - monotonic()
|
|
169
|
+
if remaining <= 0:
|
|
170
|
+
raise TimeoutError('RPC timed out before request publication')
|
|
171
|
+
channel.basic_publish(
|
|
172
|
+
exchange='',
|
|
173
|
+
routing_key=self.queue,
|
|
174
|
+
body=self.encode(body),
|
|
175
|
+
properties=BasicProperties(
|
|
176
|
+
reply_to=reply_queue,
|
|
177
|
+
correlation_id=correlation_id,
|
|
178
|
+
content_type=self.encode.format,
|
|
179
|
+
content_encoding=self.encode.charset,
|
|
180
|
+
expiration=str(max(
|
|
181
|
+
1,
|
|
182
|
+
ceil(remaining * 1000),
|
|
183
|
+
)),
|
|
184
|
+
),
|
|
185
|
+
mandatory=True,
|
|
186
|
+
)
|
|
187
|
+
while not responses:
|
|
188
|
+
remaining = deadline - monotonic()
|
|
189
|
+
if remaining <= 0:
|
|
190
|
+
raise TimeoutError(f'RPC response timed out for queue {self.queue!r}')
|
|
191
|
+
connection.process_data_events(time_limit=min(
|
|
192
|
+
remaining,
|
|
193
|
+
0.1,
|
|
194
|
+
))
|
|
195
|
+
properties, payload = responses[0]
|
|
196
|
+
response = self.decode(
|
|
197
|
+
payload,
|
|
198
|
+
properties.content_type,
|
|
199
|
+
properties.content_encoding,
|
|
200
|
+
)
|
|
201
|
+
if not isinstance(
|
|
202
|
+
response,
|
|
203
|
+
dict,
|
|
204
|
+
) or type(response.get('ok')) is not bool:
|
|
205
|
+
raise ValueError('Invalid RPC response envelope')
|
|
206
|
+
if response['ok']:
|
|
207
|
+
if 'result' not in response:
|
|
208
|
+
raise ValueError('RPC response has no result')
|
|
209
|
+
return response['result']
|
|
210
|
+
error = response.get('error')
|
|
211
|
+
if not isinstance(
|
|
212
|
+
error,
|
|
213
|
+
dict,
|
|
214
|
+
) or not isinstance(
|
|
215
|
+
error.get('code'),
|
|
216
|
+
str,
|
|
217
|
+
) or not isinstance(
|
|
218
|
+
error.get('message'),
|
|
219
|
+
str,
|
|
220
|
+
):
|
|
221
|
+
raise ValueError('Invalid RPC error response')
|
|
222
|
+
raise RemoteError(
|
|
223
|
+
error['code'],
|
|
224
|
+
error['message'],
|
|
225
|
+
)
|
|
226
|
+
finally:
|
|
227
|
+
if connection.is_open:
|
|
228
|
+
connection.close()
|
|
229
|
+
|
|
230
|
+
def pop(self, timeout: int = None) -> Optional[Event]:
|
|
231
|
+
channel = self.channel
|
|
232
|
+
channel.basic_qos(0, 1, False)
|
|
233
|
+
deadline = time() + (timeout / 1000) if timeout else None
|
|
234
|
+
while True:
|
|
235
|
+
method, properties, body = channel.basic_get(queue=self.queue, auto_ack=False)
|
|
236
|
+
if method:
|
|
237
|
+
return Event(
|
|
238
|
+
channel,
|
|
239
|
+
self.queue,
|
|
240
|
+
None,
|
|
241
|
+
method.delivery_tag,
|
|
242
|
+
properties,
|
|
243
|
+
self.decode(body, properties.content_type, properties.content_encoding),
|
|
244
|
+
)
|
|
245
|
+
if deadline and time() >= deadline:
|
|
246
|
+
break
|
|
247
|
+
sleep(0.1)
|
|
248
|
+
return
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from abc import (
|
|
4
|
+
ABCMeta,
|
|
5
|
+
abstractmethod,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from json import (
|
|
9
|
+
dumps,
|
|
10
|
+
loads,
|
|
11
|
+
)
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
__all__ = (
|
|
15
|
+
'Encoder',
|
|
16
|
+
'Decoder',
|
|
17
|
+
'JavaScriptObjectNotationEncoder',
|
|
18
|
+
'JavaScriptObjectNotationDecoder',
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
class Encoder(metaclass=ABCMeta):
|
|
22
|
+
"""Encoder Interface"""
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def __call__(self, obj: Any) -> bytes:
|
|
25
|
+
raise NotImplementedError('{} must be implemented __call__'.format(self.__class__.__name__))
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def format(self) -> str:
|
|
30
|
+
raise NotImplementedError('{} must be implemented format'.format(self.__class__.__name__))
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def charset(self) -> str:
|
|
35
|
+
raise NotImplementedError('{} must be implemented charset'.format(self.__class__.__name__))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Decoder(metaclass=ABCMeta):
|
|
39
|
+
"""Decoder Interface"""
|
|
40
|
+
@abstractmethod
|
|
41
|
+
def __call__(self, obj: bytes, format: str, charset: str) -> Any:
|
|
42
|
+
raise NotImplementedError('{} must be implemented __call__'.format(self.__class__.__name__))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class JavaScriptObjectNotationEncoder(Encoder):
|
|
46
|
+
"""JavaScriptObjectNotation Encoder"""
|
|
47
|
+
def __call__(self, obj: Any) -> bytes:
|
|
48
|
+
return dumps(obj, ensure_ascii=False).encode(self.charset)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def format(self) -> str: return 'application/json'
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def charset(self) -> str: return 'utf-8'
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class JavaScriptObjectNotationDecoder(Decoder):
|
|
58
|
+
"""JavaScriptObjectNotation Decoder"""
|
|
59
|
+
def __call__(self, obj: bytes, format: str, charset: str) -> Any:
|
|
60
|
+
if format != 'application/json': raise ValueError('format is not support')
|
|
61
|
+
return loads(obj.decode(charset))
|