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

added base64 #415

Merged
merged 14 commits into from
Jun 13, 2024
11 changes: 10 additions & 1 deletion mesop/dataclass_utils/dataclass_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
from deepdiff.path import parse_path

from mesop.exceptions import MesopException
import base64 # Import base64 for encoding
richard-to marked this conversation as resolved.
Show resolved Hide resolved

_PANDAS_OBJECT_KEY = "__pandas.DataFrame__"
_BYTES_OBJECT_KEY = "__python.bytes__" # Add new key for bytes
richard-to marked this conversation as resolved.
Show resolved Hide resolved
_DIFF_ACTION_DATA_FRAME_CHANGED = "data_frame_changed"

C = TypeVar("C")
Expand Down Expand Up @@ -125,6 +127,9 @@ def default(self, obj):
except ImportError:
pass

if isinstance(obj, bytes):
return {_BYTES_OBJECT_KEY: base64.b64encode(obj).decode('utf-8')} # Encode bytes
richard-to marked this conversation as resolved.
Show resolved Hide resolved

if is_dataclass(obj):
return asdict(obj)

Expand All @@ -149,6 +154,10 @@ def decode_mesop_json_state_hook(dct):

if _PANDAS_OBJECT_KEY in dct:
return pd.read_json(StringIO(dct[_PANDAS_OBJECT_KEY]), orient="table")

if _BYTES_OBJECT_KEY in dct:
return base64.b64decode(dct[_BYTES_OBJECT_KEY]) # Decode bytes
richard-to marked this conversation as resolved.
Show resolved Hide resolved

return dct


Expand Down Expand Up @@ -218,4 +227,4 @@ def diff_state(state1: Any, state2: Any) -> str:
Delta(differences, always_include_values=True).to_flat_dicts()
+ custom_actions,
cls=MesopJSONEncoder,
)
)
27 changes: 27 additions & 0 deletions mesop/dataclass_utils/dataclass_utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ class WithPandasDataFrame:
val: pd.DataFrame | None = None


@dataclass
class WithBytes:
data: bytes = b""


JSON_STR = """{"b": {"c": {"val": "<init>"}},
"list_b": [
{"c": {"val": "1"}},
Expand Down Expand Up @@ -128,6 +133,20 @@ def test_serialize_pandas_dataframe():
)


@pytest.mark.parametrize(
"input_bytes, expected_json",
[
(b"hello world", '{"data": {"__python.bytes__": "aGVsbG8gd29ybGQ="}}'),
(b"", '{"data": {"__python.bytes__": ""}}'),
(b"unicode \xe2\x9c\x93", '{"data": {"__python.bytes__": "dW5pY29kZSDijJM="}}'),
],
)

def test_serialize_bytes(input_bytes, expected_json):
serialized_dataclass = serialize_dataclass(WithBytes(data=input_bytes))
assert serialized_dataclass == expected_json


def test_update_dataclass_from_json_nested_dataclass():
b = B()
update_dataclass_from_json(b, """{"c": {"val": "<init>"}}""")
Expand Down Expand Up @@ -191,5 +210,13 @@ def test_update_dataclass_with_pandas_dataframe():
)


def test_update_dataclass_with_bytes():
bytes_data = b"hello world"
serialized_dataclass = serialize_dataclass(WithBytes(data=bytes_data))
bytes_state = WithBytes()
update_dataclass_from_json(bytes_state, serialized_dataclass)
assert bytes_state.data == bytes_data


if __name__ == "__main__":
raise SystemExit(pytest.main([__file__]))
17 changes: 17 additions & 0 deletions mesop/dataclass_utils/diff_state_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,23 @@ class C:
]


def test_diff_bytes():
@dataclass
class C:
val1: bytes = b"val1"

s1 = C()
s2 = C(val1=b"VAL1")

assert json.loads(diff_state(s1, s2)) == [
{
"path": ["val1"],
"action": "values_changed",
"value": {"__python.bytes__": "VkFMMS=="},
}
]


def test_diff_not_dataclass():
class C:
val1: set[int] = field(default_factory=lambda: {1, 2, 3})
Expand Down
Loading