@@ -600,18 +600,22 @@ def save(self, obj, save_persistent_id=True):
600
600
self .save_global (obj , rv )
601
601
return
602
602
603
- # Assert that reduce() returned a tuple
604
- if not isinstance (rv , tuple ):
605
- raise PicklingError (f'__reduce__ must return a string or tuple, not { _T (rv )} ' )
606
-
607
- # Assert that it returned an appropriately sized tuple
608
- l = len (rv )
609
- if not (2 <= l <= 6 ):
610
- raise PicklingError ("tuple returned by __reduce__ "
611
- "must contain 2 through 6 elements" )
612
-
613
- # Save the reduce() output and finally memoize the object
614
- self .save_reduce (obj = obj , * rv )
603
+ try :
604
+ # Assert that reduce() returned a tuple
605
+ if not isinstance (rv , tuple ):
606
+ raise PicklingError (f'__reduce__ must return a string or tuple, not { _T (rv )} ' )
607
+
608
+ # Assert that it returned an appropriately sized tuple
609
+ l = len (rv )
610
+ if not (2 <= l <= 6 ):
611
+ raise PicklingError ("tuple returned by __reduce__ "
612
+ "must contain 2 through 6 elements" )
613
+
614
+ # Save the reduce() output and finally memoize the object
615
+ self .save_reduce (obj = obj , * rv )
616
+ except BaseException as exc :
617
+ exc .add_note (f'when serializing { _T (obj )} object' )
618
+ raise
615
619
616
620
def persistent_id (self , obj ):
617
621
# This exists so a subclass can override it
@@ -652,13 +656,25 @@ def save_reduce(self, func, args, state=None, listitems=None,
652
656
raise PicklingError (f"first argument to __newobj_ex__() "
653
657
f"must be { obj .__class__ !r} , not { cls !r} " )
654
658
if self .proto >= 4 :
655
- save (cls )
656
- save (args )
657
- save (kwargs )
659
+ try :
660
+ save (cls )
661
+ except BaseException as exc :
662
+ exc .add_note (f'when serializing { _T (obj )} class' )
663
+ raise
664
+ try :
665
+ save (args )
666
+ save (kwargs )
667
+ except BaseException as exc :
668
+ exc .add_note (f'when serializing { _T (obj )} __new__ arguments' )
669
+ raise
658
670
write (NEWOBJ_EX )
659
671
else :
660
672
func = partial (cls .__new__ , cls , * args , ** kwargs )
661
- save (func )
673
+ try :
674
+ save (func )
675
+ except BaseException as exc :
676
+ exc .add_note (f'when serializing { _T (obj )} reconstructor' )
677
+ raise
662
678
save (())
663
679
write (REDUCE )
664
680
elif self .proto >= 2 and func_name == "__newobj__" :
@@ -695,12 +711,28 @@ def save_reduce(self, func, args, state=None, listitems=None,
695
711
raise PicklingError (f"first argument to __newobj__() "
696
712
f"must be { obj .__class__ !r} , not { cls !r} " )
697
713
args = args [1 :]
698
- save (cls )
699
- save (args )
714
+ try :
715
+ save (cls )
716
+ except BaseException as exc :
717
+ exc .add_note (f'when serializing { _T (obj )} class' )
718
+ raise
719
+ try :
720
+ save (args )
721
+ except BaseException as exc :
722
+ exc .add_note (f'when serializing { _T (obj )} __new__ arguments' )
723
+ raise
700
724
write (NEWOBJ )
701
725
else :
702
- save (func )
703
- save (args )
726
+ try :
727
+ save (func )
728
+ except BaseException as exc :
729
+ exc .add_note (f'when serializing { _T (obj )} reconstructor' )
730
+ raise
731
+ try :
732
+ save (args )
733
+ except BaseException as exc :
734
+ exc .add_note (f'when serializing { _T (obj )} reconstructor arguments' )
735
+ raise
704
736
write (REDUCE )
705
737
706
738
if obj is not None :
@@ -718,23 +750,35 @@ def save_reduce(self, func, args, state=None, listitems=None,
718
750
# items and dict items (as (key, value) tuples), or None.
719
751
720
752
if listitems is not None :
721
- self ._batch_appends (listitems )
753
+ self ._batch_appends (listitems , obj )
722
754
723
755
if dictitems is not None :
724
- self ._batch_setitems (dictitems )
756
+ self ._batch_setitems (dictitems , obj )
725
757
726
758
if state is not None :
727
759
if state_setter is None :
728
- save (state )
760
+ try :
761
+ save (state )
762
+ except BaseException as exc :
763
+ exc .add_note (f'when serializing { _T (obj )} state' )
764
+ raise
729
765
write (BUILD )
730
766
else :
731
767
# If a state_setter is specified, call it instead of load_build
732
768
# to update obj's with its previous state.
733
769
# First, push state_setter and its tuple of expected arguments
734
770
# (obj, state) onto the stack.
735
- save (state_setter )
771
+ try :
772
+ save (state_setter )
773
+ except BaseException as exc :
774
+ exc .add_note (f'when serializing { _T (obj )} state setter' )
775
+ raise
736
776
save (obj ) # simple BINGET opcode as obj is already memoized.
737
- save (state )
777
+ try :
778
+ save (state )
779
+ except BaseException as exc :
780
+ exc .add_note (f'when serializing { _T (obj )} state' )
781
+ raise
738
782
write (TUPLE2 )
739
783
# Trigger a state_setter(obj, state) function call.
740
784
write (REDUCE )
@@ -914,8 +958,12 @@ def save_tuple(self, obj):
914
958
save = self .save
915
959
memo = self .memo
916
960
if n <= 3 and self .proto >= 2 :
917
- for element in obj :
918
- save (element )
961
+ for i , element in enumerate (obj ):
962
+ try :
963
+ save (element )
964
+ except BaseException as exc :
965
+ exc .add_note (f'when serializing { _T (obj )} item { i } ' )
966
+ raise
919
967
# Subtle. Same as in the big comment below.
920
968
if id (obj ) in memo :
921
969
get = self .get (memo [id (obj )][0 ])
@@ -929,8 +977,12 @@ def save_tuple(self, obj):
929
977
# has more than 3 elements.
930
978
write = self .write
931
979
write (MARK )
932
- for element in obj :
933
- save (element )
980
+ for i , element in enumerate (obj ):
981
+ try :
982
+ save (element )
983
+ except BaseException as exc :
984
+ exc .add_note (f'when serializing { _T (obj )} item { i } ' )
985
+ raise
934
986
935
987
if id (obj ) in memo :
936
988
# Subtle. d was not in memo when we entered save_tuple(), so
@@ -960,38 +1012,52 @@ def save_list(self, obj):
960
1012
self .write (MARK + LIST )
961
1013
962
1014
self .memoize (obj )
963
- self ._batch_appends (obj )
1015
+ self ._batch_appends (obj , obj )
964
1016
965
1017
dispatch [list ] = save_list
966
1018
967
1019
_BATCHSIZE = 1000
968
1020
969
- def _batch_appends (self , items ):
1021
+ def _batch_appends (self , items , obj ):
970
1022
# Helper to batch up APPENDS sequences
971
1023
save = self .save
972
1024
write = self .write
973
1025
974
1026
if not self .bin :
975
- for x in items :
976
- save (x )
1027
+ for i , x in enumerate (items ):
1028
+ try :
1029
+ save (x )
1030
+ except BaseException as exc :
1031
+ exc .add_note (f'when serializing { _T (obj )} item { i } ' )
1032
+ raise
977
1033
write (APPEND )
978
1034
return
979
1035
980
1036
it = iter (items )
1037
+ start = 0
981
1038
while True :
982
1039
tmp = list (islice (it , self ._BATCHSIZE ))
983
1040
n = len (tmp )
984
1041
if n > 1 :
985
1042
write (MARK )
986
- for x in tmp :
987
- save (x )
1043
+ for i , x in enumerate (tmp , start ):
1044
+ try :
1045
+ save (x )
1046
+ except BaseException as exc :
1047
+ exc .add_note (f'when serializing { _T (obj )} item { i } ' )
1048
+ raise
988
1049
write (APPENDS )
989
1050
elif n :
990
- save (tmp [0 ])
1051
+ try :
1052
+ save (tmp [0 ])
1053
+ except BaseException as exc :
1054
+ exc .add_note (f'when serializing { _T (obj )} item { start } ' )
1055
+ raise
991
1056
write (APPEND )
992
1057
# else tmp is empty, and we're done
993
1058
if n < self ._BATCHSIZE :
994
1059
return
1060
+ start += n
995
1061
996
1062
def save_dict (self , obj ):
997
1063
if self .bin :
@@ -1000,19 +1066,23 @@ def save_dict(self, obj):
1000
1066
self .write (MARK + DICT )
1001
1067
1002
1068
self .memoize (obj )
1003
- self ._batch_setitems (obj .items ())
1069
+ self ._batch_setitems (obj .items (), obj )
1004
1070
1005
1071
dispatch [dict ] = save_dict
1006
1072
1007
- def _batch_setitems (self , items ):
1073
+ def _batch_setitems (self , items , obj ):
1008
1074
# Helper to batch up SETITEMS sequences; proto >= 1 only
1009
1075
save = self .save
1010
1076
write = self .write
1011
1077
1012
1078
if not self .bin :
1013
1079
for k , v in items :
1014
1080
save (k )
1015
- save (v )
1081
+ try :
1082
+ save (v )
1083
+ except BaseException as exc :
1084
+ exc .add_note (f'when serializing { _T (obj )} item { k !r} ' )
1085
+ raise
1016
1086
write (SETITEM )
1017
1087
return
1018
1088
@@ -1024,12 +1094,20 @@ def _batch_setitems(self, items):
1024
1094
write (MARK )
1025
1095
for k , v in tmp :
1026
1096
save (k )
1027
- save (v )
1097
+ try :
1098
+ save (v )
1099
+ except BaseException as exc :
1100
+ exc .add_note (f'when serializing { _T (obj )} item { k !r} ' )
1101
+ raise
1028
1102
write (SETITEMS )
1029
1103
elif n :
1030
1104
k , v = tmp [0 ]
1031
1105
save (k )
1032
- save (v )
1106
+ try :
1107
+ save (v )
1108
+ except BaseException as exc :
1109
+ exc .add_note (f'when serializing { _T (obj )} item { k !r} ' )
1110
+ raise
1033
1111
write (SETITEM )
1034
1112
# else tmp is empty, and we're done
1035
1113
if n < self ._BATCHSIZE :
@@ -1052,8 +1130,12 @@ def save_set(self, obj):
1052
1130
n = len (batch )
1053
1131
if n > 0 :
1054
1132
write (MARK )
1055
- for item in batch :
1056
- save (item )
1133
+ try :
1134
+ for item in batch :
1135
+ save (item )
1136
+ except BaseException as exc :
1137
+ exc .add_note (f'when serializing { _T (obj )} element' )
1138
+ raise
1057
1139
write (ADDITEMS )
1058
1140
if n < self ._BATCHSIZE :
1059
1141
return
@@ -1068,8 +1150,12 @@ def save_frozenset(self, obj):
1068
1150
return
1069
1151
1070
1152
write (MARK )
1071
- for item in obj :
1072
- save (item )
1153
+ try :
1154
+ for item in obj :
1155
+ save (item )
1156
+ except BaseException as exc :
1157
+ exc .add_note (f'when serializing { _T (obj )} element' )
1158
+ raise
1073
1159
1074
1160
if id (obj ) in self .memo :
1075
1161
# If the object is already in the memo, this means it is
0 commit comments