Skip to content

Commit 8663dbc

Browse files
authored
improve var base typing (#4718)
* improve var base typing * fix pyi * dang it darglint * drain _process in tests * fixes #4576 * dang it darglint
1 parent 12a42b6 commit 8663dbc

File tree

21 files changed

+280
-265
lines changed

21 files changed

+280
-265
lines changed

reflex/components/base/error_boundary.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111
from reflex.state import FrontendEventExceptionState
1212
from reflex.vars.base import Var
1313
from reflex.vars.function import ArgsFunctionOperation
14+
from reflex.vars.object import ObjectVar
1415

1516

1617
def on_error_spec(
17-
error: Var[Dict[str, str]], info: Var[Dict[str, str]]
18+
error: ObjectVar[Dict[str, str]], info: ObjectVar[Dict[str, str]]
1819
) -> Tuple[Var[str], Var[str]]:
1920
"""The spec for the on_error event handler.
2021

reflex/components/base/error_boundary.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ from reflex.components.component import Component
99
from reflex.event import BASE_STATE, EventType
1010
from reflex.style import Style
1111
from reflex.vars.base import Var
12+
from reflex.vars.object import ObjectVar
1213

1314
def on_error_spec(
14-
error: Var[Dict[str, str]], info: Var[Dict[str, str]]
15+
error: ObjectVar[Dict[str, str]], info: ObjectVar[Dict[str, str]]
1516
) -> Tuple[Var[str], Var[str]]: ...
1617

1718
class ErrorBoundary(Component):

reflex/components/component.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2457,6 +2457,7 @@ def render_dict_to_var(tag: dict | Component | str, imported_names: set[str]) ->
24572457
@dataclasses.dataclass(
24582458
eq=False,
24592459
frozen=True,
2460+
slots=True,
24602461
)
24612462
class LiteralComponentVar(CachedVarOperation, LiteralVar, ComponentVar):
24622463
"""A Var that represents a Component."""

reflex/components/core/foreach.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from reflex.components.tags import IterTag
1212
from reflex.constants import MemoizationMode
1313
from reflex.state import ComponentState
14+
from reflex.utils.exceptions import UntypedVarError
1415
from reflex.vars.base import LiteralVar, Var
1516

1617

@@ -51,6 +52,7 @@ def create(
5152
Raises:
5253
ForeachVarError: If the iterable is of type Any.
5354
TypeError: If the render function is a ComponentState.
55+
UntypedVarError: If the iterable is of type Any without a type annotation.
5456
"""
5557
iterable = LiteralVar.create(iterable)
5658
if iterable._var_type == Any:
@@ -72,8 +74,14 @@ def create(
7274
iterable=iterable,
7375
render_fn=render_fn,
7476
)
75-
# Keep a ref to a rendered component to determine correct imports/hooks/styles.
76-
component.children = [component._render().render_component()]
77+
try:
78+
# Keep a ref to a rendered component to determine correct imports/hooks/styles.
79+
component.children = [component._render().render_component()]
80+
except UntypedVarError as e:
81+
raise UntypedVarError(
82+
f"Could not foreach over var `{iterable!s}` without a type annotation. "
83+
"See https://reflex.dev/docs/library/dynamic-rendering/foreach/"
84+
) from e
7785
return component
7886

7987
def _render(self) -> IterTag:

reflex/components/datadisplay/dataeditor.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,8 @@ def create(cls, *children, **props) -> Component:
387387
raise ValueError(
388388
"DataEditor data must be an ArrayVar if rows is not provided."
389389
)
390-
props["rows"] = data.length() if isinstance(data, Var) else len(data)
390+
391+
props["rows"] = data.length() if isinstance(data, ArrayVar) else len(data)
391392

392393
if not isinstance(columns, Var) and len(columns):
393394
if types.is_dataframe(type(data)) or (

reflex/components/datadisplay/shiki_code_block.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -621,18 +621,22 @@ def add_imports(self) -> dict[str, list[str]]:
621621
622622
Returns:
623623
Imports for the component.
624+
625+
Raises:
626+
ValueError: If the transformers are not of type LiteralVar.
624627
"""
625628
imports = defaultdict(list)
629+
if not isinstance(self.transformers, LiteralVar):
630+
raise ValueError(
631+
f"transformers should be a LiteralVar type. Got {type(self.transformers)} instead."
632+
)
626633
for transformer in self.transformers._var_value:
627634
if isinstance(transformer, ShikiBaseTransformers):
628635
imports[transformer.library].extend(
629636
[ImportVar(tag=str(fn)) for fn in transformer.fns]
630637
)
631-
(
638+
if transformer.library not in self.lib_dependencies:
632639
self.lib_dependencies.append(transformer.library)
633-
if transformer.library not in self.lib_dependencies
634-
else None
635-
)
636640
return imports
637641

638642
@classmethod

reflex/event.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import dataclasses
66
import inspect
7-
import sys
87
import types
98
import urllib.parse
109
from base64 import b64encode
@@ -541,7 +540,7 @@ class JavasciptKeyboardEvent:
541540
shiftKey: bool = False # noqa: N815
542541

543542

544-
def input_event(e: Var[JavascriptInputEvent]) -> Tuple[Var[str]]:
543+
def input_event(e: ObjectVar[JavascriptInputEvent]) -> Tuple[Var[str]]:
545544
"""Get the value from an input event.
546545
547546
Args:
@@ -562,7 +561,9 @@ class KeyInputInfo(TypedDict):
562561
shift_key: bool
563562

564563

565-
def key_event(e: Var[JavasciptKeyboardEvent]) -> Tuple[Var[str], Var[KeyInputInfo]]:
564+
def key_event(
565+
e: ObjectVar[JavasciptKeyboardEvent],
566+
) -> Tuple[Var[str], Var[KeyInputInfo]]:
566567
"""Get the key from a keyboard event.
567568
568569
Args:
@@ -572,15 +573,15 @@ def key_event(e: Var[JavasciptKeyboardEvent]) -> Tuple[Var[str], Var[KeyInputInf
572573
The key from the keyboard event.
573574
"""
574575
return (
575-
e.key,
576+
e.key.to(str),
576577
Var.create(
577578
{
578579
"alt_key": e.altKey,
579580
"ctrl_key": e.ctrlKey,
580581
"meta_key": e.metaKey,
581582
"shift_key": e.shiftKey,
582583
},
583-
),
584+
).to(KeyInputInfo),
584585
)
585586

586587

@@ -1354,7 +1355,7 @@ def unwrap_var_annotation(annotation: GenericType):
13541355
Returns:
13551356
The unwrapped annotation.
13561357
"""
1357-
if get_origin(annotation) is Var and (args := get_args(annotation)):
1358+
if get_origin(annotation) in (Var, ObjectVar) and (args := get_args(annotation)):
13581359
return args[0]
13591360
return annotation
13601361

@@ -1620,7 +1621,7 @@ class EventVar(ObjectVar, python_types=EventSpec):
16201621
@dataclasses.dataclass(
16211622
eq=False,
16221623
frozen=True,
1623-
**{"slots": True} if sys.version_info >= (3, 10) else {},
1624+
slots=True,
16241625
)
16251626
class LiteralEventVar(VarOperationCall, LiteralVar, EventVar):
16261627
"""A literal event var."""
@@ -1681,7 +1682,7 @@ class EventChainVar(BuilderFunctionVar, python_types=EventChain):
16811682
@dataclasses.dataclass(
16821683
eq=False,
16831684
frozen=True,
1684-
**{"slots": True} if sys.version_info >= (3, 10) else {},
1685+
slots=True,
16851686
)
16861687
# Note: LiteralVar is second in the inheritance list allowing it act like a
16871688
# CachedVarOperation (ArgsFunctionOperation) and get the _js_expr from the
@@ -1713,6 +1714,9 @@ def create(
17131714
17141715
Returns:
17151716
The created LiteralEventChainVar instance.
1717+
1718+
Raises:
1719+
ValueError: If the invocation is not a FunctionVar.
17161720
"""
17171721
arg_spec = (
17181722
value.args_spec[0]
@@ -1740,6 +1744,11 @@ def create(
17401744
else:
17411745
invocation = value.invocation
17421746

1747+
if invocation is not None and not isinstance(invocation, FunctionVar):
1748+
raise ValueError(
1749+
f"EventChain invocation must be a FunctionVar, got {invocation!s} of type {invocation._var_type!s}."
1750+
)
1751+
17431752
return cls(
17441753
_js_expr="",
17451754
_var_type=EventChain,

reflex/experimental/client_state.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import dataclasses
66
import re
7-
import sys
87
from typing import Any, Callable, Union
98

109
from reflex import constants
@@ -49,7 +48,7 @@ def _client_state_ref_dict(var_name: str) -> str:
4948
@dataclasses.dataclass(
5049
eq=False,
5150
frozen=True,
52-
**{"slots": True} if sys.version_info >= (3, 10) else {},
51+
slots=True,
5352
)
5453
class ClientStateVar(Var):
5554
"""A Var that exists on the client via useState."""

reflex/state.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,9 +1637,11 @@ async def get_var_value(self, var: Var[VAR_TYPE]) -> VAR_TYPE:
16371637
if not isinstance(var, Var):
16381638
return var
16391639

1640+
unset = object()
1641+
16401642
# Fast case: this is a literal var and the value is known.
1641-
if hasattr(var, "_var_value"):
1642-
return var._var_value
1643+
if (var_value := getattr(var, "_var_value", unset)) is not unset:
1644+
return var_value # pyright: ignore [reportReturnType]
16431645

16441646
var_data = var._get_all_var_data()
16451647
if var_data is None or not var_data.state:

reflex/utils/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ class VarAttributeError(ReflexError, AttributeError):
7575
"""Custom AttributeError for var related errors."""
7676

7777

78+
class UntypedVarError(ReflexError, TypeError):
79+
"""Custom TypeError for untyped var errors."""
80+
81+
7882
class UntypedComputedVarError(ReflexError, TypeError):
7983
"""Custom TypeError for untyped computed var errors."""
8084

0 commit comments

Comments
 (0)