|
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