1
1
import enum
2
- import warnings
2
+ from abc import ABC , abstractmethod
3
3
from struct import pack , unpack
4
4
from typing import (
5
5
TYPE_CHECKING ,
@@ -92,18 +92,14 @@ def read_bytes(self) -> bytes:
92
92
return self .data [self .offset :]
93
93
94
94
95
- class RawMessage :
95
+ class MessageFormat ( ABC ) :
96
96
"""A class to represent a raw Redis message."""
97
97
98
98
__slots__ = (
99
99
"data" ,
100
100
"headers" ,
101
101
)
102
102
103
- IDENTITY_HEADER = (
104
- b"\x89 BIN\x0d \x0a \x1a \x0a " # to avoid confusion with other formats
105
- )
106
-
107
103
def __init__ (
108
104
self ,
109
105
data : bytes ,
@@ -120,7 +116,7 @@ def build(
120
116
reply_to : Optional [str ],
121
117
headers : Optional ["AnyDict" ],
122
118
correlation_id : str ,
123
- ) -> "RawMessage " :
119
+ ) -> "MessageFormat " :
124
120
payload , content_type = encode_message (message )
125
121
126
122
headers_to_send = {
@@ -141,6 +137,25 @@ def build(
141
137
headers = headers_to_send ,
142
138
)
143
139
140
+ @classmethod
141
+ @abstractmethod
142
+ def encode (
143
+ cls ,
144
+ * ,
145
+ message : Union [Sequence ["SendableMessage" ], "SendableMessage" ],
146
+ reply_to : Optional [str ],
147
+ headers : Optional ["AnyDict" ],
148
+ correlation_id : str ,
149
+ ) -> bytes :
150
+ raise NotImplementedError ()
151
+
152
+ @classmethod
153
+ @abstractmethod
154
+ def parse (cls , data : bytes ) -> Tuple [bytes , "AnyDict" ]:
155
+ raise NotImplementedError ()
156
+
157
+
158
+ class JSONMessageFormat (MessageFormat ):
144
159
@classmethod
145
160
def encode (
146
161
cls ,
@@ -149,29 +164,54 @@ def encode(
149
164
reply_to : Optional [str ],
150
165
headers : Optional ["AnyDict" ],
151
166
correlation_id : str ,
152
- to_json : bool = False ,
153
167
) -> bytes :
154
168
msg = cls .build (
155
169
message = message ,
156
170
reply_to = reply_to ,
157
171
headers = headers ,
158
172
correlation_id = correlation_id ,
159
173
)
160
- if to_json :
161
- warnings .warn (
162
- "JSON encoding deprecated in **FastStream 0.6.0**. "
163
- "Please don't use it unless it's necessary"
164
- "Format will be removed in **FastStream 0.7.0**." ,
165
- DeprecationWarning ,
166
- stacklevel = 2 ,
167
- )
168
- return dump_json (
169
- {
170
- "data" : msg .data ,
171
- "headers" : msg .headers ,
172
- }
173
- )
174
+ return dump_json (
175
+ {
176
+ "data" : msg .data ,
177
+ "headers" : msg .headers ,
178
+ }
179
+ )
180
+
181
+ @classmethod
182
+ def parse (cls , data : bytes ) -> Tuple [bytes , "AnyDict" ]:
183
+ headers : AnyDict
184
+ try :
185
+ parsed_data = json_loads (data )
186
+ data = parsed_data ["data" ].encode ()
187
+ headers = parsed_data ["headers" ]
188
+ except Exception :
189
+ # Raw Redis message format
190
+ data = data
191
+ headers = {}
192
+ return data , headers
193
+
194
+
195
+ class BinaryMessageFormatV1 (MessageFormat ):
196
+ IDENTITY_HEADER = (
197
+ b"\x89 BIN\x0d \x0a \x1a \x0a " # to avoid confusion with other formats
198
+ )
174
199
200
+ @classmethod
201
+ def encode (
202
+ cls ,
203
+ * ,
204
+ message : Union [Sequence ["SendableMessage" ], "SendableMessage" ],
205
+ reply_to : Optional [str ],
206
+ headers : Optional ["AnyDict" ],
207
+ correlation_id : str ,
208
+ ) -> bytes :
209
+ msg = cls .build (
210
+ message = message ,
211
+ reply_to = reply_to ,
212
+ headers = headers ,
213
+ correlation_id = correlation_id ,
214
+ )
175
215
writer = BinaryWriter ()
176
216
writer .write (cls .IDENTITY_HEADER )
177
217
writer .write_int (FastStreamMessageVersion .v1 .value )
@@ -185,41 +225,26 @@ def encode(
185
225
@classmethod
186
226
def parse (cls , data : bytes ) -> Tuple [bytes , "AnyDict" ]:
187
227
headers : AnyDict
188
-
189
- # FastStream message format
190
228
try :
191
229
reader = BinaryReader (data )
230
+ headers = {}
192
231
magic_header = reader .read_until (len (cls .IDENTITY_HEADER ))
193
232
message_version = reader .read_int ()
194
233
if (
195
234
magic_header == cls .IDENTITY_HEADER
196
235
and message_version == FastStreamMessageVersion .v1 .value
197
236
):
198
237
header_count = reader .read_int ()
199
- headers = {}
200
238
for _ in range (header_count ):
201
239
key = reader .read_string ()
202
240
value = reader .read_string ()
203
241
headers [key ] = value
204
242
205
243
data = reader .read_bytes ()
206
- else :
207
- # JSON message format
208
- parsed_data = json_loads (data )
209
- data = parsed_data ["data" ].decode ()
210
- headers = parsed_data ["headers" ]
211
- warnings .warn (
212
- "JSON decoding deprecated in **FastStream 0.6.0**. "
213
- "Please don't use it unless it's necessary"
214
- "Format will be removed in **FastStream 0.7.0**." ,
215
- DeprecationWarning ,
216
- stacklevel = 2 ,
217
- )
218
244
except Exception :
219
245
# Raw Redis message format
220
246
data = data
221
247
headers = {}
222
-
223
248
return data , headers
224
249
225
250
@@ -228,9 +253,11 @@ class SimpleParser:
228
253
229
254
def __init__ (
230
255
self ,
256
+ message_format : Type ["MessageFormat" ] = JSONMessageFormat ,
231
257
pattern : Optional ["Pattern[str]" ] = None ,
232
258
) -> None :
233
259
self .pattern = pattern
260
+ self .message_format = message_format
234
261
235
262
async def parse_message (
236
263
self ,
@@ -256,7 +283,7 @@ def _parse_data(
256
283
self ,
257
284
message : Mapping [str , Any ],
258
285
) -> Tuple [bytes , "AnyDict" , List ["AnyDict" ]]:
259
- return (* RawMessage .parse (message ["data" ]), [])
286
+ return (* self . message_format .parse (message ["data" ]), [])
260
287
261
288
def get_path (self , message : Mapping [str , Any ]) -> "AnyDict" :
262
289
if (
@@ -295,7 +322,7 @@ def _parse_data(
295
322
batch_headers : List [AnyDict ] = []
296
323
297
324
for x in message ["data" ]:
298
- msg_data , msg_headers = _decode_batch_body_item (x )
325
+ msg_data , msg_headers = _decode_batch_body_item (x , self . message_format )
299
326
body .append (msg_data )
300
327
batch_headers .append (msg_headers )
301
328
@@ -319,7 +346,7 @@ def _parse_data(
319
346
cls , message : Mapping [str , Any ]
320
347
) -> Tuple [bytes , "AnyDict" , List ["AnyDict" ]]:
321
348
data = message ["data" ]
322
- return (* RawMessage .parse (data .get (bDATA_KEY ) or dump_json (data )), [])
349
+ return (* JSONMessageFormat .parse (data .get (bDATA_KEY ) or dump_json (data )), [])
323
350
324
351
325
352
class RedisBatchStreamParser (SimpleParser ):
@@ -333,7 +360,9 @@ def _parse_data(
333
360
batch_headers : List [AnyDict ] = []
334
361
335
362
for x in message ["data" ]:
336
- msg_data , msg_headers = _decode_batch_body_item (x .get (bDATA_KEY , x ))
363
+ msg_data , msg_headers = _decode_batch_body_item (
364
+ x .get (bDATA_KEY , x ), self .message_format
365
+ )
337
366
body .append (msg_data )
338
367
batch_headers .append (msg_headers )
339
368
@@ -349,8 +378,10 @@ def _parse_data(
349
378
)
350
379
351
380
352
- def _decode_batch_body_item (msg_content : bytes ) -> Tuple [Any , "AnyDict" ]:
353
- msg_body , headers = RawMessage .parse (msg_content )
381
+ def _decode_batch_body_item (
382
+ msg_content : bytes , message_format : Type ["MessageFormat" ]
383
+ ) -> Tuple [Any , "AnyDict" ]:
384
+ msg_body , headers = message_format .parse (msg_content )
354
385
try :
355
386
return json_loads (msg_body ), headers
356
387
except Exception :
0 commit comments