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
« 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
6import contextlib
7import copy
8import io
9import pickle as pkl
10from typing import (
11 TYPE_CHECKING,
12 Iterator,
13)
14import warnings
16import numpy as np
18from pandas._libs.arrays import NDArrayBacked
19from pandas._libs.tslibs import BaseOffset
21from pandas import Index
22from pandas.core.arrays import (
23 DatetimeArray,
24 PeriodArray,
25 TimedeltaArray,
26)
27from pandas.core.internals import BlockManager
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 )
36def load_reduce(self):
37 stack = self.stack
38 args = stack.pop()
39 func = stack[-1]
41 try:
42 stack[-1] = func(*args)
43 return
44 except TypeError as err:
46 # If we have a deprecated function,
47 # try to replace and try again.
49 msg = "_reconstruct: First argument must be a sub-type of ndarray"
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
68 raise
71_sparse_msg = """\
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"""
78class _LoadSparseSeries:
79 # To load a SparseSeries as a Series[Sparse]
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
87 warnings.warn(
88 _sparse_msg.format(cls="SparseSeries", new="Series"),
89 FutureWarning,
90 stacklevel=6,
91 )
93 return Series(dtype=object)
96class _LoadSparseFrame:
97 # To load a SparseDataFrame as a DataFrame[Sparse]
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
105 warnings.warn(
106 _sparse_msg.format(cls="SparseDataFrame", new="DataFrame"),
107 FutureWarning,
108 stacklevel=6,
109 )
111 return DataFrame()
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}
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.
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)
209Unpickler.dispatch = copy.copy(Unpickler.dispatch)
210Unpickler.dispatch[pkl.REDUCE[0]] = load_reduce
213def load_newobj(self):
214 args = self.stack.pop()
215 cls = self.stack[-1]
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)
231 self.stack[-1] = obj
234Unpickler.dispatch[pkl.NEWOBJ[0]] = load_newobj
237def load_newobj_ex(self):
238 kwargs = self.stack.pop()
239 args = self.stack.pop()
240 cls = self.stack.pop()
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)
250try:
251 Unpickler.dispatch[pkl.NEWOBJ_EX[0]] = load_newobj_ex
252except (AttributeError, KeyError):
253 pass
256def load(fh, encoding: str | None = None, is_verbose: bool = False):
257 """
258 Load a pickle, with a provided encoding,
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]
275 return up.load()
276 except (ValueError, TypeError):
277 raise
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()
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)