reflex/tests/utils/test_serializers.py
Khaleel Al-Adhami a5c73ad8e5
Use old serializer system in LiteralVar (#3875)
* use serializer system

* add checks for unsupported operands

* and and or are now supported

* format

* remove unnecessary call to JSON

* put base before rest

* fix failing testcase

* add hinting to get static analysis to complain

* damn

* big changes

* get typeguard from extensions

* please darglint

* dangit darglint

* remove one from vars

* add without data and use it in plotly

* DARGLINT

* change format for special props

* add pyi

* delete instances of Var.create

* modify client state to work

* fixed so much

* remove every Var.create

* delete all basevar stuff

* checkpoint

* fix pyi

* get older python to work

* dangit darglint

* add simple fix to last failing testcase

* remove var name unwrapped and put client state on immutable var

* fix older python

* fox event issues

* change forms pyi

* make test less strict

* use rx state directly

* add typeignore to page_id

* implement foreach

* delete .web states folder silly

* update reflex chakra

* fix issue when on mount or on unmount is not set

* nuke Var

* run pyi

* import immutablevar in critical location

* delete unwrap vars

* bring back array ref

* fix style props in app

* /health endpoint for K8 Liveness and Readiness probes (#3855)

* Added API Endpoint

* Added API Endpoint

* Added Unit Tests

* Added Unit Tests

* main

* Apply suggestions from Code Review

* Fix Ruff Formatting

* Update Socket Events

* Async Functions

* Update find_replace (#3886)

* [REF-3592]Promote `rx.progress` from radix themes (#3878)

* Promote `rx.progress` from radix themes

* fix pyi

* add warning when accessing `rx._x.progress`

* Use correct flexgen backend URL (#3891)

* Remove demo template (#3888)

* gitignore .web (#3885)

* update overflowY in AUTO_HEIGHT_JS from hidden to scroll (#3882)

* Retain mutability inside `async with self` block (#3884)

When emitting a state update, restore `_self_mutable` to the value it had
previously so that `yield` in the middle of `async with self` does not result
in an immutable StateProxy.

Fix #3869

* Include child imports in markdown component_map (#3883)

If a component in the markdown component_map contains children components, use
`_get_all_imports` to recursively enumerate them.

Fix #3880

* [REF-3570] Remove deprecated REDIS_URL syntax (#3892)

* mixin computed vars should only be applied to highest level state (#3833)

* improve state hierarchy validation, drop old testing special case (#3894)

* fix var dependency dicts (#3842)

* Adding array to array pluck operation. (#3868)

* fix initial state without cv fallback (#3670)

* add fragment to foreach (#3877)

* Update docker-example (#3324)

* /health endpoint for K8 Liveness and Readiness probes (#3855)

* Added API Endpoint

* Added API Endpoint

* Added Unit Tests

* Added Unit Tests

* main

* Apply suggestions from Code Review

* Fix Ruff Formatting

* Update Socket Events

* Async Functions

* /health endpoint for K8 Liveness and Readiness probes (#3855)

* Added API Endpoint

* Added API Endpoint

* Added Unit Tests

* Added Unit Tests

* main

* Apply suggestions from Code Review

* Fix Ruff Formatting

* Update Socket Events

* Async Functions

* Merge branch 'main' into use-old-serializer-in-literalvar

* [REF-3570] Remove deprecated REDIS_URL syntax (#3892)

* /health endpoint for K8 Liveness and Readiness probes (#3855)

* Added API Endpoint

* Added API Endpoint

* Added Unit Tests

* Added Unit Tests

* main

* Apply suggestions from Code Review

* Fix Ruff Formatting

* Update Socket Events

* Async Functions

* [REF-3570] Remove deprecated REDIS_URL syntax (#3892)

* remove extra var

Co-authored-by: Masen Furer <m_github@0x26.net>

* resolve typo

* write better doc for var.create

* return var value when we know it's literal var

* fix unit test

* less bloat for ToOperations

* simplify ImmutableComputedVar.__get__ (#3902)

* simplify ImmutableComputedVar.__get__

* ruff it

---------

Co-authored-by: Samarth Bhadane <samarthbhadane119@gmail.com>
Co-authored-by: Elijah Ahianyo <elijahahianyo@gmail.com>
Co-authored-by: Masen Furer <m_github@0x26.net>
Co-authored-by: benedikt-bartscher <31854409+benedikt-bartscher@users.noreply.github.com>
Co-authored-by: Vishnu Deva <vishnu.deva12@gmail.com>
Co-authored-by: abulvenz <a.eismann@senbax.de>
2024-09-10 11:43:37 -07:00

230 lines
6.7 KiB
Python

import datetime
from enum import Enum
from pathlib import Path
from typing import Any, Dict, Type
import pytest
from reflex.base import Base
from reflex.components.core.colors import Color
from reflex.ivars.base import ImmutableVar, LiteralVar
from reflex.utils import serializers
@pytest.mark.parametrize(
"type_,expected",
[(str, True), (dict, True), (Dict[int, int], True), (Enum, True)],
)
def test_has_serializer(type_: Type, expected: bool):
"""Test that has_serializer returns the correct value.
Args:
type_: The type to check.
expected: The expected result.
"""
assert serializers.has_serializer(type_) == expected
@pytest.mark.parametrize(
"type_,expected",
[
(datetime.datetime, serializers.serialize_datetime),
(datetime.date, serializers.serialize_datetime),
(datetime.time, serializers.serialize_datetime),
(datetime.timedelta, serializers.serialize_datetime),
(Enum, serializers.serialize_enum),
],
)
def test_get_serializer(type_: Type, expected: serializers.Serializer):
"""Test that get_serializer returns the correct value.
Args:
type_: The type to check.
expected: The expected result.
"""
assert serializers.get_serializer(type_) == expected
def test_add_serializer():
"""Test that adding a serializer works."""
class Foo:
"""A test class."""
def __init__(self, name: str):
self.name = name
def serialize_foo(value: Foo) -> str:
"""Serialize an foo to a string.
Args:
value: The value to serialize.
Returns:
The serialized value.
"""
return value.name
# Initially there should be no serializer for int.
assert not serializers.has_serializer(Foo)
assert serializers.serialize(Foo("hi")) is None
# Register the serializer.
assert serializers.serializer(serialize_foo) == serialize_foo
# There should now be a serializer for int.
assert serializers.has_serializer(Foo)
assert serializers.get_serializer(Foo) == serialize_foo
assert serializers.serialize(Foo("hi")) == "hi"
# Remove the serializer.
serializers.SERIALIZERS.pop(Foo)
# LRU cache will still have the serializer, so we need to clear it.
assert serializers.has_serializer(Foo)
serializers.get_serializer.cache_clear()
assert not serializers.has_serializer(Foo)
class StrEnum(str, Enum):
"""An enum also inheriting from str."""
FOO = "foo"
BAR = "bar"
class TestEnum(Enum):
"""A lone enum class."""
FOO = "foo"
BAR = "bar"
class EnumWithPrefix(Enum):
"""An enum with a serializer adding a prefix."""
FOO = "foo"
BAR = "bar"
@serializers.serializer
def serialize_EnumWithPrefix(enum: EnumWithPrefix) -> str:
return "prefix_" + enum.value
class BaseSubclass(Base):
"""A class inheriting from Base for testing."""
ts: datetime.timedelta = datetime.timedelta(1, 1, 1)
@pytest.mark.parametrize(
"value,expected",
[
("test", "test"),
(1, "1"),
(1.0, "1.0"),
(True, "true"),
(False, "false"),
(None, "null"),
([1, 2, 3], "[1, 2, 3]"),
([1, "2", 3.0], '[1, "2", 3.0]'),
([{"key": 1}, {"key": 2}], '[({ ["key"] : 1 }), ({ ["key"] : 2 })]'),
(StrEnum.FOO, "foo"),
([StrEnum.FOO, StrEnum.BAR], '["foo", "bar"]'),
(
{"key1": [1, 2, 3], "key2": [StrEnum.FOO, StrEnum.BAR]},
'({ ["key1"] : [1, 2, 3], ["key2"] : ["foo", "bar"] })',
),
(EnumWithPrefix.FOO, "prefix_foo"),
([EnumWithPrefix.FOO, EnumWithPrefix.BAR], '["prefix_foo", "prefix_bar"]'),
(
{"key1": EnumWithPrefix.FOO, "key2": EnumWithPrefix.BAR},
'({ ["key1"] : "prefix_foo", ["key2"] : "prefix_bar" })',
),
(TestEnum.FOO, "foo"),
([TestEnum.FOO, TestEnum.BAR], '["foo", "bar"]'),
(
{"key1": TestEnum.FOO, "key2": TestEnum.BAR},
'({ ["key1"] : "foo", ["key2"] : "bar" })',
),
(
BaseSubclass(ts=datetime.timedelta(1, 1, 1)),
'({ ["ts"] : "1 day, 0:00:01.000001" })',
),
(
[1, LiteralVar.create("hi"), ImmutableVar.create("bye")],
'[1, "hi", bye]',
),
(
(1, LiteralVar.create("hi"), ImmutableVar.create("bye")),
'[1, "hi", bye]',
),
({1: 2, 3: 4}, "({ [1] : 2, [3] : 4 })"),
(
{1: LiteralVar.create("hi"), 3: ImmutableVar.create("bye")},
'({ [1] : "hi", [3] : bye })',
),
(datetime.datetime(2021, 1, 1, 1, 1, 1, 1), "2021-01-01 01:01:01.000001"),
(datetime.date(2021, 1, 1), "2021-01-01"),
(datetime.time(1, 1, 1, 1), "01:01:01.000001"),
(datetime.timedelta(1, 1, 1), "1 day, 0:00:01.000001"),
(
[datetime.timedelta(1, 1, 1), datetime.timedelta(1, 1, 2)],
'["1 day, 0:00:01.000001", "1 day, 0:00:01.000002"]',
),
(Color(color="slate", shade=1), "var(--slate-1)"),
(Color(color="orange", shade=1, alpha=True), "var(--orange-a1)"),
(Color(color="accent", shade=1, alpha=True), "var(--accent-a1)"),
],
)
def test_serialize(value: Any, expected: str):
"""Test that serialize returns the correct value.
Args:
value: The value to serialize.
expected: The expected result.
"""
assert serializers.serialize(value) == expected
@pytest.mark.parametrize(
"value,expected,exp_var_is_string",
[
("test", '"test"', False),
(1, "1", False),
(1.0, "1.0", False),
(True, "true", False),
(False, "false", False),
([1, 2, 3], "[1, 2, 3]", False),
([{"key": 1}, {"key": 2}], '[({ ["key"] : 1 }), ({ ["key"] : 2 })]', False),
(StrEnum.FOO, '"foo"', False),
([StrEnum.FOO, StrEnum.BAR], '["foo", "bar"]', False),
(
BaseSubclass(ts=datetime.timedelta(1, 1, 1)),
'({ ["ts"] : "1 day, 0:00:01.000001" })',
False,
),
(
datetime.datetime(2021, 1, 1, 1, 1, 1, 1),
'"2021-01-01 01:01:01.000001"',
True,
),
(Color(color="slate", shade=1), '"var(--slate-1)"', True),
(BaseSubclass, '"BaseSubclass"', True),
(Path("."), '"."', True),
],
)
def test_serialize_var_to_str(value: Any, expected: str, exp_var_is_string: bool):
"""Test that serialize with `to=str` passed to a Var is marked with _var_is_string.
Args:
value: The value to serialize.
expected: The expected result.
exp_var_is_string: The expected value of _var_is_string.
"""
v = LiteralVar.create(value)
assert str(v) == expected