Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add to/from/read/write json functions to the plotly.io module #1188

Merged
merged 6 commits into from
Sep 21, 2018
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions optional-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ coverage==4.3.1
mock==2.0.0
nose==1.3.3
pytest==3.5.1
backports.tempfile==1.0

## orca ##
psutil
Expand Down
2 changes: 2 additions & 0 deletions plotly/io/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
from ._orca import to_image, write_image
from . import orca

from ._json import to_json, from_json, read_json, write_json
193 changes: 193 additions & 0 deletions plotly/io/_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
from six import string_types
import json

from plotly.utils import PlotlyJSONEncoder
from plotly.io._utils import (validate_coerce_fig_to_dict,
validate_coerce_output_type)


def to_json(fig,
validate=True,
pretty=False,
remove_uids=True):
"""
Convert a figure to a JSON string representation

Parameters
----------
fig:
Figure object or dict representing a figure

validate: bool (default True)
True if the figure should be validated before being converted to
JSON, False otherwise.

pretty: bool (default False)
True if JSON representation should be pretty-printed, False if
representation should be as compact as possible.

remove_uids: bool (default True)
True if trace UIDs should be omitted from the JSON representation

Returns
-------
str
Representation of figure as a JSON string
"""
# Validate figure
# ---------------
fig_dict = validate_coerce_fig_to_dict(fig, validate)

# Remove trace uid
# ----------------
if remove_uids:
for trace in fig_dict.get('data', []):
trace.pop('uid')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one of my traces was a Heatmap... which does not have a pop method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, did you set validate=False? If not, then validate_coerce_fig_to_dict() should have made sure that the traces were converted into dicts.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up using validate=False and remove_uids=False


# Dump to a JSON string and return
# --------------------------------
opts = {'sort_keys': True}
if pretty:
opts['indent'] = 2
else:
# Remove all whitespace
opts['separators'] = (',', ':')

return json.dumps(fig_dict, cls=PlotlyJSONEncoder, **opts)


def write_json(fig, file, validate=True, pretty=False, remove_uids=True):
"""
Convert a figure to JSON and write it to a file or writeable
object

Parameters
----------
fig:
Figure object or dict representing a figure

file: str or writeable
A string representing a local file path or a writeable object
(e.g. an open file descriptor)

pretty: bool (default False)
True if JSON representation should be pretty-printed, False if
representation should be as compact as possible.

remove_uids: bool (default True)
True if trace UIDs should be omitted from the JSON representation

Returns
-------
None
"""

# Get JSON string
# ---------------
# Pass through validate argument and let to_json handle validation logic
json_str = to_json(
fig, validate=validate, pretty=pretty, remove_uids=remove_uids)

# Check if file is a string
# -------------------------
file_is_str = isinstance(file, string_types)

# Open file
# ---------
if file_is_str:
with open(file, 'w') as f:
f.write(json_str)
else:
file.write(json_str)


def from_json(value, output_type='Figure', skip_invalid=False):
"""
Construct a figure from a JSON string

Parameters
----------
value: str
String containing the JSON representation of a figure
output_type: type or str (default 'Figure')
The output figure type or type name.
One of: graph_objs.Figure, 'Figure,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing quote character

graph_objs.FigureWidget, 'FigureWidget'
skip_invalid: bool (default False)
False if invalid figure properties should result in an exception
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and "True if invalid figure properties should be silently ignored."


Raises
------
ValueError
if value is not a string, or if skip_invalid=False and value contains
invalid figure properties

Returns
-------
Figure or FigureWidget
"""

# Validate value
# --------------
if not isinstance(value, string_types):
raise ValueError("""
from_json requires a string argument but received value of type {typ}
Received value: {value}""".format(typ=type(value),
value=value))

# Decode JSON
# -----------
fig_dict = json.loads(value)

# Validate coerce output type
# ---------------------------
cls = validate_coerce_output_type(output_type)

# Create and return figure
# ------------------------
fig = cls(fig_dict, skip_invalid=skip_invalid)
return fig


def read_json(file, output_type='Figure', skip_invalid=False):
"""
Construct a figure from the JSON contents of a local file or readable
Python object

Parameters
----------
file: str or readable
A string containing the path to a local file or a read-able Python
object (e.g. an open file descriptor)

output_type: type or str (default 'Figure')
The output figure type or type name.
One of: graph_objs.Figure, 'Figure,
graph_objs.FigureWidget, 'FigureWidget'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add newline

skip_invalid: bool (default False)
False if invalid figure properties should result in an exception
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update description as above


Returns
-------
Figure or FigureWidget
"""

# Check if file is a string
# -------------------------
# If it's a string we assume it's a local file path. If it's not a string
# then we assume it's a read-able Python object
file_is_str = isinstance(file, string_types)

# Read file contents into JSON string
# -----------------------------------
if file_is_str:
with open(file, 'r') as f:
json_str = f.read()
else:
json_str = file.read()

# Construct and return figure
# ---------------------------
return from_json(json_str,
skip_invalid=skip_invalid,
output_type=output_type)
15 changes: 2 additions & 13 deletions plotly/io/_orca.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
from six import string_types

import plotly
from plotly.basedatatypes import BaseFigure
from plotly.files import PLOTLY_DIR
from plotly.io._utils import validate_coerce_fig_to_dict
from plotly.optional_imports import get_module

psutil = get_module('psutil')
Expand Down Expand Up @@ -1281,18 +1281,7 @@ def to_image(fig,

# Validate figure
# ---------------
if isinstance(fig, BaseFigure):
fig_dict = fig.to_plotly_json()
elif isinstance(fig, dict):
if validate:
# This will raise an exception if fig is not a valid plotly figure
fig_dict = plotly.graph_objs.Figure(fig).to_plotly_json()
else:
fig_dict = fig
else:
raise ValueError("""
The fig parameter must be a dict or Figure.
Received value of type {typ}: {v}""".format(typ=type(fig), v=fig))
fig_dict = validate_coerce_fig_to_dict(fig, validate)

# Request image from server
# -------------------------
Expand Down
32 changes: 32 additions & 0 deletions plotly/io/_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import plotly
from plotly.basedatatypes import BaseFigure
import plotly.graph_objs as go


def validate_coerce_fig_to_dict(fig, validate):
if isinstance(fig, BaseFigure):
fig_dict = fig.to_dict()
elif isinstance(fig, dict):
if validate:
# This will raise an exception if fig is not a valid plotly figure
fig_dict = plotly.graph_objs.Figure(fig).to_plotly_json()
else:
fig_dict = fig
else:
raise ValueError("""
The fig parameter must be a dict or Figure.
Received value of type {typ}: {v}""".format(typ=type(fig), v=fig))
return fig_dict


def validate_coerce_output_type(output_type):
if output_type == 'Figure' or output_type == go.Figure:
cls = go.Figure
elif (output_type == 'FigureWidget' or
(hasattr(go, 'FigureWidget') and output_type == go.FigureWidget)):
cls = go.FigureWidget
else:
raise ValueError("""
Invalid output type: {output_type}
Must be one of: 'Figure', 'FigureWidget'""")
return cls
Loading