Var: __bool__ and __iter__ always raise a TypeError (#1750)

This commit is contained in:
Masen Furer 2023-09-05 13:44:22 -07:00 committed by GitHub
parent e99d6723bc
commit 71811a600c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 126 additions and 73 deletions

View File

@ -6,9 +6,9 @@
{% filter indent(width=indent_width) %} {% filter indent(width=indent_width) %}
{%- if component is not mapping %} {%- if component is not mapping %}
{{- component }} {{- component }}
{%- elif component.iterable %} {%- elif "iterable" in component %}
{{- render_iterable_tag(component) }} {{- render_iterable_tag(component) }}
{%- elif component.cond %} {%- elif "cond" in component %}
{{- render_condition_tag(component) }} {{- render_condition_tag(component) }}
{%- elif component.children|length %} {%- elif component.children|length %}
{{- render_tag(component) }} {{- render_tag(component) }}

View File

@ -66,7 +66,11 @@ class DataTable(Gridjs):
"Annotation of the computed var assigned to the data field should be provided." "Annotation of the computed var assigned to the data field should be provided."
) )
if columns and isinstance(columns, ComputedVar) and columns.type_ == Any: if (
columns is not None
and isinstance(columns, ComputedVar)
and columns.type_ == Any
):
raise ValueError( raise ValueError(
"Annotation of the computed var assigned to the column field should be provided." "Annotation of the computed var assigned to the column field should be provided."
) )
@ -75,7 +79,7 @@ class DataTable(Gridjs):
if ( if (
types.is_dataframe(type(data)) types.is_dataframe(type(data))
or (isinstance(data, Var) and types.is_dataframe(data.type_)) or (isinstance(data, Var) and types.is_dataframe(data.type_))
) and props.get("columns"): ) and columns is not None:
raise ValueError( raise ValueError(
"Cannot pass in both a pandas dataframe and columns to the data_table component." "Cannot pass in both a pandas dataframe and columns to the data_table component."
) )
@ -84,7 +88,7 @@ class DataTable(Gridjs):
if ( if (
(isinstance(data, Var) and types._issubclass(data.type_, List)) (isinstance(data, Var) and types._issubclass(data.type_, List))
or issubclass(type(data), List) or issubclass(type(data), List)
) and not props.get("columns"): ) and columns is None:
raise ValueError( raise ValueError(
"column field should be specified when the data field is a list type" "column field should be specified when the data field is a list type"
) )

View File

@ -45,7 +45,7 @@ class Link(ChakraComponent):
Returns: Returns:
Component: The link component Component: The link component
""" """
if props.get("href"): if props.get("href") is not None:
if not len(children): if not len(children):
raise ValueError("Link without a child will not display") raise ValueError("Link without a child will not display")
else: else:

View File

@ -885,7 +885,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow):
self.dirty_vars.add(cvar) self.dirty_vars.add(cvar)
dirty_vars.add(cvar) dirty_vars.add(cvar)
actual_var = self.computed_vars.get(cvar) actual_var = self.computed_vars.get(cvar)
if actual_var: if actual_var is not None:
actual_var.mark_dirty(instance=self) actual_var.mark_dirty(instance=self)
def _dirty_computed_vars(self, from_vars: set[str] | None = None) -> set[str]: def _dirty_computed_vars(self, from_vars: set[str] | None = None) -> set[str]:

View File

@ -542,7 +542,7 @@ def format_ref(ref: str) -> str:
return f"ref_{clean_ref}" return f"ref_{clean_ref}"
def format_array_ref(refs: str, idx) -> str: def format_array_ref(refs: str, idx: Var | None) -> str:
"""Format a ref accessed by array. """Format a ref accessed by array.
Args: Args:
@ -553,11 +553,10 @@ def format_array_ref(refs: str, idx) -> str:
The formatted ref. The formatted ref.
""" """
clean_ref = re.sub(r"[^\w]+", "_", refs) clean_ref = re.sub(r"[^\w]+", "_", refs)
if idx: if idx is not None:
idx.is_local = True idx.is_local = True
return f"refs_{clean_ref}[{idx}]" return f"refs_{clean_ref}[{idx}]"
else: return f"refs_{clean_ref}"
return f"refs_{clean_ref}"
def format_dict(prop: ComponentStyle) -> str: def format_dict(prop: ComponentStyle) -> str:

View File

@ -205,6 +205,27 @@ class Var(ABC):
out = format.format_string(out) out = format.format_string(out)
return out return out
def __bool__(self) -> bool:
"""Raise exception if using Var in a boolean context.
Raises:
TypeError: when attempting to bool-ify the Var.
"""
raise TypeError(
f"Cannot convert Var {self.full_name!r} to bool for use with `if`, `and`, `or`, and `not`. "
"Instead use `rx.cond` and bitwise operators `&` (and), `|` (or), `~` (invert)."
)
def __iter__(self) -> Any:
"""Raise exception if using Var in an iterable context.
Raises:
TypeError: when attempting to iterate over the Var.
"""
raise TypeError(
f"Cannot iterate over Var {self.full_name!r}. Instead use `rx.foreach`."
)
def __format__(self, format_spec: str) -> str: def __format__(self, format_spec: str) -> str:
"""Format the var into a Javascript equivalent to an f-string. """Format the var into a Javascript equivalent to an f-string.
@ -1414,7 +1435,7 @@ def get_local_storage(key: Var | str | None = None) -> BaseVar:
Raises: Raises:
TypeError: if the wrong key type is provided. TypeError: if the wrong key type is provided.
""" """
if key: if key is not None:
if not (isinstance(key, Var) and key.type_ == str) and not isinstance(key, str): if not (isinstance(key, Var) and key.type_ == str) and not isinstance(key, str):
type_ = type(key) if not isinstance(key, Var) else key.type_ type_ = type(key) if not isinstance(key, Var) else key.type_
raise TypeError( raise TypeError(

View File

@ -50,8 +50,8 @@ def test_render_child_props():
) )
)._render() )._render()
assert tag.props["sx"] == {"foo": "bar", "baz": "quuc"} assert tag.props["sx"] == {"foo": "bar", "baz": "quuc"}
assert tag.props["value"] == BaseVar( assert tag.props["value"].equals(
name="real", type_=str, is_local=True, is_string=False BaseVar(name="real", type_=str, is_local=True, is_string=False)
) )
assert len(tag.props["onChange"].events) == 1 assert len(tag.props["onChange"].events) == 1
assert tag.props["onChange"].events[0].handler == S.on_change assert tag.props["onChange"].events[0].handler == S.on_change
@ -75,6 +75,7 @@ def test_render_child_props_recursive():
on_change=S.on_change, on_change=S.on_change,
), ),
value="inner", value="inner",
debounce_timeout=666,
force_notify_on_blur=False, force_notify_on_blur=False,
), ),
debounce_timeout=42, debounce_timeout=42,
@ -84,8 +85,8 @@ def test_render_child_props_recursive():
force_notify_by_enter=False, force_notify_by_enter=False,
)._render() )._render()
assert tag.props["sx"] == {"foo": "bar", "baz": "quuc"} assert tag.props["sx"] == {"foo": "bar", "baz": "quuc"}
assert tag.props["value"] == BaseVar( assert tag.props["value"].equals(
name="real", type_=str, is_local=True, is_string=False BaseVar(name="outer", type_=str, is_local=True, is_string=False)
) )
assert tag.props["forceNotifyOnBlur"].name == "false" assert tag.props["forceNotifyOnBlur"].name == "false"
assert tag.props["forceNotifyByEnter"].name == "false" assert tag.props["forceNotifyByEnter"].name == "false"

View File

@ -104,7 +104,7 @@ def test_cond_no_else():
assert isinstance(comp, Fragment) assert isinstance(comp, Fragment)
comp = comp.children[0] comp = comp.children[0]
assert isinstance(comp, Cond) assert isinstance(comp, Cond)
assert comp.cond == True # noqa assert comp.cond._decode() is True # type: ignore
assert comp.comp1 == Fragment.create(Text.create("hello")) assert comp.comp1 == Fragment.create(Text.create("hello"))
assert comp.comp2 == Fragment.create() assert comp.comp2 == Fragment.create()

View File

@ -186,6 +186,6 @@ def test_foreach_render(state_var, render_fn, render_dict):
rend = component.render() rend = component.render()
arg_index = rend["arg_index"] arg_index = rend["arg_index"]
assert rend["iterable_state"] == render_dict["iterable_state"] assert rend["iterable_state"] == render_dict["iterable_state"]
assert rend["arg_index"] == render_dict["arg_index"] assert arg_index.name == render_dict["arg_index"]
assert arg_index.type_ == int assert arg_index.type_ == int
assert rend["iterable_type"] == render_dict["iterable_type"] assert rend["iterable_type"] == render_dict["iterable_type"]

View File

@ -329,8 +329,8 @@ def test_valid_props(component1, text: str, number: int):
number: A test number. number: A test number.
""" """
c = component1.create(text=text, number=number) c = component1.create(text=text, number=number)
assert c.text == text assert c.text._decode() == text
assert c.number == number assert c.number._decode() == number
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -357,7 +357,7 @@ def test_var_props(component1, test_state):
test_state: A test state. test_state: A test state.
""" """
c1 = component1.create(text="hello", number=test_state.num) c1 = component1.create(text="hello", number=test_state.num)
assert c1.number == test_state.num assert c1.number.equals(test_state.num)
def test_get_controlled_triggers(component1, component2): def test_get_controlled_triggers(component1, component2):

View File

@ -52,8 +52,8 @@ def test_is_valid_prop(prop: Var, valid: bool):
def test_add_props(): def test_add_props():
"""Test that the props are added.""" """Test that the props are added."""
tag = Tag().add_props(key="value", key2=42, invalid=None, invalid2={}) tag = Tag().add_props(key="value", key2=42, invalid=None, invalid2={})
assert tag.props["key"] == Var.create("value") assert tag.props["key"].equals(Var.create("value"))
assert tag.props["key2"] == Var.create(42) assert tag.props["key2"].equals(Var.create(42))
assert "invalid" not in tag.props assert "invalid" not in tag.props
assert "invalid2" not in tag.props assert "invalid2" not in tag.props
@ -100,7 +100,8 @@ def test_format_tag(tag: Tag, expected: Dict):
tag_dict = dict(tag) tag_dict = dict(tag)
assert tag_dict["name"] == expected["name"] assert tag_dict["name"] == expected["name"]
assert tag_dict["contents"] == expected["contents"] assert tag_dict["contents"] == expected["contents"]
assert tag_dict["props"] == expected["props"] for prop, prop_value in tag_dict["props"].items():
assert prop_value.equals(Var.create_safe(expected["props"][prop]))
def test_format_cond_tag(): def test_format_cond_tag():
@ -116,7 +117,8 @@ def test_format_cond_tag():
tag_dict["true_value"], tag_dict["true_value"],
tag_dict["false_value"], tag_dict["false_value"],
) )
assert cond == "logged_in" assert cond.name == "logged_in"
assert cond.type_ == bool
assert true_value["name"] == "h1" assert true_value["name"] == "h1"
assert true_value["contents"] == "True content" assert true_value["contents"] == "True content"

View File

@ -930,5 +930,6 @@ async def test_process_events(gen_state, mocker):
async for _update in process(app, event, "mock_sid", {}, "127.0.0.1"): async for _update in process(app, event, "mock_sid", {}, "127.0.0.1"):
pass pass
assert gen_state.value == 5
assert app.state_manager.get_state("token").value == 5
assert app.postprocess.call_count == 6 assert app.postprocess.call_count == 6

View File

@ -55,7 +55,10 @@ def test_call_event_handler():
# Test passing vars as args. # Test passing vars as args.
assert event_spec.handler == handler assert event_spec.handler == handler
assert event_spec.args == (("arg1", "first"), ("arg2", "second")) assert event_spec.args[0][0].equals(Var.create_safe("arg1"))
assert event_spec.args[0][1].equals(Var.create_safe("first"))
assert event_spec.args[1][0].equals(Var.create_safe("arg2"))
assert event_spec.args[1][1].equals(Var.create_safe("second"))
assert ( assert (
format.format_event(event_spec) format.format_event(event_spec)
== 'E("test_fn_with_args", {arg1:first,arg2:second})' == 'E("test_fn_with_args", {arg1:first,arg2:second})'
@ -77,10 +80,10 @@ def test_call_event_handler():
) )
assert event_spec.handler == handler assert event_spec.handler == handler
assert event_spec.args == ( assert event_spec.args[0][0].equals(Var.create_safe("arg1"))
("arg1", format.json_dumps(first)), assert event_spec.args[0][1].equals(Var.create_safe(first))
("arg2", format.json_dumps(second)), assert event_spec.args[1][0].equals(Var.create_safe("arg2"))
) assert event_spec.args[1][1].equals(Var.create_safe(second))
handler = EventHandler(fn=test_fn_with_args) handler = EventHandler(fn=test_fn_with_args)
with pytest.raises(TypeError): with pytest.raises(TypeError):
@ -121,7 +124,8 @@ def test_event_redirect():
spec = event.redirect("/path") spec = event.redirect("/path")
assert isinstance(spec, EventSpec) assert isinstance(spec, EventSpec)
assert spec.handler.fn.__qualname__ == "_redirect" assert spec.handler.fn.__qualname__ == "_redirect"
assert spec.args == (("path", "/path"),) assert spec.args[0][0].equals(Var.create_safe("path"))
assert spec.args[0][1].equals(Var.create_safe("/path"))
assert format.format_event(spec) == 'E("_redirect", {path:"/path"})' assert format.format_event(spec) == 'E("_redirect", {path:"/path"})'
spec = event.redirect(Var.create_safe("path")) spec = event.redirect(Var.create_safe("path"))
assert format.format_event(spec) == 'E("_redirect", {path:path})' assert format.format_event(spec) == 'E("_redirect", {path:path})'
@ -132,7 +136,8 @@ def test_event_console_log():
spec = event.console_log("message") spec = event.console_log("message")
assert isinstance(spec, EventSpec) assert isinstance(spec, EventSpec)
assert spec.handler.fn.__qualname__ == "_console" assert spec.handler.fn.__qualname__ == "_console"
assert spec.args == (("message", "message"),) assert spec.args[0][0].equals(Var.create_safe("message"))
assert spec.args[0][1].equals(Var.create_safe("message"))
assert format.format_event(spec) == 'E("_console", {message:"message"})' assert format.format_event(spec) == 'E("_console", {message:"message"})'
spec = event.console_log(Var.create_safe("message")) spec = event.console_log(Var.create_safe("message"))
assert format.format_event(spec) == 'E("_console", {message:message})' assert format.format_event(spec) == 'E("_console", {message:message})'
@ -143,7 +148,8 @@ def test_event_window_alert():
spec = event.window_alert("message") spec = event.window_alert("message")
assert isinstance(spec, EventSpec) assert isinstance(spec, EventSpec)
assert spec.handler.fn.__qualname__ == "_alert" assert spec.handler.fn.__qualname__ == "_alert"
assert spec.args == (("message", "message"),) assert spec.args[0][0].equals(Var.create_safe("message"))
assert spec.args[0][1].equals(Var.create_safe("message"))
assert format.format_event(spec) == 'E("_alert", {message:"message"})' assert format.format_event(spec) == 'E("_alert", {message:"message"})'
spec = event.window_alert(Var.create_safe("message")) spec = event.window_alert(Var.create_safe("message"))
assert format.format_event(spec) == 'E("_alert", {message:message})' assert format.format_event(spec) == 'E("_alert", {message:message})'
@ -154,7 +160,8 @@ def test_set_focus():
spec = event.set_focus("input1") spec = event.set_focus("input1")
assert isinstance(spec, EventSpec) assert isinstance(spec, EventSpec)
assert spec.handler.fn.__qualname__ == "_set_focus" assert spec.handler.fn.__qualname__ == "_set_focus"
assert spec.args == (("ref", Var.create_safe("ref_input1")),) assert spec.args[0][0].equals(Var.create_safe("ref"))
assert spec.args[0][1].equals(Var.create_safe("ref_input1"))
assert format.format_event(spec) == 'E("_set_focus", {ref:ref_input1})' assert format.format_event(spec) == 'E("_set_focus", {ref:ref_input1})'
spec = event.set_focus("input1") spec = event.set_focus("input1")
assert format.format_event(spec) == 'E("_set_focus", {ref:ref_input1})' assert format.format_event(spec) == 'E("_set_focus", {ref:ref_input1})'
@ -165,10 +172,10 @@ def test_set_value():
spec = event.set_value("input1", "") spec = event.set_value("input1", "")
assert isinstance(spec, EventSpec) assert isinstance(spec, EventSpec)
assert spec.handler.fn.__qualname__ == "_set_value" assert spec.handler.fn.__qualname__ == "_set_value"
assert spec.args == ( assert spec.args[0][0].equals(Var.create_safe("ref"))
("ref", Var.create_safe("ref_input1")), assert spec.args[0][1].equals(Var.create_safe("ref_input1"))
("value", ""), assert spec.args[1][0].equals(Var.create_safe("value"))
) assert spec.args[1][1].equals(Var.create_safe(""))
assert format.format_event(spec) == 'E("_set_value", {ref:ref_input1,value:""})' assert format.format_event(spec) == 'E("_set_value", {ref:ref_input1,value:""})'
spec = event.set_value("input1", Var.create_safe("message")) spec = event.set_value("input1", Var.create_safe("message"))
assert ( assert (
@ -181,10 +188,10 @@ def test_set_cookie():
spec = event.set_cookie("testkey", "testvalue") spec = event.set_cookie("testkey", "testvalue")
assert isinstance(spec, EventSpec) assert isinstance(spec, EventSpec)
assert spec.handler.fn.__qualname__ == "_set_cookie" assert spec.handler.fn.__qualname__ == "_set_cookie"
assert spec.args == ( assert spec.args[0][0].equals(Var.create_safe("key"))
("key", "testkey"), assert spec.args[0][1].equals(Var.create_safe("testkey"))
("value", "testvalue"), assert spec.args[1][0].equals(Var.create_safe("value"))
) assert spec.args[1][1].equals(Var.create_safe("testvalue"))
assert ( assert (
format.format_event(spec) format.format_event(spec)
== 'E("_set_cookie", {key:"testkey",value:"testvalue"})' == 'E("_set_cookie", {key:"testkey",value:"testvalue"})'
@ -196,7 +203,10 @@ def test_remove_cookie():
spec = event.remove_cookie("testkey") spec = event.remove_cookie("testkey")
assert isinstance(spec, EventSpec) assert isinstance(spec, EventSpec)
assert spec.handler.fn.__qualname__ == "_remove_cookie" assert spec.handler.fn.__qualname__ == "_remove_cookie"
assert spec.args == (("key", "testkey"), ("options", {})) assert spec.args[0][0].equals(Var.create_safe("key"))
assert spec.args[0][1].equals(Var.create_safe("testkey"))
assert spec.args[1][0].equals(Var.create_safe("options"))
assert spec.args[1][1].equals(Var.create_safe({}))
assert ( assert (
format.format_event(spec) == 'E("_remove_cookie", {key:"testkey",options:{}})' format.format_event(spec) == 'E("_remove_cookie", {key:"testkey",options:{}})'
) )
@ -213,7 +223,10 @@ def test_remove_cookie_with_options():
spec = event.remove_cookie("testkey", options) spec = event.remove_cookie("testkey", options)
assert isinstance(spec, EventSpec) assert isinstance(spec, EventSpec)
assert spec.handler.fn.__qualname__ == "_remove_cookie" assert spec.handler.fn.__qualname__ == "_remove_cookie"
assert spec.args == (("key", "testkey"), ("options", options)) assert spec.args[0][0].equals(Var.create_safe("key"))
assert spec.args[0][1].equals(Var.create_safe("testkey"))
assert spec.args[1][0].equals(Var.create_safe("options"))
assert spec.args[1][1].equals(Var.create_safe(options))
assert ( assert (
format.format_event(spec) format.format_event(spec)
== f'E("_remove_cookie", {{key:"testkey",options:{json.dumps(options)}}})' == f'E("_remove_cookie", {{key:"testkey",options:{json.dumps(options)}}})'
@ -225,10 +238,10 @@ def test_set_local_storage():
spec = event.set_local_storage("testkey", "testvalue") spec = event.set_local_storage("testkey", "testvalue")
assert isinstance(spec, EventSpec) assert isinstance(spec, EventSpec)
assert spec.handler.fn.__qualname__ == "_set_local_storage" assert spec.handler.fn.__qualname__ == "_set_local_storage"
assert spec.args == ( assert spec.args[0][0].equals(Var.create_safe("key"))
("key", "testkey"), assert spec.args[0][1].equals(Var.create_safe("testkey"))
("value", "testvalue"), assert spec.args[1][0].equals(Var.create_safe("value"))
) assert spec.args[1][1].equals(Var.create_safe("testvalue"))
assert ( assert (
format.format_event(spec) format.format_event(spec)
== 'E("_set_local_storage", {key:"testkey",value:"testvalue"})' == 'E("_set_local_storage", {key:"testkey",value:"testvalue"})'
@ -249,5 +262,6 @@ def test_remove_local_storage():
spec = event.remove_local_storage("testkey") spec = event.remove_local_storage("testkey")
assert isinstance(spec, EventSpec) assert isinstance(spec, EventSpec)
assert spec.handler.fn.__qualname__ == "_remove_local_storage" assert spec.handler.fn.__qualname__ == "_remove_local_storage"
assert spec.args == (("key", "testkey"),) assert spec.args[0][0].equals(Var.create_safe("key"))
assert spec.args[0][1].equals(Var.create_safe("testkey"))
assert format.format_event(spec) == 'E("_remove_local_storage", {key:"testkey"})' assert format.format_event(spec) == 'E("_remove_local_storage", {key:"testkey"})'

View File

@ -408,18 +408,18 @@ def test_get_class_substate():
def test_get_class_var(): def test_get_class_var():
"""Test getting the var of a class.""" """Test getting the var of a class."""
assert TestState.get_class_var(("num1",)) == TestState.num1 assert TestState.get_class_var(("num1",)).equals(TestState.num1)
assert TestState.get_class_var(("num2",)) == TestState.num2 assert TestState.get_class_var(("num2",)).equals(TestState.num2)
assert ChildState.get_class_var(("value",)) == ChildState.value assert ChildState.get_class_var(("value",)).equals(ChildState.value)
assert GrandchildState.get_class_var(("value2",)) == GrandchildState.value2 assert GrandchildState.get_class_var(("value2",)).equals(GrandchildState.value2)
assert TestState.get_class_var(("child_state", "value")) == ChildState.value assert TestState.get_class_var(("child_state", "value")).equals(ChildState.value)
assert ( assert TestState.get_class_var(
TestState.get_class_var(("child_state", "grandchild_state", "value2")) ("child_state", "grandchild_state", "value2")
== GrandchildState.value2 ).equals(
GrandchildState.value2,
) )
assert ( assert ChildState.get_class_var(("grandchild_state", "value2")).equals(
ChildState.get_class_var(("grandchild_state", "value2")) GrandchildState.value2,
== GrandchildState.value2
) )
with pytest.raises(ValueError): with pytest.raises(ValueError):
TestState.get_class_var(("invalid_var",)) TestState.get_class_var(("invalid_var",))
@ -837,21 +837,32 @@ def test_get_query_params(test_state):
assert test_state.get_query_params() == params assert test_state.get_query_params() == params
def test_add_var(test_state): def test_add_var():
test_state.add_var("dynamic_int", int, 42) class DynamicState(State):
assert test_state.dynamic_int == 42 pass
test_state.add_var("dynamic_list", List[int], [5, 10]) ds1 = DynamicState()
assert test_state.dynamic_list == [5, 10] assert "dynamic_int" not in ds1.__dict__
assert test_state.dynamic_list == [5, 10] assert not hasattr(ds1, "dynamic_int")
ds1.add_var("dynamic_int", int, 42)
# Existing instances get the BaseVar
assert ds1.dynamic_int.equals(DynamicState.dynamic_int) # type: ignore
# New instances get an actual value with the default
assert DynamicState().dynamic_int == 42
# how to test that one? ds1.add_var("dynamic_list", List[int], [5, 10])
# test_state.dynamic_list.append(15) assert ds1.dynamic_list.equals(DynamicState.dynamic_list) # type: ignore
# assert test_state.dynamic_list == [5, 10, 15] ds2 = DynamicState()
assert ds2.dynamic_list == [5, 10]
ds2.dynamic_list.append(15)
assert ds2.dynamic_list == [5, 10, 15]
assert DynamicState().dynamic_list == [5, 10]
test_state.add_var("dynamic_dict", Dict[str, int], {"k1": 5, "k2": 10}) ds1.add_var("dynamic_dict", Dict[str, int], {"k1": 5, "k2": 10})
assert test_state.dynamic_dict == {"k1": 5, "k2": 10} assert ds1.dynamic_dict.equals(DynamicState.dynamic_dict) # type: ignore
assert test_state.dynamic_dict == {"k1": 5, "k2": 10} assert ds2.dynamic_dict.equals(DynamicState.dynamic_dict) # type: ignore
assert DynamicState().dynamic_dict == {"k1": 5, "k2": 10}
assert DynamicState().dynamic_dict == {"k1": 5, "k2": 10}
def test_add_var_default_handlers(test_state): def test_add_var_default_handlers(test_state):