Skip to content
Merged
Show file tree
Hide file tree
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
90 changes: 1 addition & 89 deletions packages/python/plotly/_plotly_utils/basevalidators.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,83 +51,6 @@ def to_scalar_or_list(v):
return v


plotlyjsShortTypes = {
"int8": "i1",
"uint8": "u1",
"int16": "i2",
"uint16": "u2",
"int32": "i4",
"uint32": "u4",
"float32": "f4",
"float64": "f8",
}

int8min = -128
int8max = 127
int16min = -32768
int16max = 32767
int32min = -2147483648
int32max = 2147483647

uint8max = 255
uint16max = 65535
uint32max = 4294967295


def to_typed_array_spec(v):
"""
Convert numpy array to plotly.js typed array spec
If not possible return the original value
"""
v = copy_to_readonly_numpy_array(v)

np = get_module("numpy", should_load=False)
if not isinstance(v, np.ndarray):
return v

dtype = str(v.dtype)

# convert default Big Ints until we could support them in plotly.js
if dtype == "int64":
max = v.max()
min = v.min()
if max <= int8max and min >= int8min:
v = v.astype("int8")
elif max <= int16max and min >= int16min:
v = v.astype("int16")
elif max <= int32max and min >= int32min:
v = v.astype("int32")
else:
return v

elif dtype == "uint64":
max = v.max()
min = v.min()
if max <= uint8max and min >= 0:
v = v.astype("uint8")
elif max <= uint16max and min >= 0:
v = v.astype("uint16")
elif max <= uint32max and min >= 0:
v = v.astype("uint32")
else:
return v

dtype = str(v.dtype)

if dtype in plotlyjsShortTypes:
arrObj = {
"dtype": plotlyjsShortTypes[dtype],
"bdata": base64.b64encode(v).decode("ascii"),
}

if v.ndim > 1:
arrObj["shape"] = str(v.shape)[1:-1]

return arrObj

return v


def copy_to_readonly_numpy_array(v, kind=None, force_numeric=False):
"""
Convert an array-like value into a read-only numpy array
Expand Down Expand Up @@ -292,15 +215,6 @@ def is_typed_array_spec(v):
return isinstance(v, dict) and "bdata" in v and "dtype" in v


def has_skipped_key(all_parent_keys):
"""
Return whether any keys in the parent hierarchy are in the list of keys that
are skipped for conversion to the typed array spec
"""
skipped_keys = ["geojson", "layer", "range"]
return any(skipped_key in all_parent_keys for skipped_key in skipped_keys)


def is_none_or_typed_array_spec(v):
return v is None or is_typed_array_spec(v)

Expand Down Expand Up @@ -500,10 +414,8 @@ def description(self):
def validate_coerce(self, v):
if is_none_or_typed_array_spec(v):
pass
elif has_skipped_key(self.parent_name):
v = to_scalar_or_list(v)
elif is_homogeneous_array(v):
v = to_typed_array_spec(v)
v = copy_to_readonly_numpy_array(v)
elif is_simple_array(v):
v = to_scalar_or_list(v)
else:
Expand Down
107 changes: 106 additions & 1 deletion packages/python/plotly/_plotly_utils/utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,116 @@
import base64
import decimal
import json as _json
import sys
import re
from functools import reduce

from _plotly_utils.optional_imports import get_module
from _plotly_utils.basevalidators import ImageUriValidator
from _plotly_utils.basevalidators import (
ImageUriValidator,
copy_to_readonly_numpy_array,
is_homogeneous_array,
)


int8min = -128
int8max = 127
int16min = -32768
int16max = 32767
int32min = -2147483648
int32max = 2147483647

uint8max = 255
uint16max = 65535
uint32max = 4294967295

plotlyjsShortTypes = {
"int8": "i1",
"uint8": "u1",
"int16": "i2",
"uint16": "u2",
"int32": "i4",
"uint32": "u4",
"float32": "f4",
"float64": "f8",
}


def to_typed_array_spec(v):
"""
Convert numpy array to plotly.js typed array spec
If not possible return the original value
"""
v = copy_to_readonly_numpy_array(v)

np = get_module("numpy", should_load=False)
if not isinstance(v, np.ndarray):
return v

dtype = str(v.dtype)

# convert default Big Ints until we could support them in plotly.js
if dtype == "int64":
max = v.max()
min = v.min()
if max <= int8max and min >= int8min:
v = v.astype("int8")
elif max <= int16max and min >= int16min:
v = v.astype("int16")
elif max <= int32max and min >= int32min:
v = v.astype("int32")
else:
return v

elif dtype == "uint64":
max = v.max()
min = v.min()
if max <= uint8max and min >= 0:
v = v.astype("uint8")
elif max <= uint16max and min >= 0:
v = v.astype("uint16")
elif max <= uint32max and min >= 0:
v = v.astype("uint32")
else:
return v

dtype = str(v.dtype)

if dtype in plotlyjsShortTypes:
arrObj = {
"dtype": plotlyjsShortTypes[dtype],
"bdata": base64.b64encode(v).decode("ascii"),
}

if v.ndim > 1:
arrObj["shape"] = str(v.shape)[1:-1]

return arrObj

return v


def is_skipped_key(key):
"""
Return whether any keys in the parent hierarchy are in the list of keys that
are skipped for conversion to the typed array spec
"""
skipped_keys = ["geojson", "layer", "range"]
return any(skipped_key in key for skipped_key in skipped_keys)


def convert_to_base64(obj):
if isinstance(obj, dict):
for key, value in obj.items():
if is_skipped_key(key):
continue
elif is_homogeneous_array(value):
obj[key] = to_typed_array_spec(value)
else:
convert_to_base64(value)
elif isinstance(obj, list) or isinstance(obj, tuple):
for i, value in enumerate(obj):
convert_to_base64(value)


def cumsum(x):
Expand Down
4 changes: 4 additions & 0 deletions packages/python/plotly/plotly/basedatatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
display_string_positions,
chomp_empty_strings,
find_closest_string,
convert_to_base64,
)
from _plotly_utils.exceptions import PlotlyKeyError
from .optional_imports import get_module
Expand Down Expand Up @@ -3310,6 +3311,9 @@ def to_dict(self):
if frames:
res["frames"] = frames

# Add base64 conversion before sending to the front-end
convert_to_base64(res)

return res

def to_plotly_json(self):
Expand Down
1 change: 1 addition & 0 deletions packages/python/plotly/plotly/io/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def validate_coerce_fig_to_dict(fig, validate):
typ=type(fig), v=fig
)
)

return fig_dict


Expand Down