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 , cast , ClassVar , Generic , Literal , Optional , TypeVar
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 Decoder as MsgspecDecoder # type: ignore[import-not-found] # Optional dependency
28
+ from msgspec .msgpack import Encoder as MsgspecEncoder # type: ignore[import-not-found] # Optional dependency
29
29
30
30
HAS_MSGSPEC = True
31
31
except ImportError :
32
32
MsgspecDecoder = MsgspecEncoder = None
33
33
HAS_MSGSPEC = False
34
34
35
35
try :
36
- import orjson
36
+ import orjson # type: ignore[import-not-found] # Optional dependency
37
37
38
38
HAS_ORJSON = True
39
39
except ImportError :
@@ -116,7 +116,7 @@ def encode_message(
116
116
"""
117
117
serialized = serializer .serialize (obj ) if serializer else obj
118
118
119
- return encoder .encode (serialized ) if encoder else serialized
119
+ return cast ( MsgT , encoder .encode (serialized ) if encoder else serialized )
120
120
121
121
@classmethod
122
122
def decode_message (
@@ -137,7 +137,7 @@ def decode_message(
137
137
"""
138
138
serialized = encoder .decode (message ) if encoder else message
139
139
140
- return serializer .deserialize (serialized ) if serializer else serialized
140
+ return cast ( ObjT , serializer .deserialize (serialized ) if serializer else serialized )
141
141
142
142
def __init__ (
143
143
self ,
@@ -296,6 +296,8 @@ def _get_available_encoder_decoder(
296
296
return None , None , None
297
297
298
298
299
+ PayloadType = Literal ['pydantic' , 'python' , 'collection_tuple' , 'collection_sequence' , 'collection_mapping' ]
300
+
299
301
class Serializer :
300
302
"""
301
303
Object serialization with specialized Pydantic model support.
@@ -474,6 +476,7 @@ def to_sequence(self, obj: Any) -> str | Any:
474
476
:param obj: Object to serialize to sequence format
475
477
:return: Serialized sequence string or bytes
476
478
"""
479
+ payload_type : PayloadType
477
480
if isinstance (obj , BaseModel ):
478
481
payload_type = "pydantic"
479
482
payload = self .to_sequence_pydantic (obj )
@@ -515,7 +518,7 @@ def to_sequence(self, obj: Any) -> str | Any:
515
518
payload_type = "python"
516
519
payload = self .to_sequence_python (obj )
517
520
518
- return self .pack_next_sequence (payload_type , payload , None )
521
+ return self .pack_next_sequence (payload_type , payload if payload is not None else "" , None )
519
522
520
523
def from_sequence (self , data : str | Any ) -> Any : # noqa: C901, PLR0912
521
524
"""
@@ -529,6 +532,7 @@ def from_sequence(self, data: str | Any) -> Any: # noqa: C901, PLR0912
529
532
:raises ValueError: If sequence format is invalid or contains multiple
530
533
packed sequences
531
534
"""
535
+ payload : str | bytes | None
532
536
type_ , payload , remaining = self .unpack_next_sequence (data )
533
537
if remaining is not None :
534
538
raise ValueError ("Data contains multiple packed sequences; expected one." )
@@ -540,16 +544,16 @@ def from_sequence(self, data: str | Any) -> Any: # noqa: C901, PLR0912
540
544
return self .from_sequence_python (payload )
541
545
542
546
if type_ in {"collection_sequence" , "collection_tuple" }:
543
- items = []
547
+ c_items = []
544
548
while payload :
545
549
type_ , item_payload , payload = self .unpack_next_sequence (payload )
546
550
if type_ == "pydantic" :
547
- items .append (self .from_sequence_pydantic (item_payload ))
551
+ c_items .append (self .from_sequence_pydantic (item_payload ))
548
552
elif type_ == "python" :
549
- items .append (self .from_sequence_python (item_payload ))
553
+ c_items .append (self .from_sequence_python (item_payload ))
550
554
else :
551
555
raise ValueError ("Invalid type in collection sequence" )
552
- return items
556
+ return c_items
553
557
554
558
if type_ != "collection_mapping" :
555
559
raise ValueError (f"Invalid type for mapping sequence: { type_ } " )
@@ -604,6 +608,7 @@ def from_sequence_pydantic(self, data: str | bytes) -> BaseModel:
604
608
:param data: Sequence data containing class metadata and JSON
605
609
:return: Reconstructed Pydantic model instance
606
610
"""
611
+ json_data : str | bytes | bytearray
607
612
if isinstance (data , bytes ):
608
613
class_name_end = data .index (b"|" )
609
614
class_name = data [:class_name_end ].decode ()
@@ -647,13 +652,7 @@ def from_sequence_python(self, data: str | bytes) -> Any:
647
652
648
653
def pack_next_sequence ( # noqa: C901, PLR0912
649
654
self ,
650
- type_ : Literal [
651
- "pydantic" ,
652
- "python" ,
653
- "collection_tuple" ,
654
- "collection_sequence" ,
655
- "collection_mapping" ,
656
- ],
655
+ type_ : PayloadType ,
657
656
payload : str | bytes ,
658
657
current : str | bytes | None ,
659
658
) -> str | bytes :
@@ -672,9 +671,11 @@ def pack_next_sequence( # noqa: C901, PLR0912
672
671
raise ValueError ("Payload and current must be of the same type" )
673
672
674
673
payload_len = len (payload )
675
-
674
+ payload_len_output : str | bytes
675
+ payload_type : str | bytes
676
+ delimiter : str | bytes
676
677
if isinstance (payload , bytes ):
677
- payload_len = payload_len .to_bytes (
678
+ payload_len_output = payload_len .to_bytes (
678
679
length = (payload_len .bit_length () + 7 ) // 8 if payload_len > 0 else 1 ,
679
680
byteorder = "big" ,
680
681
)
@@ -692,7 +693,7 @@ def pack_next_sequence( # noqa: C901, PLR0912
692
693
raise ValueError (f"Unknown type for packing: { type_ } " )
693
694
delimiter = b"|"
694
695
else :
695
- payload_len = str (payload_len )
696
+ payload_len_output = str (payload_len )
696
697
if type_ == "pydantic" :
697
698
payload_type = "P"
698
699
elif type_ == "python" :
@@ -707,20 +708,14 @@ def pack_next_sequence( # noqa: C901, PLR0912
707
708
raise ValueError (f"Unknown type for packing: { type_ } " )
708
709
delimiter = "|"
709
710
710
- next_sequence = payload_type + delimiter + payload_len + delimiter + payload
711
-
712
- return current + next_sequence if current else next_sequence
711
+ # Type ignores because types are enforced at runtime
712
+ next_sequence = payload_type + delimiter + payload_len_output + delimiter + payload # type: ignore[operator]
713
+ return current + next_sequence if current else next_sequence # type: ignore[operator]
713
714
714
715
def unpack_next_sequence ( # noqa: C901, PLR0912
715
716
self , data : str | bytes
716
717
) -> tuple [
717
- Literal [
718
- "pydantic" ,
719
- "python" ,
720
- "collection_tuple" ,
721
- "collection_sequence" ,
722
- "collection_mapping" ,
723
- ],
718
+ PayloadType ,
724
719
str | bytes ,
725
720
str | bytes | None ,
726
721
]:
@@ -731,57 +726,58 @@ def unpack_next_sequence( # noqa: C901, PLR0912
731
726
:return: Tuple of (type, payload, remaining_data)
732
727
:raises ValueError: If sequence format is invalid or unknown type character
733
728
"""
729
+ type_ : PayloadType
734
730
if isinstance (data , bytes ):
735
731
if len (data ) < len (b"T|N" ) or data [1 :2 ] != b"|" :
736
732
raise ValueError ("Invalid packed data format" )
737
733
738
- type_char = data [0 :1 ]
739
- if type_char == b"P" :
734
+ type_char_b = data [0 :1 ]
735
+ if type_char_b == b"P" :
740
736
type_ = "pydantic"
741
- elif type_char == b"p" :
737
+ elif type_char_b == b"p" :
742
738
type_ = "python"
743
- elif type_char == b"T" :
739
+ elif type_char_b == b"T" :
744
740
type_ = "collection_tuple"
745
- elif type_char == b"S" :
741
+ elif type_char_b == b"S" :
746
742
type_ = "collection_sequence"
747
- elif type_char == b"M" :
743
+ elif type_char_b == b"M" :
748
744
type_ = "collection_mapping"
749
745
else :
750
746
raise ValueError ("Unknown type character in packed data" )
751
747
752
748
len_end = data .index (b"|" , 2 )
753
749
payload_len = int .from_bytes (data [2 :len_end ], "big" )
754
- payload = data [len_end + 1 : len_end + 1 + payload_len ]
755
- remaining = (
750
+ payload_b = data [len_end + 1 : len_end + 1 + payload_len ]
751
+ remaining_b = (
756
752
data [len_end + 1 + payload_len :]
757
753
if len_end + 1 + payload_len < len (data )
758
754
else None
759
755
)
760
756
761
- return type_ , payload , remaining
757
+ return type_ , payload_b , remaining_b
762
758
763
759
if len (data ) < len ("T|N" ) or data [1 ] != "|" :
764
760
raise ValueError ("Invalid packed data format" )
765
761
766
- type_char = data [0 ]
767
- if type_char == "P" :
762
+ type_char_s = data [0 ]
763
+ if type_char_s == "P" :
768
764
type_ = "pydantic"
769
- elif type_char == "p" :
765
+ elif type_char_s == "p" :
770
766
type_ = "python"
771
- elif type_char == "S" :
767
+ elif type_char_s == "S" :
772
768
type_ = "collection_sequence"
773
- elif type_char == "M" :
769
+ elif type_char_s == "M" :
774
770
type_ = "collection_mapping"
775
771
else :
776
772
raise ValueError ("Unknown type character in packed data" )
777
773
778
774
len_end = data .index ("|" , 2 )
779
775
payload_len = int (data [2 :len_end ])
780
- payload = data [len_end + 1 : len_end + 1 + payload_len ]
781
- remaining = (
776
+ payload_s = data [len_end + 1 : len_end + 1 + payload_len ]
777
+ remaining_s = (
782
778
data [len_end + 1 + payload_len :]
783
779
if len_end + 1 + payload_len < len (data )
784
780
else None
785
781
)
786
782
787
- return type_ , payload , remaining
783
+ return type_ , payload_s , remaining_s
0 commit comments