fix dynamic icons for underscore and positional argument (#4767)

* fix dynamic icons for underscore and positional argument

* use no return
This commit is contained in:
Khaleel Al-Adhami 2025-02-07 14:20:29 -08:00 committed by GitHub
parent b3b79a652d
commit f3220470e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 75 additions and 23 deletions

View File

@ -107,9 +107,7 @@ class StickyBadge(A):
default=True,
global_ref=False,
)
localhost_hostnames = Var.create(
["localhost", "127.0.0.1", "[::1]"]
).guess_type()
localhost_hostnames = Var.create(["localhost", "127.0.0.1", "[::1]"])
is_localhost_expr = localhost_hostnames.contains(
Var("window.location.hostname", _var_type=str).guess_type(),
)

View File

@ -4,7 +4,7 @@ from reflex.components.component import Component
from reflex.utils import format
from reflex.utils.imports import ImportVar
from reflex.vars.base import LiteralVar, Var
from reflex.vars.sequence import LiteralStringVar
from reflex.vars.sequence import LiteralStringVar, StringVar
class LucideIconComponent(Component):
@ -40,7 +40,12 @@ class Icon(LucideIconComponent):
The created component.
"""
if children:
if len(children) == 1 and isinstance(children[0], str):
if len(children) == 1:
child = Var.create(children[0]).guess_type()
if not isinstance(child, StringVar):
raise AttributeError(
f"Icon name must be a string, got {children[0]._var_type if isinstance(children[0], Var) else children[0]}"
)
props["tag"] = children[0]
else:
raise AttributeError(
@ -56,7 +61,10 @@ class Icon(LucideIconComponent):
else:
raise TypeError(f"Icon name must be a string, got {type(tag)}")
elif isinstance(tag, Var):
return DynamicIcon.create(name=tag, **props)
tag_stringified = tag.guess_type()
if not isinstance(tag_stringified, StringVar):
raise TypeError(f"Icon name must be a string, got {tag._var_type}")
return DynamicIcon.create(name=tag_stringified.replace("_", "-"), **props)
if (
not isinstance(tag, str)

View File

@ -75,9 +75,9 @@ from reflex.utils.types import (
if TYPE_CHECKING:
from reflex.state import BaseState
from .number import BooleanVar, NumberVar
from .object import ObjectVar
from .sequence import ArrayVar, StringVar
from .number import BooleanVar, LiteralBooleanVar, LiteralNumberVar, NumberVar
from .object import LiteralObjectVar, ObjectVar
from .sequence import ArrayVar, LiteralArrayVar, LiteralStringVar, StringVar
VAR_TYPE = TypeVar("VAR_TYPE", covariant=True)
@ -573,13 +573,21 @@ class Var(Generic[VAR_TYPE]):
return value_with_replaced
@overload
@classmethod
def create( # pyright: ignore[reportOverlappingOverload]
cls,
value: NoReturn,
_var_data: VarData | None = None,
) -> Var[Any]: ...
@overload
@classmethod
def create( # pyright: ignore[reportOverlappingOverload]
cls,
value: bool,
_var_data: VarData | None = None,
) -> BooleanVar: ...
) -> LiteralBooleanVar: ...
@overload
@classmethod
@ -587,7 +595,7 @@ class Var(Generic[VAR_TYPE]):
cls,
value: int,
_var_data: VarData | None = None,
) -> NumberVar[int]: ...
) -> LiteralNumberVar[int]: ...
@overload
@classmethod
@ -595,7 +603,15 @@ class Var(Generic[VAR_TYPE]):
cls,
value: float,
_var_data: VarData | None = None,
) -> NumberVar[float]: ...
) -> LiteralNumberVar[float]: ...
@overload
@classmethod
def create( # pyright: ignore [reportOverlappingOverload]
cls,
value: str,
_var_data: VarData | None = None,
) -> LiteralStringVar: ...
@overload
@classmethod
@ -611,7 +627,7 @@ class Var(Generic[VAR_TYPE]):
cls,
value: None,
_var_data: VarData | None = None,
) -> NoneVar: ...
) -> LiteralNoneVar: ...
@overload
@classmethod
@ -619,7 +635,7 @@ class Var(Generic[VAR_TYPE]):
cls,
value: MAPPING_TYPE,
_var_data: VarData | None = None,
) -> ObjectVar[MAPPING_TYPE]: ...
) -> LiteralObjectVar[MAPPING_TYPE]: ...
@overload
@classmethod
@ -627,7 +643,7 @@ class Var(Generic[VAR_TYPE]):
cls,
value: SEQUENCE_TYPE,
_var_data: VarData | None = None,
) -> ArrayVar[SEQUENCE_TYPE]: ...
) -> LiteralArrayVar[SEQUENCE_TYPE]: ...
@overload
@classmethod

View File

@ -974,7 +974,7 @@ def boolean_not_operation(value: BooleanVar):
frozen=True,
slots=True,
)
class LiteralNumberVar(LiteralVar, NumberVar):
class LiteralNumberVar(LiteralVar, NumberVar[NUMBER_T]):
"""Base class for immutable literal number vars."""
_var_value: float | int = dataclasses.field(default=0)

View File

@ -372,6 +372,33 @@ class StringVar(Var[STRING_TYPE], python_types=str):
return string_ge_operation(self, other)
@overload
def replace( # pyright: ignore [reportOverlappingOverload]
self, search_value: StringVar | str, new_value: StringVar | str
) -> StringVar: ...
@overload
def replace(
self, search_value: Any, new_value: Any
) -> CustomVarOperationReturn[StringVar]: ...
def replace(self, search_value: Any, new_value: Any) -> StringVar: # pyright: ignore [reportInconsistentOverload]
"""Replace a string with a value.
Args:
search_value: The string to search.
new_value: The value to be replaced with.
Returns:
The string replace operation.
"""
if not isinstance(search_value, (StringVar, str)):
raise_unsupported_operand_types("replace", (type(self), type(search_value)))
if not isinstance(new_value, (StringVar, str)):
raise_unsupported_operand_types("replace", (type(self), type(new_value)))
return string_replace_operation(self, search_value, new_value)
@var_operation
def string_lt_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str):
@ -570,7 +597,7 @@ def array_join_operation(array: ArrayVar, sep: StringVar[Any] | str = ""):
@var_operation
def string_replace_operation(
string: StringVar, search_value: StringVar | str, new_value: StringVar | str
string: StringVar[Any], search_value: StringVar | str, new_value: StringVar | str
):
"""Replace a string with a value.
@ -583,7 +610,7 @@ def string_replace_operation(
The string replace operation.
"""
return var_operation_return(
js_expression=f"{string}.replace({search_value}, {new_value})",
js_expression=f"{string}.replaceAll({search_value}, {new_value})",
var_type=str,
)

View File

@ -11,6 +11,7 @@ from reflex.components.lucide.icon import Icon
from reflex.components.radix.themes.layout.box import Box
from reflex.style import Style
from reflex.vars import Var
from reflex.vars.base import LiteralVar
@pytest.mark.parametrize(
@ -99,7 +100,9 @@ def test_create_shiki_code_block(
applied_styles = component.style
for key, value in expected_styles.items():
assert Var.create(applied_styles[key])._var_value == value
var = Var.create(applied_styles[key])
assert isinstance(var, LiteralVar)
assert var._var_value == value
@pytest.mark.parametrize(

View File

@ -74,11 +74,11 @@ class ObjectState(rx.State):
@pytest.mark.parametrize("type_", [Base, Bare, SqlaModel, Dataclass])
def test_var_create(type_: GenericType) -> None:
def test_var_create(type_: type[Base | Bare | SqlaModel | Dataclass]) -> None:
my_object = type_()
var = Var.create(my_object)
assert var._var_type is type_
assert isinstance(var, ObjectVar)
quantity = var.quantity
assert quantity._var_type is int
@ -94,12 +94,12 @@ def test_literal_create(type_: GenericType) -> None:
@pytest.mark.parametrize("type_", [Base, Bare, SqlaModel, Dataclass])
def test_guess(type_: GenericType) -> None:
def test_guess(type_: type[Base | Bare | SqlaModel | Dataclass]) -> None:
my_object = type_()
var = Var.create(my_object)
var = var.guess_type()
assert var._var_type is type_
assert isinstance(var, ObjectVar)
quantity = var.quantity
assert quantity._var_type is int