12
12
13
13
import json
14
14
from collections .abc import Mapping
15
- from typing import Annotated , Any , ClassVar , Generic , Literal , Optional , TypeVar
15
+ from typing import Annotated , Any , ClassVar , Generic , Literal , Optional , TypeVar , cast
16
16
17
17
try :
18
- import msgpack
18
+ import msgpack # type: ignore[import-untyped] # Optional dependency
19
19
from msgpack import Packer , Unpacker
20
20
21
21
HAS_MSGPACK = True
24
24
HAS_MSGPACK = False
25
25
26
26
try :
27
- from msgspec .msgpack import Decoder as MsgspecDecoder
28
- from msgspec .msgpack import Encoder as MsgspecEncoder
27
+ from msgspec .msgpack import ( # type: ignore[import-not-found] # Optional dependency
28
+ Decoder as MsgspecDecoder ,
29
+ )
30
+ from msgspec .msgpack import ( # type: ignore[import-not-found] # Optional dependency
31
+ Encoder as MsgspecEncoder ,
32
+ )
29
33
30
34
HAS_MSGSPEC = True
31
35
except ImportError :
32
36
MsgspecDecoder = MsgspecEncoder = None
33
37
HAS_MSGSPEC = False
34
38
35
39
try :
36
- import orjson
40
+ import orjson # type: ignore[import-not-found] # Optional dependency
37
41
38
42
HAS_ORJSON = True
39
43
except ImportError :
@@ -116,7 +120,7 @@ def encode_message(
116
120
"""
117
121
serialized = serializer .serialize (obj ) if serializer else obj
118
122
119
- return encoder .encode (serialized ) if encoder else serialized
123
+ return cast ( "MsgT" , encoder .encode (serialized ) if encoder else serialized )
120
124
121
125
@classmethod
122
126
def decode_message (
@@ -137,7 +141,9 @@ def decode_message(
137
141
"""
138
142
serialized = encoder .decode (message ) if encoder else message
139
143
140
- return serializer .deserialize (serialized ) if serializer else serialized
144
+ return cast (
145
+ "ObjT" , serializer .deserialize (serialized ) if serializer else serialized
146
+ )
141
147
142
148
def __init__ (
143
149
self ,
@@ -296,6 +302,15 @@ def _get_available_encoder_decoder(
296
302
return None , None , None
297
303
298
304
305
+ PayloadType = Literal [
306
+ "pydantic" ,
307
+ "python" ,
308
+ "collection_tuple" ,
309
+ "collection_sequence" ,
310
+ "collection_mapping" ,
311
+ ]
312
+
313
+
299
314
class Serializer :
300
315
"""
301
316
Object serialization with specialized Pydantic model support.
@@ -474,6 +489,7 @@ def to_sequence(self, obj: Any) -> str | Any:
474
489
:param obj: Object to serialize to sequence format
475
490
:return: Serialized sequence string or bytes
476
491
"""
492
+ payload_type : PayloadType
477
493
if isinstance (obj , BaseModel ):
478
494
payload_type = "pydantic"
479
495
payload = self .to_sequence_pydantic (obj )
@@ -515,7 +531,9 @@ def to_sequence(self, obj: Any) -> str | Any:
515
531
payload_type = "python"
516
532
payload = self .to_sequence_python (obj )
517
533
518
- return self .pack_next_sequence (payload_type , payload , None )
534
+ return self .pack_next_sequence (
535
+ payload_type , payload if payload is not None else "" , None
536
+ )
519
537
520
538
def from_sequence (self , data : str | Any ) -> Any : # noqa: C901, PLR0912
521
539
"""
@@ -529,6 +547,7 @@ def from_sequence(self, data: str | Any) -> Any: # noqa: C901, PLR0912
529
547
:raises ValueError: If sequence format is invalid or contains multiple
530
548
packed sequences
531
549
"""
550
+ payload : str | bytes | None
532
551
type_ , payload , remaining = self .unpack_next_sequence (data )
533
552
if remaining is not None :
534
553
raise ValueError ("Data contains multiple packed sequences; expected one." )
@@ -540,16 +559,16 @@ def from_sequence(self, data: str | Any) -> Any: # noqa: C901, PLR0912
540
559
return self .from_sequence_python (payload )
541
560
542
561
if type_ in {"collection_sequence" , "collection_tuple" }:
543
- items = []
562
+ c_items = []
544
563
while payload :
545
564
type_ , item_payload , payload = self .unpack_next_sequence (payload )
546
565
if type_ == "pydantic" :
547
- items .append (self .from_sequence_pydantic (item_payload ))
566
+ c_items .append (self .from_sequence_pydantic (item_payload ))
548
567
elif type_ == "python" :
549
- items .append (self .from_sequence_python (item_payload ))
568
+ c_items .append (self .from_sequence_python (item_payload ))
550
569
else :
551
570
raise ValueError ("Invalid type in collection sequence" )
552
- return items
571
+ return c_items
553
572
554
573
if type_ != "collection_mapping" :
555
574
raise ValueError (f"Invalid type for mapping sequence: { type_ } " )
@@ -604,6 +623,7 @@ def from_sequence_pydantic(self, data: str | bytes) -> BaseModel:
604
623
:param data: Sequence data containing class metadata and JSON
605
624
:return: Reconstructed Pydantic model instance
606
625
"""
626
+ json_data : str | bytes | bytearray
607
627
if isinstance (data , bytes ):
608
628
class_name_end = data .index (b"|" )
609
629
class_name = data [:class_name_end ].decode ()
@@ -647,13 +667,7 @@ def from_sequence_python(self, data: str | bytes) -> Any:
647
667
648
668
def pack_next_sequence ( # noqa: C901, PLR0912
649
669
self ,
650
- type_ : Literal [
651
- "pydantic" ,
652
- "python" ,
653
- "collection_tuple" ,
654
- "collection_sequence" ,
655
- "collection_mapping" ,
656
- ],
670
+ type_ : PayloadType ,
657
671
payload : str | bytes ,
658
672
current : str | bytes | None ,
659
673
) -> str | bytes :
@@ -672,9 +686,11 @@ def pack_next_sequence( # noqa: C901, PLR0912
672
686
raise ValueError ("Payload and current must be of the same type" )
673
687
674
688
payload_len = len (payload )
675
-
689
+ payload_len_output : str | bytes
690
+ payload_type : str | bytes
691
+ delimiter : str | bytes
676
692
if isinstance (payload , bytes ):
677
- payload_len = payload_len .to_bytes (
693
+ payload_len_output = payload_len .to_bytes (
678
694
length = (payload_len .bit_length () + 7 ) // 8 if payload_len > 0 else 1 ,
679
695
byteorder = "big" ,
680
696
)
@@ -692,7 +708,7 @@ def pack_next_sequence( # noqa: C901, PLR0912
692
708
raise ValueError (f"Unknown type for packing: { type_ } " )
693
709
delimiter = b"|"
694
710
else :
695
- payload_len = str (payload_len )
711
+ payload_len_output = str (payload_len )
696
712
if type_ == "pydantic" :
697
713
payload_type = "P"
698
714
elif type_ == "python" :
@@ -707,20 +723,16 @@ def pack_next_sequence( # noqa: C901, PLR0912
707
723
raise ValueError (f"Unknown type for packing: { type_ } " )
708
724
delimiter = "|"
709
725
710
- next_sequence = payload_type + delimiter + payload_len + delimiter + payload
711
-
712
- return current + next_sequence if current else next_sequence
726
+ # Type ignores because types are enforced at runtime
727
+ next_sequence = (
728
+ payload_type + delimiter + payload_len_output + delimiter + payload # type: ignore[operator]
729
+ )
730
+ return current + next_sequence if current else next_sequence # type: ignore[operator]
713
731
714
732
def unpack_next_sequence ( # noqa: C901, PLR0912
715
733
self , data : str | bytes
716
734
) -> tuple [
717
- Literal [
718
- "pydantic" ,
719
- "python" ,
720
- "collection_tuple" ,
721
- "collection_sequence" ,
722
- "collection_mapping" ,
723
- ],
735
+ PayloadType ,
724
736
str | bytes ,
725
737
str | bytes | None ,
726
738
]:
@@ -731,57 +743,58 @@ def unpack_next_sequence( # noqa: C901, PLR0912
731
743
:return: Tuple of (type, payload, remaining_data)
732
744
:raises ValueError: If sequence format is invalid or unknown type character
733
745
"""
746
+ type_ : PayloadType
734
747
if isinstance (data , bytes ):
735
748
if len (data ) < len (b"T|N" ) or data [1 :2 ] != b"|" :
736
749
raise ValueError ("Invalid packed data format" )
737
750
738
- type_char = data [0 :1 ]
739
- if type_char == b"P" :
751
+ type_char_b = data [0 :1 ]
752
+ if type_char_b == b"P" :
740
753
type_ = "pydantic"
741
- elif type_char == b"p" :
754
+ elif type_char_b == b"p" :
742
755
type_ = "python"
743
- elif type_char == b"T" :
756
+ elif type_char_b == b"T" :
744
757
type_ = "collection_tuple"
745
- elif type_char == b"S" :
758
+ elif type_char_b == b"S" :
746
759
type_ = "collection_sequence"
747
- elif type_char == b"M" :
760
+ elif type_char_b == b"M" :
748
761
type_ = "collection_mapping"
749
762
else :
750
763
raise ValueError ("Unknown type character in packed data" )
751
764
752
765
len_end = data .index (b"|" , 2 )
753
766
payload_len = int .from_bytes (data [2 :len_end ], "big" )
754
- payload = data [len_end + 1 : len_end + 1 + payload_len ]
755
- remaining = (
767
+ payload_b = data [len_end + 1 : len_end + 1 + payload_len ]
768
+ remaining_b = (
756
769
data [len_end + 1 + payload_len :]
757
770
if len_end + 1 + payload_len < len (data )
758
771
else None
759
772
)
760
773
761
- return type_ , payload , remaining
774
+ return type_ , payload_b , remaining_b
762
775
763
776
if len (data ) < len ("T|N" ) or data [1 ] != "|" :
764
777
raise ValueError ("Invalid packed data format" )
765
778
766
- type_char = data [0 ]
767
- if type_char == "P" :
779
+ type_char_s = data [0 ]
780
+ if type_char_s == "P" :
768
781
type_ = "pydantic"
769
- elif type_char == "p" :
782
+ elif type_char_s == "p" :
770
783
type_ = "python"
771
- elif type_char == "S" :
784
+ elif type_char_s == "S" :
772
785
type_ = "collection_sequence"
773
- elif type_char == "M" :
786
+ elif type_char_s == "M" :
774
787
type_ = "collection_mapping"
775
788
else :
776
789
raise ValueError ("Unknown type character in packed data" )
777
790
778
791
len_end = data .index ("|" , 2 )
779
792
payload_len = int (data [2 :len_end ])
780
- payload = data [len_end + 1 : len_end + 1 + payload_len ]
781
- remaining = (
793
+ payload_s = data [len_end + 1 : len_end + 1 + payload_len ]
794
+ remaining_s = (
782
795
data [len_end + 1 + payload_len :]
783
796
if len_end + 1 + payload_len < len (data )
784
797
else None
785
798
)
786
799
787
- return type_ , payload , remaining
800
+ return type_ , payload_s , remaining_s
0 commit comments