@@ -79,57 +79,9 @@ def default(self, data):
7979
8080 # Special cases for numpy and pandas types
8181 # 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
13385
13486 raise TypeError ("Unable to serialize %r (type: %s)" % (data , type (data )))
13587
@@ -200,3 +152,94 @@ def loads(self, s, mimetype=None):
200152 )
201153
202154 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