Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit dd4f974

Browse files
committed
If encoders aren't relevant, raise NotEncodable instead of returning None
Returning None for encoders failed in the case where the encodable value was actually None, like pd.NaT -> None
1 parent bff27b8 commit dd4f974

File tree

1 file changed

+67
-44
lines changed

1 file changed

+67
-44
lines changed

plotly/utils.py

Lines changed: 67 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -72,59 +72,75 @@ def ensure_dir_exists(directory):
7272

7373

7474
### Custom JSON encoders ###
75+
class NotEncodable(Exception):
76+
pass
77+
7578
class _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

Comments
 (0)