@@ -72,59 +72,75 @@ def ensure_dir_exists(directory):
7272
7373
7474### Custom JSON encoders ###
75+ class NotEncodable (Exception ):
76+ pass
77+
7578class _plotlyJSONEncoder (json .JSONEncoder ):
7679 def numpyJSONEncoder (self , obj ):
7780 try :
7881 import numpy
79- if type (obj ).__module__ .split ('.' )[0 ] == numpy .__name__ :
80- l = obj .tolist ()
81- d = self .datetimeJSONEncoder (l )
82- return d if d is not None else l
8382 except :
84- pass
85- return None
83+ raise NotEncodable
84+
85+ if type (obj ).__module__ .split ('.' )[0 ] == numpy .__name__ :
86+ l = obj .tolist ()
87+ try :
88+ return self .datetimeJSONEncoder (l )
89+ except NotEncodable :
90+ return l
91+ else :
92+ raise NotEncodable
8693
8794 def datetimeJSONEncoder (self , obj ):
8895 # if datetime or iterable of datetimes, convert to a string that plotly understands
8996 # format as %Y-%m-%d %H:%M:%S.%f, %Y-%m-%d %H:%M:%S, or %Y-%m-%d depending on what non-zero resolution was provided
9097 import datetime
9198 try :
92- if isinstance (obj , (datetime .datetime , datetime .date )):
93- if obj .microsecond != 0 :
94- return obj .strftime ('%Y-%m-%d %H:%M:%S.%f' )
95- elif obj .second != 0 or obj .minute != 0 or obj .hour != 0 :
96- return obj .strftime ('%Y-%m-%d %H:%M:%S' )
97- else :
98- return obj .strftime ('%Y-%m-%d' )
99- elif isinstance (obj [0 ], (datetime .datetime , datetime .date )):
100- return [o .strftime (
101- '%Y-%m-%d %H:%M:%S.%f' ) if o .microsecond != 0 else
102- o .strftime ('%Y-%m-%d %H:%M:%S' ) if o .second != 0 or o .minute != 0 or o .hour != 0 else
103- o .strftime ('%Y-%m-%d' )
104- for o in obj ]
10599 except :
106- pass
107- return None
100+ if isinstance (obj , (datetime .datetime , datetime .date )):
101+ if obj .microsecond != 0 :
102+ return obj .strftime ('%Y-%m-%d %H:%M:%S.%f' )
103+ elif obj .second != 0 or obj .minute != 0 or obj .hour != 0 :
104+ return obj .strftime ('%Y-%m-%d %H:%M:%S' )
105+ else :
106+ return obj .strftime ('%Y-%m-%d' )
107+ elif isinstance (obj [0 ], (datetime .datetime , datetime .date )):
108+ return [o .strftime (
109+ '%Y-%m-%d %H:%M:%S.%f' ) if o .microsecond != 0 else
110+ o .strftime ('%Y-%m-%d %H:%M:%S' ) if o .second != 0 or o .minute != 0 or o .hour != 0 else
111+ o .strftime ('%Y-%m-%d' )
112+ for o in obj ]
113+ else :
114+ raise NotEncodable
108115
109116 def pandasJSONEncoder (self , obj ):
110117 try :
111118 import pandas
112- if isinstance (obj , pandas .Series ):
113- return obj .tolist ()
114119 except :
115- pass
116- return None
120+ raise NotEncodable
121+
122+ if isinstance (obj , pandas .Series ):
123+ serializable_list = []
124+ for li in list (obj ):
125+ try :
126+ json .dumps (li )
127+ serializable_list .append (li )
128+ except TypeError :
129+ serializable_list .append (self .default (li ))
130+
131+ return serializable_list
132+ elif isinstance (obj , pandas .Index ):
133+ return obj .tolist ()
134+ elif obj is pandas .NaT :
135+ return None
136+ else :
137+ raise NotEncodable
117138
118139 def sageJSONEncoder (self , obj ):
119140 try :
120141 from sage .all import RR , ZZ
121- if obj in RR :
122- return float (obj )
123- elif obj in ZZ :
124- return int (obj )
125142 except :
126- pass
127- return None
143+ raise NotEncodable
128144
129145 def maskedNumbersEncoder (self , obj ):
130146 """This catches masked numbers which can't be serialized.
@@ -140,26 +156,33 @@ def maskedNumbersEncoder(self, obj):
140156 except :
141157 pass
142158 return None
159+ if obj in RR :
160+ return float (obj )
161+ elif obj in ZZ :
162+ return int (obj )
163+ else :
164+ raise NotEncodable
143165
144166 def builtinJSONEncoder (self , obj ):
145167 try :
146168 return obj .to_plotly_json ()
147169 except AttributeError :
148- return None
170+ raise NotEncodable
171+
149172
150173 def default (self , obj ):
151- try :
152- return json . dumps ( obj )
153- except TypeError as e :
154- encoders = ( self . builtinJSONEncoder , self .datetimeJSONEncoder ,
155- self .numpyJSONEncoder , self . pandasJSONEncoder ,
156- self . sageJSONEncoder , self . maskedNumbersEncoder )
157- for encoder in encoders :
158- s = encoder (obj )
159- if s is not None :
160- return s
161- raise e
162- return json . JSONEncoder . default ( self , obj )
174+ encoders = ( self . builtinJSONEncoder ,
175+ self . datetimeJSONEncoder ,
176+ self . numpyJSONEncoder ,
177+ self .pandasJSONEncoder ,
178+ self .sageJSONEncoder )
179+ for encoder in encoders :
180+ try :
181+ return encoder (obj )
182+ except NotEncodable :
183+ pass
184+
185+ raise TypeError ( repr ( obj ) + " is not JSON serializable" )
163186
164187
165188### unicode stuff ###
0 commit comments