Skip to content

copied PlotlyJSONEncode class to play around #626

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

Closed
wants to merge 1 commit into from
Closed
Changes from all 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
191 changes: 190 additions & 1 deletion plotly/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class PlotlyJSONEncoder(json.JSONEncoder):
See PlotlyJSONEncoder.default for more implementation information.

Additionally, this encoder overrides nan functionality so that 'Inf',
'NaN' and '-Inf' encode to 'null'. Which is stricter JSON than the Python
'NaN' and '-Inf' encode to 'null', which is stricter JSON than the Python
version.

"""
Expand All @@ -140,7 +140,196 @@ def encode(self, o):
Note that setting invalid separators will cause a failure at this step.

"""
encoded_o =

# this will raise errors in a normal-expected way
encoded_o = super(PlotlyJSONEncoder, self).encode(o)
print encoded_o

# now:
# 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)
print new_o
except ValueError:

# invalid separators will fail here. raise a helpful exception
raise ValueError(
"Encoding into strict JSON failed. Did you set the separators "
"valid JSON separators?"
)
else:
print 'else'
return json.dumps(new_o, sort_keys=self.sort_keys,
indent=self.indent,
separators=(self.item_separator,
self.key_separator))

def default(self, obj):
"""
Accept an object (of unknown type) and try to encode with priority:
1. builtin: user-defined objects
2. sage: sage math cloud
3. pandas: dataframes/series
4. numpy: ndarrays
5. datetime: time/datetime objects

Each method throws a NotEncoded exception if it fails.

The default method will only get hit if the object is not a type that
is naturally encoded by json:

Normal objects:
dict object
list, tuple array
str, unicode string
int, long, float number
True true
False false
None null

Extended objects:
float('nan') 'NaN'
float('infinity') 'Infinity'
float('-infinity') '-Infinity'

Therefore, we only anticipate either unknown iterables or values here.

"""
# TODO: The ordering if these methods is *very* important. Is this OK?
encoding_methods = (
self.encode_as_plotly,
self.encode_as_sage,
self.encode_as_numpy,
self.encode_as_pandas,
self.encode_as_datetime,
self.encode_as_date,
self.encode_as_list # because some values have `tolist` do last.
)
for encoding_method in encoding_methods:
try:
return encoding_method(obj)
except NotEncodable:
pass
return json.JSONEncoder.default(self, obj)

@staticmethod
def encode_as_plotly(obj):
"""Attempt to use a builtin `to_plotly_json` method."""
try:
return obj.to_plotly_json()
except AttributeError:
raise NotEncodable

@staticmethod
def encode_as_list(obj):
"""Attempt to use `tolist` method to convert to normal Python list."""
if hasattr(obj, 'tolist'):
return obj.tolist()
else:
raise NotEncodable

@staticmethod
def encode_as_sage(obj):
"""Attempt to convert sage.all.RR to floats and sage.all.ZZ to ints"""
if not _sage_imported:
raise NotEncodable

if obj in sage.all.RR:
return float(obj)
elif obj in sage.all.ZZ:
return int(obj)
else:
raise NotEncodable

@staticmethod
def encode_as_pandas(obj):
"""Attempt to convert pandas.NaT"""
if not _pandas_imported:
raise NotEncodable

if obj is pandas.NaT:
return None
else:
raise NotEncodable

@staticmethod
def encode_as_numpy(obj):
"""Attempt to convert numpy.ma.core.masked"""
if not _numpy_imported:
raise NotEncodable

if obj is numpy.ma.core.masked:
return float('nan')
else:
raise NotEncodable

@staticmethod
def encode_as_datetime(obj):
"""Attempt to convert to utc-iso time string using datetime methods."""

# first we need to get this into utc
try:
obj = obj.astimezone(pytz.utc)
except ValueError:
# we'll get a value error if trying to convert with naive datetime
pass
except TypeError:
# pandas throws a typeerror here instead of a value error, it's OK
pass
except AttributeError:
# we'll get an attribute error if astimezone DNE
raise NotEncodable

# now we need to get a nicely formatted time string
try:
time_string = obj.isoformat()
except AttributeError:
raise NotEncodable
else:
return iso_to_plotly_time_string(time_string)

@staticmethod
def encode_as_date(obj):
"""Attempt to convert to utc-iso time string using date methods."""
try:
time_string = obj.isoformat()
except AttributeError:
raise NotEncodable
else:
return iso_to_plotly_time_string(time_string)


class OLDPlotlyJSONEncoder(json.JSONEncoder):
"""
Meant to be passed as the `cls` kwarg to json.dumps(obj, cls=..)

See PlotlyJSONEncoder.default for more implementation information.

Additionally, this encoder overrides nan functionality so that 'Inf',
'NaN' and '-Inf' encode to 'null', which is stricter JSON than the Python
version.

"""
def coerce_to_strict(self, const):
"""
This is used to ultimately *encode* into strict JSON, see `encode`

"""
# before python 2.7, 'true', 'false', 'null', were include here.
if const in ('Infinity', '-Infinity', 'NaN'):
return None
else:
return const

def encode(self, o):
"""
Load and then dump the result using parse_constant kwarg

Note that setting invalid separators will cause a failure at this step.

"""
# this will raise errors in a normal-expected way
encoded_o = super(PlotlyJSONEncoder, self).encode(o)

Expand Down