|
6 | 6 |
|
7 | 7 | """
|
8 | 8 |
|
9 |
| -import json |
10 | 9 | import os.path
|
11 | 10 | import sys
|
12 | 11 | import threading
|
|
32 | 31 | except ImportError:
|
33 | 32 | _sage_imported = False
|
34 | 33 |
|
| 34 | +if sys.version[:3] == '2.6': |
| 35 | + import simplejson as json |
| 36 | +else: |
| 37 | + import json |
| 38 | + |
35 | 39 |
|
36 | 40 | ### incase people are using threading, we lock file reads
|
37 | 41 | lock = threading.Lock()
|
@@ -124,66 +128,45 @@ class PlotlyJSONEncoder(json.JSONEncoder):
|
124 | 128 | version.
|
125 | 129 |
|
126 | 130 | """
|
| 131 | + def coerce_to_strict(self, const): |
| 132 | + """ |
| 133 | + This is used to ultimately *encode* into strict JSON, see `encode` |
127 | 134 |
|
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): |
133 | 135 | """
|
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 |
136 | 141 |
|
137 |
| - For example:: |
| 142 | + def encode(self, o): |
| 143 | + """ |
| 144 | + Load and then dump the result using parse_constant kwarg |
138 | 145 |
|
139 |
| - for chunk in JSONEncoder().iterencode(bigobject): |
140 |
| - mysocket.write(chunk) |
| 146 | + Note that setting invalid separators will cause a failure at this step. |
141 | 147 |
|
142 | 148 | """
|
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) |
174 | 149 |
|
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) |
179 | 152 |
|
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: |
181 | 159 |
|
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)) |
187 | 170 |
|
188 | 171 | def default(self, obj):
|
189 | 172 | """
|
|
0 commit comments