@@ -72,59 +72,75 @@ def ensure_dir_exists(directory):
72
72
73
73
74
74
### Custom JSON encoders ###
75
+ class NotEncodable (Exception ):
76
+ pass
77
+
75
78
class _plotlyJSONEncoder (json .JSONEncoder ):
76
79
def numpyJSONEncoder (self , obj ):
77
80
try :
78
81
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
83
82
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
86
93
87
94
def datetimeJSONEncoder (self , obj ):
88
95
# if datetime or iterable of datetimes, convert to a string that plotly understands
89
96
# 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
90
97
import datetime
91
98
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 ]
105
99
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
108
115
109
116
def pandasJSONEncoder (self , obj ):
110
117
try :
111
118
import pandas
112
- if isinstance (obj , pandas .Series ):
113
- return obj .tolist ()
114
119
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
117
138
118
139
def sageJSONEncoder (self , obj ):
119
140
try :
120
141
from sage .all import RR , ZZ
121
- if obj in RR :
122
- return float (obj )
123
- elif obj in ZZ :
124
- return int (obj )
125
142
except :
126
- pass
127
- return None
143
+ raise NotEncodable
128
144
129
145
def maskedNumbersEncoder (self , obj ):
130
146
"""This catches masked numbers which can't be serialized.
@@ -140,26 +156,33 @@ def maskedNumbersEncoder(self, obj):
140
156
except :
141
157
pass
142
158
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
143
165
144
166
def builtinJSONEncoder (self , obj ):
145
167
try :
146
168
return obj .to_plotly_json ()
147
169
except AttributeError :
148
- return None
170
+ raise NotEncodable
171
+
149
172
150
173
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" )
163
186
164
187
165
188
### unicode stuff ###
0 commit comments