Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
370c5fe
feat: basic serializer implemented for TreeContainer
Sep 20, 2021
37ca266
fix: added check for serializable component create component
Sep 21, 2021
df3e29d
feat: deserialize values in renderer side
Sep 22, 2021
a376f13
wip: quick pseudo codes
Sep 22, 2021
9580fcb
chore: working on descriptive comments
Sep 22, 2021
e0ac4c6
feat: implemented setting props in treeContainer
Sep 22, 2021
4df589a
feat: implemented pseudo code to work
Sep 23, 2021
61bd86e
feat implemented serializedKeys for component
Sep 24, 2021
c72691b
feat: serialization basically done up to milestone 4
Sep 28, 2021
c8bd9c9
chore: some code reset to clean unnecessary changes
Sep 28, 2021
6739a70
Merge branch 'dev' of github.com:plotly/dash into two-way-serialization
Sep 28, 2021
3fb7766
fix: lint fix
Sep 28, 2021
2ab2fe9
chore: add pandas to requirement
Sep 28, 2021
fca42b9
update serialization
Oct 7, 2021
b64688a
Merge branch 'two-way-serialization' of github.com:workaholicpanda/da…
Oct 7, 2021
3d22a35
feat: (wip) add to_parquet serialization
Oct 8, 2021
1fc29c7
chore: add some todo comments
Oct 8, 2021
ba5192a
feat: (wip) implemented parquet reader & writer in JS side
Oct 12, 2021
b2caa71
Merge branch 'dev' of github.com:plotly/dash into two-way-serialization
Oct 12, 2021
d147eed
feat: (wip) add 3 different dataframe formats
Oct 15, 2021
810ee27
fix: avoid try/except as much as possible
Oct 15, 2021
592641f
fix: remove PROP_ID, use tempfile for parquet output
Oct 15, 2021
333e3a9
Set up parquetjs-lite (#8)
akksi Oct 18, 2021
77cec9e
feat: clean up codes and prepare for PR review
Oct 19, 2021
6296a5e
fix: bug fix
Oct 19, 2021
8d43b8c
Merge branch 'dev' of github.com:plotly/dash into two-way-serialization
Oct 19, 2021
51e6cba
feat: changed serialization applying
Oct 20, 2021
1000d5a
chore: minor refactor & clean up
Oct 20, 2021
0d64649
chore: lint fixes
Oct 20, 2021
1ccf6db
feat: add `columns` to serialized dataframe
Oct 21, 2021
6a22b80
fix: deserialize bug
Oct 21, 2021
1dd0769
fix: lint
Oct 21, 2021
6382620
Two way serialization reading parquets (#9)
workaholicAB Oct 27, 2021
74a14bc
fix: parquets reader
Oct 28, 2021
97f760b
fix: async deserialization
Oct 28, 2021
dec2bfb
fix: make serialize value async
Oct 28, 2021
24c8a42
fixed asyncronize issue in renderer side
Nov 1, 2021
54fa534
changed file open to with
Nov 1, 2021
e6ee69a
fix: use to_dict for now
Nov 2, 2021
9d9a7c6
Merge branch 'dev' of github.com:plotly/dash into two-way-serialization
Nov 2, 2021
9d072ab
updated package-lock files
Nov 2, 2021
f16df3d
just trying
Nov 2, 2021
30f03e2
Revert "just trying"
Nov 2, 2021
1b57555
Revert "updated package-lock files"
Nov 2, 2021
3d47069
trying to disable serialize
Nov 3, 2021
66a3733
formatted code style to fix testing issue
Nov 16, 2021
f820ef3
Merge branch 'dev' of github.com:plotly/dash into two-way-serialization
Nov 17, 2021
889a578
update renderer package-lock.json
Nov 17, 2021
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
15 changes: 10 additions & 5 deletions components/dash-html-components/tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ def test_click_simple(dash_duo):
[
html.Div(id="container"),
html.Button("Click", id="button", n_clicks=0),
html.Iframe(id="video", allow="fullscreen", referrerPolicy="origin"),
html.Iframe(id="video", allow="fullscreen",
referrerPolicy="origin"),
]
)

Expand All @@ -37,8 +38,10 @@ def update_output(n_clicks):

assert not dash_duo.get_logs()

assert dash_duo.find_element("#video").get_attribute("allow") == "fullscreen"
assert dash_duo.find_element("#video").get_attribute("referrerpolicy") == "origin"
assert dash_duo.find_element(
"#video").get_attribute("allow") == "fullscreen"
assert dash_duo.find_element("#video").get_attribute(
"referrerpolicy") == "origin"


def test_click_prev(dash_duo):
Expand All @@ -50,8 +53,10 @@ def test_click_prev(dash_duo):
app.layout = html.Div(
[
html.Div(id="container"),
html.Button("Click", id="button-1", n_clicks=0, n_clicks_timestamp=-1),
html.Button("Click", id="button-2", n_clicks=0, n_clicks_timestamp=-1),
html.Button("Click", id="button-1",
n_clicks=0, n_clicks_timestamp=-1),
html.Button("Click", id="button-2",
n_clicks=0, n_clicks_timestamp=-1),
]
)

Expand Down
5 changes: 4 additions & 1 deletion dash/_callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
stringify_id,
to_json,
)
from ._dash_serializer import DashSerializer

from . import _validate

Expand Down Expand Up @@ -180,7 +181,9 @@ def add_context(*args, **kwargs):
if not isinstance(vali, NoUpdate):
has_update = True
id_str = stringify_id(speci["id"])
component_ids[id_str][speci["property"]] = vali
component_ids[id_str][
speci["property"]
] = DashSerializer.serialize_value(vali)

if not has_update:
raise PreventUpdate
Expand Down
139 changes: 139 additions & 0 deletions dash/_dash_serializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import base64
import json
from typing import cast

from os import remove
import io
import tempfile
import pandas as pd
from pandas.core.frame import DataFrame
import pyarrow as pa

PROP_TYPE = "__type"
PROP_VALUE = "__value"
PROP_ENGINE = "__engine"


class DataFrameSerializer:
@classmethod
def __serialize_using_fastparquet(cls, df):
with tempfile.NamedTemporaryFile("w") as temp_file:
outputPath = temp_file.name

df.to_parquet(outputPath, compression="gzip", engine="fastparquet")
with open(outputPath, "rb") as f:
buffer_val = f.read()
f.close()
remove(outputPath)
return base64.b64encode(buffer_val).decode("utf-8")

@classmethod
def pyarrow_table_to_bytes(cls, table: pa.Table) -> bytes:
sink = pa.BufferOutputStream()
writer = pa.RecordBatchStreamWriter(sink, table.schema)
writer.write_table(table)
writer.close()
byteData = cast(bytes, sink.getvalue().to_pybytes())
return base64.b64encode(byteData).decode("utf-8")

@classmethod
def __serialize_using_pyarrow(cls, df, useFile=False, useParquetFormat=False):
if not useParquetFormat:
table = pa.Table.from_pandas(df)
return DataFrameSerializer.pyarrow_table_to_bytes(table)

if useFile:
with tempfile.NamedTemporaryFile("w") as temp_file:
outputPath = temp_file.name

df.to_parquet(outputPath, compression="gzip", engine="pyarrow")
with open(outputPath, "rb") as f:
buffer_val = f.read()
f.close()
remove(outputPath)
return base64.b64encode(buffer_val).decode("utf-8")

ret_buffer = df.to_parquet(compression="gzip", engine="pyarrow")
return base64.b64encode(ret_buffer).decode("utf-8")

@classmethod
def __serialize_using_to_dict(cls, df):
return {"records": df.to_dict("records"), "columns": df.columns}

@classmethod
def serialize(cls, prop, engine="to_dict"):
supportedEngines = {
"fastparquet": DataFrameSerializer.__serialize_using_fastparquet,
"pyarrow": DataFrameSerializer.__serialize_using_pyarrow,
"to_dict": DataFrameSerializer.__serialize_using_to_dict,
}
return {
PROP_TYPE: "pd.DataFrame",
PROP_VALUE: supportedEngines[engine](prop),
PROP_ENGINE: engine,
}

@classmethod
def deserialize(cls, prop):
[engine, value] = [
prop[PROP_ENGINE],
prop[PROP_VALUE],
]
if engine == "to_dict":
return DataFrame.from_records(value["records"], columns=value["columns"])
return pd.read_parquet(io.BytesIO(value), engine)


class DashSerializer:
@classmethod
def serialize_value(cls, value):
if isinstance(value, pd.DataFrame):
# return serializer.serialize(prop, engine="pyarrow")
return DataFrameSerializer.serialize(value, engine="to_dict")
# return DataFrameSerializer.serialize(value, engine="fastparquet")
return value

@classmethod
def serialize_prop(cls, component, propName):
serializeFn = getattr(component, "serialize", None)
if serializeFn:
return serializeFn()

propValue = getattr(component, propName)
if isinstance(propValue, pd.DataFrame):
# return serializer.serialize(prop, engine="pyarrow")
return DataFrameSerializer.serialize(propValue, engine="to_dict")
# return DataFrameSerializer.serialize(propValue, engine="fastparquet")
return propValue

@classmethod
def deserialize_value(cls, prop):
deserializeFn = getattr(prop, "deserialize", None)
if deserializeFn:
return deserializeFn()

try:
jsonObj = json.loads(prop) if isinstance(prop, str) else prop
_type = (
jsonObj[PROP_TYPE]
if isinstance(jsonObj, dict) and PROP_TYPE in jsonObj
else None
)
if _type == "pd.DataFrame":
return DataFrameSerializer.deserialize(jsonObj)
except ValueError:
pass

return prop

@classmethod
def deserialize(cls, obj):
props = obj if isinstance(obj, list) else [obj]
return [
(
{**prop, "value": DashSerializer.deserialize_value(prop["value"])}
if "value" in prop
else prop
)
for prop in props
]
Loading