diff --git a/.circleci/config.yml b/.circleci/config.yml index fae56c4ceae..fff2cbe1999 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -103,7 +103,7 @@ jobs: name: Build html figures command: | . venv/bin/activate - pip install pandas statsmodels --quiet + pip install pandas statsmodels orjson --quiet python test/percy/plotly-express.py - run: name: Run percy snapshots diff --git a/packages/python/plotly/plotly/basedatatypes.py b/packages/python/plotly/plotly/basedatatypes.py index 038b5ded5cd..e4ab9f4ad44 100644 --- a/packages/python/plotly/plotly/basedatatypes.py +++ b/packages/python/plotly/plotly/basedatatypes.py @@ -916,6 +916,12 @@ def update(self, dict1=None, overwrite=False, **kwargs): BaseFigure._perform_update(self[k], v) else: self[k] = v + + # # instrumentation + # from plotly.io._json import to_json_plotly + # + # to_json_plotly(self) + return self def pop(self, key, *args): diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index fe362c7e1a1..1bc46e96f33 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -2092,6 +2092,12 @@ def make_figure(args, constructor, trace_patch=None, layout_patch=None): configure_axes(args, constructor, fig, orders) configure_animation_controls(args, constructor, fig) + + # instrumentation + # from plotly.io._json import to_json_plotly + # + # to_json_plotly(fig) + return fig diff --git a/packages/python/plotly/plotly/io/_json.py b/packages/python/plotly/plotly/io/_json.py index 3bda778c76e..0bca132bbc3 100644 --- a/packages/python/plotly/plotly/io/_json.py +++ b/packages/python/plotly/plotly/io/_json.py @@ -57,7 +57,78 @@ def coerce_to_strict(const): return const +def time_engine(engine, plotly_object): + import time + # time in seconds + t_total = 0 + n_total = 0 + + # Call function for at least total of 2 seconds and at least 10 times + n_min = 10 + t_min = 1 + + while t_total < t_min or n_total < n_min: + t0 = time.perf_counter() + _to_json_plotly(plotly_object, engine=engine) + t1 = time.perf_counter() + n_total += 1 + t_total += (t1 - t0) + + # return time in ms + return 1000 * t_total / n_total + + def to_json_plotly(plotly_object, pretty=False, engine=None): + if engine is not None: + return _to_json_plotly(plotly_object, pretty=pretty , engine=engine) + + # instrucment _to_json_plotly by running it with all 3 engines and comparing results + # before returnin + + import timeit + from IPython import get_ipython + ipython = get_ipython() + orjson = get_module("orjson", should_load=True) + results = {} + timing = {} + result_str = None + for engine in ["json", "orjson"]: + if orjson is None and engine == "orjson": + continue + + result_str = _to_json_plotly(plotly_object, pretty=pretty, engine=engine) + results[engine] = from_json_plotly(result_str, engine=engine) + timing[engine] = time_engine(engine, plotly_object) + + # Check matches + if "orjson" in results: + if results["json"] != results["orjson"]: + raise ValueError( + """ + {json} + + {orjson}""".format( + json=results["json"], orjson=results["orjson"] + ) + ) + + # write timing + import uuid + import pickle + import os + uid = str(uuid.uuid4()) + with open("json_timing.csv".format(engine), "at") as f: + f.write("{}, {}, {}, {}\n".format( + timing["json"], timing["orjson"], len(result_str), uid) + ) + os.makedirs("json_object", exist_ok=True) + with open("json_object/{uid}.pkl".format(uid=uid), "wb") as f: + pickle.dump(plotly_object, f) + + return result_str + + +def _to_json_plotly(plotly_object, pretty=False, engine=None): """ Convert a plotly/Dash object to a JSON string representation @@ -466,7 +537,7 @@ def clean_to_json_compatible(obj, **kwargs): elif obj.dtype.kind == "U": return obj.tolist() elif obj.dtype.kind == "O": - # Treat object array as a lists, continue processing + # Treat object array as plain list, allow recursive processing below obj = obj.tolist() elif isinstance(obj, np.datetime64): return str(obj) diff --git a/packages/python/plotly/tox.ini b/packages/python/plotly/tox.ini index 5b4a8345545..ca9ec6a9676 100644 --- a/packages/python/plotly/tox.ini +++ b/packages/python/plotly/tox.ini @@ -75,7 +75,7 @@ deps= optional: matplotlib==2.2.3 optional: scikit-image==0.14.4 optional: kaleido - optional: orjson==3.4.6;python_version>"3.5" + orjson==3.4.6;python_version>"3.5" ; CORE ENVIRONMENTS [testenv:py27-core]