@@ -79,57 +79,9 @@ def default(self, data):
79
79
80
80
# Special cases for numpy and pandas types
81
81
# These are expensive to import so we try them last.
82
- try :
83
- import numpy as np
84
-
85
- if isinstance (
86
- data ,
87
- (
88
- np .int_ ,
89
- np .intc ,
90
- np .int8 ,
91
- np .int16 ,
92
- np .int32 ,
93
- np .int64 ,
94
- np .uint8 ,
95
- np .uint16 ,
96
- np .uint32 ,
97
- np .uint64 ,
98
- ),
99
- ):
100
- return int (data )
101
- elif isinstance (
102
- data ,
103
- (
104
- np .float_ ,
105
- np .float16 ,
106
- np .float32 ,
107
- np .float64 ,
108
- ),
109
- ):
110
- return float (data )
111
- elif isinstance (data , np .bool_ ):
112
- return bool (data )
113
- elif isinstance (data , np .datetime64 ):
114
- return data .item ().isoformat ()
115
- elif isinstance (data , np .ndarray ):
116
- return data .tolist ()
117
- except ImportError :
118
- pass
119
-
120
- try :
121
- import pandas as pd
122
-
123
- if isinstance (data , (pd .Series , pd .Categorical )):
124
- return data .tolist ()
125
- elif isinstance (data , pd .Timestamp ) and data is not getattr (
126
- pd , "NaT" , None
127
- ):
128
- return data .isoformat ()
129
- elif data is getattr (pd , "NA" , None ):
130
- return None
131
- except ImportError :
132
- pass
82
+ serialized , value = _attempt_serialize_numpy_or_pandas (data )
83
+ if serialized :
84
+ return value
133
85
134
86
raise TypeError ("Unable to serialize %r (type: %s)" % (data , type (data )))
135
87
@@ -200,3 +152,94 @@ def loads(self, s, mimetype=None):
200
152
)
201
153
202
154
return deserializer .loads (s )
155
+
156
+
157
+ def _attempt_serialize_numpy_or_pandas (data ):
158
+ """Attempts to serialize a value from the numpy or pandas libraries.
159
+ This function is separate from JSONSerializer because the inner functions
160
+ are rewritten to be no-ops if either library isn't available to avoid
161
+ attempting to import and raising an ImportError over and over again.
162
+
163
+ Returns a tuple of (bool, Any) where the bool corresponds to whether
164
+ the second value contains a properly serialized value and thus
165
+ should be returned by JSONSerializer.default().
166
+ """
167
+ serialized , value = _attempt_serialize_numpy (data )
168
+ if serialized :
169
+ return serialized , value
170
+
171
+ serialized , value = _attempt_serialize_pandas (data )
172
+ if serialized :
173
+ return serialized , value
174
+
175
+ return False , None
176
+
177
+
178
+ def _attempt_serialize_numpy (data ):
179
+ global _attempt_serialize_numpy
180
+ try :
181
+ import numpy as np # type: ignore
182
+
183
+ if isinstance (
184
+ data ,
185
+ (
186
+ np .int_ ,
187
+ np .intc ,
188
+ np .int8 ,
189
+ np .int16 ,
190
+ np .int32 ,
191
+ np .int64 ,
192
+ np .uint8 ,
193
+ np .uint16 ,
194
+ np .uint32 ,
195
+ np .uint64 ,
196
+ ),
197
+ ):
198
+ return True , int (data )
199
+ elif isinstance (
200
+ data ,
201
+ (
202
+ np .float_ ,
203
+ np .float16 ,
204
+ np .float32 ,
205
+ np .float64 ,
206
+ ),
207
+ ):
208
+ return True , float (data )
209
+ elif isinstance (data , np .bool_ ):
210
+ return True , bool (data )
211
+ elif isinstance (data , np .datetime64 ):
212
+ return True , data .item ().isoformat ()
213
+ elif isinstance (data , np .ndarray ):
214
+ return True , data .tolist ()
215
+
216
+ except ImportError :
217
+ # Since we failed to import 'numpy' we don't want to try again.
218
+ _attempt_serialize_numpy = _attempt_serialize_noop
219
+
220
+ return False , None
221
+
222
+
223
+ def _attempt_serialize_pandas (data ):
224
+ global _attempt_serialize_pandas
225
+ try :
226
+ import pandas as pd # type: ignore
227
+
228
+ if isinstance (data , (pd .Series , pd .Categorical )):
229
+ return True , data .tolist ()
230
+ elif isinstance (data , pd .Timestamp ) and data is not getattr (pd , "NaT" , None ):
231
+ return True , data .isoformat ()
232
+ elif data is getattr (pd , "NA" , None ):
233
+ return True , None
234
+
235
+ except ImportError :
236
+ # Since we failed to import 'pandas' we don't want to try again.
237
+ _attempt_serialize_pandas = _attempt_serialize_noop
238
+
239
+ return False , None
240
+
241
+
242
+ def _attempt_serialize_noop (data ): # noqa
243
+ # Short-circuit if the above functions can't import
244
+ # the corresponding library on the first attempt.
245
+ return False , None
0 commit comments