Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/pandas/compat/pickle_compat.py: 31%

107 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1""" 

2Support pre-0.12 series pickle compatibility. 

3""" 

4from __future__ import annotations 

5 

6import contextlib 

7import copy 

8import io 

9import pickle as pkl 

10from typing import ( 

11 TYPE_CHECKING, 

12 Iterator, 

13) 

14import warnings 

15 

16import numpy as np 

17 

18from pandas._libs.arrays import NDArrayBacked 

19from pandas._libs.tslibs import BaseOffset 

20 

21from pandas import Index 

22from pandas.core.arrays import ( 

23 DatetimeArray, 

24 PeriodArray, 

25 TimedeltaArray, 

26) 

27from pandas.core.internals import BlockManager 

28 

29if TYPE_CHECKING: 29 ↛ 30line 29 didn't jump to line 30, because the condition on line 29 was never true

30 from pandas import ( 

31 DataFrame, 

32 Series, 

33 ) 

34 

35 

36def load_reduce(self): 

37 stack = self.stack 

38 args = stack.pop() 

39 func = stack[-1] 

40 

41 try: 

42 stack[-1] = func(*args) 

43 return 

44 except TypeError as err: 

45 

46 # If we have a deprecated function, 

47 # try to replace and try again. 

48 

49 msg = "_reconstruct: First argument must be a sub-type of ndarray" 

50 

51 if msg in str(err): 

52 try: 

53 cls = args[0] 

54 stack[-1] = object.__new__(cls) 

55 return 

56 except TypeError: 

57 pass 

58 elif args and isinstance(args[0], type) and issubclass(args[0], BaseOffset): 

59 # TypeError: object.__new__(Day) is not safe, use Day.__new__() 

60 cls = args[0] 

61 stack[-1] = cls.__new__(*args) 

62 return 

63 elif args and issubclass(args[0], PeriodArray): 

64 cls = args[0] 

65 stack[-1] = NDArrayBacked.__new__(*args) 

66 return 

67 

68 raise 

69 

70 

71_sparse_msg = """\ 

72 

73Loading a saved '{cls}' as a {new} with sparse values. 

74'{cls}' is now removed. You should re-save this dataset in its new format. 

75""" 

76 

77 

78class _LoadSparseSeries: 

79 # To load a SparseSeries as a Series[Sparse] 

80 

81 # https://github.com/python/mypy/issues/1020 

82 # error: Incompatible return type for "__new__" (returns "Series", but must return 

83 # a subtype of "_LoadSparseSeries") 

84 def __new__(cls) -> Series: # type: ignore[misc] 

85 from pandas import Series 

86 

87 warnings.warn( 

88 _sparse_msg.format(cls="SparseSeries", new="Series"), 

89 FutureWarning, 

90 stacklevel=6, 

91 ) 

92 

93 return Series(dtype=object) 

94 

95 

96class _LoadSparseFrame: 

97 # To load a SparseDataFrame as a DataFrame[Sparse] 

98 

99 # https://github.com/python/mypy/issues/1020 

100 # error: Incompatible return type for "__new__" (returns "DataFrame", but must 

101 # return a subtype of "_LoadSparseFrame") 

102 def __new__(cls) -> DataFrame: # type: ignore[misc] 

103 from pandas import DataFrame 

104 

105 warnings.warn( 

106 _sparse_msg.format(cls="SparseDataFrame", new="DataFrame"), 

107 FutureWarning, 

108 stacklevel=6, 

109 ) 

110 

111 return DataFrame() 

112 

113 

114# If classes are moved, provide compat here. 

115_class_locations_map = { 

116 ("pandas.core.sparse.array", "SparseArray"): ("pandas.core.arrays", "SparseArray"), 

117 # 15477 

118 ("pandas.core.base", "FrozenNDArray"): ("numpy", "ndarray"), 

119 ("pandas.core.indexes.frozen", "FrozenNDArray"): ("numpy", "ndarray"), 

120 ("pandas.core.base", "FrozenList"): ("pandas.core.indexes.frozen", "FrozenList"), 

121 # 10890 

122 ("pandas.core.series", "TimeSeries"): ("pandas.core.series", "Series"), 

123 ("pandas.sparse.series", "SparseTimeSeries"): ( 

124 "pandas.core.sparse.series", 

125 "SparseSeries", 

126 ), 

127 # 12588, extensions moving 

128 ("pandas._sparse", "BlockIndex"): ("pandas._libs.sparse", "BlockIndex"), 

129 ("pandas.tslib", "Timestamp"): ("pandas._libs.tslib", "Timestamp"), 

130 # 18543 moving period 

131 ("pandas._period", "Period"): ("pandas._libs.tslibs.period", "Period"), 

132 ("pandas._libs.period", "Period"): ("pandas._libs.tslibs.period", "Period"), 

133 # 18014 moved __nat_unpickle from _libs.tslib-->_libs.tslibs.nattype 

134 ("pandas.tslib", "__nat_unpickle"): ( 

135 "pandas._libs.tslibs.nattype", 

136 "__nat_unpickle", 

137 ), 

138 ("pandas._libs.tslib", "__nat_unpickle"): ( 

139 "pandas._libs.tslibs.nattype", 

140 "__nat_unpickle", 

141 ), 

142 # 15998 top-level dirs moving 

143 ("pandas.sparse.array", "SparseArray"): ( 

144 "pandas.core.arrays.sparse", 

145 "SparseArray", 

146 ), 

147 ("pandas.sparse.series", "SparseSeries"): ( 

148 "pandas.compat.pickle_compat", 

149 "_LoadSparseSeries", 

150 ), 

151 ("pandas.sparse.frame", "SparseDataFrame"): ( 

152 "pandas.core.sparse.frame", 

153 "_LoadSparseFrame", 

154 ), 

155 ("pandas.indexes.base", "_new_Index"): ("pandas.core.indexes.base", "_new_Index"), 

156 ("pandas.indexes.base", "Index"): ("pandas.core.indexes.base", "Index"), 

157 ("pandas.indexes.numeric", "Int64Index"): ( 

158 "pandas.core.indexes.numeric", 

159 "Int64Index", 

160 ), 

161 ("pandas.indexes.range", "RangeIndex"): ("pandas.core.indexes.range", "RangeIndex"), 

162 ("pandas.indexes.multi", "MultiIndex"): ("pandas.core.indexes.multi", "MultiIndex"), 

163 ("pandas.tseries.index", "_new_DatetimeIndex"): ( 

164 "pandas.core.indexes.datetimes", 

165 "_new_DatetimeIndex", 

166 ), 

167 ("pandas.tseries.index", "DatetimeIndex"): ( 

168 "pandas.core.indexes.datetimes", 

169 "DatetimeIndex", 

170 ), 

171 ("pandas.tseries.period", "PeriodIndex"): ( 

172 "pandas.core.indexes.period", 

173 "PeriodIndex", 

174 ), 

175 # 19269, arrays moving 

176 ("pandas.core.categorical", "Categorical"): ("pandas.core.arrays", "Categorical"), 

177 # 19939, add timedeltaindex, float64index compat from 15998 move 

178 ("pandas.tseries.tdi", "TimedeltaIndex"): ( 

179 "pandas.core.indexes.timedeltas", 

180 "TimedeltaIndex", 

181 ), 

182 ("pandas.indexes.numeric", "Float64Index"): ( 

183 "pandas.core.indexes.numeric", 

184 "Float64Index", 

185 ), 

186 ("pandas.core.sparse.series", "SparseSeries"): ( 

187 "pandas.compat.pickle_compat", 

188 "_LoadSparseSeries", 

189 ), 

190 ("pandas.core.sparse.frame", "SparseDataFrame"): ( 

191 "pandas.compat.pickle_compat", 

192 "_LoadSparseFrame", 

193 ), 

194} 

195 

196 

197# our Unpickler sub-class to override methods and some dispatcher 

198# functions for compat and uses a non-public class of the pickle module. 

199 

200 

201class Unpickler(pkl._Unpickler): 

202 def find_class(self, module, name): 

203 # override superclass 

204 key = (module, name) 

205 module, name = _class_locations_map.get(key, key) 

206 return super().find_class(module, name) 

207 

208 

209Unpickler.dispatch = copy.copy(Unpickler.dispatch) 

210Unpickler.dispatch[pkl.REDUCE[0]] = load_reduce 

211 

212 

213def load_newobj(self): 

214 args = self.stack.pop() 

215 cls = self.stack[-1] 

216 

217 # compat 

218 if issubclass(cls, Index): 

219 obj = object.__new__(cls) 

220 elif issubclass(cls, DatetimeArray) and not args: 

221 arr = np.array([], dtype="M8[ns]") 

222 obj = cls.__new__(cls, arr, arr.dtype) 

223 elif issubclass(cls, TimedeltaArray) and not args: 

224 arr = np.array([], dtype="m8[ns]") 

225 obj = cls.__new__(cls, arr, arr.dtype) 

226 elif cls is BlockManager and not args: 

227 obj = cls.__new__(cls, (), [], None, False) 

228 else: 

229 obj = cls.__new__(cls, *args) 

230 

231 self.stack[-1] = obj 

232 

233 

234Unpickler.dispatch[pkl.NEWOBJ[0]] = load_newobj 

235 

236 

237def load_newobj_ex(self): 

238 kwargs = self.stack.pop() 

239 args = self.stack.pop() 

240 cls = self.stack.pop() 

241 

242 # compat 

243 if issubclass(cls, Index): 

244 obj = object.__new__(cls) 

245 else: 

246 obj = cls.__new__(cls, *args, **kwargs) 

247 self.append(obj) 

248 

249 

250try: 

251 Unpickler.dispatch[pkl.NEWOBJ_EX[0]] = load_newobj_ex 

252except (AttributeError, KeyError): 

253 pass 

254 

255 

256def load(fh, encoding: str | None = None, is_verbose: bool = False): 

257 """ 

258 Load a pickle, with a provided encoding, 

259 

260 Parameters 

261 ---------- 

262 fh : a filelike object 

263 encoding : an optional encoding 

264 is_verbose : show exception output 

265 """ 

266 try: 

267 fh.seek(0) 

268 if encoding is not None: 

269 up = Unpickler(fh, encoding=encoding) 

270 else: 

271 up = Unpickler(fh) 

272 # "Unpickler" has no attribute "is_verbose" [attr-defined] 

273 up.is_verbose = is_verbose # type: ignore[attr-defined] 

274 

275 return up.load() 

276 except (ValueError, TypeError): 

277 raise 

278 

279 

280def loads( 

281 bytes_object: bytes, 

282 *, 

283 fix_imports: bool = True, 

284 encoding: str = "ASCII", 

285 errors: str = "strict", 

286): 

287 """ 

288 Analogous to pickle._loads. 

289 """ 

290 fd = io.BytesIO(bytes_object) 

291 return Unpickler( 

292 fd, fix_imports=fix_imports, encoding=encoding, errors=errors 

293 ).load() 

294 

295 

296@contextlib.contextmanager 

297def patch_pickle() -> Iterator[None]: 

298 """ 

299 Temporarily patch pickle to use our unpickler. 

300 """ 

301 orig_loads = pkl.loads 

302 try: 

303 setattr(pkl, "loads", loads) 

304 yield 

305 finally: 

306 setattr(pkl, "loads", orig_loads)