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

Skip to content

Commit 64b8abb

Browse files
committed
Introduce more flexible strict JSON implementation
The previous version was impossible to maintain across different json versions.
1 parent 26b1b51 commit 64b8abb

File tree

1 file changed

+35
-52
lines changed

1 file changed

+35
-52
lines changed

plotly/utils.py

Lines changed: 35 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
77
"""
88

9-
import json
109
import os.path
1110
import sys
1211
import threading
@@ -32,6 +31,11 @@
3231
except ImportError:
3332
_sage_imported = False
3433

34+
if sys.version[:3] == '2.6':
35+
import simplejson as json
36+
else:
37+
import json
38+
3539

3640
### incase people are using threading, we lock file reads
3741
lock = threading.Lock()
@@ -124,66 +128,45 @@ class PlotlyJSONEncoder(json.JSONEncoder):
124128
version.
125129
126130
"""
131+
def coerce_to_strict(self, const):
132+
"""
133+
This is used to ultimately *encode* into strict JSON, see `encode`
127134
128-
# we want stricter JSON, so convert NaN, Inf, -Inf --> 'null'
129-
nan_str = inf_str = neg_inf_str = 'null'
130-
131-
# uses code from official python json.encoder module. Same licence applies.
132-
def iterencode(self, o, _one_shot=False):
133135
"""
134-
Encode the given object and yield each string
135-
representation as available.
136+
# before python 2.7, 'true', 'false', 'null', were include here.
137+
if const in ('Infinity', '-Infinity', 'NaN'):
138+
return None
139+
else:
140+
return const
136141

137-
For example::
142+
def encode(self, o):
143+
"""
144+
Load and then dump the result using parse_constant kwarg
138145
139-
for chunk in JSONEncoder().iterencode(bigobject):
140-
mysocket.write(chunk)
146+
Note that setting invalid separators will cause a failure at this step.
141147
142148
"""
143-
if self.check_circular:
144-
markers = {}
145-
else:
146-
markers = None
147-
if self.ensure_ascii:
148-
_encoder = json.encoder.encode_basestring_ascii
149-
else:
150-
_encoder = json.encoder.encode_basestring
151-
if self.encoding != 'utf-8':
152-
def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding):
153-
if isinstance(o, str):
154-
o = o.decode(_encoding)
155-
return _orig_encoder(o)
156-
157-
def floatstr(o, allow_nan=self.allow_nan,
158-
_repr=json.encoder.FLOAT_REPR, _inf=json.encoder.INFINITY,
159-
_neginf=-json.encoder.INFINITY):
160-
# Check for specials. Note that this type of test is processor
161-
# and/or platform-specific, so do tests which don't depend on the
162-
# internals.
163-
164-
# *any* two NaNs are not equivalent (even to itself) try:
165-
# float('NaN') == float('NaN')
166-
if o != o:
167-
text = self.nan_str
168-
elif o == _inf:
169-
text = self.inf_str
170-
elif o == _neginf:
171-
text = self.neg_inf_str
172-
else:
173-
return _repr(o)
174149

175-
if not allow_nan:
176-
raise ValueError(
177-
"Out of range float values are not JSON compliant: " +
178-
repr(o))
150+
# this will raise errors in a normal-expected way
151+
encoded_o = super(PlotlyJSONEncoder, self).encode(o)
179152

180-
return text
153+
# now:
154+
# 1. `loads` to switch Infinity, -Infinity, NaN to None
155+
# 2. `dumps` again so you get 'null' instead of extended JSON
156+
try:
157+
new_o = json.loads(encoded_o, parse_constant=self.coerce_to_strict)
158+
except ValueError:
181159

182-
_iterencode = json.encoder._make_iterencode(
183-
markers, self.default, _encoder, self.indent, floatstr,
184-
self.key_separator, self.item_separator, self.sort_keys,
185-
self.skipkeys, _one_shot)
186-
return _iterencode(o, 0)
160+
# invalid separators will fail here. raise a helpful exception
161+
raise ValueError(
162+
"Encoding into strict JSON failed. Did you set the separators "
163+
"valid JSON separators?"
164+
)
165+
else:
166+
return json.dumps(new_o, sort_keys=self.sort_keys,
167+
indent=self.indent,
168+
separators=(self.item_separator,
169+
self.key_separator))
187170

188171
def default(self, obj):
189172
"""

0 commit comments

Comments
 (0)