diff --git a/plotly/exceptions.py b/plotly/exceptions.py index 9d83f78a72..05df864497 100644 --- a/plotly/exceptions.py +++ b/plotly/exceptions.py @@ -5,7 +5,9 @@ A module that contains plotly's exception hierarchy. """ -import json +from __future__ import absolute_import + +from plotly.api.utils import to_native_utf8_string # Base Plotly Error @@ -21,7 +23,7 @@ class PlotlyRequestError(PlotlyError): """General API error. Raised for *all* failed requests.""" def __init__(self, message, status_code, content): - self.message = message + self.message = to_native_utf8_string(message) self.status_code = status_code self.content = content diff --git a/plotly/grid_objs/grid_objs.py b/plotly/grid_objs/grid_objs.py index 782e3dc2fe..128d3bf90a 100644 --- a/plotly/grid_objs/grid_objs.py +++ b/plotly/grid_objs/grid_objs.py @@ -5,9 +5,10 @@ """ from __future__ import absolute_import -import json from collections import MutableSequence +from requests.compat import json as _json + from plotly import exceptions, utils __all__ = None @@ -66,7 +67,7 @@ def __init__(self, data, name): def __str__(self): max_chars = 10 - jdata = json.dumps(self.data, cls=utils.PlotlyJSONEncoder) + jdata = _json.dumps(self.data, cls=utils.PlotlyJSONEncoder) if len(jdata) > max_chars: data_string = jdata[:max_chars] + "...]" else: diff --git a/plotly/offline/offline.py b/plotly/offline/offline.py index 965b5af09e..dfbd60ae6f 100644 --- a/plotly/offline/offline.py +++ b/plotly/offline/offline.py @@ -5,7 +5,6 @@ """ from __future__ import absolute_import -import json import os import uuid import warnings @@ -13,6 +12,8 @@ import time import webbrowser +from requests.compat import json as _json + import plotly from plotly import tools, utils from plotly.exceptions import PlotlyError @@ -183,10 +184,12 @@ def _plot_html(figure_or_data, config, validate, default_width, height = str(height) + 'px' plotdivid = uuid.uuid4() - jdata = json.dumps(figure.get('data', []), cls=utils.PlotlyJSONEncoder) - jlayout = json.dumps(figure.get('layout', {}), cls=utils.PlotlyJSONEncoder) + jdata = _json.dumps(figure.get('data', []), cls=utils.PlotlyJSONEncoder) + jlayout = _json.dumps(figure.get('layout', {}), + cls=utils.PlotlyJSONEncoder) if 'frames' in figure_or_data: - jframes = json.dumps(figure.get('frames', {}), cls=utils.PlotlyJSONEncoder) + jframes = _json.dumps(figure.get('frames', {}), + cls=utils.PlotlyJSONEncoder) configkeys = ( 'editable', @@ -211,7 +214,7 @@ def _plot_html(figure_or_data, config, validate, default_width, ) config_clean = dict((k, config[k]) for k in configkeys if k in config) - jconfig = json.dumps(config_clean) + jconfig = _json.dumps(config_clean) # TODO: The get_config 'source of truth' should # really be somewhere other than plotly.plotly diff --git a/plotly/plotly/plotly.py b/plotly/plotly/plotly.py index 5043eaf9ce..2440512efb 100644 --- a/plotly/plotly/plotly.py +++ b/plotly/plotly/plotly.py @@ -17,12 +17,12 @@ from __future__ import absolute_import import copy -import json import os import warnings import six import six.moves +from requests.compat import json as _json from plotly import exceptions, tools, utils, files from plotly.api import v1, v2 @@ -642,7 +642,7 @@ def write(self, trace, layout=None, validate=True, stream_object.update(dict(layout=layout)) # TODO: allow string version of this? - jdata = json.dumps(stream_object, cls=utils.PlotlyJSONEncoder) + jdata = _json.dumps(stream_object, cls=utils.PlotlyJSONEncoder) jdata += "\n" try: @@ -1056,7 +1056,7 @@ def append_columns(cls, columns, grid=None, grid_url=None): # This is sorta gross, we need to double-encode this. body = { - 'cols': json.dumps(columns, cls=utils.PlotlyJSONEncoder) + 'cols': _json.dumps(columns, cls=utils.PlotlyJSONEncoder) } fid = grid_id response = v2.grids.col_create(fid, body) diff --git a/plotly/tests/test_core/test_get_requests/test_get_requests.py b/plotly/tests/test_core/test_get_requests/test_get_requests.py index 4c4fd939e4..1719d86b38 100644 --- a/plotly/tests/test_core/test_get_requests/test_get_requests.py +++ b/plotly/tests/test_core/test_get_requests/test_get_requests.py @@ -6,11 +6,11 @@ """ import copy -import json -import requests +import requests import six from nose.plugins.attrib import attr +from requests.compat import json as _json default_headers = {'plotly-username': '', @@ -37,9 +37,9 @@ def test_user_does_not_exist(): resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) response = requests.get(server + resource, headers=hd) if six.PY3: - content = json.loads(response.content.decode('unicode_escape')) + content = _json.loads(response.content.decode('unicode_escape')) else: - content = json.loads(response.content) + content = _json.loads(response.content) print(response.status_code) print(content) assert response.status_code == 404 @@ -60,9 +60,9 @@ def test_file_does_not_exist(): resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) response = requests.get(server + resource, headers=hd) if six.PY3: - content = json.loads(response.content.decode('unicode_escape')) + content = _json.loads(response.content.decode('unicode_escape')) else: - content = json.loads(response.content) + content = _json.loads(response.content) print(response.status_code) print(content) assert response.status_code == 404 @@ -100,9 +100,9 @@ def test_private_permission_defined(): resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) response = requests.get(server + resource, headers=hd) if six.PY3: - content = json.loads(response.content.decode('unicode_escape')) + content = _json.loads(response.content.decode('unicode_escape')) else: - content = json.loads(response.content) + content = _json.loads(response.content) print(response.status_code) print(content) assert response.status_code == 403 @@ -122,9 +122,9 @@ def test_missing_headers(): del hd[header] response = requests.get(server + resource, headers=hd) if six.PY3: - content = json.loads(response.content.decode('unicode_escape')) + content = _json.loads(response.content.decode('unicode_escape')) else: - content = json.loads(response.content) + content = _json.loads(response.content) print(response.status_code) print(content) assert response.status_code == 422 @@ -142,13 +142,13 @@ def test_valid_request(): resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) response = requests.get(server + resource, headers=hd) if six.PY3: - content = json.loads(response.content.decode('unicode_escape')) + content = _json.loads(response.content.decode('unicode_escape')) else: - content = json.loads(response.content) + content = _json.loads(response.content) print(response.status_code) print(content) assert response.status_code == 200 - # content = json.loads(res.content) + # content = _json.loads(res.content) # response_payload = content['payload'] # figure = response_payload['figure'] # if figure['data'][0]['x'] != [u'1', u'2', u'3']: diff --git a/plotly/tests/test_core/test_graph_reference/test_graph_reference.py b/plotly/tests/test_core/test_graph_reference/test_graph_reference.py index c7d155af90..005d33a7c0 100644 --- a/plotly/tests/test_core/test_graph_reference/test_graph_reference.py +++ b/plotly/tests/test_core/test_graph_reference/test_graph_reference.py @@ -4,12 +4,12 @@ """ from __future__ import absolute_import -import json import os from pkg_resources import resource_string from unittest import TestCase from nose.plugins.attrib import attr +from requests.compat import json as _json from plotly import graph_reference as gr from plotly.api import v2 @@ -28,7 +28,7 @@ def test_default_schema_is_up_to_date(self): path = os.path.join('package_data', 'default-schema.json') s = resource_string('plotly', path).decode('utf-8') - default_schema = json.loads(s) + default_schema = _json.loads(s) msg = ( 'The default, hard-coded plot schema we ship with pip is out of ' diff --git a/plotly/tests/test_core/test_offline/test_offline.py b/plotly/tests/test_core/test_offline/test_offline.py index f845709a28..cc4b903de7 100644 --- a/plotly/tests/test_core/test_offline/test_offline.py +++ b/plotly/tests/test_core/test_offline/test_offline.py @@ -4,12 +4,13 @@ """ from __future__ import absolute_import -from nose.tools import raises from unittest import TestCase -from plotly.tests.utils import PlotlyTestCase -import json + +from requests.compat import json as _json import plotly +from plotly.tests.utils import PlotlyTestCase + fig = { 'data': [ @@ -35,8 +36,9 @@ def _read_html(self, file_url): return f.read() def test_default_plot_generates_expected_html(self): - data_json = json.dumps(fig['data'], cls=plotly.utils.PlotlyJSONEncoder) - layout_json = json.dumps( + data_json = _json.dumps(fig['data'], + cls=plotly.utils.PlotlyJSONEncoder) + layout_json = _json.dumps( fig['layout'], cls=plotly.utils.PlotlyJSONEncoder) diff --git a/plotly/tests/test_core/test_plotly/test_plot.py b/plotly/tests/test_core/test_plotly/test_plot.py index 25b6d208aa..77e9c84d6b 100644 --- a/plotly/tests/test_core/test_plotly/test_plot.py +++ b/plotly/tests/test_core/test_plotly/test_plot.py @@ -7,9 +7,9 @@ """ from __future__ import absolute_import -import json import requests import six +from requests.compat import json as _json from unittest import TestCase from nose.plugins.attrib import attr @@ -296,10 +296,10 @@ def generate_conflicting_plot_options_with_json_writes_of_config(): """ def gen_test(plot_options): def test(self): - config = json.load(open(CONFIG_FILE)) + config = _json.load(open(CONFIG_FILE)) with open(CONFIG_FILE, 'w') as f: config.update(plot_options) - f.write(json.dumps(config)) + f.write(_json.dumps(config)) self.assertRaises(PlotlyError, py._plot_option_logic, {}) return test diff --git a/plotly/tests/test_core/test_utils/test_utils.py b/plotly/tests/test_core/test_utils/test_utils.py index cb38648b8b..b406a6464a 100644 --- a/plotly/tests/test_core/test_utils/test_utils.py +++ b/plotly/tests/test_core/test_utils/test_utils.py @@ -1,8 +1,9 @@ from __future__ import absolute_import -import json from unittest import TestCase +from requests.compat import json as _json + from plotly.utils import PlotlyJSONEncoder, get_by_path, node_generator @@ -10,7 +11,7 @@ class TestJSONEncoder(TestCase): def test_nan_to_null(self): array = [1, float('NaN'), float('Inf'), float('-Inf'), 'platypus'] - result = json.dumps(array, cls=PlotlyJSONEncoder) + result = _json.dumps(array, cls=PlotlyJSONEncoder) expected_result = '[1, null, null, null, "platypus"]' self.assertEqual(result, expected_result) diff --git a/plotly/tests/test_optional/test_offline/test_offline.py b/plotly/tests/test_optional/test_offline/test_offline.py index 93d2c4c377..61fb1840cf 100644 --- a/plotly/tests/test_optional/test_offline/test_offline.py +++ b/plotly/tests/test_optional/test_offline/test_offline.py @@ -6,9 +6,9 @@ from nose.tools import raises from nose.plugins.attrib import attr +from requests.compat import json as _json from unittest import TestCase -import json import plotly @@ -75,8 +75,8 @@ def test_default_mpl_plot_generates_expected_html(self): figure = plotly.tools.mpl_to_plotly(fig) data = figure['data'] layout = figure['layout'] - data_json = json.dumps(data, cls=plotly.utils.PlotlyJSONEncoder) - layout_json = json.dumps(layout, cls=plotly.utils.PlotlyJSONEncoder) + data_json = _json.dumps(data, cls=plotly.utils.PlotlyJSONEncoder) + layout_json = _json.dumps(layout, cls=plotly.utils.PlotlyJSONEncoder) html = self._read_html(plotly.offline.plot_mpl(fig)) # just make sure a few of the parts are in here diff --git a/plotly/tests/test_optional/test_utils/test_utils.py b/plotly/tests/test_optional/test_utils/test_utils.py index a72f89a8b6..17997f5a15 100644 --- a/plotly/tests/test_optional/test_utils/test_utils.py +++ b/plotly/tests/test_optional/test_utils/test_utils.py @@ -5,7 +5,6 @@ from __future__ import absolute_import import datetime -import json import math import decimal from datetime import datetime as dt @@ -16,6 +15,7 @@ import pytz from nose.plugins.attrib import attr from pandas.util.testing import assert_series_equal +from requests.compat import json as _json from plotly import utils from plotly.graph_objs import Scatter, Scatter3d, Figure, Data @@ -179,7 +179,7 @@ def test_column_json_encoding(): Column(mixed_list, 'col 2'), Column(np_list, 'col 3') ] - json_columns = json.dumps( + json_columns = _json.dumps( columns, cls=utils.PlotlyJSONEncoder, sort_keys=True ) assert('[{"data": [1, 2, 3], "name": "col 1"}, ' @@ -198,8 +198,8 @@ def test_figure_json_encoding(): data = Data([s1, s2]) figure = Figure(data=data) - js1 = json.dumps(s1, cls=utils.PlotlyJSONEncoder, sort_keys=True) - js2 = json.dumps(s2, cls=utils.PlotlyJSONEncoder, sort_keys=True) + js1 = _json.dumps(s1, cls=utils.PlotlyJSONEncoder, sort_keys=True) + js2 = _json.dumps(s2, cls=utils.PlotlyJSONEncoder, sort_keys=True) assert(js1 == '{"type": "scatter3d", "x": [1, 2, 3], ' '"y": [1, 2, 3, null, null, null, "2014-01-05"], ' @@ -208,8 +208,8 @@ def test_figure_json_encoding(): assert(js2 == '{"type": "scatter", "x": [1, 2, 3]}') # Test JSON encoding works - json.dumps(data, cls=utils.PlotlyJSONEncoder, sort_keys=True) - json.dumps(figure, cls=utils.PlotlyJSONEncoder, sort_keys=True) + _json.dumps(data, cls=utils.PlotlyJSONEncoder, sort_keys=True) + _json.dumps(figure, cls=utils.PlotlyJSONEncoder, sort_keys=True) # Test data wasn't mutated assert(bool(np.asarray(np_list == @@ -221,18 +221,18 @@ def test_figure_json_encoding(): def test_datetime_json_encoding(): - j1 = json.dumps(dt_list, cls=utils.PlotlyJSONEncoder) + j1 = _json.dumps(dt_list, cls=utils.PlotlyJSONEncoder) assert(j1 == '["2014-01-05", ' '"2014-01-05 01:01:01", ' '"2014-01-05 01:01:01.000001"]') - j2 = json.dumps({"x": dt_list}, cls=utils.PlotlyJSONEncoder) + j2 = _json.dumps({"x": dt_list}, cls=utils.PlotlyJSONEncoder) assert(j2 == '{"x": ["2014-01-05", ' '"2014-01-05 01:01:01", ' '"2014-01-05 01:01:01.000001"]}') def test_pandas_json_encoding(): - j1 = json.dumps(df['col 1'], cls=utils.PlotlyJSONEncoder) + j1 = _json.dumps(df['col 1'], cls=utils.PlotlyJSONEncoder) assert(j1 == '[1, 2, 3, "2014-01-05", null, null, null]') # Test that data wasn't mutated @@ -240,28 +240,28 @@ def test_pandas_json_encoding(): pd.Series([1, 2, 3, dt(2014, 1, 5), pd.NaT, np.NaN, np.Inf], name='col 1')) - j2 = json.dumps(df.index, cls=utils.PlotlyJSONEncoder) + j2 = _json.dumps(df.index, cls=utils.PlotlyJSONEncoder) assert(j2 == '[0, 1, 2, 3, 4, 5, 6]') nat = [pd.NaT] - j3 = json.dumps(nat, cls=utils.PlotlyJSONEncoder) + j3 = _json.dumps(nat, cls=utils.PlotlyJSONEncoder) assert(j3 == '[null]') assert(nat[0] is pd.NaT) - j4 = json.dumps(rng, cls=utils.PlotlyJSONEncoder) + j4 = _json.dumps(rng, cls=utils.PlotlyJSONEncoder) assert(j4 == '["2011-01-01", "2011-01-01 01:00:00"]') - j5 = json.dumps(ts, cls=utils.PlotlyJSONEncoder) + j5 = _json.dumps(ts, cls=utils.PlotlyJSONEncoder) assert(j5 == '[1.5, 2.5]') assert_series_equal(ts, pd.Series([1.5, 2.5], index=rng)) - j6 = json.dumps(ts.index, cls=utils.PlotlyJSONEncoder) + j6 = _json.dumps(ts.index, cls=utils.PlotlyJSONEncoder) assert(j6 == '["2011-01-01", "2011-01-01 01:00:00"]') def test_numpy_masked_json_encoding(): l = [1, 2, np.ma.core.masked] - j1 = json.dumps(l, cls=utils.PlotlyJSONEncoder) + j1 = _json.dumps(l, cls=utils.PlotlyJSONEncoder) print(j1) assert(j1 == '[1, 2, null]') @@ -285,18 +285,18 @@ def test_masked_constants_example(): renderer = PlotlyRenderer() Exporter(renderer).run(fig) - json.dumps(renderer.plotly_fig, cls=utils.PlotlyJSONEncoder) + _json.dumps(renderer.plotly_fig, cls=utils.PlotlyJSONEncoder) - jy = json.dumps(renderer.plotly_fig['data'][1]['y'], + jy = _json.dumps(renderer.plotly_fig['data'][1]['y'], cls=utils.PlotlyJSONEncoder) print(jy) - array = json.loads(jy) + array = _json.loads(jy) assert(array == [-398.11793027, -398.11792966, -398.11786308, None]) def test_numpy_dates(): a = np.arange(np.datetime64('2011-07-11'), np.datetime64('2011-07-18')) - j1 = json.dumps(a, cls=utils.PlotlyJSONEncoder) + j1 = _json.dumps(a, cls=utils.PlotlyJSONEncoder) assert(j1 == '["2011-07-11", "2011-07-12", "2011-07-13", ' '"2011-07-14", "2011-07-15", "2011-07-16", ' '"2011-07-17"]') @@ -304,5 +304,5 @@ def test_numpy_dates(): def test_datetime_dot_date(): a = [datetime.date(2014, 1, 1), datetime.date(2014, 1, 2)] - j1 = json.dumps(a, cls=utils.PlotlyJSONEncoder) + j1 = _json.dumps(a, cls=utils.PlotlyJSONEncoder) assert(j1 == '["2014-01-01", "2014-01-02"]') diff --git a/plotly/tests/utils.py b/plotly/tests/utils.py index f8b1438e6f..2d1113d68b 100644 --- a/plotly/tests/utils.py +++ b/plotly/tests/utils.py @@ -1,5 +1,4 @@ import copy -import json from numbers import Number as Num from unittest import TestCase diff --git a/plotly/utils.py b/plotly/utils.py index 782779c457..a7716cc4d3 100644 --- a/plotly/utils.py +++ b/plotly/utils.py @@ -7,7 +7,6 @@ """ from __future__ import absolute_import -import json import os.path import re import sys @@ -16,6 +15,8 @@ import pytz +from requests.compat import json as _json + from . exceptions import PlotlyError @@ -51,7 +52,7 @@ def load_json_dict(filename, *args): lock.acquire() with open(filename, "r") as f: try: - data = json.load(f) + data = _json.load(f) if not isinstance(data, dict): data = {} except: @@ -66,7 +67,7 @@ def save_json_dict(filename, json_dict): """Save json to file. Error if path DNE, not a dict, or invalid json.""" if isinstance(json_dict, dict): # this will raise a TypeError if something goes wrong - json_string = json.dumps(json_dict, indent=4) + json_string = _json.dumps(json_dict, indent=4) lock.acquire() with open(filename, "w") as f: f.write(json_string) @@ -112,7 +113,7 @@ class NotEncodable(Exception): pass -class PlotlyJSONEncoder(json.JSONEncoder): +class PlotlyJSONEncoder(_json.JSONEncoder): """ Meant to be passed as the `cls` kwarg to json.dumps(obj, cls=..) @@ -149,7 +150,8 @@ def encode(self, o): # 1. `loads` to switch Infinity, -Infinity, NaN to None # 2. `dumps` again so you get 'null' instead of extended JSON try: - new_o = json.loads(encoded_o, parse_constant=self.coerce_to_strict) + new_o = _json.loads(encoded_o, + parse_constant=self.coerce_to_strict) except ValueError: # invalid separators will fail here. raise a helpful exception @@ -158,10 +160,10 @@ def encode(self, o): "valid JSON separators?" ) else: - return json.dumps(new_o, sort_keys=self.sort_keys, - indent=self.indent, - separators=(self.item_separator, - self.key_separator)) + return _json.dumps(new_o, sort_keys=self.sort_keys, + indent=self.indent, + separators=(self.item_separator, + self.key_separator)) def default(self, obj): """ @@ -210,7 +212,7 @@ def default(self, obj): return encoding_method(obj) except NotEncodable: pass - return json.JSONEncoder.default(self, obj) + return _json.JSONEncoder.default(self, obj) @staticmethod def encode_as_plotly(obj): diff --git a/plotly/widgets/graph_widget.py b/plotly/widgets/graph_widget.py index a359474a7d..f7eb0a8608 100644 --- a/plotly/widgets/graph_widget.py +++ b/plotly/widgets/graph_widget.py @@ -2,11 +2,11 @@ Module to allow Plotly graphs to interact with IPython widgets. """ -import json import uuid from collections import deque from pkg_resources import resource_string +from requests.compat import json as _json # TODO: protected imports? from IPython.html import widgets @@ -93,7 +93,7 @@ def _handle_msg(self, message): while self._clientMessages: _message = self._clientMessages.popleft() _message['graphId'] = self._graphId - _message = json.dumps(_message) + _message = _json.dumps(_message) self._message = _message if content.get('event', '') in ['click', 'hover', 'zoom']: @@ -131,7 +131,7 @@ def _handle_outgoing_message(self, message): else: message['graphId'] = self._graphId message['uid'] = str(uuid.uuid4()) - self._message = json.dumps(message, cls=utils.PlotlyJSONEncoder) + self._message = _json.dumps(message, cls=utils.PlotlyJSONEncoder) def on_click(self, callback, remove=False): """ Assign a callback to click events propagated