From 56f0d6375b5456edcfaed35c5ecbbeaa77039aeb Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 12 Nov 2024 16:01:32 -0800 Subject: [PATCH 01/86] add typing to function vars --- reflex/event.py | 6 +- reflex/utils/telemetry.py | 3 +- reflex/vars/base.py | 22 ++- reflex/vars/function.py | 304 +++++++++++++++++++++++++++++++++----- tests/units/test_var.py | 2 +- 5 files changed, 296 insertions(+), 41 deletions(-) diff --git a/reflex/event.py b/reflex/event.py index 85a2541a592..13167297140 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -45,6 +45,8 @@ from reflex.vars.base import LiteralVar, Var from reflex.vars.function import ( ArgsFunctionOperation, + ArgsFunctionOperationBuilder, + BuilderFunctionVar, FunctionArgs, FunctionStringVar, FunctionVar, @@ -1580,7 +1582,7 @@ def create( ) -class EventChainVar(FunctionVar, python_types=EventChain): +class EventChainVar(BuilderFunctionVar, python_types=EventChain): """Base class for event chain vars.""" @@ -1592,7 +1594,7 @@ class EventChainVar(FunctionVar, python_types=EventChain): # Note: LiteralVar is second in the inheritance list allowing it act like a # CachedVarOperation (ArgsFunctionOperation) and get the _js_expr from the # _cached_var_name property. -class LiteralEventChainVar(ArgsFunctionOperation, LiteralVar, EventChainVar): +class LiteralEventChainVar(ArgsFunctionOperationBuilder, LiteralVar, EventChainVar): """A literal event chain var.""" _var_value: EventChain = dataclasses.field(default=None) # type: ignore diff --git a/reflex/utils/telemetry.py b/reflex/utils/telemetry.py index 806b916fcb4..815d37a1be4 100644 --- a/reflex/utils/telemetry.py +++ b/reflex/utils/telemetry.py @@ -51,7 +51,8 @@ def get_python_version() -> str: Returns: The Python version. """ - return platform.python_version() + # Remove the "+" from the version string in case user is using a pre-release version. + return platform.python_version().rstrip("+") def get_reflex_version() -> str: diff --git a/reflex/vars/base.py b/reflex/vars/base.py index b9aa55eb371..200f693defd 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -361,21 +361,29 @@ def _var_is_string(self) -> bool: return False def __init_subclass__( - cls, python_types: Tuple[GenericType, ...] | GenericType = types.Unset, **kwargs + cls, + python_types: Tuple[GenericType, ...] | GenericType = types.Unset(), + default_type: GenericType = types.Unset(), + **kwargs, ): """Initialize the subclass. Args: python_types: The python types that the var represents. + default_type: The default type of the var. Defaults to the first python type. **kwargs: Additional keyword arguments. """ super().__init_subclass__(**kwargs) - if python_types is not types.Unset: + if python_types or default_type: python_types = ( - python_types if isinstance(python_types, tuple) else (python_types,) + (python_types if isinstance(python_types, tuple) else (python_types,)) + if python_types + else () ) + default_type = default_type or (python_types[0] if python_types else Any) + @dataclasses.dataclass( eq=False, frozen=True, @@ -388,7 +396,7 @@ class ToVarOperation(ToOperation, cls): default=Var(_js_expr="null", _var_type=None), ) - _default_var_type: ClassVar[GenericType] = python_types[0] + _default_var_type: ClassVar[GenericType] = default_type ToVarOperation.__name__ = f'To{cls.__name__.removesuffix("Var")}Operation' @@ -588,6 +596,12 @@ def to( output: type[list] | type[tuple] | type[set], ) -> ArrayVar: ... + @overload + def to( + self, + output: type[dict], + ) -> ObjectVar[dict]: ... + @overload def to( self, output: Type[ObjectVar], var_type: Type[VAR_INSIDE] diff --git a/reflex/vars/function.py b/reflex/vars/function.py index 98f3b23358d..28c11837235 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -4,32 +4,184 @@ import dataclasses import sys -from typing import Any, Callable, Optional, Sequence, Tuple, Type, Union +from typing import ( + Any, + Generic, + Optional, + ParamSpec, + Sequence, + Tuple, + Type, + Union, + overload, +) + +from typing_extensions import Callable, Concatenate, Protocol, TypeVar from reflex.utils import format from reflex.utils.types import GenericType from .base import CachedVarOperation, LiteralVar, Var, VarData, cached_property_no_lock +P = ParamSpec("P") +V1 = TypeVar("V1") +V2 = TypeVar("V2") +V3 = TypeVar("V3") +V4 = TypeVar("V4") +V5 = TypeVar("V5") +V6 = TypeVar("V6") +R = TypeVar("R") + + +class ReflexCallable(Protocol[P, R]): + """Protocol for a callable.""" + + __call__: Callable[P, R] -class FunctionVar(Var[Callable], python_types=Callable): + +CALLABLE_TYPE = TypeVar("CALLABLE_TYPE", bound=ReflexCallable, infer_variance=True) +OTHER_CALLABLE_TYPE = TypeVar( + "OTHER_CALLABLE_TYPE", bound=ReflexCallable, infer_variance=True +) + + +class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[..., Any]): """Base class for immutable function vars.""" - def __call__(self, *args: Var | Any) -> ArgsFunctionOperation: - """Call the function with the given arguments. + @overload + def partial(self) -> FunctionVar[CALLABLE_TYPE]: ... + + @overload + def partial( + self: FunctionVar[ReflexCallable[Concatenate[V1, P], R]], + arg1: Union[V1, Var[V1]], + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + @overload + def partial( + self: FunctionVar[ReflexCallable[Concatenate[V1, V2, P], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + @overload + def partial( + self: FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, P], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + @overload + def partial( + self: FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, V4, P], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4]], + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + @overload + def partial( + self: FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, V4, V5, P], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4]], + arg5: Union[V5, Var[V5]], + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + @overload + def partial( + self: FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, V4, V5, V6, P], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4]], + arg5: Union[V5, Var[V5]], + arg6: Union[V6, Var[V6]], + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + @overload + def partial( + self: FunctionVar[ReflexCallable[..., R]], *args: Var | Any + ) -> FunctionVar[ReflexCallable[..., R]]: ... + + @overload + def partial(self, *args: Var | Any) -> FunctionVar: ... + + def partial(self, *args: Var | Any) -> FunctionVar: # type: ignore + """Partially apply the function with the given arguments. Args: - *args: The arguments to call the function with. + *args: The arguments to partially apply the function with. Returns: - The function call operation. + The partially applied function. """ + if not args: + return ArgsFunctionOperation.create((), self) return ArgsFunctionOperation.create( ("...args",), VarOperationCall.create(self, *args, Var(_js_expr="...args")), ) - def call(self, *args: Var | Any) -> VarOperationCall: + @overload + def call( + self: FunctionVar[ReflexCallable[[V1], R]], arg1: Union[V1, Var[V1]] + ) -> VarOperationCall[[V1], R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + ) -> VarOperationCall[[V1, V2], R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, V3], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + ) -> VarOperationCall[[V1, V2, V3], R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, V3, V4], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4]], + ) -> VarOperationCall[[V1, V2, V3, V4], R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, V3, V4, V5], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4]], + arg5: Union[V5, Var[V5]], + ) -> VarOperationCall[[V1, V2, V3, V4, V5], R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, V3, V4, V5, V6], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4]], + arg5: Union[V5, Var[V5]], + arg6: Union[V6, Var[V6]], + ) -> VarOperationCall[[V1, V2, V3, V4, V5, V6], R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[..., R]], *args: Var | Any + ) -> VarOperationCall[..., R]: ... + + def call(self, *args: Var | Any) -> Var: # type: ignore """Call the function with the given arguments. Args: @@ -38,19 +190,29 @@ def call(self, *args: Var | Any) -> VarOperationCall: Returns: The function call operation. """ - return VarOperationCall.create(self, *args) + return VarOperationCall.create(self, *args).guess_type() + + __call__ = call + + +class BuilderFunctionVar( + FunctionVar[CALLABLE_TYPE], default_type=ReflexCallable[..., Any] +): + """Base class for immutable function vars with the builder pattern.""" + __call__ = FunctionVar.partial -class FunctionStringVar(FunctionVar): + +class FunctionStringVar(FunctionVar[CALLABLE_TYPE]): """Base class for immutable function vars from a string.""" @classmethod def create( cls, func: str, - _var_type: Type[Callable] = Callable, + _var_type: Type[OTHER_CALLABLE_TYPE] = ReflexCallable[..., Any], _var_data: VarData | None = None, - ) -> FunctionStringVar: + ) -> FunctionStringVar[OTHER_CALLABLE_TYPE]: """Create a new function var from a string. Args: @@ -60,7 +222,7 @@ def create( Returns: The function var. """ - return cls( + return FunctionStringVar( _js_expr=func, _var_type=_var_type, _var_data=_var_data, @@ -72,10 +234,10 @@ def create( frozen=True, **{"slots": True} if sys.version_info >= (3, 10) else {}, ) -class VarOperationCall(CachedVarOperation, Var): +class VarOperationCall(Generic[P, R], CachedVarOperation, Var[R]): """Base class for immutable vars that are the result of a function call.""" - _func: Optional[FunctionVar] = dataclasses.field(default=None) + _func: Optional[FunctionVar[ReflexCallable[P, R]]] = dataclasses.field(default=None) _args: Tuple[Union[Var, Any], ...] = dataclasses.field(default_factory=tuple) @cached_property_no_lock @@ -103,7 +265,7 @@ def _cached_get_all_var_data(self) -> VarData | None: @classmethod def create( cls, - func: FunctionVar, + func: FunctionVar[ReflexCallable[P, R]], *args: Var | Any, _var_type: GenericType = Any, _var_data: VarData | None = None, @@ -118,9 +280,15 @@ def create( Returns: The function call var. """ + function_return_type = ( + func._var_type.__args__[1] + if getattr(func._var_type, "__args__", None) + else Any + ) + var_type = _var_type if _var_type is not Any else function_return_type return cls( _js_expr="", - _var_type=_var_type, + _var_type=var_type, _var_data=_var_data, _func=func, _args=args, @@ -157,6 +325,33 @@ class FunctionArgs: rest: Optional[str] = None +def format_args_function_operation( + args: FunctionArgs, return_expr: Var | Any, explicit_return: bool +) -> str: + """Format an args function operation. + + Args: + args: The function arguments. + return_expr: The return expression. + explicit_return: Whether to use explicit return syntax. + + Returns: + The formatted args function operation. + """ + arg_names_str = ", ".join( + [arg if isinstance(arg, str) else arg.to_javascript() for arg in args.args] + ) + (f", ...{args.rest}" if args.rest else "") + + return_expr_str = str(LiteralVar.create(return_expr)) + + # Wrap return expression in curly braces if explicit return syntax is used. + return_expr_str_wrapped = ( + format.wrap(return_expr_str, "{", "}") if explicit_return else return_expr_str + ) + + return f"(({arg_names_str}) => {return_expr_str_wrapped})" + + @dataclasses.dataclass( eq=False, frozen=True, @@ -176,23 +371,64 @@ def _cached_var_name(self) -> str: Returns: The name of the var. """ - arg_names_str = ", ".join( - [ - arg if isinstance(arg, str) else arg.to_javascript() - for arg in self._args.args - ] - ) + (f", ...{self._args.rest}" if self._args.rest else "") - - return_expr_str = str(LiteralVar.create(self._return_expr)) - - # Wrap return expression in curly braces if explicit return syntax is used. - return_expr_str_wrapped = ( - format.wrap(return_expr_str, "{", "}") - if self._explicit_return - else return_expr_str + return format_args_function_operation( + self._args, self._return_expr, self._explicit_return + ) + + @classmethod + def create( + cls, + args_names: Sequence[Union[str, DestructuredArg]], + return_expr: Var | Any, + rest: str | None = None, + explicit_return: bool = False, + _var_type: GenericType = Callable, + _var_data: VarData | None = None, + ): + """Create a new function var. + + Args: + args_names: The names of the arguments. + return_expr: The return expression of the function. + rest: The name of the rest argument. + explicit_return: Whether to use explicit return syntax. + _var_data: Additional hooks and imports associated with the Var. + + Returns: + The function var. + """ + return cls( + _js_expr="", + _var_type=_var_type, + _var_data=_var_data, + _args=FunctionArgs(args=tuple(args_names), rest=rest), + _return_expr=return_expr, + _explicit_return=explicit_return, ) - return f"(({arg_names_str}) => {return_expr_str_wrapped})" + +@dataclasses.dataclass( + eq=False, + frozen=True, + **{"slots": True} if sys.version_info >= (3, 10) else {}, +) +class ArgsFunctionOperationBuilder(CachedVarOperation, BuilderFunctionVar): + """Base class for immutable function defined via arguments and return expression with the builder pattern.""" + + _args: FunctionArgs = dataclasses.field(default_factory=FunctionArgs) + _return_expr: Union[Var, Any] = dataclasses.field(default=None) + _explicit_return: bool = dataclasses.field(default=False) + + @cached_property_no_lock + def _cached_var_name(self) -> str: + """The name of the var. + + Returns: + The name of the var. + """ + return format_args_function_operation( + self._args, self._return_expr, self._explicit_return + ) @classmethod def create( @@ -203,7 +439,7 @@ def create( explicit_return: bool = False, _var_type: GenericType = Callable, _var_data: VarData | None = None, - ) -> ArgsFunctionOperation: + ): """Create a new function var. Args: @@ -226,7 +462,9 @@ def create( ) -JSON_STRINGIFY = FunctionStringVar.create("JSON.stringify") +JSON_STRINGIFY = FunctionStringVar.create( + "JSON.stringify", _var_type=ReflexCallable[[Any], str] +) ARRAY_ISARRAY = FunctionStringVar.create("Array.isArray") PROTOTYPE_TO_STRING = FunctionStringVar.create( "((__to_string) => __to_string.toString())" diff --git a/tests/units/test_var.py b/tests/units/test_var.py index 59447392131..4940246e79e 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -928,7 +928,7 @@ def test_function_var(): == '(((a, b) => ({ ["args"] : [a, b], ["result"] : a + b }))(1, 2))' ) - increment_func = addition_func(1) + increment_func = addition_func.partial(1) assert ( str(increment_func.call(2)) == "(((...args) => (((a, b) => a + b)(1, ...args)))(2))" From 9b06d684cde6dfe1c314ae5c4c7bb0b1048dd767 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 12 Nov 2024 16:03:40 -0800 Subject: [PATCH 02/86] import ParamSpec from typing_extensions --- reflex/vars/function.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index 28c11837235..0c354dc600b 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -4,19 +4,9 @@ import dataclasses import sys -from typing import ( - Any, - Generic, - Optional, - ParamSpec, - Sequence, - Tuple, - Type, - Union, - overload, -) +from typing import Any, Generic, Optional, Sequence, Tuple, Type, Union, overload -from typing_extensions import Callable, Concatenate, Protocol, TypeVar +from typing_extensions import Callable, Concatenate, ParamSpec, Protocol, TypeVar from reflex.utils import format from reflex.utils.types import GenericType From 7aa9245514a5db19373b387a1a50fda5c60ce353 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 12 Nov 2024 16:07:17 -0800 Subject: [PATCH 03/86] remove ellipsis as they are not supported in 3.9 --- reflex/vars/function.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index 0c354dc600b..fb9b0d769a9 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -35,7 +35,7 @@ class ReflexCallable(Protocol[P, R]): ) -class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[..., Any]): +class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]): """Base class for immutable function vars.""" @overload @@ -94,8 +94,8 @@ def partial( @overload def partial( - self: FunctionVar[ReflexCallable[..., R]], *args: Var | Any - ) -> FunctionVar[ReflexCallable[..., R]]: ... + self: FunctionVar[ReflexCallable[P, R]], *args: Var | Any + ) -> FunctionVar[ReflexCallable[P, R]]: ... @overload def partial(self, *args: Var | Any) -> FunctionVar: ... @@ -168,8 +168,11 @@ def call( @overload def call( - self: FunctionVar[ReflexCallable[..., R]], *args: Var | Any - ) -> VarOperationCall[..., R]: ... + self: FunctionVar[ReflexCallable[P, R]], *args: Var | Any + ) -> VarOperationCall[P, R]: ... + + @overload + def call(self, *args: Var | Any) -> Var: ... def call(self, *args: Var | Any) -> Var: # type: ignore """Call the function with the given arguments. @@ -186,7 +189,7 @@ def call(self, *args: Var | Any) -> Var: # type: ignore class BuilderFunctionVar( - FunctionVar[CALLABLE_TYPE], default_type=ReflexCallable[..., Any] + FunctionVar[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any] ): """Base class for immutable function vars with the builder pattern.""" @@ -200,7 +203,7 @@ class FunctionStringVar(FunctionVar[CALLABLE_TYPE]): def create( cls, func: str, - _var_type: Type[OTHER_CALLABLE_TYPE] = ReflexCallable[..., Any], + _var_type: Type[OTHER_CALLABLE_TYPE] = ReflexCallable[Any, Any], _var_data: VarData | None = None, ) -> FunctionStringVar[OTHER_CALLABLE_TYPE]: """Create a new function var from a string. From 48951dbabd4ca561063350df68b189673a2edefe Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 12 Nov 2024 16:11:30 -0800 Subject: [PATCH 04/86] try importing everything from extensions --- reflex/vars/function.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index fb9b0d769a9..8fb6cec1793 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -4,9 +4,22 @@ import dataclasses import sys -from typing import Any, Generic, Optional, Sequence, Tuple, Type, Union, overload -from typing_extensions import Callable, Concatenate, ParamSpec, Protocol, TypeVar +from typing_extensions import ( + Any, + Callable, + Concatenate, + Generic, + Optional, + ParamSpec, + Protocol, + Sequence, + Tuple, + Type, + TypeVar, + Union, + overload, +) from reflex.utils import format from reflex.utils.types import GenericType @@ -458,7 +471,10 @@ def create( JSON_STRINGIFY = FunctionStringVar.create( "JSON.stringify", _var_type=ReflexCallable[[Any], str] ) -ARRAY_ISARRAY = FunctionStringVar.create("Array.isArray") +ARRAY_ISARRAY = FunctionStringVar.create( + "Array.isArray", _var_type=ReflexCallable[[Any], bool] +) PROTOTYPE_TO_STRING = FunctionStringVar.create( - "((__to_string) => __to_string.toString())" + "((__to_string) => __to_string.toString())", + _var_type=ReflexCallable[[Any], str], ) From 7ada0ea5b976a51627aebe06c38a96ce3b7bfe4d Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 12 Nov 2024 16:23:06 -0800 Subject: [PATCH 05/86] special case 3.9 --- reflex/event.py | 3 +-- reflex/vars/function.py | 32 ++++++++++++++++++++++---------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/reflex/event.py b/reflex/event.py index 13167297140..312c9887f68 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -799,8 +799,7 @@ def scroll_to(elem_id: str, align_to_top: bool | Var[bool] = True) -> EventSpec: get_element_by_id = FunctionStringVar.create("document.getElementById") return run_script( - get_element_by_id(elem_id) - .call(elem_id) + get_element_by_id.call(elem_id) .to(ObjectVar) .scrollIntoView.to(FunctionVar) .call(align_to_top), diff --git a/reflex/vars/function.py b/reflex/vars/function.py index 8fb6cec1793..f600af5e0d4 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -468,13 +468,25 @@ def create( ) -JSON_STRINGIFY = FunctionStringVar.create( - "JSON.stringify", _var_type=ReflexCallable[[Any], str] -) -ARRAY_ISARRAY = FunctionStringVar.create( - "Array.isArray", _var_type=ReflexCallable[[Any], bool] -) -PROTOTYPE_TO_STRING = FunctionStringVar.create( - "((__to_string) => __to_string.toString())", - _var_type=ReflexCallable[[Any], str], -) +if python_version := sys.version_info[:2] >= (3, 10): + JSON_STRINGIFY = FunctionStringVar.create( + "JSON.stringify", _var_type=ReflexCallable[[Any], str] + ) + ARRAY_ISARRAY = FunctionStringVar.create( + "Array.isArray", _var_type=ReflexCallable[[Any], bool] + ) + PROTOTYPE_TO_STRING = FunctionStringVar.create( + "((__to_string) => __to_string.toString())", + _var_type=ReflexCallable[[Any], str], + ) +else: + JSON_STRINGIFY = FunctionStringVar.create( + "JSON.stringify", _var_type=ReflexCallable[Any, str] + ) + ARRAY_ISARRAY = FunctionStringVar.create( + "Array.isArray", _var_type=ReflexCallable[Any, bool] + ) + PROTOTYPE_TO_STRING = FunctionStringVar.create( + "((__to_string) => __to_string.toString())", + _var_type=ReflexCallable[Any, str], + ) From 6745d6cb9d7a60d65c40c8ca98639e53f7026c67 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 12 Nov 2024 16:35:04 -0800 Subject: [PATCH 06/86] don't use Any from extensions --- reflex/vars/function.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index f600af5e0d4..bc619f18de3 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -4,15 +4,10 @@ import dataclasses import sys - -from typing_extensions import ( +from typing import ( Any, Callable, - Concatenate, - Generic, Optional, - ParamSpec, - Protocol, Sequence, Tuple, Type, @@ -21,6 +16,8 @@ overload, ) +from typing_extensions import Concatenate, Generic, ParamSpec, Protocol + from reflex.utils import format from reflex.utils.types import GenericType From f9b24fe5bd896bbe271c321d72db22262b446e85 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 12 Nov 2024 16:38:30 -0800 Subject: [PATCH 07/86] get typevar from extensions --- reflex/vars/function.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index bc619f18de3..c65b38f707d 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -4,19 +4,9 @@ import dataclasses import sys -from typing import ( - Any, - Callable, - Optional, - Sequence, - Tuple, - Type, - TypeVar, - Union, - overload, -) +from typing import Any, Callable, Optional, Sequence, Tuple, Type, Union, overload -from typing_extensions import Concatenate, Generic, ParamSpec, Protocol +from typing_extensions import Concatenate, Generic, ParamSpec, Protocol, TypeVar from reflex.utils import format from reflex.utils.types import GenericType From 05bd41c04089db107226531e6508f9586d08d949 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 13 Nov 2024 13:22:01 -0800 Subject: [PATCH 08/86] add validation --- reflex/vars/function.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index c65b38f707d..2139071b950 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -109,6 +109,7 @@ def partial(self, *args: Var | Any) -> FunctionVar: # type: ignore Returns: The partially applied function. """ + self._pre_check(*args) if not args: return ArgsFunctionOperation.create((), self) return ArgsFunctionOperation.create( @@ -183,8 +184,20 @@ def call(self, *args: Var | Any) -> Var: # type: ignore Returns: The function call operation. """ + self._pre_check(*args) return VarOperationCall.create(self, *args).guess_type() + def _pre_check(self, *args: Var | Any) -> bool: + """Check if the function can be called with the given arguments. + + Args: + *args: The arguments to call the function with. + + Returns: + True if the function can be called with the given arguments. + """ + return True + __call__ = call @@ -354,6 +367,9 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar): """Base class for immutable function defined via arguments and return expression.""" _args: FunctionArgs = dataclasses.field(default_factory=FunctionArgs) + _validators: Tuple[Callable[[Any], bool], ...] = dataclasses.field( + default_factory=tuple + ) _return_expr: Union[Var, Any] = dataclasses.field(default=None) _explicit_return: bool = dataclasses.field(default=False) @@ -368,12 +384,27 @@ def _cached_var_name(self) -> str: self._args, self._return_expr, self._explicit_return ) + def _pre_check(self, *args: Var | Any) -> bool: + """Check if the function can be called with the given arguments. + + Args: + *args: The arguments to call the function with. + + Returns: + True if the function can be called with the given arguments. + """ + return all( + validator(arg) + for validator, arg in zip(self._validators, args, strict=False) + ) + @classmethod def create( cls, args_names: Sequence[Union[str, DestructuredArg]], return_expr: Var | Any, rest: str | None = None, + validators: Sequence[Callable[[Any], bool]] = (), explicit_return: bool = False, _var_type: GenericType = Callable, _var_data: VarData | None = None, @@ -395,6 +426,7 @@ def create( _var_type=_var_type, _var_data=_var_data, _args=FunctionArgs(args=tuple(args_names), rest=rest), + _validators=tuple(validators), _return_expr=return_expr, _explicit_return=explicit_return, ) From ebc81811c062e16eded036fe3f7a4b07b91dab1b Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 13 Nov 2024 13:51:47 -0800 Subject: [PATCH 09/86] fix silly mistakes --- reflex/vars/function.py | 77 +++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index 2139071b950..d719e5ced87 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -9,6 +9,7 @@ from typing_extensions import Concatenate, Generic, ParamSpec, Protocol, TypeVar from reflex.utils import format +from reflex.utils.exceptions import VarTypeError from reflex.utils.types import GenericType from .base import CachedVarOperation, LiteralVar, Var, VarData, cached_property_no_lock @@ -109,12 +110,22 @@ def partial(self, *args: Var | Any) -> FunctionVar: # type: ignore Returns: The partially applied function. """ - self._pre_check(*args) if not args: - return ArgsFunctionOperation.create((), self) + return self + remaining_validators = self._pre_check(*args) + if self.__call__ is self.partial: + # if the default behavior is partial, we should return a new partial function + return ArgsFunctionOperationBuilder.create( + (), + VarOperationCall.create(self, *args, Var(_js_expr="...args")), + rest="args", + validators=remaining_validators, + ) return ArgsFunctionOperation.create( - ("...args",), + (), VarOperationCall.create(self, *args, Var(_js_expr="...args")), + rest="args", + validators=remaining_validators, ) @overload @@ -187,7 +198,7 @@ def call(self, *args: Var | Any) -> Var: # type: ignore self._pre_check(*args) return VarOperationCall.create(self, *args).guess_type() - def _pre_check(self, *args: Var | Any) -> bool: + def _pre_check(self, *args: Var | Any) -> Tuple[Callable[[Any], bool], ...]: """Check if the function can be called with the given arguments. Args: @@ -196,7 +207,7 @@ def _pre_check(self, *args: Var | Any) -> bool: Returns: True if the function can be called with the given arguments. """ - return True + return tuple() __call__ = call @@ -346,7 +357,8 @@ def format_args_function_operation( """ arg_names_str = ", ".join( [arg if isinstance(arg, str) else arg.to_javascript() for arg in args.args] - ) + (f", ...{args.rest}" if args.rest else "") + + ([f"...{args.rest}"] if args.rest else []) + ) return_expr_str = str(LiteralVar.create(return_expr)) @@ -371,6 +383,7 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar): default_factory=tuple ) _return_expr: Union[Var, Any] = dataclasses.field(default=None) + _function_name: str = dataclasses.field(default="") _explicit_return: bool = dataclasses.field(default=False) @cached_property_no_lock @@ -384,7 +397,7 @@ def _cached_var_name(self) -> str: self._args, self._return_expr, self._explicit_return ) - def _pre_check(self, *args: Var | Any) -> bool: + def _pre_check(self, *args: Var | Any) -> Tuple[Callable[[Any], bool], ...]: """Check if the function can be called with the given arguments. Args: @@ -393,10 +406,17 @@ def _pre_check(self, *args: Var | Any) -> bool: Returns: True if the function can be called with the given arguments. """ - return all( - validator(arg) - for validator, arg in zip(self._validators, args, strict=False) - ) + for i, (validator, arg) in enumerate(zip(self._validators, args)): + if not validator(arg): + arg_name = self._args.args[i] if i < len(self._args.args) else None + if arg_name is not None: + raise VarTypeError( + f"Invalid argument {str(arg)} provided to {arg_name} in {self._function_name or 'var operation'}" + ) + raise VarTypeError( + f"Invalid argument {str(arg)} provided to argument {i} in {self._function_name or 'var operation'}" + ) + return self._validators[len(args) :] @classmethod def create( @@ -405,6 +425,7 @@ def create( return_expr: Var | Any, rest: str | None = None, validators: Sequence[Callable[[Any], bool]] = (), + function_name: str = "", explicit_return: bool = False, _var_type: GenericType = Callable, _var_data: VarData | None = None, @@ -415,6 +436,8 @@ def create( args_names: The names of the arguments. return_expr: The return expression of the function. rest: The name of the rest argument. + validators: The validators for the arguments. + function_name: The name of the function. explicit_return: Whether to use explicit return syntax. _var_data: Additional hooks and imports associated with the Var. @@ -426,6 +449,7 @@ def create( _var_type=_var_type, _var_data=_var_data, _args=FunctionArgs(args=tuple(args_names), rest=rest), + _function_name=function_name, _validators=tuple(validators), _return_expr=return_expr, _explicit_return=explicit_return, @@ -441,7 +465,11 @@ class ArgsFunctionOperationBuilder(CachedVarOperation, BuilderFunctionVar): """Base class for immutable function defined via arguments and return expression with the builder pattern.""" _args: FunctionArgs = dataclasses.field(default_factory=FunctionArgs) + _validators: Tuple[Callable[[Any], bool], ...] = dataclasses.field( + default_factory=tuple + ) _return_expr: Union[Var, Any] = dataclasses.field(default=None) + _function_name: str = dataclasses.field(default="") _explicit_return: bool = dataclasses.field(default=False) @cached_property_no_lock @@ -455,12 +483,35 @@ def _cached_var_name(self) -> str: self._args, self._return_expr, self._explicit_return ) + def _pre_check(self, *args: Var | Any) -> Tuple[Callable[[Any], bool], ...]: + """Check if the function can be called with the given arguments. + + Args: + *args: The arguments to call the function with. + + Returns: + True if the function can be called with the given arguments. + """ + for i, (validator, arg) in enumerate(zip(self._validators, args)): + if not validator(arg): + arg_name = self._args.args[i] if i < len(self._args.args) else None + if arg_name is not None: + raise VarTypeError( + f"Invalid argument {str(arg)} provided to {arg_name} in {self._function_name or 'var operation'}" + ) + raise VarTypeError( + f"Invalid argument {str(arg)} provided to argument {i} in {self._function_name or 'var operation'}" + ) + return self._validators[len(args) :] + @classmethod def create( cls, args_names: Sequence[Union[str, DestructuredArg]], return_expr: Var | Any, rest: str | None = None, + validators: Sequence[Callable[[Any], bool]] = (), + function_name: str = "", explicit_return: bool = False, _var_type: GenericType = Callable, _var_data: VarData | None = None, @@ -471,6 +522,8 @@ def create( args_names: The names of the arguments. return_expr: The return expression of the function. rest: The name of the rest argument. + validators: The validators for the arguments. + function_name: The name of the function. explicit_return: Whether to use explicit return syntax. _var_data: Additional hooks and imports associated with the Var. @@ -482,6 +535,8 @@ def create( _var_type=_var_type, _var_data=_var_data, _args=FunctionArgs(args=tuple(args_names), rest=rest), + _function_name=function_name, + _validators=tuple(validators), _return_expr=return_expr, _explicit_return=explicit_return, ) From f4aa1f58c386a5e59bcc4bb9837093e7d3977438 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 13 Nov 2024 18:17:53 -0800 Subject: [PATCH 10/86] implement type computers --- reflex/vars/base.py | 440 +++++++++++++++++++++++++++++----------- reflex/vars/function.py | 232 ++++++++++++++------- reflex/vars/number.py | 111 +++++----- reflex/vars/object.py | 43 ++-- reflex/vars/sequence.py | 186 +++++++++++------ tests/units/test_var.py | 4 +- 6 files changed, 699 insertions(+), 317 deletions(-) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 200f693defd..1e6d4163ed3 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -14,7 +14,7 @@ import string import sys import warnings -from types import CodeType, FunctionType +from types import CodeType, EllipsisType, FunctionType from typing import ( TYPE_CHECKING, Any, @@ -26,7 +26,6 @@ Iterable, List, Literal, - NoReturn, Optional, Set, Tuple, @@ -38,7 +37,14 @@ overload, ) -from typing_extensions import ParamSpec, TypeGuard, deprecated, get_type_hints, override +from typing_extensions import ( + ParamSpec, + Protocol, + TypeGuard, + deprecated, + get_type_hints, + override, +) from reflex import constants from reflex.base import Base @@ -69,6 +75,7 @@ if TYPE_CHECKING: from reflex.state import BaseState + from .function import ArgsFunctionOperation, ReflexCallable from .number import BooleanVar, NumberVar from .object import ObjectVar from .sequence import ArrayVar, StringVar @@ -79,6 +86,36 @@ warnings.filterwarnings("ignore", message="fields may not start with an underscore") +P = ParamSpec("P") +R = TypeVar("R") + + +class ReflexCallable(Protocol[P, R]): + """Protocol for a callable.""" + + __call__: Callable[P, R] + + +def unwrap_reflex_callalbe( + callable_type: GenericType, +) -> Tuple[Union[EllipsisType, Tuple[GenericType, ...]], GenericType]: + """Unwrap the ReflexCallable type. + + Args: + callable_type: The ReflexCallable type to unwrap. + + Returns: + The unwrapped ReflexCallable type. + """ + if callable_type is ReflexCallable: + return Ellipsis, Any + if get_origin(callable_type) is not ReflexCallable: + return Ellipsis, Any + args = get_args(callable_type) + if not args or len(args) != 2: + return Ellipsis, Any + return args + @dataclasses.dataclass( eq=False, @@ -409,9 +446,11 @@ def __post_init__(self): if _var_data or _js_expr != self._js_expr: self.__init__( - _js_expr=_js_expr, - _var_type=self._var_type, - _var_data=VarData.merge(self._var_data, _var_data), + **{ + **dataclasses.asdict(self), + "_js_expr": _js_expr, + "_var_data": VarData.merge(self._var_data, _var_data), + } ) def __hash__(self) -> int: @@ -690,6 +729,12 @@ def guess_type(self: Var[bool]) -> BooleanVar: ... @overload def guess_type(self: Var[int] | Var[float] | Var[int | float]) -> NumberVar: ... + @overload + def guess_type(self: Var[list] | Var[tuple] | Var[set]) -> ArrayVar: ... + + @overload + def guess_type(self: Var[dict]) -> ObjectVar[dict]: ... + @overload def guess_type(self) -> Self: ... @@ -1413,71 +1458,94 @@ def get_python_literal(value: Union[LiteralVar, Any]) -> Any | None: return value +def validate_arg(type_hint: GenericType) -> Callable[[Any], bool]: + """Create a validator for an argument. + + Args: + type_hint: The type hint of the argument. + + Returns: + The validator. + """ + + def validate(value: Any): + return True + + return validate + + P = ParamSpec("P") T = TypeVar("T") +V1 = TypeVar("V1") +V2 = TypeVar("V2") +V3 = TypeVar("V3") +V4 = TypeVar("V4") +V5 = TypeVar("V5") -# NoReturn is used to match CustomVarOperationReturn with no type hint. -@overload -def var_operation( - func: Callable[P, CustomVarOperationReturn[NoReturn]], -) -> Callable[P, Var]: ... - +class TypeComputer(Protocol): + """A protocol for type computers.""" -@overload -def var_operation( - func: Callable[P, CustomVarOperationReturn[bool]], -) -> Callable[P, BooleanVar]: ... + def __call__(self, *args: Var) -> Tuple[GenericType, Union[TypeComputer, None]]: + """Compute the type of the operation. + Args: + *args: The arguments to compute the type of. -NUMBER_T = TypeVar("NUMBER_T", int, float, Union[int, float]) + Returns: + The type of the operation. + """ + ... @overload def var_operation( - func: Callable[P, CustomVarOperationReturn[NUMBER_T]], -) -> Callable[P, NumberVar[NUMBER_T]]: ... + func: Callable[[], CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation[ReflexCallable[[], T]]: ... @overload def var_operation( - func: Callable[P, CustomVarOperationReturn[str]], -) -> Callable[P, StringVar]: ... - - -LIST_T = TypeVar("LIST_T", bound=Union[List[Any], Tuple, Set]) + func: Callable[[Var[V1]], CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation[ReflexCallable[[V1], T]]: ... @overload def var_operation( - func: Callable[P, CustomVarOperationReturn[LIST_T]], -) -> Callable[P, ArrayVar[LIST_T]]: ... + func: Callable[[Var[V1], Var[V2]], CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation[ReflexCallable[[V1, V2], T]]: ... -OBJECT_TYPE = TypeVar("OBJECT_TYPE", bound=Dict) +@overload +def var_operation( + func: Callable[[Var[V1], Var[V2], Var[V3]], CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation[ReflexCallable[[V1, V2, V3], T]]: ... @overload def var_operation( - func: Callable[P, CustomVarOperationReturn[OBJECT_TYPE]], -) -> Callable[P, ObjectVar[OBJECT_TYPE]]: ... + func: Callable[[Var[V1], Var[V2], Var[V3], Var[V4]], CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation[ReflexCallable[[V1, V2, V3, V4], T]]: ... @overload def var_operation( - func: Callable[P, CustomVarOperationReturn[T]], -) -> Callable[P, Var[T]]: ... + func: Callable[ + [Var[V1], Var[V2], Var[V3], Var[V4], Var[V5]], + CustomVarOperationReturn[T], + ], +) -> ArgsFunctionOperation[ReflexCallable[[V1, V2, V3, V4, V5], T]]: ... def var_operation( - func: Callable[P, CustomVarOperationReturn[T]], -) -> Callable[P, Var[T]]: + func: Callable[..., CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation: """Decorator for creating a var operation. Example: ```python @var_operation - def add(a: NumberVar, b: NumberVar): + def add(a: Var[int], b: Var[int]): return custom_var_operation(f"{a} + {b}") ``` @@ -1487,26 +1555,61 @@ def add(a: NumberVar, b: NumberVar): Returns: The decorated function. """ + from .function import ArgsFunctionOperation, ReflexCallable - @functools.wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> Var[T]: - func_args = list(inspect.signature(func).parameters) - args_vars = { - func_args[i]: (LiteralVar.create(arg) if not isinstance(arg, Var) else arg) - for i, arg in enumerate(args) - } - kwargs_vars = { - key: LiteralVar.create(value) if not isinstance(value, Var) else value - for key, value in kwargs.items() - } - - return CustomVarOperation.create( - name=func.__name__, - args=tuple(list(args_vars.items()) + list(kwargs_vars.items())), - return_var=func(*args_vars.values(), **kwargs_vars), # type: ignore - ).guess_type() + func_name = func.__name__ - return wrapper + func_arg_spec = inspect.getfullargspec(func) + + if func_arg_spec.kwonlyargs: + raise TypeError(f"Function {func_name} cannot have keyword-only arguments.") + if func_arg_spec.varargs: + raise TypeError(f"Function {func_name} cannot have variable arguments.") + + arg_names = func_arg_spec.args + + type_hints = get_type_hints(func) + + if not all( + (get_origin((type_hint := type_hints.get(arg_name, Any))) or type_hint) is Var + and len(get_args(type_hint)) <= 1 + for arg_name in arg_names + ): + raise TypeError( + f"Function {func_name} must have type hints of the form `Var[Type]`." + ) + + args_with_type_hints = tuple( + (arg_name, (args[0] if (args := get_args(type_hints[arg_name])) else Any)) + for arg_name in arg_names + ) + + arg_vars = tuple( + ( + Var("_" + arg_name, _var_type=arg_python_type) + if not isinstance(arg_python_type, TypeVar) + else Var("_" + arg_name) + ) + for arg_name, arg_python_type in args_with_type_hints + ) + + custom_operation_return = func(*arg_vars) + + args_operation = ArgsFunctionOperation.create( + tuple(map(str, arg_vars)), + custom_operation_return, + validators=tuple( + validate_arg(type_hints.get(arg_name, Any)) for arg_name in arg_names + ), + function_name=func_name, + type_computer=custom_operation_return._type_computer, + _var_type=ReflexCallable[ + tuple(arg_python_type for _, arg_python_type in args_with_type_hints), + custom_operation_return._var_type, + ], + ) + + return args_operation def figure_out_type(value: Any) -> types.GenericType: @@ -1621,66 +1724,6 @@ def __hash__(self) -> int: ) -def and_operation(a: Var | Any, b: Var | Any) -> Var: - """Perform a logical AND operation on two variables. - - Args: - a: The first variable. - b: The second variable. - - Returns: - The result of the logical AND operation. - """ - return _and_operation(a, b) # type: ignore - - -@var_operation -def _and_operation(a: Var, b: Var): - """Perform a logical AND operation on two variables. - - Args: - a: The first variable. - b: The second variable. - - Returns: - The result of the logical AND operation. - """ - return var_operation_return( - js_expression=f"({a} && {b})", - var_type=unionize(a._var_type, b._var_type), - ) - - -def or_operation(a: Var | Any, b: Var | Any) -> Var: - """Perform a logical OR operation on two variables. - - Args: - a: The first variable. - b: The second variable. - - Returns: - The result of the logical OR operation. - """ - return _or_operation(a, b) # type: ignore - - -@var_operation -def _or_operation(a: Var, b: Var): - """Perform a logical OR operation on two variables. - - Args: - a: The first variable. - b: The second variable. - - Returns: - The result of the logical OR operation. - """ - return var_operation_return( - js_expression=f"({a} || {b})", - var_type=unionize(a._var_type, b._var_type), - ) - - @dataclasses.dataclass( eq=False, frozen=True, @@ -2289,14 +2332,22 @@ def wrapper(fget: Callable[[BASE_STATE], Any]) -> ComputedVar: RETURN = TypeVar("RETURN") +@dataclasses.dataclass( + eq=False, + frozen=True, + **{"slots": True} if sys.version_info >= (3, 10) else {}, +) class CustomVarOperationReturn(Var[RETURN]): """Base class for custom var operations.""" + _type_computer: Optional[TypeComputer] = dataclasses.field(default=None) + @classmethod def create( cls, js_expression: str, _var_type: Type[RETURN] | None = None, + _type_computer: Optional[TypeComputer] = None, _var_data: VarData | None = None, ) -> CustomVarOperationReturn[RETURN]: """Create a CustomVarOperation. @@ -2304,6 +2355,7 @@ def create( Args: js_expression: The JavaScript expression to evaluate. _var_type: The type of the var. + _type_computer: A function to compute the type of the var given the arguments. _var_data: Additional hooks and imports associated with the Var. Returns: @@ -2312,6 +2364,7 @@ def create( return CustomVarOperationReturn( _js_expr=js_expression, _var_type=_var_type or Any, + _type_computer=_type_computer, _var_data=_var_data, ) @@ -2319,6 +2372,7 @@ def create( def var_operation_return( js_expression: str, var_type: Type[RETURN] | None = None, + type_computer: Optional[TypeComputer] = None, var_data: VarData | None = None, ) -> CustomVarOperationReturn[RETURN]: """Shortcut for creating a CustomVarOperationReturn. @@ -2326,15 +2380,17 @@ def var_operation_return( Args: js_expression: The JavaScript expression to evaluate. var_type: The type of the var. + type_computer: A function to compute the type of the var given the arguments. var_data: Additional hooks and imports associated with the Var. Returns: The CustomVarOperationReturn. """ return CustomVarOperationReturn.create( - js_expression, - var_type, - var_data, + js_expression=js_expression, + _var_type=var_type, + _type_computer=type_computer, + _var_data=var_data, ) @@ -2942,3 +2998,157 @@ def field(value: T) -> Field[T]: The Field. """ return value # type: ignore + + +def and_operation(a: Var | Any, b: Var | Any) -> Var: + """Perform a logical AND operation on two variables. + + Args: + a: The first variable. + b: The second variable. + + Returns: + The result of the logical AND operation. + """ + return _and_operation(a, b) # type: ignore + + +@var_operation +def _and_operation(a: Var, b: Var): + """Perform a logical AND operation on two variables. + + Args: + a: The first variable. + b: The second variable. + + Returns: + The result of the logical AND operation. + """ + + def type_computer(*args: Var): + if not args: + return (ReflexCallable[[Any, Any], Any], type_computer) + if len(args) == 1: + return ( + ReflexCallable[[Any], Any], + functools.partial(type_computer, args[0]), + ) + return ( + ReflexCallable[[], unionize(args[0]._var_type, args[1]._var_type)], + None, + ) + + return var_operation_return( + js_expression=f"({a} && {b})", + type_computer=type_computer, + ) + + +def or_operation(a: Var | Any, b: Var | Any) -> Var: + """Perform a logical OR operation on two variables. + + Args: + a: The first variable. + b: The second variable. + + Returns: + The result of the logical OR operation. + """ + return _or_operation(a, b) # type: ignore + + +@var_operation +def _or_operation(a: Var, b: Var): + """Perform a logical OR operation on two variables. + + Args: + a: The first variable. + b: The second variable. + + Returns: + The result of the logical OR operation. + """ + + def type_computer(*args: Var): + if not args: + return (ReflexCallable[[Any, Any], Any], type_computer) + if len(args) == 1: + return ( + ReflexCallable[[Any], Any], + functools.partial(type_computer, args[0]), + ) + return ( + ReflexCallable[[], unionize(args[0]._var_type, args[1]._var_type)], + None, + ) + + return var_operation_return( + js_expression=f"({a} || {b})", + type_computer=type_computer, + ) + + +def passthrough_unary_type_computer(no_args: GenericType) -> TypeComputer: + """Create a type computer for unary operations. + + Args: + no_args: The type to return when no arguments are provided. + + Returns: + The type computer. + """ + + def type_computer(*args: Var): + if not args: + return (no_args, type_computer) + return (ReflexCallable[[], args[0]._var_type], None) + + return type_computer + + +def unary_type_computer( + no_args: GenericType, computer: Callable[[Var], GenericType] +) -> TypeComputer: + """Create a type computer for unary operations. + + Args: + no_args: The type to return when no arguments are provided. + computer: The function to compute the type. + + Returns: + The type computer. + """ + + def type_computer(*args: Var): + if not args: + return (no_args, type_computer) + return (ReflexCallable[[], computer(args[0])], None) + + return type_computer + + +def nary_type_computer( + *types: GenericType, computer: Callable[..., GenericType] +) -> TypeComputer: + """Create a type computer for n-ary operations. + + Args: + types: The types to return when no arguments are provided. + computer: The function to compute the type. + + Returns: + The type computer. + """ + + def type_computer(*args: Var): + if len(args) != len(types): + return ( + ReflexCallable[[], types[len(args)]], + functools.partial(type_computer, *args), + ) + return ( + ReflexCallable[[], computer(args)], + None, + ) + + return type_computer diff --git a/reflex/vars/function.py b/reflex/vars/function.py index d719e5ced87..ebe3eba5c9c 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -6,28 +6,31 @@ import sys from typing import Any, Callable, Optional, Sequence, Tuple, Type, Union, overload -from typing_extensions import Concatenate, Generic, ParamSpec, Protocol, TypeVar +from typing_extensions import Concatenate, Generic, ParamSpec, TypeVar from reflex.utils import format from reflex.utils.exceptions import VarTypeError from reflex.utils.types import GenericType -from .base import CachedVarOperation, LiteralVar, Var, VarData, cached_property_no_lock +from .base import ( + CachedVarOperation, + LiteralVar, + ReflexCallable, + TypeComputer, + Var, + VarData, + cached_property_no_lock, + unwrap_reflex_callalbe, +) P = ParamSpec("P") +R = TypeVar("R") V1 = TypeVar("V1") V2 = TypeVar("V2") V3 = TypeVar("V3") V4 = TypeVar("V4") V5 = TypeVar("V5") V6 = TypeVar("V6") -R = TypeVar("R") - - -class ReflexCallable(Protocol[P, R]): - """Protocol for a callable.""" - - __call__: Callable[P, R] CALLABLE_TYPE = TypeVar("CALLABLE_TYPE", bound=ReflexCallable, infer_variance=True) @@ -112,20 +115,37 @@ def partial(self, *args: Var | Any) -> FunctionVar: # type: ignore """ if not args: return self + + args = tuple(map(LiteralVar.create, args)) + remaining_validators = self._pre_check(*args) + + partial_types, type_computer = self._partial_type(*args) + if self.__call__ is self.partial: # if the default behavior is partial, we should return a new partial function return ArgsFunctionOperationBuilder.create( (), - VarOperationCall.create(self, *args, Var(_js_expr="...args")), + VarOperationCall.create( + self, + *args, + Var(_js_expr="...args"), + _var_type=self._return_type(*args), + ), rest="args", validators=remaining_validators, + type_computer=type_computer, + _var_type=partial_types, ) return ArgsFunctionOperation.create( (), - VarOperationCall.create(self, *args, Var(_js_expr="...args")), + VarOperationCall.create( + self, *args, Var(_js_expr="...args"), _var_type=self._return_type(*args) + ), rest="args", validators=remaining_validators, + type_computer=type_computer, + _var_type=partial_types, ) @overload @@ -194,9 +214,56 @@ def call(self, *args: Var | Any) -> Var: # type: ignore Returns: The function call operation. + + Raises: + VarTypeError: If the number of arguments is invalid """ + arg_len = self._arg_len() + if arg_len is not None and len(args) != arg_len: + raise VarTypeError(f"Invalid number of arguments provided to {str(self)}") + args = tuple(map(LiteralVar.create, args)) self._pre_check(*args) - return VarOperationCall.create(self, *args).guess_type() + return_type = self._return_type(*args) + return VarOperationCall.create(self, *args, _var_type=return_type).guess_type() + + def _partial_type( + self, *args: Var | Any + ) -> Tuple[GenericType, Optional[TypeComputer]]: + """Override the type of the function call with the given arguments. + + Args: + *args: The arguments to call the function with. + + Returns: + The overridden type of the function call. + """ + args_types, return_type = unwrap_reflex_callalbe(self._var_type) + if isinstance(args_types, tuple): + return ReflexCallable[[*args_types[len(args) :]], return_type], None + return ReflexCallable[..., return_type], None + + def _arg_len(self) -> int | None: + """Get the number of arguments the function takes. + + Returns: + The number of arguments the function takes. + """ + args_types, _ = unwrap_reflex_callalbe(self._var_type) + if isinstance(args_types, tuple): + return len(args_types) + return None + + def _return_type(self, *args: Var | Any) -> GenericType: + """Override the type of the function call with the given arguments. + + Args: + *args: The arguments to call the function with. + + Returns: + The overridden type of the function call. + """ + partial_types, _ = self._partial_type(*args) + return unwrap_reflex_callalbe(partial_types)[1] def _pre_check(self, *args: Var | Any) -> Tuple[Callable[[Any], bool], ...]: """Check if the function can be called with the given arguments. @@ -343,11 +410,12 @@ class FunctionArgs: def format_args_function_operation( - args: FunctionArgs, return_expr: Var | Any, explicit_return: bool + self: ArgsFunctionOperation | ArgsFunctionOperationBuilder, ) -> str: """Format an args function operation. Args: + self: The function operation. args: The function arguments. return_expr: The return expression. explicit_return: Whether to use explicit return syntax. @@ -356,26 +424,76 @@ def format_args_function_operation( The formatted args function operation. """ arg_names_str = ", ".join( - [arg if isinstance(arg, str) else arg.to_javascript() for arg in args.args] - + ([f"...{args.rest}"] if args.rest else []) + [ + arg if isinstance(arg, str) else arg.to_javascript() + for arg in self._args.args + ] + + ([f"...{self._args.rest}"] if self._args.rest else []) ) - return_expr_str = str(LiteralVar.create(return_expr)) + return_expr_str = str(LiteralVar.create(self._return_expr)) # Wrap return expression in curly braces if explicit return syntax is used. return_expr_str_wrapped = ( - format.wrap(return_expr_str, "{", "}") if explicit_return else return_expr_str + format.wrap(return_expr_str, "{", "}") + if self._explicit_return + else return_expr_str ) return f"(({arg_names_str}) => {return_expr_str_wrapped})" +def pre_check_args( + self: ArgsFunctionOperation | ArgsFunctionOperationBuilder, *args: Var | Any +) -> Tuple[Callable[[Any], bool], ...]: + """Check if the function can be called with the given arguments. + + Args: + self: The function operation. + *args: The arguments to call the function with. + + Returns: + True if the function can be called with the given arguments. + """ + for i, (validator, arg) in enumerate(zip(self._validators, args)): + if not validator(arg): + arg_name = self._args.args[i] if i < len(self._args.args) else None + if arg_name is not None: + raise VarTypeError( + f"Invalid argument {str(arg)} provided to {arg_name} in {self._function_name or 'var operation'}" + ) + raise VarTypeError( + f"Invalid argument {str(arg)} provided to argument {i} in {self._function_name or 'var operation'}" + ) + return self._validators[len(args) :] + + +def figure_partial_type( + self: ArgsFunctionOperation | ArgsFunctionOperationBuilder, + *args: Var | Any, +) -> Tuple[GenericType, Optional[TypeComputer]]: + """Figure out the return type of the function. + + Args: + self: The function operation. + *args: The arguments to call the function with. + + Returns: + The return type of the function. + """ + return ( + self._type_computer(*args) + if self._type_computer is not None + else FunctionVar._partial_type(self, *args) + ) + + @dataclasses.dataclass( eq=False, frozen=True, **{"slots": True} if sys.version_info >= (3, 10) else {}, ) -class ArgsFunctionOperation(CachedVarOperation, FunctionVar): +class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]): """Base class for immutable function defined via arguments and return expression.""" _args: FunctionArgs = dataclasses.field(default_factory=FunctionArgs) @@ -384,39 +502,14 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar): ) _return_expr: Union[Var, Any] = dataclasses.field(default=None) _function_name: str = dataclasses.field(default="") + _type_computer: Optional[TypeComputer] = dataclasses.field(default=None) _explicit_return: bool = dataclasses.field(default=False) - @cached_property_no_lock - def _cached_var_name(self) -> str: - """The name of the var. + _cached_var_name = cached_property_no_lock(format_args_function_operation) - Returns: - The name of the var. - """ - return format_args_function_operation( - self._args, self._return_expr, self._explicit_return - ) - - def _pre_check(self, *args: Var | Any) -> Tuple[Callable[[Any], bool], ...]: - """Check if the function can be called with the given arguments. - - Args: - *args: The arguments to call the function with. + _pre_check = pre_check_args - Returns: - True if the function can be called with the given arguments. - """ - for i, (validator, arg) in enumerate(zip(self._validators, args)): - if not validator(arg): - arg_name = self._args.args[i] if i < len(self._args.args) else None - if arg_name is not None: - raise VarTypeError( - f"Invalid argument {str(arg)} provided to {arg_name} in {self._function_name or 'var operation'}" - ) - raise VarTypeError( - f"Invalid argument {str(arg)} provided to argument {i} in {self._function_name or 'var operation'}" - ) - return self._validators[len(args) :] + _partial_type = figure_partial_type @classmethod def create( @@ -427,6 +520,7 @@ def create( validators: Sequence[Callable[[Any], bool]] = (), function_name: str = "", explicit_return: bool = False, + type_computer: Optional[TypeComputer] = None, _var_type: GenericType = Callable, _var_data: VarData | None = None, ): @@ -439,6 +533,8 @@ def create( validators: The validators for the arguments. function_name: The name of the function. explicit_return: Whether to use explicit return syntax. + type_computer: A function to compute the return type. + _var_type: The type of the var. _var_data: Additional hooks and imports associated with the Var. Returns: @@ -453,6 +549,7 @@ def create( _validators=tuple(validators), _return_expr=return_expr, _explicit_return=explicit_return, + _type_computer=type_computer, ) @@ -461,7 +558,9 @@ def create( frozen=True, **{"slots": True} if sys.version_info >= (3, 10) else {}, ) -class ArgsFunctionOperationBuilder(CachedVarOperation, BuilderFunctionVar): +class ArgsFunctionOperationBuilder( + CachedVarOperation, BuilderFunctionVar[CALLABLE_TYPE] +): """Base class for immutable function defined via arguments and return expression with the builder pattern.""" _args: FunctionArgs = dataclasses.field(default_factory=FunctionArgs) @@ -470,39 +569,14 @@ class ArgsFunctionOperationBuilder(CachedVarOperation, BuilderFunctionVar): ) _return_expr: Union[Var, Any] = dataclasses.field(default=None) _function_name: str = dataclasses.field(default="") + _type_computer: Optional[TypeComputer] = dataclasses.field(default=None) _explicit_return: bool = dataclasses.field(default=False) - @cached_property_no_lock - def _cached_var_name(self) -> str: - """The name of the var. + _cached_var_name = cached_property_no_lock(format_args_function_operation) - Returns: - The name of the var. - """ - return format_args_function_operation( - self._args, self._return_expr, self._explicit_return - ) + _pre_check = pre_check_args - def _pre_check(self, *args: Var | Any) -> Tuple[Callable[[Any], bool], ...]: - """Check if the function can be called with the given arguments. - - Args: - *args: The arguments to call the function with. - - Returns: - True if the function can be called with the given arguments. - """ - for i, (validator, arg) in enumerate(zip(self._validators, args)): - if not validator(arg): - arg_name = self._args.args[i] if i < len(self._args.args) else None - if arg_name is not None: - raise VarTypeError( - f"Invalid argument {str(arg)} provided to {arg_name} in {self._function_name or 'var operation'}" - ) - raise VarTypeError( - f"Invalid argument {str(arg)} provided to argument {i} in {self._function_name or 'var operation'}" - ) - return self._validators[len(args) :] + _partial_type = figure_partial_type @classmethod def create( @@ -513,6 +587,7 @@ def create( validators: Sequence[Callable[[Any], bool]] = (), function_name: str = "", explicit_return: bool = False, + type_computer: Optional[TypeComputer] = None, _var_type: GenericType = Callable, _var_data: VarData | None = None, ): @@ -525,6 +600,8 @@ def create( validators: The validators for the arguments. function_name: The name of the function. explicit_return: Whether to use explicit return syntax. + type_computer: A function to compute the return type. + _var_type: The type of the var. _var_data: Additional hooks and imports associated with the Var. Returns: @@ -539,6 +616,7 @@ def create( _validators=tuple(validators), _return_expr=return_expr, _explicit_return=explicit_return, + _type_computer=type_computer, ) diff --git a/reflex/vars/number.py b/reflex/vars/number.py index a762796e2e5..0f2cb4e46e3 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -3,19 +3,11 @@ from __future__ import annotations import dataclasses +import functools import json import math import sys -from typing import ( - TYPE_CHECKING, - Any, - Callable, - NoReturn, - Type, - TypeVar, - Union, - overload, -) +from typing import TYPE_CHECKING, Any, Callable, NoReturn, TypeVar, Union, overload from reflex.constants.base import Dirs from reflex.utils.exceptions import PrimitiveUnserializableToJSON, VarTypeError @@ -25,8 +17,11 @@ from .base import ( CustomVarOperationReturn, LiteralVar, + ReflexCallable, Var, VarData, + nary_type_computer, + passthrough_unary_type_computer, unionize, var_operation, var_operation_return, @@ -544,8 +539,8 @@ def _is_strict_int(self) -> bool: def binary_number_operation( - func: Callable[[NumberVar, NumberVar], str], -) -> Callable[[number_types, number_types], NumberVar]: + func: Callable[[Var[int | float], Var[int | float]], str], +): """Decorator to create a binary number operation. Args: @@ -555,30 +550,37 @@ def binary_number_operation( The binary number operation. """ - @var_operation - def operation(lhs: NumberVar, rhs: NumberVar): + def operation( + lhs: Var[int | float], rhs: Var[int | float] + ) -> CustomVarOperationReturn[int | float]: + def type_computer(*args: Var): + if not args: + return ( + ReflexCallable[[int | float, int | float], int | float], + type_computer, + ) + if len(args) == 1: + return ( + ReflexCallable[[int | float], int | float], + functools.partial(type_computer, args[0]), + ) + return ( + ReflexCallable[[], unionize(args[0]._var_type, args[1]._var_type)], + None, + ) + return var_operation_return( js_expression=func(lhs, rhs), - var_type=unionize(lhs._var_type, rhs._var_type), + type_computer=type_computer, ) - def wrapper(lhs: number_types, rhs: number_types) -> NumberVar: - """Create the binary number operation. - - Args: - lhs: The first number. - rhs: The second number. - - Returns: - The binary number operation. - """ - return operation(lhs, rhs) # type: ignore + operation.__name__ = func.__name__ - return wrapper + return var_operation(operation) @binary_number_operation -def number_add_operation(lhs: NumberVar, rhs: NumberVar): +def number_add_operation(lhs: Var[int | float], rhs: Var[int | float]): """Add two numbers. Args: @@ -592,7 +594,7 @@ def number_add_operation(lhs: NumberVar, rhs: NumberVar): @binary_number_operation -def number_subtract_operation(lhs: NumberVar, rhs: NumberVar): +def number_subtract_operation(lhs: Var[int | float], rhs: Var[int | float]): """Subtract two numbers. Args: @@ -605,8 +607,15 @@ def number_subtract_operation(lhs: NumberVar, rhs: NumberVar): return f"({lhs} - {rhs})" +unary_operation_type_computer = passthrough_unary_type_computer( + ReflexCallable[[int | float], int | float] +) + + @var_operation -def number_abs_operation(value: NumberVar): +def number_abs_operation( + value: Var[int | float], +) -> CustomVarOperationReturn[int | float]: """Get the absolute value of the number. Args: @@ -616,12 +625,12 @@ def number_abs_operation(value: NumberVar): The number absolute operation. """ return var_operation_return( - js_expression=f"Math.abs({value})", var_type=value._var_type + js_expression=f"Math.abs({value})", type_computer=unary_operation_type_computer ) @binary_number_operation -def number_multiply_operation(lhs: NumberVar, rhs: NumberVar): +def number_multiply_operation(lhs: Var[int | float], rhs: Var[int | float]): """Multiply two numbers. Args: @@ -636,7 +645,7 @@ def number_multiply_operation(lhs: NumberVar, rhs: NumberVar): @var_operation def number_negate_operation( - value: NumberVar[NUMBER_T], + value: Var[NUMBER_T], ) -> CustomVarOperationReturn[NUMBER_T]: """Negate the number. @@ -646,11 +655,13 @@ def number_negate_operation( Returns: The number negation operation. """ - return var_operation_return(js_expression=f"-({value})", var_type=value._var_type) + return var_operation_return( + js_expression=f"-({value})", type_computer=unary_operation_type_computer + ) @binary_number_operation -def number_true_division_operation(lhs: NumberVar, rhs: NumberVar): +def number_true_division_operation(lhs: Var[int | float], rhs: Var[int | float]): """Divide two numbers. Args: @@ -664,7 +675,7 @@ def number_true_division_operation(lhs: NumberVar, rhs: NumberVar): @binary_number_operation -def number_floor_division_operation(lhs: NumberVar, rhs: NumberVar): +def number_floor_division_operation(lhs: Var[int | float], rhs: Var[int | float]): """Floor divide two numbers. Args: @@ -678,7 +689,7 @@ def number_floor_division_operation(lhs: NumberVar, rhs: NumberVar): @binary_number_operation -def number_modulo_operation(lhs: NumberVar, rhs: NumberVar): +def number_modulo_operation(lhs: Var[int | float], rhs: Var[int | float]): """Modulo two numbers. Args: @@ -692,7 +703,7 @@ def number_modulo_operation(lhs: NumberVar, rhs: NumberVar): @binary_number_operation -def number_exponent_operation(lhs: NumberVar, rhs: NumberVar): +def number_exponent_operation(lhs: Var[int | float], rhs: Var[int | float]): """Exponentiate two numbers. Args: @@ -706,7 +717,7 @@ def number_exponent_operation(lhs: NumberVar, rhs: NumberVar): @var_operation -def number_round_operation(value: NumberVar): +def number_round_operation(value: Var[int | float]): """Round the number. Args: @@ -719,7 +730,7 @@ def number_round_operation(value: NumberVar): @var_operation -def number_ceil_operation(value: NumberVar): +def number_ceil_operation(value: Var[int | float]): """Ceil the number. Args: @@ -732,7 +743,7 @@ def number_ceil_operation(value: NumberVar): @var_operation -def number_floor_operation(value: NumberVar): +def number_floor_operation(value: Var[int | float]): """Floor the number. Args: @@ -745,7 +756,7 @@ def number_floor_operation(value: NumberVar): @var_operation -def number_trunc_operation(value: NumberVar): +def number_trunc_operation(value: Var[int | float]): """Trunc the number. Args: @@ -838,7 +849,7 @@ def __ge__(self, other: Any): @var_operation -def boolean_to_number_operation(value: BooleanVar): +def boolean_to_number_operation(value: Var[bool]): """Convert the boolean to a number. Args: @@ -969,7 +980,7 @@ def not_equal_operation(lhs: Var, rhs: Var): @var_operation -def boolean_not_operation(value: BooleanVar): +def boolean_not_operation(value: Var[bool]): """Boolean NOT the boolean. Args: @@ -1117,7 +1128,7 @@ def boolify(value: Var): @var_operation def ternary_operation( - condition: BooleanVar, if_true: Var[T], if_false: Var[U] + condition: Var[bool], if_true: Var[T], if_false: Var[U] ) -> CustomVarOperationReturn[Union[T, U]]: """Create a ternary operation. @@ -1129,12 +1140,14 @@ def ternary_operation( Returns: The ternary operation. """ - type_value: Union[Type[T], Type[U]] = unionize( - if_true._var_type, if_false._var_type - ) value: CustomVarOperationReturn[Union[T, U]] = var_operation_return( js_expression=f"({condition} ? {if_true} : {if_false})", - var_type=type_value, + type_computer=nary_type_computer( + ReflexCallable[[bool, Any, Any], Any], + ReflexCallable[[Any, Any], Any], + ReflexCallable[[Any], Any], + computer=lambda args: unionize(args[1]._var_type, args[2]._var_type), + ), ) return value diff --git a/reflex/vars/object.py b/reflex/vars/object.py index e60ea09e315..6aabd8c8048 100644 --- a/reflex/vars/object.py +++ b/reflex/vars/object.py @@ -21,15 +21,23 @@ from reflex.utils import types from reflex.utils.exceptions import VarAttributeError -from reflex.utils.types import GenericType, get_attribute_access_type, get_origin +from reflex.utils.types import ( + GenericType, + get_attribute_access_type, + get_origin, + unionize, +) from .base import ( CachedVarOperation, LiteralVar, + ReflexCallable, Var, VarData, cached_property_no_lock, figure_out_type, + nary_type_computer, + unary_type_computer, var_operation, var_operation_return, ) @@ -406,7 +414,7 @@ def create( @var_operation -def object_keys_operation(value: ObjectVar): +def object_keys_operation(value: Var): """Get the keys of an object. Args: @@ -422,7 +430,7 @@ def object_keys_operation(value: ObjectVar): @var_operation -def object_values_operation(value: ObjectVar): +def object_values_operation(value: Var): """Get the values of an object. Args: @@ -433,12 +441,15 @@ def object_values_operation(value: ObjectVar): """ return var_operation_return( js_expression=f"Object.values({value})", - var_type=List[value._value_type()], + type_computer=unary_type_computer( + ReflexCallable[[Any], List[Any]], + lambda x: List[x.to(ObjectVar)._value_type()], + ), ) @var_operation -def object_entries_operation(value: ObjectVar): +def object_entries_operation(value: Var): """Get the entries of an object. Args: @@ -447,14 +458,18 @@ def object_entries_operation(value: ObjectVar): Returns: The entries of the object. """ + value = value.to(ObjectVar) return var_operation_return( js_expression=f"Object.entries({value})", - var_type=List[Tuple[str, value._value_type()]], + type_computer=unary_type_computer( + ReflexCallable[[Any], List[Tuple[str, Any]]], + lambda x: List[Tuple[str, x.to(ObjectVar)._value_type()]], + ), ) @var_operation -def object_merge_operation(lhs: ObjectVar, rhs: ObjectVar): +def object_merge_operation(lhs: Var, rhs: Var): """Merge two objects. Args: @@ -466,10 +481,14 @@ def object_merge_operation(lhs: ObjectVar, rhs: ObjectVar): """ return var_operation_return( js_expression=f"({{...{lhs}, ...{rhs}}})", - var_type=Dict[ - Union[lhs._key_type(), rhs._key_type()], - Union[lhs._value_type(), rhs._value_type()], - ], + type_computer=nary_type_computer( + ReflexCallable[[Any, Any], Dict[Any, Any]], + ReflexCallable[[Any], Dict[Any, Any]], + computer=lambda args: Dict[ + unionize(*[arg.to(ObjectVar)._key_type() for arg in args]), + unionize(*[arg.to(ObjectVar)._value_type() for arg in args]), + ], + ), ) @@ -526,7 +545,7 @@ def create( @var_operation -def object_has_own_property_operation(object: ObjectVar, key: Var): +def object_has_own_property_operation(object: Var, key: Var): """Check if an object has a key. Args: diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 08429883fec..84f4d814680 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -3,6 +3,7 @@ from __future__ import annotations import dataclasses +import functools import inspect import json import re @@ -34,6 +35,7 @@ CachedVarOperation, CustomVarOperationReturn, LiteralVar, + ReflexCallable, Var, VarData, _global_vars, @@ -41,7 +43,10 @@ figure_out_type, get_python_literal, get_unique_variable_name, + nary_type_computer, + passthrough_unary_type_computer, unionize, + unwrap_reflex_callalbe, var_operation, var_operation_return, ) @@ -353,7 +358,7 @@ def __ge__(self, other: Any): @var_operation -def string_lt_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str): +def string_lt_operation(lhs: Var[str], rhs: Var[str]): """Check if a string is less than another string. Args: @@ -367,7 +372,7 @@ def string_lt_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str): @var_operation -def string_gt_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str): +def string_gt_operation(lhs: Var[str], rhs: Var[str]): """Check if a string is greater than another string. Args: @@ -381,7 +386,7 @@ def string_gt_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str): @var_operation -def string_le_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str): +def string_le_operation(lhs: Var[str], rhs: Var[str]): """Check if a string is less than or equal to another string. Args: @@ -395,7 +400,7 @@ def string_le_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str): @var_operation -def string_ge_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str): +def string_ge_operation(lhs: Var[str], rhs: Var[str]): """Check if a string is greater than or equal to another string. Args: @@ -409,7 +414,7 @@ def string_ge_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str): @var_operation -def string_lower_operation(string: StringVar[Any]): +def string_lower_operation(string: Var[str]): """Convert a string to lowercase. Args: @@ -422,7 +427,7 @@ def string_lower_operation(string: StringVar[Any]): @var_operation -def string_upper_operation(string: StringVar[Any]): +def string_upper_operation(string: Var[str]): """Convert a string to uppercase. Args: @@ -435,7 +440,7 @@ def string_upper_operation(string: StringVar[Any]): @var_operation -def string_strip_operation(string: StringVar[Any]): +def string_strip_operation(string: Var[str]): """Strip a string. Args: @@ -449,7 +454,7 @@ def string_strip_operation(string: StringVar[Any]): @var_operation def string_contains_field_operation( - haystack: StringVar[Any], needle: StringVar[Any] | str, field: StringVar[Any] | str + haystack: Var[str], needle: Var[str], field: Var[str] ): """Check if a string contains another string. @@ -468,7 +473,7 @@ def string_contains_field_operation( @var_operation -def string_contains_operation(haystack: StringVar[Any], needle: StringVar[Any] | str): +def string_contains_operation(haystack: Var[str], needle: Var[str]): """Check if a string contains another string. Args: @@ -484,9 +489,7 @@ def string_contains_operation(haystack: StringVar[Any], needle: StringVar[Any] | @var_operation -def string_starts_with_operation( - full_string: StringVar[Any], prefix: StringVar[Any] | str -): +def string_starts_with_operation(full_string: Var[str], prefix: Var[str]): """Check if a string starts with a prefix. Args: @@ -502,7 +505,7 @@ def string_starts_with_operation( @var_operation -def string_item_operation(string: StringVar[Any], index: NumberVar | int): +def string_item_operation(string: Var[str], index: Var[int]): """Get an item from a string. Args: @@ -515,23 +518,9 @@ def string_item_operation(string: StringVar[Any], index: NumberVar | int): return var_operation_return(js_expression=f"{string}.at({index})", var_type=str) -@var_operation -def array_join_operation(array: ArrayVar, sep: StringVar[Any] | str = ""): - """Join the elements of an array. - - Args: - array: The array. - sep: The separator. - - Returns: - The joined elements. - """ - return var_operation_return(js_expression=f"{array}.join({sep})", var_type=str) - - @var_operation def string_replace_operation( - string: StringVar, search_value: StringVar | str, new_value: StringVar | str + string: Var[str], search_value: Var[str], new_value: Var[str] ): """Replace a string with a value. @@ -1046,7 +1035,7 @@ def pluck(self, field: StringVar | str) -> ArrayVar: Returns: The array pluck operation. """ - return array_pluck_operation(self, field) + return array_pluck_operation(self, field).guess_type() @overload def __mul__(self, other: NumberVar | int) -> ArrayVar[ARRAY_VAR_TYPE]: ... @@ -1300,7 +1289,7 @@ def create( @var_operation -def string_split_operation(string: StringVar[Any], sep: StringVar | str = ""): +def string_split_operation(string: Var[str], sep: Var[str]): """Split a string. Args: @@ -1394,9 +1383,9 @@ def create( @var_operation def array_pluck_operation( - array: ArrayVar[ARRAY_VAR_TYPE], - field: StringVar | str, -) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]: + array: Var[ARRAY_VAR_TYPE], + field: Var[str], +) -> CustomVarOperationReturn[List]: """Pluck a field from an array of objects. Args: @@ -1408,13 +1397,27 @@ def array_pluck_operation( """ return var_operation_return( js_expression=f"{array}.map(e=>e?.[{field}])", - var_type=array._var_type, + var_type=List[Any], ) +@var_operation +def array_join_operation(array: Var[ARRAY_VAR_TYPE], sep: Var[str]): + """Join the elements of an array. + + Args: + array: The array. + sep: The separator. + + Returns: + The joined elements. + """ + return var_operation_return(js_expression=f"{array}.join({sep})", var_type=str) + + @var_operation def array_reverse_operation( - array: ArrayVar[ARRAY_VAR_TYPE], + array: Var[ARRAY_VAR_TYPE], ) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]: """Reverse an array. @@ -1426,12 +1429,12 @@ def array_reverse_operation( """ return var_operation_return( js_expression=f"{array}.slice().reverse()", - var_type=array._var_type, + type_computer=passthrough_unary_type_computer(ReflexCallable[[List], List]), ) @var_operation -def array_lt_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tuple): +def array_lt_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): """Check if an array is less than another array. Args: @@ -1445,7 +1448,7 @@ def array_lt_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tupl @var_operation -def array_gt_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tuple): +def array_gt_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): """Check if an array is greater than another array. Args: @@ -1459,7 +1462,7 @@ def array_gt_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tupl @var_operation -def array_le_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tuple): +def array_le_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): """Check if an array is less than or equal to another array. Args: @@ -1473,7 +1476,7 @@ def array_le_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tupl @var_operation -def array_ge_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tuple): +def array_ge_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): """Check if an array is greater than or equal to another array. Args: @@ -1487,7 +1490,7 @@ def array_ge_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tupl @var_operation -def array_length_operation(array: ArrayVar): +def array_length_operation(array: Var[ARRAY_VAR_TYPE]): """Get the length of an array. Args: @@ -1517,7 +1520,7 @@ def is_tuple_type(t: GenericType) -> bool: @var_operation -def array_item_operation(array: ArrayVar, index: NumberVar | int): +def array_item_operation(array: Var[ARRAY_VAR_TYPE], index: Var[int]): """Get an item from an array. Args: @@ -1527,23 +1530,45 @@ def array_item_operation(array: ArrayVar, index: NumberVar | int): Returns: The item from the array. """ - args = typing.get_args(array._var_type) - if args and isinstance(index, LiteralNumberVar) and is_tuple_type(array._var_type): - index_value = int(index._var_value) - element_type = args[index_value % len(args)] - else: - element_type = unionize(*args) + + def type_computer(*args): + if len(args) == 0: + return ( + ReflexCallable[[List[Any], int], Any], + functools.partial(type_computer, *args), + ) + + array = args[0] + array_args = typing.get_args(array._var_type) + + if len(args) == 1: + return ( + ReflexCallable[[int], unionize(*array_args)], + functools.partial(type_computer, *args), + ) + + index = args[1] + + if ( + array_args + and isinstance(index, LiteralNumberVar) + and is_tuple_type(array._var_type) + ): + index_value = int(index._var_value) + element_type = array_args[index_value % len(array_args)] + else: + element_type = unionize(*array_args) + + return (ReflexCallable[[], element_type], None) return var_operation_return( js_expression=f"{str(array)}.at({str(index)})", - var_type=element_type, + type_computer=type_computer, ) @var_operation -def array_range_operation( - start: NumberVar | int, stop: NumberVar | int, step: NumberVar | int -): +def array_range_operation(start: Var[int], stop: Var[int], step: Var[int]): """Create a range of numbers. Args: @@ -1562,7 +1587,7 @@ def array_range_operation( @var_operation def array_contains_field_operation( - haystack: ArrayVar, needle: Any | Var, field: StringVar | str + haystack: Var[ARRAY_VAR_TYPE], needle: Var, field: Var[str] ): """Check if an array contains an element. @@ -1581,7 +1606,7 @@ def array_contains_field_operation( @var_operation -def array_contains_operation(haystack: ArrayVar, needle: Any | Var): +def array_contains_operation(haystack: Var[ARRAY_VAR_TYPE], needle: Var): """Check if an array contains an element. Args: @@ -1599,7 +1624,7 @@ def array_contains_operation(haystack: ArrayVar, needle: Any | Var): @var_operation def repeat_array_operation( - array: ArrayVar[ARRAY_VAR_TYPE], count: NumberVar | int + array: Var[ARRAY_VAR_TYPE], count: Var[int] ) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]: """Repeat an array a number of times. @@ -1610,20 +1635,34 @@ def repeat_array_operation( Returns: The repeated array. """ + + def type_computer(*args: Var): + if not args: + return ( + ReflexCallable[[List[Any], int], List[Any]], + type_computer, + ) + if len(args) == 1: + return ( + ReflexCallable[[int], args[0]._var_type], + functools.partial(type_computer, *args), + ) + return (ReflexCallable[[], args[0]._var_type], None) + return var_operation_return( js_expression=f"Array.from({{ length: {count} }}).flatMap(() => {array})", - var_type=array._var_type, + type_computer=type_computer, ) if TYPE_CHECKING: - from .function import FunctionVar + pass @var_operation def map_array_operation( - array: ArrayVar[ARRAY_VAR_TYPE], - function: FunctionVar, + array: Var[ARRAY_VAR_TYPE], + function: Var[ReflexCallable], ): """Map a function over an array. @@ -1634,14 +1673,33 @@ def map_array_operation( Returns: The mapped array. """ + + def type_computer(*args: Var): + if not args: + return ( + ReflexCallable[[List[Any], ReflexCallable], List[Any]], + type_computer, + ) + if len(args) == 1: + return ( + ReflexCallable[[ReflexCallable], List[Any]], + functools.partial(type_computer, *args), + ) + return (ReflexCallable[[], List[args[0]._var_type]], None) + return var_operation_return( - js_expression=f"{array}.map({function})", var_type=List[Any] + js_expression=f"{array}.map({function})", + type_computer=nary_type_computer( + ReflexCallable[[List[Any], ReflexCallable], List[Any]], + ReflexCallable[[ReflexCallable], List[Any]], + computer=lambda args: List[unwrap_reflex_callalbe(args[1]._var_type)[1]], + ), ) @var_operation def array_concat_operation( - lhs: ArrayVar[ARRAY_VAR_TYPE], rhs: ArrayVar[ARRAY_VAR_TYPE] + lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE] ) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]: """Concatenate two arrays. @@ -1654,7 +1712,11 @@ def array_concat_operation( """ return var_operation_return( js_expression=f"[...{lhs}, ...{rhs}]", - var_type=Union[lhs._var_type, rhs._var_type], + type_computer=nary_type_computer( + ReflexCallable[[List[Any], List[Any]], List[Any]], + ReflexCallable[[List[Any]], List[Any]], + computer=lambda args: unionize(args[0]._var_type, args[1]._var_type), + ), ) diff --git a/tests/units/test_var.py b/tests/units/test_var.py index 4940246e79e..69732740fa9 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -963,11 +963,11 @@ def test_function_var(): def test_var_operation(): @var_operation - def add(a: Union[NumberVar, int], b: Union[NumberVar, int]): + def add(a: Var[int], b: Var[int]): return var_operation_return(js_expression=f"({a} + {b})", var_type=int) assert str(add(1, 2)) == "(1 + 2)" - assert str(add(a=4, b=-9)) == "(4 + -9)" + assert str(add(4, -9)) == "(4 + -9)" five = LiteralNumberVar.create(5) seven = add(2, five) From 9d7e353ed3424d8df0adea64850a69fa85f21b48 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 13 Nov 2024 18:40:06 -0800 Subject: [PATCH 11/86] fix pyright issues --- reflex/vars/base.py | 14 +++++------ reflex/vars/function.py | 10 ++++---- reflex/vars/number.py | 32 ++++++++++++------------- reflex/vars/object.py | 11 +++++---- reflex/vars/sequence.py | 52 +++++++++++++++++++++-------------------- 5 files changed, 62 insertions(+), 57 deletions(-) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 1e6d4163ed3..13b0635c9a0 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -75,7 +75,7 @@ if TYPE_CHECKING: from reflex.state import BaseState - from .function import ArgsFunctionOperation, ReflexCallable + from .function import ArgsFunctionOperation from .number import BooleanVar, NumberVar from .object import ObjectVar from .sequence import ArrayVar, StringVar @@ -930,7 +930,7 @@ def __ne__(self, other: Var | Any) -> BooleanVar: """ from .number import equal_operation - return ~equal_operation(self, other) + return (~equal_operation(self, other)).guess_type() def bool(self) -> BooleanVar: """Convert the var to a boolean. @@ -940,7 +940,7 @@ def bool(self) -> BooleanVar: """ from .number import boolify - return boolify(self) + return boolify(self).guess_type() def __and__(self, other: Var | Any) -> Var: """Perform a logical AND operation on the current instance and another variable. @@ -992,7 +992,7 @@ def __invert__(self) -> BooleanVar: Returns: A `BooleanVar` object representing the result of the logical NOT operation. """ - return ~self.bool() + return (~self.bool()).guess_type() def to_string(self, use_json: bool = True) -> StringVar: """Convert the var to a string. @@ -1539,7 +1539,7 @@ def var_operation( def var_operation( func: Callable[..., CustomVarOperationReturn[T]], -) -> ArgsFunctionOperation: +) -> ArgsFunctionOperation[ReflexCallable[..., T]]: """Decorator for creating a var operation. Example: @@ -1604,7 +1604,7 @@ def add(a: Var[int], b: Var[int]): function_name=func_name, type_computer=custom_operation_return._type_computer, _var_type=ReflexCallable[ - tuple(arg_python_type for _, arg_python_type in args_with_type_hints), + tuple(arg_python_type for _, arg_python_type in args_with_type_hints), # type: ignore custom_operation_return._var_type, ], ) @@ -3143,7 +3143,7 @@ def nary_type_computer( def type_computer(*args: Var): if len(args) != len(types): return ( - ReflexCallable[[], types[len(args)]], + ReflexCallable[[], types[len(args)]], # type: ignore functools.partial(type_computer, *args), ) return ( diff --git a/reflex/vars/function.py b/reflex/vars/function.py index ebe3eba5c9c..e41c348c819 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -239,7 +239,7 @@ def _partial_type( """ args_types, return_type = unwrap_reflex_callalbe(self._var_type) if isinstance(args_types, tuple): - return ReflexCallable[[*args_types[len(args) :]], return_type], None + return ReflexCallable[[*args_types[len(args) :]], return_type], None # type: ignore return ReflexCallable[..., return_type], None def _arg_len(self) -> int | None: @@ -507,9 +507,9 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]): _cached_var_name = cached_property_no_lock(format_args_function_operation) - _pre_check = pre_check_args + _pre_check = pre_check_args # type: ignore - _partial_type = figure_partial_type + _partial_type = figure_partial_type # type: ignore @classmethod def create( @@ -574,9 +574,9 @@ class ArgsFunctionOperationBuilder( _cached_var_name = cached_property_no_lock(format_args_function_operation) - _pre_check = pre_check_args + _pre_check = pre_check_args # type: ignore - _partial_type = figure_partial_type + _partial_type = figure_partial_type # type: ignore @classmethod def create( diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 0f2cb4e46e3..87793e14068 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -70,7 +70,7 @@ def __add__(self, other: Any): """ if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("+", (type(self), type(other))) - return number_add_operation(self, +other) + return number_add_operation(self, +other).guess_type() @overload def __radd__(self, other: number_types) -> NumberVar: ... @@ -89,7 +89,7 @@ def __radd__(self, other: Any): """ if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("+", (type(other), type(self))) - return number_add_operation(+other, self) + return number_add_operation(+other, self).guess_type() @overload def __sub__(self, other: number_types) -> NumberVar: ... @@ -109,7 +109,7 @@ def __sub__(self, other: Any): if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("-", (type(self), type(other))) - return number_subtract_operation(self, +other) + return number_subtract_operation(self, +other).guess_type() @overload def __rsub__(self, other: number_types) -> NumberVar: ... @@ -129,7 +129,7 @@ def __rsub__(self, other: Any): if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("-", (type(other), type(self))) - return number_subtract_operation(+other, self) + return number_subtract_operation(+other, self).guess_type() def __abs__(self): """Get the absolute value of the number. @@ -164,7 +164,7 @@ def __mul__(self, other: Any): if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("*", (type(self), type(other))) - return number_multiply_operation(self, +other) + return number_multiply_operation(self, +other).guess_type() @overload def __rmul__(self, other: number_types | boolean_types) -> NumberVar: ... @@ -191,7 +191,7 @@ def __rmul__(self, other: Any): if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("*", (type(other), type(self))) - return number_multiply_operation(+other, self) + return number_multiply_operation(+other, self).guess_type() @overload def __truediv__(self, other: number_types) -> NumberVar: ... @@ -211,7 +211,7 @@ def __truediv__(self, other: Any): if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("/", (type(self), type(other))) - return number_true_division_operation(self, +other) + return number_true_division_operation(self, +other).guess_type() @overload def __rtruediv__(self, other: number_types) -> NumberVar: ... @@ -231,7 +231,7 @@ def __rtruediv__(self, other: Any): if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("/", (type(other), type(self))) - return number_true_division_operation(+other, self) + return number_true_division_operation(+other, self).guess_type() @overload def __floordiv__(self, other: number_types) -> NumberVar: ... @@ -251,7 +251,7 @@ def __floordiv__(self, other: Any): if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("//", (type(self), type(other))) - return number_floor_division_operation(self, +other) + return number_floor_division_operation(self, +other).guess_type() @overload def __rfloordiv__(self, other: number_types) -> NumberVar: ... @@ -271,7 +271,7 @@ def __rfloordiv__(self, other: Any): if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("//", (type(other), type(self))) - return number_floor_division_operation(+other, self) + return number_floor_division_operation(+other, self).guess_type() @overload def __mod__(self, other: number_types) -> NumberVar: ... @@ -291,7 +291,7 @@ def __mod__(self, other: Any): if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("%", (type(self), type(other))) - return number_modulo_operation(self, +other) + return number_modulo_operation(self, +other).guess_type() @overload def __rmod__(self, other: number_types) -> NumberVar: ... @@ -311,7 +311,7 @@ def __rmod__(self, other: Any): if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("%", (type(other), type(self))) - return number_modulo_operation(+other, self) + return number_modulo_operation(+other, self).guess_type() @overload def __pow__(self, other: number_types) -> NumberVar: ... @@ -331,7 +331,7 @@ def __pow__(self, other: Any): if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("**", (type(self), type(other))) - return number_exponent_operation(self, +other) + return number_exponent_operation(self, +other).guess_type() @overload def __rpow__(self, other: number_types) -> NumberVar: ... @@ -351,7 +351,7 @@ def __rpow__(self, other: Any): if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("**", (type(other), type(self))) - return number_exponent_operation(+other, self) + return number_exponent_operation(+other, self).guess_type() def __neg__(self): """Negate the number. @@ -874,7 +874,7 @@ def comparison_operator( """ @var_operation - def operation(lhs: Var, rhs: Var): + def operation(lhs: Var[Any], rhs: Var[Any]): return var_operation_return( js_expression=func(lhs, rhs), var_type=bool, @@ -890,7 +890,7 @@ def wrapper(lhs: Var | Any, rhs: Var | Any) -> BooleanVar: Returns: The comparison operation. """ - return operation(lhs, rhs) + return operation(lhs, rhs).guess_type() return wrapper diff --git a/reflex/vars/object.py b/reflex/vars/object.py index 6aabd8c8048..8607e7b9fe3 100644 --- a/reflex/vars/object.py +++ b/reflex/vars/object.py @@ -91,7 +91,7 @@ def keys(self) -> ArrayVar[List[str]]: Returns: The keys of the object. """ - return object_keys_operation(self) + return object_keys_operation(self).guess_type() @overload def values( @@ -107,7 +107,7 @@ def values(self) -> ArrayVar: Returns: The values of the object. """ - return object_values_operation(self) + return object_values_operation(self).guess_type() @overload def entries( @@ -123,7 +123,7 @@ def entries(self) -> ArrayVar: Returns: The entries of the object. """ - return object_entries_operation(self) + return object_entries_operation(self).guess_type() items = entries @@ -296,7 +296,7 @@ def contains(self, key: Var | Any) -> BooleanVar: Returns: The result of the check. """ - return object_has_own_property_operation(self, key) + return object_has_own_property_operation(self, key).guess_type() @dataclasses.dataclass( @@ -445,6 +445,7 @@ def object_values_operation(value: Var): ReflexCallable[[Any], List[Any]], lambda x: List[x.to(ObjectVar)._value_type()], ), + var_type=List[Any], ) @@ -465,6 +466,7 @@ def object_entries_operation(value: Var): ReflexCallable[[Any], List[Tuple[str, Any]]], lambda x: List[Tuple[str, x.to(ObjectVar)._value_type()]], ), + var_type=List[Tuple[str, Any]], ) @@ -489,6 +491,7 @@ def object_merge_operation(lhs: Var, rhs: Var): unionize(*[arg.to(ObjectVar)._value_type() for arg in args]), ], ), + var_type=Dict[Any, Any], ) diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 84f4d814680..908cd933420 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -168,7 +168,7 @@ def __getitem__(self, i: Any) -> StringVar: isinstance(i, NumberVar) and i._is_strict_float() ): raise_unsupported_operand_types("[]", (type(self), type(i))) - return string_item_operation(self, i) + return string_item_operation(self, i).guess_type() def length(self) -> NumberVar: """Get the length of the string. @@ -184,7 +184,7 @@ def lower(self) -> StringVar: Returns: The string lower operation. """ - return string_lower_operation(self) + return string_lower_operation(self).guess_type() def upper(self) -> StringVar: """Convert the string to uppercase. @@ -192,7 +192,7 @@ def upper(self) -> StringVar: Returns: The string upper operation. """ - return string_upper_operation(self) + return string_upper_operation(self).guess_type() def strip(self) -> StringVar: """Strip the string. @@ -200,7 +200,7 @@ def strip(self) -> StringVar: Returns: The string strip operation. """ - return string_strip_operation(self) + return string_strip_operation(self).guess_type() def reversed(self) -> StringVar: """Reverse the string. @@ -235,8 +235,8 @@ def contains(self, other: Any, field: Any = None) -> BooleanVar: if field is not None: if not isinstance(field, (StringVar, str)): raise_unsupported_operand_types("contains", (type(self), type(field))) - return string_contains_field_operation(self, other, field) - return string_contains_operation(self, other) + return string_contains_field_operation(self, other, field).guess_type() + return string_contains_operation(self, other).guess_type() @overload def split(self, separator: StringVar | str = "") -> ArrayVar[List[str]]: ... @@ -255,7 +255,7 @@ def split(self, separator: Any = "") -> ArrayVar[List[str]]: """ if not isinstance(separator, (StringVar, str)): raise_unsupported_operand_types("split", (type(self), type(separator))) - return string_split_operation(self, separator) + return string_split_operation(self, separator).guess_type() @overload def startswith(self, prefix: StringVar | str) -> BooleanVar: ... @@ -274,7 +274,7 @@ def startswith(self, prefix: Any) -> BooleanVar: """ if not isinstance(prefix, (StringVar, str)): raise_unsupported_operand_types("startswith", (type(self), type(prefix))) - return string_starts_with_operation(self, prefix) + return string_starts_with_operation(self, prefix).guess_type() @overload def __lt__(self, other: StringVar | str) -> BooleanVar: ... @@ -294,7 +294,7 @@ def __lt__(self, other: Any): if not isinstance(other, (StringVar, str)): raise_unsupported_operand_types("<", (type(self), type(other))) - return string_lt_operation(self, other) + return string_lt_operation(self, other).guess_type() @overload def __gt__(self, other: StringVar | str) -> BooleanVar: ... @@ -314,7 +314,7 @@ def __gt__(self, other: Any): if not isinstance(other, (StringVar, str)): raise_unsupported_operand_types(">", (type(self), type(other))) - return string_gt_operation(self, other) + return string_gt_operation(self, other).guess_type() @overload def __le__(self, other: StringVar | str) -> BooleanVar: ... @@ -334,7 +334,7 @@ def __le__(self, other: Any): if not isinstance(other, (StringVar, str)): raise_unsupported_operand_types("<=", (type(self), type(other))) - return string_le_operation(self, other) + return string_le_operation(self, other).guess_type() @overload def __ge__(self, other: StringVar | str) -> BooleanVar: ... @@ -354,7 +354,7 @@ def __ge__(self, other: Any): if not isinstance(other, (StringVar, str)): raise_unsupported_operand_types(">=", (type(self), type(other))) - return string_ge_operation(self, other) + return string_ge_operation(self, other).guess_type() @var_operation @@ -796,7 +796,7 @@ def join(self, sep: Any = "") -> StringVar: i._var_value if isinstance(i, LiteralStringVar) else i for i in args ) ) - return array_join_operation(self, sep) + return array_join_operation(self, sep).guess_type() def reverse(self) -> ArrayVar[ARRAY_VAR_TYPE]: """Reverse the array. @@ -804,7 +804,7 @@ def reverse(self) -> ArrayVar[ARRAY_VAR_TYPE]: Returns: The reversed array. """ - return array_reverse_operation(self) + return array_reverse_operation(self).to(ArrayVar, self._var_type) @overload def __add__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> ArrayVar[ARRAY_VAR_TYPE]: ... @@ -824,7 +824,9 @@ def __add__(self, other: Any) -> ArrayVar[ARRAY_VAR_TYPE]: if not isinstance(other, ArrayVar): raise_unsupported_operand_types("+", (type(self), type(other))) - return array_concat_operation(self, other) + return array_concat_operation(self, other).to( + ArrayVar, unionize(self._var_type, other._var_type) + ) @overload def __getitem__(self, i: slice) -> ArrayVar[ARRAY_VAR_TYPE]: ... @@ -945,7 +947,7 @@ def length(self) -> NumberVar: Returns: The length of the array. """ - return array_length_operation(self) + return array_length_operation(self).guess_type() @overload @classmethod @@ -1002,7 +1004,7 @@ def range( start = first_endpoint end = second_endpoint - return array_range_operation(start, end, step or 1) + return array_range_operation(start, end, step or 1).guess_type() @overload def contains(self, other: Any) -> BooleanVar: ... @@ -1023,8 +1025,8 @@ def contains(self, other: Any, field: Any = None) -> BooleanVar: if field is not None: if not isinstance(field, (StringVar, str)): raise_unsupported_operand_types("contains", (type(self), type(field))) - return array_contains_field_operation(self, other, field) - return array_contains_operation(self, other) + return array_contains_field_operation(self, other, field).guess_type() + return array_contains_operation(self, other).guess_type() def pluck(self, field: StringVar | str) -> ArrayVar: """Pluck a field from the array. @@ -1057,7 +1059,7 @@ def __mul__(self, other: Any) -> ArrayVar[ARRAY_VAR_TYPE]: ): raise_unsupported_operand_types("*", (type(self), type(other))) - return repeat_array_operation(self, other) + return repeat_array_operation(self, other).to(ArrayVar, self._var_type) __rmul__ = __mul__ # type: ignore @@ -1079,7 +1081,7 @@ def __lt__(self, other: Any): if not isinstance(other, (ArrayVar, list, tuple)): raise_unsupported_operand_types("<", (type(self), type(other))) - return array_lt_operation(self, other) + return array_lt_operation(self, other).guess_type() @overload def __gt__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ... @@ -1099,7 +1101,7 @@ def __gt__(self, other: Any): if not isinstance(other, (ArrayVar, list, tuple)): raise_unsupported_operand_types(">", (type(self), type(other))) - return array_gt_operation(self, other) + return array_gt_operation(self, other).guess_type() @overload def __le__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ... @@ -1119,7 +1121,7 @@ def __le__(self, other: Any): if not isinstance(other, (ArrayVar, list, tuple)): raise_unsupported_operand_types("<=", (type(self), type(other))) - return array_le_operation(self, other) + return array_le_operation(self, other).guess_type() @overload def __ge__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ... @@ -1139,7 +1141,7 @@ def __ge__(self, other: Any): if not isinstance(other, (ArrayVar, list, tuple)): raise_unsupported_operand_types(">=", (type(self), type(other))) - return array_ge_operation(self, other) + return array_ge_operation(self, other).guess_type() def foreach(self, fn: Any): """Apply a function to each element of the array. @@ -1692,7 +1694,7 @@ def type_computer(*args: Var): type_computer=nary_type_computer( ReflexCallable[[List[Any], ReflexCallable], List[Any]], ReflexCallable[[ReflexCallable], List[Any]], - computer=lambda args: List[unwrap_reflex_callalbe(args[1]._var_type)[1]], + computer=lambda args: List[unwrap_reflex_callalbe(args[1]._var_type)[1]], # type: ignore ), ) From 3cdd2097b6c4419cbe9cf254b69c5bd36f924968 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 13 Nov 2024 18:43:20 -0800 Subject: [PATCH 12/86] fix pyright issues outside of vars --- reflex/components/datadisplay/shiki_code_block.py | 2 +- reflex/vars/number.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 2b4e1f5063a..3c4a5e736dd 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -823,7 +823,7 @@ def _strip_transformer_triggers(code: str | StringVar) -> StringVar | str: if isinstance(code, Var): return string_replace_operation( code, StringVar(_js_expr=f"/{regex_pattern}/g", _var_type=str), "" - ) + ).guess_type() if isinstance(code, str): return re.sub(regex_pattern, "", code) diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 87793e14068..28a9dfadcc5 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -359,7 +359,7 @@ def __neg__(self): Returns: The number negation operation. """ - return number_negate_operation(self) + return number_negate_operation(self).guess_type() def __invert__(self): """Boolean NOT the number. @@ -383,7 +383,7 @@ def __round__(self): Returns: The number round operation. """ - return number_round_operation(self) + return number_round_operation(self).guess_type() def __ceil__(self): """Ceil the number. @@ -391,7 +391,7 @@ def __ceil__(self): Returns: The number ceil operation. """ - return number_ceil_operation(self) + return number_ceil_operation(self).guess_type() def __floor__(self): """Floor the number. @@ -399,7 +399,7 @@ def __floor__(self): Returns: The number floor operation. """ - return number_floor_operation(self) + return number_floor_operation(self).guess_type() def __trunc__(self): """Trunc the number. @@ -407,7 +407,7 @@ def __trunc__(self): Returns: The number trunc operation. """ - return number_trunc_operation(self) + return number_trunc_operation(self).guess_type() @overload def __lt__(self, other: number_types) -> BooleanVar: ... From a9db61b3713b637f8cbb2b42e7e7095ab88a09d1 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 13 Nov 2024 19:03:42 -0800 Subject: [PATCH 13/86] get it right pyright --- reflex/vars/base.py | 6 ++-- reflex/vars/function.py | 6 ++-- reflex/vars/number.py | 10 +++--- reflex/vars/sequence.py | 72 ++++++++++++++++++++++++++--------------- 4 files changed, 57 insertions(+), 37 deletions(-) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 13b0635c9a0..2e02fbb0836 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -1491,9 +1491,6 @@ def __call__(self, *args: Var) -> Tuple[GenericType, Union[TypeComputer, None]]: Args: *args: The arguments to compute the type of. - - Returns: - The type of the operation. """ ... @@ -1554,6 +1551,9 @@ def add(a: Var[int], b: Var[int]): Returns: The decorated function. + + Raises: + TypeError: If the function has keyword-only arguments or arguments without Var type hints. """ from .function import ArgsFunctionOperation, ReflexCallable diff --git a/reflex/vars/function.py b/reflex/vars/function.py index e41c348c819..371276ca153 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -416,9 +416,6 @@ def format_args_function_operation( Args: self: The function operation. - args: The function arguments. - return_expr: The return expression. - explicit_return: Whether to use explicit return syntax. Returns: The formatted args function operation. @@ -454,6 +451,9 @@ def pre_check_args( Returns: True if the function can be called with the given arguments. + + Raises: + VarTypeError: If the arguments are invalid. """ for i, (validator, arg) in enumerate(zip(self._validators, args)): if not validator(arg): diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 28a9dfadcc5..3eb59e42790 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -367,7 +367,7 @@ def __invert__(self): Returns: The boolean NOT operation. """ - return boolean_not_operation(self.bool()) + return boolean_not_operation(self.bool()).guess_type() def __pos__(self) -> NumberVar: """Positive the number. @@ -518,7 +518,7 @@ def bool(self): The boolean value of the number. """ if is_optional(self._var_type): - return boolify((self != None) & (self != 0)) # noqa: E711 + return boolify((self != None) & (self != 0)).guess_type() # noqa: E711 return self != 0 def _is_strict_float(self) -> bool: @@ -777,7 +777,7 @@ def __invert__(self): Returns: The boolean NOT operation. """ - return boolean_not_operation(self) + return boolean_not_operation(self).guess_type() def __int__(self): """Convert the boolean to an int. @@ -785,7 +785,7 @@ def __int__(self): Returns: The boolean to int operation. """ - return boolean_to_number_operation(self) + return boolean_to_number_operation(self).guess_type() def __pos__(self): """Convert the boolean to an int. @@ -793,7 +793,7 @@ def __pos__(self): Returns: The boolean to int operation. """ - return boolean_to_number_operation(self) + return boolean_to_number_operation(self).guess_type() def bool(self) -> BooleanVar: """Boolean conversion. diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 908cd933420..759f9ac5d7b 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -12,6 +12,7 @@ from typing import ( TYPE_CHECKING, Any, + Callable, Dict, List, Literal, @@ -20,10 +21,11 @@ Tuple, Type, Union, + cast, overload, ) -from typing_extensions import TypeVar +from typing_extensions import TypeAliasType, TypeVar from reflex import constants from reflex.constants.base import REFLEX_VAR_OPENING_TAG @@ -59,6 +61,7 @@ ) if TYPE_CHECKING: + from .function import FunctionVar from .object import ObjectVar STRING_TYPE = TypeVar("STRING_TYPE", default=str) @@ -751,6 +754,7 @@ def create( OTHER_TUPLE = TypeVar("OTHER_TUPLE") INNER_ARRAY_VAR = TypeVar("INNER_ARRAY_VAR") +ANOTHER_ARRAY_VAR = TypeVar("ANOTHER_ARRAY_VAR") KEY_TYPE = TypeVar("KEY_TYPE") VALUE_TYPE = TypeVar("VALUE_TYPE") @@ -939,7 +943,7 @@ def __getitem__(self, i: Any) -> ArrayVar[ARRAY_VAR_TYPE] | Var: isinstance(i, NumberVar) and i._is_strict_float() ): raise_unsupported_operand_types("[]", (type(self), type(i))) - return array_item_operation(self, i) + return array_item_operation(self, i).guess_type() def length(self) -> NumberVar: """Get the length of the array. @@ -1143,7 +1147,11 @@ def __ge__(self, other: Any): return array_ge_operation(self, other).guess_type() - def foreach(self, fn: Any): + def foreach( + self: ARRAY_VAR_OF_LIST_ELEMENT[INNER_ARRAY_VAR], + fn: Callable[[Var[INNER_ARRAY_VAR]], ANOTHER_ARRAY_VAR] + | Callable[[], ANOTHER_ARRAY_VAR], + ) -> ArrayVar[List[ANOTHER_ARRAY_VAR]]: """Apply a function to each element of the array. Args: @@ -1167,37 +1175,49 @@ def foreach(self, fn: Any): ) if num_args == 0: - return_value = fn() - function_var = ArgsFunctionOperation.create(tuple(), return_value) - else: - # generic number var - number_var = Var("").to(NumberVar, int) + return_value = fn() # type: ignore + simple_function_var: FunctionVar[ReflexCallable[[], ANOTHER_ARRAY_VAR]] = ( + ArgsFunctionOperation.create(tuple(), return_value) + ) + return map_array_operation(self, simple_function_var).guess_type() + + # generic number var + number_var = Var("").to(NumberVar, int) - first_arg_type = self[number_var]._var_type + first_arg_type = self[number_var]._var_type - arg_name = get_unique_variable_name() + arg_name = get_unique_variable_name() - # get first argument type - first_arg = Var( + # get first argument type + first_arg = cast( + Var[Any], + Var( _js_expr=arg_name, _var_type=first_arg_type, - ).guess_type() + ).guess_type(), + ) - function_var = ArgsFunctionOperation.create( - (arg_name,), - Var.create(fn(first_arg)), - ) + function_var: FunctionVar[ + ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR] + ] = ArgsFunctionOperation.create( + (arg_name,), + Var.create(fn(first_arg)), # type: ignore + ) - return map_array_operation(self, function_var) + return map_array_operation(self, function_var).guess_type() LIST_ELEMENT = TypeVar("LIST_ELEMENT") -ARRAY_VAR_OF_LIST_ELEMENT = Union[ - ArrayVar[List[LIST_ELEMENT]], - ArrayVar[Set[LIST_ELEMENT]], - ArrayVar[Tuple[LIST_ELEMENT, ...]], -] +ARRAY_VAR_OF_LIST_ELEMENT = TypeAliasType( + "ARRAY_VAR_OF_LIST_ELEMENT", + Union[ + ArrayVar[List[LIST_ELEMENT]], + ArrayVar[Tuple[LIST_ELEMENT, ...]], + ArrayVar[Set[LIST_ELEMENT]], + ], + type_params=(LIST_ELEMENT,), +) @dataclasses.dataclass( @@ -1663,9 +1683,9 @@ def type_computer(*args: Var): @var_operation def map_array_operation( - array: Var[ARRAY_VAR_TYPE], - function: Var[ReflexCallable], -): + array: Var[ARRAY_VAR_OF_LIST_ELEMENT[INNER_ARRAY_VAR]], + function: Var[ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR]], +) -> CustomVarOperationReturn[List[ANOTHER_ARRAY_VAR]]: """Map a function over an array. Args: From 2c041530136a21ec35e57afd4435bf92f0772ab7 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 14 Nov 2024 09:45:44 -0800 Subject: [PATCH 14/86] special case ellipsis types --- reflex/vars/base.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 2e02fbb0836..a3f8357b267 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -14,7 +14,7 @@ import string import sys import warnings -from types import CodeType, EllipsisType, FunctionType +from types import CodeType, FunctionType from typing import ( TYPE_CHECKING, Any, @@ -96,9 +96,17 @@ class ReflexCallable(Protocol[P, R]): __call__: Callable[P, R] +if sys.version_info >= (3, 10): + from types import EllipsisType + + ReflexCallableParams = Union[EllipsisType, Tuple[GenericType, ...]] +else: + ReflexCallableParams = Union[Any, Tuple[GenericType, ...]] + + def unwrap_reflex_callalbe( callable_type: GenericType, -) -> Tuple[Union[EllipsisType, Tuple[GenericType, ...]], GenericType]: +) -> Tuple[ReflexCallableParams, GenericType]: """Unwrap the ReflexCallable type. Args: From 5f0546f32ee497ddbe0a102eee54537fbd81a1fb Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 15 Nov 2024 14:53:51 -0800 Subject: [PATCH 15/86] what am i doing anymore --- poetry.lock | 359 ++-- pyproject.toml | 3 +- reflex/.templates/web/utils/state.js | 27 + reflex/components/core/cond.py | 22 +- reflex/vars/base.py | 277 ++- reflex/vars/function.py | 245 ++- reflex/vars/number.py | 180 +- reflex/vars/object.py | 37 +- reflex/vars/sequence.py | 1850 +++++++------------ tests/units/components/core/test_foreach.py | 8 +- 10 files changed, 1382 insertions(+), 1626 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8199c975ad9..a954cc6274d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "alembic" @@ -386,73 +386,73 @@ files = [ [[package]] name = "coverage" -version = "7.6.4" +version = "7.6.5" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07"}, - {file = "coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a"}, - {file = "coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa"}, - {file = "coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522"}, - {file = "coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf"}, - {file = "coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5"}, - {file = "coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17"}, - {file = "coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a"}, - {file = "coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e"}, - {file = "coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef"}, - {file = "coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e"}, - {file = "coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901"}, - {file = "coverage-7.6.4-cp39-cp39-win32.whl", hash = "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09"}, - {file = "coverage-7.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f"}, - {file = "coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e"}, - {file = "coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73"}, + {file = "coverage-7.6.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d5fc459f1b62aa328b5c6943b4fa060fa63e7749e41c974929c503dc01d0527b"}, + {file = "coverage-7.6.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:197fc6b5e6271c4f822486cabbd91f32e73f784076b69c91179c5a9fec2d1442"}, + {file = "coverage-7.6.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a7cab0762dfbf0b0cd6eb22f7bceade31bda0f0647f9420cbb45571de4493a3"}, + {file = "coverage-7.6.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee4559597f53455d70b9935e25c21fd05aebbb8d540af04097f7cf6dc7562754"}, + {file = "coverage-7.6.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e68b894ee1a170da94b7da381527f277ec00c67f6141e79aa1ce8eebbb5561"}, + {file = "coverage-7.6.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fe4ea637711f1f1895895578972e3d0ed5efb6ef970ba0e2e26d9fad1e3c820e"}, + {file = "coverage-7.6.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1d5f036235a747cd30be433ef7ba6dab5ac41d8dc69d54094d5438c34fe8d565"}, + {file = "coverage-7.6.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a6ab7b88b1a614bc1db015e68048eb29b0c30ffa01be3d7d04da1f320db0f01"}, + {file = "coverage-7.6.5-cp310-cp310-win32.whl", hash = "sha256:ad712a72cd734fb4265041005011bbf61f8d6cba74e12c91f14a9cda63a80a64"}, + {file = "coverage-7.6.5-cp310-cp310-win_amd64.whl", hash = "sha256:61e03bb66c087b74aea6c28d10a49f72eca98b95438a8db1ae6dfcdd060f9039"}, + {file = "coverage-7.6.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dffec9f67f4eb8bc9c5df720833f1f1ca36b73d86e6f95b422ca5210e264cc26"}, + {file = "coverage-7.6.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2fde790ac0024af19fc5327fd50890dad0c31b653f6d2ed91ab2810c046bfe22"}, + {file = "coverage-7.6.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3250186381ec8e9b71234fb92ef77da87d81cbf20df3364f8f5ebf7180ec030d"}, + {file = "coverage-7.6.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ecfa205ce1fab6d8e94fe011eec04f6035a6069f70c331efd7cd1cd2d33d897"}, + {file = "coverage-7.6.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15af7bfbc37de33e7df3f740cc735057606c63bbe44aee8b07339a3e7bb8ecf6"}, + {file = "coverage-7.6.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:caf4d6af23af0e0df4e40e9985f6063d7f5434f225ee4d4ed7001f1428302403"}, + {file = "coverage-7.6.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5dcf2da597fe616a41c59e29fd8d390ac2149aeed421172eef14470c7e9dcd06"}, + {file = "coverage-7.6.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebc76107d896a53116e5ef21998f321b630b574a65b78b01176ca64e8978b43e"}, + {file = "coverage-7.6.5-cp311-cp311-win32.whl", hash = "sha256:0e9e4cd48dca252d99bb97b14f13b5940813937cc7ec568418c1a195dec9cbcc"}, + {file = "coverage-7.6.5-cp311-cp311-win_amd64.whl", hash = "sha256:a6eb14739a20c5a46073c8ad066ada17d91d14599ed98d724614db46fbae867b"}, + {file = "coverage-7.6.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9ae01c434cb0d445008257bb42dcd38112190e5bfc3a4480fde49572b16bc2ae"}, + {file = "coverage-7.6.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c72ef3be899f389c9f0934a9d06a28fa097ade096760102c732583c04cc31d75"}, + {file = "coverage-7.6.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2fc574b4fb082a0141d4df00079c4877d46cb98e8ec979cbd9a92426f5abd8a"}, + {file = "coverage-7.6.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1bc0eba158ad9d1883efb4f1bf08f88a999e091daf30454fd5f136322e700c72"}, + {file = "coverage-7.6.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a360b282c0acbf3541cc67e8d8a2a65589ea6cfa10c7e8a48e318bf28ca90f94"}, + {file = "coverage-7.6.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b22f96d3f2425942a649d786f57ae431425c9a970afae784cd865c1ffee34bad"}, + {file = "coverage-7.6.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:70eca9c6bf742feaf3ee453c1aaa932c2ab88ca420f411d90aa43ae831127b22"}, + {file = "coverage-7.6.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c4bafec5da3498d498a4ca3136f5a01fded487c6a54f18aea0bcd673feedf1b"}, + {file = "coverage-7.6.5-cp312-cp312-win32.whl", hash = "sha256:edecf498cabb335e8a683eb672558355bb9536d4397c54f1e135d9b8910512a3"}, + {file = "coverage-7.6.5-cp312-cp312-win_amd64.whl", hash = "sha256:e7c40ae56761d3c08f916019b2f8579a147f93be8e12f0f2bf4edc4ea9e1c0ab"}, + {file = "coverage-7.6.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:49ea4a739dc14856d7c5f935da90db123b77a850cfddcfacb490a28de8f87257"}, + {file = "coverage-7.6.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0c51339a28aa43d0f2b1211e57ceeeeed5e09f4deb6fc543d939de68069e81e"}, + {file = "coverage-7.6.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:040c3d5cf4db24e7cb890bf4b547a25bd3a3516c58c9f2a22f822199ee2ad8ed"}, + {file = "coverage-7.6.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0b7e67f9d3b156ab93fce71485fadd043ab04b45d5d88623c6d94f7d16ced5b"}, + {file = "coverage-7.6.5-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e078bfb114025c55fdbaa802f4c13e20e6ce4e10a96918d7234656b41f69e649"}, + {file = "coverage-7.6.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:559cdb21aca30810e648ac08270535c1d2e17226ebbdf90860a060d3680cb05f"}, + {file = "coverage-7.6.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:23e2dd956277061f24d9eda7539113a9c35a9409a9935647a34ced79b8aacb75"}, + {file = "coverage-7.6.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e7c4ccb41dc9830b2ca8592e401045a81740f627c7c0348bdc3b7373ce52f8e"}, + {file = "coverage-7.6.5-cp313-cp313-win32.whl", hash = "sha256:9d3565bb7deaa12d634426f113e6b106028c535667ba7756af65f00464981ba5"}, + {file = "coverage-7.6.5-cp313-cp313-win_amd64.whl", hash = "sha256:5039410420d9ddcd5b8566d3afbb28b89d70c4481dbb283ea543263cbefa2b67"}, + {file = "coverage-7.6.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:77b640aa78d4d9f620fb2e1b2a41b0d196120c188d0a7f678761d668d6251fcc"}, + {file = "coverage-7.6.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bb3799f6279df37e369027128926de4c159e6399000316ebd7a69e55b84dc97f"}, + {file = "coverage-7.6.5-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55aba7ab64e8af37a18064f23f399dff10041fa3aaf201528f12004968638b9f"}, + {file = "coverage-7.6.5-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6065a988d724dd3328cb21e97378bef0549b2f8b7ac0a3376785d9f7f05dc736"}, + {file = "coverage-7.6.5-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f092d222e4286cdd1ab9707da36944c11ba6294d8c9b18534057f03e6866367"}, + {file = "coverage-7.6.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1dc99aece5f899955eece053a798e279f7fe7059dd5e2a95af82878cfe4a44e1"}, + {file = "coverage-7.6.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1b14515f83ffa7a6787e725d804c6b11dd317a6bd0373d8519a61e4a587fe534"}, + {file = "coverage-7.6.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9fa6d90130165346935541f3762933dae07e237ff7d6d780fae556039f08a470"}, + {file = "coverage-7.6.5-cp313-cp313t-win32.whl", hash = "sha256:1be9ec4c49becb35955b9d69c27e6385aedd40d233f1cf065e8430c59924b30e"}, + {file = "coverage-7.6.5-cp313-cp313t-win_amd64.whl", hash = "sha256:7ff4fd7679df56e36fc838ef227e95e3aa1b0ca0548daede7f8ae6e54479c115"}, + {file = "coverage-7.6.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:23abf0846290aa57d629c4f4181d0d56cbaa45d3999e60cb0df1d2bab7bc6bfe"}, + {file = "coverage-7.6.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4903685e8059e170182ac4681ee72d2dfbb92692225023c1e325a9d85c1be31"}, + {file = "coverage-7.6.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ad9621fd9773b1461f8942da4130fbb16ee0a877eb58bc57532ea41cce20d3e"}, + {file = "coverage-7.6.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7324358a77f37ffd8ba94d3c8326eb316c972ec72264f36fc3be04cff8542465"}, + {file = "coverage-7.6.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf182001229411cd6a90d180973b345bd6fe255dbbac362100e6a625dfb107f5"}, + {file = "coverage-7.6.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4601dacd88556c94c9fb5063b9354b1fe971af9a5b25b2575faefd12bf8170a5"}, + {file = "coverage-7.6.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e5aa3d62285ef1b16f655e1ae298c6fa919209637d317934e382e9b99c28c118"}, + {file = "coverage-7.6.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8cb5601620c3d98d2c98847272acc2406333d43c9d7d49386d879bd451677429"}, + {file = "coverage-7.6.5-cp39-cp39-win32.whl", hash = "sha256:c32428f6285344caedd945236f31c46645bb10faae8702d1409bb49df218e55a"}, + {file = "coverage-7.6.5-cp39-cp39-win_amd64.whl", hash = "sha256:809e868eee27d056bc72590c69940c119775d218681b1a8ef9ba0ef8d7693e53"}, + {file = "coverage-7.6.5-pp39.pp310-none-any.whl", hash = "sha256:49145276f39f940b18a539e1e4a378e06c64a127922450ffd2fb82b9fe1ad3d9"}, + {file = "coverage-7.6.5.tar.gz", hash = "sha256:6069188329fbe0a63876719099076261ce7a1adeea95bf236cff4353a8451b0d"}, ] [package.dependencies] @@ -585,13 +585,13 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi" -version = "0.115.4" +version = "0.115.5" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.115.4-py3-none-any.whl", hash = "sha256:0b504a063ffb3cf96a5e27dc1bc32c80ca743a2528574f9cdc77daa2d31b4742"}, - {file = "fastapi-0.115.4.tar.gz", hash = "sha256:db653475586b091cb8b2fec2ac54a680ac6a158e07406e1abae31679e8826349"}, + {file = "fastapi-0.115.5-py3-none-any.whl", hash = "sha256:596b95adbe1474da47049e802f9a65ab2ffa9c2b07e7efee70eb8a66c9f2f796"}, + {file = "fastapi-0.115.5.tar.gz", hash = "sha256:0e7a4d0dc0d01c68df21887cce0945e72d3c48b9f4f79dfe7a7d53aa08fbb289"}, ] [package.dependencies] @@ -785,13 +785,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "identify" -version = "2.6.1" +version = "2.6.2" description = "File identification library for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, - {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, + {file = "identify-2.6.2-py2.py3-none-any.whl", hash = "sha256:c097384259f49e372f4ea00a19719d95ae27dd5ff0fd77ad630aa891306b82f3"}, + {file = "identify-2.6.2.tar.gz", hash = "sha256:fab5c716c24d7a789775228823797296a2994b075fb6080ac83a102772a98cbd"}, ] [package.extras] @@ -1288,13 +1288,13 @@ attrs = ">=19.2.0" [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -1350,8 +1350,8 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.23.2", markers = "python_version == \"3.11\""}, {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, {version = ">=1.22.4", markers = "python_version < \"3.11\""}, ] python-dateutil = ">=2.8.2" @@ -1669,8 +1669,8 @@ files = [ annotated-types = ">=0.6.0" pydantic-core = "2.23.4" typing-extensions = [ - {version = ">=4.6.1", markers = "python_version < \"3.13\""}, {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, ] [package.extras] @@ -1822,21 +1822,23 @@ files = [ [[package]] name = "pyright" -version = "1.1.334" +version = "1.1.389" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.334-py3-none-any.whl", hash = "sha256:dcb13e8358e021189672c4d6ebcad192ab061e4c7225036973ec493183c6da68"}, - {file = "pyright-1.1.334.tar.gz", hash = "sha256:3adaf10f1f4209575dc022f9c897f7ef024639b7ea5b3cbe49302147e6949cd4"}, + {file = "pyright-1.1.389-py3-none-any.whl", hash = "sha256:41e9620bba9254406dc1f621a88ceab5a88af4c826feb4f614d95691ed243a60"}, + {file = "pyright-1.1.389.tar.gz", hash = "sha256:716bf8cc174ab8b4dcf6828c3298cac05c5ed775dda9910106a5dcfe4c7fe220"}, ] [package.dependencies] nodeenv = ">=1.6.0" +typing-extensions = ">=4.1" [package.extras] -all = ["twine (>=3.4.1)"] +all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"] dev = ["twine (>=3.4.1)"] +nodejs = ["nodejs-wheel-binaries"] [[package]] name = "pysocks" @@ -2350,23 +2352,23 @@ websocket-client = ">=1.8,<2.0" [[package]] name = "setuptools" -version = "75.3.0" +version = "75.5.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"}, - {file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"}, + {file = "setuptools-75.5.0-py3-none-any.whl", hash = "sha256:87cb777c3b96d638ca02031192d40390e0ad97737e27b6b4fa831bea86f2f829"}, + {file = "setuptools-75.5.0.tar.gz", hash = "sha256:5c4ccb41111392671f02bb5f8436dfc5a9a7185e80500531b133f5775c4163ef"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"] +core = ["importlib-metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"] [[package]] name = "shellingham" @@ -2634,13 +2636,13 @@ files = [ [[package]] name = "tomli" -version = "2.0.2" +version = "2.1.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, + {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, ] [[package]] @@ -2828,108 +2830,91 @@ test = ["websockets"] [[package]] name = "websockets" -version = "13.1" +version = "14.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, - {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, - {file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"}, - {file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"}, - {file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"}, - {file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"}, - {file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"}, - {file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"}, - {file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"}, - {file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"}, - {file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"}, - {file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"}, - {file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"}, - {file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"}, - {file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"}, - {file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"}, - {file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"}, - {file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"}, - {file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"}, - {file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"}, - {file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"}, - {file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"}, - {file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"}, - {file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"}, - {file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"}, - {file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"}, - {file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"}, - {file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"}, - {file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"}, - {file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"}, - {file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"}, - {file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"}, - {file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"}, - {file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"}, - {file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"}, - {file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"}, - {file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"}, - {file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"}, - {file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"}, - {file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"}, - {file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"}, + {file = "websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29"}, + {file = "websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179"}, + {file = "websockets-14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2177ee3901075167f01c5e335a6685e71b162a54a89a56001f1c3e9e3d2ad250"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f14a96a0034a27f9d47fd9788913924c89612225878f8078bb9d55f859272b0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f874ba705deea77bcf64a9da42c1f5fc2466d8f14daf410bc7d4ceae0a9fcb0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bea45f19b7ca000380fbd4e02552be86343080120d074b87f25593ce1700ad58"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:219c8187b3ceeadbf2afcf0f25a4918d02da7b944d703b97d12fb01510869078"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad2ab2547761d79926effe63de21479dfaf29834c50f98c4bf5b5480b5838434"}, + {file = "websockets-14.1-cp310-cp310-win32.whl", hash = "sha256:1288369a6a84e81b90da5dbed48610cd7e5d60af62df9851ed1d1d23a9069f10"}, + {file = "websockets-14.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0744623852f1497d825a49a99bfbec9bea4f3f946df6eb9d8a2f0c37a2fec2e"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac"}, + {file = "websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23"}, + {file = "websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e"}, + {file = "websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d"}, + {file = "websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05"}, + {file = "websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0"}, + {file = "websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b"}, + {file = "websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a"}, + {file = "websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6"}, + {file = "websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01bb2d4f0a6d04538d3c5dfd27c0643269656c28045a53439cbf1c004f90897a"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:414ffe86f4d6f434a8c3b7913655a1a5383b617f9bf38720e7c0799fac3ab1c6"}, + {file = "websockets-14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fda642151d5affdee8a430bd85496f2e2517be3a2b9d2484d633d5712b15c56"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd7c11968bc3860d5c78577f0dbc535257ccec41750675d58d8dc66aa47fe52c"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a032855dc7db987dff813583d04f4950d14326665d7e714d584560b140ae6b8b"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7e7ea2f782408c32d86b87a0d2c1fd8871b0399dd762364c731d86c86069a78"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:39450e6215f7d9f6f7bc2a6da21d79374729f5d052333da4d5825af8a97e6735"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ceada5be22fa5a5a4cdeec74e761c2ee7db287208f54c718f2df4b7e200b8d4a"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3fc753451d471cff90b8f467a1fc0ae64031cf2d81b7b34e1811b7e2691bc4bc"}, + {file = "websockets-14.1-cp39-cp39-win32.whl", hash = "sha256:14839f54786987ccd9d03ed7f334baec0f02272e7ec4f6e9d427ff584aeea8b4"}, + {file = "websockets-14.1-cp39-cp39-win_amd64.whl", hash = "sha256:d9fd19ecc3a4d5ae82ddbfb30962cf6d874ff943e56e0c81f5169be2fda62979"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5dc25a9dbd1a7f61eca4b7cb04e74ae4b963d658f9e4f9aad9cd00b688692c8"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:04a97aca96ca2acedf0d1f332c861c5a4486fdcba7bcef35873820f940c4231e"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df174ece723b228d3e8734a6f2a6febbd413ddec39b3dc592f5a4aa0aff28098"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034feb9f4286476f273b9a245fb15f02c34d9586a5bc936aff108c3ba1b21beb"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c308dabd2b380807ab64b62985eaccf923a78ebc572bd485375b9ca2b7dc7"}, + {file = "websockets-14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a42d3ecbb2db5080fc578314439b1d79eef71d323dc661aa616fb492436af5d"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddaa4a390af911da6f680be8be4ff5aaf31c4c834c1a9147bc21cbcbca2d4370"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4c805c6034206143fbabd2d259ec5e757f8b29d0a2f0bf3d2fe5d1f60147a4a"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:205f672a6c2c671a86d33f6d47c9b35781a998728d2c7c2a3e1cf3333fcb62b7"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef440054124728cc49b01c33469de06755e5a7a4e83ef61934ad95fc327fbb0"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7591d6f440af7f73c4bd9404f3772bfee064e639d2b6cc8c94076e71b2471c1"}, + {file = "websockets-14.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:25225cc79cfebc95ba1d24cd3ab86aaa35bcd315d12fa4358939bd55e9bd74a5"}, + {file = "websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e"}, + {file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"}, ] [[package]] name = "wheel" -version = "0.44.0" +version = "0.45.0" description = "A built-package format for Python" optional = false python-versions = ">=3.8" files = [ - {file = "wheel-0.44.0-py3-none-any.whl", hash = "sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f"}, - {file = "wheel-0.44.0.tar.gz", hash = "sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49"}, + {file = "wheel-0.45.0-py3-none-any.whl", hash = "sha256:52f0baa5e6522155090a09c6bd95718cc46956d1b51d537ea5454249edb671c7"}, + {file = "wheel-0.45.0.tar.gz", hash = "sha256:a57353941a3183b3d5365346b567a260a0602a0f8a635926a7dede41b94c674a"}, ] [package.extras] @@ -3030,13 +3015,13 @@ h11 = ">=0.9.0,<1" [[package]] name = "zipp" -version = "3.20.2" +version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, - {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, + {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, + {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] @@ -3050,4 +3035,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "937f0cadb1a4566117dad8d0be6018ad1a8fe9aeb19c499d2a010d36ef391ee1" +content-hash = "8efad13f953b1865334876fa726e321ea73c0ca6591553ecf5d562425a2c52ee" diff --git a/pyproject.toml b/pyproject.toml index 20bf81d9225..e49dcae9eff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,7 @@ reflex-chakra = ">=0.6.0" [tool.poetry.group.dev.dependencies] pytest = ">=7.1.2,<9.0" pytest-mock = ">=3.10.0,<4.0" -pyright = ">=1.1.229,<1.1.335" +pyright = ">=1.1.229,<=1.1.389" darglint = ">=1.8.1,<2.0" dill = ">=0.3.8" toml = ">=0.10.2,<1.0" @@ -88,6 +88,7 @@ requires = ["poetry-core>=1.5.1"] build-backend = "poetry.core.masonry.api" [tool.pyright] +reportIncompatibleMethodOverride = false [tool.ruff] target-version = "py39" diff --git a/reflex/.templates/web/utils/state.js b/reflex/.templates/web/utils/state.js index 3899ddc8936..5a39f59e120 100644 --- a/reflex/.templates/web/utils/state.js +++ b/reflex/.templates/web/utils/state.js @@ -859,6 +859,33 @@ export const isTrue = (val) => { return Boolean(val); }; +/** + * Returns a copy of a section of an array. + * @param {Array | string} arrayLike The array to slice. + * @param {[number, number, number]} slice The slice to apply. + * @returns The sliced array. + */ +export const atSlice = (arrayLike, slice) => { + const array = [...arrayLike]; + const [startSlice, endSlice, stepSlice] = slice; + if (stepSlice ?? null === null) { + return array.slice(startSlice ?? undefined, endSlice ?? undefined); + } + const step = stepSlice ?? 1; + if (step > 0) { + return array + .slice(startSlice ?? undefined, endSlice ?? undefined) + .filter((_, i) => i % step === 0); + } + const actualStart = (endSlice ?? null) === null ? 0 : endSlice + 1; + const actualEnd = + (startSlice ?? null) === null ? array.length : startSlice + 1; + return array + .slice(actualStart, actualEnd) + .reverse() + .filter((_, i) => i % step === 0); +}; + /** * Get the value from a ref. * @param ref The ref to get the value from. diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index 5b6ee2a7f3a..19b585a474b 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -138,34 +138,18 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var: """ # Convert the condition to a Var. cond_var = LiteralVar.create(condition) - if cond_var is None: - raise ValueError("The condition must be set.") - # If the first component is a component, create a Cond component. + # If the first component is a component, create a Fragment if the second component is not set. if isinstance(c1, BaseComponent): - if c2 is not None and not isinstance(c2, BaseComponent): - raise ValueError("Both arguments must be components.") - return Cond.create(cond_var, c1, c2) + c2 = c2 if c2 is not None else Fragment.create() - # Otherwise, create a conditional Var. # Check that the second argument is valid. - if isinstance(c2, BaseComponent): - raise ValueError("Both arguments must be props.") if c2 is None: raise ValueError("For conditional vars, the second argument must be set.") - def create_var(cond_part): - return LiteralVar.create(cond_part) - - # convert the truth and false cond parts into vars so the _var_data can be obtained. - c1 = create_var(c1) - c2 = create_var(c2) - # Create the conditional var. return ternary_operation( - cond_var.bool()._replace( # type: ignore - merge_var_data=VarData(imports=_IS_TRUE_IMPORT), - ), # type: ignore + cond_var.bool(), c1, c2, ) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index a3f8357b267..8945d130bcd 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -26,7 +26,9 @@ Iterable, List, Literal, + NoReturn, Optional, + Sequence, Set, Tuple, Type, @@ -69,6 +71,7 @@ _isinstance, get_origin, has_args, + typehint_issubclass, unionize, ) @@ -82,6 +85,9 @@ VAR_TYPE = TypeVar("VAR_TYPE", covariant=True) +VALUE = TypeVar("VALUE") +INT_OR_FLOAT = TypeVar("INT_OR_FLOAT", int, float) +FAKE_VAR_TYPE = TypeVar("FAKE_VAR_TYPE") OTHER_VAR_TYPE = TypeVar("OTHER_VAR_TYPE") warnings.filterwarnings("ignore", message="fields may not start with an underscore") @@ -545,11 +551,11 @@ def _replace( @classmethod def create( cls, - value: Any, + value: FAKE_VAR_TYPE, _var_is_local: bool | None = None, _var_is_string: bool | None = None, _var_data: VarData | None = None, - ) -> Var: + ) -> Var[FAKE_VAR_TYPE]: """Create a var from a value. Args: @@ -587,7 +593,7 @@ def create( return LiteralVar.create(value) if _var_is_string is False or _var_is_local is True: - return cls( + return Var( _js_expr=value, _var_data=_var_data, ) @@ -629,19 +635,22 @@ def __format__(self, format_spec: str) -> str: return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._js_expr}" @overload - def to(self, output: Type[str]) -> StringVar: ... + def to(self, output: Type[bool]) -> BooleanVar: ... # pyright: ignore [reportOverlappingOverload] + + @overload + def to(self, output: Type[int]) -> NumberVar[int]: ... @overload - def to(self, output: Type[bool]) -> BooleanVar: ... + def to(self, output: type[float]) -> NumberVar[float]: ... @overload - def to(self, output: type[int] | type[float]) -> NumberVar: ... + def to(self, output: Type[str]) -> StringVar: ... # pyright: ignore [reportOverlappingOverload] @overload def to( self, - output: type[list] | type[tuple] | type[set], - ) -> ArrayVar: ... + output: type[Sequence[VALUE]] | type[set[VALUE]], + ) -> ArrayVar[Sequence[VALUE]]: ... @overload def to( @@ -728,20 +737,29 @@ def to( return self + # We use `NoReturn` here to catch `Var[Any]` and `Var[Unknown]` cases first. @overload - def guess_type(self: Var[str]) -> StringVar: ... + def guess_type(self: Var[NoReturn]) -> Var: ... # pyright: ignore [reportOverlappingOverload] @overload def guess_type(self: Var[bool]) -> BooleanVar: ... @overload - def guess_type(self: Var[int] | Var[float] | Var[int | float]) -> NumberVar: ... + def guess_type(self: Var[INT_OR_FLOAT]) -> NumberVar[INT_OR_FLOAT]: ... + + @overload + def guess_type(self: Var[str]) -> StringVar: ... # pyright: ignore [reportOverlappingOverload] @overload - def guess_type(self: Var[list] | Var[tuple] | Var[set]) -> ArrayVar: ... + def guess_type(self: Var[Sequence[VALUE]]) -> ArrayVar[Sequence[VALUE]]: ... @overload - def guess_type(self: Var[dict]) -> ObjectVar[dict]: ... + def guess_type(self: Var[Set[VALUE]]) -> ArrayVar[Set[VALUE]]: ... + + @overload + def guess_type( + self: Var[Dict[VALUE, OTHER_VAR_TYPE]], + ) -> ObjectVar[Dict[VALUE, OTHER_VAR_TYPE]]: ... @overload def guess_type(self) -> Self: ... @@ -925,7 +943,7 @@ def __eq__(self, other: Var | Any) -> BooleanVar: """ from .number import equal_operation - return equal_operation(self, other) + return equal_operation(self, other).guess_type() def __ne__(self, other: Var | Any) -> BooleanVar: """Check if the current object is not equal to the given object. @@ -948,7 +966,7 @@ def bool(self) -> BooleanVar: """ from .number import boolify - return boolify(self).guess_type() + return boolify(self) # pyright: ignore [reportReturnType] def __and__(self, other: Var | Any) -> Var: """Perform a logical AND operation on the current instance and another variable. @@ -1150,7 +1168,7 @@ def _var_state(self) -> str: @overload @classmethod - def range(cls, stop: int | NumberVar, /) -> ArrayVar[List[int]]: ... + def range(cls, stop: int | NumberVar, /) -> ArrayVar[Sequence[int]]: ... @overload @classmethod @@ -1160,15 +1178,16 @@ def range( end: int | NumberVar, step: int | NumberVar = 1, /, - ) -> ArrayVar[List[int]]: ... + ) -> ArrayVar[Sequence[int]]: ... @classmethod def range( cls, - first_endpoint: int | NumberVar, - second_endpoint: int | NumberVar | None = None, - step: int | NumberVar | None = None, - ) -> ArrayVar[List[int]]: + first_endpoint: int | Var[int], + second_endpoint: int | Var[int] | None = None, + step: int | Var[int] | None = None, + /, + ) -> ArrayVar[Sequence[int]]: """Create a range of numbers. Args: @@ -1181,7 +1200,11 @@ def range( """ from .sequence import ArrayVar - return ArrayVar.range(first_endpoint, second_endpoint, step) + if second_endpoint is None: + return ArrayVar.range.call(first_endpoint).guess_type() + if step is None: + return ArrayVar.range.call(first_endpoint, second_endpoint).guess_type() + return ArrayVar.range.call(first_endpoint, second_endpoint, step).guess_type() def __bool__(self) -> bool: """Raise exception if using Var in a boolean context. @@ -1221,6 +1244,27 @@ def __contains__(self, _: Any) -> Var: VAR_INSIDE = TypeVar("VAR_INSIDE") +class VarWithDefault(Var[VAR_TYPE]): + """Annotate an optional argument.""" + + def __init__(self, default_value: VAR_TYPE): + """Initialize the default value. + + Args: + default_value: The default value. + """ + self._default = default_value + + @property + def default(self) -> Var[VAR_TYPE]: + """Get the default value. + + Returns: + The default value. + """ + return Var.create(self._default) + + class ToOperation: """A var operation that converts a var to another type.""" @@ -1362,9 +1406,6 @@ def create( Raises: TypeError: If the value is not a supported type for LiteralVar. """ - from .object import LiteralObjectVar - from .sequence import LiteralStringVar - if isinstance(value, Var): if _var_data is None: return value @@ -1377,6 +1418,9 @@ def create( from reflex.event import EventHandler from reflex.utils.format import get_event_handler_parts + from .object import LiteralObjectVar + from .sequence import LiteralStringVar + if isinstance(value, EventHandler): return Var(_js_expr=".".join(filter(None, get_event_handler_parts(value)))) @@ -1466,7 +1510,7 @@ def get_python_literal(value: Union[LiteralVar, Any]) -> Any | None: return value -def validate_arg(type_hint: GenericType) -> Callable[[Any], bool]: +def validate_arg(type_hint: GenericType) -> Callable[[Any], str | None]: """Create a validator for an argument. Args: @@ -1477,7 +1521,15 @@ def validate_arg(type_hint: GenericType) -> Callable[[Any], bool]: """ def validate(value: Any): - return True + if isinstance(value, LiteralVar): + if not _isinstance(value._var_value, type_hint): + return f"Expected {type_hint} but got {value._var_value} of type {type(value._var_value)}." + elif isinstance(value, Var): + if not typehint_issubclass(value._var_type, type_hint): + return f"Expected {type_hint} but got {value._var_type}." + else: + if not _isinstance(value, type_hint): + return f"Expected {type_hint} but got {value} of type {type(value)}." return validate @@ -1505,14 +1557,58 @@ def __call__(self, *args: Var) -> Tuple[GenericType, Union[TypeComputer, None]]: @overload def var_operation( - func: Callable[[], CustomVarOperationReturn[T]], -) -> ArgsFunctionOperation[ReflexCallable[[], T]]: ... + func: Callable[[Var[V1], Var[V2], Var[V3]], CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation[ReflexCallable[[V1, V2, V3], T]]: ... @overload def var_operation( - func: Callable[[Var[V1]], CustomVarOperationReturn[T]], -) -> ArgsFunctionOperation[ReflexCallable[[V1], T]]: ... + func: Callable[[Var[V1], Var[V2], VarWithDefault[V3]], CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation[ReflexCallable[[V1, V2, VarWithDefault[V3]], T]]: ... + + +@overload +def var_operation( + func: Callable[ + [ + Var[V1], + VarWithDefault[V2], + VarWithDefault[V3], + ], + CustomVarOperationReturn[T], + ], +) -> ArgsFunctionOperation[ + ReflexCallable[ + [ + V1, + VarWithDefault[V2], + VarWithDefault[V3], + ], + T, + ] +]: ... + + +@overload +def var_operation( + func: Callable[ + [ + VarWithDefault[V1], + VarWithDefault[V2], + VarWithDefault[V3], + ], + CustomVarOperationReturn[T], + ], +) -> ArgsFunctionOperation[ + ReflexCallable[ + [ + VarWithDefault[V1], + VarWithDefault[V1], + VarWithDefault[V1], + ], + T, + ] +]: ... @overload @@ -1523,23 +1619,68 @@ def var_operation( @overload def var_operation( - func: Callable[[Var[V1], Var[V2], Var[V3]], CustomVarOperationReturn[T]], -) -> ArgsFunctionOperation[ReflexCallable[[V1, V2, V3], T]]: ... + func: Callable[ + [ + Var[V1], + VarWithDefault[V2], + ], + CustomVarOperationReturn[T], + ], +) -> ArgsFunctionOperation[ + ReflexCallable[ + [ + V1, + VarWithDefault[V2], + ], + T, + ] +]: ... + + +@overload +def var_operation( + func: Callable[ + [ + VarWithDefault[V1], + VarWithDefault[V2], + ], + CustomVarOperationReturn[T], + ], +) -> ArgsFunctionOperation[ + ReflexCallable[ + [ + VarWithDefault[V1], + VarWithDefault[V2], + ], + T, + ] +]: ... @overload def var_operation( - func: Callable[[Var[V1], Var[V2], Var[V3], Var[V4]], CustomVarOperationReturn[T]], -) -> ArgsFunctionOperation[ReflexCallable[[V1, V2, V3, V4], T]]: ... + func: Callable[[Var[V1]], CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation[ReflexCallable[[V1], T]]: ... @overload def var_operation( func: Callable[ - [Var[V1], Var[V2], Var[V3], Var[V4], Var[V5]], + [VarWithDefault[V1]], CustomVarOperationReturn[T], ], -) -> ArgsFunctionOperation[ReflexCallable[[V1, V2, V3, V4, V5], T]]: ... +) -> ArgsFunctionOperation[ + ReflexCallable[ + [VarWithDefault[V1]], + T, + ] +]: ... + + +@overload +def var_operation( + func: Callable[[], CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation[ReflexCallable[[], T]]: ... def var_operation( @@ -1568,6 +1709,7 @@ def add(a: Var[int], b: Var[int]): func_name = func.__name__ func_arg_spec = inspect.getfullargspec(func) + func_signature = inspect.signature(func) if func_arg_spec.kwonlyargs: raise TypeError(f"Function {func_name} cannot have keyword-only arguments.") @@ -1576,10 +1718,23 @@ def add(a: Var[int], b: Var[int]): arg_names = func_arg_spec.args + arg_default_values: Sequence[inspect.Parameter.empty | VarWithDefault] = tuple( + ( + default_value + if isinstance( + (default_value := func_signature.parameters[arg_name].default), + VarWithDefault, + ) + else inspect.Parameter.empty() + ) + for arg_name in arg_names + ) + type_hints = get_type_hints(func) if not all( - (get_origin((type_hint := type_hints.get(arg_name, Any))) or type_hint) is Var + (get_origin((type_hint := type_hints.get(arg_name, Any))) or type_hint) + in (Var, VarWithDefault) and len(get_args(type_hint)) <= 1 for arg_name in arg_names ): @@ -1606,13 +1761,22 @@ def add(a: Var[int], b: Var[int]): args_operation = ArgsFunctionOperation.create( tuple(map(str, arg_vars)), custom_operation_return, + default_values=arg_default_values, validators=tuple( - validate_arg(type_hints.get(arg_name, Any)) for arg_name in arg_names + validate_arg(arg_type) + if not isinstance(arg_type, TypeVar) + else validate_arg(arg_type.__bound__ or Any) + for _, arg_type in args_with_type_hints ), function_name=func_name, type_computer=custom_operation_return._type_computer, _var_type=ReflexCallable[ - tuple(arg_python_type for _, arg_python_type in args_with_type_hints), # type: ignore + tuple( + arg_python_type + if isinstance(arg_default_values[i], inspect.Parameter) + else VarWithDefault[arg_python_type] + for i, (_, arg_python_type) in enumerate(args_with_type_hints) + ), # type: ignore custom_operation_return._var_type, ], ) @@ -2018,10 +2182,10 @@ def __get__( @overload def __get__( - self: ComputedVar[list[LIST_INSIDE]], + self: ComputedVar[Sequence[LIST_INSIDE]], instance: None, owner: Type, - ) -> ArrayVar[list[LIST_INSIDE]]: ... + ) -> ArrayVar[Sequence[LIST_INSIDE]]: ... @overload def __get__( @@ -2030,13 +2194,6 @@ def __get__( owner: Type, ) -> ArrayVar[set[LIST_INSIDE]]: ... - @overload - def __get__( - self: ComputedVar[tuple[LIST_INSIDE, ...]], - instance: None, - owner: Type, - ) -> ArrayVar[tuple[LIST_INSIDE, ...]]: ... - @overload def __get__(self, instance: None, owner: Type) -> ComputedVar[RETURN_TYPE]: ... @@ -2175,7 +2332,7 @@ def _deps( d.update( self._deps( objclass=objclass, - obj=ref_obj, + obj=ref_obj, # pyright: ignore [reportArgumentType] ) ) # recurse into property fget functions @@ -2218,7 +2375,7 @@ def mark_dirty(self, instance) -> None: with contextlib.suppress(AttributeError): delattr(instance, self._cache_attr) - def _determine_var_type(self) -> Type: + def _determine_var_type(self) -> GenericType: """Get the type of the var. Returns: @@ -2672,8 +2829,12 @@ def _extract_var_data(value: Iterable) -> list[VarData | None]: var_datas.append(sub._var_data) elif not isinstance(sub, str): # Recurse into dict values. - if hasattr(sub, "values") and callable(sub.values): - var_datas.extend(_extract_var_data(sub.values())) + if ( + (values_fn := getattr(sub, "values", None)) is not None + and callable(values_fn) + and isinstance((values := values_fn()), Iterable) + ): + var_datas.extend(_extract_var_data(values)) # Recurse into iterable values (or dict keys). var_datas.extend(_extract_var_data(sub)) @@ -2682,9 +2843,9 @@ def _extract_var_data(value: Iterable) -> list[VarData | None]: var_datas.append(value._var_data) else: # Recurse when value is a dict itself. - values = getattr(value, "values", None) - if callable(values): - var_datas.extend(_extract_var_data(values())) + values_fn = getattr(value, "values", None) + if callable(values_fn) and isinstance((values := values_fn()), Iterable): + var_datas.extend(_extract_var_data(values)) return var_datas @@ -2966,10 +3127,10 @@ def __get__(self: Field[None], instance: None, owner) -> NoneVar: ... @overload def __get__( - self: Field[List[V]] | Field[Set[V]] | Field[Tuple[V, ...]], + self: Field[Sequence[V]] | Field[Set[V]], instance: None, owner, - ) -> ArrayVar[List[V]]: ... + ) -> ArrayVar[Sequence[V]]: ... @overload def __get__( @@ -3151,7 +3312,7 @@ def nary_type_computer( def type_computer(*args: Var): if len(args) != len(types): return ( - ReflexCallable[[], types[len(args)]], # type: ignore + types[len(args)], functools.partial(type_computer, *args), ) return ( diff --git a/reflex/vars/function.py b/reflex/vars/function.py index 371276ca153..c79a44ce02a 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -3,14 +3,25 @@ from __future__ import annotations import dataclasses +import inspect import sys -from typing import Any, Callable, Optional, Sequence, Tuple, Type, Union, overload +from typing import ( + Any, + Callable, + NoReturn, + Optional, + Sequence, + Tuple, + Type, + Union, + overload, +) from typing_extensions import Concatenate, Generic, ParamSpec, TypeVar from reflex.utils import format from reflex.utils.exceptions import VarTypeError -from reflex.utils.types import GenericType +from reflex.utils.types import GenericType, Unset, get_origin from .base import ( CachedVarOperation, @@ -19,12 +30,14 @@ TypeComputer, Var, VarData, + VarWithDefault, cached_property_no_lock, unwrap_reflex_callalbe, ) P = ParamSpec("P") R = TypeVar("R") +R2 = TypeVar("R2") V1 = TypeVar("V1") V2 = TypeVar("V2") V3 = TypeVar("V3") @@ -47,20 +60,39 @@ def partial(self) -> FunctionVar[CALLABLE_TYPE]: ... @overload def partial( - self: FunctionVar[ReflexCallable[Concatenate[V1, P], R]], + self: FunctionVar[ReflexCallable[Concatenate[VarWithDefault[V1], P], R]] + | FunctionVar[ReflexCallable[Concatenate[V1, P], R]], arg1: Union[V1, Var[V1]], ) -> FunctionVar[ReflexCallable[P, R]]: ... @overload def partial( - self: FunctionVar[ReflexCallable[Concatenate[V1, V2, P], R]], + self: FunctionVar[ + ReflexCallable[Concatenate[VarWithDefault[V1], VarWithDefault[V2], P], R] + ] + | FunctionVar[ReflexCallable[Concatenate[V1, VarWithDefault[V2], P], R]] + | FunctionVar[ReflexCallable[Concatenate[V1, V2, P], R]], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], ) -> FunctionVar[ReflexCallable[P, R]]: ... @overload def partial( - self: FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, P], R]], + self: FunctionVar[ + ReflexCallable[ + Concatenate[ + VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3], P + ], + R, + ] + ] + | FunctionVar[ + ReflexCallable[ + Concatenate[V1, VarWithDefault[V2], VarWithDefault[V3], P], R + ] + ] + | FunctionVar[ReflexCallable[Concatenate[V1, V2, VarWithDefault[V3], P], R]] + | FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, P], R]], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], arg3: Union[V3, Var[V3]], @@ -148,11 +180,58 @@ def partial(self, *args: Var | Any) -> FunctionVar: # type: ignore _var_type=partial_types, ) + @overload + def call(self: FunctionVar[ReflexCallable[[], R]]) -> VarOperationCall[[], R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[VarWithDefault[V1]], R]], + arg1: Union[V1, Var[V1], Unset] = Unset(), + ) -> VarOperationCall[[VarWithDefault[V1]], R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[VarWithDefault[V1], VarWithDefault[V2]], R]], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> VarOperationCall[[VarWithDefault[V1], VarWithDefault[V2]], R]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3]], R + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> VarOperationCall[ + [VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3]], R + ]: ... + @overload def call( self: FunctionVar[ReflexCallable[[V1], R]], arg1: Union[V1, Var[V1]] ) -> VarOperationCall[[V1], R]: ... + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, VarWithDefault[V2]], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> VarOperationCall[[V1, VarWithDefault[V2]], R]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[V1, VarWithDefault[V2], VarWithDefault[V3]], R] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> VarOperationCall[[V1, VarWithDefault[V2], VarWithDefault[V3]], R]: ... + @overload def call( self: FunctionVar[ReflexCallable[[V1, V2], R]], @@ -160,6 +239,14 @@ def call( arg2: Union[V2, Var[V2]], ) -> VarOperationCall[[V1, V2], R]: ... + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, VarWithDefault[V3]], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> VarOperationCall[[V1, V2, VarWithDefault[V3]], R]: ... + @overload def call( self: FunctionVar[ReflexCallable[[V1, V2, V3], R]], @@ -198,15 +285,11 @@ def call( arg6: Union[V6, Var[V6]], ) -> VarOperationCall[[V1, V2, V3, V4, V5, V6], R]: ... + # Capture Any to allow for arbitrary number of arguments @overload - def call( - self: FunctionVar[ReflexCallable[P, R]], *args: Var | Any - ) -> VarOperationCall[P, R]: ... + def call(self: FunctionVar[NoReturn], *args: Var | Any) -> VarOperationCall: ... - @overload - def call(self, *args: Var | Any) -> Var: ... - - def call(self, *args: Var | Any) -> Var: # type: ignore + def call(self, *args: Var | Any) -> VarOperationCall: # type: ignore """Call the function with the given arguments. Args: @@ -218,13 +301,53 @@ def call(self, *args: Var | Any) -> Var: # type: ignore Raises: VarTypeError: If the number of arguments is invalid """ + required_arg_len = self._required_arg_len() arg_len = self._arg_len() - if arg_len is not None and len(args) != arg_len: - raise VarTypeError(f"Invalid number of arguments provided to {str(self)}") + if arg_len is not None: + if len(args) < required_arg_len: + raise VarTypeError( + f"Passed {len(args)} arguments, expected at least {required_arg_len} for {str(self)}" + ) + if len(args) > arg_len: + raise VarTypeError( + f"Passed {len(args)} arguments, expected at most {arg_len} for {str(self)}" + ) args = tuple(map(LiteralVar.create, args)) self._pre_check(*args) return_type = self._return_type(*args) - return VarOperationCall.create(self, *args, _var_type=return_type).guess_type() + return VarOperationCall.create(self, *args, _var_type=return_type) + + def chain( + self: FunctionVar[ReflexCallable[P, R]], + other: FunctionVar[ReflexCallable[[R], R2]] + | FunctionVar[ReflexCallable[[R, VarWithDefault[Any]], R2]] + | FunctionVar[ + ReflexCallable[[R, VarWithDefault[Any], VarWithDefault[Any]], R2] + ], + ) -> FunctionVar[ReflexCallable[P, R2]]: + """Chain two functions together. + + Args: + other: The other function to chain. + + Returns: + The chained function. + """ + self_arg_type, self_return_type = unwrap_reflex_callalbe(self._var_type) + _, other_return_type = unwrap_reflex_callalbe(other._var_type) + + return ArgsFunctionOperationBuilder.create( + (), + VarOperationCall.create( + other, + VarOperationCall.create( + self, Var(_js_expr="...args"), _var_type=self_return_type + ), + _var_type=other_return_type, + ), + rest="arg", + _var_type=ReflexCallable[self_arg_type, other_return_type], # pyright: ignore [reportInvalidTypeArguments] + ) def _partial_type( self, *args: Var | Any @@ -253,6 +376,21 @@ def _arg_len(self) -> int | None: return len(args_types) return None + def _required_arg_len(self) -> int: + """Get the number of required arguments the function takes. + + Returns: + The number of required arguments the function takes. + """ + args_types, _ = unwrap_reflex_callalbe(self._var_type) + if isinstance(args_types, tuple): + return sum( + 1 + for arg_type in args_types + if get_origin(arg_type) is not VarWithDefault + ) + return 0 + def _return_type(self, *args: Var | Any) -> GenericType: """Override the type of the function call with the given arguments. @@ -265,7 +403,9 @@ def _return_type(self, *args: Var | Any) -> GenericType: partial_types, _ = self._partial_type(*args) return unwrap_reflex_callalbe(partial_types)[1] - def _pre_check(self, *args: Var | Any) -> Tuple[Callable[[Any], bool], ...]: + def _pre_check( + self, *args: Var | Any + ) -> Tuple[Callable[[Any], Optional[str]], ...]: """Check if the function can be called with the given arguments. Args: @@ -276,6 +416,30 @@ def _pre_check(self, *args: Var | Any) -> Tuple[Callable[[Any], bool], ...]: """ return tuple() + @overload + def __get__(self, instance: None, owner: Any) -> FunctionVar[CALLABLE_TYPE]: ... + + @overload + def __get__( + self: FunctionVar[ReflexCallable[Concatenate[V1, P], R]], + instance: Var[V1], + owner: Any, + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + def __get__(self, instance: Any, owner: Any): + """Get the function var. + + Args: + instance: The instance of the class. + owner: The owner of the class. + + Returns: + The function var. + """ + if instance is None: + return self + return self.partial(instance) + __call__ = call @@ -321,7 +485,9 @@ def create( class VarOperationCall(Generic[P, R], CachedVarOperation, Var[R]): """Base class for immutable vars that are the result of a function call.""" - _func: Optional[FunctionVar[ReflexCallable[P, R]]] = dataclasses.field(default=None) + _func: Optional[FunctionVar[ReflexCallable[..., R]]] = dataclasses.field( + default=None + ) _args: Tuple[Union[Var, Any], ...] = dataclasses.field(default_factory=tuple) @cached_property_no_lock @@ -331,7 +497,8 @@ def _cached_var_name(self) -> str: Returns: The name of the var. """ - return f"({str(self._func)}({', '.join([str(LiteralVar.create(arg)) for arg in self._args])}))" + func_str = str(self._func) + return f"({func_str}({', '.join([str(LiteralVar.create(arg)) for arg in self._args])}))" @cached_property_no_lock def _cached_get_all_var_data(self) -> VarData | None: @@ -422,8 +589,16 @@ def format_args_function_operation( """ arg_names_str = ", ".join( [ - arg if isinstance(arg, str) else arg.to_javascript() - for arg in self._args.args + (arg if isinstance(arg, str) else arg.to_javascript()) + + ( + f" = {str(default_value.default)}" + if i < len(self._default_values) + and not isinstance( + (default_value := self._default_values[i]), inspect.Parameter.empty + ) + else "" + ) + for i, arg in enumerate(self._args.args) ] + ([f"...{self._args.rest}"] if self._args.rest else []) ) @@ -442,7 +617,7 @@ def format_args_function_operation( def pre_check_args( self: ArgsFunctionOperation | ArgsFunctionOperationBuilder, *args: Var | Any -) -> Tuple[Callable[[Any], bool], ...]: +) -> Tuple[Callable[[Any], Optional[str]], ...]: """Check if the function can be called with the given arguments. Args: @@ -456,14 +631,14 @@ def pre_check_args( VarTypeError: If the arguments are invalid. """ for i, (validator, arg) in enumerate(zip(self._validators, args)): - if not validator(arg): + if (validation_message := validator(arg)) is not None: arg_name = self._args.args[i] if i < len(self._args.args) else None if arg_name is not None: raise VarTypeError( - f"Invalid argument {str(arg)} provided to {arg_name} in {self._function_name or 'var operation'}" + f"Invalid argument {str(arg)} provided to {arg_name} in {self._function_name or 'var operation'}. {validation_message}" ) raise VarTypeError( - f"Invalid argument {str(arg)} provided to argument {i} in {self._function_name or 'var operation'}" + f"Invalid argument {str(arg)} provided to argument {i} in {self._function_name or 'var operation'}. {validation_message}" ) return self._validators[len(args) :] @@ -497,7 +672,10 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]): """Base class for immutable function defined via arguments and return expression.""" _args: FunctionArgs = dataclasses.field(default_factory=FunctionArgs) - _validators: Tuple[Callable[[Any], bool], ...] = dataclasses.field( + _default_values: Tuple[VarWithDefault | inspect.Parameter.empty, ...] = ( + dataclasses.field(default_factory=tuple) + ) + _validators: Tuple[Callable[[Any], Optional[str]], ...] = dataclasses.field( default_factory=tuple ) _return_expr: Union[Var, Any] = dataclasses.field(default=None) @@ -516,8 +694,10 @@ def create( cls, args_names: Sequence[Union[str, DestructuredArg]], return_expr: Var | Any, + /, + default_values: Sequence[VarWithDefault | inspect.Parameter.empty] = (), rest: str | None = None, - validators: Sequence[Callable[[Any], bool]] = (), + validators: Sequence[Callable[[Any], Optional[str]]] = (), function_name: str = "", explicit_return: bool = False, type_computer: Optional[TypeComputer] = None, @@ -529,6 +709,7 @@ def create( Args: args_names: The names of the arguments. return_expr: The return expression of the function. + default_values: The default values of the arguments. rest: The name of the rest argument. validators: The validators for the arguments. function_name: The name of the function. @@ -545,6 +726,7 @@ def create( _var_type=_var_type, _var_data=_var_data, _args=FunctionArgs(args=tuple(args_names), rest=rest), + _default_values=tuple(default_values), _function_name=function_name, _validators=tuple(validators), _return_expr=return_expr, @@ -564,7 +746,10 @@ class ArgsFunctionOperationBuilder( """Base class for immutable function defined via arguments and return expression with the builder pattern.""" _args: FunctionArgs = dataclasses.field(default_factory=FunctionArgs) - _validators: Tuple[Callable[[Any], bool], ...] = dataclasses.field( + _default_values: Tuple[VarWithDefault | inspect.Parameter.empty, ...] = ( + dataclasses.field(default_factory=tuple) + ) + _validators: Tuple[Callable[[Any], Optional[str]], ...] = dataclasses.field( default_factory=tuple ) _return_expr: Union[Var, Any] = dataclasses.field(default=None) @@ -583,8 +768,10 @@ def create( cls, args_names: Sequence[Union[str, DestructuredArg]], return_expr: Var | Any, + /, + default_values: Sequence[VarWithDefault | inspect.Parameter.empty] = (), rest: str | None = None, - validators: Sequence[Callable[[Any], bool]] = (), + validators: Sequence[Callable[[Any], Optional[str]]] = (), function_name: str = "", explicit_return: bool = False, type_computer: Optional[TypeComputer] = None, @@ -596,6 +783,7 @@ def create( Args: args_names: The names of the arguments. return_expr: The return expression of the function. + default_values: The default values of the arguments. rest: The name of the rest argument. validators: The validators for the arguments. function_name: The name of the function. @@ -612,6 +800,7 @@ def create( _var_type=_var_type, _var_data=_var_data, _args=FunctionArgs(args=tuple(args_names), rest=rest), + _default_values=tuple(default_values), _function_name=function_name, _validators=tuple(validators), _return_expr=return_expr, diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 3eb59e42790..014cbd16b2a 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -30,6 +30,7 @@ NUMBER_T = TypeVar("NUMBER_T", int, float, Union[int, float], bool) if TYPE_CHECKING: + from .function import FunctionVar from .sequence import ArrayVar @@ -53,13 +54,7 @@ def raise_unsupported_operand_types( class NumberVar(Var[NUMBER_T], python_types=(int, float)): """Base class for immutable number vars.""" - @overload - def __add__(self, other: number_types) -> NumberVar: ... - - @overload - def __add__(self, other: NoReturn) -> NoReturn: ... - - def __add__(self, other: Any): + def __add__(self, other: number_types) -> NumberVar: """Add two numbers. Args: @@ -72,13 +67,7 @@ def __add__(self, other: Any): raise_unsupported_operand_types("+", (type(self), type(other))) return number_add_operation(self, +other).guess_type() - @overload - def __radd__(self, other: number_types) -> NumberVar: ... - - @overload - def __radd__(self, other: NoReturn) -> NoReturn: ... - - def __radd__(self, other: Any): + def __radd__(self, other: number_types) -> NumberVar: """Add two numbers. Args: @@ -91,13 +80,7 @@ def __radd__(self, other: Any): raise_unsupported_operand_types("+", (type(other), type(self))) return number_add_operation(+other, self).guess_type() - @overload - def __sub__(self, other: number_types) -> NumberVar: ... - - @overload - def __sub__(self, other: NoReturn) -> NoReturn: ... - - def __sub__(self, other: Any): + def __sub__(self, other: number_types) -> NumberVar: """Subtract two numbers. Args: @@ -111,13 +94,7 @@ def __sub__(self, other: Any): return number_subtract_operation(self, +other).guess_type() - @overload - def __rsub__(self, other: number_types) -> NumberVar: ... - - @overload - def __rsub__(self, other: NoReturn) -> NoReturn: ... - - def __rsub__(self, other: Any): + def __rsub__(self, other: number_types) -> NumberVar: """Subtract two numbers. Args: @@ -193,13 +170,7 @@ def __rmul__(self, other: Any): return number_multiply_operation(+other, self).guess_type() - @overload - def __truediv__(self, other: number_types) -> NumberVar: ... - - @overload - def __truediv__(self, other: NoReturn) -> NoReturn: ... - - def __truediv__(self, other: Any): + def __truediv__(self, other: number_types) -> NumberVar: """Divide two numbers. Args: @@ -213,13 +184,7 @@ def __truediv__(self, other: Any): return number_true_division_operation(self, +other).guess_type() - @overload - def __rtruediv__(self, other: number_types) -> NumberVar: ... - - @overload - def __rtruediv__(self, other: NoReturn) -> NoReturn: ... - - def __rtruediv__(self, other: Any): + def __rtruediv__(self, other: number_types) -> NumberVar: """Divide two numbers. Args: @@ -233,13 +198,7 @@ def __rtruediv__(self, other: Any): return number_true_division_operation(+other, self).guess_type() - @overload - def __floordiv__(self, other: number_types) -> NumberVar: ... - - @overload - def __floordiv__(self, other: NoReturn) -> NoReturn: ... - - def __floordiv__(self, other: Any): + def __floordiv__(self, other: number_types) -> NumberVar: """Floor divide two numbers. Args: @@ -253,13 +212,7 @@ def __floordiv__(self, other: Any): return number_floor_division_operation(self, +other).guess_type() - @overload - def __rfloordiv__(self, other: number_types) -> NumberVar: ... - - @overload - def __rfloordiv__(self, other: NoReturn) -> NoReturn: ... - - def __rfloordiv__(self, other: Any): + def __rfloordiv__(self, other: number_types) -> NumberVar: """Floor divide two numbers. Args: @@ -273,13 +226,7 @@ def __rfloordiv__(self, other: Any): return number_floor_division_operation(+other, self).guess_type() - @overload - def __mod__(self, other: number_types) -> NumberVar: ... - - @overload - def __mod__(self, other: NoReturn) -> NoReturn: ... - - def __mod__(self, other: Any): + def __mod__(self, other: number_types) -> NumberVar: """Modulo two numbers. Args: @@ -293,13 +240,7 @@ def __mod__(self, other: Any): return number_modulo_operation(self, +other).guess_type() - @overload - def __rmod__(self, other: number_types) -> NumberVar: ... - - @overload - def __rmod__(self, other: NoReturn) -> NoReturn: ... - - def __rmod__(self, other: Any): + def __rmod__(self, other: number_types) -> NumberVar: """Modulo two numbers. Args: @@ -313,13 +254,7 @@ def __rmod__(self, other: Any): return number_modulo_operation(+other, self).guess_type() - @overload - def __pow__(self, other: number_types) -> NumberVar: ... - - @overload - def __pow__(self, other: NoReturn) -> NoReturn: ... - - def __pow__(self, other: Any): + def __pow__(self, other: number_types) -> NumberVar: """Exponentiate two numbers. Args: @@ -333,13 +268,7 @@ def __pow__(self, other: Any): return number_exponent_operation(self, +other).guess_type() - @overload - def __rpow__(self, other: number_types) -> NumberVar: ... - - @overload - def __rpow__(self, other: NoReturn) -> NoReturn: ... - - def __rpow__(self, other: Any): + def __rpow__(self, other: number_types) -> NumberVar: """Exponentiate two numbers. Args: @@ -409,13 +338,7 @@ def __trunc__(self): """ return number_trunc_operation(self).guess_type() - @overload - def __lt__(self, other: number_types) -> BooleanVar: ... - - @overload - def __lt__(self, other: NoReturn) -> NoReturn: ... - - def __lt__(self, other: Any): + def __lt__(self, other: number_types) -> BooleanVar: """Less than comparison. Args: @@ -426,15 +349,9 @@ def __lt__(self, other: Any): """ if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("<", (type(self), type(other))) - return less_than_operation(self, +other) - - @overload - def __le__(self, other: number_types) -> BooleanVar: ... + return less_than_operation(self, +other).guess_type() - @overload - def __le__(self, other: NoReturn) -> NoReturn: ... - - def __le__(self, other: Any): + def __le__(self, other: number_types) -> BooleanVar: """Less than or equal comparison. Args: @@ -445,9 +362,9 @@ def __le__(self, other: Any): """ if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("<=", (type(self), type(other))) - return less_than_or_equal_operation(self, +other) + return less_than_or_equal_operation(self, +other).guess_type() - def __eq__(self, other: Any): + def __eq__(self, other: Any) -> BooleanVar: """Equal comparison. Args: @@ -457,10 +374,10 @@ def __eq__(self, other: Any): The result of the comparison. """ if isinstance(other, NUMBER_TYPES): - return equal_operation(self, +other) - return equal_operation(self, other) + return equal_operation(self, +other).guess_type() + return equal_operation(self, other).guess_type() - def __ne__(self, other: Any): + def __ne__(self, other: Any) -> BooleanVar: """Not equal comparison. Args: @@ -470,16 +387,10 @@ def __ne__(self, other: Any): The result of the comparison. """ if isinstance(other, NUMBER_TYPES): - return not_equal_operation(self, +other) - return not_equal_operation(self, other) - - @overload - def __gt__(self, other: number_types) -> BooleanVar: ... + return not_equal_operation(self, +other).guess_type() + return not_equal_operation(self, other).guess_type() - @overload - def __gt__(self, other: NoReturn) -> NoReturn: ... - - def __gt__(self, other: Any): + def __gt__(self, other: number_types) -> BooleanVar: """Greater than comparison. Args: @@ -490,15 +401,9 @@ def __gt__(self, other: Any): """ if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types(">", (type(self), type(other))) - return greater_than_operation(self, +other) - - @overload - def __ge__(self, other: number_types) -> BooleanVar: ... + return greater_than_operation(self, +other).guess_type() - @overload - def __ge__(self, other: NoReturn) -> NoReturn: ... - - def __ge__(self, other: Any): + def __ge__(self, other: number_types) -> BooleanVar: """Greater than or equal comparison. Args: @@ -509,9 +414,9 @@ def __ge__(self, other: Any): """ if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types(">=", (type(self), type(other))) - return greater_than_or_equal_operation(self, +other) + return greater_than_or_equal_operation(self, +other).guess_type() - def bool(self): + def bool(self) -> BooleanVar: """Boolean conversion. Returns: @@ -863,7 +768,7 @@ def boolean_to_number_operation(value: Var[bool]): def comparison_operator( func: Callable[[Var, Var], str], -) -> Callable[[Var | Any, Var | Any], BooleanVar]: +) -> FunctionVar[ReflexCallable[[Any, Any], bool]]: """Decorator to create a comparison operation. Args: @@ -873,26 +778,15 @@ def comparison_operator( The comparison operation. """ - @var_operation def operation(lhs: Var[Any], rhs: Var[Any]): return var_operation_return( js_expression=func(lhs, rhs), var_type=bool, ) - def wrapper(lhs: Var | Any, rhs: Var | Any) -> BooleanVar: - """Create the comparison operation. - - Args: - lhs: The first value. - rhs: The second value. - - Returns: - The comparison operation. - """ - return operation(lhs, rhs).guess_type() + operation.__name__ = func.__name__ - return wrapper + return var_operation(operation) @comparison_operator @@ -1104,6 +998,14 @@ def create(cls, value: bool, _var_data: VarData | None = None): f"$/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")], } +_AT_SLICE_IMPORT: ImportDict = { + f"$/{Dirs.STATE_PATH}": [ImportVar(tag="atSlice")], +} + +_RANGE_IMPORT: ImportDict = { + f"$/{Dirs.UTILS}/helpers/range": [ImportVar(tag="range", is_default=True)], +} + @var_operation def boolify(value: Var): @@ -1122,8 +1024,8 @@ def boolify(value: Var): ) -T = TypeVar("T") -U = TypeVar("U") +T = TypeVar("T", bound=Any) +U = TypeVar("U", bound=Any) @var_operation diff --git a/reflex/vars/object.py b/reflex/vars/object.py index 8607e7b9fe3..fb47984c3d0 100644 --- a/reflex/vars/object.py +++ b/reflex/vars/object.py @@ -11,6 +11,7 @@ Dict, List, NoReturn, + Sequence, Tuple, Type, TypeVar, @@ -71,9 +72,9 @@ def _value_type( ) -> Type[VALUE_TYPE]: ... @overload - def _value_type(self) -> Type: ... + def _value_type(self) -> GenericType: ... - def _value_type(self) -> Type: + def _value_type(self) -> GenericType: """Get the type of the values of the object. Returns: @@ -85,7 +86,7 @@ def _value_type(self) -> Type: args = get_args(self._var_type) if issubclass(fixed_type, dict) else () return args[1] if args else Any - def keys(self) -> ArrayVar[List[str]]: + def keys(self) -> ArrayVar[Sequence[str]]: """Get the keys of the object. Returns: @@ -96,7 +97,7 @@ def keys(self) -> ArrayVar[List[str]]: @overload def values( self: ObjectVar[Dict[Any, VALUE_TYPE]], - ) -> ArrayVar[List[VALUE_TYPE]]: ... + ) -> ArrayVar[Sequence[VALUE_TYPE]]: ... @overload def values(self) -> ArrayVar: ... @@ -112,7 +113,7 @@ def values(self) -> ArrayVar: @overload def entries( self: ObjectVar[Dict[Any, VALUE_TYPE]], - ) -> ArrayVar[List[Tuple[str, VALUE_TYPE]]]: ... + ) -> ArrayVar[Sequence[Tuple[str, VALUE_TYPE]]]: ... @overload def entries(self) -> ArrayVar: ... @@ -163,9 +164,9 @@ def __getitem__( @overload def __getitem__( - self: ObjectVar[Dict[Any, list[ARRAY_INNER_TYPE]]], + self: ObjectVar[Dict[Any, Sequence[ARRAY_INNER_TYPE]]], key: Var | Any, - ) -> ArrayVar[list[ARRAY_INNER_TYPE]]: ... + ) -> ArrayVar[Sequence[ARRAY_INNER_TYPE]]: ... @overload def __getitem__( @@ -173,12 +174,6 @@ def __getitem__( key: Var | Any, ) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ... - @overload - def __getitem__( - self: ObjectVar[Dict[Any, tuple[ARRAY_INNER_TYPE, ...]]], - key: Var | Any, - ) -> ArrayVar[tuple[ARRAY_INNER_TYPE, ...]]: ... - @overload def __getitem__( self: ObjectVar[Dict[Any, dict[OTHER_KEY_TYPE, VALUE_TYPE]]], @@ -202,7 +197,7 @@ def __getitem__(self, key: Var | Any) -> Var: # NoReturn is used here to catch when key value is Any @overload - def __getattr__( + def __getattr__( # pyright: ignore [reportOverlappingOverload] self: ObjectVar[Dict[Any, NoReturn]], name: str, ) -> Var: ... @@ -225,9 +220,9 @@ def __getattr__( @overload def __getattr__( - self: ObjectVar[Dict[Any, list[ARRAY_INNER_TYPE]]], + self: ObjectVar[Dict[Any, Sequence[ARRAY_INNER_TYPE]]], name: str, - ) -> ArrayVar[list[ARRAY_INNER_TYPE]]: ... + ) -> ArrayVar[Sequence[ARRAY_INNER_TYPE]]: ... @overload def __getattr__( @@ -235,12 +230,6 @@ def __getattr__( name: str, ) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ... - @overload - def __getattr__( - self: ObjectVar[Dict[Any, tuple[ARRAY_INNER_TYPE, ...]]], - name: str, - ) -> ArrayVar[tuple[ARRAY_INNER_TYPE, ...]]: ... - @overload def __getattr__( self: ObjectVar[Dict[Any, dict[OTHER_KEY_TYPE, VALUE_TYPE]]], @@ -311,7 +300,7 @@ class LiteralObjectVar(CachedVarOperation, ObjectVar[OBJECT_TYPE], LiteralVar): default_factory=dict ) - def _key_type(self) -> Type: + def _key_type(self) -> GenericType: """Get the type of the keys of the object. Returns: @@ -320,7 +309,7 @@ def _key_type(self) -> Type: args_list = typing.get_args(self._var_type) return args_list[0] if args_list else Any - def _value_type(self) -> Type: + def _value_type(self) -> GenericType: """Get the type of the values of the object. Returns: diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 759f9ac5d7b..7b71a8208ee 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -13,16 +13,14 @@ TYPE_CHECKING, Any, Callable, - Dict, + ClassVar, List, - Literal, - NoReturn, + Sequence, Set, Tuple, Type, Union, cast, - overload, ) from typing_extensions import TypeAliasType, TypeVar @@ -40,6 +38,7 @@ ReflexCallable, Var, VarData, + VarWithDefault, _global_vars, cached_property_no_lock, figure_out_type, @@ -53,7 +52,9 @@ var_operation_return, ) from .number import ( - BooleanVar, + _AT_SLICE_IMPORT, + _IS_TRUE_IMPORT, + _RANGE_IMPORT, LiteralNumberVar, NumberVar, raise_unsupported_operand_types, @@ -62,302 +63,18 @@ if TYPE_CHECKING: from .function import FunctionVar - from .object import ObjectVar STRING_TYPE = TypeVar("STRING_TYPE", default=str) +ARRAY_VAR_TYPE = TypeVar("ARRAY_VAR_TYPE", bound=Union[Set, Tuple, Sequence]) +OTHER_ARRAY_VAR_TYPE = TypeVar( + "OTHER_ARRAY_VAR_TYPE", bound=Union[Set, Tuple, Sequence] +) +INNER_ARRAY_VAR = TypeVar("INNER_ARRAY_VAR", covariant=True) +ANOTHER_ARRAY_VAR = TypeVar("ANOTHER_ARRAY_VAR", covariant=True) -class StringVar(Var[STRING_TYPE], python_types=str): - """Base class for immutable string vars.""" - - @overload - def __add__(self, other: StringVar | str) -> ConcatVarOperation: ... - - @overload - def __add__(self, other: NoReturn) -> NoReturn: ... - - def __add__(self, other: Any) -> ConcatVarOperation: - """Concatenate two strings. - - Args: - other: The other string. - - Returns: - The string concatenation operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types("+", (type(self), type(other))) - - return ConcatVarOperation.create(self, other) - - @overload - def __radd__(self, other: StringVar | str) -> ConcatVarOperation: ... - - @overload - def __radd__(self, other: NoReturn) -> NoReturn: ... - - def __radd__(self, other: Any) -> ConcatVarOperation: - """Concatenate two strings. - - Args: - other: The other string. - - Returns: - The string concatenation operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types("+", (type(other), type(self))) - - return ConcatVarOperation.create(other, self) - - @overload - def __mul__(self, other: NumberVar | int) -> StringVar: ... - - @overload - def __mul__(self, other: NoReturn) -> NoReturn: ... - - def __mul__(self, other: Any) -> StringVar: - """Multiply the sequence by a number or an integer. - - Args: - other: The number or integer to multiply the sequence by. - - Returns: - StringVar: The resulting sequence after multiplication. - """ - if not isinstance(other, (NumberVar, int)): - raise_unsupported_operand_types("*", (type(self), type(other))) - - return (self.split() * other).join() - - @overload - def __rmul__(self, other: NumberVar | int) -> StringVar: ... - - @overload - def __rmul__(self, other: NoReturn) -> NoReturn: ... - - def __rmul__(self, other: Any) -> StringVar: - """Multiply the sequence by a number or an integer. - - Args: - other: The number or integer to multiply the sequence by. - - Returns: - StringVar: The resulting sequence after multiplication. - """ - if not isinstance(other, (NumberVar, int)): - raise_unsupported_operand_types("*", (type(other), type(self))) - - return (self.split() * other).join() - - @overload - def __getitem__(self, i: slice) -> StringVar: ... - - @overload - def __getitem__(self, i: int | NumberVar) -> StringVar: ... - - def __getitem__(self, i: Any) -> StringVar: - """Get a slice of the string. - - Args: - i: The slice. - - Returns: - The string slice operation. - """ - if isinstance(i, slice): - return self.split()[i].join() - if not isinstance(i, (int, NumberVar)) or ( - isinstance(i, NumberVar) and i._is_strict_float() - ): - raise_unsupported_operand_types("[]", (type(self), type(i))) - return string_item_operation(self, i).guess_type() - - def length(self) -> NumberVar: - """Get the length of the string. - - Returns: - The string length operation. - """ - return self.split().length() - - def lower(self) -> StringVar: - """Convert the string to lowercase. - - Returns: - The string lower operation. - """ - return string_lower_operation(self).guess_type() - - def upper(self) -> StringVar: - """Convert the string to uppercase. - - Returns: - The string upper operation. - """ - return string_upper_operation(self).guess_type() - - def strip(self) -> StringVar: - """Strip the string. - - Returns: - The string strip operation. - """ - return string_strip_operation(self).guess_type() - - def reversed(self) -> StringVar: - """Reverse the string. - - Returns: - The string reverse operation. - """ - return self.split().reverse().join() - - @overload - def contains( - self, other: StringVar | str, field: StringVar | str | None = None - ) -> BooleanVar: ... - - @overload - def contains( - self, other: NoReturn, field: StringVar | str | None = None - ) -> NoReturn: ... - - def contains(self, other: Any, field: Any = None) -> BooleanVar: - """Check if the string contains another string. - - Args: - other: The other string. - field: The field to check. - - Returns: - The string contains operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types("contains", (type(self), type(other))) - if field is not None: - if not isinstance(field, (StringVar, str)): - raise_unsupported_operand_types("contains", (type(self), type(field))) - return string_contains_field_operation(self, other, field).guess_type() - return string_contains_operation(self, other).guess_type() - - @overload - def split(self, separator: StringVar | str = "") -> ArrayVar[List[str]]: ... - - @overload - def split(self, separator: NoReturn) -> NoReturn: ... - - def split(self, separator: Any = "") -> ArrayVar[List[str]]: - """Split the string. - - Args: - separator: The separator. - - Returns: - The string split operation. - """ - if not isinstance(separator, (StringVar, str)): - raise_unsupported_operand_types("split", (type(self), type(separator))) - return string_split_operation(self, separator).guess_type() - - @overload - def startswith(self, prefix: StringVar | str) -> BooleanVar: ... - - @overload - def startswith(self, prefix: NoReturn) -> NoReturn: ... - - def startswith(self, prefix: Any) -> BooleanVar: - """Check if the string starts with a prefix. - - Args: - prefix: The prefix. - - Returns: - The string starts with operation. - """ - if not isinstance(prefix, (StringVar, str)): - raise_unsupported_operand_types("startswith", (type(self), type(prefix))) - return string_starts_with_operation(self, prefix).guess_type() - - @overload - def __lt__(self, other: StringVar | str) -> BooleanVar: ... - - @overload - def __lt__(self, other: NoReturn) -> NoReturn: ... - - def __lt__(self, other: Any): - """Check if the string is less than another string. - - Args: - other: The other string. - - Returns: - The string less than operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types("<", (type(self), type(other))) - - return string_lt_operation(self, other).guess_type() - - @overload - def __gt__(self, other: StringVar | str) -> BooleanVar: ... - - @overload - def __gt__(self, other: NoReturn) -> NoReturn: ... - - def __gt__(self, other: Any): - """Check if the string is greater than another string. - - Args: - other: The other string. - - Returns: - The string greater than operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types(">", (type(self), type(other))) - - return string_gt_operation(self, other).guess_type() - - @overload - def __le__(self, other: StringVar | str) -> BooleanVar: ... - - @overload - def __le__(self, other: NoReturn) -> NoReturn: ... - - def __le__(self, other: Any): - """Check if the string is less than or equal to another string. - - Args: - other: The other string. - - Returns: - The string less than or equal operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types("<=", (type(self), type(other))) - - return string_le_operation(self, other).guess_type() - - @overload - def __ge__(self, other: StringVar | str) -> BooleanVar: ... - - @overload - def __ge__(self, other: NoReturn) -> NoReturn: ... - - def __ge__(self, other: Any): - """Check if the string is greater than or equal to another string. - - Args: - other: The other string. - - Returns: - The string greater than or equal operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types(">=", (type(self), type(other))) - - return string_ge_operation(self, other).guess_type() +KEY_TYPE = TypeVar("KEY_TYPE") +VALUE_TYPE = TypeVar("VALUE_TYPE") @var_operation @@ -457,7 +174,9 @@ def string_strip_operation(string: Var[str]): @var_operation def string_contains_field_operation( - haystack: Var[str], needle: Var[str], field: Var[str] + haystack: Var[str], + needle: Var[str], + field: VarWithDefault[str] = VarWithDefault(""), ): """Check if a string contains another string. @@ -470,7 +189,7 @@ def string_contains_field_operation( The string contains operation. """ return var_operation_return( - js_expression=f"{haystack}.some(obj => obj[{field}] === {needle})", + js_expression=f"{field.bool()} ? {haystack}.some(obj => obj[{field}] === {needle}) : {haystack}.some(obj => obj === {needle})", var_type=bool, ) @@ -541,617 +260,589 @@ def string_replace_operation( ) -# Compile regex for finding reflex var tags. -_decode_var_pattern_re = ( - rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}" -) -_decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL) +@var_operation +def array_pluck_operation( + array: Var[Sequence[Any]], + field: Var[str], +) -> CustomVarOperationReturn[Sequence[Any]]: + """Pluck a field from an array of objects. + Args: + array: The array to pluck from. + field: The field to pluck from the objects in the array. -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class LiteralStringVar(LiteralVar, StringVar[str]): - """Base class for immutable literal string vars.""" + Returns: + The reversed array. + """ + return var_operation_return( + js_expression=f"{array}.map(e=>e?.[{field}])", + var_type=List[Any], + ) - _var_value: str = dataclasses.field(default="") - @classmethod - def create( - cls, - value: str, - _var_type: GenericType | None = None, - _var_data: VarData | None = None, - ) -> StringVar: - """Create a var from a string value. +@var_operation +def array_join_operation( + array: Var[Sequence[Any]], sep: VarWithDefault[str] = VarWithDefault("") +): + """Join the elements of an array. - Args: - value: The value to create the var from. - _var_type: The type of the var. - _var_data: Additional hooks and imports associated with the Var. + Args: + array: The array. + sep: The separator. - Returns: - The var. - """ - # Determine var type in case the value is inherited from str. - _var_type = _var_type or type(value) or str + Returns: + The joined elements. + """ + return var_operation_return(js_expression=f"{array}.join({sep})", var_type=str) - if REFLEX_VAR_OPENING_TAG in value: - strings_and_vals: list[Var | str] = [] - offset = 0 - # Find all tags - while m := _decode_var_pattern.search(value): - start, end = m.span() +@var_operation +def array_reverse_operation( + array: Var[Sequence[INNER_ARRAY_VAR]], +) -> CustomVarOperationReturn[Sequence[INNER_ARRAY_VAR]]: + """Reverse an array. - strings_and_vals.append(value[:start]) + Args: + array: The array to reverse. - serialized_data = m.group(1) + Returns: + The reversed array. + """ + return var_operation_return( + js_expression=f"{array}.slice().reverse()", + type_computer=passthrough_unary_type_computer(ReflexCallable[[List], List]), + ) - if serialized_data.isnumeric() or ( - serialized_data[0] == "-" and serialized_data[1:].isnumeric() - ): - # This is a global immutable var. - var = _global_vars[int(serialized_data)] - strings_and_vals.append(var) - value = value[(end + len(var._js_expr)) :] - offset += end - start +@var_operation +def array_lt_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): + """Check if an array is less than another array. - strings_and_vals.append(value) + Args: + lhs: The left-hand side array. + rhs: The right-hand side array. - filtered_strings_and_vals = [ - s for s in strings_and_vals if isinstance(s, Var) or s - ] - if len(filtered_strings_and_vals) == 1: - only_string = filtered_strings_and_vals[0] - if isinstance(only_string, str): - return LiteralVar.create(only_string).to(StringVar, _var_type) - else: - return only_string.to(StringVar, only_string._var_type) + Returns: + The array less than operation. + """ + return var_operation_return(js_expression=f"{lhs} < {rhs}", var_type=bool) - if len( - literal_strings := [ - s - for s in filtered_strings_and_vals - if isinstance(s, (str, LiteralStringVar)) - ] - ) == len(filtered_strings_and_vals): - return LiteralStringVar.create( - "".join( - s._var_value if isinstance(s, LiteralStringVar) else s - for s in literal_strings - ), - _var_type=_var_type, - _var_data=VarData.merge( - _var_data, - *( - s._get_all_var_data() - for s in filtered_strings_and_vals - if isinstance(s, Var) - ), - ), - ) - concat_result = ConcatVarOperation.create( - *filtered_strings_and_vals, - _var_data=_var_data, - ) - - return ( - concat_result - if _var_type is str - else concat_result.to(StringVar, _var_type) - ) +@var_operation +def array_gt_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): + """Check if an array is greater than another array. - return LiteralStringVar( - _js_expr=json.dumps(value), - _var_type=_var_type, - _var_data=_var_data, - _var_value=value, - ) + Args: + lhs: The left-hand side array. + rhs: The right-hand side array. - def __hash__(self) -> int: - """Get the hash of the var. + Returns: + The array greater than operation. + """ + return var_operation_return(js_expression=f"{lhs} > {rhs}", var_type=bool) - Returns: - The hash of the var. - """ - return hash((self.__class__.__name__, self._var_value)) - def json(self) -> str: - """Get the JSON representation of the var. +@var_operation +def array_le_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): + """Check if an array is less than or equal to another array. - Returns: - The JSON representation of the var. - """ - return json.dumps(self._var_value) + Args: + lhs: The left-hand side array. + rhs: The right-hand side array. + Returns: + The array less than or equal operation. + """ + return var_operation_return(js_expression=f"{lhs} <= {rhs}", var_type=bool) -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class ConcatVarOperation(CachedVarOperation, StringVar[str]): - """Representing a concatenation of literal string vars.""" - _var_value: Tuple[Var, ...] = dataclasses.field(default_factory=tuple) +@var_operation +def array_ge_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): + """Check if an array is greater than or equal to another array. - @cached_property_no_lock - def _cached_var_name(self) -> str: - """The name of the var. + Args: + lhs: The left-hand side array. + rhs: The right-hand side array. - Returns: - The name of the var. - """ - list_of_strs: List[Union[str, Var]] = [] - last_string = "" - for var in self._var_value: - if isinstance(var, LiteralStringVar): - last_string += var._var_value - else: - if last_string: - list_of_strs.append(last_string) - last_string = "" - list_of_strs.append(var) + Returns: + The array greater than or equal operation. + """ + return var_operation_return(js_expression=f"{lhs} >= {rhs}", var_type=bool) - if last_string: - list_of_strs.append(last_string) - list_of_strs_filtered = [ - str(LiteralVar.create(s)) for s in list_of_strs if isinstance(s, Var) or s - ] +@var_operation +def array_length_operation(array: Var[ARRAY_VAR_TYPE]): + """Get the length of an array. - if len(list_of_strs_filtered) == 1: - return list_of_strs_filtered[0] + Args: + array: The array. - return "(" + "+".join(list_of_strs_filtered) + ")" + Returns: + The length of the array. + """ + return var_operation_return( + js_expression=f"{array}.length", + var_type=int, + ) - @cached_property_no_lock - def _cached_get_all_var_data(self) -> VarData | None: - """Get all the VarData asVarDatae Var. - Returns: - The VarData associated with the Var. - """ - return VarData.merge( - *[ - var._get_all_var_data() - for var in self._var_value - if isinstance(var, Var) - ], - self._var_data, - ) +@var_operation +def string_split_operation( + string: Var[str], sep: VarWithDefault[str] = VarWithDefault("") +): + """Split a string. - @classmethod - def create( - cls, - *value: Var | str, - _var_data: VarData | None = None, - ) -> ConcatVarOperation: - """Create a var from a string value. + Args: + string: The string to split. + sep: The separator. - Args: - value: The values to concatenate. - _var_data: Additional hooks and imports associated with the Var. + Returns: + The split string. + """ + return var_operation_return( + js_expression=f"isTrue({sep}) ? {string}.split({sep}) : [...{string}]", + var_type=Sequence[str], + var_data=VarData(imports=_IS_TRUE_IMPORT), + ) - Returns: - The var. - """ - return cls( - _js_expr="", - _var_type=str, - _var_data=_var_data, - _var_value=tuple(map(LiteralVar.create, value)), - ) +@var_operation +def array_slice_operation( + array: Var[Sequence[INNER_ARRAY_VAR]], + slice: Var[slice], +) -> CustomVarOperationReturn[Sequence[INNER_ARRAY_VAR]]: + """Get a slice from an array. -ARRAY_VAR_TYPE = TypeVar("ARRAY_VAR_TYPE", bound=Union[List, Tuple, Set]) + Args: + array: The array. + slice: The slice. -OTHER_TUPLE = TypeVar("OTHER_TUPLE") + Returns: + The item or slice from the array. + """ + return var_operation_return( + js_expression=f"at_slice({array}, {slice})", + type_computer=nary_type_computer( + ReflexCallable[[List, slice], Any], + ReflexCallable[[slice], Any], + computer=lambda args: args[0]._var_type, + ), + var_data=VarData( + imports=_AT_SLICE_IMPORT, + ), + ) -INNER_ARRAY_VAR = TypeVar("INNER_ARRAY_VAR") -ANOTHER_ARRAY_VAR = TypeVar("ANOTHER_ARRAY_VAR") -KEY_TYPE = TypeVar("KEY_TYPE") -VALUE_TYPE = TypeVar("VALUE_TYPE") +@var_operation +def array_item_operation( + array: Var[Sequence[INNER_ARRAY_VAR]], index: Var[int] +) -> CustomVarOperationReturn[INNER_ARRAY_VAR]: + """Get an item from an array. + Args: + array: The array. + index: The index of the item. -class ArrayVar(Var[ARRAY_VAR_TYPE], python_types=(list, tuple, set)): - """Base class for immutable array vars.""" + Returns: + The item from the array. + """ - @overload - def join(self, sep: StringVar | str = "") -> StringVar: ... + def type_computer(*args): + if len(args) == 0: + return ( + ReflexCallable[[List[Any], int], Any], + functools.partial(type_computer, *args), + ) - @overload - def join(self, sep: NoReturn) -> NoReturn: ... + array = args[0] + array_args = typing.get_args(array._var_type) - def join(self, sep: Any = "") -> StringVar: - """Join the elements of the array. + if len(args) == 1: + return ( + ReflexCallable[[int], unionize(*array_args)], + functools.partial(type_computer, *args), + ) - Args: - sep: The separator between elements. + index = args[1] - Returns: - The joined elements. - """ - if not isinstance(sep, (StringVar, str)): - raise_unsupported_operand_types("join", (type(self), type(sep))) if ( - isinstance(self, LiteralArrayVar) - and ( - len( - args := [ - x - for x in self._var_value - if isinstance(x, (LiteralStringVar, str)) - ] - ) - == len(self._var_value) - ) - and isinstance(sep, (LiteralStringVar, str)) + array_args + and isinstance(index, LiteralNumberVar) + and is_tuple_type(array._var_type) ): - sep_str = sep._var_value if isinstance(sep, LiteralStringVar) else sep - return LiteralStringVar.create( - sep_str.join( - i._var_value if isinstance(i, LiteralStringVar) else i for i in args - ) - ) - return array_join_operation(self, sep).guess_type() + index_value = int(index._var_value) + element_type = array_args[index_value % len(array_args)] + else: + element_type = unionize(*array_args) - def reverse(self) -> ArrayVar[ARRAY_VAR_TYPE]: - """Reverse the array. + return (ReflexCallable[[], element_type], None) - Returns: - The reversed array. - """ - return array_reverse_operation(self).to(ArrayVar, self._var_type) + return var_operation_return( + js_expression=f"{array}.at({index})", + type_computer=type_computer, + ) - @overload - def __add__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> ArrayVar[ARRAY_VAR_TYPE]: ... - @overload - def __add__(self, other: NoReturn) -> NoReturn: ... +@var_operation +def array_range_operation( + e1: Var[int], + e2: VarWithDefault[int | None] = VarWithDefault(None), + step: VarWithDefault[int] = VarWithDefault(1), +) -> CustomVarOperationReturn[Sequence[int]]: + """Create a range of numbers. - def __add__(self, other: Any) -> ArrayVar[ARRAY_VAR_TYPE]: - """Concatenate two arrays. + Args: + e1: The end of the range if e2 is not provided, otherwise the start of the range. + e2: The end of the range. + step: The step of the range. - Parameters: - other: The other array to concatenate. + Returns: + The range of numbers. + """ + return var_operation_return( + js_expression=f"range({e1}, {e2}, {step})", + var_type=List[int], + var_data=VarData( + imports=_RANGE_IMPORT, + ), + ) - Returns: - ArrayConcatOperation: The concatenation of the two arrays. - """ - if not isinstance(other, ArrayVar): - raise_unsupported_operand_types("+", (type(self), type(other))) - return array_concat_operation(self, other).to( - ArrayVar, unionize(self._var_type, other._var_type) - ) +@var_operation +def array_contains_field_operation( + haystack: Var[ARRAY_VAR_TYPE], + needle: Var[Any], + field: VarWithDefault[str] = VarWithDefault(""), +): + """Check if an array contains an element. - @overload - def __getitem__(self, i: slice) -> ArrayVar[ARRAY_VAR_TYPE]: ... + Args: + haystack: The array to check. + needle: The element to check for. + field: The field to check. - @overload - def __getitem__( - self: ( - ArrayVar[Tuple[int, OTHER_TUPLE]] - | ArrayVar[Tuple[float, OTHER_TUPLE]] - | ArrayVar[Tuple[int | float, OTHER_TUPLE]] - ), - i: Literal[0, -2], - ) -> NumberVar: ... - - @overload - def __getitem__( - self: ( - ArrayVar[Tuple[Any, int]] - | ArrayVar[Tuple[Any, float]] - | ArrayVar[Tuple[Any, int | float]] - ), - i: Literal[1, -1], - ) -> NumberVar: ... - - @overload - def __getitem__( - self: ArrayVar[Tuple[str, Any]], i: Literal[0, -2] - ) -> StringVar: ... - - @overload - def __getitem__( - self: ArrayVar[Tuple[Any, str]], i: Literal[1, -1] - ) -> StringVar: ... - - @overload - def __getitem__( - self: ArrayVar[Tuple[bool, Any]], i: Literal[0, -2] - ) -> BooleanVar: ... - - @overload - def __getitem__( - self: ArrayVar[Tuple[Any, bool]], i: Literal[1, -1] - ) -> BooleanVar: ... - - @overload - def __getitem__( - self: ( - ARRAY_VAR_OF_LIST_ELEMENT[int] - | ARRAY_VAR_OF_LIST_ELEMENT[float] - | ARRAY_VAR_OF_LIST_ELEMENT[int | float] + Returns: + The array contains operation. + """ + return var_operation_return( + js_expression=f"isTrue({field}) ? {haystack}.some(obj => obj[{field}] === {needle}) : {haystack}.some(obj => obj === {needle})", + var_type=bool, + var_data=VarData( + imports=_IS_TRUE_IMPORT, ), - i: int | NumberVar, - ) -> NumberVar: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[str], i: int | NumberVar - ) -> StringVar: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[bool], i: int | NumberVar - ) -> BooleanVar: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[List[INNER_ARRAY_VAR]], - i: int | NumberVar, - ) -> ArrayVar[List[INNER_ARRAY_VAR]]: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[Set[INNER_ARRAY_VAR]], - i: int | NumberVar, - ) -> ArrayVar[Set[INNER_ARRAY_VAR]]: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[Tuple[KEY_TYPE, VALUE_TYPE]], - i: int | NumberVar, - ) -> ArrayVar[Tuple[KEY_TYPE, VALUE_TYPE]]: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[Tuple[INNER_ARRAY_VAR, ...]], - i: int | NumberVar, - ) -> ArrayVar[Tuple[INNER_ARRAY_VAR, ...]]: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[Dict[KEY_TYPE, VALUE_TYPE]], - i: int | NumberVar, - ) -> ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]]: ... - - @overload - def __getitem__(self, i: int | NumberVar) -> Var: ... - - def __getitem__(self, i: Any) -> ArrayVar[ARRAY_VAR_TYPE] | Var: - """Get a slice of the array. + ) - Args: - i: The slice. - Returns: - The array slice operation. - """ - if isinstance(i, slice): - return ArraySliceOperation.create(self, i) - if not isinstance(i, (int, NumberVar)) or ( - isinstance(i, NumberVar) and i._is_strict_float() - ): - raise_unsupported_operand_types("[]", (type(self), type(i))) - return array_item_operation(self, i).guess_type() +@var_operation +def array_contains_operation(haystack: Var[ARRAY_VAR_TYPE], needle: Var): + """Check if an array contains an element. - def length(self) -> NumberVar: - """Get the length of the array. + Args: + haystack: The array to check. + needle: The element to check for. - Returns: - The length of the array. - """ - return array_length_operation(self).guess_type() + Returns: + The array contains operation. + """ + return var_operation_return( + js_expression=f"{haystack}.includes({needle})", + var_type=bool, + ) - @overload - @classmethod - def range(cls, stop: int | NumberVar, /) -> ArrayVar[List[int]]: ... - @overload - @classmethod - def range( - cls, - start: int | NumberVar, - end: int | NumberVar, - step: int | NumberVar = 1, - /, - ) -> ArrayVar[List[int]]: ... +@var_operation +def repeat_array_operation( + array: Var[Sequence[INNER_ARRAY_VAR]], count: Var[int] +) -> CustomVarOperationReturn[Sequence[INNER_ARRAY_VAR]]: + """Repeat an array a number of times. - @overload - @classmethod - def range( - cls, - first_endpoint: int | NumberVar, - second_endpoint: int | NumberVar | None = None, - step: int | NumberVar | None = None, - ) -> ArrayVar[List[int]]: ... + Args: + array: The array to repeat. + count: The number of times to repeat the array. - @classmethod - def range( - cls, - first_endpoint: int | NumberVar, - second_endpoint: int | NumberVar | None = None, - step: int | NumberVar | None = None, - ) -> ArrayVar[List[int]]: - """Create a range of numbers. + Returns: + The repeated array. + """ - Args: - first_endpoint: The end of the range if second_endpoint is not provided, otherwise the start of the range. - second_endpoint: The end of the range. - step: The step of the range. + def type_computer(*args: Var): + if not args: + return ( + ReflexCallable[[List[Any], int], List[Any]], + type_computer, + ) + if len(args) == 1: + return ( + ReflexCallable[[int], args[0]._var_type], + functools.partial(type_computer, *args), + ) + return (ReflexCallable[[], args[0]._var_type], None) - Returns: - The range of numbers. - """ - if any( - not isinstance(i, (int, NumberVar)) - for i in (first_endpoint, second_endpoint, step) - if i is not None - ): - raise_unsupported_operand_types( - "range", (type(first_endpoint), type(second_endpoint), type(step)) + return var_operation_return( + js_expression=f"Array.from({{ length: {count} }}).flatMap(() => {array})", + type_computer=type_computer, + ) + + +@var_operation +def repeat_string_operation( + string: Var[str], count: Var[int] +) -> CustomVarOperationReturn[str]: + """Repeat a string a number of times. + + Args: + string: The string to repeat. + count: The number of times to repeat the string. + + Returns: + The repeated string. + """ + return var_operation_return( + js_expression=f"{string}.repeat({count})", + var_type=str, + ) + + +if TYPE_CHECKING: + pass + + +@var_operation +def map_array_operation( + array: Var[Sequence[INNER_ARRAY_VAR]], + function: Var[ + ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR] + | ReflexCallable[[], ANOTHER_ARRAY_VAR] + ], +) -> CustomVarOperationReturn[Sequence[ANOTHER_ARRAY_VAR]]: + """Map a function over an array. + + Args: + array: The array. + function: The function to map. + + Returns: + The mapped array. + """ + + def type_computer(*args: Var): + if not args: + return ( + ReflexCallable[[List[Any], ReflexCallable], List[Any]], + type_computer, ) - if second_endpoint is None: - start = 0 - end = first_endpoint - else: - start = first_endpoint - end = second_endpoint + if len(args) == 1: + return ( + ReflexCallable[[ReflexCallable], List[Any]], + functools.partial(type_computer, *args), + ) + return (ReflexCallable[[], List[args[0]._var_type]], None) - return array_range_operation(start, end, step or 1).guess_type() + return var_operation_return( + js_expression=f"{array}.map({function})", + type_computer=nary_type_computer( + ReflexCallable[[List[Any], ReflexCallable], List[Any]], + ReflexCallable[[ReflexCallable], List[Any]], + computer=lambda args: List[unwrap_reflex_callalbe(args[1]._var_type)[1]], # type: ignore + ), + ) - @overload - def contains(self, other: Any) -> BooleanVar: ... - @overload - def contains(self, other: Any, field: StringVar | str) -> BooleanVar: ... +@var_operation +def array_concat_operation( + lhs: Var[Sequence[INNER_ARRAY_VAR]], rhs: Var[Sequence[ANOTHER_ARRAY_VAR]] +) -> CustomVarOperationReturn[Sequence[INNER_ARRAY_VAR | ANOTHER_ARRAY_VAR]]: + """Concatenate two arrays. - def contains(self, other: Any, field: Any = None) -> BooleanVar: - """Check if the array contains an element. + Args: + lhs: The left-hand side array. + rhs: The right-hand side array. - Args: - other: The element to check for. - field: The field to check. + Returns: + The concatenated array. + """ + return var_operation_return( + js_expression=f"[...{lhs}, ...{rhs}]", + type_computer=nary_type_computer( + ReflexCallable[[List[Any], List[Any]], List[Any]], + ReflexCallable[[List[Any]], List[Any]], + computer=lambda args: unionize(args[0]._var_type, args[1]._var_type), + ), + ) - Returns: - The array contains operation. - """ - if field is not None: - if not isinstance(field, (StringVar, str)): - raise_unsupported_operand_types("contains", (type(self), type(field))) - return array_contains_field_operation(self, other, field).guess_type() - return array_contains_operation(self, other).guess_type() - def pluck(self, field: StringVar | str) -> ArrayVar: - """Pluck a field from the array. +@var_operation +def string_concat_operation( + lhs: Var[str], rhs: Var[str] +) -> CustomVarOperationReturn[str]: + """Concatenate two strings. - Args: - field: The field to pluck from the array. + Args: + lhs: The left-hand side string. + rhs: The right-hand side string. - Returns: - The array pluck operation. - """ - return array_pluck_operation(self, field).guess_type() + Returns: + The concatenated string. + """ + return var_operation_return( + js_expression=f"{lhs} + {rhs}", + var_type=str, + ) - @overload - def __mul__(self, other: NumberVar | int) -> ArrayVar[ARRAY_VAR_TYPE]: ... - @overload - def __mul__(self, other: NoReturn) -> NoReturn: ... +@var_operation +def reverse_string_concat_operation( + lhs: Var[str], rhs: Var[str] +) -> CustomVarOperationReturn[str]: + """Concatenate two strings in reverse order. + + Args: + lhs: The left-hand side string. + rhs: The right-hand side string. - def __mul__(self, other: Any) -> ArrayVar[ARRAY_VAR_TYPE]: - """Multiply the sequence by a number or integer. + Returns: + The concatenated string. + """ + return var_operation_return( + js_expression=f"{rhs} + {lhs}", + var_type=str, + ) - Parameters: - other: The number or integer to multiply the sequence by. - Returns: - ArrayVar[ARRAY_VAR_TYPE]: The result of multiplying the sequence by the given number or integer. - """ - if not isinstance(other, (NumberVar, int)) or ( - isinstance(other, NumberVar) and other._is_strict_float() - ): - raise_unsupported_operand_types("*", (type(self), type(other))) +class SliceVar(Var[slice], python_types=slice): + """Base class for immutable slice vars.""" - return repeat_array_operation(self, other).to(ArrayVar, self._var_type) - __rmul__ = __mul__ # type: ignore +@dataclasses.dataclass( + eq=False, + frozen=True, + **{"slots": True} if sys.version_info >= (3, 10) else {}, +) +class LiteralSliceVar(CachedVarOperation, LiteralVar, SliceVar): + """Base class for immutable literal slice vars.""" - @overload - def __lt__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ... + _var_value: slice = dataclasses.field(default=slice(None)) - @overload - def __lt__(self, other: list | tuple) -> BooleanVar: ... + @cached_property_no_lock + def _cached_var_name(self) -> str: + """The name of the var. - def __lt__(self, other: Any): - """Check if the array is less than another array. + Returns: + The name of the var. + """ + return f"[{str(LiteralVar.create(self._var_value.start))}, {str(LiteralVar.create(self._var_value.stop))}, {str(LiteralVar.create(self._var_value.step))}]" - Args: - other: The other array. + @cached_property_no_lock + def _cached_get_all_var_data(self) -> VarData | None: + """Get all the VarData asVarDatae Var. Returns: - The array less than operation. + The VarData associated with the Var. """ - if not isinstance(other, (ArrayVar, list, tuple)): - raise_unsupported_operand_types("<", (type(self), type(other))) + return VarData.merge( + *[ + var._get_all_var_data() + for var in [ + self._var_value.start, + self._var_value.stop, + self._var_value.step, + ] + if isinstance(var, Var) + ], + self._var_data, + ) + + @classmethod + def create( + cls, + value: slice, + _var_type: Type[slice] | None = None, + _var_data: VarData | None = None, + ) -> SliceVar: + """Create a var from a slice value. - return array_lt_operation(self, other).guess_type() + Args: + value: The value to create the var from. + _var_type: The type of the var. + _var_data: Additional hooks and imports associated with the Var. - @overload - def __gt__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ... + Returns: + The var. + """ + return cls( + _js_expr="", + _var_type=_var_type, + _var_data=_var_data, + _var_value=value, + ) - @overload - def __gt__(self, other: list | tuple) -> BooleanVar: ... + def __hash__(self) -> int: + """Get the hash of the var. - def __gt__(self, other: Any): - """Check if the array is greater than another array. + Returns: + The hash of the var. + """ + return hash( + ( + self.__class__.__name__, + self._var_value.start, + self._var_value.stop, + self._var_value.step, + ) + ) - Args: - other: The other array. + def json(self) -> str: + """Get the JSON representation of the var. Returns: - The array greater than operation. + The JSON representation of the var. """ - if not isinstance(other, (ArrayVar, list, tuple)): - raise_unsupported_operand_types(">", (type(self), type(other))) + return json.dumps( + [self._var_value.start, self._var_value.stop, self._var_value.step] + ) - return array_gt_operation(self, other).guess_type() - @overload - def __le__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ... +class ArrayVar(Var[ARRAY_VAR_TYPE], python_types=(Sequence, set)): + """Base class for immutable array vars.""" - @overload - def __le__(self, other: list | tuple) -> BooleanVar: ... + join = array_join_operation - def __le__(self, other: Any): - """Check if the array is less than or equal to another array. + reverse = array_reverse_operation - Args: - other: The other array. + __add__ = array_concat_operation - Returns: - The array less than or equal operation. - """ - if not isinstance(other, (ArrayVar, list, tuple)): - raise_unsupported_operand_types("<=", (type(self), type(other))) + __getitem__ = array_item_operation - return array_le_operation(self, other).guess_type() + slice = array_slice_operation - @overload - def __ge__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ... + length = array_length_operation - @overload - def __ge__(self, other: list | tuple) -> BooleanVar: ... + range: ClassVar[ + FunctionVar[ + ReflexCallable[ + [int, VarWithDefault[int | None], VarWithDefault[int]], Sequence[int] + ] + ] + ] = array_range_operation - def __ge__(self, other: Any): - """Check if the array is greater than or equal to another array. + contains = array_contains_field_operation - Args: - other: The other array. + pluck = array_pluck_operation - Returns: - The array greater than or equal operation. - """ - if not isinstance(other, (ArrayVar, list, tuple)): - raise_unsupported_operand_types(">=", (type(self), type(other))) + __rmul__ = __mul__ = repeat_array_operation + + __lt__ = array_lt_operation + + __gt__ = array_gt_operation + + __le__ = array_le_operation - return array_ge_operation(self, other).guess_type() + __ge__ = array_ge_operation def foreach( - self: ARRAY_VAR_OF_LIST_ELEMENT[INNER_ARRAY_VAR], + self: ArrayVar[Sequence[INNER_ARRAY_VAR]], fn: Callable[[Var[INNER_ARRAY_VAR]], ANOTHER_ARRAY_VAR] | Callable[[], ANOTHER_ARRAY_VAR], - ) -> ArrayVar[List[ANOTHER_ARRAY_VAR]]: + ) -> ArrayVar[Sequence[ANOTHER_ARRAY_VAR]]: """Apply a function to each element of the array. Args: @@ -1184,7 +875,7 @@ def foreach( # generic number var number_var = Var("").to(NumberVar, int) - first_arg_type = self[number_var]._var_type + first_arg_type = self.__getitem__(number_var)._var_type arg_name = get_unique_variable_name() @@ -1197,23 +888,23 @@ def foreach( ).guess_type(), ) - function_var: FunctionVar[ - ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR] - ] = ArgsFunctionOperation.create( - (arg_name,), - Var.create(fn(first_arg)), # type: ignore + function_var = cast( + Var[ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR]], + ArgsFunctionOperation.create( + (arg_name,), + Var.create(fn(first_arg)), # type: ignore + ), ) - return map_array_operation(self, function_var).guess_type() + return map_array_operation.call(self, function_var).guess_type() -LIST_ELEMENT = TypeVar("LIST_ELEMENT") +LIST_ELEMENT = TypeVar("LIST_ELEMENT", covariant=True) ARRAY_VAR_OF_LIST_ELEMENT = TypeAliasType( "ARRAY_VAR_OF_LIST_ELEMENT", Union[ - ArrayVar[List[LIST_ELEMENT]], - ArrayVar[Tuple[LIST_ELEMENT, ...]], + ArrayVar[Sequence[LIST_ELEMENT]], ArrayVar[Set[LIST_ELEMENT]], ], type_params=(LIST_ELEMENT,), @@ -1229,9 +920,8 @@ class LiteralArrayVar(CachedVarOperation, LiteralVar, ArrayVar[ARRAY_VAR_TYPE]): """Base class for immutable literal array vars.""" _var_value: Union[ - List[Union[Var, Any]], + Sequence[Union[Var, Any]], Set[Union[Var, Any]], - Tuple[Union[Var, Any], ...], ] = dataclasses.field(default_factory=list) @cached_property_no_lock @@ -1310,20 +1000,47 @@ def create( ) -@var_operation -def string_split_operation(string: Var[str], sep: Var[str]): - """Split a string. +class StringVar(Var[STRING_TYPE], python_types=str): + """Base class for immutable string vars.""" - Args: - string: The string to split. - sep: The separator. + __add__ = string_concat_operation - Returns: - The split string. - """ - return var_operation_return( - js_expression=f"{string}.split({sep})", var_type=List[str] - ) + __radd__ = reverse_string_concat_operation + + __getitem__ = string_item_operation + + lower = string_lower_operation + + upper = string_upper_operation + + strip = string_strip_operation + + contains = string_contains_field_operation + + split = string_split_operation + + length = split.chain(array_length_operation) + + reversed = split.chain(array_reverse_operation).chain(array_join_operation) + + startswith = string_starts_with_operation + + __rmul__ = __mul__ = repeat_string_operation + + __lt__ = string_lt_operation + + __gt__ = string_gt_operation + + __le__ = string_le_operation + + __ge__ = string_ge_operation + + +# Compile regex for finding reflex var tags. +_decode_var_pattern_re = ( + rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}" +) +_decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL) @dataclasses.dataclass( @@ -1331,200 +1048,200 @@ def string_split_operation(string: Var[str], sep: Var[str]): frozen=True, **{"slots": True} if sys.version_info >= (3, 10) else {}, ) -class ArraySliceOperation(CachedVarOperation, ArrayVar): - """Base class for immutable string vars that are the result of a string slice operation.""" - - _array: ArrayVar = dataclasses.field( - default_factory=lambda: LiteralArrayVar.create([]) - ) - _start: NumberVar | int = dataclasses.field(default_factory=lambda: 0) - _stop: NumberVar | int = dataclasses.field(default_factory=lambda: 0) - _step: NumberVar | int = dataclasses.field(default_factory=lambda: 1) - - @cached_property_no_lock - def _cached_var_name(self) -> str: - """The name of the var. - - Returns: - The name of the var. - - Raises: - ValueError: If the slice step is zero. - """ - start, end, step = self._start, self._stop, self._step +class LiteralStringVar(LiteralVar, StringVar[str]): + """Base class for immutable literal string vars.""" - normalized_start = ( - LiteralVar.create(start) if start is not None else Var(_js_expr="undefined") - ) - normalized_end = ( - LiteralVar.create(end) if end is not None else Var(_js_expr="undefined") - ) - if step is None: - return f"{str(self._array)}.slice({str(normalized_start)}, {str(normalized_end)})" - if not isinstance(step, Var): - if step < 0: - actual_start = end + 1 if end is not None else 0 - actual_end = start + 1 if start is not None else self._array.length() - return str(self._array[actual_start:actual_end].reverse()[::-step]) - if step == 0: - raise ValueError("slice step cannot be zero") - return f"{str(self._array)}.slice({str(normalized_start)}, {str(normalized_end)}).filter((_, i) => i % {str(step)} === 0)" - - actual_start_reverse = end + 1 if end is not None else 0 - actual_end_reverse = start + 1 if start is not None else self._array.length() - - return f"{str(self.step)} > 0 ? {str(self._array)}.slice({str(normalized_start)}, {str(normalized_end)}).filter((_, i) => i % {str(step)} === 0) : {str(self._array)}.slice({str(actual_start_reverse)}, {str(actual_end_reverse)}).reverse().filter((_, i) => i % {str(-step)} === 0)" + _var_value: str = dataclasses.field(default="") @classmethod def create( cls, - array: ArrayVar, - slice: slice, + value: str, + _var_type: GenericType | None = None, _var_data: VarData | None = None, - ) -> ArraySliceOperation: + ) -> StringVar: """Create a var from a string value. Args: - array: The array. - slice: The slice. + value: The value to create the var from. + _var_type: The type of the var. _var_data: Additional hooks and imports associated with the Var. Returns: The var. """ - return cls( - _js_expr="", - _var_type=array._var_type, - _var_data=_var_data, - _array=array, - _start=slice.start, - _stop=slice.stop, - _step=slice.step, - ) + # Determine var type in case the value is inherited from str. + _var_type = _var_type or type(value) or str + if REFLEX_VAR_OPENING_TAG in value: + strings_and_vals: list[Var | str] = [] + offset = 0 -@var_operation -def array_pluck_operation( - array: Var[ARRAY_VAR_TYPE], - field: Var[str], -) -> CustomVarOperationReturn[List]: - """Pluck a field from an array of objects. - - Args: - array: The array to pluck from. - field: The field to pluck from the objects in the array. - - Returns: - The reversed array. - """ - return var_operation_return( - js_expression=f"{array}.map(e=>e?.[{field}])", - var_type=List[Any], - ) + # Find all tags + while m := _decode_var_pattern.search(value): + start, end = m.span() + strings_and_vals.append(value[:start]) -@var_operation -def array_join_operation(array: Var[ARRAY_VAR_TYPE], sep: Var[str]): - """Join the elements of an array. + serialized_data = m.group(1) - Args: - array: The array. - sep: The separator. + if serialized_data.isnumeric() or ( + serialized_data[0] == "-" and serialized_data[1:].isnumeric() + ): + # This is a global immutable var. + var = _global_vars[int(serialized_data)] + strings_and_vals.append(var) + value = value[(end + len(var._js_expr)) :] - Returns: - The joined elements. - """ - return var_operation_return(js_expression=f"{array}.join({sep})", var_type=str) + offset += end - start + strings_and_vals.append(value) -@var_operation -def array_reverse_operation( - array: Var[ARRAY_VAR_TYPE], -) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]: - """Reverse an array. + filtered_strings_and_vals = [ + s for s in strings_and_vals if isinstance(s, Var) or s + ] + if len(filtered_strings_and_vals) == 1: + only_string = filtered_strings_and_vals[0] + if isinstance(only_string, str): + return LiteralVar.create(only_string).to(StringVar, _var_type) + else: + return only_string.to(StringVar, only_string._var_type) - Args: - array: The array to reverse. + if len( + literal_strings := [ + s + for s in filtered_strings_and_vals + if isinstance(s, (str, LiteralStringVar)) + ] + ) == len(filtered_strings_and_vals): + return LiteralStringVar.create( + "".join( + s._var_value if isinstance(s, LiteralStringVar) else s + for s in literal_strings + ), + _var_type=_var_type, + _var_data=VarData.merge( + _var_data, + *( + s._get_all_var_data() + for s in filtered_strings_and_vals + if isinstance(s, Var) + ), + ), + ) - Returns: - The reversed array. - """ - return var_operation_return( - js_expression=f"{array}.slice().reverse()", - type_computer=passthrough_unary_type_computer(ReflexCallable[[List], List]), - ) + concat_result = ConcatVarOperation.create( + *filtered_strings_and_vals, + _var_data=_var_data, + ) + return ( + concat_result + if _var_type is str + else concat_result.to(StringVar, _var_type) + ) -@var_operation -def array_lt_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): - """Check if an array is less than another array. + return LiteralStringVar( + _js_expr=json.dumps(value), + _var_type=_var_type, + _var_data=_var_data, + _var_value=value, + ) - Args: - lhs: The left-hand side array. - rhs: The right-hand side array. + def __hash__(self) -> int: + """Get the hash of the var. - Returns: - The array less than operation. - """ - return var_operation_return(js_expression=f"{lhs} < {rhs}", var_type=bool) + Returns: + The hash of the var. + """ + return hash((self.__class__.__name__, self._var_value)) + def json(self) -> str: + """Get the JSON representation of the var. -@var_operation -def array_gt_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): - """Check if an array is greater than another array. + Returns: + The JSON representation of the var. + """ + return json.dumps(self._var_value) - Args: - lhs: The left-hand side array. - rhs: The right-hand side array. - Returns: - The array greater than operation. - """ - return var_operation_return(js_expression=f"{lhs} > {rhs}", var_type=bool) +@dataclasses.dataclass( + eq=False, + frozen=True, + **{"slots": True} if sys.version_info >= (3, 10) else {}, +) +class ConcatVarOperation(CachedVarOperation, StringVar[str]): + """Representing a concatenation of literal string vars.""" + _var_value: Tuple[Var, ...] = dataclasses.field(default_factory=tuple) -@var_operation -def array_le_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): - """Check if an array is less than or equal to another array. + @cached_property_no_lock + def _cached_var_name(self) -> str: + """The name of the var. - Args: - lhs: The left-hand side array. - rhs: The right-hand side array. + Returns: + The name of the var. + """ + list_of_strs: List[Union[str, Var]] = [] + last_string = "" + for var in self._var_value: + if isinstance(var, LiteralStringVar): + last_string += var._var_value + else: + if last_string: + list_of_strs.append(last_string) + last_string = "" + list_of_strs.append(var) - Returns: - The array less than or equal operation. - """ - return var_operation_return(js_expression=f"{lhs} <= {rhs}", var_type=bool) + if last_string: + list_of_strs.append(last_string) + list_of_strs_filtered = [ + str(LiteralVar.create(s)) for s in list_of_strs if isinstance(s, Var) or s + ] -@var_operation -def array_ge_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): - """Check if an array is greater than or equal to another array. + if len(list_of_strs_filtered) == 1: + return list_of_strs_filtered[0] - Args: - lhs: The left-hand side array. - rhs: The right-hand side array. + return "(" + "+".join(list_of_strs_filtered) + ")" - Returns: - The array greater than or equal operation. - """ - return var_operation_return(js_expression=f"{lhs} >= {rhs}", var_type=bool) + @cached_property_no_lock + def _cached_get_all_var_data(self) -> VarData | None: + """Get all the VarData asVarDatae Var. + Returns: + The VarData associated with the Var. + """ + return VarData.merge( + *[ + var._get_all_var_data() + for var in self._var_value + if isinstance(var, Var) + ], + self._var_data, + ) -@var_operation -def array_length_operation(array: Var[ARRAY_VAR_TYPE]): - """Get the length of an array. + @classmethod + def create( + cls, + *value: Var | str, + _var_data: VarData | None = None, + ) -> ConcatVarOperation: + """Create a var from a string value. - Args: - array: The array. + Args: + value: The values to concatenate. + _var_data: Additional hooks and imports associated with the Var. - Returns: - The length of the array. - """ - return var_operation_return( - js_expression=f"{array}.length", - var_type=int, - ) + Returns: + The var. + """ + return cls( + _js_expr="", + _var_type=str, + _var_data=_var_data, + _var_value=tuple(map(LiteralVar.create, value)), + ) def is_tuple_type(t: GenericType) -> bool: @@ -1541,207 +1258,6 @@ def is_tuple_type(t: GenericType) -> bool: return get_origin(t) is tuple -@var_operation -def array_item_operation(array: Var[ARRAY_VAR_TYPE], index: Var[int]): - """Get an item from an array. - - Args: - array: The array. - index: The index of the item. - - Returns: - The item from the array. - """ - - def type_computer(*args): - if len(args) == 0: - return ( - ReflexCallable[[List[Any], int], Any], - functools.partial(type_computer, *args), - ) - - array = args[0] - array_args = typing.get_args(array._var_type) - - if len(args) == 1: - return ( - ReflexCallable[[int], unionize(*array_args)], - functools.partial(type_computer, *args), - ) - - index = args[1] - - if ( - array_args - and isinstance(index, LiteralNumberVar) - and is_tuple_type(array._var_type) - ): - index_value = int(index._var_value) - element_type = array_args[index_value % len(array_args)] - else: - element_type = unionize(*array_args) - - return (ReflexCallable[[], element_type], None) - - return var_operation_return( - js_expression=f"{str(array)}.at({str(index)})", - type_computer=type_computer, - ) - - -@var_operation -def array_range_operation(start: Var[int], stop: Var[int], step: Var[int]): - """Create a range of numbers. - - Args: - start: The start of the range. - stop: The end of the range. - step: The step of the range. - - Returns: - The range of numbers. - """ - return var_operation_return( - js_expression=f"Array.from({{ length: ({str(stop)} - {str(start)}) / {str(step)} }}, (_, i) => {str(start)} + i * {str(step)})", - var_type=List[int], - ) - - -@var_operation -def array_contains_field_operation( - haystack: Var[ARRAY_VAR_TYPE], needle: Var, field: Var[str] -): - """Check if an array contains an element. - - Args: - haystack: The array to check. - needle: The element to check for. - field: The field to check. - - Returns: - The array contains operation. - """ - return var_operation_return( - js_expression=f"{haystack}.some(obj => obj[{field}] === {needle})", - var_type=bool, - ) - - -@var_operation -def array_contains_operation(haystack: Var[ARRAY_VAR_TYPE], needle: Var): - """Check if an array contains an element. - - Args: - haystack: The array to check. - needle: The element to check for. - - Returns: - The array contains operation. - """ - return var_operation_return( - js_expression=f"{haystack}.includes({needle})", - var_type=bool, - ) - - -@var_operation -def repeat_array_operation( - array: Var[ARRAY_VAR_TYPE], count: Var[int] -) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]: - """Repeat an array a number of times. - - Args: - array: The array to repeat. - count: The number of times to repeat the array. - - Returns: - The repeated array. - """ - - def type_computer(*args: Var): - if not args: - return ( - ReflexCallable[[List[Any], int], List[Any]], - type_computer, - ) - if len(args) == 1: - return ( - ReflexCallable[[int], args[0]._var_type], - functools.partial(type_computer, *args), - ) - return (ReflexCallable[[], args[0]._var_type], None) - - return var_operation_return( - js_expression=f"Array.from({{ length: {count} }}).flatMap(() => {array})", - type_computer=type_computer, - ) - - -if TYPE_CHECKING: - pass - - -@var_operation -def map_array_operation( - array: Var[ARRAY_VAR_OF_LIST_ELEMENT[INNER_ARRAY_VAR]], - function: Var[ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR]], -) -> CustomVarOperationReturn[List[ANOTHER_ARRAY_VAR]]: - """Map a function over an array. - - Args: - array: The array. - function: The function to map. - - Returns: - The mapped array. - """ - - def type_computer(*args: Var): - if not args: - return ( - ReflexCallable[[List[Any], ReflexCallable], List[Any]], - type_computer, - ) - if len(args) == 1: - return ( - ReflexCallable[[ReflexCallable], List[Any]], - functools.partial(type_computer, *args), - ) - return (ReflexCallable[[], List[args[0]._var_type]], None) - - return var_operation_return( - js_expression=f"{array}.map({function})", - type_computer=nary_type_computer( - ReflexCallable[[List[Any], ReflexCallable], List[Any]], - ReflexCallable[[ReflexCallable], List[Any]], - computer=lambda args: List[unwrap_reflex_callalbe(args[1]._var_type)[1]], # type: ignore - ), - ) - - -@var_operation -def array_concat_operation( - lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE] -) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]: - """Concatenate two arrays. - - Args: - lhs: The left-hand side array. - rhs: The right-hand side array. - - Returns: - The concatenated array. - """ - return var_operation_return( - js_expression=f"[...{lhs}, ...{rhs}]", - type_computer=nary_type_computer( - ReflexCallable[[List[Any], List[Any]], List[Any]], - ReflexCallable[[List[Any]], List[Any]], - computer=lambda args: unionize(args[0]._var_type, args[1]._var_type), - ), - ) - - class ColorVar(StringVar[Color], python_types=Color): """Base class for immutable color vars.""" @@ -1802,7 +1318,7 @@ def _cached_var_name(self) -> str: Returns: The name of the var. """ - alpha = self._var_value.alpha + alpha = cast(Union[Var[bool], bool], self._var_value.alpha) alpha = ( ternary_operation( alpha, diff --git a/tests/units/components/core/test_foreach.py b/tests/units/components/core/test_foreach.py index 228165d3e1a..f781a9254ac 100644 --- a/tests/units/components/core/test_foreach.py +++ b/tests/units/components/core/test_foreach.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Set, Tuple, Union +from typing import Dict, List, Sequence, Set, Tuple, Union import pytest @@ -115,8 +115,10 @@ def display_colors_set(color): return box(text(color)) -def display_nested_list_element(element: ArrayVar[List[str]], index: NumberVar[int]): - assert element._var_type == List[str] +def display_nested_list_element( + element: ArrayVar[Sequence[str]], index: NumberVar[int] +): + assert element._var_type == Sequence[str] assert index._var_type is int return box(text(element[index])) From 53b98543cc656f42a21b84b9fdb15380d3d08e73 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 15 Nov 2024 14:58:19 -0800 Subject: [PATCH 16/86] default factory --- reflex/vars/sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 7b71a8208ee..0668547ea32 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -720,7 +720,7 @@ class SliceVar(Var[slice], python_types=slice): class LiteralSliceVar(CachedVarOperation, LiteralVar, SliceVar): """Base class for immutable literal slice vars.""" - _var_value: slice = dataclasses.field(default=slice(None)) + _var_value: slice = dataclasses.field(default_factory=lambda: slice(None)) @cached_property_no_lock def _cached_var_name(self) -> str: From 88cfb3b7e25799f12cf3296f0084e4da2411b31e Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 15 Nov 2024 15:11:44 -0800 Subject: [PATCH 17/86] handle var at top level --- reflex/compiler/compiler.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index 9f81f319d4a..e9e3f57e2c4 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -560,7 +560,11 @@ def compile_unevaluated_page( """ # Generate the component if it is a callable. component = page.component - component = component if isinstance(component, Component) else component() + component = ( + component + if isinstance(component, Component) + else (Fragment.create(component) if isinstance(component, Var) else component()) + ) # unpack components that return tuples in an rx.fragment. if isinstance(component, tuple): From 702670ff26dfcf969d81b126cb9a4f25993a1f0c Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 15 Nov 2024 15:47:46 -0800 Subject: [PATCH 18/86] add components to var data --- reflex/components/base/bare.py | 39 +++++++++++++++++++++++----------- reflex/components/component.py | 13 +++++------- reflex/vars/base.py | 29 +++++++++++++++++-------- 3 files changed, 52 insertions(+), 29 deletions(-) diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index c70b4c844c1..4758181e3e2 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -4,7 +4,7 @@ from typing import Any, Iterator -from reflex.components.component import Component, LiteralComponentVar +from reflex.components.component import Component from reflex.components.tags import Tag from reflex.components.tags.tagless import Tagless from reflex.utils.imports import ParsedImportDict @@ -39,8 +39,11 @@ def _get_all_hooks_internal(self) -> dict[str, None]: The hooks for the component. """ hooks = super()._get_all_hooks_internal() - if isinstance(self.contents, LiteralComponentVar): - hooks |= self.contents._var_value._get_all_hooks_internal() + if isinstance(self.contents, Var): + var_data = self.contents._get_all_var_data() + if var_data: + for component in var_data.components: + hooks |= component._get_all_hooks_internal() return hooks def _get_all_hooks(self) -> dict[str, None]: @@ -50,8 +53,11 @@ def _get_all_hooks(self) -> dict[str, None]: The hooks for the component. """ hooks = super()._get_all_hooks() - if isinstance(self.contents, LiteralComponentVar): - hooks |= self.contents._var_value._get_all_hooks() + if isinstance(self.contents, Var): + var_data = self.contents._get_all_var_data() + if var_data: + for component in var_data.components: + hooks |= component._get_all_hooks() return hooks def _get_all_imports(self) -> ParsedImportDict: @@ -61,7 +67,7 @@ def _get_all_imports(self) -> ParsedImportDict: The imports for the component. """ imports = super()._get_all_imports() - if isinstance(self.contents, LiteralComponentVar): + if isinstance(self.contents, Var): var_data = self.contents._get_all_var_data() if var_data: imports |= {k: list(v) for k, v in var_data.imports} @@ -74,8 +80,11 @@ def _get_all_dynamic_imports(self) -> set[str]: The dynamic imports. """ dynamic_imports = super()._get_all_dynamic_imports() - if isinstance(self.contents, LiteralComponentVar): - dynamic_imports |= self.contents._var_value._get_all_dynamic_imports() + if isinstance(self.contents, Var): + var_data = self.contents._get_all_var_data() + if var_data: + for component in var_data.components: + dynamic_imports |= component._get_all_dynamic_imports() return dynamic_imports def _get_all_custom_code(self) -> set[str]: @@ -85,8 +94,11 @@ def _get_all_custom_code(self) -> set[str]: The custom code. """ custom_code = super()._get_all_custom_code() - if isinstance(self.contents, LiteralComponentVar): - custom_code |= self.contents._var_value._get_all_custom_code() + if isinstance(self.contents, Var): + var_data = self.contents._get_all_var_data() + if var_data: + for component in var_data.components: + custom_code |= component._get_all_custom_code() return custom_code def _get_all_refs(self) -> set[str]: @@ -96,8 +108,11 @@ def _get_all_refs(self) -> set[str]: The refs for the children. """ refs = super()._get_all_refs() - if isinstance(self.contents, LiteralComponentVar): - refs |= self.contents._var_value._get_all_refs() + if isinstance(self.contents, Var): + var_data = self.contents._get_all_var_data() + if var_data: + for component in var_data.components: + refs |= component._get_all_refs() return refs def _render(self) -> Tag: diff --git a/reflex/components/component.py b/reflex/components/component.py index face5d557f8..8cf0001d2dd 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -2521,17 +2521,14 @@ def _cached_get_all_var_data(self) -> VarData | None: "@emotion/react": [ ImportVar(tag="jsx"), ], - } - ), - VarData( - imports=self._var_value._get_all_imports(), - ), - VarData( - imports={ "react": [ ImportVar(tag="Fragment"), ], - } + }, + components=(self._var_value,), + ), + VarData( + imports=self._var_value._get_all_imports(), ), ) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 8945d130bcd..c8aa997318f 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -76,6 +76,7 @@ ) if TYPE_CHECKING: + from reflex.components.component import BaseComponent from reflex.state import BaseState from .function import ArgsFunctionOperation @@ -166,12 +167,16 @@ class VarData: # Hooks that need to be present in the component to render this var hooks: Tuple[str, ...] = dataclasses.field(default_factory=tuple) + # Components that need to be present in the component to render this var + components: Tuple[BaseComponent, ...] = dataclasses.field(default_factory=tuple) + def __init__( self, state: str = "", field_name: str = "", imports: ImportDict | ParsedImportDict | None = None, hooks: dict[str, None] | None = None, + components: Iterable[BaseComponent] | None = None, ): """Initialize the var data. @@ -180,6 +185,7 @@ def __init__( field_name: The name of the field in the state. imports: Imports needed to render this var. hooks: Hooks that need to be present in the component to render this var. + components: Components that need to be present in the component to render this var. """ immutable_imports: ImmutableParsedImportDict = tuple( sorted( @@ -190,6 +196,9 @@ def __init__( object.__setattr__(self, "field_name", field_name) object.__setattr__(self, "imports", immutable_imports) object.__setattr__(self, "hooks", tuple(hooks or {})) + object.__setattr__( + self, "components", tuple(components) if components is not None else tuple() + ) def old_school_imports(self) -> ImportDict: """Return the imports as a mutable dict. @@ -235,15 +244,17 @@ def merge(*all: VarData | None) -> VarData | None: *(var_data.imports for var_data in all_var_datas) ) - if state or _imports or hooks or field_name: - return VarData( - state=state, - field_name=field_name, - imports=_imports, - hooks=hooks, - ) + components = tuple( + component for var_data in all_var_datas for component in var_data.components + ) - return None + return VarData( + state=state, + field_name=field_name, + imports=_imports, + hooks=hooks, + components=components, + ) def __bool__(self) -> bool: """Check if the var data is non-empty. @@ -251,7 +262,7 @@ def __bool__(self) -> bool: Returns: True if any field is set to a non-default value. """ - return bool(self.state or self.imports or self.hooks or self.field_name) + return any(getattr(self, field.name) for field in dataclasses.fields(self)) @classmethod def from_state(cls, state: Type[BaseState] | str, field_name: str = "") -> VarData: From eac54d60d238d0a9cd624a91c648478fab036bc7 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 15 Nov 2024 15:54:04 -0800 Subject: [PATCH 19/86] call guess type --- reflex/vars/function.py | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index c79a44ce02a..76dcadffc7b 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -181,20 +181,20 @@ def partial(self, *args: Var | Any) -> FunctionVar: # type: ignore ) @overload - def call(self: FunctionVar[ReflexCallable[[], R]]) -> VarOperationCall[[], R]: ... + def call(self: FunctionVar[ReflexCallable[[], R]]) -> Var[R]: ... @overload def call( self: FunctionVar[ReflexCallable[[VarWithDefault[V1]], R]], arg1: Union[V1, Var[V1], Unset] = Unset(), - ) -> VarOperationCall[[VarWithDefault[V1]], R]: ... + ) -> Var[R]: ... @overload def call( self: FunctionVar[ReflexCallable[[VarWithDefault[V1], VarWithDefault[V2]], R]], arg1: Union[V1, Var[V1], Unset] = Unset(), arg2: Union[V2, Var[V2], Unset] = Unset(), - ) -> VarOperationCall[[VarWithDefault[V1], VarWithDefault[V2]], R]: ... + ) -> Var[R]: ... @overload def call( @@ -206,21 +206,19 @@ def call( arg1: Union[V1, Var[V1], Unset] = Unset(), arg2: Union[V2, Var[V2], Unset] = Unset(), arg3: Union[V3, Var[V3], Unset] = Unset(), - ) -> VarOperationCall[ - [VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3]], R - ]: ... + ) -> Var[R]: ... @overload def call( self: FunctionVar[ReflexCallable[[V1], R]], arg1: Union[V1, Var[V1]] - ) -> VarOperationCall[[V1], R]: ... + ) -> Var[R]: ... @overload def call( self: FunctionVar[ReflexCallable[[V1, VarWithDefault[V2]], R]], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2], Unset] = Unset(), - ) -> VarOperationCall[[V1, VarWithDefault[V2]], R]: ... + ) -> Var[R]: ... @overload def call( @@ -230,7 +228,7 @@ def call( arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2], Unset] = Unset(), arg3: Union[V3, Var[V3], Unset] = Unset(), - ) -> VarOperationCall[[V1, VarWithDefault[V2], VarWithDefault[V3]], R]: ... + ) -> Var[R]: ... @overload def call( @@ -245,7 +243,7 @@ def call( arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], arg3: Union[V3, Var[V3], Unset] = Unset(), - ) -> VarOperationCall[[V1, V2, VarWithDefault[V3]], R]: ... + ) -> Var[R]: ... @overload def call( @@ -253,7 +251,7 @@ def call( arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], arg3: Union[V3, Var[V3]], - ) -> VarOperationCall[[V1, V2, V3], R]: ... + ) -> Var[R]: ... @overload def call( @@ -262,7 +260,7 @@ def call( arg2: Union[V2, Var[V2]], arg3: Union[V3, Var[V3]], arg4: Union[V4, Var[V4]], - ) -> VarOperationCall[[V1, V2, V3, V4], R]: ... + ) -> Var[R]: ... @overload def call( @@ -272,7 +270,7 @@ def call( arg3: Union[V3, Var[V3]], arg4: Union[V4, Var[V4]], arg5: Union[V5, Var[V5]], - ) -> VarOperationCall[[V1, V2, V3, V4, V5], R]: ... + ) -> Var[R]: ... @overload def call( @@ -283,13 +281,13 @@ def call( arg4: Union[V4, Var[V4]], arg5: Union[V5, Var[V5]], arg6: Union[V6, Var[V6]], - ) -> VarOperationCall[[V1, V2, V3, V4, V5, V6], R]: ... + ) -> Var[R]: ... # Capture Any to allow for arbitrary number of arguments @overload - def call(self: FunctionVar[NoReturn], *args: Var | Any) -> VarOperationCall: ... + def call(self: FunctionVar[NoReturn], *args: Var | Any) -> Var: ... - def call(self, *args: Var | Any) -> VarOperationCall: # type: ignore + def call(self, *args: Var | Any) -> Var: # pyright: ignore [reportInconsistentOverload] """Call the function with the given arguments. Args: @@ -315,7 +313,7 @@ def call(self, *args: Var | Any) -> VarOperationCall: # type: ignore args = tuple(map(LiteralVar.create, args)) self._pre_check(*args) return_type = self._return_type(*args) - return VarOperationCall.create(self, *args, _var_type=return_type) + return VarOperationCall.create(self, *args, _var_type=return_type).guess_type() def chain( self: FunctionVar[ReflexCallable[P, R]], From 7f1dc7c8417ba1e908eed0b671d9b9f454ef11ed Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 15 Nov 2024 15:58:03 -0800 Subject: [PATCH 20/86] remove .bool --- reflex/vars/sequence.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 0668547ea32..a3f0c80ebc4 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -189,8 +189,11 @@ def string_contains_field_operation( The string contains operation. """ return var_operation_return( - js_expression=f"{field.bool()} ? {haystack}.some(obj => obj[{field}] === {needle}) : {haystack}.some(obj => obj === {needle})", + js_expression=f"isTrue({field}) ? {haystack}.some(obj => obj[{field}] === {needle}) : {haystack}.some(obj => obj === {needle})", var_type=bool, + var_data=VarData( + imports=_IS_TRUE_IMPORT, + ), ) From 92b1232806614f319966c53951068136639c0a23 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 15 Nov 2024 16:16:13 -0800 Subject: [PATCH 21/86] would this fix it? no clue --- reflex/components/tags/iter_tag.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/components/tags/iter_tag.py b/reflex/components/tags/iter_tag.py index 38ecaf81c79..3f7aa47f283 100644 --- a/reflex/components/tags/iter_tag.py +++ b/reflex/components/tags/iter_tag.py @@ -131,7 +131,7 @@ def render_component(self) -> Component: component = self.render_fn(arg, index) # Nested foreach components or cond must be wrapped in fragments. - if isinstance(component, (Foreach, Cond)): + if isinstance(component, (Foreach, Cond, Var)): component = Fragment.create(component) # Set the component key. From 079cc56f59c7ec9f05570b4561fd6ac8d1b64d81 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 15 Nov 2024 16:59:28 -0800 Subject: [PATCH 22/86] more types --- reflex/utils/types.py | 68 ++++++++++++++++++++++++++------- tests/units/utils/test_utils.py | 14 ++++++- 2 files changed, 67 insertions(+), 15 deletions(-) diff --git a/reflex/utils/types.py b/reflex/utils/types.py index 27b6e7ce7f1..0d6f878ecf7 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -7,6 +7,7 @@ import inspect import sys import types +from collections import abc from functools import cached_property, lru_cache, wraps from typing import ( TYPE_CHECKING, @@ -21,6 +22,7 @@ Sequence, Tuple, Type, + TypeVar, Union, _GenericAlias, # type: ignore get_args, @@ -29,6 +31,7 @@ from typing import get_origin as get_origin_og import sqlalchemy +import typing_extensions import reflex from reflex.components.core.breakpoints import Breakpoints @@ -810,24 +813,63 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo provided_args = get_args(possible_subclass) accepted_args = get_args(possible_superclass) - if accepted_type_origin is Union: - if provided_type_origin is not Union: - return any( - typehint_issubclass(possible_subclass, accepted_arg) - for accepted_arg in accepted_args - ) + if provided_type_origin is Union: return all( - any( - typehint_issubclass(provided_arg, accepted_arg) - for accepted_arg in accepted_args - ) + typehint_issubclass(provided_arg, possible_superclass) for provided_arg in provided_args ) + if accepted_type_origin is Union: + return any( + typehint_issubclass(possible_subclass, accepted_arg) + for accepted_arg in accepted_args + ) + + # Check specifically for Sequence and Iterable + if (accepted_type_origin or possible_superclass) in ( + Sequence, + abc.Sequence, + Iterable, + abc.Iterable, + ): + iterable_type = accepted_args[0] if accepted_args else Any + + if provided_type_origin is None: + if not issubclass( + possible_subclass, (accepted_type_origin or possible_superclass) + ): + return False + + if issubclass(possible_subclass, str) and not isinstance( + iterable_type, TypeVar + ): + return typehint_issubclass(str, iterable_type) + + if not issubclass( + provided_type_origin, (accepted_type_origin or possible_superclass) + ): + return False + + if not isinstance(iterable_type, (TypeVar, typing_extensions.TypeVar)): + if provided_type_origin in (list, tuple, set): + # Ensure all specific types are compatible with accepted types + return all( + typehint_issubclass(provided_arg, iterable_type) + for provided_arg in provided_args + if provided_arg is not ... # Ellipsis in Tuples + ) + if possible_subclass is dict: + # Ensure all specific types are compatible with accepted types + return all( + typehint_issubclass(provided_arg, iterable_type) + for provided_arg in provided_args[:1] + ) + return True + # Check if the origin of both types is the same (e.g., list for List[int]) - # This probably should be issubclass instead of == - if (provided_type_origin or possible_subclass) != ( - accepted_type_origin or possible_superclass + if not issubclass( + provided_type_origin or possible_subclass, + accepted_type_origin or possible_superclass, ): return False diff --git a/tests/units/utils/test_utils.py b/tests/units/utils/test_utils.py index dd1a3b3ef7d..2a71f3461d7 100644 --- a/tests/units/utils/test_utils.py +++ b/tests/units/utils/test_utils.py @@ -2,7 +2,7 @@ import typing from functools import cached_property from pathlib import Path -from typing import Any, ClassVar, Dict, List, Literal, Type, Union +from typing import Any, ClassVar, Dict, List, Literal, Sequence, Tuple, Type, Union import pytest import typer @@ -109,10 +109,20 @@ def test_is_generic_alias(cls: type, expected: bool): (Dict[str, str], dict[str, str], True), (Dict[str, str], dict[str, Any], True), (Dict[str, Any], dict[str, Any], True), + (List[int], Sequence[int], True), + (List[str], Sequence[int], False), + (Tuple[int], Sequence[int], True), + (Tuple[int, str], Sequence[int], False), + (Tuple[int, ...], Sequence[int], True), + (str, Sequence[int], False), + (str, Sequence[str], True), ], ) def test_typehint_issubclass(subclass, superclass, expected): - assert types.typehint_issubclass(subclass, superclass) == expected + if expected: + assert types.typehint_issubclass(subclass, superclass) + else: + assert not types.typehint_issubclass(subclass, superclass) def test_validate_invalid_bun_path(mocker): From 7d0a4f7133abb805977067e26c32e6f096bc02b5 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 15 Nov 2024 17:12:42 -0800 Subject: [PATCH 23/86] make safe issubclass --- reflex/utils/types.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/reflex/utils/types.py b/reflex/utils/types.py index 0d6f878ecf7..7de4fe20673 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -778,6 +778,24 @@ def wrapper(*args, **kwargs): StateIterBases = get_base_class(StateIterVar) +def safe_issubclass(cls: Any, class_or_tuple: Any, /) -> bool: + """Check if a class is a subclass of another class or a tuple of classes. + + Args: + cls: The class to check. + class_or_tuple: The class or tuple of classes to check against. + + Returns: + Whether the class is a subclass of the other class or tuple of classes. + """ + try: + return issubclass(cls, class_or_tuple) + except TypeError as e: + raise TypeError( + f"Invalid arguments for issubclass: {cls}, {class_or_tuple}" + ) from e + + def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> bool: """Check if a type hint is a subclass of another type hint. @@ -798,7 +816,7 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo if provided_type_origin is None and accepted_type_origin is None: # In this case, we are dealing with a non-generic type, so we can use issubclass - return issubclass(possible_subclass, possible_superclass) + return safe_issubclass(possible_subclass, possible_superclass) # Remove this check when Python 3.10 is the minimum supported version if hasattr(types, "UnionType"): @@ -835,17 +853,17 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo iterable_type = accepted_args[0] if accepted_args else Any if provided_type_origin is None: - if not issubclass( + if not safe_issubclass( possible_subclass, (accepted_type_origin or possible_superclass) ): return False - if issubclass(possible_subclass, str) and not isinstance( + if safe_issubclass(possible_subclass, str) and not isinstance( iterable_type, TypeVar ): return typehint_issubclass(str, iterable_type) - if not issubclass( + if not safe_issubclass( provided_type_origin, (accepted_type_origin or possible_superclass) ): return False @@ -867,7 +885,7 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo return True # Check if the origin of both types is the same (e.g., list for List[int]) - if not issubclass( + if not safe_issubclass( provided_type_origin or possible_subclass, accepted_type_origin or possible_superclass, ): From ed1ae0d3a212821c149e4b8b5f4663efa6b1013a Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 15 Nov 2024 17:15:43 -0800 Subject: [PATCH 24/86] add missing return --- reflex/utils/types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/reflex/utils/types.py b/reflex/utils/types.py index 7de4fe20673..6a0a717bb0d 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -862,6 +862,7 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo iterable_type, TypeVar ): return typehint_issubclass(str, iterable_type) + return True if not safe_issubclass( provided_type_origin, (accepted_type_origin or possible_superclass) From 2b05ee98ed5b220b7a6775145516c15c02e0f7ed Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 15 Nov 2024 17:30:45 -0800 Subject: [PATCH 25/86] make it handle slice --- reflex/vars/sequence.py | 51 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index a3f0c80ebc4..cdaae28e427 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -409,6 +409,51 @@ def string_split_operation( ) +def _element_type(array: Var, index: Var) -> Any: + array_args = typing.get_args(array._var_type) + + if ( + array_args + and isinstance(index, LiteralNumberVar) + and is_tuple_type(array._var_type) + ): + index_value = int(index._var_value) + return array_args[index_value % len(array_args)] + + return unionize(*(array_arg for array_arg in array_args if array_arg is not ...)) + + +@var_operation +def array_item_or_slice_operation( + array: Var[Sequence[INNER_ARRAY_VAR]], + index_or_slice: Var[Union[int, slice]], +) -> CustomVarOperationReturn[Union[INNER_ARRAY_VAR, Sequence[INNER_ARRAY_VAR]]]: + """Get an item or slice from an array. + + Args: + array: The array. + index_or_slice: The index or slice. + + Returns: + The item or slice from the array. + """ + return var_operation_return( + js_expression=f"Array.isArray({index_or_slice}) ? at_slice({array}, {index_or_slice}) : {array}.at({index_or_slice})", + type_computer=nary_type_computer( + ReflexCallable[[Sequence, Union[int, slice]], Any], + ReflexCallable[[Union[int, slice]], Any], + computer=lambda args: ( + args[0]._var_type + if args[1]._var_type is slice + else (_element_type(args[0], args[1])) + ), + ), + var_data=VarData( + imports=_AT_SLICE_IMPORT, + ), + ) + + @var_operation def array_slice_operation( array: Var[Sequence[INNER_ARRAY_VAR]], @@ -424,7 +469,7 @@ def array_slice_operation( The item or slice from the array. """ return var_operation_return( - js_expression=f"at_slice({array}, {slice})", + js_expression=f"atSlice({array}, {slice})", type_computer=nary_type_computer( ReflexCallable[[List, slice], Any], ReflexCallable[[slice], Any], @@ -813,7 +858,9 @@ class ArrayVar(Var[ARRAY_VAR_TYPE], python_types=(Sequence, set)): __add__ = array_concat_operation - __getitem__ = array_item_operation + __getitem__ = array_item_or_slice_operation + + at = array_item_operation slice = array_slice_operation From 2e1bc057a48d8cbc114afeed57c4fb8766cde871 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 15 Nov 2024 17:39:14 -0800 Subject: [PATCH 26/86] aaaaa --- reflex/vars/base.py | 1 + reflex/vars/sequence.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index c8aa997318f..95c6f62db57 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -1264,6 +1264,7 @@ def __init__(self, default_value: VAR_TYPE): Args: default_value: The default value. """ + super().__init__("") self._default = default_value @property diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index cdaae28e427..2fdc3bb7a8e 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -818,7 +818,7 @@ def create( """ return cls( _js_expr="", - _var_type=_var_type, + _var_type=slice if _var_type is None else _var_type, _var_data=_var_data, _var_value=value, ) From 8830d5ab77a8b159fd23d54605cfe2253a6a5800 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 12 Dec 2024 07:33:36 +0300 Subject: [PATCH 27/86] update poetry --- poetry.lock | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3bca7998922..4d567aee029 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1147,6 +1147,7 @@ files = [ {file = "nh3-0.2.19-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:00810cd5275f5c3f44b9eb0e521d1a841ee2f8023622de39ffc7d88bd533d8e0"}, {file = "nh3-0.2.19-cp38-abi3-win32.whl", hash = "sha256:7e98621856b0a911c21faa5eef8f8ea3e691526c2433f9afc2be713cb6fbdb48"}, {file = "nh3-0.2.19-cp38-abi3-win_amd64.whl", hash = "sha256:75c7cafb840f24430b009f7368945cb5ca88b2b54bb384ebfba495f16bc9c121"}, + {file = "nh3-0.2.19.tar.gz", hash = "sha256:790056b54c068ff8dceb443eaefb696b84beff58cca6c07afd754d17692a4804"}, ] [[package]] @@ -1356,8 +1357,8 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.22.4", markers = "python_version < \"3.11\""}, ] python-dateutil = ">=2.8.2" @@ -1542,18 +1543,18 @@ type = ["mypy (>=1.11.2)"] [[package]] name = "playwright" -version = "1.49.0" +version = "1.49.1" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.9" files = [ - {file = "playwright-1.49.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:704532a2d8ba580ec9e1895bfeafddce2e3d52320d4eb8aa38e80376acc5cbb0"}, - {file = "playwright-1.49.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e453f02c4e5cc2db7e9759c47e7425f32e50ac76c76b7eb17c69eed72f01c4d8"}, - {file = "playwright-1.49.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:37ae985309184472946a6eb1a237e5d93c9e58a781fa73b75c8751325002a5d4"}, - {file = "playwright-1.49.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:68d94beffb3c9213e3ceaafa66171affd9a5d9162e0c8a3eed1b1132c2e57598"}, - {file = "playwright-1.49.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f12d2aecdb41fc25a624cb15f3e8391c252ebd81985e3d5c1c261fe93779345"}, - {file = "playwright-1.49.0-py3-none-win32.whl", hash = "sha256:91103de52d470594ad375b512d7143fa95d6039111ae11a93eb4fe2f2b4a4858"}, - {file = "playwright-1.49.0-py3-none-win_amd64.whl", hash = "sha256:34d28a2c2d46403368610be4339898dc9c34eb9f7c578207b4715c49743a072a"}, + {file = "playwright-1.49.1-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:1041ffb45a0d0bc44d698d3a5aa3ac4b67c9bd03540da43a0b70616ad52592b8"}, + {file = "playwright-1.49.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9f38ed3d0c1f4e0a6d1c92e73dd9a61f8855133249d6f0cec28648d38a7137be"}, + {file = "playwright-1.49.1-py3-none-macosx_11_0_universal2.whl", hash = "sha256:3be48c6d26dc819ca0a26567c1ae36a980a0303dcd4249feb6f59e115aaddfb8"}, + {file = "playwright-1.49.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:753ca90ee31b4b03d165cfd36e477309ebf2b4381953f2a982ff612d85b147d2"}, + {file = "playwright-1.49.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd9bc8dab37aa25198a01f555f0a2e2c3813fe200fef018ac34dfe86b34994b9"}, + {file = "playwright-1.49.1-py3-none-win32.whl", hash = "sha256:43b304be67f096058e587dac453ece550eff87b8fbed28de30f4f022cc1745bb"}, + {file = "playwright-1.49.1-py3-none-win_amd64.whl", hash = "sha256:47b23cb346283278f5b4d1e1990bcb6d6302f80c0aa0ca93dd0601a1400191df"}, ] [package.dependencies] @@ -3078,4 +3079,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "d62cd1897d8f73e9aad9e907beb82be509dc5e33d8f37b36ebf26ad1f3075a9f" +content-hash = "ca33ba05f9f8b42e06d1304b4319470b390b944370a8cd2b90a015773992a983" From bd2ea5b41730e498eb5a878ec49a497c880219f0 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 12 Dec 2024 07:35:18 +0300 Subject: [PATCH 28/86] update poetry version --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4d567aee029..d924074605e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1837,13 +1837,13 @@ files = [ [[package]] name = "pyright" -version = "1.1.389" +version = "1.1.390" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.389-py3-none-any.whl", hash = "sha256:41e9620bba9254406dc1f621a88ceab5a88af4c826feb4f614d95691ed243a60"}, - {file = "pyright-1.1.389.tar.gz", hash = "sha256:716bf8cc174ab8b4dcf6828c3298cac05c5ed775dda9910106a5dcfe4c7fe220"}, + {file = "pyright-1.1.390-py3-none-any.whl", hash = "sha256:ecebfba5b6b50af7c1a44c2ba144ba2ab542c227eb49bc1f16984ff714e0e110"}, + {file = "pyright-1.1.390.tar.gz", hash = "sha256:aad7f160c49e0fbf8209507a15e17b781f63a86a1facb69ca877c71ef2e9538d"}, ] [package.dependencies] @@ -3079,4 +3079,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "ca33ba05f9f8b42e06d1304b4319470b390b944370a8cd2b90a015773992a983" +content-hash = "bc22926076587ecfa7ac1135f41a883f9920d020606374442a97db7d18795353" diff --git a/pyproject.toml b/pyproject.toml index fec7cb550b7..2e757dcf4d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,7 +64,7 @@ typing_extensions = ">=4.6.0" [tool.poetry.group.dev.dependencies] pytest = ">=7.1.2,<9.0" pytest-mock = ">=3.10.0,<4.0" -pyright = ">=1.1.229,<=1.1.389" +pyright = ">=1.1.229,<=1.1.390" darglint = ">=1.8.1,<2.0" dill = ">=0.3.8" toml = ">=0.10.2,<1.0" From a5526afaeb50808e46977e01b5f0e640328eb575 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 2 Jan 2025 15:20:03 -0800 Subject: [PATCH 29/86] update pyright once again --- poetry.lock | 1010 +++++++++++++++++++-------------------- pyproject.toml | 2 +- reflex/vars/sequence.py | 2 +- 3 files changed, 499 insertions(+), 515 deletions(-) diff --git a/poetry.lock b/poetry.lock index d924074605e..0baf3ba7dfe 100644 --- a/poetry.lock +++ b/poetry.lock @@ -76,19 +76,19 @@ files = [ [[package]] name = "attrs" -version = "24.2.0" +version = "24.3.0" description = "Classes Without Boilerplate" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, - {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, + {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, + {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, ] [package.extras] benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] @@ -146,13 +146,13 @@ virtualenv = ["virtualenv (>=20.0.35)"] [[package]] name = "certifi" -version = "2024.8.30" +version = "2024.12.14" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, + {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, + {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, ] [[package]] @@ -247,127 +247,114 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.4.0" +version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.7" files = [ - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, - {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, - {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, ] [[package]] name = "click" -version = "8.1.7" +version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, ] [package.dependencies] @@ -386,73 +373,73 @@ files = [ [[package]] name = "coverage" -version = "7.6.9" +version = "7.6.10" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb"}, - {file = "coverage-7.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710"}, - {file = "coverage-7.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa"}, - {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1"}, - {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec"}, - {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3"}, - {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5"}, - {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073"}, - {file = "coverage-7.6.9-cp310-cp310-win32.whl", hash = "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198"}, - {file = "coverage-7.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717"}, - {file = "coverage-7.6.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9"}, - {file = "coverage-7.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c"}, - {file = "coverage-7.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7"}, - {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9"}, - {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4"}, - {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1"}, - {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b"}, - {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3"}, - {file = "coverage-7.6.9-cp311-cp311-win32.whl", hash = "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0"}, - {file = "coverage-7.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b"}, - {file = "coverage-7.6.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8"}, - {file = "coverage-7.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a"}, - {file = "coverage-7.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015"}, - {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3"}, - {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae"}, - {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4"}, - {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6"}, - {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f"}, - {file = "coverage-7.6.9-cp312-cp312-win32.whl", hash = "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692"}, - {file = "coverage-7.6.9-cp312-cp312-win_amd64.whl", hash = "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97"}, - {file = "coverage-7.6.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664"}, - {file = "coverage-7.6.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c"}, - {file = "coverage-7.6.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014"}, - {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00"}, - {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d"}, - {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a"}, - {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077"}, - {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb"}, - {file = "coverage-7.6.9-cp313-cp313-win32.whl", hash = "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba"}, - {file = "coverage-7.6.9-cp313-cp313-win_amd64.whl", hash = "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1"}, - {file = "coverage-7.6.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419"}, - {file = "coverage-7.6.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a"}, - {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4"}, - {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae"}, - {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030"}, - {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be"}, - {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e"}, - {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9"}, - {file = "coverage-7.6.9-cp313-cp313t-win32.whl", hash = "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b"}, - {file = "coverage-7.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611"}, - {file = "coverage-7.6.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902"}, - {file = "coverage-7.6.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be"}, - {file = "coverage-7.6.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599"}, - {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08"}, - {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464"}, - {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845"}, - {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf"}, - {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678"}, - {file = "coverage-7.6.9-cp39-cp39-win32.whl", hash = "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6"}, - {file = "coverage-7.6.9-cp39-cp39-win_amd64.whl", hash = "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4"}, - {file = "coverage-7.6.9-pp39.pp310-none-any.whl", hash = "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b"}, - {file = "coverage-7.6.9.tar.gz", hash = "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d"}, + {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, + {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5"}, + {file = "coverage-7.6.10-cp310-cp310-win32.whl", hash = "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244"}, + {file = "coverage-7.6.10-cp310-cp310-win_amd64.whl", hash = "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e"}, + {file = "coverage-7.6.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3"}, + {file = "coverage-7.6.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377"}, + {file = "coverage-7.6.10-cp311-cp311-win32.whl", hash = "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8"}, + {file = "coverage-7.6.10-cp311-cp311-win_amd64.whl", hash = "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609"}, + {file = "coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853"}, + {file = "coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852"}, + {file = "coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359"}, + {file = "coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247"}, + {file = "coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9"}, + {file = "coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694"}, + {file = "coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6"}, + {file = "coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e"}, + {file = "coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe"}, + {file = "coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2"}, + {file = "coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312"}, + {file = "coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d"}, + {file = "coverage-7.6.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a"}, + {file = "coverage-7.6.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d"}, + {file = "coverage-7.6.10-cp39-cp39-win32.whl", hash = "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18"}, + {file = "coverage-7.6.10-cp39-cp39-win_amd64.whl", hash = "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59"}, + {file = "coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"}, + {file = "coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23"}, ] [package.dependencies] @@ -784,13 +771,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "identify" -version = "2.6.3" +version = "2.6.4" description = "File identification library for Python" optional = false python-versions = ">=3.9" files = [ - {file = "identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd"}, - {file = "identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02"}, + {file = "identify-2.6.4-py2.py3-none-any.whl", hash = "sha256:993b0f01b97e0568c179bb9196391ff391bfb88a99099dbf5ce392b68f42d0af"}, + {file = "identify-2.6.4.tar.gz", hash = "sha256:285a7d27e397652e8cafe537a6cc97dd470a970f48fb2e9d979aa38eae5513ac"}, ] [package.extras] @@ -919,13 +906,13 @@ trio = ["async_generator", "trio"] [[package]] name = "jinja2" -version = "3.1.4" +version = "3.1.5" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, - {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, + {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, + {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, ] [package.dependencies] @@ -936,17 +923,17 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "keyring" -version = "25.5.0" +version = "25.6.0" description = "Store and access your passwords safely." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "keyring-25.5.0-py3-none-any.whl", hash = "sha256:e67f8ac32b04be4714b42fe84ce7dad9c40985b9ca827c592cc303e7c26d9741"}, - {file = "keyring-25.5.0.tar.gz", hash = "sha256:4c753b3ec91717fe713c4edd522d625889d8973a349b0e582622f49766de58e6"}, + {file = "keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd"}, + {file = "keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66"}, ] [package.dependencies] -importlib-metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} +importlib_metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} "jaraco.classes" = "*" "jaraco.context" = "*" "jaraco.functools" = "*" @@ -1119,35 +1106,35 @@ files = [ [[package]] name = "nh3" -version = "0.2.19" -description = "Python bindings to the ammonia HTML sanitization library." +version = "0.2.20" +description = "Python binding to Ammonia HTML sanitizer Rust crate" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "nh3-0.2.19-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ec9c8bf86e397cb88c560361f60fdce478b5edb8b93f04ead419b72fbe937ea6"}, - {file = "nh3-0.2.19-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0adf00e2b2026fa10a42537b60d161e516f206781c7515e4e97e09f72a8c5d0"}, - {file = "nh3-0.2.19-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3805161c4e12088bd74752ba69630e915bc30fe666034f47217a2f16b16efc37"}, - {file = "nh3-0.2.19-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3dedd7858a21312f7675841529941035a2ac91057db13402c8fe907aa19205a"}, - {file = "nh3-0.2.19-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:0b6820fc64f2ff7ef3e7253a093c946a87865c877b3889149a6d21d322ed8dbd"}, - {file = "nh3-0.2.19-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:833b3b5f1783ce95834a13030300cea00cbdfd64ea29260d01af9c4821da0aa9"}, - {file = "nh3-0.2.19-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5d4f5e2189861b352b73acb803b5f4bb409c2f36275d22717e27d4e0c217ae55"}, - {file = "nh3-0.2.19-cp313-cp313t-win32.whl", hash = "sha256:2b926f179eb4bce72b651bfdf76f8aa05d167b2b72bc2f3657fd319f40232adc"}, - {file = "nh3-0.2.19-cp313-cp313t-win_amd64.whl", hash = "sha256:ac536a4b5c073fdadd8f5f4889adabe1cbdae55305366fb870723c96ca7f49c3"}, - {file = "nh3-0.2.19-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c2e3f0d18cc101132fe10ab7ef5c4f41411297e639e23b64b5e888ccaad63f41"}, - {file = "nh3-0.2.19-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11270b16c1b012677e3e2dd166c1aa273388776bf99a3e3677179db5097ee16a"}, - {file = "nh3-0.2.19-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fc483dd8d20f8f8c010783a25a84db3bebeadced92d24d34b40d687f8043ac69"}, - {file = "nh3-0.2.19-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d53a4577b6123ca1d7e8483fad3e13cb7eda28913d516bd0a648c1a473aa21a9"}, - {file = "nh3-0.2.19-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdb20740d24ab9f2a1341458a00a11205294e97e905de060eeab1ceca020c09c"}, - {file = "nh3-0.2.19-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8325d51e47cb5b11f649d55e626d56c76041ba508cd59e0cb1cf687cc7612f1"}, - {file = "nh3-0.2.19-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8eb7affc590e542fa7981ef508cd1644f62176bcd10d4429890fc629b47f0bc"}, - {file = "nh3-0.2.19-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2eb021804e9df1761abeb844bb86648d77aa118a663c82f50ea04110d87ed707"}, - {file = "nh3-0.2.19-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a7b928862daddb29805a1010a0282f77f4b8b238a37b5f76bc6c0d16d930fd22"}, - {file = "nh3-0.2.19-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed06ed78f6b69d57463b46a04f68f270605301e69d80756a8adf7519002de57d"}, - {file = "nh3-0.2.19-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:df8eac98fec80bd6f5fd0ae27a65de14f1e1a65a76d8e2237eb695f9cd1121d9"}, - {file = "nh3-0.2.19-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:00810cd5275f5c3f44b9eb0e521d1a841ee2f8023622de39ffc7d88bd533d8e0"}, - {file = "nh3-0.2.19-cp38-abi3-win32.whl", hash = "sha256:7e98621856b0a911c21faa5eef8f8ea3e691526c2433f9afc2be713cb6fbdb48"}, - {file = "nh3-0.2.19-cp38-abi3-win_amd64.whl", hash = "sha256:75c7cafb840f24430b009f7368945cb5ca88b2b54bb384ebfba495f16bc9c121"}, - {file = "nh3-0.2.19.tar.gz", hash = "sha256:790056b54c068ff8dceb443eaefb696b84beff58cca6c07afd754d17692a4804"}, + {file = "nh3-0.2.20-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e1061a4ab6681f6bdf72b110eea0c4e1379d57c9de937db3be4202f7ad6043db"}, + {file = "nh3-0.2.20-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb4254b1dac4a1ee49919a5b3f1caf9803ea8dada1816d9e8289e63d3cd0dd9a"}, + {file = "nh3-0.2.20-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ae9cbd713524cdb81e64663d0d6aae26f678db9f2cd9db0bf162606f1f9f20c"}, + {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e1f7370b4e14cc03f5ae141ef30a1caf81fa5787711f80be9081418dd9eb79d2"}, + {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:ac4d27dc836a476efffc6eb661994426b8b805c951b29c9cf2ff36bc9ad58bc5"}, + {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4fd2e9248725ebcedac3997a8d3da0d90a12a28c9179c6ba51f1658938ac30d0"}, + {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f7d564871833ddbe54df3aa59053b1110729d3a800cb7628ae8f42adb3d75208"}, + {file = "nh3-0.2.20-cp313-cp313t-win32.whl", hash = "sha256:d2a176fd4306b6f0f178a3f67fac91bd97a3a8d8fafb771c9b9ef675ba5c8886"}, + {file = "nh3-0.2.20-cp313-cp313t-win_amd64.whl", hash = "sha256:6ed834c68452a600f517dd3e1534dbfaff1f67f98899fecf139a055a25d99150"}, + {file = "nh3-0.2.20-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:76e2f603b30c02ff6456b233a83fc377dedab6a50947b04e960a6b905637b776"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:181063c581defe683bd4bb78188ac9936d208aebbc74c7f7c16b6a32ae2ebb38"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:231addb7643c952cd6d71f1c8702d703f8fe34afcb20becb3efb319a501a12d7"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1b9a8340a0aab991c68a5ca938d35ef4a8a3f4bf1b455da8855a40bee1fa0ace"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10317cd96fe4bbd4eb6b95f3920b71c902157ad44fed103fdcde43e3b8ee8be6"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8698db4c04b140800d1a1cd3067fda399e36e1e2b8fc1fe04292a907350a3e9b"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eb04b9c3deb13c3a375ea39fd4a3c00d1f92e8fb2349f25f1e3e4506751774b"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92f3f1c4f47a2c6f3ca7317b1d5ced05bd29556a75d3a4e2715652ae9d15c05d"}, + {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ddefa9fd6794a87e37d05827d299d4b53a3ec6f23258101907b96029bfef138a"}, + {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ce3731c8f217685d33d9268362e5b4f770914e922bba94d368ab244a59a6c397"}, + {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:09f037c02fc2c43b211ff1523de32801dcfb0918648d8e651c36ef890f1731ec"}, + {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:813f1c8012dd64c990514b795508abb90789334f76a561fa0fd4ca32d2275330"}, + {file = "nh3-0.2.20-cp38-abi3-win32.whl", hash = "sha256:47b2946c0e13057855209daeffb45dc910bd0c55daf10190bb0b4b60e2999784"}, + {file = "nh3-0.2.20-cp38-abi3-win_amd64.whl", hash = "sha256:da87573f03084edae8eb87cfe811ec338606288f81d333c07d2a9a0b9b976c0b"}, + {file = "nh3-0.2.20.tar.gz", hash = "sha256:9705c42d7ff88a0bea546c82d7fe5e59135e3d3f057e485394f491248a1f8ed5"}, ] [[package]] @@ -1217,66 +1204,66 @@ files = [ [[package]] name = "numpy" -version = "2.2.0" +version = "2.2.1" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" files = [ - {file = "numpy-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa"}, - {file = "numpy-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219"}, - {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e"}, - {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9"}, - {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3"}, - {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83"}, - {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a"}, - {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31"}, - {file = "numpy-2.2.0-cp310-cp310-win32.whl", hash = "sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661"}, - {file = "numpy-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4"}, - {file = "numpy-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6"}, - {file = "numpy-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90"}, - {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608"}, - {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da"}, - {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74"}, - {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e"}, - {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b"}, - {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d"}, - {file = "numpy-2.2.0-cp311-cp311-win32.whl", hash = "sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410"}, - {file = "numpy-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73"}, - {file = "numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3"}, - {file = "numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e"}, - {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67"}, - {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e"}, - {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038"}, - {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03"}, - {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a"}, - {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef"}, - {file = "numpy-2.2.0-cp312-cp312-win32.whl", hash = "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1"}, - {file = "numpy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3"}, - {file = "numpy-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367"}, - {file = "numpy-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae"}, - {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69"}, - {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13"}, - {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671"}, - {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571"}, - {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d"}, - {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742"}, - {file = "numpy-2.2.0-cp313-cp313-win32.whl", hash = "sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e"}, - {file = "numpy-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2"}, - {file = "numpy-2.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95"}, - {file = "numpy-2.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c"}, - {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca"}, - {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d"}, - {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529"}, - {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3"}, - {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab"}, - {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72"}, - {file = "numpy-2.2.0-cp313-cp313t-win32.whl", hash = "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066"}, - {file = "numpy-2.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881"}, - {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773"}, - {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e"}, - {file = "numpy-2.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7"}, - {file = "numpy-2.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221"}, - {file = "numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0"}, + {file = "numpy-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5edb4e4caf751c1518e6a26a83501fda79bff41cc59dac48d70e6d65d4ec4440"}, + {file = "numpy-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa3017c40d513ccac9621a2364f939d39e550c542eb2a894b4c8da92b38896ab"}, + {file = "numpy-2.2.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:61048b4a49b1c93fe13426e04e04fdf5a03f456616f6e98c7576144677598675"}, + {file = "numpy-2.2.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:7671dc19c7019103ca44e8d94917eba8534c76133523ca8406822efdd19c9308"}, + {file = "numpy-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4250888bcb96617e00bfa28ac24850a83c9f3a16db471eca2ee1f1714df0f957"}, + {file = "numpy-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7746f235c47abc72b102d3bce9977714c2444bdfaea7888d241b4c4bb6a78bf"}, + {file = "numpy-2.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:059e6a747ae84fce488c3ee397cee7e5f905fd1bda5fb18c66bc41807ff119b2"}, + {file = "numpy-2.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f62aa6ee4eb43b024b0e5a01cf65a0bb078ef8c395e8713c6e8a12a697144528"}, + {file = "numpy-2.2.1-cp310-cp310-win32.whl", hash = "sha256:48fd472630715e1c1c89bf1feab55c29098cb403cc184b4859f9c86d4fcb6a95"}, + {file = "numpy-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:b541032178a718c165a49638d28272b771053f628382d5e9d1c93df23ff58dbf"}, + {file = "numpy-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40f9e544c1c56ba8f1cf7686a8c9b5bb249e665d40d626a23899ba6d5d9e1484"}, + {file = "numpy-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9b57eaa3b0cd8db52049ed0330747b0364e899e8a606a624813452b8203d5f7"}, + {file = "numpy-2.2.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bc8a37ad5b22c08e2dbd27df2b3ef7e5c0864235805b1e718a235bcb200cf1cb"}, + {file = "numpy-2.2.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:9036d6365d13b6cbe8f27a0eaf73ddcc070cae584e5ff94bb45e3e9d729feab5"}, + {file = "numpy-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51faf345324db860b515d3f364eaa93d0e0551a88d6218a7d61286554d190d73"}, + {file = "numpy-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38efc1e56b73cc9b182fe55e56e63b044dd26a72128fd2fbd502f75555d92591"}, + {file = "numpy-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:31b89fa67a8042e96715c68e071a1200c4e172f93b0fbe01a14c0ff3ff820fc8"}, + {file = "numpy-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4c86e2a209199ead7ee0af65e1d9992d1dce7e1f63c4b9a616500f93820658d0"}, + {file = "numpy-2.2.1-cp311-cp311-win32.whl", hash = "sha256:b34d87e8a3090ea626003f87f9392b3929a7bbf4104a05b6667348b6bd4bf1cd"}, + {file = "numpy-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:360137f8fb1b753c5cde3ac388597ad680eccbbbb3865ab65efea062c4a1fd16"}, + {file = "numpy-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:694f9e921a0c8f252980e85bce61ebbd07ed2b7d4fa72d0e4246f2f8aa6642ab"}, + {file = "numpy-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3683a8d166f2692664262fd4900f207791d005fb088d7fdb973cc8d663626faa"}, + {file = "numpy-2.2.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:780077d95eafc2ccc3ced969db22377b3864e5b9a0ea5eb347cc93b3ea900315"}, + {file = "numpy-2.2.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:55ba24ebe208344aa7a00e4482f65742969a039c2acfcb910bc6fcd776eb4355"}, + {file = "numpy-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b1d07b53b78bf84a96898c1bc139ad7f10fda7423f5fd158fd0f47ec5e01ac7"}, + {file = "numpy-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5062dc1a4e32a10dc2b8b13cedd58988261416e811c1dc4dbdea4f57eea61b0d"}, + {file = "numpy-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fce4f615f8ca31b2e61aa0eb5865a21e14f5629515c9151850aa936c02a1ee51"}, + {file = "numpy-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:67d4cda6fa6ffa073b08c8372aa5fa767ceb10c9a0587c707505a6d426f4e046"}, + {file = "numpy-2.2.1-cp312-cp312-win32.whl", hash = "sha256:32cb94448be47c500d2c7a95f93e2f21a01f1fd05dd2beea1ccd049bb6001cd2"}, + {file = "numpy-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:ba5511d8f31c033a5fcbda22dd5c813630af98c70b2661f2d2c654ae3cdfcfc8"}, + {file = "numpy-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f1d09e520217618e76396377c81fba6f290d5f926f50c35f3a5f72b01a0da780"}, + {file = "numpy-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ecc47cd7f6ea0336042be87d9e7da378e5c7e9b3c8ad0f7c966f714fc10d821"}, + {file = "numpy-2.2.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f419290bc8968a46c4933158c91a0012b7a99bb2e465d5ef5293879742f8797e"}, + {file = "numpy-2.2.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5b6c390bfaef8c45a260554888966618328d30e72173697e5cabe6b285fb2348"}, + {file = "numpy-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:526fc406ab991a340744aad7e25251dd47a6720a685fa3331e5c59fef5282a59"}, + {file = "numpy-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74e6fdeb9a265624ec3a3918430205dff1df7e95a230779746a6af78bc615af"}, + {file = "numpy-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:53c09385ff0b72ba79d8715683c1168c12e0b6e84fb0372e97553d1ea91efe51"}, + {file = "numpy-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f3eac17d9ec51be534685ba877b6ab5edc3ab7ec95c8f163e5d7b39859524716"}, + {file = "numpy-2.2.1-cp313-cp313-win32.whl", hash = "sha256:9ad014faa93dbb52c80d8f4d3dcf855865c876c9660cb9bd7553843dd03a4b1e"}, + {file = "numpy-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:164a829b6aacf79ca47ba4814b130c4020b202522a93d7bff2202bfb33b61c60"}, + {file = "numpy-2.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4dfda918a13cc4f81e9118dea249e192ab167a0bb1966272d5503e39234d694e"}, + {file = "numpy-2.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:733585f9f4b62e9b3528dd1070ec4f52b8acf64215b60a845fa13ebd73cd0712"}, + {file = "numpy-2.2.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:89b16a18e7bba224ce5114db863e7029803c179979e1af6ad6a6b11f70545008"}, + {file = "numpy-2.2.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:676f4eebf6b2d430300f1f4f4c2461685f8269f94c89698d832cdf9277f30b84"}, + {file = "numpy-2.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27f5cdf9f493b35f7e41e8368e7d7b4bbafaf9660cba53fb21d2cd174ec09631"}, + {file = "numpy-2.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1ad395cf254c4fbb5b2132fee391f361a6e8c1adbd28f2cd8e79308a615fe9d"}, + {file = "numpy-2.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:08ef779aed40dbc52729d6ffe7dd51df85796a702afbf68a4f4e41fafdc8bda5"}, + {file = "numpy-2.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:26c9c4382b19fcfbbed3238a14abf7ff223890ea1936b8890f058e7ba35e8d71"}, + {file = "numpy-2.2.1-cp313-cp313t-win32.whl", hash = "sha256:93cf4e045bae74c90ca833cba583c14b62cb4ba2cba0abd2b141ab52548247e2"}, + {file = "numpy-2.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:bff7d8ec20f5f42607599f9994770fa65d76edca264a87b5e4ea5629bce12268"}, + {file = "numpy-2.2.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7ba9cc93a91d86365a5d270dee221fdc04fb68d7478e6bf6af650de78a8339e3"}, + {file = "numpy-2.2.1-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3d03883435a19794e41f147612a77a8f56d4e52822337844fff3d4040a142964"}, + {file = "numpy-2.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4511d9e6071452b944207c8ce46ad2f897307910b402ea5fa975da32e0102800"}, + {file = "numpy-2.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5c5cc0cbabe9452038ed984d05ac87910f89370b9242371bd9079cb4af61811e"}, + {file = "numpy-2.2.1.tar.gz", hash = "sha256:45681fd7128c8ad1c379f0ca0776a8b0c6583d2f69889ddac01559dfe4390918"}, ] [[package]] @@ -1392,93 +1379,89 @@ xml = ["lxml (>=4.9.2)"] [[package]] name = "pillow" -version = "11.0.0" +version = "11.1.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.9" files = [ - {file = "pillow-11.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6619654954dc4936fcff82db8eb6401d3159ec6be81e33c6000dfd76ae189947"}, - {file = "pillow-11.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3c5ac4bed7519088103d9450a1107f76308ecf91d6dabc8a33a2fcfb18d0fba"}, - {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a65149d8ada1055029fcb665452b2814fe7d7082fcb0c5bed6db851cb69b2086"}, - {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88a58d8ac0cc0e7f3a014509f0455248a76629ca9b604eca7dc5927cc593c5e9"}, - {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c26845094b1af3c91852745ae78e3ea47abf3dbcd1cf962f16b9a5fbe3ee8488"}, - {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1a61b54f87ab5786b8479f81c4b11f4d61702830354520837f8cc791ebba0f5f"}, - {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:674629ff60030d144b7bca2b8330225a9b11c482ed408813924619c6f302fdbb"}, - {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:598b4e238f13276e0008299bd2482003f48158e2b11826862b1eb2ad7c768b97"}, - {file = "pillow-11.0.0-cp310-cp310-win32.whl", hash = "sha256:9a0f748eaa434a41fccf8e1ee7a3eed68af1b690e75328fd7a60af123c193b50"}, - {file = "pillow-11.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a5629742881bcbc1f42e840af185fd4d83a5edeb96475a575f4da50d6ede337c"}, - {file = "pillow-11.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:ee217c198f2e41f184f3869f3e485557296d505b5195c513b2bfe0062dc537f1"}, - {file = "pillow-11.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc"}, - {file = "pillow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a"}, - {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3"}, - {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5"}, - {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b"}, - {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa"}, - {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306"}, - {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9"}, - {file = "pillow-11.0.0-cp311-cp311-win32.whl", hash = "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5"}, - {file = "pillow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291"}, - {file = "pillow-11.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9"}, - {file = "pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923"}, - {file = "pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903"}, - {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4"}, - {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f"}, - {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9"}, - {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7"}, - {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6"}, - {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc"}, - {file = "pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6"}, - {file = "pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47"}, - {file = "pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25"}, - {file = "pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699"}, - {file = "pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38"}, - {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2"}, - {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2"}, - {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527"}, - {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa"}, - {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f"}, - {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb"}, - {file = "pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798"}, - {file = "pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de"}, - {file = "pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84"}, - {file = "pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b"}, - {file = "pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003"}, - {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2"}, - {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a"}, - {file = "pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8"}, - {file = "pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8"}, - {file = "pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904"}, - {file = "pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3"}, - {file = "pillow-11.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2e46773dc9f35a1dd28bd6981332fd7f27bec001a918a72a79b4133cf5291dba"}, - {file = "pillow-11.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2679d2258b7f1192b378e2893a8a0a0ca472234d4c2c0e6bdd3380e8dfa21b6a"}, - {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda2616eb2313cbb3eebbe51f19362eb434b18e3bb599466a1ffa76a033fb916"}, - {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ec184af98a121fb2da42642dea8a29ec80fc3efbaefb86d8fdd2606619045d"}, - {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:8594f42df584e5b4bb9281799698403f7af489fba84c34d53d1c4bfb71b7c4e7"}, - {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c12b5ae868897c7338519c03049a806af85b9b8c237b7d675b8c5e089e4a618e"}, - {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:70fbbdacd1d271b77b7721fe3cdd2d537bbbd75d29e6300c672ec6bb38d9672f"}, - {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5178952973e588b3f1360868847334e9e3bf49d19e169bbbdfaf8398002419ae"}, - {file = "pillow-11.0.0-cp39-cp39-win32.whl", hash = "sha256:8c676b587da5673d3c75bd67dd2a8cdfeb282ca38a30f37950511766b26858c4"}, - {file = "pillow-11.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:94f3e1780abb45062287b4614a5bc0874519c86a777d4a7ad34978e86428b8dd"}, - {file = "pillow-11.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:290f2cc809f9da7d6d622550bbf4c1e57518212da51b6a30fe8e0a270a5b78bd"}, - {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2"}, - {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2"}, - {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b"}, - {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a0d3b115009ebb8ac3d2ebec5c2982cc693da935f4ab7bb5c8ebe2f47d36f2"}, - {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:73853108f56df97baf2bb8b522f3578221e56f646ba345a372c78326710d3830"}, - {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e58876c91f97b0952eb766123bfef372792ab3f4e3e1f1a2267834c2ab131734"}, - {file = "pillow-11.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:224aaa38177597bb179f3ec87eeefcce8e4f85e608025e9cfac60de237ba6316"}, - {file = "pillow-11.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bd2d3bdb846d757055910f0a59792d33b555800813c3b39ada1829c372ccb06"}, - {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:375b8dd15a1f5d2feafff536d47e22f69625c1aa92f12b339ec0b2ca40263273"}, - {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:daffdf51ee5db69a82dd127eabecce20729e21f7a3680cf7cbb23f0829189790"}, - {file = "pillow-11.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7326a1787e3c7b0429659e0a944725e1b03eeaa10edd945a86dead1913383944"}, - {file = "pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739"}, + {file = "pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8"}, + {file = "pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192"}, + {file = "pillow-11.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07dba04c5e22824816b2615ad7a7484432d7f540e6fa86af60d2de57b0fcee2"}, + {file = "pillow-11.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e267b0ed063341f3e60acd25c05200df4193e15a4a5807075cd71225a2386e26"}, + {file = "pillow-11.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bd165131fd51697e22421d0e467997ad31621b74bfc0b75956608cb2906dda07"}, + {file = "pillow-11.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:abc56501c3fd148d60659aae0af6ddc149660469082859fa7b066a298bde9482"}, + {file = "pillow-11.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:54ce1c9a16a9561b6d6d8cb30089ab1e5eb66918cb47d457bd996ef34182922e"}, + {file = "pillow-11.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73ddde795ee9b06257dac5ad42fcb07f3b9b813f8c1f7f870f402f4dc54b5269"}, + {file = "pillow-11.1.0-cp310-cp310-win32.whl", hash = "sha256:3a5fe20a7b66e8135d7fd617b13272626a28278d0e578c98720d9ba4b2439d49"}, + {file = "pillow-11.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6123aa4a59d75f06e9dd3dac5bf8bc9aa383121bb3dd9a7a612e05eabc9961a"}, + {file = "pillow-11.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:a76da0a31da6fcae4210aa94fd779c65c75786bc9af06289cd1c184451ef7a65"}, + {file = "pillow-11.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457"}, + {file = "pillow-11.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35"}, + {file = "pillow-11.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2"}, + {file = "pillow-11.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f189805c8be5ca5add39e6f899e6ce2ed824e65fb45f3c28cb2841911da19070"}, + {file = "pillow-11.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dd0052e9db3474df30433f83a71b9b23bd9e4ef1de13d92df21a52c0303b8ab6"}, + {file = "pillow-11.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:837060a8599b8f5d402e97197d4924f05a2e0d68756998345c829c33186217b1"}, + {file = "pillow-11.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa8dd43daa836b9a8128dbe7d923423e5ad86f50a7a14dc688194b7be5c0dea2"}, + {file = "pillow-11.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a2f91f8a8b367e7a57c6e91cd25af510168091fb89ec5146003e424e1558a96"}, + {file = "pillow-11.1.0-cp311-cp311-win32.whl", hash = "sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f"}, + {file = "pillow-11.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761"}, + {file = "pillow-11.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71"}, + {file = "pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a"}, + {file = "pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b"}, + {file = "pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3"}, + {file = "pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a"}, + {file = "pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1"}, + {file = "pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f"}, + {file = "pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91"}, + {file = "pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c"}, + {file = "pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6"}, + {file = "pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf"}, + {file = "pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5"}, + {file = "pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc"}, + {file = "pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0"}, + {file = "pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1"}, + {file = "pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec"}, + {file = "pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5"}, + {file = "pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114"}, + {file = "pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352"}, + {file = "pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3"}, + {file = "pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9"}, + {file = "pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c"}, + {file = "pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65"}, + {file = "pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861"}, + {file = "pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081"}, + {file = "pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c"}, + {file = "pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547"}, + {file = "pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab"}, + {file = "pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9"}, + {file = "pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe"}, + {file = "pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756"}, + {file = "pillow-11.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:bf902d7413c82a1bfa08b06a070876132a5ae6b2388e2712aab3a7cbc02205c6"}, + {file = "pillow-11.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c1eec9d950b6fe688edee07138993e54ee4ae634c51443cfb7c1e7613322718e"}, + {file = "pillow-11.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e275ee4cb11c262bd108ab2081f750db2a1c0b8c12c1897f27b160c8bd57bbc"}, + {file = "pillow-11.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4db853948ce4e718f2fc775b75c37ba2efb6aaea41a1a5fc57f0af59eee774b2"}, + {file = "pillow-11.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:ab8a209b8485d3db694fa97a896d96dd6533d63c22829043fd9de627060beade"}, + {file = "pillow-11.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:54251ef02a2309b5eec99d151ebf5c9904b77976c8abdcbce7891ed22df53884"}, + {file = "pillow-11.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5bb94705aea800051a743aa4874bb1397d4695fb0583ba5e425ee0328757f196"}, + {file = "pillow-11.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89dbdb3e6e9594d512780a5a1c42801879628b38e3efc7038094430844e271d8"}, + {file = "pillow-11.1.0-cp39-cp39-win32.whl", hash = "sha256:e5449ca63da169a2e6068dd0e2fcc8d91f9558aba89ff6d02121ca8ab11e79e5"}, + {file = "pillow-11.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:3362c6ca227e65c54bf71a5f88b3d4565ff1bcbc63ae72c34b07bbb1cc59a43f"}, + {file = "pillow-11.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:b20be51b37a75cc54c2c55def3fa2c65bb94ba859dde241cd0a4fd302de5ae0a"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8c730dc3a83e5ac137fbc92dfcfe1511ce3b2b5d7578315b63dbbb76f7f51d90"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d33d2fae0e8b170b6a6c57400e077412240f6f5bb2a342cf1ee512a787942bb"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8d65b38173085f24bc07f8b6c505cbb7418009fa1a1fcb111b1f4961814a442"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:015c6e863faa4779251436db398ae75051469f7c903b043a48f078e437656f83"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d44ff19eea13ae4acdaaab0179fa68c0c6f2f45d66a4d8ec1eda7d6cecbcc15f"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d3d8da4a631471dfaf94c10c85f5277b1f8e42ac42bade1ac67da4b4a7359b73"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4637b88343166249fe8aa94e7c4a62a180c4b3898283bb5d3d2fd5fe10d8e4e0"}, + {file = "pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20"}, ] [package.extras] docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] fpx = ["olefile"] mic = ["olefile"] -tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "trove-classifiers (>=2024.10.12)"] typing = ["typing-extensions"] xmp = ["defusedxml"] @@ -1611,32 +1594,32 @@ virtualenv = ">=20.10.0" [[package]] name = "psutil" -version = "6.1.0" +version = "6.1.1" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "psutil-6.1.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ff34df86226c0227c52f38b919213157588a678d049688eded74c76c8ba4a5d0"}, - {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c0e0c00aa18ca2d3b2b991643b799a15fc8f0563d2ebb6040f64ce8dc027b942"}, - {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:000d1d1ebd634b4efb383f4034437384e44a6d455260aaee2eca1e9c1b55f047"}, - {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5cd2bcdc75b452ba2e10f0e8ecc0b57b827dd5d7aaffbc6821b2a9a242823a76"}, - {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:045f00a43c737f960d273a83973b2511430d61f283a44c96bf13a6e829ba8fdc"}, - {file = "psutil-6.1.0-cp27-none-win32.whl", hash = "sha256:9118f27452b70bb1d9ab3198c1f626c2499384935aaf55388211ad982611407e"}, - {file = "psutil-6.1.0-cp27-none-win_amd64.whl", hash = "sha256:a8506f6119cff7015678e2bce904a4da21025cc70ad283a53b099e7620061d85"}, - {file = "psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688"}, - {file = "psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e"}, - {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38"}, - {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b"}, - {file = "psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a"}, - {file = "psutil-6.1.0-cp36-cp36m-win32.whl", hash = "sha256:6d3fbbc8d23fcdcb500d2c9f94e07b1342df8ed71b948a2649b5cb060a7c94ca"}, - {file = "psutil-6.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1209036fbd0421afde505a4879dee3b2fd7b1e14fee81c0069807adcbbcca747"}, - {file = "psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e"}, - {file = "psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be"}, - {file = "psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a"}, -] - -[package.extras] -dev = ["black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "wheel"] + {file = "psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8"}, + {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777"}, + {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8df0178ba8a9e5bc84fed9cfa61d54601b371fbec5c8eebad27575f1e105c0d4"}, + {file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:1924e659d6c19c647e763e78670a05dbb7feaf44a0e9c94bf9e14dfc6ba50468"}, + {file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:018aeae2af92d943fdf1da6b58665124897cfc94faa2ca92098838f83e1b1bca"}, + {file = "psutil-6.1.1-cp27-none-win32.whl", hash = "sha256:6d4281f5bbca041e2292be3380ec56a9413b790579b8e593b1784499d0005dac"}, + {file = "psutil-6.1.1-cp27-none-win_amd64.whl", hash = "sha256:c777eb75bb33c47377c9af68f30e9f11bc78e0f07fbf907be4a5d70b2fe5f030"}, + {file = "psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8"}, + {file = "psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377"}, + {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003"}, + {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160"}, + {file = "psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3"}, + {file = "psutil-6.1.1-cp36-cp36m-win32.whl", hash = "sha256:384636b1a64b47814437d1173be1427a7c83681b17a450bfc309a1953e329603"}, + {file = "psutil-6.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8be07491f6ebe1a693f17d4f11e69d0dc1811fa082736500f649f79df7735303"}, + {file = "psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53"}, + {file = "psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649"}, + {file = "psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5"}, +] + +[package.extras] +dev = ["abi3audit", "black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] test = ["pytest", "pytest-xdist", "setuptools"] [[package]] @@ -1663,18 +1646,18 @@ files = [ [[package]] name = "pydantic" -version = "2.10.3" +version = "2.10.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"}, - {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"}, + {file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"}, + {file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.27.1" +pydantic-core = "2.27.2" typing-extensions = ">=4.12.2" [package.extras] @@ -1683,111 +1666,111 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.27.1" +version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"}, - {file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"}, - {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278"}, - {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05"}, - {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4"}, - {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f"}, - {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08"}, - {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6"}, - {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807"}, - {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c"}, - {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206"}, - {file = "pydantic_core-2.27.1-cp310-none-win32.whl", hash = "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c"}, - {file = "pydantic_core-2.27.1-cp310-none-win_amd64.whl", hash = "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17"}, - {file = "pydantic_core-2.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8"}, - {file = "pydantic_core-2.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330"}, - {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52"}, - {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4"}, - {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c"}, - {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de"}, - {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025"}, - {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e"}, - {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919"}, - {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c"}, - {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc"}, - {file = "pydantic_core-2.27.1-cp311-none-win32.whl", hash = "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9"}, - {file = "pydantic_core-2.27.1-cp311-none-win_amd64.whl", hash = "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5"}, - {file = "pydantic_core-2.27.1-cp311-none-win_arm64.whl", hash = "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89"}, - {file = "pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f"}, - {file = "pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02"}, - {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c"}, - {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac"}, - {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb"}, - {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529"}, - {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35"}, - {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089"}, - {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381"}, - {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb"}, - {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae"}, - {file = "pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c"}, - {file = "pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16"}, - {file = "pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e"}, - {file = "pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073"}, - {file = "pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08"}, - {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf"}, - {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737"}, - {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2"}, - {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107"}, - {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51"}, - {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a"}, - {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc"}, - {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960"}, - {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23"}, - {file = "pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05"}, - {file = "pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337"}, - {file = "pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5"}, - {file = "pydantic_core-2.27.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62"}, - {file = "pydantic_core-2.27.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab"}, - {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864"}, - {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067"}, - {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd"}, - {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5"}, - {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78"}, - {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f"}, - {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36"}, - {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a"}, - {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b"}, - {file = "pydantic_core-2.27.1-cp38-none-win32.whl", hash = "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618"}, - {file = "pydantic_core-2.27.1-cp38-none-win_amd64.whl", hash = "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4"}, - {file = "pydantic_core-2.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967"}, - {file = "pydantic_core-2.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60"}, - {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854"}, - {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9"}, - {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd"}, - {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be"}, - {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e"}, - {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792"}, - {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01"}, - {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9"}, - {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131"}, - {file = "pydantic_core-2.27.1-cp39-none-win32.whl", hash = "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3"}, - {file = "pydantic_core-2.27.1-cp39-none-win_amd64.whl", hash = "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840"}, - {file = "pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, ] [package.dependencies] @@ -1837,13 +1820,13 @@ files = [ [[package]] name = "pyright" -version = "1.1.390" +version = "1.1.391" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.390-py3-none-any.whl", hash = "sha256:ecebfba5b6b50af7c1a44c2ba144ba2ab542c227eb49bc1f16984ff714e0e110"}, - {file = "pyright-1.1.390.tar.gz", hash = "sha256:aad7f160c49e0fbf8209507a15e17b781f63a86a1facb69ca877c71ef2e9538d"}, + {file = "pyright-1.1.391-py3-none-any.whl", hash = "sha256:54fa186f8b3e8a55a44ebfa842636635688670c6896dcf6cf4a7fc75062f4d15"}, + {file = "pyright-1.1.391.tar.gz", hash = "sha256:66b2d42cdf5c3cbab05f2f4b76e8bec8aa78e679bfa0b6ad7b923d9e027cadb2"}, ] [package.dependencies] @@ -1891,20 +1874,20 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "pytest-asyncio" -version = "0.24.0" +version = "0.25.1" description = "Pytest support for asyncio" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b"}, - {file = "pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276"}, + {file = "pytest_asyncio-0.25.1-py3-none-any.whl", hash = "sha256:c84878849ec63ff2ca509423616e071ef9cd8cc93c053aa33b5b8fb70a990671"}, + {file = "pytest_asyncio-0.25.1.tar.gz", hash = "sha256:79be8a72384b0c917677e00daa711e07db15259f4d23203c59012bcd989d4aee"}, ] [package.dependencies] pytest = ">=8.2,<9" [package.extras] -docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] [[package]] @@ -2013,13 +1996,13 @@ six = ">=1.5" [[package]] name = "python-engineio" -version = "4.10.1" +version = "4.11.2" description = "Engine.IO server and client for Python" optional = false python-versions = ">=3.6" files = [ - {file = "python_engineio-4.10.1-py3-none-any.whl", hash = "sha256:445a94004ec8034960ab99e7ce4209ec619c6e6b6a12aedcb05abeab924025c0"}, - {file = "python_engineio-4.10.1.tar.gz", hash = "sha256:166cea8dd7429638c5c4e3a4895beae95196e860bc6f29ed0b9fe753d1ef2072"}, + {file = "python_engineio-4.11.2-py3-none-any.whl", hash = "sha256:f0971ac4c65accc489154fe12efd88f53ca8caf04754c46a66e85f5102ef22ad"}, + {file = "python_engineio-4.11.2.tar.gz", hash = "sha256:145bb0daceb904b4bb2d3eb2d93f7dbb7bb87a6a0c4f20a94cc8654dec977129"}, ] [package.dependencies] @@ -2032,13 +2015,13 @@ docs = ["sphinx"] [[package]] name = "python-multipart" -version = "0.0.19" +version = "0.0.20" description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.8" files = [ - {file = "python_multipart-0.0.19-py3-none-any.whl", hash = "sha256:f8d5b0b9c618575bf9df01c684ded1d94a338839bdd8223838afacfb4bb2082d"}, - {file = "python_multipart-0.0.19.tar.gz", hash = "sha256:905502ef39050557b7a6af411f454bc19526529ca46ae6831508438890ce12cc"}, + {file = "python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104"}, + {file = "python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13"}, ] [[package]] @@ -2060,18 +2043,18 @@ unidecode = ["Unidecode (>=1.1.1)"] [[package]] name = "python-socketio" -version = "5.11.4" +version = "5.12.1" description = "Socket.IO server and client for Python" optional = false python-versions = ">=3.8" files = [ - {file = "python_socketio-5.11.4-py3-none-any.whl", hash = "sha256:42efaa3e3e0b166fc72a527488a13caaac2cefc76174252486503bd496284945"}, - {file = "python_socketio-5.11.4.tar.gz", hash = "sha256:8b0b8ff2964b2957c865835e936310190639c00310a47d77321a594d1665355e"}, + {file = "python_socketio-5.12.1-py3-none-any.whl", hash = "sha256:24a0ea7cfff0e021eb28c68edbf7914ee4111bdf030b95e4d250c4dc9af7a386"}, + {file = "python_socketio-5.12.1.tar.gz", hash = "sha256:0299ff1f470b676c09c1bfab1dead25405077d227b2c13cf217a34dadc68ba9c"}, ] [package.dependencies] bidict = ">=0.21.0" -python-engineio = ">=4.8.0" +python-engineio = ">=4.11.0" [package.extras] asyncio-client = ["aiohttp (>=3.4)"] @@ -2215,13 +2198,13 @@ reflex = ">=0.6.0a" [[package]] name = "reflex-hosting-cli" -version = "0.1.30" +version = "0.1.32" description = "Reflex Hosting CLI" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "reflex_hosting_cli-0.1.30-py3-none-any.whl", hash = "sha256:778c98d635003d8668158c22eaa0f7124d2bac92c8a1aabaed710960ca97796e"}, - {file = "reflex_hosting_cli-0.1.30.tar.gz", hash = "sha256:a0fdc73e595e6b9fd661e1307ae37267fb3815cc457b7f15938ba921c12fc0b6"}, + {file = "reflex_hosting_cli-0.1.32-py3-none-any.whl", hash = "sha256:86b4222f3e99d949a209be7de8c457ededebc1f12a721ee6669c6c35fdecc508"}, + {file = "reflex_hosting_cli-0.1.32.tar.gz", hash = "sha256:0b8e4b4b30d9261bf6d720265f1c428b2840bb630896e60a1a2faa095901ed59"}, ] [package.dependencies] @@ -2231,6 +2214,7 @@ pipdeptree = ">=2.13.1,<2.17.0" platformdirs = ">=3.10.0,<5.0" pydantic = ">=1.10.2,<3.0" python-dateutil = ">=2.8.1" +pyyaml = ">=6.0.2,<7.0.0" rich = ">=13.0.0,<14.0" tabulate = ">=0.9.0,<0.10.0" typer = ">=0.15.0,<1" @@ -2703,13 +2687,13 @@ files = [ [[package]] name = "trio" -version = "0.27.0" +version = "0.28.0" description = "A friendly Python library for async concurrency and I/O" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "trio-0.27.0-py3-none-any.whl", hash = "sha256:68eabbcf8f457d925df62da780eff15ff5dc68fd6b367e2dde59f7aaf2a0b884"}, - {file = "trio-0.27.0.tar.gz", hash = "sha256:1dcc95ab1726b2da054afea8fd761af74bad79bd52381b84eae408e983c76831"}, + {file = "trio-0.28.0-py3-none-any.whl", hash = "sha256:56d58977acc1635735a96581ec70513cc781b8b6decd299c487d3be2a721cd94"}, + {file = "trio-0.28.0.tar.gz", hash = "sha256:4e547896fe9e8a5658e54e4c7c5fa1db748cbbbaa7c965e7d40505b928c73c05"}, ] [package.dependencies] @@ -2804,13 +2788,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.3" +version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, - {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, + {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, + {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.dependencies] @@ -2824,13 +2808,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.32.1" +version = "0.34.0" description = "The lightning-fast ASGI server." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e"}, - {file = "uvicorn-0.32.1.tar.gz", hash = "sha256:ee9519c246a72b1c084cea8d3b44ed6026e78a4a309cbedae9c37e4cb9fbb175"}, + {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"}, + {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"}, ] [package.dependencies] @@ -3079,4 +3063,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "bc22926076587ecfa7ac1135f41a883f9920d020606374442a97db7d18795353" +content-hash = "fe1302de010997620d26332a86519ca96b9bba6ecfbe75ee43d8538f0afac266" diff --git a/pyproject.toml b/pyproject.toml index 1e51bca9846..0989bac5f5f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,7 @@ typing_extensions = ">=4.6.0" [tool.poetry.group.dev.dependencies] pytest = ">=7.1.2,<9.0" pytest-mock = ">=3.10.0,<4.0" -pyright = ">=1.1.229,<=1.1.390" +pyright = ">=1.1.229,<=1.1.391" darglint = ">=1.8.1,<2.0" dill = ">=0.3.8" toml = ">=0.10.2,<1.0" diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 24378a9ff3d..e28b781dc7c 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -777,7 +777,7 @@ def _cached_var_name(self) -> str: Returns: The name of the var. """ - return f"[{str(LiteralVar.create(self._var_value.start))}, {str(LiteralVar.create(self._var_value.stop))}, {str(LiteralVar.create(self._var_value.step))}]" + return f"[{LiteralVar.create(self._var_value.start)!s}, {LiteralVar.create(self._var_value.stop)!s}, {LiteralVar.create(self._var_value.step)!s}]" @cached_property_no_lock def _cached_get_all_var_data(self) -> VarData | None: From 056de9e2772888784bbc7653a85d73fb5f14f9f5 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 15 Jan 2025 10:15:55 -0800 Subject: [PATCH 30/86] poetry update --- poetry.lock | 316 ++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 2 files changed, 159 insertions(+), 159 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0baf3ba7dfe..e22568c6000 100644 --- a/poetry.lock +++ b/poetry.lock @@ -32,13 +32,13 @@ files = [ [[package]] name = "anyio" -version = "4.7.0" +version = "4.8.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"}, - {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"}, + {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, + {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, ] [package.dependencies] @@ -49,7 +49,7 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -771,13 +771,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "identify" -version = "2.6.4" +version = "2.6.5" description = "File identification library for Python" optional = false python-versions = ">=3.9" files = [ - {file = "identify-2.6.4-py2.py3-none-any.whl", hash = "sha256:993b0f01b97e0568c179bb9196391ff391bfb88a99099dbf5ce392b68f42d0af"}, - {file = "identify-2.6.4.tar.gz", hash = "sha256:285a7d27e397652e8cafe537a6cc97dd470a970f48fb2e9d979aa38eae5513ac"}, + {file = "identify-2.6.5-py2.py3-none-any.whl", hash = "sha256:14181a47091eb75b337af4c23078c9d09225cd4c48929f521f3bf16b09d02566"}, + {file = "identify-2.6.5.tar.gz", hash = "sha256:c10b33f250e5bba374fae86fb57f3adcebf1161bce7cdf92031915fd480c13bc"}, ] [package.extras] @@ -1095,13 +1095,13 @@ files = [ [[package]] name = "more-itertools" -version = "10.5.0" +version = "10.6.0" description = "More routines for operating on iterables, beyond itertools" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6"}, - {file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"}, + {file = "more-itertools-10.6.0.tar.gz", hash = "sha256:2cd7fad1009c31cc9fb6a035108509e6547547a7a738374f10bd49a09eb3ee3b"}, + {file = "more_itertools-10.6.0-py3-none-any.whl", hash = "sha256:6eb054cb4b6db1473f6e15fcc676a08e4732548acd47c708f0e179c2c7c01e89"}, ] [[package]] @@ -1646,13 +1646,13 @@ files = [ [[package]] name = "pydantic" -version = "2.10.4" +version = "2.10.5" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"}, - {file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"}, + {file = "pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"}, + {file = "pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff"}, ] [package.dependencies] @@ -1795,13 +1795,13 @@ dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", " [[package]] name = "pygments" -version = "2.18.0" +version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, + {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, + {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, ] [package.extras] @@ -1820,13 +1820,13 @@ files = [ [[package]] name = "pyright" -version = "1.1.391" +version = "1.1.392.post0" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.391-py3-none-any.whl", hash = "sha256:54fa186f8b3e8a55a44ebfa842636635688670c6896dcf6cf4a7fc75062f4d15"}, - {file = "pyright-1.1.391.tar.gz", hash = "sha256:66b2d42cdf5c3cbab05f2f4b76e8bec8aa78e679bfa0b6ad7b923d9e027cadb2"}, + {file = "pyright-1.1.392.post0-py3-none-any.whl", hash = "sha256:252f84458a46fa2f0fd4e2f91fc74f50b9ca52c757062e93f6c250c0d8329eb2"}, + {file = "pyright-1.1.392.post0.tar.gz", hash = "sha256:3b7f88de74a28dcfa90c7d90c782b6569a48c2be5f9d4add38472bdaac247ebd"}, ] [package.dependencies] @@ -1874,13 +1874,13 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "pytest-asyncio" -version = "0.25.1" +version = "0.25.2" description = "Pytest support for asyncio" optional = false python-versions = ">=3.9" files = [ - {file = "pytest_asyncio-0.25.1-py3-none-any.whl", hash = "sha256:c84878849ec63ff2ca509423616e071ef9cd8cc93c053aa33b5b8fb70a990671"}, - {file = "pytest_asyncio-0.25.1.tar.gz", hash = "sha256:79be8a72384b0c917677e00daa711e07db15259f4d23203c59012bcd989d4aee"}, + {file = "pytest_asyncio-0.25.2-py3-none-any.whl", hash = "sha256:0d0bb693f7b99da304a0634afc0a4b19e49d5e0de2d670f38dc4bfa5727c5075"}, + {file = "pytest_asyncio-0.25.2.tar.gz", hash = "sha256:3f8ef9a98f45948ea91a0ed3dc4268b5326c0e7bce73892acc654df4262ad45f"}, ] [package.dependencies] @@ -2351,23 +2351,23 @@ websocket-client = ">=1.8,<2.0" [[package]] name = "setuptools" -version = "75.6.0" +version = "75.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" files = [ - {file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"}, - {file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"}, + {file = "setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3"}, + {file = "setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "shellingham" @@ -2433,72 +2433,58 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.36" +version = "2.0.37" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.36-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:59b8f3adb3971929a3e660337f5dacc5942c2cdb760afcabb2614ffbda9f9f72"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37350015056a553e442ff672c2d20e6f4b6d0b2495691fa239d8aa18bb3bc908"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8318f4776c85abc3f40ab185e388bee7a6ea99e7fa3a30686580b209eaa35c08"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c245b1fbade9c35e5bd3b64270ab49ce990369018289ecfde3f9c318411aaa07"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:69f93723edbca7342624d09f6704e7126b152eaed3cdbb634cb657a54332a3c5"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f9511d8dd4a6e9271d07d150fb2f81874a3c8c95e11ff9af3a2dfc35fe42ee44"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-win32.whl", hash = "sha256:c3f3631693003d8e585d4200730616b78fafd5a01ef8b698f6967da5c605b3fa"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-win_amd64.whl", hash = "sha256:a86bfab2ef46d63300c0f06936bd6e6c0105faa11d509083ba8f2f9d237fb5b5"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fd3a55deef00f689ce931d4d1b23fa9f04c880a48ee97af488fd215cf24e2a6c"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4f5e9cd989b45b73bd359f693b935364f7e1f79486e29015813c338450aa5a71"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ddd9db6e59c44875211bc4c7953a9f6638b937b0a88ae6d09eb46cced54eff"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2519f3a5d0517fc159afab1015e54bb81b4406c278749779be57a569d8d1bb0d"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59b1ee96617135f6e1d6f275bbe988f419c5178016f3d41d3c0abb0c819f75bb"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:39769a115f730d683b0eb7b694db9789267bcd027326cccc3125e862eb03bfd8"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-win32.whl", hash = "sha256:66bffbad8d6271bb1cc2f9a4ea4f86f80fe5e2e3e501a5ae2a3dc6a76e604e6f"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-win_amd64.whl", hash = "sha256:23623166bfefe1487d81b698c423f8678e80df8b54614c2bf4b4cfcd7c711959"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7b64e6ec3f02c35647be6b4851008b26cff592a95ecb13b6788a54ef80bbdd4"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46331b00096a6db1fdc052d55b101dbbfc99155a548e20a0e4a8e5e4d1362855"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdf3386a801ea5aba17c6410dd1dc8d39cf454ca2565541b5ac42a84e1e28f53"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9dfa18ff2a67b09b372d5db8743c27966abf0e5344c555d86cc7199f7ad83a"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:90812a8933df713fdf748b355527e3af257a11e415b613dd794512461eb8a686"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1bc330d9d29c7f06f003ab10e1eaced295e87940405afe1b110f2eb93a233588"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-win32.whl", hash = "sha256:79d2e78abc26d871875b419e1fd3c0bca31a1cb0043277d0d850014599626c2e"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-win_amd64.whl", hash = "sha256:b544ad1935a8541d177cb402948b94e871067656b3a0b9e91dbec136b06a2ff5"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5cc79df7f4bc3d11e4b542596c03826063092611e481fcf1c9dfee3c94355ef"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3c01117dd36800f2ecaa238c65365b7b16497adc1522bf84906e5710ee9ba0e8"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bc633f4ee4b4c46e7adcb3a9b5ec083bf1d9a97c1d3854b92749d935de40b9b"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e46ed38affdfc95d2c958de328d037d87801cfcbea6d421000859e9789e61c2"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b2985c0b06e989c043f1dc09d4fe89e1616aadd35392aea2844f0458a989eacf"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a121d62ebe7d26fec9155f83f8be5189ef1405f5973ea4874a26fab9f1e262c"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-win32.whl", hash = "sha256:0572f4bd6f94752167adfd7c1bed84f4b240ee6203a95e05d1e208d488d0d436"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-win_amd64.whl", hash = "sha256:8c78ac40bde930c60e0f78b3cd184c580f89456dd87fc08f9e3ee3ce8765ce88"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:be9812b766cad94a25bc63bec11f88c4ad3629a0cec1cd5d4ba48dc23860486b"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50aae840ebbd6cdd41af1c14590e5741665e5272d2fee999306673a1bb1fdb4d"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4557e1f11c5f653ebfdd924f3f9d5ebfc718283b0b9beebaa5dd6b77ec290971"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:07b441f7d03b9a66299ce7ccf3ef2900abc81c0db434f42a5694a37bd73870f2"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:28120ef39c92c2dd60f2721af9328479516844c6b550b077ca450c7d7dc68575"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-win32.whl", hash = "sha256:b81ee3d84803fd42d0b154cb6892ae57ea6b7c55d8359a02379965706c7efe6c"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-win_amd64.whl", hash = "sha256:f942a799516184c855e1a32fbc7b29d7e571b52612647866d4ec1c3242578fcb"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3d6718667da04294d7df1670d70eeddd414f313738d20a6f1d1f379e3139a545"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:72c28b84b174ce8af8504ca28ae9347d317f9dba3999e5981a3cd441f3712e24"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b11d0cfdd2b095e7b0686cf5fabeb9c67fae5b06d265d8180715b8cfa86522e3"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e32092c47011d113dc01ab3e1d3ce9f006a47223b18422c5c0d150af13a00687"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6a440293d802d3011028e14e4226da1434b373cbaf4a4bbb63f845761a708346"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c54a1e53a0c308a8e8a7dffb59097bff7facda27c70c286f005327f21b2bd6b1"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-win32.whl", hash = "sha256:1e0d612a17581b6616ff03c8e3d5eff7452f34655c901f75d62bd86449d9750e"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-win_amd64.whl", hash = "sha256:8958b10490125124463095bbdadda5aa22ec799f91958e410438ad6c97a7b793"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc022184d3e5cacc9579e41805a681187650e170eb2fd70e28b86192a479dcaa"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b817d41d692bf286abc181f8af476c4fbef3fd05e798777492618378448ee689"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4e46a888b54be23d03a89be510f24a7652fe6ff660787b96cd0e57a4ebcb46d"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4ae3005ed83f5967f961fd091f2f8c5329161f69ce8480aa8168b2d7fe37f06"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:03e08af7a5f9386a43919eda9de33ffda16b44eb11f3b313e6822243770e9763"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3dbb986bad3ed5ceaf090200eba750b5245150bd97d3e67343a3cfed06feecf7"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-win32.whl", hash = "sha256:9fe53b404f24789b5ea9003fc25b9a3988feddebd7e7b369c8fac27ad6f52f28"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-win_amd64.whl", hash = "sha256:af148a33ff0349f53512a049c6406923e4e02bf2f26c5fb285f143faf4f0e46a"}, - {file = "SQLAlchemy-2.0.36-py3-none-any.whl", hash = "sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e"}, - {file = "sqlalchemy-2.0.36.tar.gz", hash = "sha256:7f2767680b6d2398aea7082e45a774b2b0767b5c8d8ffb9c8b683088ea9b29c5"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da36c3b0e891808a7542c5c89f224520b9a16c7f5e4d6a1156955605e54aef0e"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e7402ff96e2b073a98ef6d6142796426d705addd27b9d26c3b32dbaa06d7d069"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41296bbcaa55ef5fdd32389a35c710133b097f7b2609d8218c0eabded43a1d84"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6c67415258f9f3c69867ec02fea1bf6508153709ecbd731a982442a590f2b7e4"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-win32.whl", hash = "sha256:650dcb70739957a492ad8acff65d099a9586b9b8920e3507ca61ec3ce650bb72"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-win_amd64.whl", hash = "sha256:93d1543cd8359040c02b6614421c8e10cd7a788c40047dbc507ed46c29ae5636"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:78361be6dc9073ed17ab380985d1e45e48a642313ab68ab6afa2457354ff692c"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b661b49d0cb0ab311a189b31e25576b7ac3e20783beb1e1817d72d9d02508bf5"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa2c0913f02341d25fb858e4fb2031e6b0813494cca1ba07d417674128ce11b"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db18ff6b8c0f1917f8b20f8eca35c28bbccb9f83afa94743e03d40203ed83de9"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-win32.whl", hash = "sha256:46954173612617a99a64aee103bcd3f078901b9a8dcfc6ae80cbf34ba23df989"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-win_amd64.whl", hash = "sha256:7b7e772dc4bc507fdec4ee20182f15bd60d2a84f1e087a8accf5b5b7a0dcf2ba"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2952748ecd67ed3b56773c185e85fc084f6bdcdec10e5032a7c25a6bc7d682ef"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3151822aa1db0eb5afd65ccfafebe0ef5cda3a7701a279c8d0bf17781a793bb4"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cdba1f73b64530c47b27118b7053b8447e6d6f3c8104e3ac59f3d40c33aa9fd"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf5ae8a9dcf657fd72144a7fd01f243236ea39e7344e579a121c4205aedf07bb"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-win32.whl", hash = "sha256:ea308cec940905ba008291d93619d92edaf83232ec85fbd514dcb329f3192761"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-win_amd64.whl", hash = "sha256:635d8a21577341dfe4f7fa59ec394b346da12420b86624a69e466d446de16aff"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8c4096727193762e72ce9437e2a86a110cf081241919ce3fab8e89c02f6b6658"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e4fb5ac86d8fe8151966814f6720996430462e633d225497566b3996966b9bdb"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f95fc8e3f34b5f6b3effb49d10ac97c569ec8e32f985612d9b25dd12d0d2e94"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:12b0f1ec623cccf058cf21cb544f0e74656618165b083d78145cafde156ea7b6"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-win32.whl", hash = "sha256:293f9ade06b2e68dd03cfb14d49202fac47b7bb94bffcff174568c951fbc7af2"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-win_amd64.whl", hash = "sha256:d70f53a0646cc418ca4853da57cf3ddddbccb8c98406791f24426f2dd77fd0e2"}, + {file = "SQLAlchemy-2.0.37-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:44f569d0b1eb82301b92b72085583277316e7367e038d97c3a1a899d9a05e342"}, + {file = "SQLAlchemy-2.0.37-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfff7be361048244c3aa0f60b5e63221c5e0f0e509f4e47b8910e22b57d10ae7"}, + {file = "SQLAlchemy-2.0.37-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:84b9f23b0fa98a6a4b99d73989350a94e4a4ec476b9a7dfe9b79ba5939f5e80b"}, + {file = "SQLAlchemy-2.0.37-cp37-cp37m-win32.whl", hash = "sha256:51bc9cfef83e0ac84f86bf2b10eaccb27c5a3e66a1212bef676f5bee6ef33ebb"}, + {file = "SQLAlchemy-2.0.37-cp37-cp37m-win_amd64.whl", hash = "sha256:8e47f1af09444f87c67b4f1bb6231e12ba6d4d9f03050d7fc88df6d075231a49"}, + {file = "SQLAlchemy-2.0.37-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6b788f14c5bb91db7f468dcf76f8b64423660a05e57fe277d3f4fad7b9dcb7ce"}, + {file = "SQLAlchemy-2.0.37-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521ef85c04c33009166777c77e76c8a676e2d8528dc83a57836b63ca9c69dcd1"}, + {file = "SQLAlchemy-2.0.37-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cce918ada64c956b62ca2c2af59b125767097ec1dca89650a6221e887521bfd7"}, + {file = "SQLAlchemy-2.0.37-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cf95a60b36997dad99692314c4713f141b61c5b0b4cc5c3426faad570b31ca01"}, + {file = "SQLAlchemy-2.0.37-cp38-cp38-win32.whl", hash = "sha256:d75ead7dd4d255068ea0f21492ee67937bd7c90964c8f3c2bea83c7b7f81b95f"}, + {file = "SQLAlchemy-2.0.37-cp38-cp38-win_amd64.whl", hash = "sha256:74bbd1d0a9bacf34266a7907d43260c8d65d31d691bb2356f41b17c2dca5b1d0"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:648ec5acf95ad59255452ef759054f2176849662af4521db6cb245263ae4aa33"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:35bd2df269de082065d4b23ae08502a47255832cc3f17619a5cea92ce478b02b"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82df02816c14f8dc9f4d74aea4cb84a92f4b0620235daa76dde002409a3fbb5a"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:955a2a765aa1bd81aafa69ffda179d4fe3e2a3ad462a736ae5b6f387f78bfeb8"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-win32.whl", hash = "sha256:03f0528c53ca0b67094c4764523c1451ea15959bbf0a8a8a3096900014db0278"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-win_amd64.whl", hash = "sha256:4b12885dc85a2ab2b7d00995bac6d967bffa8594123b02ed21e8eb2205a7584b"}, + {file = "SQLAlchemy-2.0.37-py3-none-any.whl", hash = "sha256:a8998bf9f8658bd3839cbc44ddbe982955641863da0c1efe5b00c1ab4f5c16b1"}, + {file = "sqlalchemy-2.0.37.tar.gz", hash = "sha256:12b28d99a9c14eaf4055810df1001557176716de0167b91026e648e65229bffb"}, ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} +greenlet = {version = "!=0.4.17", markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} typing-extensions = ">=4.6.0" [package.extras] @@ -2827,13 +2813,13 @@ standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", [[package]] name = "virtualenv" -version = "20.28.0" +version = "20.28.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" files = [ - {file = "virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0"}, - {file = "virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa"}, + {file = "virtualenv-20.28.1-py3-none-any.whl", hash = "sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb"}, + {file = "virtualenv-20.28.1.tar.gz", hash = "sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329"}, ] [package.dependencies] @@ -2955,76 +2941,90 @@ test = ["pytest (>=6.0.0)", "setuptools (>=65)"] [[package]] name = "wrapt" -version = "1.17.0" +version = "1.17.2" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" files = [ - {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"}, - {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"}, - {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"}, - {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"}, - {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"}, - {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"}, - {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"}, - {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"}, - {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"}, - {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"}, - {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"}, - {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"}, - {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"}, - {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"}, - {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"}, - {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"}, - {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"}, - {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"}, - {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"}, - {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"}, - {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"}, - {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"}, - {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"}, + {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"}, + {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"}, + {file = "wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7"}, + {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c"}, + {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72"}, + {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061"}, + {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2"}, + {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c"}, + {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62"}, + {file = "wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563"}, + {file = "wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f"}, + {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"}, + {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda"}, + {file = "wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438"}, + {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a"}, + {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000"}, + {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6"}, + {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b"}, + {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662"}, + {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72"}, + {file = "wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317"}, + {file = "wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3"}, + {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925"}, + {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392"}, + {file = "wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40"}, + {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d"}, + {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b"}, + {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98"}, + {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82"}, + {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae"}, + {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9"}, + {file = "wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9"}, + {file = "wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991"}, + {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125"}, + {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998"}, + {file = "wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5"}, + {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8"}, + {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6"}, + {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc"}, + {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2"}, + {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b"}, + {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504"}, + {file = "wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a"}, + {file = "wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845"}, + {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192"}, + {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b"}, + {file = "wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0"}, + {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306"}, + {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb"}, + {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681"}, + {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6"}, + {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6"}, + {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f"}, + {file = "wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555"}, + {file = "wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c"}, + {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9"}, + {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119"}, + {file = "wrapt-1.17.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6"}, + {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9"}, + {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a"}, + {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2"}, + {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a"}, + {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04"}, + {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f"}, + {file = "wrapt-1.17.2-cp38-cp38-win32.whl", hash = "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7"}, + {file = "wrapt-1.17.2-cp38-cp38-win_amd64.whl", hash = "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3"}, + {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a"}, + {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061"}, + {file = "wrapt-1.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82"}, + {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9"}, + {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f"}, + {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b"}, + {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f"}, + {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8"}, + {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9"}, + {file = "wrapt-1.17.2-cp39-cp39-win32.whl", hash = "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb"}, + {file = "wrapt-1.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb"}, + {file = "wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8"}, + {file = "wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3"}, ] [[package]] @@ -3063,4 +3063,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "fe1302de010997620d26332a86519ca96b9bba6ecfbe75ee43d8538f0afac266" +content-hash = "ac7633388e00416af61c93e16c61c3b4a446e406afab2758585088e2b3416eee" diff --git a/pyproject.toml b/pyproject.toml index b5a1ba661ae..c152f8c580a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ typing_extensions = ">=4.6.0" [tool.poetry.group.dev.dependencies] pytest = ">=7.1.2,<9.0" pytest-mock = ">=3.10.0,<4.0" -pyright = ">=1.1.229,<=1.1.391" +pyright = ">=1.1.392.post0,<1.2" darglint = ">=1.8.1,<2.0" dill = ">=0.3.8" toml = ">=0.10.2,<1.0" From 2a02e96d87a43d94f6e05a3299634f08b2fc96f8 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 15 Jan 2025 10:25:25 -0800 Subject: [PATCH 31/86] make the thing compile again --- reflex/vars/datetime.py | 150 +++++++++++----------------------------- 1 file changed, 40 insertions(+), 110 deletions(-) diff --git a/reflex/vars/datetime.py b/reflex/vars/datetime.py index b6f4c24c615..740f768e433 100644 --- a/reflex/vars/datetime.py +++ b/reflex/vars/datetime.py @@ -5,10 +5,7 @@ import dataclasses import sys from datetime import date, datetime -from typing import Any, NoReturn, TypeVar, Union, overload - -from reflex.utils.exceptions import VarTypeError -from reflex.vars.number import BooleanVar +from typing import TypeVar, Union from .base import ( CustomVarOperationReturn, @@ -24,97 +21,32 @@ datetime_types = Union[datetime, date] -def raise_var_type_error(): - """Raise a VarTypeError. - - Raises: - VarTypeError: Cannot compare a datetime object with a non-datetime object. - """ - raise VarTypeError("Cannot compare a datetime object with a non-datetime object.") - - -class DateTimeVar(Var[DATETIME_T], python_types=(datetime, date)): - """A variable that holds a datetime or date object.""" - - @overload - def __lt__(self, other: datetime_types) -> BooleanVar: ... - - @overload - def __lt__(self, other: NoReturn) -> NoReturn: ... - - def __lt__(self, other: Any): - """Less than comparison. - - Args: - other: The other datetime to compare. - - Returns: - The result of the comparison. - """ - if not isinstance(other, DATETIME_TYPES): - raise_var_type_error() - return date_lt_operation(self, other) - - @overload - def __le__(self, other: datetime_types) -> BooleanVar: ... - - @overload - def __le__(self, other: NoReturn) -> NoReturn: ... - - def __le__(self, other: Any): - """Less than or equal comparison. - - Args: - other: The other datetime to compare. - - Returns: - The result of the comparison. - """ - if not isinstance(other, DATETIME_TYPES): - raise_var_type_error() - return date_le_operation(self, other) - - @overload - def __gt__(self, other: datetime_types) -> BooleanVar: ... - - @overload - def __gt__(self, other: NoReturn) -> NoReturn: ... - - def __gt__(self, other: Any): - """Greater than comparison. - - Args: - other: The other datetime to compare. - - Returns: - The result of the comparison. - """ - if not isinstance(other, DATETIME_TYPES): - raise_var_type_error() - return date_gt_operation(self, other) - - @overload - def __ge__(self, other: datetime_types) -> BooleanVar: ... - - @overload - def __ge__(self, other: NoReturn) -> NoReturn: ... - - def __ge__(self, other: Any): - """Greater than or equal comparison. +def date_compare_operation( + lhs: Var[datetime_types], + rhs: Var[datetime_types], + strict: bool = False, +) -> CustomVarOperationReturn[bool]: + """Check if the value is less than the other value. - Args: - other: The other datetime to compare. + Args: + lhs: The left-hand side of the operation. + rhs: The right-hand side of the operation. + strict: Whether to use strict comparison. - Returns: - The result of the comparison. - """ - if not isinstance(other, DATETIME_TYPES): - raise_var_type_error() - return date_ge_operation(self, other) + Returns: + The result of the operation. + """ + return var_operation_return( + f"({lhs} { '<' if strict else '<='} {rhs})", + bool, + ) @var_operation -def date_gt_operation(lhs: Var | Any, rhs: Var | Any) -> CustomVarOperationReturn: +def date_gt_operation( + lhs: Var[datetime_types], + rhs: Var[datetime_types], +) -> CustomVarOperationReturn: """Greater than comparison. Args: @@ -128,7 +60,10 @@ def date_gt_operation(lhs: Var | Any, rhs: Var | Any) -> CustomVarOperationRetur @var_operation -def date_lt_operation(lhs: Var | Any, rhs: Var | Any) -> CustomVarOperationReturn: +def date_lt_operation( + lhs: Var[datetime_types], + rhs: Var[datetime_types], +) -> CustomVarOperationReturn: """Less than comparison. Args: @@ -142,7 +77,9 @@ def date_lt_operation(lhs: Var | Any, rhs: Var | Any) -> CustomVarOperationRetur @var_operation -def date_le_operation(lhs: Var | Any, rhs: Var | Any) -> CustomVarOperationReturn: +def date_le_operation( + lhs: Var[datetime_types], rhs: Var[datetime_types] +) -> CustomVarOperationReturn: """Less than or equal comparison. Args: @@ -156,7 +93,9 @@ def date_le_operation(lhs: Var | Any, rhs: Var | Any) -> CustomVarOperationRetur @var_operation -def date_ge_operation(lhs: Var | Any, rhs: Var | Any) -> CustomVarOperationReturn: +def date_ge_operation( + lhs: Var[datetime_types], rhs: Var[datetime_types] +) -> CustomVarOperationReturn: """Greater than or equal comparison. Args: @@ -169,25 +108,16 @@ def date_ge_operation(lhs: Var | Any, rhs: Var | Any) -> CustomVarOperationRetur return date_compare_operation(rhs, lhs) -def date_compare_operation( - lhs: DateTimeVar[DATETIME_T] | Any, - rhs: DateTimeVar[DATETIME_T] | Any, - strict: bool = False, -) -> CustomVarOperationReturn: - """Check if the value is less than the other value. +class DateTimeVar(Var[DATETIME_T], python_types=(datetime, date)): + """A variable that holds a datetime or date object.""" - Args: - lhs: The left-hand side of the operation. - rhs: The right-hand side of the operation. - strict: Whether to use strict comparison. + __lt__ = date_lt_operation - Returns: - The result of the operation. - """ - return var_operation_return( - f"({lhs} { '<' if strict else '<='} {rhs})", - bool, - ) + __le__ = date_le_operation + + __gt__ = date_gt_operation + + __ge__ = date_ge_operation @dataclasses.dataclass( From 45dde0072ec9325ca38495a16ac2b8e78e305b28 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 15 Jan 2025 10:32:15 -0800 Subject: [PATCH 32/86] handle vars with var_type being BaseComponent --- reflex/components/core/cond.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index fa63ca895e1..5f70fef2264 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -10,6 +10,7 @@ from reflex.constants import Dirs from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode from reflex.utils.imports import ImportDict, ImportVar +from reflex.utils.types import safe_issubclass from reflex.vars import VarData from reflex.vars.base import LiteralVar, Var from reflex.vars.number import ternary_operation @@ -140,7 +141,9 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var: cond_var = LiteralVar.create(condition) # If the first component is a component, create a Fragment if the second component is not set. - if isinstance(c1, BaseComponent): + if isinstance(c1, BaseComponent) or ( + isinstance(c1, Var) and safe_issubclass(c1._var_type, BaseComponent) + ): c2 = c2 if c2 is not None else Fragment.create() # Check that the second argument is valid. From f0f84d541052139ec07e466a4504f6dad442c82a Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 15 Jan 2025 17:00:06 -0800 Subject: [PATCH 33/86] fix some tests --- pyproject.toml | 2 +- reflex/.templates/web/utils/state.js | 21 +- reflex/components/base/bare.py | 4 +- reflex/components/component.py | 32 +- reflex/components/core/banner.py | 26 +- reflex/components/core/cond.py | 20 +- reflex/components/core/match.py | 10 +- reflex/components/datadisplay/code.py | 2 +- reflex/components/markdown/markdown.py | 4 +- reflex/utils/types.py | 16 + reflex/vars/base.py | 77 +++- reflex/vars/function.py | 50 ++- reflex/vars/number.py | 19 +- reflex/vars/object.py | 3 + reflex/vars/sequence.py | 95 ++++- tests/units/components/core/test_banner.py | 2 + tests/units/components/test_component.py | 2 +- tests/units/test_state.py | 7 +- tests/units/test_var.py | 393 +++++++++++++++------ 19 files changed, 607 insertions(+), 178 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c152f8c580a..67bcef1cb21 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -103,5 +103,5 @@ asyncio_default_fixture_loop_scope = "function" asyncio_mode = "auto" [tool.codespell] -skip = "docs/*,*.html,examples/*, *.pyi" +skip = "docs/*,*.html,examples/*, *.pyi, *.lock" ignore-words-list = "te, TreeE" diff --git a/reflex/.templates/web/utils/state.js b/reflex/.templates/web/utils/state.js index 4f2f4c04604..3cec050a8cf 100644 --- a/reflex/.templates/web/utils/state.js +++ b/reflex/.templates/web/utils/state.js @@ -300,10 +300,7 @@ export const applyEvent = async (event, socket) => { // Send the event to the server. if (socket) { - socket.emit( - "event", - event, - ); + socket.emit("event", event); return true; } @@ -410,7 +407,7 @@ export const connect = async ( autoUnref: false, }); // Ensure undefined fields in events are sent as null instead of removed - socket.current.io.encoder.replacer = (k, v) => (v === undefined ? null : v) + socket.current.io.encoder.replacer = (k, v) => (v === undefined ? null : v); function checkVisibility() { if (document.visibilityState === "visible") { @@ -488,7 +485,7 @@ export const uploadFiles = async ( return false; } - const upload_ref_name = `__upload_controllers_${upload_id}` + const upload_ref_name = `__upload_controllers_${upload_id}`; if (refs[upload_ref_name]) { console.log("Upload already in progress for ", upload_id); @@ -924,6 +921,18 @@ export const atSlice = (arrayLike, slice) => { .filter((_, i) => i % step === 0); }; +/** + * Get the value at a slice or index. + * @param {Array | string} arrayLike The array to get the value from. + * @param {number | [number, number, number]} sliceOrIndex The slice or index to get the value at. + * @returns The value at the slice or index. + */ +export const atSliceOrIndex = (arrayLike, sliceOrIndex) => { + return Array.isArray(sliceOrIndex) + ? atSlice(arrayLike, sliceOrIndex) + : arrayLike.at(sliceOrIndex); +}; + /** * Get the value from a ref. * @param ref The ref to get the value from. diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index ae1a4822422..b6d2befd5a8 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -61,13 +61,13 @@ def _get_all_hooks(self) -> dict[str, VarData | None]: hooks |= component._get_all_hooks() return hooks - def _get_all_imports(self) -> ParsedImportDict: + def _get_all_imports(self, collapse: bool = False) -> ParsedImportDict: """Include the imports for the component. Returns: The imports for the component. """ - imports = super()._get_all_imports() + imports = super()._get_all_imports(collapse=collapse) if isinstance(self.contents, Var): var_data = self.contents._get_all_var_data() if var_data: diff --git a/reflex/components/component.py b/reflex/components/component.py index 68a43d8893b..015406d921e 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -930,6 +930,7 @@ def _validate_component_children(self, children: List[Component]): children: The children of the component. """ + from reflex.components.base.bare import Bare from reflex.components.base.fragment import Fragment from reflex.components.core.cond import Cond from reflex.components.core.foreach import Foreach @@ -960,6 +961,16 @@ def validate_child(child): validate_child(child.comp1) validate_child(child.comp2) + if ( + isinstance(child, Bare) + and child.contents is not None + and isinstance(child.contents, Var) + ): + var_data = child.contents._get_all_var_data() + if var_data is not None: + for c in var_data.components: + validate_child(c) + if isinstance(child, Match): for cases in child.match_cases: validate_child(cases[-1]) @@ -970,10 +981,23 @@ def validate_child(child): f"The component `{comp_name}` cannot have `{child_name}` as a child component" ) - if self._valid_children and child_name not in [ - *self._valid_children, - *allowed_components, - ]: + valid_children = self._valid_children + allowed_components + + def child_is_in_valid(child): + if type(child).__name__ in valid_children: + return True + + if ( + not isinstance(child, Bare) + or child.contents is None + or not isinstance(child.contents, Var) + or (var_data := child.contents._get_all_var_data()) is None + ): + return False + + return all(child_is_in_valid(c) for c in var_data.components) + + if self._valid_children and not child_is_in_valid(child): valid_child_list = ", ".join( [f"`{v_child}`" for v_child in self._valid_children] ) diff --git a/reflex/components/core/banner.py b/reflex/components/core/banner.py index b7b6fae6c84..e25951fb09b 100644 --- a/reflex/components/core/banner.py +++ b/reflex/components/core/banner.py @@ -175,6 +175,8 @@ def create(cls, comp: Optional[Component] = None) -> Component: Returns: The connection banner component. """ + from reflex.components.base.bare import Bare + if not comp: comp = Flex.create( Text.create( @@ -189,7 +191,7 @@ def create(cls, comp: Optional[Component] = None) -> Component: position="fixed", ) - return cond(has_connection_errors, comp) + return Bare.create(cond(has_connection_errors, comp)) class ConnectionModal(Component): @@ -205,18 +207,22 @@ def create(cls, comp: Optional[Component] = None) -> Component: Returns: The connection banner component. """ + from reflex.components.base.bare import Bare + if not comp: comp = Text.create(*default_connection_error()) - return cond( - has_too_many_connection_errors, - DialogRoot.create( - DialogContent.create( - DialogTitle.create("Connection Error"), - comp, + return Bare.create( + cond( + has_too_many_connection_errors, + DialogRoot.create( + DialogContent.create( + DialogTitle.create("Connection Error"), + comp, + ), + open=has_too_many_connection_errors, + z_index=9999, ), - open=has_too_many_connection_errors, - z_index=9999, - ), + ) ) diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index 5f70fef2264..ccb13c17b1a 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -12,7 +12,8 @@ from reflex.utils.imports import ImportDict, ImportVar from reflex.utils.types import safe_issubclass from reflex.vars import VarData -from reflex.vars.base import LiteralVar, Var +from reflex.vars.base import LiteralVar, ReflexCallable, Var +from reflex.vars.function import ArgsFunctionOperation from reflex.vars.number import ternary_operation _IS_TRUE_IMPORT: ImportDict = { @@ -150,12 +151,23 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var: if c2 is None: raise ValueError("For conditional vars, the second argument must be set.") + c1 = Var.create(c1) + c2 = Var.create(c2) + # Create the conditional var. return ternary_operation( cond_var.bool(), - c1, - c2, - ) + ArgsFunctionOperation.create( + (), + c1, + _var_type=ReflexCallable[[], c1._var_type], + ), + ArgsFunctionOperation.create( + (), + c2, + _var_type=ReflexCallable[[], c2._var_type], + ), + ).call() @overload diff --git a/reflex/components/core/match.py b/reflex/components/core/match.py index 8b9382c8998..3c1e27004d9 100644 --- a/reflex/components/core/match.py +++ b/reflex/components/core/match.py @@ -184,11 +184,17 @@ def _validate_return_types(cls, match_cases: List[List[Var]]) -> None: return_type = Var for index, case in enumerate(match_cases): - if not types._issubclass(type(case[-1]), return_type): + if not ( + types._issubclass(type(case[-1]), return_type) + or ( + isinstance(case[-1], Var) + and types.typehint_issubclass(case[-1]._var_type, return_type) + ) + ): raise MatchTypeError( f"Match cases should have the same return types. Case {index} with return " f"value `{case[-1]._js_expr if isinstance(case[-1], Var) else textwrap.shorten(str(case[-1]), width=250)}`" - f" of type {type(case[-1])!r} is not {return_type}" + f" of type {(type(case[-1]) if not isinstance(case[-1], Var) else case[-1]._var_type)!r} is not {return_type}" ) @classmethod diff --git a/reflex/components/datadisplay/code.py b/reflex/components/datadisplay/code.py index 8a433c18caa..dfccad149d1 100644 --- a/reflex/components/datadisplay/code.py +++ b/reflex/components/datadisplay/code.py @@ -447,7 +447,7 @@ def create( # react-syntax-highlighter doesn't have an explicit "light" or "dark" theme so we use one-light and one-dark # themes respectively to ensure code compatibility. - if "theme" in props and not isinstance(props["theme"], Var): + if props.get("theme") is not None and not isinstance(props["theme"], Var): props["theme"] = getattr(Theme, format.to_snake_case(props["theme"])) # type: ignore console.deprecate( feature_name="theme prop as string", diff --git a/reflex/components/markdown/markdown.py b/reflex/components/markdown/markdown.py index 7c65c0d4342..bf94f5f70fd 100644 --- a/reflex/components/markdown/markdown.py +++ b/reflex/components/markdown/markdown.py @@ -113,8 +113,8 @@ def create_map_fn_var( explicit_return = explicit_return or cls._explicit_return return ArgsFunctionOperation.create( - args_names=(DestructuredArg(fields=tuple(fn_args)),), - return_expr=fn_body, + (DestructuredArg(fields=tuple(fn_args)),), + fn_body, explicit_return=explicit_return, ) diff --git a/reflex/utils/types.py b/reflex/utils/types.py index d6c8729efeb..6503be8be03 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -850,6 +850,22 @@ def safe_issubclass(cls: Any, class_or_tuple: Any, /) -> bool: ) from e +def infallible_issubclass(cls: Any, class_or_tuple: Any, /) -> bool: + """Check if a class is a subclass of another class or a tuple of classes. + + Args: + cls: The class to check. + class_or_tuple: The class or tuple of classes to check against. + + Returns: + Whether the class is a subclass of the other class or tuple of classes. + """ + try: + return issubclass(cls, class_or_tuple) + except TypeError: + return False + + def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> bool: """Check if a type hint is a subclass of another type hint. diff --git a/reflex/vars/base.py b/reflex/vars/base.py index db279097725..397d540ede3 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -51,7 +51,7 @@ from reflex import constants from reflex.base import Base from reflex.constants.compiler import Hooks -from reflex.utils import console, exceptions, imports, serializers, types +from reflex.utils import console, imports, serializers, types from reflex.utils.exceptions import ( VarAttributeError, VarDependencyError, @@ -72,6 +72,7 @@ _isinstance, get_origin, has_args, + infallible_issubclass, typehint_issubclass, unionize, ) @@ -125,8 +126,25 @@ def unwrap_reflex_callalbe( """ if callable_type is ReflexCallable: return Ellipsis, Any - if get_origin(callable_type) is not ReflexCallable: + + origin = get_origin(callable_type) + + if origin is not ReflexCallable: + if origin in types.UnionTypes: + args = get_args(callable_type) + params: List[ReflexCallableParams] = [] + return_types: List[GenericType] = [] + for arg in args: + param, return_type = unwrap_reflex_callalbe(arg) + if param not in params: + params.append(param) + return_types.append(return_type) + return ( + Ellipsis if len(params) > 1 else params[0], + unionize(*return_types), + ) return Ellipsis, Any + args = get_args(callable_type) if not args or len(args) != 2: return Ellipsis, Any @@ -143,6 +161,7 @@ class VarSubclassEntry: var_subclass: Type[Var] to_var_subclass: Type[ToOperation] python_types: Tuple[GenericType, ...] + is_subclass: Callable[[GenericType], bool] | None _var_subclasses: List[VarSubclassEntry] = [] @@ -208,7 +227,7 @@ def __init__( object.__setattr__(self, "imports", immutable_imports) object.__setattr__(self, "hooks", tuple(hooks or {})) object.__setattr__( - self, "components", tuple(components) if components is not None else tuple() + self, "components", tuple(components) if components is not None else () ) object.__setattr__(self, "deps", tuple(deps or [])) object.__setattr__(self, "position", position or None) @@ -444,6 +463,7 @@ def __init_subclass__( cls, python_types: Tuple[GenericType, ...] | GenericType = types.Unset(), default_type: GenericType = types.Unset(), + is_subclass: Callable[[GenericType], bool] | types.Unset = types.Unset(), **kwargs, ): """Initialize the subclass. @@ -451,11 +471,12 @@ def __init_subclass__( Args: python_types: The python types that the var represents. default_type: The default type of the var. Defaults to the first python type. + is_subclass: A function to check if a type is a subclass of the var. **kwargs: Additional keyword arguments. """ super().__init_subclass__(**kwargs) - if python_types or default_type: + if python_types or default_type or is_subclass: python_types = ( (python_types if isinstance(python_types, tuple) else (python_types,)) if python_types @@ -480,7 +501,14 @@ class ToVarOperation(ToOperation, cls): ToVarOperation.__name__ = f'To{cls.__name__.removesuffix("Var")}Operation' - _var_subclasses.append(VarSubclassEntry(cls, ToVarOperation, python_types)) + _var_subclasses.append( + VarSubclassEntry( + cls, + ToVarOperation, + python_types, + is_subclass if not isinstance(is_subclass, types.Unset) else None, + ) + ) def __post_init__(self): """Post-initialize the var.""" @@ -726,7 +754,12 @@ def to( # If the first argument is a python type, we map it to the corresponding Var type. for var_subclass in _var_subclasses[::-1]: - if fixed_output_type in var_subclass.python_types: + if ( + var_subclass.python_types + and infallible_issubclass(fixed_output_type, var_subclass.python_types) + ) or ( + var_subclass.is_subclass and var_subclass.is_subclass(fixed_output_type) + ): return self.to(var_subclass.var_subclass, output) if fixed_output_type is None: @@ -801,12 +834,13 @@ def guess_type(self) -> Var: Raises: TypeError: If the type is not supported for guessing. """ - from .number import NumberVar from .object import ObjectVar var_type = self._var_type + if var_type is None: return self.to(None) + if types.is_optional(var_type): var_type = types.get_args(var_type)[0] @@ -818,10 +852,15 @@ def guess_type(self) -> Var: if fixed_type in types.UnionTypes: inner_types = get_args(var_type) - if all( - inspect.isclass(t) and issubclass(t, (int, float)) for t in inner_types - ): - return self.to(NumberVar, self._var_type) + for var_subclass in _var_subclasses: + if all( + ( + infallible_issubclass(t, var_subclass.python_types) + or (var_subclass.is_subclass and var_subclass.is_subclass(t)) + ) + for t in inner_types + ): + return self.to(var_subclass.var_subclass, self._var_type) if can_use_in_object_var(var_type): return self.to(ObjectVar, self._var_type) @@ -839,7 +878,9 @@ def guess_type(self) -> Var: return self.to(None) for var_subclass in _var_subclasses[::-1]: - if issubclass(fixed_type, var_subclass.python_types): + if infallible_issubclass(fixed_type, var_subclass.python_types) or ( + var_subclass.is_subclass and var_subclass.is_subclass(fixed_type) + ): return self.to(var_subclass.var_subclass, self._var_type) if can_use_in_object_var(fixed_type): @@ -1799,6 +1840,7 @@ def add(a: Var[int], b: Var[int]): ), function_name=func_name, type_computer=custom_operation_return._type_computer, + _raw_js_function=custom_operation_return._raw_js_function, _var_type=ReflexCallable[ tuple( arg_python_type @@ -2541,15 +2583,17 @@ def wrapper(fget: Callable[[BASE_STATE], Any]) -> ComputedVar: class CustomVarOperationReturn(Var[RETURN]): """Base class for custom var operations.""" - _type_computer: Optional[TypeComputer] = dataclasses.field(default=None) + _type_computer: TypeComputer | None = dataclasses.field(default=None) + _raw_js_function: str | None = dataclasses.field(default=None) @classmethod def create( cls, js_expression: str, _var_type: Type[RETURN] | None = None, - _type_computer: Optional[TypeComputer] = None, + _type_computer: TypeComputer | None = None, _var_data: VarData | None = None, + _raw_js_function: str | None = None, ) -> CustomVarOperationReturn[RETURN]: """Create a CustomVarOperation. @@ -2558,6 +2602,7 @@ def create( _var_type: The type of the var. _type_computer: A function to compute the type of the var given the arguments. _var_data: Additional hooks and imports associated with the Var. + _raw_js_function: If provided, it will be used when the operation is being called with all of its arguments at once. Returns: The CustomVarOperation. @@ -2567,6 +2612,7 @@ def create( _var_type=_var_type or Any, _type_computer=_type_computer, _var_data=_var_data, + _raw_js_function=_raw_js_function, ) @@ -2575,6 +2621,7 @@ def var_operation_return( var_type: Type[RETURN] | None = None, type_computer: Optional[TypeComputer] = None, var_data: VarData | None = None, + _raw_js_function: str | None = None, ) -> CustomVarOperationReturn[RETURN]: """Shortcut for creating a CustomVarOperationReturn. @@ -2583,6 +2630,7 @@ def var_operation_return( var_type: The type of the var. type_computer: A function to compute the type of the var given the arguments. var_data: Additional hooks and imports associated with the Var. + _raw_js_function: If provided, it will be used when the operation is being called with all of its arguments at once. Returns: The CustomVarOperationReturn. @@ -2592,6 +2640,7 @@ def var_operation_return( _var_type=var_type, _type_computer=type_computer, _var_data=var_data, + _raw_js_function=_raw_js_function, ) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index b023cb9b26c..b62a1e1bea0 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -52,7 +52,23 @@ ) -class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]): +def type_is_reflex_callable(type_: Any) -> bool: + """Check if a type is a ReflexCallable. + + Args: + type_: The type to check. + + Returns: + True if the type is a ReflexCallable. + """ + return type_ is ReflexCallable or get_origin(type_) is ReflexCallable + + +class FunctionVar( + Var[CALLABLE_TYPE], + default_type=ReflexCallable[Any, Any], + is_subclass=type_is_reflex_callable, +): """Base class for immutable function vars.""" @overload @@ -304,15 +320,27 @@ def call(self, *args: Var | Any) -> Var: # pyright: ignore [reportInconsistentO if arg_len is not None: if len(args) < required_arg_len: raise VarTypeError( - f"Passed {len(args)} arguments, expected at least {required_arg_len} for {str(self)}" + f"Passed {len(args)} arguments, expected at least {required_arg_len} for {self!s}" ) if len(args) > arg_len: raise VarTypeError( - f"Passed {len(args)} arguments, expected at most {arg_len} for {str(self)}" + f"Passed {len(args)} arguments, expected at most {arg_len} for {self!s}" ) args = tuple(map(LiteralVar.create, args)) self._pre_check(*args) return_type = self._return_type(*args) + if ( + isinstance(self, (ArgsFunctionOperation, ArgsFunctionOperationBuilder)) + and self._raw_js_function + ): + return VarOperationCall.create( + FunctionStringVar.create( + self._raw_js_function, _var_type=self._var_type + ), + *args, + _var_type=return_type, + ).guess_type() + return VarOperationCall.create(self, *args, _var_type=return_type).guess_type() def chain( @@ -412,7 +440,7 @@ def _pre_check( Returns: True if the function can be called with the given arguments. """ - return tuple() + return () @overload def __get__(self, instance: None, owner: Any) -> FunctionVar[CALLABLE_TYPE]: ... @@ -588,7 +616,7 @@ def format_args_function_operation( [ (arg if isinstance(arg, str) else arg.to_javascript()) + ( - f" = {str(default_value.default)}" + f" = {default_value.default!s}" if i < len(self._default_values) and not isinstance( (default_value := self._default_values[i]), inspect.Parameter.empty @@ -632,10 +660,10 @@ def pre_check_args( arg_name = self._args.args[i] if i < len(self._args.args) else None if arg_name is not None: raise VarTypeError( - f"Invalid argument {str(arg)} provided to {arg_name} in {self._function_name or 'var operation'}. {validation_message}" + f"Invalid argument {arg!s} provided to {arg_name} in {self._function_name or 'var operation'}. {validation_message}" ) raise VarTypeError( - f"Invalid argument {str(arg)} provided to argument {i} in {self._function_name or 'var operation'}. {validation_message}" + f"Invalid argument {arg!s} provided to argument {i} in {self._function_name or 'var operation'}. {validation_message}" ) return self._validators[len(args) :] @@ -679,6 +707,7 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]): _function_name: str = dataclasses.field(default="") _type_computer: Optional[TypeComputer] = dataclasses.field(default=None) _explicit_return: bool = dataclasses.field(default=False) + _raw_js_function: str | None = dataclasses.field(default=None) _cached_var_name = cached_property_no_lock(format_args_function_operation) @@ -698,6 +727,7 @@ def create( function_name: str = "", explicit_return: bool = False, type_computer: Optional[TypeComputer] = None, + _raw_js_function: str | None = None, _var_type: GenericType = Callable, _var_data: VarData | None = None, ): @@ -712,6 +742,7 @@ def create( function_name: The name of the function. explicit_return: Whether to use explicit return syntax. type_computer: A function to compute the return type. + _raw_js_function: If provided, it will be used when the operation is being called with all of its arguments at once. _var_type: The type of the var. _var_data: Additional hooks and imports associated with the Var. @@ -723,6 +754,7 @@ def create( _var_type=_var_type, _var_data=_var_data, _args=FunctionArgs(args=tuple(args_names), rest=rest), + _raw_js_function=_raw_js_function, _default_values=tuple(default_values), _function_name=function_name, _validators=tuple(validators), @@ -753,6 +785,7 @@ class ArgsFunctionOperationBuilder( _function_name: str = dataclasses.field(default="") _type_computer: Optional[TypeComputer] = dataclasses.field(default=None) _explicit_return: bool = dataclasses.field(default=False) + _raw_js_function: str | None = dataclasses.field(default=None) _cached_var_name = cached_property_no_lock(format_args_function_operation) @@ -772,6 +805,7 @@ def create( function_name: str = "", explicit_return: bool = False, type_computer: Optional[TypeComputer] = None, + _raw_js_function: str | None = None, _var_type: GenericType = Callable, _var_data: VarData | None = None, ): @@ -788,6 +822,7 @@ def create( type_computer: A function to compute the return type. _var_type: The type of the var. _var_data: Additional hooks and imports associated with the Var. + _raw_js_function: If provided, it will be used when the operation is being called with all of its arguments at once. Returns: The function var. @@ -797,6 +832,7 @@ def create( _var_type=_var_type, _var_data=_var_data, _args=FunctionArgs(args=tuple(args_names), rest=rest), + _raw_js_function=_raw_js_function, _default_values=tuple(default_values), _function_name=function_name, _validators=tuple(validators), diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 865ce5cbafa..25d1520ea50 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -530,7 +530,9 @@ def number_abs_operation( The number absolute operation. """ return var_operation_return( - js_expression=f"Math.abs({value})", type_computer=unary_operation_type_computer + js_expression=f"Math.abs({value})", + type_computer=unary_operation_type_computer, + _raw_js_function="Math.abs", ) @@ -657,7 +659,11 @@ def number_floor_operation(value: Var[int | float]): Returns: The number floor operation. """ - return var_operation_return(js_expression=f"Math.floor({value})", var_type=int) + return var_operation_return( + js_expression=f"Math.floor({value})", + var_type=int, + _raw_js_function="Math.floor", + ) @var_operation @@ -763,7 +769,9 @@ def boolean_to_number_operation(value: Var[bool]): Returns: The boolean to number operation. """ - return var_operation_return(js_expression=f"Number({value})", var_type=int) + return var_operation_return( + js_expression=f"Number({value})", var_type=int, _raw_js_function="Number" + ) def comparison_operator( @@ -1002,6 +1010,10 @@ def create(cls, value: bool, _var_data: VarData | None = None): f"$/{Dirs.STATE_PATH}": [ImportVar(tag="atSlice")], } +_AT_SLICE_OR_INDEX: ImportDict = { + f"$/{Dirs.STATE_PATH}": [ImportVar(tag="atSliceOrIndex")], +} + _RANGE_IMPORT: ImportDict = { f"$/{Dirs.UTILS}/helpers/range": [ImportVar(tag="range", is_default=True)], } @@ -1021,6 +1033,7 @@ def boolify(value: Var): js_expression=f"isTrue({value})", var_type=bool, var_data=VarData(imports=_IS_TRUE_IMPORT), + _raw_js_function="isTrue", ) diff --git a/reflex/vars/object.py b/reflex/vars/object.py index dfc76d4cf71..7c793a0fb91 100644 --- a/reflex/vars/object.py +++ b/reflex/vars/object.py @@ -415,6 +415,7 @@ def object_keys_operation(value: Var): return var_operation_return( js_expression=f"Object.keys({value})", var_type=List[str], + _raw_js_function="Object.keys", ) @@ -435,6 +436,7 @@ def object_values_operation(value: Var): lambda x: List[x.to(ObjectVar)._value_type()], ), var_type=List[Any], + _raw_js_function="Object.values", ) @@ -456,6 +458,7 @@ def object_entries_operation(value: Var): lambda x: List[Tuple[str, x.to(ObjectVar)._value_type()]], ), var_type=List[Tuple[str, Any]], + _raw_js_function="Object.entries", ) diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 6136e23986d..997a5a00e60 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -30,8 +30,7 @@ from reflex.constants.colors import Color from reflex.utils.exceptions import VarTypeError from reflex.utils.types import GenericType, get_origin - -from .base import ( +from reflex.vars.base import ( CachedVarOperation, CustomVarOperationReturn, LiteralVar, @@ -51,8 +50,10 @@ var_operation, var_operation_return, ) + from .number import ( _AT_SLICE_IMPORT, + _AT_SLICE_OR_INDEX, _IS_TRUE_IMPORT, _RANGE_IMPORT, LiteralNumberVar, @@ -88,7 +89,7 @@ def string_lt_operation(lhs: Var[str], rhs: Var[str]): Returns: The string less than operation. """ - return var_operation_return(js_expression=f"{lhs} < {rhs}", var_type=bool) + return var_operation_return(js_expression=f"({lhs} < {rhs})", var_type=bool) @var_operation @@ -102,7 +103,7 @@ def string_gt_operation(lhs: Var[str], rhs: Var[str]): Returns: The string greater than operation. """ - return var_operation_return(js_expression=f"{lhs} > {rhs}", var_type=bool) + return var_operation_return(js_expression=f"({lhs} > {rhs})", var_type=bool) @var_operation @@ -116,7 +117,7 @@ def string_le_operation(lhs: Var[str], rhs: Var[str]): Returns: The string less than or equal operation. """ - return var_operation_return(js_expression=f"{lhs} <= {rhs}", var_type=bool) + return var_operation_return(js_expression=f"({lhs} <= {rhs})", var_type=bool) @var_operation @@ -130,7 +131,7 @@ def string_ge_operation(lhs: Var[str], rhs: Var[str]): Returns: The string greater than or equal operation. """ - return var_operation_return(js_expression=f"{lhs} >= {rhs}", var_type=bool) + return var_operation_return(js_expression=f"({lhs} >= {rhs})", var_type=bool) @var_operation @@ -143,7 +144,11 @@ def string_lower_operation(string: Var[str]): Returns: The lowercase string. """ - return var_operation_return(js_expression=f"{string}.toLowerCase()", var_type=str) + return var_operation_return( + js_expression=f"String.prototype.toLowerCase.apply({string})", + var_type=str, + _raw_js_function="String.prototype.toLowerCase.apply", + ) @var_operation @@ -156,7 +161,11 @@ def string_upper_operation(string: Var[str]): Returns: The uppercase string. """ - return var_operation_return(js_expression=f"{string}.toUpperCase()", var_type=str) + return var_operation_return( + js_expression=f"String.prototype.toUpperCase.apply({string})", + var_type=str, + _raw_js_function="String.prototype.toUpperCase.apply", + ) @var_operation @@ -169,7 +178,11 @@ def string_strip_operation(string: Var[str]): Returns: The stripped string. """ - return var_operation_return(js_expression=f"{string}.trim()", var_type=str) + return var_operation_return( + js_expression=f"String.prototype.trim.apply({string})", + var_type=str, + _raw_js_function="String.prototype.trim.apply", + ) @var_operation @@ -259,6 +272,59 @@ def string_item_operation(string: Var[str], index: Var[int]): return var_operation_return(js_expression=f"{string}.at({index})", var_type=str) +@var_operation +def string_slice_operation( + string: Var[str], slice: Var[slice] +) -> CustomVarOperationReturn[str]: + """Get a slice from a string. + + Args: + string: The string. + slice: The slice. + + Returns: + The sliced string. + """ + return var_operation_return( + js_expression=f'atSlice({string}.split(""), {slice}).join("")', + type_computer=nary_type_computer( + ReflexCallable[[List[str], slice], str], + ReflexCallable[[slice], str], + computer=lambda args: str, + ), + var_data=VarData( + imports=_AT_SLICE_IMPORT, + ), + ) + + +@var_operation +def string_index_or_slice_operation( + string: Var[str], index_or_slice: Var[Union[int, slice]] +) -> CustomVarOperationReturn[Union[str, Sequence[str]]]: + """Get an item or slice from a string. + + Args: + string: The string. + index_or_slice: The index or slice. + + Returns: + The item or slice from the string. + """ + return var_operation_return( + js_expression=f"Array.prototype.join.apply(atSliceOrIndex({string}, {index_or_slice}), [''])", + _raw_js_function="atSliceOrIndex", + type_computer=nary_type_computer( + ReflexCallable[[List[str], Union[int, slice]], str], + ReflexCallable[[Union[int, slice]], str], + computer=lambda args: str, + ), + var_data=VarData( + imports=_AT_SLICE_OR_INDEX, + ), + ) + + @var_operation def string_replace_operation( string: Var[str], search_value: Var[str], new_value: Var[str] @@ -454,7 +520,8 @@ def array_item_or_slice_operation( The item or slice from the array. """ return var_operation_return( - js_expression=f"Array.isArray({index_or_slice}) ? at_slice({array}, {index_or_slice}) : {array}.at({index_or_slice})", + js_expression=f"atSliceOrIndex({array}, {index_or_slice})", + _raw_js_function="atSliceOrIndex", type_computer=nary_type_computer( ReflexCallable[[Sequence, Union[int, slice]], Any], ReflexCallable[[Union[int, slice]], Any], @@ -465,7 +532,7 @@ def array_item_or_slice_operation( ), ), var_data=VarData( - imports=_AT_SLICE_IMPORT, + imports=_AT_SLICE_OR_INDEX, ), ) @@ -1073,7 +1140,11 @@ class StringVar(Var[STRING_TYPE], python_types=str): __radd__ = reverse_string_concat_operation - __getitem__ = string_item_operation + __getitem__ = string_index_or_slice_operation + + at = string_item_operation + + slice = string_slice_operation lower = string_lower_operation diff --git a/tests/units/components/core/test_banner.py b/tests/units/components/core/test_banner.py index e1498d12c0a..fc572e9a6ce 100644 --- a/tests/units/components/core/test_banner.py +++ b/tests/units/components/core/test_banner.py @@ -25,6 +25,7 @@ def test_connection_banner(): "react", "$/utils/context", "$/utils/state", + "@emotion/react", RadixThemesComponent().library or "", "$/env.json", ) @@ -43,6 +44,7 @@ def test_connection_modal(): "react", "$/utils/context", "$/utils/state", + "@emotion/react", RadixThemesComponent().library or "", "$/env.json", ) diff --git a/tests/units/components/test_component.py b/tests/units/components/test_component.py index 674873b690f..766f96e61dd 100644 --- a/tests/units/components/test_component.py +++ b/tests/units/components/test_component.py @@ -846,7 +846,7 @@ def get_event_triggers(self) -> Dict[str, Any]: assert comp.render()["props"][0] == ( "onFoo={((__e, _alpha, _bravo, _charlie) => (addEvents(" - f'[(Event("{C1State.get_full_name()}.mock_handler", ({{ ["_e"] : __e["target"]["value"], ["_bravo"] : _bravo["nested"], ["_charlie"] : (_charlie["custom"] + 42) }}), ({{ }})))], ' + f'[(Event("{C1State.get_full_name()}.mock_handler", ({{ ["_e"] : __e["target"]["value"], ["_bravo"] : _bravo["nested"], ["_charlie"] : (((_lhs, _rhs) => (_lhs + _rhs))(_charlie["custom"], 42)) }}), ({{ }})))], ' "[__e, _alpha, _bravo, _charlie], ({ }))))}" ) diff --git a/tests/units/test_state.py b/tests/units/test_state.py index 41fac443e50..3898e4658dc 100644 --- a/tests/units/test_state.py +++ b/tests/units/test_state.py @@ -432,12 +432,15 @@ def test_default_setters(test_state): def test_class_indexing_with_vars(): """Test that we can index into a state var with another var.""" prop = TestState.array[TestState.num1] - assert str(prop) == f"{TestState.get_name()}.array.at({TestState.get_name()}.num1)" + assert ( + str(prop) + == f"(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({TestState.get_name()}.array, ...args)))({TestState.get_name()}.num1))" + ) prop = TestState.mapping["a"][TestState.num1] assert ( str(prop) - == f'{TestState.get_name()}.mapping["a"].at({TestState.get_name()}.num1)' + == f'(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({TestState.get_name()}.mapping["a"], ...args)))({TestState.get_name()}.num1))' ) prop = TestState.mapping[TestState.map_key] diff --git a/tests/units/test_var.py b/tests/units/test_var.py index f3d22f63225..0aa0d2f377f 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -307,30 +307,65 @@ def test_basic_operations(TestObj): Args: TestObj: The test object. """ - assert str(v(1) == v(2)) == "(1 === 2)" - assert str(v(1) != v(2)) == "(1 !== 2)" - assert str(LiteralNumberVar.create(1) < 2) == "(1 < 2)" - assert str(LiteralNumberVar.create(1) <= 2) == "(1 <= 2)" - assert str(LiteralNumberVar.create(1) > 2) == "(1 > 2)" - assert str(LiteralNumberVar.create(1) >= 2) == "(1 >= 2)" - assert str(LiteralNumberVar.create(1) + 2) == "(1 + 2)" - assert str(LiteralNumberVar.create(1) - 2) == "(1 - 2)" - assert str(LiteralNumberVar.create(1) * 2) == "(1 * 2)" - assert str(LiteralNumberVar.create(1) / 2) == "(1 / 2)" - assert str(LiteralNumberVar.create(1) // 2) == "Math.floor(1 / 2)" - assert str(LiteralNumberVar.create(1) % 2) == "(1 % 2)" - assert str(LiteralNumberVar.create(1) ** 2) == "(1 ** 2)" - assert str(LiteralNumberVar.create(1) & v(2)) == "(1 && 2)" - assert str(LiteralNumberVar.create(1) | v(2)) == "(1 || 2)" - assert str(LiteralArrayVar.create([1, 2, 3])[0]) == "[1, 2, 3].at(0)" + assert str(v(1) == v(2)) == "(((_lhs, _rhs) => (_lhs === _rhs))(1, 2))" + assert str(v(1) != v(2)) == "(((_lhs, _rhs) => (_lhs !== _rhs))(1, 2))" + assert ( + str(LiteralNumberVar.create(1) < 2) == "(((_lhs, _rhs) => (_lhs < _rhs))(1, 2))" + ) + assert ( + str(LiteralNumberVar.create(1) <= 2) + == "(((_lhs, _rhs) => (_lhs <= _rhs))(1, 2))" + ) + assert ( + str(LiteralNumberVar.create(1) > 2) == "(((_lhs, _rhs) => (_lhs > _rhs))(1, 2))" + ) + assert ( + str(LiteralNumberVar.create(1) >= 2) + == "(((_lhs, _rhs) => (_lhs >= _rhs))(1, 2))" + ) + assert ( + str(LiteralNumberVar.create(1) + 2) == "(((_lhs, _rhs) => (_lhs + _rhs))(1, 2))" + ) + assert ( + str(LiteralNumberVar.create(1) - 2) == "(((_lhs, _rhs) => (_lhs - _rhs))(1, 2))" + ) + assert ( + str(LiteralNumberVar.create(1) * 2) == "(((_lhs, _rhs) => (_lhs * _rhs))(1, 2))" + ) + assert ( + str(LiteralNumberVar.create(1) / 2) == "(((_lhs, _rhs) => (_lhs / _rhs))(1, 2))" + ) + assert ( + str(LiteralNumberVar.create(1) // 2) + == "(((_lhs, _rhs) => Math.floor(_lhs / _rhs))(1, 2))" + ) + assert ( + str(LiteralNumberVar.create(1) % 2) == "(((_lhs, _rhs) => (_lhs % _rhs))(1, 2))" + ) + assert ( + str(LiteralNumberVar.create(1) ** 2) + == "(((_lhs, _rhs) => (_lhs ** _rhs))(1, 2))" + ) + assert str(LiteralNumberVar.create(1) & v(2)) == "(((_a, _b) => (_a && _b))(1, 2))" + assert str(LiteralNumberVar.create(1) | v(2)) == "(((_a, _b) => (_a || _b))(1, 2))" + assert ( + str(LiteralArrayVar.create([1, 2, 3])[0]) + == "(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3], ...args)))(0))" + ) assert ( str(LiteralObjectVar.create({"a": 1, "b": 2})["a"]) == '({ ["a"] : 1, ["b"] : 2 })["a"]' ) - assert str(v("foo") == v("bar")) == '("foo" === "bar")' - assert str(Var(_js_expr="foo") == Var(_js_expr="bar")) == "(foo === bar)" assert ( - str(LiteralVar.create("foo") == LiteralVar.create("bar")) == '("foo" === "bar")' + str(v("foo") == v("bar")) == '(((_lhs, _rhs) => (_lhs === _rhs))("foo", "bar"))' + ) + assert ( + str(Var(_js_expr="foo") == Var(_js_expr="bar")) + == "(((_lhs, _rhs) => (_lhs === _rhs))(foo, bar))" + ) + assert ( + str(LiteralVar.create("foo") == LiteralVar.create("bar")) + == '(((_lhs, _rhs) => (_lhs === _rhs))("foo", "bar"))' ) print(Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state")) assert ( @@ -338,33 +373,39 @@ def test_basic_operations(TestObj): Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state").bar == LiteralVar.create("bar") ) - == '(state.foo["bar"] === "bar")' + == '(((_lhs, _rhs) => (_lhs === _rhs))(state.foo["bar"], "bar"))' ) assert ( str(Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state").bar) == 'state.foo["bar"]' ) - assert str(abs(LiteralNumberVar.create(1))) == "Math.abs(1)" - assert str(LiteralArrayVar.create([1, 2, 3]).length()) == "[1, 2, 3].length" + assert str(abs(LiteralNumberVar.create(1))) == "(Math.abs(1))" + assert ( + str(LiteralArrayVar.create([1, 2, 3]).length()) + == "(((...args) => (((_array) => _array.length)([1, 2, 3], ...args)))())" + ) assert ( str(LiteralArrayVar.create([1, 2]) + LiteralArrayVar.create([3, 4])) - == "[...[1, 2], ...[3, 4]]" + == "(((...args) => (((_lhs, _rhs) => [..._lhs, ..._rhs])([1, 2], ...args)))([3, 4]))" ) # Tests for reverse operation assert ( str(LiteralArrayVar.create([1, 2, 3]).reverse()) - == "[1, 2, 3].slice().reverse()" + == "(((...args) => (((_array) => _array.slice().reverse())([1, 2, 3], ...args)))())" ) assert ( str(LiteralArrayVar.create(["1", "2", "3"]).reverse()) - == '["1", "2", "3"].slice().reverse()' + == '(((...args) => (((_array) => _array.slice().reverse())(["1", "2", "3"], ...args)))())' ) assert ( str(Var(_js_expr="foo")._var_set_state("state").to(list).reverse()) - == "state.foo.slice().reverse()" + == "(((...args) => (((_array) => _array.slice().reverse())(state.foo, ...args)))())" + ) + assert ( + str(Var(_js_expr="foo").to(list).reverse()) + == "(((...args) => (((_array) => _array.slice().reverse())(foo, ...args)))())" ) - assert str(Var(_js_expr="foo").to(list).reverse()) == "foo.slice().reverse()" assert str(Var(_js_expr="foo", _var_type=str).js_type()) == "(typeof(foo))" @@ -389,14 +430,32 @@ def test_basic_operations(TestObj): ], ) def test_list_tuple_contains(var, expected): - assert str(var.contains(1)) == f"{expected}.includes(1)" - assert str(var.contains("1")) == f'{expected}.includes("1")' - assert str(var.contains(v(1))) == f"{expected}.includes(1)" - assert str(var.contains(v("1"))) == f'{expected}.includes("1")' + assert ( + str(var.contains(1)) + == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))(1))' + ) + assert ( + str(var.contains("1")) + == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))("1"))' + ) + assert ( + str(var.contains(v(1))) + == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))(1))' + ) + assert ( + str(var.contains(v("1"))) + == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))("1"))' + ) other_state_var = Var(_js_expr="other", _var_type=str)._var_set_state("state") other_var = Var(_js_expr="other", _var_type=str) - assert str(var.contains(other_state_var)) == f"{expected}.includes(state.other)" - assert str(var.contains(other_var)) == f"{expected}.includes(other)" + assert ( + str(var.contains(other_state_var)) + == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))(state.other))' + ) + assert ( + str(var.contains(other_var)) + == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))(other))' + ) class Foo(rx.Base): @@ -446,15 +505,27 @@ def test_var_types(var, var_type): ], ) def test_str_contains(var, expected): - assert str(var.contains("1")) == f'{expected}.includes("1")' - assert str(var.contains(v("1"))) == f'{expected}.includes("1")' + assert ( + str(var.contains("1")) + == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))("1"))' + ) + assert ( + str(var.contains(v("1"))) + == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))("1"))' + ) other_state_var = Var(_js_expr="other")._var_set_state("state").to(str) other_var = Var(_js_expr="other").to(str) - assert str(var.contains(other_state_var)) == f"{expected}.includes(state.other)" - assert str(var.contains(other_var)) == f"{expected}.includes(other)" + assert ( + str(var.contains(other_state_var)) + == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))(state.other))' + ) + assert ( + str(var.contains(other_var)) + == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))(other))' + ) assert ( str(var.contains("1", "hello")) - == f'{expected}.some(obj => obj["hello"] === "1")' + == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))("1", "hello"))' ) @@ -467,16 +538,32 @@ def test_str_contains(var, expected): ], ) def test_dict_contains(var, expected): - assert str(var.contains(1)) == f"{expected}.hasOwnProperty(1)" - assert str(var.contains("1")) == f'{expected}.hasOwnProperty("1")' - assert str(var.contains(v(1))) == f"{expected}.hasOwnProperty(1)" - assert str(var.contains(v("1"))) == f'{expected}.hasOwnProperty("1")' + assert ( + str(var.contains(1)) + == f"(((_object, _key) => _object.hasOwnProperty(_key))({expected!s}, 1))" + ) + assert ( + str(var.contains("1")) + == f'(((_object, _key) => _object.hasOwnProperty(_key))({expected!s}, "1"))' + ) + assert ( + str(var.contains(v(1))) + == f"(((_object, _key) => _object.hasOwnProperty(_key))({expected!s}, 1))" + ) + assert ( + str(var.contains(v("1"))) + == f'(((_object, _key) => _object.hasOwnProperty(_key))({expected!s}, "1"))' + ) other_state_var = Var(_js_expr="other")._var_set_state("state").to(str) other_var = Var(_js_expr="other").to(str) assert ( - str(var.contains(other_state_var)) == f"{expected}.hasOwnProperty(state.other)" + str(var.contains(other_state_var)) + == f"(((_object, _key) => _object.hasOwnProperty(_key))({expected!s}, state.other))" + ) + assert ( + str(var.contains(other_var)) + == f"(((_object, _key) => _object.hasOwnProperty(_key))({expected!s}, other))" ) - assert str(var.contains(other_var)) == f"{expected}.hasOwnProperty(other)" @pytest.mark.parametrize( @@ -484,7 +571,6 @@ def test_dict_contains(var, expected): [ Var(_js_expr="list", _var_type=List[int]).guess_type(), Var(_js_expr="tuple", _var_type=Tuple[int, int]).guess_type(), - Var(_js_expr="str", _var_type=str).guess_type(), ], ) def test_var_indexing_lists(var): @@ -494,11 +580,20 @@ def test_var_indexing_lists(var): var : The str, list or tuple base var. """ # Test basic indexing. - assert str(var[0]) == f"{var._js_expr}.at(0)" - assert str(var[1]) == f"{var._js_expr}.at(1)" + assert ( + str(var[0]) + == f"(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({var!s}, ...args)))(0))" + ) + assert ( + str(var[1]) + == f"(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({var!s}, ...args)))(1))" + ) # Test negative indexing. - assert str(var[-1]) == f"{var._js_expr}.at(-1)" + assert ( + str(var[-1]) + == f"(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({var!s}, ...args)))(-1))" + ) @pytest.mark.parametrize( @@ -532,11 +627,20 @@ def test_var_indexing_str(): assert str_var[0]._var_type is str # Test basic indexing. - assert str(str_var[0]) == "str.at(0)" - assert str(str_var[1]) == "str.at(1)" + assert ( + str(str_var[0]) + == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))(0))" + ) + assert ( + str(str_var[1]) + == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))(1))" + ) # Test negative indexing. - assert str(str_var[-1]) == "str.at(-1)" + assert ( + str(str_var[-1]) + == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))(-1))" + ) @pytest.mark.parametrize( @@ -651,9 +755,18 @@ def test_var_list_slicing(var): Args: var : The str, list or tuple base var. """ - assert str(var[:1]) == f"{var._js_expr}.slice(undefined, 1)" - assert str(var[1:]) == f"{var._js_expr}.slice(1, undefined)" - assert str(var[:]) == f"{var._js_expr}.slice(undefined, undefined)" + assert ( + str(var[:1]) + == f"(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({var!s}, ...args)))([null, 1, null]))" + ) + assert ( + str(var[1:]) + == f"(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({var!s}, ...args)))([1, null, null]))" + ) + assert ( + str(var[:]) + == f"(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({var!s}, ...args)))([null, null, null]))" + ) def test_str_var_slicing(): @@ -665,16 +778,40 @@ def test_str_var_slicing(): assert str_var[:1]._var_type is str # Test basic slicing. - assert str(str_var[:1]) == 'str.split("").slice(undefined, 1).join("")' - assert str(str_var[1:]) == 'str.split("").slice(1, undefined).join("")' - assert str(str_var[:]) == 'str.split("").slice(undefined, undefined).join("")' - assert str(str_var[1:2]) == 'str.split("").slice(1, 2).join("")' + assert ( + str(str_var[:1]) + == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([null, 1, null]))" + ) + assert ( + str(str_var[1:]) + == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([1, null, null]))" + ) + assert ( + str(str_var[:]) + == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([null, null, null]))" + ) + assert ( + str(str_var[1:2]) + == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([1, 2, null]))" + ) # Test negative slicing. - assert str(str_var[:-1]) == 'str.split("").slice(undefined, -1).join("")' - assert str(str_var[-1:]) == 'str.split("").slice(-1, undefined).join("")' - assert str(str_var[:-2]) == 'str.split("").slice(undefined, -2).join("")' - assert str(str_var[-2:]) == 'str.split("").slice(-2, undefined).join("")' + assert ( + str(str_var[:-1]) + == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([null, -1, null]))" + ) + assert ( + str(str_var[-1:]) + == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([-1, null, null]))" + ) + assert ( + str(str_var[:-2]) + == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([null, -2, null]))" + ) + assert ( + str(str_var[-2:]) + == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([-2, null, null]))" + ) def test_dict_indexing(): @@ -966,8 +1103,8 @@ def test_var_operation(): def add(a: Var[int], b: Var[int]): return var_operation_return(js_expression=f"({a} + {b})", var_type=int) - assert str(add(1, 2)) == "(1 + 2)" - assert str(add(4, -9)) == "(4 + -9)" + assert str(add(1, 2)) == "(((_a, _b) => (_a + _b))(1, 2))" + assert str(add(4, -9)) == "(((_a, _b) => (_a + _b))(4, -9))" five = LiteralNumberVar.create(5) seven = add(2, five) @@ -978,13 +1115,29 @@ def add(a: Var[int], b: Var[int]): def test_string_operations(): basic_string = LiteralStringVar.create("Hello, World!") - assert str(basic_string.length()) == '"Hello, World!".split("").length' - assert str(basic_string.lower()) == '"Hello, World!".toLowerCase()' - assert str(basic_string.upper()) == '"Hello, World!".toUpperCase()' - assert str(basic_string.strip()) == '"Hello, World!".trim()' - assert str(basic_string.contains("World")) == '"Hello, World!".includes("World")' assert ( - str(basic_string.split(" ").join(",")) == '"Hello, World!".split(" ").join(",")' + str(basic_string.length()) + == '(((...args) => (((...arg) => (((_array) => _array.length)((((_string, _sep = "") => isTrue(_sep) ? _string.split(_sep) : [..._string])(...args)))))("Hello, World!", ...args)))())' + ) + assert ( + str(basic_string.lower()) + == '(((...args) => (((_string) => String.prototype.toLowerCase.apply(_string))("Hello, World!", ...args)))())' + ) + assert ( + str(basic_string.upper()) + == '(((...args) => (((_string) => String.prototype.toUpperCase.apply(_string))("Hello, World!", ...args)))())' + ) + assert ( + str(basic_string.strip()) + == '(((...args) => (((_string) => String.prototype.trim.apply(_string))("Hello, World!", ...args)))())' + ) + assert ( + str(basic_string.contains("World")) + == '(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))("Hello, World!", ...args)))("World"))' + ) + assert ( + str(basic_string.split(" ").join(",")) + == '(((...args) => (((_array, _sep = "") => _array.join(_sep))((((...args) => (((_string, _sep = "") => isTrue(_sep) ? _string.split(_sep) : [..._string])("Hello, World!", ...args)))(" ")), ...args)))(","))' ) @@ -995,7 +1148,7 @@ def test_all_number_operations(): assert ( str(complicated_number) - == "((Math.floor(((-((-5.4 + 1)) * 2) / 3) / 2) % 3) ** 2)" + == "(((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2))" ) even_more_complicated_number = ~( @@ -1004,14 +1157,20 @@ def test_all_number_operations(): assert ( str(even_more_complicated_number) - == "!(((Math.abs(Math.floor(((Math.floor(((-((-5.4 + 1)) * 2) / 3) / 2) % 3) ** 2))) || (2 && Math.round(((Math.floor(((-((-5.4 + 1)) * 2) / 3) / 2) % 3) ** 2)))) !== 0))" + == "(((_value) => !(_value))((((_lhs, _rhs) => (_lhs !== _rhs))((((_a, _b) => (_a || _b))((Math.abs((Math.floor((((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2)))))), (((_a, _b) => (_a && _b))(2, (((_value) => Math.round(_value))((((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2)))))))), 0))))" ) - assert str(LiteralNumberVar.create(5) > False) == "(5 > 0)" - assert str(LiteralBooleanVar.create(False) < 5) == "(Number(false) < 5)" + assert ( + str(LiteralNumberVar.create(5) > False) + == "(((_lhs, _rhs) => (_lhs > _rhs))(5, 0))" + ) + assert ( + str(LiteralBooleanVar.create(False) < 5) + == "(((_lhs, _rhs) => (_lhs < _rhs))((Number(false)), 5))" + ) assert ( str(LiteralBooleanVar.create(False) < LiteralBooleanVar.create(True)) - == "(Number(false) < Number(true))" + == "(((_lhs, _rhs) => (_lhs < _rhs))((Number(false)), (Number(true))))" ) @@ -1020,10 +1179,10 @@ def test_all_number_operations(): [ (Var.create(False), "false"), (Var.create(True), "true"), - (Var.create("false"), 'isTrue("false")'), - (Var.create([1, 2, 3]), "isTrue([1, 2, 3])"), - (Var.create({"a": 1, "b": 2}), 'isTrue(({ ["a"] : 1, ["b"] : 2 }))'), - (Var("mysterious_var"), "isTrue(mysterious_var)"), + (Var.create("false"), '(isTrue("false"))'), + (Var.create([1, 2, 3]), "(isTrue([1, 2, 3]))"), + (Var.create({"a": 1, "b": 2}), '(isTrue(({ ["a"] : 1, ["b"] : 2 })))'), + (Var("mysterious_var"), "(isTrue(mysterious_var))"), ], ) def test_boolify_operations(var, expected): @@ -1032,18 +1191,30 @@ def test_boolify_operations(var, expected): def test_index_operation(): array_var = LiteralArrayVar.create([1, 2, 3, 4, 5]) - assert str(array_var[0]) == "[1, 2, 3, 4, 5].at(0)" - assert str(array_var[1:2]) == "[1, 2, 3, 4, 5].slice(1, 2)" + assert ( + str(array_var[0]) + == "(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3, 4, 5], ...args)))(0))" + ) + assert ( + str(array_var[1:2]) + == "(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3, 4, 5], ...args)))([1, 2, null]))" + ) assert ( str(array_var[1:4:2]) - == "[1, 2, 3, 4, 5].slice(1, 4).filter((_, i) => i % 2 === 0)" + == "(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3, 4, 5], ...args)))([1, 4, 2]))" ) assert ( str(array_var[::-1]) - == "[1, 2, 3, 4, 5].slice(0, [1, 2, 3, 4, 5].length).slice().reverse().slice(undefined, undefined).filter((_, i) => i % 1 === 0)" + == "(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3, 4, 5], ...args)))([null, null, -1]))" + ) + assert ( + str(array_var.reverse()) + == "(((...args) => (((_array) => _array.slice().reverse())([1, 2, 3, 4, 5], ...args)))())" + ) + assert ( + str(array_var[0].to(NumberVar) + 9) + == "(((_lhs, _rhs) => (_lhs + _rhs))((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3, 4, 5], ...args)))(0)), 9))" ) - assert str(array_var.reverse()) == "[1, 2, 3, 4, 5].slice().reverse()" - assert str(array_var[0].to(NumberVar) + 9) == "([1, 2, 3, 4, 5].at(0) + 9)" @pytest.mark.parametrize( @@ -1065,24 +1236,33 @@ def test_inf_and_nan(var, expected_js): def test_array_operations(): array_var = LiteralArrayVar.create([1, 2, 3, 4, 5]) - assert str(array_var.length()) == "[1, 2, 3, 4, 5].length" - assert str(array_var.contains(3)) == "[1, 2, 3, 4, 5].includes(3)" - assert str(array_var.reverse()) == "[1, 2, 3, 4, 5].slice().reverse()" + assert ( + str(array_var.length()) + == "(((...args) => (((_array) => _array.length)([1, 2, 3, 4, 5], ...args)))())" + ) + assert ( + str(array_var.contains(3)) + == '(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))([1, 2, 3, 4, 5], ...args)))(3))' + ) + assert ( + str(array_var.reverse()) + == "(((...args) => (((_array) => _array.slice().reverse())([1, 2, 3, 4, 5], ...args)))())" + ) assert ( str(ArrayVar.range(10)) - == "Array.from({ length: (10 - 0) / 1 }, (_, i) => 0 + i * 1)" + == "(((_e1, _e2 = null, _step = 1) => range(_e1, _e2, _step))(10))" ) assert ( str(ArrayVar.range(1, 10)) - == "Array.from({ length: (10 - 1) / 1 }, (_, i) => 1 + i * 1)" + == "(((_e1, _e2 = null, _step = 1) => range(_e1, _e2, _step))(1, 10))" ) assert ( str(ArrayVar.range(1, 10, 2)) - == "Array.from({ length: (10 - 1) / 2 }, (_, i) => 1 + i * 2)" + == "(((_e1, _e2 = null, _step = 1) => range(_e1, _e2, _step))(1, 10, 2))" ) assert ( str(ArrayVar.range(1, 10, -1)) - == "Array.from({ length: (10 - 1) / -1 }, (_, i) => 1 + i * -1)" + == "(((_e1, _e2 = null, _step = 1) => range(_e1, _e2, _step))(1, 10, -1))" ) @@ -1090,21 +1270,21 @@ def test_object_operations(): object_var = LiteralObjectVar.create({"a": 1, "b": 2, "c": 3}) assert ( - str(object_var.keys()) == 'Object.keys(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))' + str(object_var.keys()) == '(Object.keys(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })))' ) assert ( str(object_var.values()) - == 'Object.values(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))' + == '(Object.values(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })))' ) assert ( str(object_var.entries()) - == 'Object.entries(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))' + == '(Object.entries(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })))' ) assert str(object_var.a) == '({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["a"]' assert str(object_var["a"]) == '({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["a"]' assert ( str(object_var.merge(LiteralObjectVar.create({"c": 4, "d": 5}))) - == '({...({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }), ...({ ["c"] : 4, ["d"] : 5 })})' + == '(((_lhs, _rhs) => ({..._lhs, ..._rhs}))(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }), ({ ["c"] : 4, ["d"] : 5 })))' ) @@ -1140,23 +1320,27 @@ def test_type_chains(): ) assert ( str(object_var.keys()[0].upper()) # type: ignore - == 'Object.keys(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })).at(0).toUpperCase()' + == '(((...args) => (((_string) => String.prototype.toUpperCase.apply(_string))((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((Object.keys(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))), ...args)))(0)), ...args)))())' ) assert ( str(object_var.entries()[1][1] - 1) # type: ignore - == '(Object.entries(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })).at(1).at(1) - 1)' + == '(((_lhs, _rhs) => (_lhs - _rhs))((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((Object.entries(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))), ...args)))(1)), ...args)))(1)), 1))' ) assert ( str(object_var["c"] + object_var["b"]) # type: ignore - == '(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["c"] + ({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["b"])' + == '(((_lhs, _rhs) => (_lhs + _rhs))(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["c"], ({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["b"]))' ) def test_nested_dict(): - arr = LiteralArrayVar.create([{"bar": ["foo", "bar"]}], List[Dict[str, List[str]]]) + arr = Var.create([{"bar": ["foo", "bar"]}]).to(List[Dict[str, List[str]]]) + first_dict = arr.at(0) + bar_element = first_dict["bar"] + first_bar_element = bar_element[0] assert ( - str(arr[0]["bar"][0]) == '[({ ["bar"] : ["foo", "bar"] })].at(0)["bar"].at(0)' + str(first_bar_element) + == '(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((((...args) => (((_array, _index) => _array.at(_index))([({ ["bar"] : ["foo", "bar"] })], ...args)))(0))["bar"], ...args)))(0))' ) @@ -1376,7 +1560,7 @@ def test_unsupported_types_for_string_contains(other): assert Var(_js_expr="var").to(str).contains(other) assert ( err.value.args[0] - == f"Unsupported Operand type(s) for contains: ToStringOperation, {type(other).__name__}" + == f"Invalid argument other provided to argument 0 in var operation. Expected but got {other._var_type}." ) @@ -1608,17 +1792,12 @@ def test_valid_var_operations(operand1_var: Var, operand2_var, operators: List[s LiteralVar.create([10, 20]), LiteralVar.create("5"), [ - "+", "-", "/", "//", "*", "%", "**", - ">", - "<", - "<=", - ">=", "^", "<<", ">>", From 990bf131c6f929a94ad06fca4fd019651867f74b Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 15 Jan 2025 17:04:36 -0800 Subject: [PATCH 34/86] fix that test --- tests/units/test_var.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/units/test_var.py b/tests/units/test_var.py index 0aa0d2f377f..a09df108887 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -1157,7 +1157,7 @@ def test_all_number_operations(): assert ( str(even_more_complicated_number) - == "(((_value) => !(_value))((((_lhs, _rhs) => (_lhs !== _rhs))((((_a, _b) => (_a || _b))((Math.abs((Math.floor((((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2)))))), (((_a, _b) => (_a && _b))(2, (((_value) => Math.round(_value))((((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2)))))))), 0))))" + == "(((_value) => !(_value))((isTrue((((_a, _b) => (_a || _b))((Math.abs((Math.floor((((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2)))))), (((_a, _b) => (_a && _b))(2, (((_value) => Math.round(_value))((((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2))))))))))))" ) assert ( From 713f907bf045e0e29808bdd397a061bbf9bab623 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 15 Jan 2025 17:08:33 -0800 Subject: [PATCH 35/86] silly me --- reflex/vars/function.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index b62a1e1bea0..b84b69fe08b 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -335,7 +335,9 @@ def call(self, *args: Var | Any) -> Var: # pyright: ignore [reportInconsistentO ): return VarOperationCall.create( FunctionStringVar.create( - self._raw_js_function, _var_type=self._var_type + self._raw_js_function, + _var_type=self._var_type, + _var_data=self._get_all_var_data(), ), *args, _var_type=return_type, From 1aa728ee4c41bb205144f9b0f03bcd59085dc778 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 15 Jan 2025 17:28:46 -0800 Subject: [PATCH 36/86] use infallible guy --- reflex/components/core/cond.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index ccb13c17b1a..98ce605d6d1 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -10,7 +10,7 @@ from reflex.constants import Dirs from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode from reflex.utils.imports import ImportDict, ImportVar -from reflex.utils.types import safe_issubclass +from reflex.utils.types import infallible_issubclass from reflex.vars import VarData from reflex.vars.base import LiteralVar, ReflexCallable, Var from reflex.vars.function import ArgsFunctionOperation @@ -143,7 +143,7 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var: # If the first component is a component, create a Fragment if the second component is not set. if isinstance(c1, BaseComponent) or ( - isinstance(c1, Var) and safe_issubclass(c1._var_type, BaseComponent) + isinstance(c1, Var) and infallible_issubclass(c1._var_type, BaseComponent) ): c2 = c2 if c2 is not None else Fragment.create() From 19b6fe9efc50bf1be95c40f546556289523ba75a Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 15 Jan 2025 18:02:44 -0800 Subject: [PATCH 37/86] handle component at largest scope --- reflex/compiler/compiler.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index 3fe6a1029eb..8944064318c 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -570,6 +570,9 @@ def compile_unevaluated_page( if isinstance(component, tuple): component = Fragment.create(*component) + if isinstance(component, Var): + component = Fragment.create(component) + component._add_style_recursive(style or {}, theme) enable_state = False From d0208e678c2d80c81da1b677e4aaa7de970c5ec4 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 15 Jan 2025 19:16:36 -0800 Subject: [PATCH 38/86] change range a bit --- reflex/.templates/web/utils/helpers/range.js | 60 ++++++++++---------- reflex/vars/sequence.py | 14 ++--- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/reflex/.templates/web/utils/helpers/range.js b/reflex/.templates/web/utils/helpers/range.js index 7d1aedaafb0..8ff97fc6746 100644 --- a/reflex/.templates/web/utils/helpers/range.js +++ b/reflex/.templates/web/utils/helpers/range.js @@ -1,43 +1,43 @@ /** * Simulate the python range() builtin function. * inspired by https://dev.to/guyariely/using-python-range-in-javascript-337p - * + * * If needed outside of an iterator context, use `Array.from(range(10))` or * spread syntax `[...range(10)]` to get an array. - * + * * @param {number} start: the start or end of the range. * @param {number} stop: the end of the range. * @param {number} step: the step of the range. * @returns {object} an object with a Symbol.iterator method over the range */ export default function range(start, stop, step) { - return { - [Symbol.iterator]() { - if (stop === undefined) { - stop = start; - start = 0; - } - if (step === undefined) { - step = 1; - } - - let i = start - step; - - return { - next() { - i += step; - if ((step > 0 && i < stop) || (step < 0 && i > stop)) { - return { - value: i, - done: false, - }; - } + return { + [Symbol.iterator]() { + if ((stop ?? undefined) === undefined) { + stop = start; + start = 0; + } + if ((step ?? undefined) === undefined) { + step = 1; + } + + let i = start - step; + + return { + next() { + i += step; + if ((step > 0 && i < stop) || (step < 0 && i > stop)) { return { - value: undefined, - done: true, + value: i, + done: false, }; - }, - }; - }, - }; - } \ No newline at end of file + } + return { + value: undefined, + done: true, + }; + }, + }; + }, + }; +} diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 997a5a00e60..6b94e07bd97 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -189,20 +189,18 @@ def string_strip_operation(string: Var[str]): def string_contains_field_operation( haystack: Var[str], needle: Var[str], - field: VarWithDefault[str] = VarWithDefault(""), ): """Check if a string contains another string. Args: haystack: The haystack. needle: The needle. - field: The field to check. Returns: The string contains operation. """ return var_operation_return( - js_expression=f"isTrue({field}) ? {haystack}.some(obj => obj[{field}] === {needle}) : {haystack}.some(obj => obj === {needle})", + js_expression=f"{haystack}.includes({needle})", var_type=bool, var_data=VarData( imports=_IS_TRUE_IMPORT, @@ -360,7 +358,7 @@ def array_pluck_operation( The reversed array. """ return var_operation_return( - js_expression=f"{array}.map(e=>e?.[{field}])", + js_expression=f"Array.prototype.map.apply({array}, [e=>e?.[{field}]])", var_type=List[Any], ) @@ -378,7 +376,9 @@ def array_join_operation( Returns: The joined elements. """ - return var_operation_return(js_expression=f"{array}.join({sep})", var_type=str) + return var_operation_return( + js_expression=f"Array.prototype.join.apply({array},[{sep}])", var_type=str + ) @var_operation @@ -631,7 +631,7 @@ def array_range_operation( The range of numbers. """ return var_operation_return( - js_expression=f"range({e1}, {e2}, {step})", + js_expression=f"[...range({e1}, {e2}, {step})]", var_type=List[int], var_data=VarData( imports=_RANGE_IMPORT, @@ -769,7 +769,7 @@ def type_computer(*args: Var): return (ReflexCallable[[], List[args[0]._var_type]], None) return var_operation_return( - js_expression=f"{array}.map({function})", + js_expression=f"Array.prototype.map.apply({array}, [{function}])", type_computer=nary_type_computer( ReflexCallable[[List[Any], ReflexCallable], List[Any]], ReflexCallable[[ReflexCallable], List[Any]], From 076cfea6aef864035609ad40163255fccb67b5e8 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 16 Jan 2025 13:48:42 -0800 Subject: [PATCH 39/86] fix and and or --- reflex/utils/types.py | 10 ++++- reflex/vars/base.py | 86 +++++++++++++++++++++++++++++------------ reflex/vars/function.py | 2 + 3 files changed, 72 insertions(+), 26 deletions(-) diff --git a/reflex/utils/types.py b/reflex/utils/types.py index 6503be8be03..d968b9a0982 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -860,6 +860,10 @@ def infallible_issubclass(cls: Any, class_or_tuple: Any, /) -> bool: Returns: Whether the class is a subclass of the other class or tuple of classes. """ + if cls is class_or_tuple or ( + isinstance(class_or_tuple, tuple) and cls in class_or_tuple + ): + return True try: return issubclass(cls, class_or_tuple) except TypeError: @@ -876,6 +880,8 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo Returns: Whether the type hint is a subclass of the other type hint. """ + if possible_subclass is possible_superclass: + return True if possible_superclass is Any: return True if possible_subclass is Any: @@ -956,7 +962,7 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo return True # Check if the origin of both types is the same (e.g., list for List[int]) - if not safe_issubclass( + if not infallible_issubclass( provided_type_origin or possible_subclass, accepted_type_origin or possible_superclass, ): @@ -968,5 +974,5 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo return all( typehint_issubclass(provided_arg, accepted_arg) for provided_arg, accepted_arg in zip(provided_args, accepted_args) - if accepted_arg is not Any + if accepted_arg is not Any and not isinstance(accepted_arg, TypeVar) ) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index cd42b34d6fb..ba54b100d7f 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -3261,11 +3261,46 @@ def and_operation(a: Var | Any, b: Var | Any) -> Var: Returns: The result of the logical AND operation. """ - return _and_operation(a, b) # type: ignore + from .function import ArgsFunctionOperation + + a = Var.create(a) + b = Var.create(b) + + return _or_func_operation( + ArgsFunctionOperation.create((), a, _var_type=ReflexCallable[[], a._var_type]), + ArgsFunctionOperation.create((), b, _var_type=ReflexCallable[[], b._var_type]), + ) + + +def or_operation(a: Var | Any, b: Var | Any) -> Var: + """Perform a logical OR operation on two variables. + + Args: + a: The first variable. + b: The second variable. + + Returns: + The result of the logical OR operation. + """ + from .function import ArgsFunctionOperation + + a = Var.create(a) + b = Var.create(b) + + return _or_func_operation( + ArgsFunctionOperation.create((), a, _var_type=ReflexCallable[[], a._var_type]), + ArgsFunctionOperation.create((), b, _var_type=ReflexCallable[[], b._var_type]), + ) + + +T_LOGICAL = TypeVar("T_LOGICAL") +U_LOGICAL = TypeVar("U_LOGICAL") @var_operation -def _and_operation(a: Var, b: Var): +def _and_func_operation( + a: Var[ReflexCallable[[], T_LOGICAL]], b: Var[ReflexCallable[[], U_LOGICAL]] +) -> CustomVarOperationReturn[ReflexCallable[[], Union[T_LOGICAL, U_LOGICAL]]]: """Perform a logical AND operation on two variables. Args: @@ -3278,38 +3313,34 @@ def _and_operation(a: Var, b: Var): def type_computer(*args: Var): if not args: - return (ReflexCallable[[Any, Any], Any], type_computer) + return ( + ReflexCallable[[ReflexCallable[[], Any], ReflexCallable[[], Any]], Any], + type_computer, + ) if len(args) == 1: return ( - ReflexCallable[[Any], Any], + ReflexCallable[[ReflexCallable[[], Any]], Any], functools.partial(type_computer, args[0]), ) + + a_return_type = unwrap_reflex_callalbe(args[0]._var_type)[1] + b_return_type = unwrap_reflex_callalbe(args[1]._var_type)[1] + return ( - ReflexCallable[[], unionize(args[0]._var_type, args[1]._var_type)], + ReflexCallable[[], unionize(a_return_type, b_return_type)], None, ) return var_operation_return( - js_expression=f"({a} && {b})", + js_expression=f"({a}() && {b}())", type_computer=type_computer, ) -def or_operation(a: Var | Any, b: Var | Any) -> Var: - """Perform a logical OR operation on two variables. - - Args: - a: The first variable. - b: The second variable. - - Returns: - The result of the logical OR operation. - """ - return _or_operation(a, b) # type: ignore - - @var_operation -def _or_operation(a: Var, b: Var): +def _or_func_operation( + a: Var[ReflexCallable[[], T_LOGICAL]], b: Var[ReflexCallable[[], U_LOGICAL]] +) -> CustomVarOperationReturn[ReflexCallable[[], Union[T_LOGICAL, U_LOGICAL]]]: """Perform a logical OR operation on two variables. Args: @@ -3322,19 +3353,26 @@ def _or_operation(a: Var, b: Var): def type_computer(*args: Var): if not args: - return (ReflexCallable[[Any, Any], Any], type_computer) + return ( + ReflexCallable[[ReflexCallable[[], Any], ReflexCallable[[], Any]], Any], + type_computer, + ) if len(args) == 1: return ( - ReflexCallable[[Any], Any], + ReflexCallable[[ReflexCallable[[], Any]], Any], functools.partial(type_computer, args[0]), ) + + a_return_type = unwrap_reflex_callalbe(args[0]._var_type)[1] + b_return_type = unwrap_reflex_callalbe(args[1]._var_type)[1] + return ( - ReflexCallable[[], unionize(args[0]._var_type, args[1]._var_type)], + ReflexCallable[[], unionize(a_return_type, b_return_type)], None, ) return var_operation_return( - js_expression=f"({a} || {b})", + js_expression=f"({a}() || {b}())", type_computer=type_computer, ) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index b84b69fe08b..f529df00877 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -751,6 +751,7 @@ def create( Returns: The function var. """ + return_expr = Var.create(return_expr) return cls( _js_expr="", _var_type=_var_type, @@ -829,6 +830,7 @@ def create( Returns: The function var. """ + return_expr = Var.create(return_expr) return cls( _js_expr="", _var_type=_var_type, From a488fe0c49e15aac3fc3182bc0f51b5edade4793 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 16 Jan 2025 14:05:38 -0800 Subject: [PATCH 40/86] i missed up that --- reflex/vars/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index ba54b100d7f..4feca42a9d0 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -3266,7 +3266,7 @@ def and_operation(a: Var | Any, b: Var | Any) -> Var: a = Var.create(a) b = Var.create(b) - return _or_func_operation( + return _and_func_operation( ArgsFunctionOperation.create((), a, _var_type=ReflexCallable[[], a._var_type]), ArgsFunctionOperation.create((), b, _var_type=ReflexCallable[[], b._var_type]), ) From f42d1f4b0fb5dc2484444e221588a918f7eba838 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 16 Jan 2025 14:34:35 -0800 Subject: [PATCH 41/86] fix component state --- reflex/compiler/compiler.py | 13 ++++--------- reflex/state.py | 9 +++++++++ reflex/vars/function.py | 2 -- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index 8944064318c..c2f5662f467 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -560,18 +560,13 @@ def compile_unevaluated_page( """ # Generate the component if it is a callable. component = page.component - component = ( - component - if isinstance(component, Component) - else (Fragment.create(component) if isinstance(component, Var) else component()) - ) - - # unpack components that return tuples in an rx.fragment. - if isinstance(component, tuple): - component = Fragment.create(*component) if isinstance(component, Var): component = Fragment.create(component) + elif isinstance(component, tuple): + component = Fragment.create(*component) + elif not isinstance(component, Component): + component = component() component._add_style_recursive(style or {}, theme) diff --git a/reflex/state.py b/reflex/state.py index 16875bec3e9..40b6dabf552 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -2554,6 +2554,9 @@ def create(cls, *children, **props) -> "Component": Returns: A new instance of the Component with an independent copy of the State. """ + from reflex.components import Component + from reflex.components.base.fragment import Fragment + cls._per_component_state_instance_count += 1 state_cls_name = f"{cls.__name__}_n{cls._per_component_state_instance_count}" component_state = type( @@ -2565,6 +2568,12 @@ def create(cls, *children, **props) -> "Component": # Save a reference to the dynamic state for pickle/unpickle. setattr(reflex.istate.dynamic, state_cls_name, component_state) component = component_state.get_component(*children, **props) + if isinstance(component, Var): + component = Fragment.create(component) + elif isinstance(component, tuple): + component = Fragment.create(*component) + elif not isinstance(component, Component): + component = component() component.State = component_state return component diff --git a/reflex/vars/function.py b/reflex/vars/function.py index f529df00877..c3fe8f75f23 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -722,7 +722,6 @@ def create( cls, args_names: Sequence[Union[str, DestructuredArg]], return_expr: Var | Any, - /, default_values: Sequence[VarWithDefault | inspect.Parameter.empty] = (), rest: str | None = None, validators: Sequence[Callable[[Any], Optional[str]]] = (), @@ -801,7 +800,6 @@ def create( cls, args_names: Sequence[Union[str, DestructuredArg]], return_expr: Var | Any, - /, default_values: Sequence[VarWithDefault | inspect.Parameter.empty] = (), rest: str | None = None, validators: Sequence[Callable[[Any], Optional[str]]] = (), From 4300f338d8b4814291602bfdf8a93bffbce08010 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 16 Jan 2025 14:50:37 -0800 Subject: [PATCH 42/86] add wrap components override --- reflex/components/base/bare.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index b6d2befd5a8..6690a5cc105 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -102,6 +102,21 @@ def _get_all_custom_code(self) -> set[str]: custom_code |= component._get_all_custom_code() return custom_code + def _get_all_app_wrap_components(self) -> dict[tuple[int, str], Component]: + """Get the components that should be wrapped in the app. + + Returns: + The components that should be wrapped in the app. + """ + app_wrap_components = super()._get_all_app_wrap_components() + if isinstance(self.contents, Var): + var_data = self.contents._get_all_var_data() + if var_data: + for component in var_data.components: + if isinstance(component, Component): + app_wrap_components |= component._get_all_app_wrap_components() + return app_wrap_components + def _get_all_refs(self) -> set[str]: """Get the refs for the children of the component. From 94c9e524741b6257cc38ffe507c95e596cc398a7 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 16 Jan 2025 15:16:48 -0800 Subject: [PATCH 43/86] fix convert to component logic --- reflex/compiler/compiler.py | 34 +++++++++++++++++++++++++--------- reflex/state.py | 10 ++-------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index c2f5662f467..54fb8b2f63a 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -4,7 +4,7 @@ from datetime import datetime from pathlib import Path -from typing import TYPE_CHECKING, Dict, Iterable, Optional, Tuple, Type, Union +from typing import TYPE_CHECKING, Callable, Dict, Iterable, Optional, Tuple, Type, Union from reflex import constants from reflex.compiler import templates, utils @@ -538,6 +538,29 @@ def purge_web_pages_dir(): if TYPE_CHECKING: from reflex.app import UnevaluatedPage + COMPONENT_TYPE = Union[Component, Var, Tuple[Union[Component, Var], ...]] + COMPONENT_TYPE_OR_CALLABLE = Union[COMPONENT_TYPE, Callable[[], COMPONENT_TYPE]] + + +def componentify_unevaluated( + possible_component: COMPONENT_TYPE_OR_CALLABLE, +) -> Component: + """Convert a possible component to a component. + + Args: + possible_component: The possible component to convert. + + Returns: + The component. + """ + if isinstance(possible_component, Var): + return Fragment.create(possible_component) + if isinstance(possible_component, tuple): + return Fragment.create(*possible_component) + if isinstance(possible_component, Component): + return possible_component + return componentify_unevaluated(possible_component()) + def compile_unevaluated_page( route: str, @@ -559,14 +582,7 @@ def compile_unevaluated_page( The compiled component and whether state should be enabled. """ # Generate the component if it is a callable. - component = page.component - - if isinstance(component, Var): - component = Fragment.create(component) - elif isinstance(component, tuple): - component = Fragment.create(*component) - elif not isinstance(component, Component): - component = component() + component = componentify_unevaluated(page.component) component._add_style_recursive(style or {}, theme) diff --git a/reflex/state.py b/reflex/state.py index 40b6dabf552..f9931f92002 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -2554,8 +2554,7 @@ def create(cls, *children, **props) -> "Component": Returns: A new instance of the Component with an independent copy of the State. """ - from reflex.components import Component - from reflex.components.base.fragment import Fragment + from reflex.compiler.compiler import componentify_unevaluated cls._per_component_state_instance_count += 1 state_cls_name = f"{cls.__name__}_n{cls._per_component_state_instance_count}" @@ -2568,12 +2567,7 @@ def create(cls, *children, **props) -> "Component": # Save a reference to the dynamic state for pickle/unpickle. setattr(reflex.istate.dynamic, state_cls_name, component_state) component = component_state.get_component(*children, **props) - if isinstance(component, Var): - component = Fragment.create(component) - elif isinstance(component, tuple): - component = Fragment.create(*component) - elif not isinstance(component, Component): - component = component() + component = componentify_unevaluated(component) component.State = component_state return component From f9d45d556264e4d4f38fc8954b1acc3bfde4b475 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 16 Jan 2025 16:02:41 -0800 Subject: [PATCH 44/86] fix tests for cond and var --- tests/units/components/core/test_cond.py | 74 ++++++++++++------------ tests/units/test_var.py | 36 ++++++------ 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/tests/units/components/core/test_cond.py b/tests/units/components/core/test_cond.py index e88f35c9a7d..71e19c7cc47 100644 --- a/tests/units/components/core/test_cond.py +++ b/tests/units/components/core/test_cond.py @@ -3,8 +3,7 @@ import pytest -from reflex.components.base.fragment import Fragment -from reflex.components.core.cond import Cond, cond +from reflex.components.core.cond import cond from reflex.components.radix.themes.typography.text import Text from reflex.state import BaseState from reflex.utils.format import format_state_name @@ -22,7 +21,10 @@ class CondState(BaseState): def test_f_string_cond_interpolation(): # make sure backticks inside interpolation don't get escaped var = LiteralVar.create(f"x {cond(True, 'a', 'b')}") - assert str(var) == '("x "+(true ? "a" : "b"))' + assert ( + str(var) + == '("x "+((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => "a"), (() => "b")))()))' + ) @pytest.mark.parametrize( @@ -40,32 +42,23 @@ def test_validate_cond(cond_state: BaseState): Args: cond_state: A fixture. """ - cond_component = cond( + first_component = Text.create("cond is True") + second_component = Text.create("cond is False") + cond_var = cond( cond_state.value, - Text.create("cond is True"), - Text.create("cond is False"), + first_component, + second_component, ) - cond_dict = cond_component.render() if type(cond_component) is Fragment else {} - assert cond_dict["name"] == "Fragment" - - [condition] = cond_dict["children"] - assert condition["cond_state"] == f"isTrue({cond_state.get_full_name()}.value)" - - # true value - true_value = condition["true_value"] - assert true_value["name"] == "Fragment" - [true_value_text] = true_value["children"] - assert true_value_text["name"] == "RadixThemesText" - assert true_value_text["children"][0]["contents"] == '{"cond is True"}' - - # false value - false_value = condition["false_value"] - assert false_value["name"] == "Fragment" + assert isinstance(cond_var, Var) + assert ( + str(cond_var) + == f'((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))({cond_state.value.bool()!s}, (() => (jsx(RadixThemesText, ({{ ["as"] : "p" }}), (jsx(Fragment, ({{ }}), "cond is True"))))), (() => (jsx(RadixThemesText, ({{ ["as"] : "p" }}), (jsx(Fragment, ({{ }}), "cond is False")))))))())' + ) - [false_value_text] = false_value["children"] - assert false_value_text["name"] == "RadixThemesText" - assert false_value_text["children"][0]["contents"] == '{"cond is False"}' + var_data = cond_var._get_all_var_data() + assert var_data is not None + assert var_data.components == (first_component, second_component) @pytest.mark.parametrize( @@ -96,25 +89,31 @@ def test_prop_cond(c1: Any, c2: Any): c1 = json.dumps(c1) if not isinstance(c2, Var): c2 = json.dumps(c2) - assert str(prop_cond) == f"(true ? {c1!s} : {c2!s})" + assert ( + str(prop_cond) + == f"((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => {c1!s}), (() => {c2!s})))())" + ) -def test_cond_no_mix(): - """Test if cond can't mix components and props.""" - with pytest.raises(ValueError): - cond(True, LiteralVar.create("hello"), Text.create("world")) +def test_cond_mix(): + """Test if cond can mix components and props.""" + x = cond(True, LiteralVar.create("hello"), Text.create("world")) + assert isinstance(x, Var) + assert ( + str(x) + == '((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => "hello"), (() => (jsx(RadixThemesText, ({ ["as"] : "p" }), (jsx(Fragment, ({ }), "world")))))))())' + ) def test_cond_no_else(): """Test if cond can be used without else.""" # Components should support the use of cond without else comp = cond(True, Text.create("hello")) - assert isinstance(comp, Fragment) - comp = comp.children[0] - assert isinstance(comp, Cond) - assert comp.cond._decode() is True # type: ignore - assert comp.comp1.render() == Fragment.create(Text.create("hello")).render() - assert comp.comp2 == Fragment.create() + assert isinstance(comp, Var) + assert ( + str(comp) + == '((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => (jsx(RadixThemesText, ({ ["as"] : "p" }), (jsx(Fragment, ({ }), "hello"))))), (() => (jsx(Fragment, ({ }))))))())' + ) # Props do not support the use of cond without else with pytest.raises(ValueError): @@ -140,7 +139,8 @@ def computed_str(self) -> str: state_name = format_state_name(CondStateComputed.get_full_name()) assert ( - str(comp) == f"(true ? {state_name}.computed_int : {state_name}.computed_str)" + str(comp) + == f"((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => {state_name}.computed_int), (() => {state_name}.computed_str)))())" ) assert comp._var_type == Union[int, str] diff --git a/tests/units/test_var.py b/tests/units/test_var.py index a09df108887..48f6eea43c3 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -346,8 +346,14 @@ def test_basic_operations(TestObj): str(LiteralNumberVar.create(1) ** 2) == "(((_lhs, _rhs) => (_lhs ** _rhs))(1, 2))" ) - assert str(LiteralNumberVar.create(1) & v(2)) == "(((_a, _b) => (_a && _b))(1, 2))" - assert str(LiteralNumberVar.create(1) | v(2)) == "(((_a, _b) => (_a || _b))(1, 2))" + assert ( + str(LiteralNumberVar.create(1) & v(2)) + == "(((_a, _b) => (_a() && _b()))((() => 1), (() => 2)))" + ) + assert ( + str(LiteralNumberVar.create(1) | v(2)) + == "(((_a, _b) => (_a() || _b()))((() => 1), (() => 2)))" + ) assert ( str(LiteralArrayVar.create([1, 2, 3])[0]) == "(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3], ...args)))(0))" @@ -507,25 +513,21 @@ def test_var_types(var, var_type): def test_str_contains(var, expected): assert ( str(var.contains("1")) - == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))("1"))' + == f'(((...args) => (((_haystack, _needle) => _haystack.includes(_needle))({expected!s}, ...args)))("1"))' ) assert ( str(var.contains(v("1"))) - == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))("1"))' + == f'(((...args) => (((_haystack, _needle) => _haystack.includes(_needle))({expected!s}, ...args)))("1"))' ) other_state_var = Var(_js_expr="other")._var_set_state("state").to(str) other_var = Var(_js_expr="other").to(str) assert ( str(var.contains(other_state_var)) - == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))(state.other))' + == f"(((...args) => (((_haystack, _needle) => _haystack.includes(_needle))({expected!s}, ...args)))(state.other))" ) assert ( str(var.contains(other_var)) - == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))(other))' - ) - assert ( - str(var.contains("1", "hello")) - == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))("1", "hello"))' + == f"(((...args) => (((_haystack, _needle) => _haystack.includes(_needle))({expected!s}, ...args)))(other))" ) @@ -1133,11 +1135,11 @@ def test_string_operations(): ) assert ( str(basic_string.contains("World")) - == '(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))("Hello, World!", ...args)))("World"))' + == '(((...args) => (((_haystack, _needle) => _haystack.includes(_needle))("Hello, World!", ...args)))("World"))' ) assert ( str(basic_string.split(" ").join(",")) - == '(((...args) => (((_array, _sep = "") => _array.join(_sep))((((...args) => (((_string, _sep = "") => isTrue(_sep) ? _string.split(_sep) : [..._string])("Hello, World!", ...args)))(" ")), ...args)))(","))' + == '(((...args) => (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))((((...args) => (((_string, _sep = "") => isTrue(_sep) ? _string.split(_sep) : [..._string])("Hello, World!", ...args)))(" ")), ...args)))(","))' ) @@ -1157,7 +1159,7 @@ def test_all_number_operations(): assert ( str(even_more_complicated_number) - == "(((_value) => !(_value))((isTrue((((_a, _b) => (_a || _b))((Math.abs((Math.floor((((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2)))))), (((_a, _b) => (_a && _b))(2, (((_value) => Math.round(_value))((((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2))))))))))))" + == "(((_value) => !(_value))((isTrue((((_a, _b) => (_a() || _b()))((() => (Math.abs((Math.floor((((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2))))))), (() => (((_a, _b) => (_a() && _b()))((() => 2), (() => (((_value) => Math.round(_value))((((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2))))))))))))))" ) assert ( @@ -1250,19 +1252,19 @@ def test_array_operations(): ) assert ( str(ArrayVar.range(10)) - == "(((_e1, _e2 = null, _step = 1) => range(_e1, _e2, _step))(10))" + == "(((_e1, _e2 = null, _step = 1) => [...range(_e1, _e2, _step)])(10))" ) assert ( str(ArrayVar.range(1, 10)) - == "(((_e1, _e2 = null, _step = 1) => range(_e1, _e2, _step))(1, 10))" + == "(((_e1, _e2 = null, _step = 1) => [...range(_e1, _e2, _step)])(1, 10))" ) assert ( str(ArrayVar.range(1, 10, 2)) - == "(((_e1, _e2 = null, _step = 1) => range(_e1, _e2, _step))(1, 10, 2))" + == "(((_e1, _e2 = null, _step = 1) => [...range(_e1, _e2, _step)])(1, 10, 2))" ) assert ( str(ArrayVar.range(1, 10, -1)) - == "(((_e1, _e2 = null, _step = 1) => range(_e1, _e2, _step))(1, 10, -1))" + == "(((_e1, _e2 = null, _step = 1) => [...range(_e1, _e2, _step)])(1, 10, -1))" ) From 94b4443afca66cc8200336ae1fc4c19e1e0f2996 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 16 Jan 2025 18:11:48 -0800 Subject: [PATCH 45/86] what if we simply didn't have match --- reflex/components/core/banner.py | 8 +- reflex/components/core/match.py | 163 +++--------------- reflex/vars/number.py | 126 +++++++++++++- tests/units/components/core/test_colors.py | 6 +- tests/units/components/core/test_foreach.py | 2 +- tests/units/components/core/test_match.py | 84 +-------- .../components/markdown/test_markdown.py | 8 +- 7 files changed, 172 insertions(+), 225 deletions(-) diff --git a/reflex/components/core/banner.py b/reflex/components/core/banner.py index e25951fb09b..aa75bb2c7ed 100644 --- a/reflex/components/core/banner.py +++ b/reflex/components/core/banner.py @@ -175,7 +175,7 @@ def create(cls, comp: Optional[Component] = None) -> Component: Returns: The connection banner component. """ - from reflex.components.base.bare import Bare + from reflex.components.base.fragment import Fragment if not comp: comp = Flex.create( @@ -191,7 +191,7 @@ def create(cls, comp: Optional[Component] = None) -> Component: position="fixed", ) - return Bare.create(cond(has_connection_errors, comp)) + return Fragment.create(cond(has_connection_errors, comp)) class ConnectionModal(Component): @@ -207,11 +207,11 @@ def create(cls, comp: Optional[Component] = None) -> Component: Returns: The connection banner component. """ - from reflex.components.base.bare import Bare + from reflex.components.base.fragment import Fragment if not comp: comp = Text.create(*default_connection_error()) - return Bare.create( + return Fragment.create( cond( has_too_many_connection_errors, DialogRoot.create( diff --git a/reflex/components/core/match.py b/reflex/components/core/match.py index 3c1e27004d9..2def33d6552 100644 --- a/reflex/components/core/match.py +++ b/reflex/components/core/match.py @@ -1,17 +1,14 @@ """rx.match.""" import textwrap -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, List, Optional, Sequence, Tuple, Union from reflex.components.base import Fragment from reflex.components.component import BaseComponent, Component, MemoizationLeaf -from reflex.components.tags import MatchTag, Tag -from reflex.style import Style -from reflex.utils import format, types +from reflex.utils import types from reflex.utils.exceptions import MatchTypeError -from reflex.utils.imports import ImportDict -from reflex.vars import VarData -from reflex.vars.base import LiteralVar, Var +from reflex.vars.base import Var +from reflex.vars.number import MatchOperation class Match(MemoizationLeaf): @@ -40,43 +37,32 @@ def create(cls, cond: Any, *cases) -> Union[Component, Var]: Raises: ValueError: When a default case is not provided for cases with Var return types. """ - match_cond_var = cls._create_condition_var(cond) - cases, default = cls._process_cases(list(cases)) - match_cases = cls._process_match_cases(cases) + cases, default = cls._process_cases(cases) + cls._process_match_cases(cases) - cls._validate_return_types(match_cases) + cls._validate_return_types(cases) - if default is None and types._issubclass(type(match_cases[0][-1]), Var): + if default is None and any( + not ( + isinstance((return_type := case[-1]), Component) + or ( + isinstance(return_type, Var) + and types.typehint_issubclass(return_type._var_type, Component) + ) + ) + for case in cases + ): raise ValueError( "For cases with return types as Vars, a default case must be provided" ) + elif default is None: + default = Fragment.create() - return cls._create_match_cond_var_or_component( - match_cond_var, match_cases, default - ) - - @classmethod - def _create_condition_var(cls, cond: Any) -> Var: - """Convert the condition to a Var. - - Args: - cond: The condition. - - Returns: - The condition as a base var - - Raises: - ValueError: If the condition is not provided. - """ - match_cond_var = LiteralVar.create(cond) - - if match_cond_var is None: - raise ValueError("The condition must be set") - return match_cond_var + return cls._create_match_cond_var_or_component(cond, cases, default) @classmethod def _process_cases( - cls, cases: List + cls, cases: Sequence ) -> Tuple[List, Optional[Union[Var, BaseComponent]]]: """Process the list of match cases and the catchall default case. @@ -99,33 +85,13 @@ def _process_cases( # Get the default case which should be the last non-tuple arg if not isinstance(cases[-1], tuple): - default = cases.pop() - default = ( - cls._create_case_var_with_var_data(default) - if not isinstance(default, BaseComponent) - else default - ) + default = cases[-1] + cases = cases[:-1] - return cases, default + return list(cases), default @classmethod - def _create_case_var_with_var_data(cls, case_element): - """Convert a case element into a Var.If the case - is a Style type, we extract the var data and merge it with the - newly created Var. - - Args: - case_element: The case element. - - Returns: - The case element Var. - """ - _var_data = case_element._var_data if isinstance(case_element, Style) else None - case_element = LiteralVar.create(case_element, _var_data=_var_data) - return case_element - - @classmethod - def _process_match_cases(cls, cases: List) -> List[List[Var]]: + def _process_match_cases(cls, cases: Sequence): """Process the individual match cases. Args: @@ -137,34 +103,18 @@ def _process_match_cases(cls, cases: List) -> List[List[Var]]: Raises: ValueError: If the default case is not the last case or the tuple elements are less than 2. """ - match_cases = [] for case in cases: if not isinstance(case, tuple): raise ValueError( "rx.match should have tuples of cases and a default case as the last argument." ) + # There should be at least two elements in a case tuple(a condition and return value) if len(case) < 2: raise ValueError( "A case tuple should have at least a match case element and a return value." ) - case_list = [] - for element in case: - # convert all non component element to vars. - el = ( - cls._create_case_var_with_var_data(element) - if not isinstance(element, BaseComponent) - else element - ) - if not isinstance(el, (Var, BaseComponent)): - raise ValueError("Case element must be a var or component") - case_list.append(el) - - match_cases.append(case_list) - - return match_cases - @classmethod def _validate_return_types(cls, match_cases: List[List[Var]]) -> None: """Validate that match cases have the same return types. @@ -202,7 +152,7 @@ def _create_match_cond_var_or_component( cls, match_cond_var: Var, match_cases: List[List[Var]], - default: Optional[Union[Var, BaseComponent]], + default: Union[Var, BaseComponent], ) -> Union[Component, Var]: """Create and return the match condition var or component. @@ -217,64 +167,7 @@ def _create_match_cond_var_or_component( Raises: ValueError: If the return types are not vars when creating a match var for Var types. """ - if default is None and types._issubclass( - type(match_cases[0][-1]), BaseComponent - ): - default = Fragment.create() - - if types._issubclass(type(match_cases[0][-1]), BaseComponent): - return Fragment.create( - cls( - cond=match_cond_var, - match_cases=match_cases, - default=default, - children=[case[-1] for case in match_cases] + [default], # type: ignore - ) - ) - - # Validate the match cases (as well as the default case) to have Var return types. - if any( - case for case in match_cases if not types._isinstance(case[-1], Var) - ) or not types._isinstance(default, Var): - raise ValueError("Return types of match cases should be Vars.") - - return Var( - _js_expr=format.format_match( - cond=str(match_cond_var), - match_cases=match_cases, - default=default, # type: ignore - ), - _var_type=default._var_type, # type: ignore - _var_data=VarData.merge( - match_cond_var._get_all_var_data(), - *[el._get_all_var_data() for case in match_cases for el in case], - default._get_all_var_data(), # type: ignore - ), - ) - - def _render(self) -> Tag: - return MatchTag( - cond=self.cond, match_cases=self.match_cases, default=self.default - ) - - def render(self) -> Dict: - """Render the component. - - Returns: - The dictionary for template of component. - """ - tag = self._render() - tag.name = "match" - return dict(tag) - - def add_imports(self) -> ImportDict: - """Add imports for the Match component. - - Returns: - The import dict. - """ - var_data = VarData.merge(self.cond._get_all_var_data()) - return var_data.old_school_imports() if var_data else {} + return MatchOperation.create(match_cond_var, match_cases, default) match = Match.create diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 9b2072b9e7f..3090e249759 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -7,18 +7,30 @@ import json import math import sys -from typing import TYPE_CHECKING, Any, Callable, NoReturn, TypeVar, Union, overload +from typing import ( + TYPE_CHECKING, + Any, + Callable, + NoReturn, + Sequence, + TypeVar, + Union, + overload, +) from reflex.constants.base import Dirs from reflex.utils.exceptions import PrimitiveUnserializableToJSON, VarTypeError from reflex.utils.imports import ImportDict, ImportVar from .base import ( + VAR_TYPE, + CachedVarOperation, CustomVarOperationReturn, LiteralVar, ReflexCallable, Var, VarData, + cached_property_no_lock, nary_type_computer, passthrough_unary_type_computer, unionize, @@ -1056,4 +1068,116 @@ def ternary_operation( return value +TUPLE_ENDS_IN_VAR = ( + tuple[Var[VAR_TYPE]] + | tuple[Var, Var[VAR_TYPE]] + | tuple[Var, Var, Var[VAR_TYPE]] + | tuple[Var, Var, Var, Var[VAR_TYPE]] + | tuple[Var, Var, Var, Var, Var[VAR_TYPE]] + | tuple[Var, Var, Var, Var, Var, Var[VAR_TYPE]] + | tuple[Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] + | tuple[Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] + | tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] + | tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] + | tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] + | tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] + | tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] + | tuple[ + Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE] + ] +) + + +@dataclasses.dataclass( + eq=False, + frozen=True, + **{"slots": True} if sys.version_info >= (3, 10) else {}, +) +class MatchOperation(CachedVarOperation, Var[VAR_TYPE]): + """Base class for immutable match operations.""" + + _cond: Var[bool] = dataclasses.field( + default_factory=lambda: LiteralBooleanVar.create(True) + ) + _cases: tuple[TUPLE_ENDS_IN_VAR[VAR_TYPE], ...] = dataclasses.field( + default_factory=tuple + ) + _default: Var[VAR_TYPE] = dataclasses.field( + default_factory=lambda: Var.create(None) + ) + + @cached_property_no_lock + def _cached_var_name(self) -> str: + """Get the name of the var. + + Returns: + The name of the var. + """ + switch_code = f"(() => {{ switch (JSON.stringify({self._cond!s})) {{" + + for case in self._cases: + conditions = case[:-1] + return_value = case[-1] + + case_conditions = " ".join( + [f"case JSON.stringify({condition!s}):" for condition in conditions] + ) + case_code = f"{case_conditions} return ({return_value!s}); break;" + switch_code += case_code + + switch_code += f"default: return ({self._default!s}); break;" + switch_code += "};})()" + + return switch_code + + @cached_property_no_lock + def _cached_get_all_var_data(self) -> VarData | None: + """Get the VarData for the var. + + Returns: + The VarData for the var. + """ + return VarData.merge( + self._cond._get_all_var_data(), + *( + case._get_all_var_data() + for cond_or_return in self._cases + for case in cond_or_return + ), + self._default._get_all_var_data(), + self._var_data, + ) + + @classmethod + def create( + cls, + cond: Any, + cases: Sequence[Sequence[Any | Var[VAR_TYPE]]], + default: Var[VAR_TYPE] | VAR_TYPE, + _var_data: VarData | None = None, + _var_type: type[VAR_TYPE] | None = None, + ): + """Create the match operation. + + Args: + cond: The condition. + cases: The cases. + default: The default case. + _var_data: Additional hooks and imports associated with the Var. + _var_type: The type of the Var. + + Returns: + The match operation. + """ + cases = tuple(tuple(Var.create(c) for c in case) for case in cases) + return cls( + _js_expr="", + _var_data=_var_data, + _var_type=_var_type, + _cond=Var.create(cond), + _cases=cases, + _default=Var.create(default), + ) + + NUMBER_TYPES = (int, float, NumberVar) diff --git a/tests/units/components/core/test_colors.py b/tests/units/components/core/test_colors.py index c1295fb4158..0a01bfcc046 100644 --- a/tests/units/components/core/test_colors.py +++ b/tests/units/components/core/test_colors.py @@ -39,7 +39,7 @@ def create_color_var(color): create_color_var( rx.color(ColorState.color, ColorState.shade, ColorState.alpha) # type: ignore ), - f'("var(--"+{color_state_name!s}.color+"-"+({color_state_name!s}.alpha ? "a" : "")+(((__to_string) => __to_string.toString())({color_state_name!s}.shade))+")")', + f'("var(--"+{color_state_name!s}.color+"-"+(((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))({color_state_name!s}.alpha, "a", ""))+(((__to_string) => __to_string.toString())({color_state_name!s}.shade))+")")', Color, ), ( @@ -78,11 +78,11 @@ def test_color(color, expected, expected_type: Union[Type[str], Type[Color]]): [ ( rx.cond(True, rx.color("mint"), rx.color("tomato", 5)), - '(true ? "var(--mint-7)" : "var(--tomato-5)")', + '((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => "var(--mint-7)"), (() => "var(--tomato-5)")))())', ), ( rx.cond(True, rx.color(ColorState.color), rx.color(ColorState.color, 5)), # type: ignore - f'(true ? ("var(--"+{color_state_name!s}.color+"-7)") : ("var(--"+{color_state_name!s}.color+"-5)"))', + f'((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => ("var(--"+{color_state_name!s}.color+"-7)")), (() => ("var(--"+{color_state_name!s}.color+"-5)"))))())', ), ( rx.match( diff --git a/tests/units/components/core/test_foreach.py b/tests/units/components/core/test_foreach.py index f241bd111c9..ddc385f65f6 100644 --- a/tests/units/components/core/test_foreach.py +++ b/tests/units/components/core/test_foreach.py @@ -128,7 +128,7 @@ def display_colors_set(color): def display_nested_list_element( element: ArrayVar[Sequence[str]], index: NumberVar[int] ): - assert element._var_type == Sequence[str] + assert element._var_type == List[str] assert index._var_type is int return box(text(element[index])) diff --git a/tests/units/components/core/test_match.py b/tests/units/components/core/test_match.py index f09e800e534..59111d1839c 100644 --- a/tests/units/components/core/test_match.py +++ b/tests/units/components/core/test_match.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Tuple +from typing import Tuple import pytest @@ -17,73 +17,6 @@ class MatchState(BaseState): string: str = "random string" -def test_match_components(): - """Test matching cases with return values as components.""" - match_case_tuples = ( - (1, rx.text("first value")), - (2, 3, rx.text("second value")), - ([1, 2], rx.text("third value")), - ("random", rx.text("fourth value")), - ({"foo": "bar"}, rx.text("fifth value")), - (MatchState.num + 1, rx.text("sixth value")), - rx.text("default value"), - ) - match_comp = Match.create(MatchState.value, *match_case_tuples) - match_dict = match_comp.render() # type: ignore - assert match_dict["name"] == "Fragment" - - [match_child] = match_dict["children"] - - assert match_child["name"] == "match" - assert str(match_child["cond"]) == f"{MatchState.get_name()}.value" - - match_cases = match_child["match_cases"] - assert len(match_cases) == 6 - - assert match_cases[0][0]._js_expr == "1" - assert match_cases[0][0]._var_type is int - first_return_value_render = match_cases[0][1].render() - assert first_return_value_render["name"] == "RadixThemesText" - assert first_return_value_render["children"][0]["contents"] == '{"first value"}' - - assert match_cases[1][0]._js_expr == "2" - assert match_cases[1][0]._var_type is int - assert match_cases[1][1]._js_expr == "3" - assert match_cases[1][1]._var_type is int - second_return_value_render = match_cases[1][2].render() - assert second_return_value_render["name"] == "RadixThemesText" - assert second_return_value_render["children"][0]["contents"] == '{"second value"}' - - assert match_cases[2][0]._js_expr == "[1, 2]" - assert match_cases[2][0]._var_type == List[int] - third_return_value_render = match_cases[2][1].render() - assert third_return_value_render["name"] == "RadixThemesText" - assert third_return_value_render["children"][0]["contents"] == '{"third value"}' - - assert match_cases[3][0]._js_expr == '"random"' - assert match_cases[3][0]._var_type is str - fourth_return_value_render = match_cases[3][1].render() - assert fourth_return_value_render["name"] == "RadixThemesText" - assert fourth_return_value_render["children"][0]["contents"] == '{"fourth value"}' - - assert match_cases[4][0]._js_expr == '({ ["foo"] : "bar" })' - assert match_cases[4][0]._var_type == Dict[str, str] - fifth_return_value_render = match_cases[4][1].render() - assert fifth_return_value_render["name"] == "RadixThemesText" - assert fifth_return_value_render["children"][0]["contents"] == '{"fifth value"}' - - assert match_cases[5][0]._js_expr == f"({MatchState.get_name()}.num + 1)" - assert match_cases[5][0]._var_type is int - fifth_return_value_render = match_cases[5][1].render() - assert fifth_return_value_render["name"] == "RadixThemesText" - assert fifth_return_value_render["children"][0]["contents"] == '{"sixth value"}' - - default = match_child["default"].render() - - assert default["name"] == "RadixThemesText" - assert default["children"][0]["contents"] == '{"default value"}' - - @pytest.mark.parametrize( "cases, expected", [ @@ -102,7 +35,7 @@ def test_match_components(): f'(() => {{ switch (JSON.stringify({MatchState.get_name()}.value)) {{case JSON.stringify(1): return ("first"); break;case JSON.stringify(2): case JSON.stringify(3): return ' '("second value"); break;case JSON.stringify([1, 2]): return ("third-value"); break;case JSON.stringify("random"): ' 'return ("fourth_value"); break;case JSON.stringify(({ ["foo"] : "bar" })): return ("fifth value"); ' - f'break;case JSON.stringify(({MatchState.get_name()}.num + 1)): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): ' + f'break;case JSON.stringify((((_lhs, _rhs) => (_lhs + _rhs))({MatchState.get_name()}.num, 1))): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): ' f'return ({MatchState.get_name()}.string); break;case JSON.stringify({MatchState.get_name()}.string): return (({MatchState.get_name()}.value+" - string")); break;default: ' 'return ("default value"); break;};})()', ), @@ -121,7 +54,7 @@ def test_match_components(): f'(() => {{ switch (JSON.stringify({MatchState.get_name()}.value)) {{case JSON.stringify(1): return ("first"); break;case JSON.stringify(2): case JSON.stringify(3): return ' '("second value"); break;case JSON.stringify([1, 2]): return ("third-value"); break;case JSON.stringify("random"): ' 'return ("fourth_value"); break;case JSON.stringify(({ ["foo"] : "bar" })): return ("fifth value"); ' - f'break;case JSON.stringify(({MatchState.get_name()}.num + 1)): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): ' + f'break;case JSON.stringify((((_lhs, _rhs) => (_lhs + _rhs))({MatchState.get_name()}.num, 1))): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): ' f'return ({MatchState.get_name()}.string); break;case JSON.stringify({MatchState.get_name()}.string): return (({MatchState.get_name()}.value+" - string")); break;default: ' f"return ({MatchState.get_name()}.string); break;}};}})()", ), @@ -143,17 +76,14 @@ def test_match_on_component_without_default(): """Test that matching cases with return values as components returns a Fragment as the default case if not provided. """ - from reflex.components.base.fragment import Fragment - match_case_tuples = ( (1, rx.text("first value")), (2, 3, rx.text("second value")), ) match_comp = Match.create(MatchState.value, *match_case_tuples) - default = match_comp.render()["children"][0]["default"] # type: ignore - assert isinstance(default, Fragment) + assert isinstance(match_comp, Var) def test_match_on_var_no_default(): @@ -247,8 +177,8 @@ def test_match_case_tuple_elements(match_case): (MatchState.num + 1, "black"), rx.text("default value"), ), - 'Match cases should have the same return types. Case 3 with return value `"red"` of type ' - " is not ", + "Match cases should have the same return types. Case 3 with return value `red` of type " + " is not ", ), ( ( @@ -261,7 +191,7 @@ def test_match_case_tuple_elements(match_case): rx.text("default value"), ), 'Match cases should have the same return types. Case 3 with return value ` {"first value"} ` ' - "of type is not ", + "of type is not ", ), ], ) diff --git a/tests/units/components/markdown/test_markdown.py b/tests/units/components/markdown/test_markdown.py index 866f32ae143..93e30f0d378 100644 --- a/tests/units/components/markdown/test_markdown.py +++ b/tests/units/components/markdown/test_markdown.py @@ -148,7 +148,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe ( "code", {}, - """(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( {children} ) : ( ); })""", + '(({node, inline, className, children, ...props}) => { const match = (className || \'\').match(/language-(?.*)/); const _language = match ? match[1] : \'\'; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( {children} ) : ( (_condition ? _if_true : _if_false))((Array.isArray(children)), (((...args) => (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\\n")), children))} css={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} customStyle={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} language={_language} style={((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))((((_lhs, _rhs) => (_lhs === _rhs))(resolvedColorMode, "light")), (() => oneLight), (() => oneDark)))())} wrapLongLines={true} {...props}/> ); })', ), ( "code", @@ -157,7 +157,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe value, **props ) }, - """(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; ; return inline ? ( {children} ) : ( ); })""", + '(({node, inline, className, children, ...props}) => { const match = (className || \'\').match(/language-(?.*)/); const _language = match ? match[1] : \'\'; ; return inline ? ( {children} ) : ( (_condition ? _if_true : _if_false))((Array.isArray(children)), (((...args) => (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\\n")), children))} decorations={[]} language={_language} theme={((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))((((_lhs, _rhs) => (_lhs === _rhs))(resolvedColorMode, "light")), (() => "one-light"), (() => "one-dark-pro")))())} transformers={[]}/> ); })', ), ( "h1", @@ -171,7 +171,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe ( "code", {"codeblock": syntax_highlighter_memoized_component(CodeBlock)}, - """(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( {children} ) : ( ); })""", + "(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( {children} ) : ( (_condition ? _if_true : _if_false))((Array.isArray(children)), (((...args) => (((_array, _sep = \"\") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))(\"\\n\")), children))} language={_language} {...props}/> ); })", ), ( "code", @@ -180,7 +180,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe ShikiHighLevelCodeBlock ) }, - """(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; ; return inline ? ( {children} ) : ( ); })""", + """(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; ; return inline ? ( {children} ) : ( (_condition ? _if_true : _if_false))((Array.isArray(children)), (((...args) => (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\\n")), children))} language={_language} {...props}/> ); })""", ), ], ) From 72f1fa7cb4ac13699079026c4b76b85c71c7c42f Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 16 Jan 2025 18:17:15 -0800 Subject: [PATCH 46/86] make the var type actually work --- reflex/vars/number.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 3090e249759..fa549b3b31c 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -1169,14 +1169,20 @@ def create( Returns: The match operation. """ + cond = Var.create(cond) cases = tuple(tuple(Var.create(c) for c in case) for case in cases) + default = Var.create(default) + var_type = _var_type or unionize( + *(case[-1]._var_type for case in cases), + default._var_type, + ) return cls( _js_expr="", _var_data=_var_data, - _var_type=_var_type, - _cond=Var.create(cond), + _var_type=var_type, + _cond=cond, _cases=cases, - _default=Var.create(default), + _default=default, ) From 3d73f561b7159e77285da6842aa9e2b1a5dcd877 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 16 Jan 2025 19:16:53 -0800 Subject: [PATCH 47/86] can't have ohio --- reflex/vars/base.py | 4 +--- reflex/vars/number.py | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 4feca42a9d0..82b169db4ec 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -218,9 +218,7 @@ def __init__( position: Position of the hook in the component. """ immutable_imports: ImmutableParsedImportDict = tuple( - sorted( - ((k, tuple(sorted(v))) for k, v in parse_imports(imports or {}).items()) - ) + (k, tuple(v)) for k, v in parse_imports(imports or {}).items() ) object.__setattr__(self, "state", state) object.__setattr__(self, "field_name", field_name) diff --git a/reflex/vars/number.py b/reflex/vars/number.py index fa549b3b31c..ee1bb4f7f33 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -1140,9 +1140,9 @@ def _cached_get_all_var_data(self) -> VarData | None: return VarData.merge( self._cond._get_all_var_data(), *( - case._get_all_var_data() - for cond_or_return in self._cases - for case in cond_or_return + cond_or_return._get_all_var_data() + for case in self._cases + for cond_or_return in case ), self._default._get_all_var_data(), self._var_data, From 112b2ed948f67bcf31002ffa81aec2f6e813e400 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 16 Jan 2025 20:06:02 -0800 Subject: [PATCH 48/86] solve some but not all pyright issues --- reflex/utils/pyi_generator.py | 42 +++++++++++++++++++++++++---------- reflex/utils/types.py | 21 ++++++++++++------ reflex/vars/number.py | 14 +++++++----- 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/reflex/utils/pyi_generator.py b/reflex/utils/pyi_generator.py index 152c0694996..52a3a9fff19 100644 --- a/reflex/utils/pyi_generator.py +++ b/reflex/utils/pyi_generator.py @@ -16,7 +16,7 @@ from multiprocessing import Pool, cpu_count from pathlib import Path from types import ModuleType, SimpleNamespace -from typing import Any, Callable, Iterable, Sequence, Type, get_args, get_origin +from typing import Any, Callable, Iterable, Sequence, Type, cast, get_args, get_origin from reflex.components.component import Component from reflex.utils import types as rx_types @@ -229,7 +229,9 @@ def _generate_imports( """ return [ *[ - ast.ImportFrom(module=name, names=[ast.alias(name=val) for val in values]) + ast.ImportFrom( + module=name, names=[ast.alias(name=val) for val in values], level=0 + ) for name, values in DEFAULT_IMPORTS.items() ], ast.Import([ast.alias("reflex")]), @@ -428,16 +430,15 @@ def type_to_ast(typ, cls: type) -> ast.AST: return ast.Name(id=base_name) # Convert all type arguments recursively - arg_nodes = [type_to_ast(arg, cls) for arg in args] + arg_nodes = cast(list[ast.expr], [type_to_ast(arg, cls) for arg in args]) # Special case for single-argument types (like List[T] or Optional[T]) if len(arg_nodes) == 1: slice_value = arg_nodes[0] else: slice_value = ast.Tuple(elts=arg_nodes, ctx=ast.Load()) - return ast.Subscript( - value=ast.Name(id=base_name), slice=ast.Index(value=slice_value), ctx=ast.Load() + value=ast.Name(id=base_name), slice=slice_value, ctx=ast.Load() ) @@ -630,7 +631,7 @@ def figure_out_return_type(annotation: Any): ), ), ast.Expr( - value=ast.Ellipsis(), + value=ast.Constant(...), ), ], decorator_list=[ @@ -641,8 +642,14 @@ def figure_out_return_type(annotation: Any): else [ast.Name(id="classmethod")] ), ], - lineno=node.lineno if node is not None else None, returns=ast.Constant(value=clz.__name__), + **( + { + "lineno": node.lineno, + } + if node is not None + else {} + ), ) return definition @@ -690,13 +697,19 @@ def _generate_staticmethod_call_functiondef( ), ], decorator_list=[ast.Name(id="staticmethod")], - lineno=node.lineno if node is not None else None, returns=ast.Constant( value=_get_type_hint( typing.get_type_hints(clz.__call__).get("return", None), type_hint_globals, ) ), + **( + { + "lineno": node.lineno, + } + if node is not None + else {} + ), ) return definition @@ -731,7 +744,12 @@ def _generate_namespace_call_functiondef( # Determine which class is wrapped by the namespace __call__ method component_clz = clz.__call__.__self__ - if clz.__call__.__func__.__name__ != "create": + func = getattr(clz.__call__, "__func__", None) + + if func is None: + raise TypeError(f"__call__ method on {clz_name} does not have a __func__") + + if func.__name__ != "create": return None definition = _generate_component_create_functiondef( @@ -914,7 +932,7 @@ def visit_ClassDef(self, node: ast.ClassDef) -> ast.ClassDef: node.body.append(call_definition) if not node.body: # We should never return an empty body. - node.body.append(ast.Expr(value=ast.Ellipsis())) + node.body.append(ast.Expr(value=ast.Constant(...))) self.current_class = None return node @@ -941,9 +959,9 @@ def visit_FunctionDef(self, node: ast.FunctionDef) -> Any: if node.name.startswith("_") and node.name != "__call__": return None # remove private methods - if node.body[-1] != ast.Expr(value=ast.Ellipsis()): + if node.body[-1] != ast.Expr(value=ast.Constant(...)): # Blank out the function body for public functions. - node.body = [ast.Expr(value=ast.Ellipsis())] + node.body = [ast.Expr(value=ast.Constant(...))] return node def visit_Assign(self, node: ast.Assign) -> ast.Assign | None: diff --git a/reflex/utils/types.py b/reflex/utils/types.py index d968b9a0982..e323c5441ef 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -69,21 +69,21 @@ def override(func: Callable) -> Callable: # Potential GenericAlias types for isinstance checks. -GenericAliasTypes = [_GenericAlias] +_GenericAliasTypes: list[type] = [_GenericAlias] with contextlib.suppress(ImportError): # For newer versions of Python. from types import GenericAlias # type: ignore - GenericAliasTypes.append(GenericAlias) + _GenericAliasTypes.append(GenericAlias) with contextlib.suppress(ImportError): # For older versions of Python. from typing import _SpecialGenericAlias # type: ignore - GenericAliasTypes.append(_SpecialGenericAlias) + _GenericAliasTypes.append(_SpecialGenericAlias) -GenericAliasTypes = tuple(GenericAliasTypes) +GenericAliasTypes = tuple(_GenericAliasTypes) # Potential Union types for isinstance checks (UnionType added in py3.10). UnionTypes = (Union, types.UnionType) if hasattr(types, "UnionType") else (Union,) @@ -181,7 +181,7 @@ def is_generic_alias(cls: GenericType) -> bool: return isinstance(cls, GenericAliasTypes) -def unionize(*args: GenericType) -> Type: +def unionize(*args: GenericType) -> GenericType: """Unionize the types. Args: @@ -415,7 +415,7 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None @lru_cache() -def get_base_class(cls: GenericType) -> Type: +def get_base_class(cls: GenericType) -> Type | tuple[Type, ...]: """Get the base class of a class. Args: @@ -435,7 +435,14 @@ def get_base_class(cls: GenericType) -> Type: return type(get_args(cls)[0]) if is_union(cls): - return tuple(get_base_class(arg) for arg in get_args(cls)) + base_classes = [] + for arg in get_args(cls): + sub_base_classes = get_base_class(arg) + if isinstance(sub_base_classes, tuple): + base_classes.extend(sub_base_classes) + else: + base_classes.append(sub_base_classes) + return tuple(base_classes) return get_base_class(cls.__origin__) if is_generic_alias(cls) else cls diff --git a/reflex/vars/number.py b/reflex/vars/number.py index ee1bb4f7f33..2d0fb3326a8 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -15,6 +15,7 @@ Sequence, TypeVar, Union, + cast, overload, ) @@ -1102,7 +1103,7 @@ class MatchOperation(CachedVarOperation, Var[VAR_TYPE]): _cases: tuple[TUPLE_ENDS_IN_VAR[VAR_TYPE], ...] = dataclasses.field( default_factory=tuple ) - _default: Var[VAR_TYPE] = dataclasses.field( + _default: Var[VAR_TYPE] = dataclasses.field( # pyright: ignore[reportAssignmentType] default_factory=lambda: Var.create(None) ) @@ -1170,11 +1171,14 @@ def create( The match operation. """ cond = Var.create(cond) - cases = tuple(tuple(Var.create(c) for c in case) for case in cases) - default = Var.create(default) + cases = cast( + tuple[TUPLE_ENDS_IN_VAR[VAR_TYPE], ...], + tuple(tuple(Var.create(c) for c in case) for case in cases), + ) + _default = cast(Var[VAR_TYPE], Var.create(default)) var_type = _var_type or unionize( *(case[-1]._var_type for case in cases), - default._var_type, + _default._var_type, ) return cls( _js_expr="", @@ -1182,7 +1186,7 @@ def create( _var_type=var_type, _cond=cond, _cases=cases, - _default=default, + _default=_default, ) From 57d8ea02e9989aeb390fe75371bf57be37627428 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 14:16:03 -0800 Subject: [PATCH 49/86] down to only two pyright error --- poetry.lock | 8 +- pyproject.toml | 3 +- reflex/base.py | 12 +-- reflex/components/core/cond.py | 6 +- reflex/components/core/match.py | 82 +++++++++---------- reflex/components/datadisplay/dataeditor.py | 2 +- .../components/radix/primitives/accordion.py | 2 +- reflex/components/tags/iter_tag.py | 9 +- reflex/event.py | 66 +++++++++++---- reflex/state.py | 20 ++--- reflex/testing.py | 10 ++- reflex/utils/console.py | 9 +- reflex/vars/base.py | 9 +- reflex/vars/number.py | 26 ++---- reflex/vars/object.py | 5 +- tests/integration/test_event_actions.py | 2 + tests/integration/test_lifespan.py | 8 +- tests/integration/test_var_operations.py | 36 ++++---- tests/units/components/core/test_cond.py | 2 +- tests/units/components/core/test_match.py | 12 +-- .../components/datadisplay/test_datatable.py | 6 +- tests/units/test_app.py | 10 +-- tests/units/test_event.py | 3 + tests/units/test_health_endpoint.py | 7 +- tests/units/test_sqlalchemy.py | 8 +- tests/units/test_state.py | 31 ++++--- 26 files changed, 219 insertions(+), 175 deletions(-) diff --git a/poetry.lock b/poetry.lock index e22568c6000..a36f44fd1f3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2813,13 +2813,13 @@ standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", [[package]] name = "virtualenv" -version = "20.28.1" +version = "20.29.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" files = [ - {file = "virtualenv-20.28.1-py3-none-any.whl", hash = "sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb"}, - {file = "virtualenv-20.28.1.tar.gz", hash = "sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329"}, + {file = "virtualenv-20.29.1-py3-none-any.whl", hash = "sha256:4e4cb403c0b0da39e13b46b1b2476e505cb0046b25f242bee80f62bf990b2779"}, + {file = "virtualenv-20.29.1.tar.gz", hash = "sha256:b8b8970138d32fb606192cb97f6cd4bb644fa486be9308fb9b63f81091b5dc35"}, ] [package.dependencies] @@ -3063,4 +3063,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "ac7633388e00416af61c93e16c61c3b4a446e406afab2758585088e2b3416eee" +content-hash = "ccd6d6b00fdcf40562854380fafdb18c990b7f6a4f2883b33aaeb0351fcdbc06" diff --git a/pyproject.toml b/pyproject.toml index 67bcef1cb21..b64284ddb05 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ fastapi = ">=0.96.0,!=0.111.0,!=0.111.1" gunicorn = ">=20.1.0,<24.0" jinja2 = ">=3.1.2,<4.0" psutil = ">=5.9.4,<7.0" -pydantic = ">=1.10.2,<3.0" +pydantic = ">=1.10.15,<3.0" python-multipart = ">=0.0.5,<0.1" python-socketio = ">=5.7.0,<6.0" redis = ">=4.3.5,<6.0" @@ -82,6 +82,7 @@ build-backend = "poetry.core.masonry.api" [tool.pyright] reportIncompatibleMethodOverride = false +reportIncompatibleVariableOverride = false [tool.ruff] target-version = "py39" diff --git a/reflex/base.py b/reflex/base.py index a88e557ef47..1f0bbe00df3 100644 --- a/reflex/base.py +++ b/reflex/base.py @@ -5,15 +5,9 @@ import os from typing import TYPE_CHECKING, Any, List, Type -try: - import pydantic.v1.main as pydantic_main - from pydantic.v1 import BaseModel - from pydantic.v1.fields import ModelField -except ModuleNotFoundError: - if not TYPE_CHECKING: - import pydantic.main as pydantic_main - from pydantic import BaseModel - from pydantic.fields import ModelField # type: ignore +import pydantic.v1.main as pydantic_main +from pydantic.v1 import BaseModel +from pydantic.v1.fields import ModelField def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None: diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index 98ce605d6d1..e34201c747b 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -113,11 +113,7 @@ def add_imports(self) -> ImportDict: @overload -def cond(condition: Any, c1: Component, c2: Any) -> Component: ... - - -@overload -def cond(condition: Any, c1: Component) -> Component: ... +def cond(condition: Any, c1: Component, c2: Any = None) -> Component: ... @overload diff --git a/reflex/components/core/match.py b/reflex/components/core/match.py index 2def33d6552..a053b9abe2b 100644 --- a/reflex/components/core/match.py +++ b/reflex/components/core/match.py @@ -1,15 +1,17 @@ """rx.match.""" import textwrap -from typing import Any, List, Optional, Sequence, Tuple, Union +from typing import Any, List, cast from reflex.components.base import Fragment from reflex.components.component import BaseComponent, Component, MemoizationLeaf from reflex.utils import types from reflex.utils.exceptions import MatchTypeError -from reflex.vars.base import Var +from reflex.vars.base import VAR_TYPE, Var from reflex.vars.number import MatchOperation +CASE_TYPE = tuple[*tuple[Any, ...], Var[VAR_TYPE] | VAR_TYPE] + class Match(MemoizationLeaf): """Match cases based on a condition.""" @@ -24,7 +26,11 @@ class Match(MemoizationLeaf): default: Any @classmethod - def create(cls, cond: Any, *cases) -> Union[Component, Var]: + def create( + cls, + cond: Any, + *cases: *tuple[*tuple[CASE_TYPE[VAR_TYPE], ...], Var[VAR_TYPE] | VAR_TYPE], + ) -> Var[VAR_TYPE]: """Create a Match Component. Args: @@ -37,10 +43,24 @@ def create(cls, cond: Any, *cases) -> Union[Component, Var]: Raises: ValueError: When a default case is not provided for cases with Var return types. """ - cases, default = cls._process_cases(cases) - cls._process_match_cases(cases) + default = None - cls._validate_return_types(cases) + if len([case for case in cases if not isinstance(case, tuple)]) > 1: + raise ValueError("rx.match can only have one default case.") + + if not cases: + raise ValueError("rx.match should have at least one case.") + + # Get the default case which should be the last non-tuple arg + if not isinstance(cases[-1], tuple): + default = cases[-1] + actual_cases = cases[:-1] + else: + actual_cases = cast(tuple[CASE_TYPE[VAR_TYPE], ...], cases) + + cls._process_match_cases(actual_cases) + + cls._validate_return_types(actual_cases) if default is None and any( not ( @@ -50,7 +70,7 @@ def create(cls, cond: Any, *cases) -> Union[Component, Var]: and types.typehint_issubclass(return_type._var_type, Component) ) ) - for case in cases + for case in actual_cases ): raise ValueError( "For cases with return types as Vars, a default case must be provided" @@ -58,40 +78,16 @@ def create(cls, cond: Any, *cases) -> Union[Component, Var]: elif default is None: default = Fragment.create() - return cls._create_match_cond_var_or_component(cond, cases, default) - - @classmethod - def _process_cases( - cls, cases: Sequence - ) -> Tuple[List, Optional[Union[Var, BaseComponent]]]: - """Process the list of match cases and the catchall default case. - - Args: - cases: The list of match cases. - - Returns: - The default case and the list of match case tuples. - - Raises: - ValueError: If there are multiple default cases. - """ - default = None - - if len([case for case in cases if not isinstance(case, tuple)]) > 1: - raise ValueError("rx.match can only have one default case.") - - if not cases: - raise ValueError("rx.match should have at least one case.") - - # Get the default case which should be the last non-tuple arg - if not isinstance(cases[-1], tuple): - default = cases[-1] - cases = cases[:-1] + default = cast(Var[VAR_TYPE] | VAR_TYPE, default) - return list(cases), default + return cls._create_match_cond_var_or_component( + cond, + actual_cases, + default, + ) @classmethod - def _process_match_cases(cls, cases: Sequence): + def _process_match_cases(cls, cases: tuple[CASE_TYPE[VAR_TYPE], ...]): """Process the individual match cases. Args: @@ -116,7 +112,9 @@ def _process_match_cases(cls, cases: Sequence): ) @classmethod - def _validate_return_types(cls, match_cases: List[List[Var]]) -> None: + def _validate_return_types( + cls, match_cases: tuple[CASE_TYPE[VAR_TYPE], ...] + ) -> None: """Validate that match cases have the same return types. Args: @@ -151,9 +149,9 @@ def _validate_return_types(cls, match_cases: List[List[Var]]) -> None: def _create_match_cond_var_or_component( cls, match_cond_var: Var, - match_cases: List[List[Var]], - default: Union[Var, BaseComponent], - ) -> Union[Component, Var]: + match_cases: tuple[CASE_TYPE[VAR_TYPE], ...], + default: VAR_TYPE | Var[VAR_TYPE], + ) -> Var[VAR_TYPE]: """Create and return the match condition var or component. Args: diff --git a/reflex/components/datadisplay/dataeditor.py b/reflex/components/datadisplay/dataeditor.py index f71f9771394..b82196fe2fd 100644 --- a/reflex/components/datadisplay/dataeditor.py +++ b/reflex/components/datadisplay/dataeditor.py @@ -303,7 +303,7 @@ class DataEditor(NoSSRComponent): # Fired when editing is finished. on_finished_editing: EventHandler[ - passthrough_event_spec(Union[GridCell, None], tuple[int, int]) + passthrough_event_spec(Union[GridCell, None], tuple[int, int]) # pyright: ignore[reportArgumentType] ] # Fired when a row is appended. diff --git a/reflex/components/radix/primitives/accordion.py b/reflex/components/radix/primitives/accordion.py index 0ba618e212c..2964b6fa5b1 100644 --- a/reflex/components/radix/primitives/accordion.py +++ b/reflex/components/radix/primitives/accordion.py @@ -197,7 +197,7 @@ class AccordionItem(AccordionComponent): # The header of the accordion item. header: Var[Union[Component, str]] # The content of the accordion item. - content: Var[Union[Component, str]] = Var.create(None) + content: Var[Union[Component, str]] = Var.create("") _valid_children: List[str] = [ "AccordionHeader", diff --git a/reflex/components/tags/iter_tag.py b/reflex/components/tags/iter_tag.py index 3f7aa47f283..f3d9c4d8f6b 100644 --- a/reflex/components/tags/iter_tag.py +++ b/reflex/components/tags/iter_tag.py @@ -4,9 +4,10 @@ import dataclasses import inspect -from typing import TYPE_CHECKING, Any, Callable, Iterable, Tuple, Type, Union, get_args +from typing import TYPE_CHECKING, Any, Callable, Iterable, Tuple, Union, get_args from reflex.components.tags.tag import Tag +from reflex.utils import types from reflex.vars import LiteralArrayVar, Var, get_unique_variable_name if TYPE_CHECKING: @@ -31,7 +32,7 @@ class IterTag(Tag): # The name of the index var. index_var_name: str = dataclasses.field(default_factory=get_unique_variable_name) - def get_iterable_var_type(self) -> Type: + def get_iterable_var_type(self) -> types.GenericType: """Get the type of the iterable var. Returns: @@ -41,10 +42,10 @@ def get_iterable_var_type(self) -> Type: try: if iterable._var_type.mro()[0] is dict: # Arg is a tuple of (key, value). - return Tuple[get_args(iterable._var_type)] # type: ignore + return Tuple[get_args(iterable._var_type)] elif iterable._var_type.mro()[0] is tuple: # Arg is a union of any possible values in the tuple. - return Union[get_args(iterable._var_type)] # type: ignore + return Union[get_args(iterable._var_type)] else: return get_args(iterable._var_type)[0] except Exception: diff --git a/reflex/event.py b/reflex/event.py index 28852fde57b..dee33bced12 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -25,7 +25,6 @@ overload, ) -import typing_extensions from typing_extensions import ( Concatenate, ParamSpec, @@ -33,6 +32,8 @@ TypeAliasType, TypedDict, TypeVar, + TypeVarTuple, + deprecated, get_args, get_origin, ) @@ -620,14 +621,16 @@ def no_args_event_spec() -> Tuple[()]: prevent_default = EventChain(events=[], args_spec=no_args_event_spec).prevent_default -T = TypeVar("T") -U = TypeVar("U") +EVENT_T = TypeVar("EVENT_T") +EVENT_U = TypeVar("EVENT_U") + +Ts = TypeVarTuple("Ts") -class IdentityEventReturn(Generic[T], Protocol): +class IdentityEventReturn(Generic[*Ts], Protocol): """Protocol for an identity event return.""" - def __call__(self, *values: Var[T]) -> Tuple[Var[T], ...]: + def __call__(self, *values: *Ts) -> tuple[*Ts]: """Return the input values. Args: @@ -641,21 +644,25 @@ def __call__(self, *values: Var[T]) -> Tuple[Var[T], ...]: @overload def passthrough_event_spec( - event_type: Type[T], / -) -> Callable[[Var[T]], Tuple[Var[T]]]: ... # type: ignore + event_type: Type[EVENT_T], / +) -> IdentityEventReturn[Var[EVENT_T]]: ... @overload def passthrough_event_spec( - event_type_1: Type[T], event_type2: Type[U], / -) -> Callable[[Var[T], Var[U]], Tuple[Var[T], Var[U]]]: ... + event_type_1: Type[EVENT_T], event_type2: Type[EVENT_U], / +) -> IdentityEventReturn[Var[EVENT_T], Var[EVENT_U]]: ... @overload -def passthrough_event_spec(*event_types: Type[T]) -> IdentityEventReturn[T]: ... +def passthrough_event_spec( + *event_types: *tuple[Type[EVENT_T]], +) -> IdentityEventReturn[*tuple[Var[EVENT_T], ...]]: ... -def passthrough_event_spec(*event_types: Type[T]) -> IdentityEventReturn[T]: # type: ignore +def passthrough_event_spec( # pyright: ignore[reportInconsistentOverload] + *event_types: Type[EVENT_T], +) -> IdentityEventReturn[*tuple[Var[EVENT_T], ...]]: """A helper function that returns the input event as output. Args: @@ -665,7 +672,7 @@ def passthrough_event_spec(*event_types: Type[T]) -> IdentityEventReturn[T]: # A function that returns the input event as output. """ - def inner(*values: Var[T]) -> Tuple[Var[T], ...]: + def inner(*values: Var[EVENT_T]) -> Tuple[Var[EVENT_T], ...]: return values inner_type = tuple(Var[event_type] for event_type in event_types) @@ -800,7 +807,7 @@ def fn(): return None fn.__qualname__ = name - fn.__signature__ = sig + fn.__signature__ = sig # pyright: ignore[reportFunctionMemberAccess] return EventSpec( handler=EventHandler(fn=fn, state_full_name=FRONTEND_EVENT_STATE), args=tuple( @@ -822,7 +829,7 @@ def redirect( @overload -@typing_extensions.deprecated("`external` is deprecated use `is_external` instead") +@deprecated("`external` is deprecated use `is_external` instead") def redirect( path: str | Var[str], is_external: Optional[bool] = None, @@ -1826,6 +1833,37 @@ def __init__(self, func: Callable[Concatenate[Any, P], T]): """ self.func = func + def throttle(self, limit_ms: int): + """Throttle the event handler. + + Args: + limit_ms: The time in milliseconds to throttle the event handler. + + Returns: + New EventHandler-like with throttle set to limit_ms. + """ + return self + + def debounce(self, delay_ms: int): + """Debounce the event handler. + + Args: + delay_ms: The time in milliseconds to debounce the event handler. + + Returns: + New EventHandler-like with debounce set to delay_ms. + """ + return self + + @property + def temporal(self): + """Do not queue the event if the backend is down. + + Returns: + New EventHandler-like with temporal set to True. + """ + return self + @property def prevent_default(self): """Prevent default behavior. diff --git a/reflex/state.py b/reflex/state.py index f9931f92002..1f7339d5cba 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -587,8 +587,8 @@ def __init_subclass__(cls, mixin: bool = False, **kwargs): if cls._item_is_event_handler(name, fn) } - for mixin in cls._mixins(): - for name, value in mixin.__dict__.items(): + for mixin_class in cls._mixins(): + for name, value in mixin_class.__dict__.items(): if name in cls.inherited_vars: continue if is_computed_var(value): @@ -599,7 +599,7 @@ def __init_subclass__(cls, mixin: bool = False, **kwargs): cls.computed_vars[newcv._js_expr] = newcv cls.vars[newcv._js_expr] = newcv continue - if types.is_backend_base_variable(name, mixin): + if types.is_backend_base_variable(name, mixin_class): cls.backend_vars[name] = copy.deepcopy(value) continue if events.get(name) is not None: @@ -3710,6 +3710,9 @@ def get_state_manager() -> StateManager: return app.state_manager +DATACLASS_FIELDS = getattr(dataclasses, "_FIELDS", "__dataclass_fields__") + + class MutableProxy(wrapt.ObjectProxy): """A proxy for a mutable object that tracks changes.""" @@ -3781,12 +3784,7 @@ def __new__(cls, wrapped: Any, *args, **kwargs) -> MutableProxy: cls.__dataclass_proxies__[wrapper_cls_name] = type( wrapper_cls_name, (cls,), - { - dataclasses._FIELDS: getattr( # pyright: ignore [reportGeneralTypeIssues] - wrapped_cls, - dataclasses._FIELDS, # pyright: ignore [reportGeneralTypeIssues] - ), - }, + {DATACLASS_FIELDS: getattr(wrapped_cls, DATACLASS_FIELDS)}, ) cls = cls.__dataclass_proxies__[wrapper_cls_name] return super().__new__(cls) @@ -3933,11 +3931,11 @@ def __getattr__(self, __name: str) -> Any: if ( isinstance(self.__wrapped__, Base) and __name not in self.__never_wrap_base_attrs__ - and hasattr(value, "__func__") + and (value_func := getattr(value, "__func__", None)) ): # Wrap methods called on Base subclasses, which might do _anything_ return wrapt.FunctionWrapper( - functools.partial(value.__func__, self), + functools.partial(value_func, self), self._wrap_recursive_decorator, ) diff --git a/reflex/testing.py b/reflex/testing.py index b3dedf398ad..68b4e99fdfc 100644 --- a/reflex/testing.py +++ b/reflex/testing.py @@ -67,10 +67,8 @@ from selenium.webdriver.remote.webelement import ( # pyright: ignore [reportMissingImports] WebElement, ) - - has_selenium = True except ImportError: - has_selenium = False + webdriver = None # The timeout (minutes) to check for the port. DEFAULT_TIMEOUT = 15 @@ -293,8 +291,12 @@ def _initialize_app(self): if p not in before_decorated_pages ] self.app_instance = self.app_module.app + if self.app_instance is None: + raise RuntimeError("App was not initialized.") if isinstance(self.app_instance._state_manager, StateManagerRedis): # Create our own redis connection for testing. + if self.app_instance.state is None: + raise RuntimeError("App state is not initialized.") self.state_manager = StateManagerRedis.create(self.app_instance.state) else: self.state_manager = self.app_instance._state_manager @@ -608,7 +610,7 @@ def frontend( Raises: RuntimeError: when selenium is not importable or frontend is not running """ - if not has_selenium: + if webdriver is None: raise RuntimeError( "Frontend functionality requires `selenium` to be installed, " "and it could not be imported." diff --git a/reflex/utils/console.py b/reflex/utils/console.py index 8929b63b635..fc32ac73ed9 100644 --- a/reflex/utils/console.py +++ b/reflex/utils/console.py @@ -203,10 +203,13 @@ def _get_first_non_framework_frame() -> FrameType | None: # Exclude utility modules that should never be the source of deprecated reflex usage. exclude_modules = [click, rx, typer, typing_extensions] exclude_roots = [ - p.parent.resolve() - if (p := Path(m.__file__)).name == "__init__.py" - else p.resolve() + ( + p.parent.resolve() + if (p := Path(m.__file__)).name == "__init__.py" + else p.resolve() + ) for m in exclude_modules + if m.__file__ ] # Specifically exclude the reflex cli module. if reflex_bin := shutil.which(b"reflex"): diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 82b169db4ec..cf92efce35d 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -3197,17 +3197,20 @@ def __set__(self, instance, value: T): def __get__(self: Field[bool], instance: None, owner) -> BooleanVar: ... @overload - def __get__(self: Field[int], instance: None, owner) -> NumberVar: ... + def __get__(self: Field[int], instance: None, owner) -> NumberVar[int]: ... @overload - def __get__(self: Field[str], instance: None, owner) -> StringVar: ... + def __get__(self: Field[float], instance: None, owner) -> NumberVar[float]: ... + + @overload + def __get__(self: Field[str], instance: None, owner) -> StringVar[str]: ... @overload def __get__(self: Field[None], instance: None, owner) -> NoneVar: ... @overload def __get__( - self: Field[Sequence[V]] | Field[Set[V]], + self: Field[Sequence[V]] | Field[Set[V]] | Field[List[V]], instance: None, owner, ) -> ArrayVar[Sequence[V]]: ... diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 2d0fb3326a8..1e85a1c7427 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -1069,24 +1069,11 @@ def ternary_operation( return value -TUPLE_ENDS_IN_VAR = ( - tuple[Var[VAR_TYPE]] - | tuple[Var, Var[VAR_TYPE]] - | tuple[Var, Var, Var[VAR_TYPE]] - | tuple[Var, Var, Var, Var[VAR_TYPE]] - | tuple[Var, Var, Var, Var, Var[VAR_TYPE]] - | tuple[Var, Var, Var, Var, Var, Var[VAR_TYPE]] - | tuple[Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] - | tuple[Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] - | tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] - | tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] - | tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] - | tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] - | tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]] - | tuple[ - Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE] - ] -) +X = tuple[*tuple[Var, ...], str] + +TUPLE_ENDS_IN_VAR = tuple[*tuple[Var[Any], ...], Var[VAR_TYPE]] + +TUPLE_ENDS_IN_VAR_RELAXED = tuple[*tuple[Var[Any] | Any, ...], Var[VAR_TYPE] | VAR_TYPE] @dataclasses.dataclass( @@ -1153,7 +1140,7 @@ def _cached_get_all_var_data(self) -> VarData | None: def create( cls, cond: Any, - cases: Sequence[Sequence[Any | Var[VAR_TYPE]]], + cases: Sequence[TUPLE_ENDS_IN_VAR_RELAXED[VAR_TYPE]], default: Var[VAR_TYPE] | VAR_TYPE, _var_data: VarData | None = None, _var_type: type[VAR_TYPE] | None = None, @@ -1175,6 +1162,7 @@ def create( tuple[TUPLE_ENDS_IN_VAR[VAR_TYPE], ...], tuple(tuple(Var.create(c) for c in case) for case in cases), ) + _default = cast(Var[VAR_TYPE], Var.create(default)) var_type = _var_type or unionize( *(case[-1]._var_type for case in cases), diff --git a/reflex/vars/object.py b/reflex/vars/object.py index 7c793a0fb91..0c3c001c9e1 100644 --- a/reflex/vars/object.py +++ b/reflex/vars/object.py @@ -45,7 +45,7 @@ from .number import BooleanVar, NumberVar, raise_unsupported_operand_types from .sequence import ArrayVar, StringVar -OBJECT_TYPE = TypeVar("OBJECT_TYPE") +OBJECT_TYPE = TypeVar("OBJECT_TYPE", covariant=True) KEY_TYPE = TypeVar("KEY_TYPE") VALUE_TYPE = TypeVar("VALUE_TYPE") @@ -164,7 +164,8 @@ def __getitem__( @overload def __getitem__( - self: ObjectVar[Dict[Any, Sequence[ARRAY_INNER_TYPE]]], + self: ObjectVar[Dict[Any, Sequence[ARRAY_INNER_TYPE]]] + | ObjectVar[Dict[Any, List[ARRAY_INNER_TYPE]]], key: Var | Any, ) -> ArrayVar[Sequence[ARRAY_INNER_TYPE]]: ... diff --git a/tests/integration/test_event_actions.py b/tests/integration/test_event_actions.py index 15f3c987740..084a8c4fa71 100644 --- a/tests/integration/test_event_actions.py +++ b/tests/integration/test_event_actions.py @@ -28,9 +28,11 @@ def on_click(self, ev): def on_click2(self): self.order.append("on_click2") + @rx.event def on_click_throttle(self): self.order.append("on_click_throttle") + @rx.event def on_click_debounce(self): self.order.append("on_click_debounce") diff --git a/tests/integration/test_lifespan.py b/tests/integration/test_lifespan.py index 0fa4a7e923d..98d8addfa19 100644 --- a/tests/integration/test_lifespan.py +++ b/tests/integration/test_lifespan.py @@ -22,9 +22,9 @@ def LifespanApp(): @asynccontextmanager async def lifespan_context(app, inc: int = 1): - global lifespan_context_global + nonlocal lifespan_context_global print(f"Lifespan context entered: {app}.") - lifespan_context_global += inc # pyright: ignore[reportUnboundVariable] + lifespan_context_global += inc try: yield finally: @@ -32,11 +32,11 @@ async def lifespan_context(app, inc: int = 1): lifespan_context_global += inc async def lifespan_task(inc: int = 1): - global lifespan_task_global + nonlocal lifespan_task_global print("Lifespan global started.") try: while True: - lifespan_task_global += inc # pyright: ignore[reportUnboundVariable] + lifespan_task_global += inc await asyncio.sleep(0.1) except asyncio.CancelledError as ce: print(f"Lifespan global cancelled: {ce}.") diff --git a/tests/integration/test_var_operations.py b/tests/integration/test_var_operations.py index 7a7c8328d55..fff4c975c1e 100644 --- a/tests/integration/test_var_operations.py +++ b/tests/integration/test_var_operations.py @@ -19,25 +19,27 @@ def VarOperations(): from reflex.vars.sequence import ArrayVar class Object(rx.Base): - str: str = "hello" + name: str = "hello" class VarOperationState(rx.State): - int_var1: int = 10 - int_var2: int = 5 - int_var3: int = 7 - float_var1: float = 10.5 - float_var2: float = 5.5 - list1: List = [1, 2] - list2: List = [3, 4] - list3: List = ["first", "second", "third"] - list4: List = [Object(name="obj_1"), Object(name="obj_2")] - str_var1: str = "first" - str_var2: str = "second" - str_var3: str = "ThIrD" - str_var4: str = "a long string" - dict1: Dict[int, int] = {1: 2} - dict2: Dict[int, int] = {3: 4} - html_str: str = "
hello
" + int_var1: rx.Field[int] = rx.field(10) + int_var2: rx.Field[int] = rx.field(5) + int_var3: rx.Field[int] = rx.field(7) + float_var1: rx.Field[float] = rx.field(10.5) + float_var2: rx.Field[float] = rx.field(5.5) + list1: rx.Field[List[int]] = rx.field([1, 2]) + list2: rx.Field[List[int]] = rx.field([3, 4]) + list3: rx.Field[List[str]] = rx.field(["first", "second", "third"]) + list4: rx.Field[List[Object]] = rx.field( + [Object(name="obj_1"), Object(name="obj_2")] + ) + str_var1: rx.Field[str] = rx.field("first") + str_var2: rx.Field[str] = rx.field("second") + str_var3: rx.Field[str] = rx.field("ThIrD") + str_var4: rx.Field[str] = rx.field("a long string") + dict1: rx.Field[Dict[int, int]] = rx.field({1: 2}) + dict2: rx.Field[Dict[int, int]] = rx.field({3: 4}) + html_str: rx.Field[str] = rx.field("
hello
") app = rx.App(state=rx.State) diff --git a/tests/units/components/core/test_cond.py b/tests/units/components/core/test_cond.py index 71e19c7cc47..06b10831735 100644 --- a/tests/units/components/core/test_cond.py +++ b/tests/units/components/core/test_cond.py @@ -13,7 +13,7 @@ @pytest.fixture def cond_state(request): class CondState(BaseState): - value: request.param["value_type"] = request.param["value"] # noqa + value: request.param["value_type"] = request.param["value"] # pyright: ignore[reportInvalidTypeForm, reportUndefinedVariable] # noqa: F821 return CondState diff --git a/tests/units/components/core/test_match.py b/tests/units/components/core/test_match.py index 59111d1839c..83581a415c5 100644 --- a/tests/units/components/core/test_match.py +++ b/tests/units/components/core/test_match.py @@ -67,7 +67,7 @@ def test_match_vars(cases, expected): cases: The match cases. expected: The expected var full name. """ - match_comp = Match.create(MatchState.value, *cases) + match_comp = Match.create(MatchState.value, *cases) # pyright: ignore[reportCallIssue] assert isinstance(match_comp, Var) assert str(match_comp) == expected @@ -131,7 +131,7 @@ def test_match_default_not_last_arg(match_case): ValueError, match="rx.match should have tuples of cases and a default case as the last argument.", ): - Match.create(MatchState.value, *match_case) + Match.create(MatchState.value, *match_case) # pyright: ignore[reportCallIssue] @pytest.mark.parametrize( @@ -161,7 +161,7 @@ def test_match_case_tuple_elements(match_case): ValueError, match="A case tuple should have at least a match case element and a return value.", ): - Match.create(MatchState.value, *match_case) + Match.create(MatchState.value, *match_case) # pyright: ignore[reportCallIssue] @pytest.mark.parametrize( @@ -203,7 +203,7 @@ def test_match_different_return_types(cases: Tuple, error_msg: str): error_msg: Expected error message. """ with pytest.raises(MatchTypeError, match=error_msg): - Match.create(MatchState.value, *cases) + Match.create(MatchState.value, *cases) # pyright: ignore[reportCallIssue] @pytest.mark.parametrize( @@ -235,9 +235,9 @@ def test_match_multiple_default_cases(match_case): match_case: the cases to match. """ with pytest.raises(ValueError, match="rx.match can only have one default case."): - Match.create(MatchState.value, *match_case) + Match.create(MatchState.value, *match_case) # pyright: ignore[reportCallIssue] def test_match_no_cond(): with pytest.raises(ValueError): - _ = Match.create(None) + _ = Match.create(None) # pyright: ignore[reportCallIssue] diff --git a/tests/units/components/datadisplay/test_datatable.py b/tests/units/components/datadisplay/test_datatable.py index b3d31ea3234..79dd233bac1 100644 --- a/tests/units/components/datadisplay/test_datatable.py +++ b/tests/units/components/datadisplay/test_datatable.py @@ -13,7 +13,8 @@ pytest.param( { "data": pd.DataFrame( - [["foo", "bar"], ["foo1", "bar1"]], columns=["column1", "column2"] + [["foo", "bar"], ["foo1", "bar1"]], + columns=["column1", "column2"], # pyright: ignore [reportArgumentType] ) }, "data", @@ -113,7 +114,8 @@ def test_computed_var_without_annotation(fixture, request, err_msg, is_data_fram def test_serialize_dataframe(): """Test if dataframe is serialized correctly.""" df = pd.DataFrame( - [["foo", "bar"], ["foo1", "bar1"]], columns=["column1", "column2"] + [["foo", "bar"], ["foo1", "bar1"]], + columns=["column1", "column2"], # pyright: ignore [reportArgumentType] ) value = serialize(df) assert value == serialize_dataframe(df) diff --git a/tests/units/test_app.py b/tests/units/test_app.py index 48a4bdda199..b8ae06aae02 100644 --- a/tests/units/test_app.py +++ b/tests/units/test_app.py @@ -9,7 +9,7 @@ import uuid from contextlib import nullcontext as does_not_raise from pathlib import Path -from typing import Generator, List, Tuple, Type +from typing import Generator, List, Tuple, Type, cast from unittest.mock import AsyncMock import pytest @@ -33,7 +33,7 @@ from reflex.components.base.fragment import Fragment from reflex.components.core.cond import Cond from reflex.components.radix.themes.typography.text import Text -from reflex.event import Event +from reflex.event import Event, EventHandler from reflex.middleware import HydrateMiddleware from reflex.model import Model from reflex.state import ( @@ -917,7 +917,7 @@ def comp_dynamic(self) -> str: """ return self.dynamic - on_load_internal = OnLoadInternalState.on_load_internal.fn + on_load_internal = cast(EventHandler, OnLoadInternalState.on_load_internal).fn def test_dynamic_arg_shadow( @@ -1190,7 +1190,7 @@ async def test_process_events(mocker, token: str): pass assert (await app.state_manager.get_state(event.substate_token)).value == 5 - assert app._postprocess.call_count == 6 + assert getattr(app._postprocess, "call_count", None) == 6 if isinstance(app.state_manager, StateManagerRedis): await app.state_manager.close() @@ -1247,7 +1247,7 @@ def test_overlay_component( if exp_page_child is not None: assert len(page.children) == 3 - children_types = (type(child) for child in page.children) + children_types = [type(child) for child in page.children] assert exp_page_child in children_types else: assert len(page.children) == 2 diff --git a/tests/units/test_event.py b/tests/units/test_event.py index d7e993efaea..bc827078d9b 100644 --- a/tests/units/test_event.py +++ b/tests/units/test_event.py @@ -5,6 +5,7 @@ import reflex as rx from reflex.event import ( Event, + EventActionsMixin, EventChain, EventHandler, EventSpec, @@ -410,6 +411,7 @@ def test_event_actions(): def test_event_actions_on_state(): class EventActionState(BaseState): + @rx.event def handler(self): pass @@ -418,6 +420,7 @@ def handler(self): assert not handler.event_actions sp_handler = EventActionState.handler.stop_propagation + assert isinstance(sp_handler, EventActionsMixin) assert sp_handler.event_actions == {"stopPropagation": True} # should NOT affect other references to the handler assert not handler.event_actions diff --git a/tests/units/test_health_endpoint.py b/tests/units/test_health_endpoint.py index 6d12d79d6a4..abfa6cc62f0 100644 --- a/tests/units/test_health_endpoint.py +++ b/tests/units/test_health_endpoint.py @@ -122,9 +122,12 @@ async def test_health( # Call the async health function response = await health() - print(json.loads(response.body)) + body = response.body + assert isinstance(body, bytes) + + print(json.loads(body)) print(expected_status) # Verify the response content and status code assert response.status_code == expected_code - assert json.loads(response.body) == expected_status + assert json.loads(body) == expected_status diff --git a/tests/units/test_sqlalchemy.py b/tests/units/test_sqlalchemy.py index 23e315785a4..4434f5ee12c 100644 --- a/tests/units/test_sqlalchemy.py +++ b/tests/units/test_sqlalchemy.py @@ -59,7 +59,7 @@ class ModelBase(Base, MappedAsDataclass): id: Mapped[Optional[int]] = mapped_column(primary_key=True, default=None) # initial table - class AlembicThing(ModelBase): # pyright: ignore[reportGeneralTypeIssues] + class AlembicThing(ModelBase): # pyright: ignore[reportRedeclaration] t1: Mapped[str] = mapped_column(default="") with Model.get_db_engine().connect() as connection: @@ -78,7 +78,7 @@ class AlembicThing(ModelBase): # pyright: ignore[reportGeneralTypeIssues] model_registry.get_metadata().clear() # Create column t2, mark t1 as optional with default - class AlembicThing(ModelBase): # pyright: ignore[reportGeneralTypeIssues] + class AlembicThing(ModelBase): # pyright: ignore[reportRedeclaration] t1: Mapped[Optional[str]] = mapped_column(default="default") t2: Mapped[str] = mapped_column(default="bar") @@ -98,7 +98,7 @@ class AlembicThing(ModelBase): # pyright: ignore[reportGeneralTypeIssues] model_registry.get_metadata().clear() # Drop column t1 - class AlembicThing(ModelBase): # pyright: ignore[reportGeneralTypeIssues] + class AlembicThing(ModelBase): # pyright: ignore[reportRedeclaration] t2: Mapped[str] = mapped_column(default="bar") assert Model.migrate(autogenerate=True) @@ -133,7 +133,7 @@ class AlembicSecond(ModelBase): # drop table (AlembicSecond) model_registry.get_metadata().clear() - class AlembicThing(ModelBase): # pyright: ignore[reportGeneralTypeIssues] + class AlembicThing(ModelBase): # pyright: ignore[reportRedeclaration] t2: Mapped[str] = mapped_column(default="bar") assert Model.migrate(autogenerate=True) diff --git a/tests/units/test_state.py b/tests/units/test_state.py index 3898e4658dc..51b31e8e10a 100644 --- a/tests/units/test_state.py +++ b/tests/units/test_state.py @@ -17,6 +17,7 @@ Dict, List, Optional, + Sequence, Set, Tuple, Union, @@ -120,8 +121,8 @@ class TestState(BaseState): num2: float = 3.14 key: str map_key: str = "a" - array: List[float] = [1, 2, 3.14] - mapping: Dict[str, List[int]] = {"a": [1, 2, 3], "b": [4, 5, 6]} + array: rx.Field[List[float]] = rx.field([1, 2, 3.14]) + mapping: rx.Field[Dict[str, List[int]]] = rx.field({"a": [1, 2, 3], "b": [4, 5, 6]}) obj: Object = Object() complex: Dict[int, Object] = {1: Object(), 2: Object()} fig: Figure = Figure() @@ -1357,6 +1358,7 @@ def test_cached_var_depends_on_event_handler(use_partial: bool): class HandlerState(BaseState): x: int = 42 + @rx.event def handler(self): self.x = self.x + 1 @@ -1367,11 +1369,11 @@ def cached_x_side_effect(self) -> int: counter += 1 return counter + assert isinstance(HandlerState.handler, EventHandler) if use_partial: - HandlerState.handler = functools.partial(HandlerState.handler.fn) + partial_guy = functools.partial(HandlerState.handler.fn) + HandlerState.handler = partial_guy # pyright: ignore[reportAttributeAccessIssue] assert isinstance(HandlerState.handler, functools.partial) - else: - assert isinstance(HandlerState.handler, EventHandler) s = HandlerState() assert "cached_x_side_effect" in s._computed_var_dependencies["x"] @@ -2025,8 +2027,11 @@ async def test_state_proxy(grandchild_state: GrandchildState, mock_app: rx.App): # ensure state update was emitted assert mock_app.event_namespace is not None - mock_app.event_namespace.emit.assert_called_once() - mcall = mock_app.event_namespace.emit.mock_calls[0] + mock_app.event_namespace.emit.assert_called_once() # pyright: ignore[reportFunctionMemberAccess] + mock_calls = getattr(mock_app.event_namespace.emit, "mock_calls", None) + assert mock_calls is not None + assert isinstance(mock_calls, Sequence) + mcall = mock_calls[0] assert mcall.args[0] == str(SocketEvent.EVENT) assert mcall.args[1] == StateUpdate( delta={ @@ -2231,7 +2236,11 @@ async def test_background_task_no_block(mock_app: rx.App, token: str): assert mock_app.event_namespace is not None emit_mock = mock_app.event_namespace.emit - first_ws_message = emit_mock.mock_calls[0].args[1] + mock_calls = getattr(emit_mock, "mock_calls", None) + assert mock_calls is not None + assert isinstance(mock_calls, Sequence) + + first_ws_message = mock_calls[0].args[1] assert ( first_ws_message.delta[BackgroundTaskState.get_full_name()].pop("router") is not None @@ -2246,7 +2255,7 @@ async def test_background_task_no_block(mock_app: rx.App, token: str): events=[], final=True, ) - for call in emit_mock.mock_calls[1:5]: + for call in mock_calls[1:5]: assert call.args[1] == StateUpdate( delta={ BackgroundTaskState.get_full_name(): { @@ -2256,7 +2265,7 @@ async def test_background_task_no_block(mock_app: rx.App, token: str): events=[], final=True, ) - assert emit_mock.mock_calls[-2].args[1] == StateUpdate( + assert mock_calls[-2].args[1] == StateUpdate( delta={ BackgroundTaskState.get_full_name(): { "order": exp_order, @@ -2267,7 +2276,7 @@ async def test_background_task_no_block(mock_app: rx.App, token: str): events=[], final=True, ) - assert emit_mock.mock_calls[-1].args[1] == StateUpdate( + assert mock_calls[-1].args[1] == StateUpdate( delta={ BackgroundTaskState.get_full_name(): { "computed_order": exp_order, From 0798cb8f608ce13c828e45e99f91bfc92d34a4ed Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 14:31:49 -0800 Subject: [PATCH 50/86] fix version python 3.10 --- reflex/components/core/match.py | 8 ++++++-- reflex/event.py | 11 ++++++----- reflex/vars/number.py | 10 ++++++---- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/reflex/components/core/match.py b/reflex/components/core/match.py index a053b9abe2b..8b577a2610b 100644 --- a/reflex/components/core/match.py +++ b/reflex/components/core/match.py @@ -3,6 +3,8 @@ import textwrap from typing import Any, List, cast +from typing_extensions import Unpack + from reflex.components.base import Fragment from reflex.components.component import BaseComponent, Component, MemoizationLeaf from reflex.utils import types @@ -10,7 +12,7 @@ from reflex.vars.base import VAR_TYPE, Var from reflex.vars.number import MatchOperation -CASE_TYPE = tuple[*tuple[Any, ...], Var[VAR_TYPE] | VAR_TYPE] +CASE_TYPE = tuple[Unpack[tuple[Any, ...]], Var[VAR_TYPE] | VAR_TYPE] class Match(MemoizationLeaf): @@ -29,7 +31,9 @@ class Match(MemoizationLeaf): def create( cls, cond: Any, - *cases: *tuple[*tuple[CASE_TYPE[VAR_TYPE], ...], Var[VAR_TYPE] | VAR_TYPE], + *cases: Unpack[ + tuple[Unpack[tuple[CASE_TYPE[VAR_TYPE], ...]], Var[VAR_TYPE] | VAR_TYPE] + ], ) -> Var[VAR_TYPE]: """Create a Match Component. diff --git a/reflex/event.py b/reflex/event.py index dee33bced12..bfab52b7104 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -33,6 +33,7 @@ TypedDict, TypeVar, TypeVarTuple, + Unpack, deprecated, get_args, get_origin, @@ -627,10 +628,10 @@ def no_args_event_spec() -> Tuple[()]: Ts = TypeVarTuple("Ts") -class IdentityEventReturn(Generic[*Ts], Protocol): +class IdentityEventReturn(Generic[Unpack[Ts]], Protocol): """Protocol for an identity event return.""" - def __call__(self, *values: *Ts) -> tuple[*Ts]: + def __call__(self, *values: Unpack[Ts]) -> tuple[Unpack[Ts]]: """Return the input values. Args: @@ -656,13 +657,13 @@ def passthrough_event_spec( @overload def passthrough_event_spec( - *event_types: *tuple[Type[EVENT_T]], -) -> IdentityEventReturn[*tuple[Var[EVENT_T], ...]]: ... + *event_types: Unpack[tuple[Type[EVENT_T]]], +) -> IdentityEventReturn[Unpack[tuple[Var[EVENT_T], ...]]]: ... def passthrough_event_spec( # pyright: ignore[reportInconsistentOverload] *event_types: Type[EVENT_T], -) -> IdentityEventReturn[*tuple[Var[EVENT_T], ...]]: +) -> IdentityEventReturn[Unpack[tuple[Var[EVENT_T], ...]]]: """A helper function that returns the input event as output. Args: diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 1e85a1c7427..4e7beb3908e 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -19,6 +19,8 @@ overload, ) +from typing_extensions import Unpack + from reflex.constants.base import Dirs from reflex.utils.exceptions import PrimitiveUnserializableToJSON, VarTypeError from reflex.utils.imports import ImportDict, ImportVar @@ -1069,11 +1071,11 @@ def ternary_operation( return value -X = tuple[*tuple[Var, ...], str] - -TUPLE_ENDS_IN_VAR = tuple[*tuple[Var[Any], ...], Var[VAR_TYPE]] +TUPLE_ENDS_IN_VAR = tuple[Unpack[tuple[Var[Any], ...]], Var[VAR_TYPE]] -TUPLE_ENDS_IN_VAR_RELAXED = tuple[*tuple[Var[Any] | Any, ...], Var[VAR_TYPE] | VAR_TYPE] +TUPLE_ENDS_IN_VAR_RELAXED = tuple[ + Unpack[tuple[Var[Any] | Any, ...]], Var[VAR_TYPE] | VAR_TYPE +] @dataclasses.dataclass( From a7230f1f4549a6474453bac26e4d1208a600431f Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 15:07:32 -0800 Subject: [PATCH 51/86] resolve pyright issues --- reflex/vars/function.py | 1214 ++++++++++++++++++++++++++++++++++----- 1 file changed, 1080 insertions(+), 134 deletions(-) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index c3fe8f75f23..121e6f6c445 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -6,8 +6,10 @@ import inspect import sys from typing import ( + TYPE_CHECKING, Any, Callable, + Mapping, NoReturn, Optional, Sequence, @@ -35,6 +37,11 @@ unwrap_reflex_callalbe, ) +if TYPE_CHECKING: + from .number import BooleanVar, NumberVar + from .object import ObjectVar + from .sequence import ArrayVar, StringVar + P = ParamSpec("P") R = TypeVar("R") R2 = TypeVar("R2") @@ -51,6 +58,9 @@ "OTHER_CALLABLE_TYPE", bound=ReflexCallable, infer_variance=True ) +MAPPING_TYPE = TypeVar("MAPPING_TYPE", bound=Mapping, covariant=True) +SEQUENCE_TYPE = TypeVar("SEQUENCE_TYPE", bound=Sequence, covariant=True) + def type_is_reflex_callable(type_: Any) -> bool: """Check if a type is a ReflexCallable. @@ -58,245 +68,1125 @@ def type_is_reflex_callable(type_: Any) -> bool: Args: type_: The type to check. - Returns: - True if the type is a ReflexCallable. - """ - return type_ is ReflexCallable or get_origin(type_) is ReflexCallable + Returns: + True if the type is a ReflexCallable. + """ + return type_ is ReflexCallable or get_origin(type_) is ReflexCallable + + +K = TypeVar("K", covariant=True) +V = TypeVar("V", covariant=True) +T = TypeVar("T", covariant=True) + + +class FunctionVar( + Var[CALLABLE_TYPE], + default_type=ReflexCallable[Any, Any], + is_subclass=type_is_reflex_callable, +): + """Base class for immutable function vars.""" + + @overload + def partial(self) -> FunctionVar[CALLABLE_TYPE]: ... + + @overload + def partial( + self: FunctionVar[ReflexCallable[Concatenate[VarWithDefault[V1], P], R]] + | FunctionVar[ReflexCallable[Concatenate[V1, P], R]], + arg1: Union[V1, Var[V1]], + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + @overload + def partial( + self: FunctionVar[ + ReflexCallable[Concatenate[VarWithDefault[V1], VarWithDefault[V2], P], R] + ] + | FunctionVar[ReflexCallable[Concatenate[V1, VarWithDefault[V2], P], R]] + | FunctionVar[ReflexCallable[Concatenate[V1, V2, P], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + @overload + def partial( + self: FunctionVar[ + ReflexCallable[ + Concatenate[ + VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3], P + ], + R, + ] + ] + | FunctionVar[ + ReflexCallable[ + Concatenate[V1, VarWithDefault[V2], VarWithDefault[V3], P], R + ] + ] + | FunctionVar[ReflexCallable[Concatenate[V1, V2, VarWithDefault[V3], P], R]] + | FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, P], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + @overload + def partial( + self: FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, V4, P], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4]], + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + @overload + def partial( + self: FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, V4, V5, P], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4]], + arg5: Union[V5, Var[V5]], + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + @overload + def partial( + self: FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, V4, V5, V6, P], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4]], + arg5: Union[V5, Var[V5]], + arg6: Union[V6, Var[V6]], + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + @overload + def partial( + self: FunctionVar[ReflexCallable[P, R]], *args: Var | Any + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + @overload + def partial(self, *args: Var | Any) -> FunctionVar: ... + + def partial(self, *args: Var | Any) -> FunctionVar: # type: ignore + """Partially apply the function with the given arguments. + + Args: + *args: The arguments to partially apply the function with. + + Returns: + The partially applied function. + """ + if not args: + return self + + args = tuple(map(LiteralVar.create, args)) + + remaining_validators = self._pre_check(*args) + + partial_types, type_computer = self._partial_type(*args) + + if self.__call__ is self.partial: + # if the default behavior is partial, we should return a new partial function + return ArgsFunctionOperationBuilder.create( + (), + VarOperationCall.create( + self, + *args, + Var(_js_expr="...args"), + _var_type=self._return_type(*args), + ), + rest="args", + validators=remaining_validators, + type_computer=type_computer, + _var_type=partial_types, + ) + return ArgsFunctionOperation.create( + (), + VarOperationCall.create( + self, *args, Var(_js_expr="...args"), _var_type=self._return_type(*args) + ), + rest="args", + validators=remaining_validators, + type_computer=type_computer, + _var_type=partial_types, + ) + + # THIS CODE IS GENERATED BY `_generate_overloads_for_function_var_call` function below. + + @overload + def call( + self: FunctionVar[ReflexCallable[[], bool]], + ) -> BooleanVar: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[], int]], + ) -> NumberVar[int]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[], float]], + ) -> NumberVar[float]: ... + + @overload + def call( # pyright: ignore[reportOverlappingOverload] + self: FunctionVar[ReflexCallable[[], str]], + ) -> StringVar[str]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[], SEQUENCE_TYPE]], + ) -> ArrayVar[SEQUENCE_TYPE]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[], MAPPING_TYPE]], + ) -> ObjectVar[MAPPING_TYPE]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[], R]], + ) -> Var[R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[VarWithDefault[V1]], bool]], + arg1: Union[V1, Var[V1], Unset] = Unset(), + ) -> BooleanVar: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[VarWithDefault[V1]], int]], + arg1: Union[V1, Var[V1], Unset] = Unset(), + ) -> NumberVar[int]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[VarWithDefault[V1]], float]], + arg1: Union[V1, Var[V1], Unset] = Unset(), + ) -> NumberVar[float]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[VarWithDefault[V1]], str]], + arg1: Union[V1, Var[V1], Unset] = Unset(), + ) -> StringVar[str]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[VarWithDefault[V1]], SEQUENCE_TYPE]], + arg1: Union[V1, Var[V1], Unset] = Unset(), + ) -> ArrayVar[SEQUENCE_TYPE]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[VarWithDefault[V1]], MAPPING_TYPE]], + arg1: Union[V1, Var[V1], Unset] = Unset(), + ) -> ObjectVar[MAPPING_TYPE]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[VarWithDefault[V1]], R]], + arg1: Union[V1, Var[V1], Unset] = Unset(), + ) -> Var[R]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[VarWithDefault[V1], VarWithDefault[V2]], bool] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> BooleanVar: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[VarWithDefault[V1], VarWithDefault[V2]], int] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> NumberVar[int]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[VarWithDefault[V1], VarWithDefault[V2]], float] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> NumberVar[float]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[VarWithDefault[V1], VarWithDefault[V2]], str] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> StringVar[str]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[VarWithDefault[V1], VarWithDefault[V2]], SEQUENCE_TYPE] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> ArrayVar[SEQUENCE_TYPE]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[VarWithDefault[V1], VarWithDefault[V2]], MAPPING_TYPE] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> ObjectVar[MAPPING_TYPE]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[VarWithDefault[V1], VarWithDefault[V2]], R]], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> Var[R]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3]], bool + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> BooleanVar: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3]], int + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> NumberVar[int]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3]], float + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> NumberVar[float]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3]], str + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> StringVar[str]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3]], + SEQUENCE_TYPE, + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> ArrayVar[SEQUENCE_TYPE]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3]], + MAPPING_TYPE, + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> ObjectVar[MAPPING_TYPE]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3]], R + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> Var[R]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [ + VarWithDefault[V1], + VarWithDefault[V2], + VarWithDefault[V3], + VarWithDefault[V4], + ], + bool, + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> BooleanVar: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [ + VarWithDefault[V1], + VarWithDefault[V2], + VarWithDefault[V3], + VarWithDefault[V4], + ], + int, + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> NumberVar[int]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [ + VarWithDefault[V1], + VarWithDefault[V2], + VarWithDefault[V3], + VarWithDefault[V4], + ], + float, + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> NumberVar[float]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [ + VarWithDefault[V1], + VarWithDefault[V2], + VarWithDefault[V3], + VarWithDefault[V4], + ], + str, + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> StringVar[str]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [ + VarWithDefault[V1], + VarWithDefault[V2], + VarWithDefault[V3], + VarWithDefault[V4], + ], + SEQUENCE_TYPE, + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> ArrayVar[SEQUENCE_TYPE]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [ + VarWithDefault[V1], + VarWithDefault[V2], + VarWithDefault[V3], + VarWithDefault[V4], + ], + MAPPING_TYPE, + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> ObjectVar[MAPPING_TYPE]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [ + VarWithDefault[V1], + VarWithDefault[V2], + VarWithDefault[V3], + VarWithDefault[V4], + ], + R, + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> Var[R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1], bool]], arg1: Union[V1, Var[V1]] + ) -> BooleanVar: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1], int]], arg1: Union[V1, Var[V1]] + ) -> NumberVar[int]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1], float]], arg1: Union[V1, Var[V1]] + ) -> NumberVar[float]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1], str]], arg1: Union[V1, Var[V1]] + ) -> StringVar[str]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1], SEQUENCE_TYPE]], arg1: Union[V1, Var[V1]] + ) -> ArrayVar[SEQUENCE_TYPE]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1], MAPPING_TYPE]], arg1: Union[V1, Var[V1]] + ) -> ObjectVar[MAPPING_TYPE]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1], R]], arg1: Union[V1, Var[V1]] + ) -> Var[R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, VarWithDefault[V2]], bool]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> BooleanVar: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, VarWithDefault[V2]], int]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> NumberVar[int]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, VarWithDefault[V2]], float]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> NumberVar[float]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, VarWithDefault[V2]], str]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> StringVar[str]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, VarWithDefault[V2]], SEQUENCE_TYPE]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> ArrayVar[SEQUENCE_TYPE]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, VarWithDefault[V2]], MAPPING_TYPE]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> ObjectVar[MAPPING_TYPE]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, VarWithDefault[V2]], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> Var[R]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[V1, VarWithDefault[V2], VarWithDefault[V3]], bool] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> BooleanVar: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[V1, VarWithDefault[V2], VarWithDefault[V3]], int] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> NumberVar[int]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[V1, VarWithDefault[V2], VarWithDefault[V3]], float] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> NumberVar[float]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[V1, VarWithDefault[V2], VarWithDefault[V3]], str] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> StringVar[str]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[V1, VarWithDefault[V2], VarWithDefault[V3]], SEQUENCE_TYPE] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> ArrayVar[SEQUENCE_TYPE]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[V1, VarWithDefault[V2], VarWithDefault[V3]], MAPPING_TYPE] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> ObjectVar[MAPPING_TYPE]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[V1, VarWithDefault[V2], VarWithDefault[V3]], R] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> Var[R]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [V1, VarWithDefault[V2], VarWithDefault[V3], VarWithDefault[V4]], bool + ] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> BooleanVar: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [V1, VarWithDefault[V2], VarWithDefault[V3], VarWithDefault[V4]], int + ] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> NumberVar[int]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [V1, VarWithDefault[V2], VarWithDefault[V3], VarWithDefault[V4]], float + ] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> NumberVar[float]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [V1, VarWithDefault[V2], VarWithDefault[V3], VarWithDefault[V4]], str + ] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> StringVar[str]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [V1, VarWithDefault[V2], VarWithDefault[V3], VarWithDefault[V4]], + SEQUENCE_TYPE, + ] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> ArrayVar[SEQUENCE_TYPE]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [V1, VarWithDefault[V2], VarWithDefault[V3], VarWithDefault[V4]], + MAPPING_TYPE, + ] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> ObjectVar[MAPPING_TYPE]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [V1, VarWithDefault[V2], VarWithDefault[V3], VarWithDefault[V4]], R + ] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> Var[R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2], bool]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + ) -> BooleanVar: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2], int]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + ) -> NumberVar[int]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2], float]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + ) -> NumberVar[float]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2], str]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + ) -> StringVar[str]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2], SEQUENCE_TYPE]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + ) -> ArrayVar[SEQUENCE_TYPE]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2], MAPPING_TYPE]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + ) -> ObjectVar[MAPPING_TYPE]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + ) -> Var[R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, VarWithDefault[V3]], bool]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> BooleanVar: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, VarWithDefault[V3]], int]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> NumberVar[int]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, VarWithDefault[V3]], float]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> NumberVar[float]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, VarWithDefault[V3]], str]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> StringVar[str]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, VarWithDefault[V3]], SEQUENCE_TYPE]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> ArrayVar[SEQUENCE_TYPE]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, VarWithDefault[V3]], MAPPING_TYPE]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> ObjectVar[MAPPING_TYPE]: ... + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, VarWithDefault[V3]], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> Var[R]: ... -class FunctionVar( - Var[CALLABLE_TYPE], - default_type=ReflexCallable[Any, Any], - is_subclass=type_is_reflex_callable, -): - """Base class for immutable function vars.""" + @overload + def call( + self: FunctionVar[ + ReflexCallable[[V1, V2, VarWithDefault[V3], VarWithDefault[V4]], bool] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> BooleanVar: ... @overload - def partial(self) -> FunctionVar[CALLABLE_TYPE]: ... + def call( + self: FunctionVar[ + ReflexCallable[[V1, V2, VarWithDefault[V3], VarWithDefault[V4]], int] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> NumberVar[int]: ... @overload - def partial( - self: FunctionVar[ReflexCallable[Concatenate[VarWithDefault[V1], P], R]] - | FunctionVar[ReflexCallable[Concatenate[V1, P], R]], + def call( + self: FunctionVar[ + ReflexCallable[[V1, V2, VarWithDefault[V3], VarWithDefault[V4]], float] + ], arg1: Union[V1, Var[V1]], - ) -> FunctionVar[ReflexCallable[P, R]]: ... + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> NumberVar[float]: ... @overload - def partial( + def call( self: FunctionVar[ - ReflexCallable[Concatenate[VarWithDefault[V1], VarWithDefault[V2], P], R] - ] - | FunctionVar[ReflexCallable[Concatenate[V1, VarWithDefault[V2], P], R]] - | FunctionVar[ReflexCallable[Concatenate[V1, V2, P], R]], + ReflexCallable[[V1, V2, VarWithDefault[V3], VarWithDefault[V4]], str] + ], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], - ) -> FunctionVar[ReflexCallable[P, R]]: ... + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> StringVar[str]: ... @overload - def partial( + def call( self: FunctionVar[ ReflexCallable[ - Concatenate[ - VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3], P - ], - R, + [V1, V2, VarWithDefault[V3], VarWithDefault[V4]], SEQUENCE_TYPE ] - ] - | FunctionVar[ + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> ArrayVar[SEQUENCE_TYPE]: ... + + @overload + def call( + self: FunctionVar[ ReflexCallable[ - Concatenate[V1, VarWithDefault[V2], VarWithDefault[V3], P], R + [V1, V2, VarWithDefault[V3], VarWithDefault[V4]], MAPPING_TYPE ] - ] - | FunctionVar[ReflexCallable[Concatenate[V1, V2, VarWithDefault[V3], P], R]] - | FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, P], R]], + ], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], - arg3: Union[V3, Var[V3]], - ) -> FunctionVar[ReflexCallable[P, R]]: ... + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> ObjectVar[MAPPING_TYPE]: ... @overload - def partial( - self: FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, V4, P], R]], + def call( + self: FunctionVar[ + ReflexCallable[[V1, V2, VarWithDefault[V3], VarWithDefault[V4]], R] + ], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], - arg3: Union[V3, Var[V3]], - arg4: Union[V4, Var[V4]], - ) -> FunctionVar[ReflexCallable[P, R]]: ... + arg3: Union[V3, Var[V3], Unset] = Unset(), + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> Var[R]: ... @overload - def partial( - self: FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, V4, V5, P], R]], + def call( + self: FunctionVar[ReflexCallable[[V1, V2, V3], bool]], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], arg3: Union[V3, Var[V3]], - arg4: Union[V4, Var[V4]], - arg5: Union[V5, Var[V5]], - ) -> FunctionVar[ReflexCallable[P, R]]: ... + ) -> BooleanVar: ... @overload - def partial( - self: FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, V4, V5, V6, P], R]], + def call( + self: FunctionVar[ReflexCallable[[V1, V2, V3], int]], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], arg3: Union[V3, Var[V3]], - arg4: Union[V4, Var[V4]], - arg5: Union[V5, Var[V5]], - arg6: Union[V6, Var[V6]], - ) -> FunctionVar[ReflexCallable[P, R]]: ... + ) -> NumberVar[int]: ... @overload - def partial( - self: FunctionVar[ReflexCallable[P, R]], *args: Var | Any - ) -> FunctionVar[ReflexCallable[P, R]]: ... + def call( + self: FunctionVar[ReflexCallable[[V1, V2, V3], float]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + ) -> NumberVar[float]: ... @overload - def partial(self, *args: Var | Any) -> FunctionVar: ... - - def partial(self, *args: Var | Any) -> FunctionVar: # type: ignore - """Partially apply the function with the given arguments. - - Args: - *args: The arguments to partially apply the function with. - - Returns: - The partially applied function. - """ - if not args: - return self + def call( + self: FunctionVar[ReflexCallable[[V1, V2, V3], str]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + ) -> StringVar[str]: ... - args = tuple(map(LiteralVar.create, args)) + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, V3], SEQUENCE_TYPE]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + ) -> ArrayVar[SEQUENCE_TYPE]: ... - remaining_validators = self._pre_check(*args) + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, V3], MAPPING_TYPE]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + ) -> ObjectVar[MAPPING_TYPE]: ... - partial_types, type_computer = self._partial_type(*args) + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, V3], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + ) -> Var[R]: ... - if self.__call__ is self.partial: - # if the default behavior is partial, we should return a new partial function - return ArgsFunctionOperationBuilder.create( - (), - VarOperationCall.create( - self, - *args, - Var(_js_expr="...args"), - _var_type=self._return_type(*args), - ), - rest="args", - validators=remaining_validators, - type_computer=type_computer, - _var_type=partial_types, - ) - return ArgsFunctionOperation.create( - (), - VarOperationCall.create( - self, *args, Var(_js_expr="...args"), _var_type=self._return_type(*args) - ), - rest="args", - validators=remaining_validators, - type_computer=type_computer, - _var_type=partial_types, - ) + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, V3, VarWithDefault[V4]], bool]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> BooleanVar: ... @overload - def call(self: FunctionVar[ReflexCallable[[], R]]) -> Var[R]: ... + def call( + self: FunctionVar[ReflexCallable[[V1, V2, V3, VarWithDefault[V4]], int]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> NumberVar[int]: ... @overload def call( - self: FunctionVar[ReflexCallable[[VarWithDefault[V1]], R]], - arg1: Union[V1, Var[V1], Unset] = Unset(), - ) -> Var[R]: ... + self: FunctionVar[ReflexCallable[[V1, V2, V3, VarWithDefault[V4]], float]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> NumberVar[float]: ... @overload def call( - self: FunctionVar[ReflexCallable[[VarWithDefault[V1], VarWithDefault[V2]], R]], - arg1: Union[V1, Var[V1], Unset] = Unset(), - arg2: Union[V2, Var[V2], Unset] = Unset(), - ) -> Var[R]: ... + self: FunctionVar[ReflexCallable[[V1, V2, V3, VarWithDefault[V4]], str]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> StringVar[str]: ... @overload def call( self: FunctionVar[ - ReflexCallable[ - [VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3]], R - ] + ReflexCallable[[V1, V2, V3, VarWithDefault[V4]], SEQUENCE_TYPE] ], - arg1: Union[V1, Var[V1], Unset] = Unset(), - arg2: Union[V2, Var[V2], Unset] = Unset(), - arg3: Union[V3, Var[V3], Unset] = Unset(), - ) -> Var[R]: ... + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> ArrayVar[SEQUENCE_TYPE]: ... @overload def call( - self: FunctionVar[ReflexCallable[[V1], R]], arg1: Union[V1, Var[V1]] - ) -> Var[R]: ... + self: FunctionVar[ + ReflexCallable[[V1, V2, V3, VarWithDefault[V4]], MAPPING_TYPE] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4], Unset] = Unset(), + ) -> ObjectVar[MAPPING_TYPE]: ... @overload def call( - self: FunctionVar[ReflexCallable[[V1, VarWithDefault[V2]], R]], + self: FunctionVar[ReflexCallable[[V1, V2, V3, VarWithDefault[V4]], R]], arg1: Union[V1, Var[V1]], - arg2: Union[V2, Var[V2], Unset] = Unset(), + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4], Unset] = Unset(), ) -> Var[R]: ... @overload def call( - self: FunctionVar[ - ReflexCallable[[V1, VarWithDefault[V2], VarWithDefault[V3]], R] - ], + self: FunctionVar[ReflexCallable[[V1, V2, V3, V4], bool]], arg1: Union[V1, Var[V1]], - arg2: Union[V2, Var[V2], Unset] = Unset(), - arg3: Union[V3, Var[V3], Unset] = Unset(), - ) -> Var[R]: ... + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4]], + ) -> BooleanVar: ... @overload def call( - self: FunctionVar[ReflexCallable[[V1, V2], R]], + self: FunctionVar[ReflexCallable[[V1, V2, V3, V4], int]], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], - ) -> VarOperationCall[[V1, V2], R]: ... + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4]], + ) -> NumberVar[int]: ... @overload def call( - self: FunctionVar[ReflexCallable[[V1, V2, VarWithDefault[V3]], R]], + self: FunctionVar[ReflexCallable[[V1, V2, V3, V4], float]], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], - arg3: Union[V3, Var[V3], Unset] = Unset(), - ) -> Var[R]: ... + arg3: Union[V3, Var[V3]], + arg4: Union[V4, Var[V4]], + ) -> NumberVar[float]: ... @overload def call( - self: FunctionVar[ReflexCallable[[V1, V2, V3], R]], + self: FunctionVar[ReflexCallable[[V1, V2, V3, V4], str]], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], arg3: Union[V3, Var[V3]], - ) -> Var[R]: ... + arg4: Union[V4, Var[V4]], + ) -> StringVar[str]: ... @overload def call( - self: FunctionVar[ReflexCallable[[V1, V2, V3, V4], R]], + self: FunctionVar[ReflexCallable[[V1, V2, V3, V4], SEQUENCE_TYPE]], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], arg3: Union[V3, Var[V3]], arg4: Union[V4, Var[V4]], - ) -> Var[R]: ... + ) -> ArrayVar[SEQUENCE_TYPE]: ... @overload def call( - self: FunctionVar[ReflexCallable[[V1, V2, V3, V4, V5], R]], + self: FunctionVar[ReflexCallable[[V1, V2, V3, V4], MAPPING_TYPE]], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], arg3: Union[V3, Var[V3]], arg4: Union[V4, Var[V4]], - arg5: Union[V5, Var[V5]], - ) -> Var[R]: ... + ) -> ObjectVar[MAPPING_TYPE]: ... @overload def call( - self: FunctionVar[ReflexCallable[[V1, V2, V3, V4, V5, V6], R]], + self: FunctionVar[ReflexCallable[[V1, V2, V3, V4], R]], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], arg3: Union[V3, Var[V3]], arg4: Union[V4, Var[V4]], - arg5: Union[V5, Var[V5]], - arg6: Union[V6, Var[V6]], ) -> Var[R]: ... # Capture Any to allow for arbitrary number of arguments @@ -866,3 +1756,59 @@ def create( "((__to_string) => __to_string.toString())", _var_type=ReflexCallable[Any, str], ) + + +def _generate_overloads_for_function_var_call(maximum_args: int = 4) -> str: + """Generate overloads for the function var call method. + + Args: + maximum_args: The maximum number of arguments to generate overloads for. + + Returns: + The generated overloads. + """ + overloads = [] + return_type_mapping = { + "bool": "BooleanVar", + "int": "NumberVar[int]", + "float": "NumberVar[float]", + "str": "StringVar[str]", + "SEQUENCE_TYPE": "ArrayVar[SEQUENCE_TYPE]", + "MAPPING_TYPE": "ObjectVar[MAPPING_TYPE]", + "R": "Var[R]", + } + for number_of_required_args in range(maximum_args + 1): + for number_of_optional_args in range( + maximum_args + 1 - number_of_required_args + ): + for return_type, return_type_var in return_type_mapping.items(): + required_args = [ + f"arg{j + 1}: Union[V" f"{j + 1}, Var[V{j + 1}]]" + for j in range(number_of_required_args) + ] + optional_args = [ + f"arg{j + 1}: Union[V" f"{j + 1}, Var[V{j + 1}], Unset] = Unset()" + for j in range( + number_of_required_args, + number_of_required_args + number_of_optional_args, + ) + ] + required_params = [f"V{j + 1}" for j in range(number_of_required_args)] + optional_params = [ + f"VarWithDefault[V{j + 1}]" + for j in range( + number_of_required_args, + number_of_required_args + number_of_optional_args, + ) + ] + function_type_hint = f"""FunctionVar[ReflexCallable[[{", ".join(required_params + optional_params)}], {return_type}]]""" + overloads.append( + f""" + @overload + def call( + self: {function_type_hint}, + {",\n ".join(required_args + optional_args)} + ) -> {return_type_var}: ... + """ + ) + return "\n".join(overloads) From 0e539a208cda30c0e41909be7fea3830bd296a9d Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 15:08:58 -0800 Subject: [PATCH 52/86] fix syntax for soy 3.10 --- reflex/vars/function.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index 121e6f6c445..652038fbe7a 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -1802,12 +1802,13 @@ def _generate_overloads_for_function_var_call(maximum_args: int = 4) -> str: ) ] function_type_hint = f"""FunctionVar[ReflexCallable[[{", ".join(required_params + optional_params)}], {return_type}]]""" + NEWLINE = "\n" overloads.append( f""" @overload def call( self: {function_type_hint}, - {",\n ".join(required_args + optional_args)} + {"," + NEWLINE + " ".join(required_args + optional_args)} ) -> {return_type_var}: ... """ ) From c6f05bb320f8a60b0e4c3b72a73cf3522aca6ecc Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 15:15:09 -0800 Subject: [PATCH 53/86] fix lineno --- reflex/utils/pyi_generator.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/reflex/utils/pyi_generator.py b/reflex/utils/pyi_generator.py index 52a3a9fff19..c1c9bc35244 100644 --- a/reflex/utils/pyi_generator.py +++ b/reflex/utils/pyi_generator.py @@ -643,13 +643,7 @@ def figure_out_return_type(annotation: Any): ), ], returns=ast.Constant(value=clz.__name__), - **( - { - "lineno": node.lineno, - } - if node is not None - else {} - ), + lineno=node.lineno if node is not None else None, # pyright: ignore[reportArgumentType] ) return definition @@ -703,13 +697,7 @@ def _generate_staticmethod_call_functiondef( type_hint_globals, ) ), - **( - { - "lineno": node.lineno, - } - if node is not None - else {} - ), + lineno=node.lineno if node is not None else None, # pyright: ignore[reportArgumentType] ) return definition From 9a987caf767f0c27cb2a2695725090c719665cae Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 15:21:33 -0800 Subject: [PATCH 54/86] dang it darglint --- reflex/components/base/bare.py | 3 +++ reflex/components/core/match.py | 6 ------ reflex/utils/pyi_generator.py | 3 +++ reflex/utils/types.py | 3 +++ reflex/vars/base.py | 8 -------- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index 6690a5cc105..4b601544f1c 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -64,6 +64,9 @@ def _get_all_hooks(self) -> dict[str, VarData | None]: def _get_all_imports(self, collapse: bool = False) -> ParsedImportDict: """Include the imports for the component. + Args: + collapse: Whether to collapse the imports. + Returns: The imports for the component. """ diff --git a/reflex/components/core/match.py b/reflex/components/core/match.py index 8b577a2610b..e697be7b220 100644 --- a/reflex/components/core/match.py +++ b/reflex/components/core/match.py @@ -97,9 +97,6 @@ def _process_match_cases(cls, cases: tuple[CASE_TYPE[VAR_TYPE], ...]): Args: cases: The match cases. - Returns: - The processed match cases. - Raises: ValueError: If the default case is not the last case or the tuple elements are less than 2. """ @@ -165,9 +162,6 @@ def _create_match_cond_var_or_component( Returns: The match component wrapped in a fragment or the match var. - - Raises: - ValueError: If the return types are not vars when creating a match var for Var types. """ return MatchOperation.create(match_cond_var, match_cases, default) diff --git a/reflex/utils/pyi_generator.py b/reflex/utils/pyi_generator.py index c1c9bc35244..e6f40c2b56e 100644 --- a/reflex/utils/pyi_generator.py +++ b/reflex/utils/pyi_generator.py @@ -718,6 +718,9 @@ def _generate_namespace_call_functiondef( Returns: The create functiondef node for the ast. + + Raises: + TypeError: If the __call__ method does not have a __func__. """ # add the imports needed by get_type_hint later type_hint_globals.update( diff --git a/reflex/utils/types.py b/reflex/utils/types.py index e323c5441ef..a27be80c51f 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -848,6 +848,9 @@ def safe_issubclass(cls: Any, class_or_tuple: Any, /) -> bool: Returns: Whether the class is a subclass of the other class or tuple of classes. + + Raises: + TypeError: If the arguments are invalid. """ try: return issubclass(cls, class_or_tuple) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 859bdcdc4ff..521bdc99704 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -241,16 +241,8 @@ def old_school_imports(self) -> ImportDict: def merge(*all: VarData | None) -> VarData | None: """Merge multiple var data objects. - Args: - *all: The var data objects to merge. - - Raises: - ReflexError: If trying to merge VarData with different positions. - Returns: The merged var data object. - - # noqa: DAR102 *all """ all_var_datas = list(filter(None, all)) From f4aa122950044bb2f583b05a5dc40f51002b4a5b Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 15:24:32 -0800 Subject: [PATCH 55/86] don't delete thomas code that's rude --- reflex/vars/base.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 521bdc99704..b0f27bd6439 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -51,7 +51,7 @@ from reflex import constants from reflex.base import Base from reflex.constants.compiler import Hooks -from reflex.utils import console, imports, serializers, types +from reflex.utils import console, exceptions, imports, serializers, types from reflex.utils.exceptions import ( VarAttributeError, VarDependencyError, @@ -243,6 +243,9 @@ def merge(*all: VarData | None) -> VarData | None: Returns: The merged var data object. + + Raises: + ReflexError: If the positions of the var data objects are different. """ all_var_datas = list(filter(None, all)) @@ -271,15 +274,36 @@ def merge(*all: VarData | None) -> VarData | None: *(var_data.imports for var_data in all_var_datas) ) + deps = [dep for var_data in all_var_datas for dep in var_data.deps] + + positions = list( + { + var_data.position + for var_data in all_var_datas + if var_data.position is not None + } + ) + components = tuple( component for var_data in all_var_datas for component in var_data.components ) + if positions: + if len(positions) > 1: + raise exceptions.ReflexError( + f"Cannot merge var data with different positions: {positions}" + ) + position = positions[0] + else: + position = None + return VarData( state=state, field_name=field_name, imports=_imports, hooks=hooks, + deps=deps, + position=position, components=components, ) From fa6c12e8b33c33dc9aeecb94943c61fd67763b80 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 15:32:34 -0800 Subject: [PATCH 56/86] remove unnecessary comment --- reflex/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/base.py b/reflex/base.py index 1f0bbe00df3..1879d76d859 100644 --- a/reflex/base.py +++ b/reflex/base.py @@ -44,7 +44,7 @@ def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None from reflex.vars import Var -class Base(BaseModel): # pyright: ignore [reportUnboundVariable] +class Base(BaseModel): """The base class subclassed by all Reflex classes. This class wraps Pydantic and provides common methods such as From aadd8b56bf728ad877c6ed0a7ed502e637a5bfcb Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 15:51:11 -0800 Subject: [PATCH 57/86] do silly things --- tests/integration/test_lifespan.py | 104 ++++++++++++++++++----------- 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/tests/integration/test_lifespan.py b/tests/integration/test_lifespan.py index d05466bb479..578d0b66447 100644 --- a/tests/integration/test_lifespan.py +++ b/tests/integration/test_lifespan.py @@ -17,45 +17,68 @@ def LifespanApp(): import reflex as rx - lifespan_task_global = 0 - lifespan_context_global = 0 - - @asynccontextmanager - async def lifespan_context(app, inc: int = 1): - nonlocal lifespan_context_global - print(f"Lifespan context entered: {app}.") - lifespan_context_global += inc - try: - yield - finally: - print("Lifespan context exited.") - lifespan_context_global += inc + def create_tasks(): + lifespan_task_global = 0 + lifespan_context_global = 0 + + def lifespan_context_global_getter(): + return lifespan_context_global - async def lifespan_task(inc: int = 1): - nonlocal lifespan_task_global - print("Lifespan global started.") - try: - while True: - lifespan_task_global += inc - await asyncio.sleep(0.1) - except asyncio.CancelledError as ce: - print(f"Lifespan global cancelled: {ce}.") - lifespan_task_global = 0 - - class LifespanState(rx.State): - interval: int = 100 - - @rx.var(cache=False) - def task_global(self) -> int: + def lifespan_task_global_getter(): return lifespan_task_global - @rx.var(cache=False) - def context_global(self) -> int: - return lifespan_context_global + @asynccontextmanager + async def lifespan_context(app, inc: int = 1): + nonlocal lifespan_context_global + print(f"Lifespan context entered: {app}.") + lifespan_context_global += inc + try: + yield + finally: + print("Lifespan context exited.") + lifespan_context_global += inc + + async def lifespan_task(inc: int = 1): + nonlocal lifespan_task_global + print("Lifespan global started.") + try: + while True: + lifespan_task_global += inc + await asyncio.sleep(0.1) + except asyncio.CancelledError as ce: + print(f"Lifespan global cancelled: {ce}.") + lifespan_task_global = 0 + + class LifespanState(rx.State): + interval: int = 100 + + @rx.var(cache=False) + def task_global(self) -> int: + return lifespan_task_global + + @rx.var(cache=False) + def context_global(self) -> int: + return lifespan_context_global + + @rx.event + def tick(self, date): + pass + + return ( + lifespan_task, + lifespan_context, + LifespanState, + lifespan_task_global_getter, + lifespan_context_global_getter, + ) - @rx.event - def tick(self, date): - pass + ( + lifespan_task, + lifespan_context, + LifespanState, + lifespan_task_global_getter, + lifespan_context_global_getter, + ) = create_tasks() def index(): return rx.vstack( @@ -113,13 +136,16 @@ async def test_lifespan(lifespan_app: AppHarness): task_global = driver.find_element(By.ID, "task_global") assert context_global.text == "2" - assert lifespan_app.app_module.lifespan_context_global == 2 # type: ignore + assert lifespan_app.app_module.lifespan_context_global_getter() == 2 # type: ignore original_task_global_text = task_global.text original_task_global_value = int(original_task_global_text) lifespan_app.poll_for_content(task_global, exp_not_equal=original_task_global_text) driver.find_element(By.ID, "toggle-tick").click() # avoid teardown errors - assert lifespan_app.app_module.lifespan_task_global > original_task_global_value # type: ignore + assert ( + lifespan_app.app_module.lifespan_task_global_getter() + > original_task_global_value + ) # type: ignore assert int(task_global.text) > original_task_global_value # Kill the backend @@ -129,5 +155,5 @@ async def test_lifespan(lifespan_app: AppHarness): lifespan_app.backend_thread.join() # Check that the lifespan tasks have been cancelled - assert lifespan_app.app_module.lifespan_task_global == 0 - assert lifespan_app.app_module.lifespan_context_global == 4 + assert lifespan_app.app_module.lifespan_task_global_getter() == 0 + assert lifespan_app.app_module.lifespan_context_global_getter() == 4 From 36af8255d35d80177a04191614cd2867a3ae2b35 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 16:06:58 -0800 Subject: [PATCH 58/86] remove unused format functions --- reflex/utils/format.py | 127 +------------------------------ tests/units/utils/test_format.py | 114 +-------------------------- 2 files changed, 2 insertions(+), 239 deletions(-) diff --git a/reflex/utils/format.py b/reflex/utils/format.py index 1d6671a0bbb..ea8ed14274c 100644 --- a/reflex/utils/format.py +++ b/reflex/utils/format.py @@ -4,9 +4,8 @@ import inspect import json -import os import re -from typing import TYPE_CHECKING, Any, List, Optional, Union +from typing import TYPE_CHECKING, Any, Optional, Union from reflex import constants from reflex.constants.state import FRONTEND_EVENT_STATE @@ -107,22 +106,6 @@ def wrap( return f"{open * num}{text}{close * num}" -def indent(text: str, indent_level: int = 2) -> str: - """Indent the given text by the given indent level. - - Args: - text: The text to indent. - indent_level: The indent level. - - Returns: - The indented text. - """ - lines = text.splitlines() - if len(lines) < 2: - return text - return os.linesep.join(f"{' ' * indent_level}{line}" for line in lines) + os.linesep - - def to_snake_case(text: str) -> str: """Convert a string to snake case. @@ -210,80 +193,6 @@ def make_default_page_title(app_name: str, route: str) -> str: return to_title_case(title) -def _escape_js_string(string: str) -> str: - """Escape the string for use as a JS string literal. - - Args: - string: The string to escape. - - Returns: - The escaped string. - """ - - # TODO: we may need to re-vist this logic after new Var API is implemented. - def escape_outside_segments(segment): - """Escape backticks in segments outside of `${}`. - - Args: - segment: The part of the string to escape. - - Returns: - The escaped or unescaped segment. - """ - if segment.startswith("${") and segment.endswith("}"): - # Return the `${}` segment unchanged - return segment - else: - # Escape backticks in the segment - segment = segment.replace(r"\`", "`") - segment = segment.replace("`", r"\`") - return segment - - # Split the string into parts, keeping the `${}` segments - parts = re.split(r"(\$\{.*?\})", string) - escaped_parts = [escape_outside_segments(part) for part in parts] - escaped_string = "".join(escaped_parts) - return escaped_string - - -def _wrap_js_string(string: str) -> str: - """Wrap string so it looks like {`string`}. - - Args: - string: The string to wrap. - - Returns: - The wrapped string. - """ - string = wrap(string, "`") - string = wrap(string, "{") - return string - - -def format_string(string: str) -> str: - """Format the given string as a JS string literal.. - - Args: - string: The string to format. - - Returns: - The formatted string. - """ - return _wrap_js_string(_escape_js_string(string)) - - -def format_var(var: Var) -> str: - """Format the given Var as a javascript value. - - Args: - var: The Var to format. - - Returns: - The formatted Var. - """ - return str(var) - - def format_route(route: str, format_case=True) -> str: """Format the given route. @@ -306,40 +215,6 @@ def format_route(route: str, format_case=True) -> str: return route -def format_match( - cond: str | Var, - match_cases: List[List[Var]], - default: Var, -) -> str: - """Format a match expression whose return type is a Var. - - Args: - cond: The condition. - match_cases: The list of cases to match. - default: The default case. - - Returns: - The formatted match expression - - """ - switch_code = f"(() => {{ switch (JSON.stringify({cond})) {{" - - for case in match_cases: - conditions = case[:-1] - return_value = case[-1] - - case_conditions = " ".join( - [f"case JSON.stringify({condition!s}):" for condition in conditions] - ) - case_code = f"{case_conditions} return ({return_value!s}); break;" - switch_code += case_code - - switch_code += f"default: return ({default!s}); break;" - switch_code += "};})()" - - return switch_code - - def format_prop( prop: Union[Var, EventChain, ComponentStyle, str], ) -> Union[int, float, str]: diff --git a/tests/units/utils/test_format.py b/tests/units/utils/test_format.py index 2a2aa8259ff..48281a11949 100644 --- a/tests/units/utils/test_format.py +++ b/tests/units/utils/test_format.py @@ -2,7 +2,7 @@ import datetime import json -from typing import Any, List +from typing import Any import plotly.graph_objects as go import pytest @@ -98,60 +98,6 @@ def test_wrap(text: str, open: str, expected: str, check_first: bool, num: int): assert format.wrap(text, open, check_first=check_first, num=num) == expected -@pytest.mark.parametrize( - "string,expected_output", - [ - ("This is a random string", "This is a random string"), - ( - "This is a random string with `backticks`", - "This is a random string with \\`backticks\\`", - ), - ( - "This is a random string with `backticks`", - "This is a random string with \\`backticks\\`", - ), - ( - "This is a string with ${someValue[`string interpolation`]} unescaped", - "This is a string with ${someValue[`string interpolation`]} unescaped", - ), - ( - "This is a string with `backticks` and ${someValue[`string interpolation`]} unescaped", - "This is a string with \\`backticks\\` and ${someValue[`string interpolation`]} unescaped", - ), - ( - "This is a string with `backticks`, ${someValue[`the first string interpolation`]} and ${someValue[`the second`]}", - "This is a string with \\`backticks\\`, ${someValue[`the first string interpolation`]} and ${someValue[`the second`]}", - ), - ], -) -def test_escape_js_string(string, expected_output): - assert format._escape_js_string(string) == expected_output - - -@pytest.mark.parametrize( - "text,indent_level,expected", - [ - ("", 2, ""), - ("hello", 2, "hello"), - ("hello\nworld", 2, " hello\n world\n"), - ("hello\nworld", 4, " hello\n world\n"), - (" hello\n world", 2, " hello\n world\n"), - ], -) -def test_indent(text: str, indent_level: int, expected: str, windows_platform: bool): - """Test indenting a string. - - Args: - text: The text to indent. - indent_level: The number of spaces to indent by. - expected: The expected output string. - windows_platform: Whether the system is windows. - """ - assert format.indent(text, indent_level) == ( - expected.replace("\n", "\r\n") if windows_platform else expected - ) - - @pytest.mark.parametrize( "input,output", [ @@ -252,25 +198,6 @@ def test_to_kebab_case(input: str, output: str): assert format.to_kebab_case(input) == output -@pytest.mark.parametrize( - "input,output", - [ - ("", "{``}"), - ("hello", "{`hello`}"), - ("hello world", "{`hello world`}"), - ("hello=`world`", "{`hello=\\`world\\``}"), - ], -) -def test_format_string(input: str, output: str): - """Test formatting the input as JS string literal. - - Args: - input: the input string. - output: the output string. - """ - assert format.format_string(input) == output - - @pytest.mark.parametrize( "input,output", [ @@ -310,45 +237,6 @@ def test_format_route(route: str, format_case: bool, expected: bool): assert format.format_route(route, format_case=format_case) == expected -@pytest.mark.parametrize( - "condition, match_cases, default,expected", - [ - ( - "state__state.value", - [ - [LiteralVar.create(1), LiteralVar.create("red")], - [LiteralVar.create(2), LiteralVar.create(3), LiteralVar.create("blue")], - [TestState.mapping, TestState.num1], - [ - LiteralVar.create(f"{TestState.map_key}-key"), - LiteralVar.create("return-key"), - ], - ], - LiteralVar.create("yellow"), - '(() => { switch (JSON.stringify(state__state.value)) {case JSON.stringify(1): return ("red"); break;case JSON.stringify(2): case JSON.stringify(3): ' - f'return ("blue"); break;case JSON.stringify({TestState.get_full_name()}.mapping): return ' - f'({TestState.get_full_name()}.num1); break;case JSON.stringify(({TestState.get_full_name()}.map_key+"-key")): return ("return-key");' - ' break;default: return ("yellow"); break;};})()', - ) - ], -) -def test_format_match( - condition: str, - match_cases: List[List[Var]], - default: Var, - expected: str, -): - """Test formatting a match statement. - - Args: - condition: The condition to match. - match_cases: List of match cases to be matched. - default: Catchall case for the match statement. - expected: The expected string output. - """ - assert format.format_match(condition, match_cases, default) == expected - - @pytest.mark.parametrize( "prop,formatted", [ From 00019daa27292a098135d6438d8c5df579d66913 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 16:17:24 -0800 Subject: [PATCH 59/86] get rid of match class --- .../jinja/web/pages/utils.js.jinja2 | 25 -- reflex/components/component.py | 13 +- reflex/components/core/__init__.py | 1 - reflex/components/core/match.py | 254 ++++++++---------- .../radix/themes/components/icon_button.py | 4 +- tests/units/components/core/test_match.py | 18 +- tests/units/components/test_component.py | 1 - 7 files changed, 129 insertions(+), 187 deletions(-) diff --git a/reflex/.templates/jinja/web/pages/utils.js.jinja2 b/reflex/.templates/jinja/web/pages/utils.js.jinja2 index 624e3bee862..d161e846d80 100644 --- a/reflex/.templates/jinja/web/pages/utils.js.jinja2 +++ b/reflex/.templates/jinja/web/pages/utils.js.jinja2 @@ -8,8 +8,6 @@ {{- component }} {%- elif "iterable" in component %} {{- render_iterable_tag(component) }} - {%- elif component.name == "match"%} - {{- render_match_tag(component) }} {%- elif "cond" in component %} {{- render_condition_tag(component) }} {%- elif component.children|length %} @@ -75,29 +73,6 @@ {% if props|length %} {{ props|join(" ") }}{% endif %} {% endmacro %} -{# Rendering Match component. #} -{# Args: #} -{# component: component dictionary #} -{% macro render_match_tag(component) %} -{ - (() => { - switch (JSON.stringify({{ component.cond._js_expr }})) { - {% for case in component.match_cases %} - {% for condition in case[:-1] %} - case JSON.stringify({{ condition._js_expr }}): - {% endfor %} - return {{ case[-1] }}; - break; - {% endfor %} - default: - return {{ component.default }}; - break; - } - })() - } -{%- endmacro %} - - {# Rendering content with args. #} {# Args: #} {# component: component dictionary #} diff --git a/reflex/components/component.py b/reflex/components/component.py index 015406d921e..f3f69bbb289 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -934,7 +934,6 @@ def _validate_component_children(self, children: List[Component]): from reflex.components.base.fragment import Fragment from reflex.components.core.cond import Cond from reflex.components.core.foreach import Foreach - from reflex.components.core.match import Match no_valid_parents_defined = all(child._valid_parents == [] for child in children) if ( @@ -945,9 +944,7 @@ def _validate_component_children(self, children: List[Component]): return comp_name = type(self).__name__ - allowed_components = [ - comp.__name__ for comp in (Fragment, Foreach, Cond, Match) - ] + allowed_components = [comp.__name__ for comp in (Fragment, Foreach, Cond)] def validate_child(child): child_name = type(child).__name__ @@ -971,11 +968,6 @@ def validate_child(child): for c in var_data.components: validate_child(c) - if isinstance(child, Match): - for cases in child.match_cases: - validate_child(cases[-1]) - validate_child(child.default) - if self._invalid_children and child_name in self._invalid_children: raise ValueError( f"The component `{comp_name}` cannot have `{child_name}` as a child component" @@ -2073,7 +2065,6 @@ def _child_var(child: Component) -> Var | Component: from reflex.components.base.bare import Bare from reflex.components.core.cond import Cond from reflex.components.core.foreach import Foreach - from reflex.components.core.match import Match if isinstance(child, Bare): return child.contents @@ -2081,8 +2072,6 @@ def _child_var(child: Component) -> Var | Component: return child.cond if isinstance(child, Foreach): return child.iterable - if isinstance(child, Match): - return child.cond return child @classmethod diff --git a/reflex/components/core/__init__.py b/reflex/components/core/__init__.py index fbe0bdc8473..c61bf90e342 100644 --- a/reflex/components/core/__init__.py +++ b/reflex/components/core/__init__.py @@ -30,7 +30,6 @@ "html": ["html", "Html"], "match": [ "match", - "Match", ], "breakpoints": ["breakpoints", "set_breakpoints"], "responsive": [ diff --git a/reflex/components/core/match.py b/reflex/components/core/match.py index e697be7b220..d1b359e498f 100644 --- a/reflex/components/core/match.py +++ b/reflex/components/core/match.py @@ -1,12 +1,12 @@ """rx.match.""" import textwrap -from typing import Any, List, cast +from typing import Any, cast from typing_extensions import Unpack from reflex.components.base import Fragment -from reflex.components.component import BaseComponent, Component, MemoizationLeaf +from reflex.components.component import BaseComponent, Component from reflex.utils import types from reflex.utils.exceptions import MatchTypeError from reflex.vars.base import VAR_TYPE, Var @@ -15,155 +15,135 @@ CASE_TYPE = tuple[Unpack[tuple[Any, ...]], Var[VAR_TYPE] | VAR_TYPE] -class Match(MemoizationLeaf): - """Match cases based on a condition.""" +def _process_match_cases(cases: tuple[CASE_TYPE[VAR_TYPE], ...]): + """Process the individual match cases. - # The condition to determine which case to match. - cond: Var[Any] + Args: + cases: The match cases. - # The list of match cases to be matched. - match_cases: List[Any] = [] + Raises: + ValueError: If the default case is not the last case or the tuple elements are less than 2. + """ + for case in cases: + if not isinstance(case, tuple): + raise ValueError( + "rx.match should have tuples of cases and a default case as the last argument." + ) + + # There should be at least two elements in a case tuple(a condition and return value) + if len(case) < 2: + raise ValueError( + "A case tuple should have at least a match case element and a return value." + ) - # The catchall case to match. - default: Any - @classmethod - def create( - cls, - cond: Any, - *cases: Unpack[ - tuple[Unpack[tuple[CASE_TYPE[VAR_TYPE], ...]], Var[VAR_TYPE] | VAR_TYPE] - ], - ) -> Var[VAR_TYPE]: - """Create a Match Component. +def _validate_return_types(match_cases: tuple[CASE_TYPE[VAR_TYPE], ...]) -> None: + """Validate that match cases have the same return types. - Args: - cond: The condition to determine which case to match. - cases: This list of cases to match. + Args: + match_cases: The match cases. - Returns: - The match component. + Raises: + MatchTypeError: If the return types of cases are different. + """ + first_case_return = match_cases[0][-1] + return_type = type(first_case_return) - Raises: - ValueError: When a default case is not provided for cases with Var return types. - """ - default = None + if types._isinstance(first_case_return, BaseComponent): + return_type = BaseComponent + elif types._isinstance(first_case_return, Var): + return_type = Var - if len([case for case in cases if not isinstance(case, tuple)]) > 1: - raise ValueError("rx.match can only have one default case.") + for index, case in enumerate(match_cases): + if not ( + types._issubclass(type(case[-1]), return_type) + or ( + isinstance(case[-1], Var) + and types.typehint_issubclass(case[-1]._var_type, return_type) + ) + ): + raise MatchTypeError( + f"Match cases should have the same return types. Case {index} with return " + f"value `{case[-1]._js_expr if isinstance(case[-1], Var) else textwrap.shorten(str(case[-1]), width=250)}`" + f" of type {(type(case[-1]) if not isinstance(case[-1], Var) else case[-1]._var_type)!r} is not {return_type}" + ) - if not cases: - raise ValueError("rx.match should have at least one case.") - # Get the default case which should be the last non-tuple arg - if not isinstance(cases[-1], tuple): - default = cases[-1] - actual_cases = cases[:-1] - else: - actual_cases = cast(tuple[CASE_TYPE[VAR_TYPE], ...], cases) +def _create_match_var( + match_cond_var: Var, + match_cases: tuple[CASE_TYPE[VAR_TYPE], ...], + default: VAR_TYPE | Var[VAR_TYPE], +) -> Var[VAR_TYPE]: + """Create the match var. - cls._process_match_cases(actual_cases) + Args: + match_cond_var: The match condition var. + match_cases: The match cases. + default: The default case. - cls._validate_return_types(actual_cases) + Returns: + The match var. + """ + return MatchOperation.create(match_cond_var, match_cases, default) - if default is None and any( - not ( - isinstance((return_type := case[-1]), Component) - or ( - isinstance(return_type, Var) - and types.typehint_issubclass(return_type._var_type, Component) - ) - ) - for case in actual_cases - ): - raise ValueError( - "For cases with return types as Vars, a default case must be provided" - ) - elif default is None: - default = Fragment.create() - default = cast(Var[VAR_TYPE] | VAR_TYPE, default) +def match( + cond: Any, + *cases: Unpack[ + tuple[Unpack[tuple[CASE_TYPE[VAR_TYPE], ...]], Var[VAR_TYPE] | VAR_TYPE] + ], +) -> Var[VAR_TYPE]: + """Create a match var. - return cls._create_match_cond_var_or_component( - cond, - actual_cases, - default, + Args: + cond: The condition to match. + cases: The match cases. Each case should be a tuple with the first elements as the match case and the last element as the return value. The last argument should be the default case. + + Returns: + The match var. + + Raises: + ValueError: If the default case is not the last case or the tuple elements are less than 2. + """ + default = None + + if len([case for case in cases if not isinstance(case, tuple)]) > 1: + raise ValueError("rx.match can only have one default case.") + + if not cases: + raise ValueError("rx.match should have at least one case.") + + # Get the default case which should be the last non-tuple arg + if not isinstance(cases[-1], tuple): + default = cases[-1] + actual_cases = cases[:-1] + else: + actual_cases = cast(tuple[CASE_TYPE[VAR_TYPE], ...], cases) + + _process_match_cases(actual_cases) + + _validate_return_types(actual_cases) + + if default is None and any( + not ( + isinstance((return_type := case[-1]), Component) + or ( + isinstance(return_type, Var) + and types.typehint_issubclass(return_type._var_type, Component) + ) ) + for case in actual_cases + ): + raise ValueError( + "For cases with return types as Vars, a default case must be provided" + ) + elif default is None: + default = Fragment.create() + + default = cast(Var[VAR_TYPE] | VAR_TYPE, default) - @classmethod - def _process_match_cases(cls, cases: tuple[CASE_TYPE[VAR_TYPE], ...]): - """Process the individual match cases. - - Args: - cases: The match cases. - - Raises: - ValueError: If the default case is not the last case or the tuple elements are less than 2. - """ - for case in cases: - if not isinstance(case, tuple): - raise ValueError( - "rx.match should have tuples of cases and a default case as the last argument." - ) - - # There should be at least two elements in a case tuple(a condition and return value) - if len(case) < 2: - raise ValueError( - "A case tuple should have at least a match case element and a return value." - ) - - @classmethod - def _validate_return_types( - cls, match_cases: tuple[CASE_TYPE[VAR_TYPE], ...] - ) -> None: - """Validate that match cases have the same return types. - - Args: - match_cases: The match cases. - - Raises: - MatchTypeError: If the return types of cases are different. - """ - first_case_return = match_cases[0][-1] - return_type = type(first_case_return) - - if types._isinstance(first_case_return, BaseComponent): - return_type = BaseComponent - elif types._isinstance(first_case_return, Var): - return_type = Var - - for index, case in enumerate(match_cases): - if not ( - types._issubclass(type(case[-1]), return_type) - or ( - isinstance(case[-1], Var) - and types.typehint_issubclass(case[-1]._var_type, return_type) - ) - ): - raise MatchTypeError( - f"Match cases should have the same return types. Case {index} with return " - f"value `{case[-1]._js_expr if isinstance(case[-1], Var) else textwrap.shorten(str(case[-1]), width=250)}`" - f" of type {(type(case[-1]) if not isinstance(case[-1], Var) else case[-1]._var_type)!r} is not {return_type}" - ) - - @classmethod - def _create_match_cond_var_or_component( - cls, - match_cond_var: Var, - match_cases: tuple[CASE_TYPE[VAR_TYPE], ...], - default: VAR_TYPE | Var[VAR_TYPE], - ) -> Var[VAR_TYPE]: - """Create and return the match condition var or component. - - Args: - match_cond_var: The match condition. - match_cases: The list of match cases. - default: The default case. - - Returns: - The match component wrapped in a fragment or the match var. - """ - return MatchOperation.create(match_cond_var, match_cases, default) - - -match = Match.create + return _create_match_var( + cond, + actual_cases, + default, + ) diff --git a/reflex/components/radix/themes/components/icon_button.py b/reflex/components/radix/themes/components/icon_button.py index 68c67485a03..9daf86691d6 100644 --- a/reflex/components/radix/themes/components/icon_button.py +++ b/reflex/components/radix/themes/components/icon_button.py @@ -5,8 +5,8 @@ from typing import Literal from reflex.components.component import Component +from reflex.components.core import match from reflex.components.core.breakpoints import Responsive -from reflex.components.core.match import Match from reflex.components.el import elements from reflex.components.lucide import Icon from reflex.style import Style @@ -77,7 +77,7 @@ def create(cls, *children, **props) -> Component: if isinstance(props["size"], str): children[0].size = RADIX_TO_LUCIDE_SIZE[props["size"]] else: - size_map_var = Match.create( + size_map_var = match( props["size"], *list(RADIX_TO_LUCIDE_SIZE.items()), 12, diff --git a/tests/units/components/core/test_match.py b/tests/units/components/core/test_match.py index 83581a415c5..862bd5ad31d 100644 --- a/tests/units/components/core/test_match.py +++ b/tests/units/components/core/test_match.py @@ -3,7 +3,7 @@ import pytest import reflex as rx -from reflex.components.core.match import Match +from reflex.components.core.match import match from reflex.state import BaseState from reflex.utils.exceptions import MatchTypeError from reflex.vars.base import Var @@ -67,7 +67,7 @@ def test_match_vars(cases, expected): cases: The match cases. expected: The expected var full name. """ - match_comp = Match.create(MatchState.value, *cases) # pyright: ignore[reportCallIssue] + match_comp = match(MatchState.value, *cases) # pyright: ignore[reportCallIssue] assert isinstance(match_comp, Var) assert str(match_comp) == expected @@ -81,7 +81,7 @@ def test_match_on_component_without_default(): (2, 3, rx.text("second value")), ) - match_comp = Match.create(MatchState.value, *match_case_tuples) + match_comp = match(MatchState.value, *match_case_tuples) assert isinstance(match_comp, Var) @@ -98,7 +98,7 @@ def test_match_on_var_no_default(): ValueError, match="For cases with return types as Vars, a default case must be provided", ): - Match.create(MatchState.value, *match_case_tuples) + match(MatchState.value, *match_case_tuples) @pytest.mark.parametrize( @@ -131,7 +131,7 @@ def test_match_default_not_last_arg(match_case): ValueError, match="rx.match should have tuples of cases and a default case as the last argument.", ): - Match.create(MatchState.value, *match_case) # pyright: ignore[reportCallIssue] + match(MatchState.value, *match_case) # pyright: ignore[reportCallIssue] @pytest.mark.parametrize( @@ -161,7 +161,7 @@ def test_match_case_tuple_elements(match_case): ValueError, match="A case tuple should have at least a match case element and a return value.", ): - Match.create(MatchState.value, *match_case) # pyright: ignore[reportCallIssue] + match(MatchState.value, *match_case) # pyright: ignore[reportCallIssue] @pytest.mark.parametrize( @@ -203,7 +203,7 @@ def test_match_different_return_types(cases: Tuple, error_msg: str): error_msg: Expected error message. """ with pytest.raises(MatchTypeError, match=error_msg): - Match.create(MatchState.value, *cases) # pyright: ignore[reportCallIssue] + match(MatchState.value, *cases) # pyright: ignore[reportCallIssue] @pytest.mark.parametrize( @@ -235,9 +235,9 @@ def test_match_multiple_default_cases(match_case): match_case: the cases to match. """ with pytest.raises(ValueError, match="rx.match can only have one default case."): - Match.create(MatchState.value, *match_case) # pyright: ignore[reportCallIssue] + match(MatchState.value, *match_case) # pyright: ignore[reportCallIssue] def test_match_no_cond(): with pytest.raises(ValueError): - _ = Match.create(None) # pyright: ignore[reportCallIssue] + _ = match(None) # pyright: ignore[reportCallIssue] diff --git a/tests/units/components/test_component.py b/tests/units/components/test_component.py index 766f96e61dd..bf2e181405e 100644 --- a/tests/units/components/test_component.py +++ b/tests/units/components/test_component.py @@ -1451,7 +1451,6 @@ def test_instantiate_all_components(): "FormControl", "Html", "Icon", - "Match", "Markdown", "MultiSelect", "Option", From b11fc5a8efd6bb94e320fc6984585612c0b757ed Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 16:17:47 -0800 Subject: [PATCH 60/86] update pyi --- reflex/components/core/__init__.pyi | 1 - 1 file changed, 1 deletion(-) diff --git a/reflex/components/core/__init__.pyi b/reflex/components/core/__init__.pyi index ea927533433..39971e4629b 100644 --- a/reflex/components/core/__init__.pyi +++ b/reflex/components/core/__init__.pyi @@ -26,7 +26,6 @@ from .foreach import Foreach as Foreach from .foreach import foreach as foreach from .html import Html as Html from .html import html as html -from .match import Match as Match from .match import match as match from .responsive import desktop_only as desktop_only from .responsive import mobile_and_tablet as mobile_and_tablet From 392c5b5a690e6c2ef76f908c5474d3fe0cd9b0f3 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 16:21:53 -0800 Subject: [PATCH 61/86] who likes cond --- reflex/components/radix/themes/color_mode.py | 43 +++++++--------- reflex/components/radix/themes/color_mode.pyi | 49 ++----------------- 2 files changed, 24 insertions(+), 68 deletions(-) diff --git a/reflex/components/radix/themes/color_mode.py b/reflex/components/radix/themes/color_mode.py index e93a26ef6a3..2f46bf72d7a 100644 --- a/reflex/components/radix/themes/color_mode.py +++ b/reflex/components/radix/themes/color_mode.py @@ -20,7 +20,7 @@ from typing import Dict, List, Literal, Optional, Union, get_args from reflex.components.component import BaseComponent -from reflex.components.core.cond import Cond, color_mode_cond, cond +from reflex.components.core.cond import color_mode_cond, cond from reflex.components.lucide.icon import Icon from reflex.components.radix.themes.components.dropdown_menu import dropdown_menu from reflex.components.radix.themes.components.switch import Switch @@ -40,28 +40,23 @@ DEFAULT_DARK_ICON: Icon = Icon.create(tag="moon") -class ColorModeIcon(Cond): - """Displays the current color mode as an icon.""" +def icon( + light_component: BaseComponent | None = None, + dark_component: BaseComponent | None = None, +): + """Create a color mode icon component. - @classmethod - def create( - cls, - light_component: BaseComponent | None = None, - dark_component: BaseComponent | None = None, - ): - """Create an icon component based on color_mode. - - Args: - light_component: the component to display when color mode is default - dark_component: the component to display when color mode is dark (non-default) + Args: + light_component: The component to render in light mode. + dark_component: The component to render in dark mode. - Returns: - The conditionally rendered component - """ - return color_mode_cond( - light=light_component or DEFAULT_LIGHT_ICON, - dark=dark_component or DEFAULT_DARK_ICON, - ) + Returns: + The color mode icon component. + """ + return color_mode_cond( + light=light_component or DEFAULT_LIGHT_ICON, + dark=dark_component or DEFAULT_DARK_ICON, + ) LiteralPosition = Literal["top-left", "top-right", "bottom-left", "bottom-right"] @@ -150,7 +145,7 @@ def color_mode_item(_color_mode): return dropdown_menu.root( dropdown_menu.trigger( super().create( - ColorModeIcon.create(), + icon(), ), **props, ), @@ -161,7 +156,7 @@ def color_mode_item(_color_mode): ), ) return IconButton.create( - ColorModeIcon.create(), + icon(), on_click=toggle_color_mode, **props, ) @@ -195,7 +190,7 @@ def create(cls, *children, **props): class ColorModeNamespace(Var): """Namespace for color mode components.""" - icon = staticmethod(ColorModeIcon.create) + icon = icon button = staticmethod(ColorModeIconButton.create) switch = staticmethod(ColorModeSwitch.create) diff --git a/reflex/components/radix/themes/color_mode.pyi b/reflex/components/radix/themes/color_mode.pyi index 3a93470175c..43a7e5004a0 100644 --- a/reflex/components/radix/themes/color_mode.pyi +++ b/reflex/components/radix/themes/color_mode.pyi @@ -7,7 +7,6 @@ from typing import Any, Dict, List, Literal, Optional, Union, overload from reflex.components.component import BaseComponent from reflex.components.core.breakpoints import Breakpoints -from reflex.components.core.cond import Cond from reflex.components.lucide.icon import Icon from reflex.components.radix.themes.components.switch import Switch from reflex.event import BASE_STATE, EventType @@ -19,48 +18,10 @@ from .components.icon_button import IconButton DEFAULT_LIGHT_ICON: Icon DEFAULT_DARK_ICON: Icon -class ColorModeIcon(Cond): - @overload - @classmethod - def create( # type: ignore - cls, - *children, - cond: Optional[Union[Any, Var[Any]]] = None, - comp1: Optional[BaseComponent] = None, - comp2: Optional[BaseComponent] = None, - style: Optional[Style] = None, - key: Optional[Any] = None, - id: Optional[Any] = None, - class_name: Optional[Any] = None, - autofocus: Optional[bool] = None, - custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None, - on_blur: Optional[EventType[[], BASE_STATE]] = None, - on_click: Optional[EventType[[], BASE_STATE]] = None, - on_context_menu: Optional[EventType[[], BASE_STATE]] = None, - on_double_click: Optional[EventType[[], BASE_STATE]] = None, - on_focus: Optional[EventType[[], BASE_STATE]] = None, - on_mount: Optional[EventType[[], BASE_STATE]] = None, - on_mouse_down: Optional[EventType[[], BASE_STATE]] = None, - on_mouse_enter: Optional[EventType[[], BASE_STATE]] = None, - on_mouse_leave: Optional[EventType[[], BASE_STATE]] = None, - on_mouse_move: Optional[EventType[[], BASE_STATE]] = None, - on_mouse_out: Optional[EventType[[], BASE_STATE]] = None, - on_mouse_over: Optional[EventType[[], BASE_STATE]] = None, - on_mouse_up: Optional[EventType[[], BASE_STATE]] = None, - on_scroll: Optional[EventType[[], BASE_STATE]] = None, - on_unmount: Optional[EventType[[], BASE_STATE]] = None, - **props, - ) -> "ColorModeIcon": - """Create an icon component based on color_mode. - - Args: - light_component: the component to display when color mode is default - dark_component: the component to display when color mode is dark (non-default) - - Returns: - The conditionally rendered component - """ - ... +def icon( + light_component: BaseComponent | None = None, + dark_component: BaseComponent | None = None, +): ... LiteralPosition = Literal["top-left", "top-right", "bottom-left", "bottom-right"] position_values: List[str] @@ -442,7 +403,7 @@ class ColorModeSwitch(Switch): ... class ColorModeNamespace(Var): - icon = staticmethod(ColorModeIcon.create) + icon = icon button = staticmethod(ColorModeIconButton.create) switch = staticmethod(ColorModeSwitch.create) From b7579f4d8ddc09504b11f92d0c91c7f65b58abf5 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 16:27:30 -0800 Subject: [PATCH 62/86] why not, remove cond --- reflex/components/component.py | 10 +- reflex/components/core/__init__.py | 2 +- reflex/components/core/__init__.pyi | 1 - reflex/components/core/cond.py | 103 +------------------ reflex/components/radix/themes/color_mode.py | 8 +- reflex/components/tags/iter_tag.py | 3 +- tests/units/components/test_component.py | 1 - tests/units/test_app.py | 5 - 8 files changed, 9 insertions(+), 124 deletions(-) diff --git a/reflex/components/component.py b/reflex/components/component.py index f3f69bbb289..60f024d633d 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -932,7 +932,6 @@ def _validate_component_children(self, children: List[Component]): """ from reflex.components.base.bare import Bare from reflex.components.base.fragment import Fragment - from reflex.components.core.cond import Cond from reflex.components.core.foreach import Foreach no_valid_parents_defined = all(child._valid_parents == [] for child in children) @@ -944,7 +943,7 @@ def _validate_component_children(self, children: List[Component]): return comp_name = type(self).__name__ - allowed_components = [comp.__name__ for comp in (Fragment, Foreach, Cond)] + allowed_components = [comp.__name__ for comp in (Fragment, Foreach)] def validate_child(child): child_name = type(child).__name__ @@ -954,10 +953,6 @@ def validate_child(child): for c in child.children: validate_child(c) - if isinstance(child, Cond): - validate_child(child.comp1) - validate_child(child.comp2) - if ( isinstance(child, Bare) and child.contents is not None @@ -2063,13 +2058,10 @@ def _child_var(child: Component) -> Var | Component: The Var from the child component or the child itself (for regular cases). """ from reflex.components.base.bare import Bare - from reflex.components.core.cond import Cond from reflex.components.core.foreach import Foreach if isinstance(child, Bare): return child.contents - if isinstance(child, Cond): - return child.cond if isinstance(child, Foreach): return child.iterable return child diff --git a/reflex/components/core/__init__.py b/reflex/components/core/__init__.py index c61bf90e342..237dac11a82 100644 --- a/reflex/components/core/__init__.py +++ b/reflex/components/core/__init__.py @@ -21,7 +21,7 @@ "colors": [ "color", ], - "cond": ["Cond", "color_mode_cond", "cond"], + "cond": ["color_mode_cond", "cond"], "debounce": ["DebounceInput", "debounce_input"], "foreach": [ "foreach", diff --git a/reflex/components/core/__init__.pyi b/reflex/components/core/__init__.pyi index 39971e4629b..902433d662a 100644 --- a/reflex/components/core/__init__.pyi +++ b/reflex/components/core/__init__.pyi @@ -17,7 +17,6 @@ from .breakpoints import set_breakpoints as set_breakpoints from .clipboard import Clipboard as Clipboard from .clipboard import clipboard as clipboard from .colors import color as color -from .cond import Cond as Cond from .cond import color_mode_cond as color_mode_cond from .cond import cond as cond from .debounce import DebounceInput as DebounceInput diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index e34201c747b..fbe91043f46 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -2,115 +2,16 @@ from __future__ import annotations -from typing import Any, Dict, Optional, overload +from typing import Any, overload from reflex.components.base.fragment import Fragment -from reflex.components.component import BaseComponent, Component, MemoizationLeaf -from reflex.components.tags import CondTag, Tag -from reflex.constants import Dirs +from reflex.components.component import BaseComponent, Component from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode -from reflex.utils.imports import ImportDict, ImportVar from reflex.utils.types import infallible_issubclass -from reflex.vars import VarData from reflex.vars.base import LiteralVar, ReflexCallable, Var from reflex.vars.function import ArgsFunctionOperation from reflex.vars.number import ternary_operation -_IS_TRUE_IMPORT: ImportDict = { - f"$/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")], -} - - -class Cond(MemoizationLeaf): - """Render one of two components based on a condition.""" - - # The cond to determine which component to render. - cond: Var[Any] - - # The component to render if the cond is true. - comp1: BaseComponent = None # type: ignore - - # The component to render if the cond is false. - comp2: BaseComponent = None # type: ignore - - @classmethod - def create( - cls, - cond: Var, - comp1: BaseComponent, - comp2: Optional[BaseComponent] = None, - ) -> Component: - """Create a conditional component. - - Args: - cond: The cond to determine which component to render. - comp1: The component to render if the cond is true. - comp2: The component to render if the cond is false. - - Returns: - The conditional component. - """ - # Wrap everything in fragments. - if type(comp1).__name__ != "Fragment": - comp1 = Fragment.create(comp1) - if comp2 is None or type(comp2).__name__ != "Fragment": - comp2 = Fragment.create(comp2) if comp2 else Fragment.create() - return Fragment.create( - cls( - cond=cond, - comp1=comp1, - comp2=comp2, - children=[comp1, comp2], - ) - ) - - def _get_props_imports(self): - """Get the imports needed for component's props. - - Returns: - The imports for the component's props of the component. - """ - return [] - - def _render(self) -> Tag: - return CondTag( - cond=self.cond, - true_value=self.comp1.render(), - false_value=self.comp2.render(), - ) - - def render(self) -> Dict: - """Render the component. - - Returns: - The dictionary for template of component. - """ - tag = self._render() - return dict( - tag.add_props( - **self.event_triggers, - key=self.key, - sx=self.style, - id=self.id, - class_name=self.class_name, - ).set( - props=tag.format_props(), - ), - cond_state=f"isTrue({self.cond!s})", - ) - - def add_imports(self) -> ImportDict: - """Add imports for the Cond component. - - Returns: - The import dict for the component. - """ - var_data = VarData.merge(self.cond._get_all_var_data()) - - imports = var_data.old_school_imports() if var_data else {} - - return {**imports, **_IS_TRUE_IMPORT} - @overload def cond(condition: Any, c1: Component, c2: Any = None) -> Component: ... diff --git a/reflex/components/radix/themes/color_mode.py b/reflex/components/radix/themes/color_mode.py index 2f46bf72d7a..dae82903fba 100644 --- a/reflex/components/radix/themes/color_mode.py +++ b/reflex/components/radix/themes/color_mode.py @@ -40,7 +40,7 @@ DEFAULT_DARK_ICON: Icon = Icon.create(tag="moon") -def icon( +def color_mode_icon( light_component: BaseComponent | None = None, dark_component: BaseComponent | None = None, ): @@ -145,7 +145,7 @@ def color_mode_item(_color_mode): return dropdown_menu.root( dropdown_menu.trigger( super().create( - icon(), + color_mode_icon(), ), **props, ), @@ -156,7 +156,7 @@ def color_mode_item(_color_mode): ), ) return IconButton.create( - icon(), + color_mode_icon(), on_click=toggle_color_mode, **props, ) @@ -190,7 +190,7 @@ def create(cls, *children, **props): class ColorModeNamespace(Var): """Namespace for color mode components.""" - icon = icon + icon = color_mode_icon button = staticmethod(ColorModeIconButton.create) switch = staticmethod(ColorModeSwitch.create) diff --git a/reflex/components/tags/iter_tag.py b/reflex/components/tags/iter_tag.py index f3d9c4d8f6b..076a993e8cb 100644 --- a/reflex/components/tags/iter_tag.py +++ b/reflex/components/tags/iter_tag.py @@ -114,7 +114,6 @@ def render_component(self) -> Component: """ # Import here to avoid circular imports. from reflex.components.base.fragment import Fragment - from reflex.components.core.cond import Cond from reflex.components.core.foreach import Foreach # Get the render function arguments. @@ -132,7 +131,7 @@ def render_component(self) -> Component: component = self.render_fn(arg, index) # Nested foreach components or cond must be wrapped in fragments. - if isinstance(component, (Foreach, Cond, Var)): + if isinstance(component, (Foreach, Var)): component = Fragment.create(component) # Set the component key. diff --git a/tests/units/components/test_component.py b/tests/units/components/test_component.py index bf2e181405e..64b798cf37a 100644 --- a/tests/units/components/test_component.py +++ b/tests/units/components/test_component.py @@ -1445,7 +1445,6 @@ def test_instantiate_all_components(): # These components all have required arguments and cannot be trivially instantiated. untested_components = { "Card", - "Cond", "DebounceInput", "Foreach", "FormControl", diff --git a/tests/units/test_app.py b/tests/units/test_app.py index c59b560cca4..3a072dc293a 100644 --- a/tests/units/test_app.py +++ b/tests/units/test_app.py @@ -31,7 +31,6 @@ ) from reflex.components import Component from reflex.components.base.fragment import Fragment -from reflex.components.core.cond import Cond from reflex.components.radix.themes.typography.text import Text from reflex.event import Event, EventHandler from reflex.middleware import HydrateMiddleware @@ -1228,10 +1227,6 @@ def test_overlay_component( assert app.overlay_component is not None generated_component = app._generate_component(app.overlay_component) # type: ignore assert isinstance(generated_component, OverlayFragment) - assert isinstance( - generated_component.children[0], - Cond, # ConnectionModal is a Cond under the hood - ) else: assert app.overlay_component is not None assert isinstance( From 9e7eeb2a6e3818c0fdaaccbba9987bf371b03ec7 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 16:29:10 -0800 Subject: [PATCH 63/86] fix imports --- reflex/components/radix/themes/components/icon_button.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/components/radix/themes/components/icon_button.py b/reflex/components/radix/themes/components/icon_button.py index 9daf86691d6..14bbad41d2d 100644 --- a/reflex/components/radix/themes/components/icon_button.py +++ b/reflex/components/radix/themes/components/icon_button.py @@ -5,8 +5,8 @@ from typing import Literal from reflex.components.component import Component -from reflex.components.core import match from reflex.components.core.breakpoints import Responsive +from reflex.components.core.match import match from reflex.components.el import elements from reflex.components.lucide import Icon from reflex.style import Style From db89a712e9cc7c72d0f6e24ab9f5ab9172eab7ef Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 16:30:05 -0800 Subject: [PATCH 64/86] reformat color_mode pyi --- reflex/components/radix/themes/color_mode.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reflex/components/radix/themes/color_mode.pyi b/reflex/components/radix/themes/color_mode.pyi index 43a7e5004a0..add7a125153 100644 --- a/reflex/components/radix/themes/color_mode.pyi +++ b/reflex/components/radix/themes/color_mode.pyi @@ -18,7 +18,7 @@ from .components.icon_button import IconButton DEFAULT_LIGHT_ICON: Icon DEFAULT_DARK_ICON: Icon -def icon( +def color_mode_icon( light_component: BaseComponent | None = None, dark_component: BaseComponent | None = None, ): ... @@ -403,7 +403,7 @@ class ColorModeSwitch(Switch): ... class ColorModeNamespace(Var): - icon = icon + icon = color_mode_icon button = staticmethod(ColorModeIconButton.create) switch = staticmethod(ColorModeSwitch.create) From b10f6e836d5af7e8f00f2dd7a5ca8cf90b69310e Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 16:34:48 -0800 Subject: [PATCH 65/86] remove even more cond and match --- .../jinja/web/pages/utils.js.jinja2 | 2 -- reflex/components/component.py | 28 ------------------- reflex/components/tags/__init__.py | 2 -- reflex/components/tags/cond_tag.py | 21 -------------- reflex/components/tags/match_tag.py | 21 -------------- tests/units/components/test_tag.py | 25 +---------------- 6 files changed, 1 insertion(+), 98 deletions(-) delete mode 100644 reflex/components/tags/cond_tag.py delete mode 100644 reflex/components/tags/match_tag.py diff --git a/reflex/.templates/jinja/web/pages/utils.js.jinja2 b/reflex/.templates/jinja/web/pages/utils.js.jinja2 index d161e846d80..e046c9160a6 100644 --- a/reflex/.templates/jinja/web/pages/utils.js.jinja2 +++ b/reflex/.templates/jinja/web/pages/utils.js.jinja2 @@ -8,8 +8,6 @@ {{- component }} {%- elif "iterable" in component %} {{- render_iterable_tag(component) }} - {%- elif "cond" in component %} - {{- render_condition_tag(component) }} {%- elif component.children|length %} {{- render_tag(component) }} {%- else %} diff --git a/reflex/components/component.py b/reflex/components/component.py index 60f024d633d..d4d2ca6122c 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -67,7 +67,6 @@ cached_property_no_lock, ) from reflex.vars.function import ArgsFunctionOperation, FunctionStringVar -from reflex.vars.number import ternary_operation from reflex.vars.object import ObjectVar from reflex.vars.sequence import LiteralArrayVar @@ -2430,33 +2429,6 @@ def render_dict_to_var(tag: dict | Component | str, imported_names: set[str]) -> func, ) - if tag["name"] == "match": - element = tag["cond"] - - conditionals = tag["default"] - - for case in tag["match_cases"][::-1]: - condition = case[0].to_string() == element.to_string() - for pattern in case[1:-1]: - condition = condition | (pattern.to_string() == element.to_string()) - - conditionals = ternary_operation( - condition, - case[-1], - conditionals, - ) - - return conditionals - - if "cond" in tag: - return ternary_operation( - tag["cond"], - render_dict_to_var(tag["true_value"], imported_names), - render_dict_to_var(tag["false_value"], imported_names) - if tag["false_value"] is not None - else Var.create(None), - ) - props = {} special_props = [] diff --git a/reflex/components/tags/__init__.py b/reflex/components/tags/__init__.py index 993da11fe69..8c8b73ab49a 100644 --- a/reflex/components/tags/__init__.py +++ b/reflex/components/tags/__init__.py @@ -1,6 +1,4 @@ """Representations for React tags.""" -from .cond_tag import CondTag from .iter_tag import IterTag -from .match_tag import MatchTag from .tag import Tag diff --git a/reflex/components/tags/cond_tag.py b/reflex/components/tags/cond_tag.py deleted file mode 100644 index b4d0fe4695e..00000000000 --- a/reflex/components/tags/cond_tag.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tag to conditionally render components.""" - -import dataclasses -from typing import Any, Dict, Optional - -from reflex.components.tags.tag import Tag -from reflex.vars.base import Var - - -@dataclasses.dataclass() -class CondTag(Tag): - """A conditional tag.""" - - # The condition to determine which component to render. - cond: Var[Any] = dataclasses.field(default_factory=lambda: Var.create(True)) - - # The code to render if the condition is true. - true_value: Dict = dataclasses.field(default_factory=dict) - - # The code to render if the condition is false. - false_value: Optional[Dict] = None diff --git a/reflex/components/tags/match_tag.py b/reflex/components/tags/match_tag.py deleted file mode 100644 index 01eedb2969c..00000000000 --- a/reflex/components/tags/match_tag.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tag to conditionally match cases.""" - -import dataclasses -from typing import Any, List - -from reflex.components.tags.tag import Tag -from reflex.vars.base import Var - - -@dataclasses.dataclass() -class MatchTag(Tag): - """A match tag.""" - - # The condition to determine which case to match. - cond: Var[Any] = dataclasses.field(default_factory=lambda: Var.create(True)) - - # The list of match cases to be matched. - match_cases: List[Any] = dataclasses.field(default_factory=list) - - # The catchall case to match. - default: Any = dataclasses.field(default=Var.create(None)) diff --git a/tests/units/components/test_tag.py b/tests/units/components/test_tag.py index a69e40b8b38..a83ebe41ad5 100644 --- a/tests/units/components/test_tag.py +++ b/tests/units/components/test_tag.py @@ -2,7 +2,7 @@ import pytest -from reflex.components.tags import CondTag, Tag, tagless +from reflex.components.tags import Tag, tagless from reflex.vars.base import LiteralVar, Var @@ -105,29 +105,6 @@ def test_format_tag(tag: Tag, expected: Dict): assert prop_value.equals(LiteralVar.create(expected["props"][prop])) -def test_format_cond_tag(): - """Test that the cond tag dict is correct.""" - tag = CondTag( - true_value=dict(Tag(name="h1", contents="True content")), - false_value=dict(Tag(name="h2", contents="False content")), - cond=Var(_js_expr="logged_in", _var_type=bool), - ) - tag_dict = dict(tag) - cond, true_value, false_value = ( - tag_dict["cond"], - tag_dict["true_value"], - tag_dict["false_value"], - ) - assert cond._js_expr == "logged_in" - assert cond._var_type is bool - - assert true_value["name"] == "h1" - assert true_value["contents"] == "True content" - - assert false_value["name"] == "h2" - assert false_value["contents"] == "False content" - - def test_tagless_string_representation(): """Test that the string representation of a tagless is correct.""" tag = tagless.Tagless(contents="Hello world") From 5d6b51c561f178f22845a1ec90ad1aef174c0eb7 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 18:16:43 -0800 Subject: [PATCH 66/86] what if i deleted rx.foreach --- reflex/components/base/bare.py | 29 +++- reflex/components/component.py | 12 +- reflex/components/core/__init__.py | 1 - reflex/components/core/__init__.pyi | 1 - reflex/components/core/foreach.py | 148 ++++------------- reflex/components/radix/primitives/slider.py | 2 +- reflex/components/radix/themes/layout/list.py | 4 +- reflex/components/tags/__init__.py | 1 - reflex/components/tags/iter_tag.py | 141 ---------------- reflex/utils/types.py | 11 ++ reflex/vars/base.py | 41 +++++ reflex/vars/sequence.py | 64 ++++++- tests/units/components/core/test_foreach.py | 157 +++--------------- tests/units/components/test_component.py | 8 +- 14 files changed, 192 insertions(+), 428 deletions(-) delete mode 100644 reflex/components/tags/iter_tag.py diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index 4b601544f1c..b07bdfcfac0 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -4,12 +4,12 @@ from typing import Any, Iterator -from reflex.components.component import Component +from reflex.components.component import Component, ComponentStyle from reflex.components.tags import Tag from reflex.components.tags.tagless import Tagless from reflex.utils.imports import ParsedImportDict from reflex.vars import BooleanVar, ObjectVar, Var -from reflex.vars.base import VarData +from reflex.vars.base import VarData, get_var_caching, set_var_caching class Bare(Component): @@ -141,6 +141,31 @@ def _render(self) -> Tag: return Tagless(contents=f"{{{self.contents!s}}}") return Tagless(contents=str(self.contents)) + def _add_style_recursive( + self, style: ComponentStyle, theme: Component | None = None + ) -> Component: + """Add style to the component and its children. + + Args: + style: The style to add. + theme: The theme to add. + + Returns: + The component with the style added. + """ + new_self = super()._add_style_recursive(style, theme) + if isinstance(self.contents, Var): + var_data = self.contents._get_all_var_data() + if var_data: + for component in var_data.components: + if isinstance(component, Component): + component._add_style_recursive(style, theme) + if get_var_caching(): + set_var_caching(False) + str(new_self) + set_var_caching(True) + return new_self + def _get_vars( self, include_children: bool = False, ignore_ids: set[int] | None = None ) -> Iterator[Var]: diff --git a/reflex/components/component.py b/reflex/components/component.py index d4d2ca6122c..fe6b9faa382 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -931,7 +931,6 @@ def _validate_component_children(self, children: List[Component]): """ from reflex.components.base.bare import Bare from reflex.components.base.fragment import Fragment - from reflex.components.core.foreach import Foreach no_valid_parents_defined = all(child._valid_parents == [] for child in children) if ( @@ -942,7 +941,7 @@ def _validate_component_children(self, children: List[Component]): return comp_name = type(self).__name__ - allowed_components = [comp.__name__ for comp in (Fragment, Foreach)] + allowed_components = [comp.__name__ for comp in (Fragment,)] def validate_child(child): child_name = type(child).__name__ @@ -1974,8 +1973,6 @@ def create(cls, component: Component) -> StatefulComponent | None: Returns: The stateful component or None if the component should not be memoized. """ - from reflex.components.core.foreach import Foreach - if component._memoization_mode.disposition == MemoizationDisposition.NEVER: # Never memoize this component. return None @@ -2004,10 +2001,6 @@ def create(cls, component: Component) -> StatefulComponent | None: # Skip BaseComponent and StatefulComponent children. if not isinstance(child, Component): continue - # Always consider Foreach something that must be memoized by the parent. - if isinstance(child, Foreach): - should_memoize = True - break child = cls._child_var(child) if isinstance(child, Var) and child._get_all_var_data(): should_memoize = True @@ -2057,12 +2050,9 @@ def _child_var(child: Component) -> Var | Component: The Var from the child component or the child itself (for regular cases). """ from reflex.components.base.bare import Bare - from reflex.components.core.foreach import Foreach if isinstance(child, Bare): return child.contents - if isinstance(child, Foreach): - return child.iterable return child @classmethod diff --git a/reflex/components/core/__init__.py b/reflex/components/core/__init__.py index 237dac11a82..534035f120f 100644 --- a/reflex/components/core/__init__.py +++ b/reflex/components/core/__init__.py @@ -25,7 +25,6 @@ "debounce": ["DebounceInput", "debounce_input"], "foreach": [ "foreach", - "Foreach", ], "html": ["html", "Html"], "match": [ diff --git a/reflex/components/core/__init__.pyi b/reflex/components/core/__init__.pyi index 902433d662a..2f1fb20846c 100644 --- a/reflex/components/core/__init__.pyi +++ b/reflex/components/core/__init__.pyi @@ -21,7 +21,6 @@ from .cond import color_mode_cond as color_mode_cond from .cond import cond as cond from .debounce import DebounceInput as DebounceInput from .debounce import debounce_input as debounce_input -from .foreach import Foreach as Foreach from .foreach import foreach as foreach from .html import Html as Html from .html import html as html diff --git a/reflex/components/core/foreach.py b/reflex/components/core/foreach.py index c9fbe5bc54a..4b6192e0b47 100644 --- a/reflex/components/core/foreach.py +++ b/reflex/components/core/foreach.py @@ -2,15 +2,11 @@ from __future__ import annotations -import inspect -from typing import Any, Callable, Iterable +from typing import Callable, Iterable -from reflex.components.base.fragment import Fragment -from reflex.components.component import Component -from reflex.components.tags import IterTag -from reflex.constants import MemoizationMode -from reflex.state import ComponentState from reflex.vars.base import LiteralVar, Var +from reflex.vars.object import ObjectVar +from reflex.vars.sequence import ArrayVar class ForeachVarError(TypeError): @@ -21,116 +17,32 @@ class ForeachRenderError(TypeError): """Raised when there is an error with the foreach render function.""" -class Foreach(Component): - """A component that takes in an iterable and a render function and renders a list of components.""" - - _memoization_mode = MemoizationMode(recursive=False) - - # The iterable to create components from. - iterable: Var[Iterable] - - # A function from the render args to the component. - render_fn: Callable = Fragment.create - - @classmethod - def create( - cls, - iterable: Var[Iterable] | Iterable, - render_fn: Callable, - ) -> Foreach: - """Create a foreach component. - - Args: - iterable: The iterable to create components from. - render_fn: A function from the render args to the component. - - Returns: - The foreach component. - - Raises: - ForeachVarError: If the iterable is of type Any. - TypeError: If the render function is a ComponentState. - """ - iterable = LiteralVar.create(iterable) - if iterable._var_type == Any: - raise ForeachVarError( - f"Could not foreach over var `{iterable!s}` of type Any. " - "(If you are trying to foreach over a state var, add a type annotation to the var). " - "See https://reflex.dev/docs/library/dynamic-rendering/foreach/" - ) - - if ( - hasattr(render_fn, "__qualname__") - and render_fn.__qualname__ == ComponentState.create.__qualname__ - ): - raise TypeError( - "Using a ComponentState as `render_fn` inside `rx.foreach` is not supported yet." - ) - - component = cls( - iterable=iterable, - render_fn=render_fn, +def foreach( + iterable: Var[Iterable] | Iterable, + render_fn: Callable, +) -> Var: + """Create a foreach component. + + Args: + iterable: The iterable to create components from. + render_fn: A function from the render args to the component. + + Returns: + The foreach component. + + Raises: + ForeachVarError: If the iterable is of type Any. + TypeError: If the render function is a ComponentState. + """ + iterable = LiteralVar.create(iterable) + if isinstance(iterable, ObjectVar): + iterable = iterable.items() + + if not isinstance(iterable, ArrayVar): + raise ForeachVarError( + f"Could not foreach over var `{iterable!s}` of type {iterable._var_type!s}. " + "(If you are trying to foreach over a state var, add a type annotation to the var). " + "See https://reflex.dev/docs/library/dynamic-rendering/foreach/" ) - # Keep a ref to a rendered component to determine correct imports/hooks/styles. - component.children = [component._render().render_component()] - return component - - def _render(self) -> IterTag: - props = {} - - render_sig = inspect.signature(self.render_fn) - params = list(render_sig.parameters.values()) - - # Validate the render function signature. - if len(params) == 0 or len(params) > 2: - raise ForeachRenderError( - "Expected 1 or 2 parameters in foreach render function, got " - f"{[p.name for p in params]}. See " - "https://reflex.dev/docs/library/dynamic-rendering/foreach/" - ) - - if len(params) >= 1: - # Determine the arg var name based on the params accepted by render_fn. - props["arg_var_name"] = params[0].name - - if len(params) == 2: - # Determine the index var name based on the params accepted by render_fn. - props["index_var_name"] = params[1].name - else: - # Otherwise, use a deterministic index, based on the render function bytecode. - code_hash = ( - hash(self.render_fn.__code__) - .to_bytes( - length=8, - byteorder="big", - signed=True, - ) - .hex() - ) - props["index_var_name"] = f"index_{code_hash}" - - return IterTag( - iterable=self.iterable, - render_fn=self.render_fn, - children=self.children, - **props, - ) - - def render(self): - """Render the component. - - Returns: - The dictionary for template of component. - """ - tag = self._render() - - return dict( - tag, - iterable_state=str(tag.iterable), - arg_name=tag.arg_var_name, - arg_index=tag.get_index_var_arg(), - iterable_type=tag.iterable._var_type.mro()[0].__name__, - ) - -foreach = Foreach.create + return iterable.foreach(render_fn) diff --git a/reflex/components/radix/primitives/slider.py b/reflex/components/radix/primitives/slider.py index 68f39e32c26..eafdd65b485 100644 --- a/reflex/components/radix/primitives/slider.py +++ b/reflex/components/radix/primitives/slider.py @@ -188,7 +188,7 @@ def __call__(**props) -> Component: else: children = [ track, - # Foreach.create(props.get("value"), lambda e: SliderThumb.create()), # foreach doesn't render Thumbs properly # noqa: ERA001 + # foreach(props.get("value"), lambda e: SliderThumb.create()), # foreach doesn't render Thumbs properly # noqa: ERA001 ] return SliderRoot.create(*children, **props) diff --git a/reflex/components/radix/themes/layout/list.py b/reflex/components/radix/themes/layout/list.py index a306e19a490..937342c7b41 100644 --- a/reflex/components/radix/themes/layout/list.py +++ b/reflex/components/radix/themes/layout/list.py @@ -5,7 +5,7 @@ from typing import Any, Iterable, Literal, Union from reflex.components.component import Component, ComponentNamespace -from reflex.components.core.foreach import Foreach +from reflex.components.core.foreach import foreach from reflex.components.el.elements.typography import Li, Ol, Ul from reflex.components.lucide.icon import Icon from reflex.components.markdown.markdown import MarkdownComponentMap @@ -70,7 +70,7 @@ def create( if not children and items is not None: if isinstance(items, Var): - children = [Foreach.create(items, ListItem.create)] + children = [foreach(items, ListItem.create)] else: children = [ListItem.create(item) for item in items] # type: ignore props["direction"] = "column" diff --git a/reflex/components/tags/__init__.py b/reflex/components/tags/__init__.py index 8c8b73ab49a..330bcc279ea 100644 --- a/reflex/components/tags/__init__.py +++ b/reflex/components/tags/__init__.py @@ -1,4 +1,3 @@ """Representations for React tags.""" -from .iter_tag import IterTag from .tag import Tag diff --git a/reflex/components/tags/iter_tag.py b/reflex/components/tags/iter_tag.py deleted file mode 100644 index 076a993e8cb..00000000000 --- a/reflex/components/tags/iter_tag.py +++ /dev/null @@ -1,141 +0,0 @@ -"""Tag to loop through a list of components.""" - -from __future__ import annotations - -import dataclasses -import inspect -from typing import TYPE_CHECKING, Any, Callable, Iterable, Tuple, Union, get_args - -from reflex.components.tags.tag import Tag -from reflex.utils import types -from reflex.vars import LiteralArrayVar, Var, get_unique_variable_name - -if TYPE_CHECKING: - from reflex.components.component import Component - - -@dataclasses.dataclass() -class IterTag(Tag): - """An iterator tag.""" - - # The var to iterate over. - iterable: Var[Iterable] = dataclasses.field( - default_factory=lambda: LiteralArrayVar.create([]) - ) - - # The component render function for each item in the iterable. - render_fn: Callable = dataclasses.field(default_factory=lambda: lambda x: x) - - # The name of the arg var. - arg_var_name: str = dataclasses.field(default_factory=get_unique_variable_name) - - # The name of the index var. - index_var_name: str = dataclasses.field(default_factory=get_unique_variable_name) - - def get_iterable_var_type(self) -> types.GenericType: - """Get the type of the iterable var. - - Returns: - The type of the iterable var. - """ - iterable = self.iterable - try: - if iterable._var_type.mro()[0] is dict: - # Arg is a tuple of (key, value). - return Tuple[get_args(iterable._var_type)] - elif iterable._var_type.mro()[0] is tuple: - # Arg is a union of any possible values in the tuple. - return Union[get_args(iterable._var_type)] - else: - return get_args(iterable._var_type)[0] - except Exception: - return Any - - def get_index_var(self) -> Var: - """Get the index var for the tag (with curly braces). - - This is used to reference the index var within the tag. - - Returns: - The index var. - """ - return Var( - _js_expr=self.index_var_name, - _var_type=int, - ).guess_type() - - def get_arg_var(self) -> Var: - """Get the arg var for the tag (with curly braces). - - This is used to reference the arg var within the tag. - - Returns: - The arg var. - """ - return Var( - _js_expr=self.arg_var_name, - _var_type=self.get_iterable_var_type(), - ).guess_type() - - def get_index_var_arg(self) -> Var: - """Get the index var for the tag (without curly braces). - - This is used to render the index var in the .map() function. - - Returns: - The index var. - """ - return Var( - _js_expr=self.index_var_name, - _var_type=int, - ).guess_type() - - def get_arg_var_arg(self) -> Var: - """Get the arg var for the tag (without curly braces). - - This is used to render the arg var in the .map() function. - - Returns: - The arg var. - """ - return Var( - _js_expr=self.arg_var_name, - _var_type=self.get_iterable_var_type(), - ).guess_type() - - def render_component(self) -> Component: - """Render the component. - - Raises: - ValueError: If the render function takes more than 2 arguments. - - Returns: - The rendered component. - """ - # Import here to avoid circular imports. - from reflex.components.base.fragment import Fragment - from reflex.components.core.foreach import Foreach - - # Get the render function arguments. - args = inspect.getfullargspec(self.render_fn).args - arg = self.get_arg_var() - index = self.get_index_var() - - if len(args) == 1: - # If the render function doesn't take the index as an argument. - component = self.render_fn(arg) - else: - # If the render function takes the index as an argument. - if len(args) != 2: - raise ValueError("The render function must take 2 arguments.") - component = self.render_fn(arg, index) - - # Nested foreach components or cond must be wrapped in fragments. - if isinstance(component, (Foreach, Var)): - component = Fragment.create(component) - - # Set the component key. - if component.key is None: - component.key = index - - return component diff --git a/reflex/utils/types.py b/reflex/utils/types.py index a27be80c51f..2c67b193a73 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -890,12 +890,23 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo Returns: Whether the type hint is a subclass of the other type hint. """ + if isinstance(possible_subclass, Sequence) and isinstance( + possible_superclass, Sequence + ): + return all( + typehint_issubclass(subclass, superclass) + for subclass, superclass in zip(possible_subclass, possible_superclass) + ) if possible_subclass is possible_superclass: return True if possible_superclass is Any: return True if possible_subclass is Any: return False + if isinstance( + possible_subclass, (TypeVar, typing_extensions.TypeVar) + ) or isinstance(possible_superclass, (TypeVar, typing_extensions.TypeVar)): + return True provided_type_origin = get_origin(possible_subclass) accepted_type_origin = get_origin(possible_superclass) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index b0f27bd6439..52e293f8270 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -151,6 +151,28 @@ def unwrap_reflex_callalbe( return args +_VAR_CACHING = True + + +def get_var_caching() -> bool: + """Get the var caching status. + + Returns: + The var caching status. + """ + return _VAR_CACHING + + +def set_var_caching(value: bool): + """Set the var caching status. + + Args: + value: The value to set the var caching status to. + """ + global _VAR_CACHING + _VAR_CACHING = value + + @dataclasses.dataclass( eq=False, frozen=True, @@ -1186,6 +1208,25 @@ def __get__(self, instance: Any, owner: Any): """ return self + def __getattribute__(self, name: str) -> Any: + """Get an attribute of the var. + + Args: + name: The name of the attribute. + + Returns: + The attribute. + """ + if not _VAR_CACHING: + try: + self_dict = object.__getattribute__(self, "__dict__") + for key in self_dict: + if key.startswith("_cached_"): + del self_dict[key] + except Exception: + pass + return super().__getattribute__(name) + def __getattr__(self, name: str): """Get an attribute of the var. diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 6b94e07bd97..5b7c1bfb6eb 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -741,7 +741,8 @@ def repeat_string_operation( def map_array_operation( array: Var[Sequence[INNER_ARRAY_VAR]], function: Var[ - ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR] + ReflexCallable[[INNER_ARRAY_VAR, int], ANOTHER_ARRAY_VAR] + | ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR] | ReflexCallable[[], ANOTHER_ARRAY_VAR] ], ) -> CustomVarOperationReturn[Sequence[ANOTHER_ARRAY_VAR]]: @@ -973,7 +974,8 @@ class ArrayVar(Var[ARRAY_VAR_TYPE], python_types=(Sequence, set)): def foreach( self: ArrayVar[Sequence[INNER_ARRAY_VAR]], - fn: Callable[[Var[INNER_ARRAY_VAR]], ANOTHER_ARRAY_VAR] + fn: Callable[[Var[INNER_ARRAY_VAR], NumberVar[int]], ANOTHER_ARRAY_VAR] + | Callable[[Var[INNER_ARRAY_VAR]], ANOTHER_ARRAY_VAR] | Callable[[], ANOTHER_ARRAY_VAR], ) -> ArrayVar[Sequence[ANOTHER_ARRAY_VAR]]: """Apply a function to each element of the array. @@ -987,21 +989,36 @@ def foreach( Raises: VarTypeError: If the function takes more than one argument. """ + from reflex.state import ComponentState + from .function import ArgsFunctionOperation if not callable(fn): raise_unsupported_operand_types("foreach", (type(self), type(fn))) # get the number of arguments of the function num_args = len(inspect.signature(fn).parameters) - if num_args > 1: + if num_args > 2: raise VarTypeError( - "The function passed to foreach should take at most one argument." + "The function passed to foreach should take at most two arguments." + ) + + if ( + hasattr(fn, "__qualname__") + and fn.__qualname__ == ComponentState.create.__qualname__ + ): + raise TypeError( + "Using a ComponentState as `render_fn` inside `rx.foreach` is not supported yet." ) if num_args == 0: - return_value = fn() # type: ignore + fn_result = fn() # pyright: ignore [reportCallIssue] + return_value = Var.create(fn_result) simple_function_var: FunctionVar[ReflexCallable[[], ANOTHER_ARRAY_VAR]] = ( - ArgsFunctionOperation.create((), return_value) + ArgsFunctionOperation.create( + (), + return_value, + _var_type=ReflexCallable[[], return_value._var_type], + ) ) return map_array_operation(self, simple_function_var).guess_type() @@ -1021,11 +1038,40 @@ def foreach( ).guess_type(), ) + if num_args == 1: + fn_result = fn(first_arg) # pyright: ignore [reportCallIssue] + + return_value = Var.create(fn_result) + + function_var = cast( + Var[ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR]], + ArgsFunctionOperation.create( + (arg_name,), + return_value, + _var_type=ReflexCallable[[first_arg_type], return_value._var_type], + ), + ) + + return map_array_operation.call(self, function_var).guess_type() + + second_arg = cast( + NumberVar[int], + Var( + _js_expr=get_unique_variable_name(), + _var_type=int, + ).guess_type(), + ) + + fn_result = fn(first_arg, second_arg) # pyright: ignore [reportCallIssue] + + return_value = Var.create(fn_result) + function_var = cast( - Var[ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR]], + Var[ReflexCallable[[INNER_ARRAY_VAR, int], ANOTHER_ARRAY_VAR]], ArgsFunctionOperation.create( - (arg_name,), - Var.create(fn(first_arg)), # type: ignore + (arg_name, second_arg._js_expr), + return_value, + _var_type=ReflexCallable[[first_arg_type, int], return_value._var_type], ), ) diff --git a/tests/units/components/core/test_foreach.py b/tests/units/components/core/test_foreach.py index ddc385f65f6..c1659540b24 100644 --- a/tests/units/components/core/test_foreach.py +++ b/tests/units/components/core/test_foreach.py @@ -6,16 +6,11 @@ from reflex import el from reflex.base import Base from reflex.components.component import Component -from reflex.components.core.foreach import ( - Foreach, - ForeachRenderError, - ForeachVarError, - foreach, -) +from reflex.components.core.foreach import ForeachVarError, foreach from reflex.components.radix.themes.layout.box import box from reflex.components.radix.themes.typography.text import text from reflex.state import BaseState, ComponentState -from reflex.vars.base import Var +from reflex.utils.exceptions import VarTypeError from reflex.vars.number import NumberVar from reflex.vars.sequence import ArrayVar @@ -141,143 +136,35 @@ def display_color_index_tuple(color): seen_index_vars = set() -@pytest.mark.parametrize( - "state_var, render_fn, render_dict", - [ - ( - ForEachState.colors_list, - display_color, - { - "iterable_state": f"{ForEachState.get_full_name()}.colors_list", - "iterable_type": "list", - }, - ), - ( - ForEachState.colors_dict_list, - display_color_name, - { - "iterable_state": f"{ForEachState.get_full_name()}.colors_dict_list", - "iterable_type": "list", - }, - ), - ( - ForEachState.colors_nested_dict_list, - display_shade, - { - "iterable_state": f"{ForEachState.get_full_name()}.colors_nested_dict_list", - "iterable_type": "list", - }, - ), - ( - ForEachState.primary_color, - display_primary_colors, - { - "iterable_state": f"{ForEachState.get_full_name()}.primary_color", - "iterable_type": "dict", - }, - ), - ( - ForEachState.color_with_shades, - display_color_with_shades, - { - "iterable_state": f"{ForEachState.get_full_name()}.color_with_shades", - "iterable_type": "dict", - }, - ), - ( - ForEachState.nested_colors_with_shades, - display_nested_color_with_shades, - { - "iterable_state": f"{ForEachState.get_full_name()}.nested_colors_with_shades", - "iterable_type": "dict", - }, - ), - ( - ForEachState.nested_colors_with_shades, - display_nested_color_with_shades_v2, - { - "iterable_state": f"{ForEachState.get_full_name()}.nested_colors_with_shades", - "iterable_type": "dict", - }, - ), - ( - ForEachState.color_tuple, - display_color_tuple, - { - "iterable_state": f"{ForEachState.get_full_name()}.color_tuple", - "iterable_type": "tuple", - }, - ), - ( - ForEachState.colors_set, - display_colors_set, - { - "iterable_state": f"{ForEachState.get_full_name()}.colors_set", - "iterable_type": "set", - }, - ), - ( - ForEachState.nested_colors_list, - lambda el, i: display_nested_list_element(el, i), - { - "iterable_state": f"{ForEachState.get_full_name()}.nested_colors_list", - "iterable_type": "list", - }, - ), - ( - ForEachState.color_index_tuple, - display_color_index_tuple, - { - "iterable_state": f"{ForEachState.get_full_name()}.color_index_tuple", - "iterable_type": "tuple", - }, - ), - ], -) -def test_foreach_render(state_var, render_fn, render_dict): - """Test that the foreach component renders without error. - - Args: - state_var: the state var. - render_fn: The render callable - render_dict: return dict on calling `component.render` - """ - component = Foreach.create(state_var, render_fn) - - rend = component.render() - assert rend["iterable_state"] == render_dict["iterable_state"] - assert rend["iterable_type"] == render_dict["iterable_type"] - - # Make sure the index vars are unique. - arg_index = rend["arg_index"] - assert isinstance(arg_index, Var) - assert arg_index._js_expr not in seen_index_vars - assert arg_index._var_type is int - seen_index_vars.add(arg_index._js_expr) - - def test_foreach_bad_annotations(): """Test that the foreach component raises a ForeachVarError if the iterable is of type Any.""" with pytest.raises(ForeachVarError): - Foreach.create( + foreach( ForEachState.bad_annotation_list, - lambda sublist: Foreach.create(sublist, lambda color: text(color)), + lambda sublist: foreach(sublist, lambda color: text(color)), ) def test_foreach_no_param_in_signature(): - """Test that the foreach component raises a ForeachRenderError if no parameters are passed.""" - with pytest.raises(ForeachRenderError): - Foreach.create( - ForEachState.colors_list, - lambda: text("color"), - ) + """Test that the foreach component DOES NOT raise an error if no parameters are passed.""" + foreach( + ForEachState.colors_list, + lambda: text("color"), + ) + + +def test_foreach_with_index(): + """Test that the foreach component works with an index.""" + foreach( + ForEachState.colors_list, + lambda color, index: text(color, index), + ) def test_foreach_too_many_params_in_signature(): """Test that the foreach component raises a ForeachRenderError if too many parameters are passed.""" - with pytest.raises(ForeachRenderError): - Foreach.create( + with pytest.raises(VarTypeError): + foreach( ForEachState.colors_list, lambda color, index, extra: text(color), ) @@ -292,13 +179,13 @@ def test_foreach_component_styles(): ) ) component._add_style_recursive({box: {"color": "red"}}) - assert 'css={({ ["color"] : "red" })}' in str(component) + assert '{ ["css"] : ({ ["color"] : "red" }) }' in str(component) def test_foreach_component_state(): """Test that using a component state to render in the foreach raises an error.""" with pytest.raises(TypeError): - Foreach.create( + foreach( ForEachState.colors_list, ComponentStateTest.create, ) @@ -306,7 +193,7 @@ def test_foreach_component_state(): def test_foreach_default_factory(): """Test that the default factory is called.""" - _ = Foreach.create( + _ = foreach( ForEachState.default_factory_list, lambda tag: text(tag.name), ) diff --git a/tests/units/components/test_component.py b/tests/units/components/test_component.py index 64b798cf37a..2544674f7d7 100644 --- a/tests/units/components/test_component.py +++ b/tests/units/components/test_component.py @@ -1446,7 +1446,6 @@ def test_instantiate_all_components(): untested_components = { "Card", "DebounceInput", - "Foreach", "FormControl", "Html", "Icon", @@ -2147,14 +2146,11 @@ def add_style(self): page = rx.vstack(rx.foreach(Var.range(3), lambda i: StyledComponent.create(i))) page._add_style_recursive(Style()) - # Expect only a single child of the foreach on the python side - assert len(page.children[0].children) == 1 - # Expect the style to be added to the child of the foreach - assert 'css={({ ["color"] : "red" })}' in str(page.children[0].children[0]) + assert '({ ["css"] : ({ ["color"] : "red" }) }),' in str(page.children[0]) # Expect only one instance of this CSS dict in the rendered page - assert str(page).count('css={({ ["color"] : "red" })}') == 1 + assert str(page).count('({ ["css"] : ({ ["color"] : "red" }) }),') == 1 class TriggerState(rx.State): From be92063421ec08958e89fa8ee063f761a224df3f Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 18:21:28 -0800 Subject: [PATCH 67/86] dang it darglint --- reflex/components/core/foreach.py | 1 - reflex/vars/sequence.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/components/core/foreach.py b/reflex/components/core/foreach.py index 4b6192e0b47..c032727bbbb 100644 --- a/reflex/components/core/foreach.py +++ b/reflex/components/core/foreach.py @@ -32,7 +32,6 @@ def foreach( Raises: ForeachVarError: If the iterable is of type Any. - TypeError: If the render function is a ComponentState. """ iterable = LiteralVar.create(iterable) if isinstance(iterable, ObjectVar): diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 5b7c1bfb6eb..1f56216a578 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -988,6 +988,7 @@ def foreach( Raises: VarTypeError: If the function takes more than one argument. + TypeError: If the function is a ComponentState. """ from reflex.state import ComponentState From 5e45ca509bfa35c15724fc61004dfb0932a1869d Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 17 Jan 2025 18:23:38 -0800 Subject: [PATCH 68/86] remove iterable from jinja --- .../jinja/web/pages/utils.js.jinja2 | 26 ------------------- reflex/components/component.py | 22 +--------------- 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/reflex/.templates/jinja/web/pages/utils.js.jinja2 b/reflex/.templates/jinja/web/pages/utils.js.jinja2 index e046c9160a6..176a5d063ac 100644 --- a/reflex/.templates/jinja/web/pages/utils.js.jinja2 +++ b/reflex/.templates/jinja/web/pages/utils.js.jinja2 @@ -6,8 +6,6 @@ {% filter indent(width=indent_width) %} {%- if component is not mapping %} {{- component }} - {%- elif "iterable" in component %} - {{- render_iterable_tag(component) }} {%- elif component.children|length %} {{- render_tag(component) }} {%- else %} @@ -40,30 +38,6 @@ {%- endmacro %} -{# Rendering condition component. #} -{# Args: #} -{# component: component dictionary #} -{% macro render_condition_tag(component) %} -{ {{- component.cond_state }} ? ( - {{ render(component.true_value) }} -) : ( - {{ render(component.false_value) }} -)} -{%- endmacro %} - - -{# Rendering iterable component. #} -{# Args: #} -{# component: component dictionary #} -{% macro render_iterable_tag(component) %} -<>{ {%- if component.iterable_type == 'dict' -%}Object.entries({{- component.iterable_state }}){%- else -%}{{- component.iterable_state }}{%- endif -%}.map(({{ component.arg_name }}, {{ component.arg_index }}) => ( - {% for child in component.children %} - {{ render(child) }} - {% endfor %} -))} -{%- endmacro %} - - {# Rendering props of a component. #} {# Args: #} {# component: component dictionary #} diff --git a/reflex/components/component.py b/reflex/components/component.py index fe6b9faa382..351be7cbb9a 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -66,7 +66,7 @@ Var, cached_property_no_lock, ) -from reflex.vars.function import ArgsFunctionOperation, FunctionStringVar +from reflex.vars.function import FunctionStringVar from reflex.vars.object import ObjectVar from reflex.vars.sequence import LiteralArrayVar @@ -2399,26 +2399,6 @@ def render_dict_to_var(tag: dict | Component | str, imported_names: set[str]) -> return render_dict_to_var(tag.render(), imported_names) return Var.create(tag) - if "iterable" in tag: - function_return = Var.create( - [ - render_dict_to_var(child.render(), imported_names) - for child in tag["children"] - ] - ) - - func = ArgsFunctionOperation.create( - (tag["arg_var_name"], tag["index_var_name"]), - function_return, - ) - - return FunctionStringVar.create("Array.prototype.map.call").call( - tag["iterable"] - if not isinstance(tag["iterable"], ObjectVar) - else tag["iterable"].items(), - func, - ) - props = {} special_props = [] From 1f9fbd88de71c74cde827868b4665d142f328d47 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 22 Jan 2025 12:47:21 -0800 Subject: [PATCH 69/86] resolve merge mistakes --- reflex/components/core/cond.py | 4 ++-- reflex/event.py | 19 ------------------- reflex/utils/types.py | 21 +-------------------- reflex/vars/base.py | 9 ++++----- reflex/vars/object.py | 2 +- 5 files changed, 8 insertions(+), 47 deletions(-) diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index fbe91043f46..62f4e92cd32 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -7,7 +7,7 @@ from reflex.components.base.fragment import Fragment from reflex.components.component import BaseComponent, Component from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode -from reflex.utils.types import infallible_issubclass +from reflex.utils.types import safe_issubclass from reflex.vars.base import LiteralVar, ReflexCallable, Var from reflex.vars.function import ArgsFunctionOperation from reflex.vars.number import ternary_operation @@ -40,7 +40,7 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var: # If the first component is a component, create a Fragment if the second component is not set. if isinstance(c1, BaseComponent) or ( - isinstance(c1, Var) and infallible_issubclass(c1._var_type, BaseComponent) + isinstance(c1, Var) and safe_issubclass(c1._var_type, BaseComponent) ): c2 = c2 if c2 is not None else Fragment.create() diff --git a/reflex/event.py b/reflex/event.py index 1e4d848db5d..700066c8285 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -34,7 +34,6 @@ TypeVar, TypeVarTuple, Unpack, - deprecated, get_args, get_origin, ) @@ -803,24 +802,6 @@ def redirect( path: str | Var[str], is_external: bool = False, replace: bool = False, -) -> EventSpec: ... - - -@overload -@deprecated("`external` is deprecated use `is_external` instead") -def redirect( - path: str | Var[str], - is_external: Optional[bool] = None, - replace: bool = False, - external: Optional[bool] = None, -) -> EventSpec: ... - - -def redirect( - path: str | Var[str], - is_external: Optional[bool] = None, - replace: bool = False, - external: Optional[bool] = None, ) -> EventSpec: """Redirect to a new path. diff --git a/reflex/utils/types.py b/reflex/utils/types.py index e37179ec2d2..6be54aff536 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -842,25 +842,6 @@ def wrapper(*args, **kwargs): def safe_issubclass(cls: Any, class_or_tuple: Any, /) -> bool: """Check if a class is a subclass of another class or a tuple of classes. - Args: - cls: The class to check. - class_or_tuple: The class or tuple of classes to check against. - - Returns: - Whether the class is a subclass of the other class or tuple of classes. - - Raises: - TypeError: If the arguments are invalid. - """ - try: - return issubclass(cls, class_or_tuple) - except TypeError as e: - return False - - -def infallible_issubclass(cls: Any, class_or_tuple: Any, /) -> bool: - """Check if a class is a subclass of another class or a tuple of classes. - Args: cls: The class to check. class_or_tuple: The class or tuple of classes to check against. @@ -981,7 +962,7 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo return True # Check if the origin of both types is the same (e.g., list for List[int]) - if not infallible_issubclass( + if not safe_issubclass( provided_type_origin or possible_subclass, accepted_type_origin or possible_superclass, ): diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 1fb2b0538f4..f5e99ce9400 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -74,9 +74,8 @@ _isinstance, get_origin, has_args, - infallible_issubclass, - typehint_issubclass, safe_issubclass, + typehint_issubclass, unionize, ) @@ -764,7 +763,7 @@ def to( for var_subclass in _var_subclasses[::-1]: if ( var_subclass.python_types - and infallible_issubclass(fixed_output_type, var_subclass.python_types) + and safe_issubclass(fixed_output_type, var_subclass.python_types) ) or ( var_subclass.is_subclass and var_subclass.is_subclass(fixed_output_type) ): @@ -863,7 +862,7 @@ def guess_type(self) -> Var: for var_subclass in _var_subclasses: if all( ( - infallible_issubclass(t, var_subclass.python_types) + safe_issubclass(t, var_subclass.python_types) or (var_subclass.is_subclass and var_subclass.is_subclass(t)) ) for t in inner_types @@ -886,7 +885,7 @@ def guess_type(self) -> Var: return self.to(None) for var_subclass in _var_subclasses[::-1]: - if infallible_issubclass(fixed_type, var_subclass.python_types) or ( + if safe_issubclass(fixed_type, var_subclass.python_types) or ( var_subclass.is_subclass and var_subclass.is_subclass(fixed_type) ): return self.to(var_subclass.var_subclass, self._var_type) diff --git a/reflex/vars/object.py b/reflex/vars/object.py index 732bcb40d55..00d14fa8d14 100644 --- a/reflex/vars/object.py +++ b/reflex/vars/object.py @@ -143,7 +143,7 @@ def merge(self, other: ObjectVar): # NoReturn is used here to catch when key value is Any @overload - def __getitem__( + def __getitem__( # pyright: ignore [reportOverlappingOverload] self: ObjectVar[Mapping[Any, NoReturn]], key: Var | Any, ) -> Var: ... From 8173e1069865d893ba96c47728c5c26aecdec44f Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 22 Jan 2025 12:51:01 -0800 Subject: [PATCH 70/86] ok we can unbreak foreach just for you --- reflex/components/core/foreach.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/reflex/components/core/foreach.py b/reflex/components/core/foreach.py index c032727bbbb..46faf16b299 100644 --- a/reflex/components/core/foreach.py +++ b/reflex/components/core/foreach.py @@ -45,3 +45,9 @@ def foreach( ) return iterable.foreach(render_fn) + + +class Foreach: + """Create a list of components from an iterable.""" + + create = staticmethod(foreach) From 84d3a2bb97f690f089f615ac924097fdb4575bc6 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 22 Jan 2025 12:53:16 -0800 Subject: [PATCH 71/86] unbreak cond why not --- reflex/components/core/cond.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index 62f4e92cd32..7ba5f2fc0a2 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -90,3 +90,9 @@ def color_mode_cond(light: Any, dark: Any = None) -> Var | Component: light, dark, ) + + +class Cond: + """Create a conditional component or Prop.""" + + create = staticmethod(cond) From e10cf07506bc5ff7e430fd7910226a1c553ba2dd Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 22 Jan 2025 13:14:55 -0800 Subject: [PATCH 72/86] make icon to static methods --- reflex/components/radix/themes/color_mode.py | 2 +- reflex/components/radix/themes/color_mode.pyi | 2 +- reflex/utils/exec.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/reflex/components/radix/themes/color_mode.py b/reflex/components/radix/themes/color_mode.py index dae82903fba..875a53456ba 100644 --- a/reflex/components/radix/themes/color_mode.py +++ b/reflex/components/radix/themes/color_mode.py @@ -190,7 +190,7 @@ def create(cls, *children, **props): class ColorModeNamespace(Var): """Namespace for color mode components.""" - icon = color_mode_icon + icon = staticmethod(color_mode_icon) button = staticmethod(ColorModeIconButton.create) switch = staticmethod(ColorModeSwitch.create) diff --git a/reflex/components/radix/themes/color_mode.pyi b/reflex/components/radix/themes/color_mode.pyi index add7a125153..c9b7b7953d0 100644 --- a/reflex/components/radix/themes/color_mode.pyi +++ b/reflex/components/radix/themes/color_mode.pyi @@ -403,7 +403,7 @@ class ColorModeSwitch(Switch): ... class ColorModeNamespace(Var): - icon = color_mode_icon + icon = staticmethod(color_mode_icon) button = staticmethod(ColorModeIconButton.create) switch = staticmethod(ColorModeSwitch.create) diff --git a/reflex/utils/exec.py b/reflex/utils/exec.py index 9ee0f1018b1..12b2e0aed8f 100644 --- a/reflex/utils/exec.py +++ b/reflex/utils/exec.py @@ -262,7 +262,7 @@ def get_reload_dirs() -> list[Path]: break reload_dirs = [module_path] - return reload_dirs + return list(map(Path.absolute, reload_dirs)) def run_uvicorn_backend(host, port, loglevel: LogLevel): From c6e2368c95f2360e996ef0714b5bb88c45c09f1d Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 22 Jan 2025 13:35:04 -0800 Subject: [PATCH 73/86] callable vars are not good --- reflex/components/core/foreach.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/components/core/foreach.py b/reflex/components/core/foreach.py index 46faf16b299..1678642ea1f 100644 --- a/reflex/components/core/foreach.py +++ b/reflex/components/core/foreach.py @@ -33,7 +33,7 @@ def foreach( Raises: ForeachVarError: If the iterable is of type Any. """ - iterable = LiteralVar.create(iterable) + iterable = LiteralVar.create(iterable).guess_type() if isinstance(iterable, ObjectVar): iterable = iterable.items() From 540382dd3e33935e4418349acba38c607ea9c466 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 22 Jan 2025 15:04:31 -0800 Subject: [PATCH 74/86] kill callable var --- reflex/components/core/upload.py | 4 +- reflex/components/core/upload.pyi | 4 +- reflex/style.py | 3 +- reflex/vars/base.py | 55 ------------------- tests/integration/test_upload.py | 2 +- .../tests_playwright/test_appearance.py | 2 +- 6 files changed, 5 insertions(+), 65 deletions(-) diff --git a/reflex/components/core/upload.py b/reflex/components/core/upload.py index 14205cc6b93..360f06b1a40 100644 --- a/reflex/components/core/upload.py +++ b/reflex/components/core/upload.py @@ -29,7 +29,7 @@ from reflex.utils import format from reflex.utils.imports import ImportVar from reflex.vars import VarData -from reflex.vars.base import CallableVar, Var, get_unique_variable_name +from reflex.vars.base import Var, get_unique_variable_name from reflex.vars.sequence import LiteralStringVar DEFAULT_UPLOAD_ID: str = "default" @@ -45,7 +45,6 @@ ) -@CallableVar def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> Var: """Get the file upload drop trigger. @@ -75,7 +74,6 @@ def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> Var: ) -@CallableVar def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> Var: """Get the list of selected files. diff --git a/reflex/components/core/upload.pyi b/reflex/components/core/upload.pyi index 6238ff9cb59..85418c8ad61 100644 --- a/reflex/components/core/upload.pyi +++ b/reflex/components/core/upload.pyi @@ -13,14 +13,12 @@ from reflex.event import BASE_STATE, CallableEventSpec, EventSpec, EventType from reflex.style import Style from reflex.utils.imports import ImportVar from reflex.vars import VarData -from reflex.vars.base import CallableVar, Var +from reflex.vars.base import Var DEFAULT_UPLOAD_ID: str upload_files_context_var_data: VarData -@CallableVar def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> Var: ... -@CallableVar def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> Var: ... @CallableEventSpec def clear_selected_files(id_: str = DEFAULT_UPLOAD_ID) -> EventSpec: ... diff --git a/reflex/style.py b/reflex/style.py index 3916bbc7c16..9a4c5f2075b 100644 --- a/reflex/style.py +++ b/reflex/style.py @@ -12,7 +12,7 @@ from reflex.utils.imports import ImportVar from reflex.utils.types import get_origin from reflex.vars import VarData -from reflex.vars.base import CallableVar, LiteralVar, Var +from reflex.vars.base import LiteralVar, Var from reflex.vars.function import FunctionVar from reflex.vars.object import ObjectVar @@ -48,7 +48,6 @@ def _color_mode_var(_js_expr: str, _var_type: Type = str) -> Var: ).guess_type() -@CallableVar def set_color_mode( new_color_mode: LiteralColorMode | Var[LiteralColorMode] | None = None, ) -> Var[EventChain]: diff --git a/reflex/vars/base.py b/reflex/vars/base.py index f5e99ce9400..f11294749b2 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -1991,61 +1991,6 @@ def __hash__(self) -> int: ) -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class CallableVar(Var): - """Decorate a Var-returning function to act as both a Var and a function. - - This is used as a compatibility shim for replacing Var objects in the - API with functions that return a family of Var. - """ - - fn: Callable[..., Var] = dataclasses.field( - default_factory=lambda: lambda: Var(_js_expr="undefined") - ) - original_var: Var = dataclasses.field( - default_factory=lambda: Var(_js_expr="undefined") - ) - - def __init__(self, fn: Callable[..., Var]): - """Initialize a CallableVar. - - Args: - fn: The function to decorate (must return Var) - """ - original_var = fn() - super(CallableVar, self).__init__( - _js_expr=original_var._js_expr, - _var_type=original_var._var_type, - _var_data=VarData.merge(original_var._get_all_var_data()), - ) - object.__setattr__(self, "fn", fn) - object.__setattr__(self, "original_var", original_var) - - def __call__(self, *args, **kwargs) -> Var: - """Call the decorated function. - - Args: - *args: The args to pass to the function. - **kwargs: The kwargs to pass to the function. - - Returns: - The Var returned from calling the function. - """ - return self.fn(*args, **kwargs) - - def __hash__(self) -> int: - """Calculate the hash of the object. - - Returns: - The hash of the object. - """ - return hash((type(self).__name__, self.original_var)) - - RETURN_TYPE = TypeVar("RETURN_TYPE") DICT_KEY = TypeVar("DICT_KEY") diff --git a/tests/integration/test_upload.py b/tests/integration/test_upload.py index 0331c15d6b1..384d0df8a6b 100644 --- a/tests/integration/test_upload.py +++ b/tests/integration/test_upload.py @@ -85,7 +85,7 @@ def index(): ), rx.box( rx.foreach( - rx.selected_files, + rx.selected_files(), lambda f: rx.text(f, as_="p"), ), id="selected_files", diff --git a/tests/integration/tests_playwright/test_appearance.py b/tests/integration/tests_playwright/test_appearance.py index 60aeeaa6b51..8e1c72fe358 100644 --- a/tests/integration/tests_playwright/test_appearance.py +++ b/tests/integration/tests_playwright/test_appearance.py @@ -61,7 +61,7 @@ def index(): rx.icon(tag="moon", size=20), value="dark", ), - on_change=set_color_mode, + on_change=set_color_mode(), variant="classic", radius="large", value=color_mode, From b2a27cb8c1457bd153717f13fc700a29f20a5177 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 22 Jan 2025 16:17:08 -0800 Subject: [PATCH 75/86] fix list of component is a component --- reflex/components/core/cond.py | 7 ++++++- reflex/utils/types.py | 16 ++++++++++++++++ reflex/vars/sequence.py | 15 ++++++++++++--- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index 7ba5f2fc0a2..0ab543248e3 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -7,6 +7,7 @@ from reflex.components.base.fragment import Fragment from reflex.components.component import BaseComponent, Component from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode +from reflex.utils import types from reflex.utils.types import safe_issubclass from reflex.vars.base import LiteralVar, ReflexCallable, Var from reflex.vars.function import ArgsFunctionOperation @@ -40,7 +41,11 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var: # If the first component is a component, create a Fragment if the second component is not set. if isinstance(c1, BaseComponent) or ( - isinstance(c1, Var) and safe_issubclass(c1._var_type, BaseComponent) + isinstance(c1, Var) + and ( + safe_issubclass(c1._var_type, BaseComponent) + or types.safe_typehint_issubclass(c1._var_type, list[BaseComponent]) + ) ): c2 = c2 if c2 is not None else Fragment.create() diff --git a/reflex/utils/types.py b/reflex/utils/types.py index 6be54aff536..3f10fa757f7 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -976,3 +976,19 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo for provided_arg, accepted_arg in zip(provided_args, accepted_args) if accepted_arg is not Any and not isinstance(accepted_arg, TypeVar) ) + + +def safe_typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> bool: + """Check if a type hint is a subclass of another type hint. + + Args: + possible_subclass: The type hint to check. + possible_superclass: The type hint to check against. + + Returns: + Whether the type hint is a subclass of the other type hint. + """ + try: + return typehint_issubclass(possible_subclass, possible_superclass) + except Exception: + return False diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 1f56216a578..d9d097e861a 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -996,13 +996,22 @@ def foreach( if not callable(fn): raise_unsupported_operand_types("foreach", (type(self), type(fn))) + # get the number of arguments of the function - num_args = len(inspect.signature(fn).parameters) - if num_args > 2: + required_num_args = len( + [ + p + for p in inspect.signature(fn).parameters.values() + if p.default != p.empty + ] + ) + if required_num_args > 2: raise VarTypeError( "The function passed to foreach should take at most two arguments." ) + num_args = len(inspect.signature(fn).parameters) + if ( hasattr(fn, "__qualname__") and fn.__qualname__ == ComponentState.create.__qualname__ @@ -1039,7 +1048,7 @@ def foreach( ).guess_type(), ) - if num_args == 1: + if required_num_args < 2: fn_result = fn(first_arg) # pyright: ignore [reportCallIssue] return_value = Var.create(fn_result) From 6a50b3a29e28eb80da5c1e6589f398e60f12c8de Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 22 Jan 2025 16:24:03 -0800 Subject: [PATCH 76/86] i was a bit silly --- reflex/vars/sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index d9d097e861a..452eb29b6a8 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -1002,7 +1002,7 @@ def foreach( [ p for p in inspect.signature(fn).parameters.values() - if p.default != p.empty + if p.default == p.empty ] ) if required_num_args > 2: From 19dd15bd4494b6da9008eaa3a1eb0cafcccd7fc1 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Wed, 22 Jan 2025 15:53:02 -0800 Subject: [PATCH 77/86] Banner components that return Fragment inherit from Fragment --- reflex/components/core/banner.py | 13 +++++-------- reflex/components/core/banner.pyi | 5 +++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/reflex/components/core/banner.py b/reflex/components/core/banner.py index aa75bb2c7ed..815510e8bfa 100644 --- a/reflex/components/core/banner.py +++ b/reflex/components/core/banner.py @@ -4,6 +4,7 @@ from typing import Optional +from reflex.components.base.fragment import Fragment from reflex.components.component import Component from reflex.components.core.cond import cond from reflex.components.el.elements.typography import Div @@ -162,7 +163,7 @@ def create(cls, *children, **props) -> Component: return super().create(*children, **props) -class ConnectionBanner(Component): +class ConnectionBanner(Fragment): """A connection banner component.""" @classmethod @@ -175,8 +176,6 @@ def create(cls, comp: Optional[Component] = None) -> Component: Returns: The connection banner component. """ - from reflex.components.base.fragment import Fragment - if not comp: comp = Flex.create( Text.create( @@ -191,10 +190,10 @@ def create(cls, comp: Optional[Component] = None) -> Component: position="fixed", ) - return Fragment.create(cond(has_connection_errors, comp)) + return super().create(cond(has_connection_errors, comp)) -class ConnectionModal(Component): +class ConnectionModal(Fragment): """A connection status modal window.""" @classmethod @@ -207,11 +206,9 @@ def create(cls, comp: Optional[Component] = None) -> Component: Returns: The connection banner component. """ - from reflex.components.base.fragment import Fragment - if not comp: comp = Text.create(*default_connection_error()) - return Fragment.create( + return super().create( cond( has_too_many_connection_errors, DialogRoot.create( diff --git a/reflex/components/core/banner.pyi b/reflex/components/core/banner.pyi index f44ee7992dd..0f10e0df1d0 100644 --- a/reflex/components/core/banner.pyi +++ b/reflex/components/core/banner.pyi @@ -5,6 +5,7 @@ # ------------------------------------------------------ from typing import Any, Dict, Literal, Optional, Union, overload +from reflex.components.base.fragment import Fragment from reflex.components.component import Component from reflex.components.el.elements.typography import Div from reflex.components.lucide.icon import Icon @@ -137,7 +138,7 @@ class ConnectionToaster(Toaster): """ ... -class ConnectionBanner(Component): +class ConnectionBanner(Fragment): @overload @classmethod def create( # type: ignore @@ -176,7 +177,7 @@ class ConnectionBanner(Component): """ ... -class ConnectionModal(Component): +class ConnectionModal(Fragment): @overload @classmethod def create( # type: ignore From 29fc4b020a54137c7e953946cf94eff1c9b885a7 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 22 Jan 2025 16:39:43 -0800 Subject: [PATCH 78/86] make the match logic better --- reflex/components/core/match.py | 53 +++++++++++++++-------- tests/units/components/core/test_match.py | 9 ++-- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/reflex/components/core/match.py b/reflex/components/core/match.py index d1b359e498f..a9e7e10c598 100644 --- a/reflex/components/core/match.py +++ b/reflex/components/core/match.py @@ -1,6 +1,5 @@ """rx.match.""" -import textwrap from typing import Any, cast from typing_extensions import Unpack @@ -46,27 +45,43 @@ def _validate_return_types(match_cases: tuple[CASE_TYPE[VAR_TYPE], ...]) -> None Raises: MatchTypeError: If the return types of cases are different. """ - first_case_return = match_cases[0][-1] - return_type = type(first_case_return) - if types._isinstance(first_case_return, BaseComponent): - return_type = BaseComponent - elif types._isinstance(first_case_return, Var): - return_type = Var - - for index, case in enumerate(match_cases): - if not ( - types._issubclass(type(case[-1]), return_type) - or ( - isinstance(case[-1], Var) - and types.typehint_issubclass(case[-1]._var_type, return_type) + def is_component_or_component_var(obj: Any) -> bool: + return types._isinstance(obj, BaseComponent) or ( + isinstance(obj, Var) + and ( + types.safe_typehint_issubclass(obj._var_type, BaseComponent) + or types.safe_typehint_issubclass(obj._var_type, list[BaseComponent]) ) - ): - raise MatchTypeError( - f"Match cases should have the same return types. Case {index} with return " - f"value `{case[-1]._js_expr if isinstance(case[-1], Var) else textwrap.shorten(str(case[-1]), width=250)}`" - f" of type {(type(case[-1]) if not isinstance(case[-1], Var) else case[-1]._var_type)!r} is not {return_type}" + ) + + def type_of_return_type(obj: Any) -> Any: + if isinstance(obj, Var): + return obj._var_type + return type(obj) + + return_types = [case[-1] for case in match_cases] + + if any( + is_component_or_component_var(return_type) for return_type in return_types + ) and not all( + is_component_or_component_var(return_type) for return_type in return_types + ): + non_component_return_types = [ + (type_of_return_type(return_type), i) + for i, return_type in enumerate(return_types) + if not is_component_or_component_var(return_type) + ] + raise MatchTypeError( + "Match cases should have the same return types. " + + "Expected return types to be of type Component or Var[Component]. " + + ". ".join( + [ + f"Return type of case {i} is {return_type}" + for return_type, i in non_component_return_types + ] ) + ) def _create_match_var( diff --git a/tests/units/components/core/test_match.py b/tests/units/components/core/test_match.py index 862bd5ad31d..129234c168c 100644 --- a/tests/units/components/core/test_match.py +++ b/tests/units/components/core/test_match.py @@ -1,3 +1,4 @@ +import re from typing import Tuple import pytest @@ -177,8 +178,7 @@ def test_match_case_tuple_elements(match_case): (MatchState.num + 1, "black"), rx.text("default value"), ), - "Match cases should have the same return types. Case 3 with return value `red` of type " - " is not ", + "Match cases should have the same return types. Expected return types to be of type Component or Var[Component]. Return type of case 3 is . Return type of case 4 is . Return type of case 5 is ", ), ( ( @@ -190,8 +190,7 @@ def test_match_case_tuple_elements(match_case): ([1, 2], rx.text("third value")), rx.text("default value"), ), - 'Match cases should have the same return types. Case 3 with return value ` {"first value"} ` ' - "of type is not ", + "Match cases should have the same return types. Expected return types to be of type Component or Var[Component]. Return type of case 0 is . Return type of case 1 is . Return type of case 2 is ", ), ], ) @@ -202,7 +201,7 @@ def test_match_different_return_types(cases: Tuple, error_msg: str): cases: The match cases. error_msg: Expected error message. """ - with pytest.raises(MatchTypeError, match=error_msg): + with pytest.raises(MatchTypeError, match=re.escape(error_msg)): match(MatchState.value, *cases) # pyright: ignore[reportCallIssue] From 6604784ca11b0b47abafdc316905da5bc3121574 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 23 Jan 2025 10:27:34 -0800 Subject: [PATCH 79/86] update pydantic requirement --- poetry.lock | 4 +++- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0ec5e7d7b46..f492584d001 100644 --- a/poetry.lock +++ b/poetry.lock @@ -461,6 +461,7 @@ files = [ {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"}, + {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385"}, {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"}, {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"}, {file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"}, @@ -471,6 +472,7 @@ files = [ {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"}, + {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba"}, {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"}, {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"}, {file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"}, @@ -2998,4 +3000,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "353bcb291d6d423963b380f3ee33769106487cacd7f8e34d6c420f2a87a66508" +content-hash = "06172aec112c6b3a49ffa42eadf09cb66408df76bcfcec8439396590f9a4c8d5" diff --git a/pyproject.toml b/pyproject.toml index 16640eabab7..ea3905f9c2a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ fastapi = ">=0.96.0,!=0.111.0,!=0.111.1" gunicorn = ">=20.1.0,<24.0" jinja2 = ">=3.1.2,<4.0" psutil = ">=5.9.4,<7.0" -pydantic = ">=1.10.15,<3.0" +pydantic = ">=1.10.17,<3.0" python-multipart = ">=0.0.5,<0.1" python-socketio = ">=5.7.0,<6.0" redis = ">=4.3.5,<6.0" From d3b12a84fa6450d1d007331b31ac968ac33a0e4e Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 23 Jan 2025 11:54:45 -0800 Subject: [PATCH 80/86] optimize var operation output --- reflex/components/core/cond.py | 20 +-- reflex/vars/base.py | 166 +++++++----------- reflex/vars/function.py | 94 ++++++++-- tests/units/components/core/test_colors.py | 6 +- tests/units/components/core/test_cond.py | 16 +- tests/units/components/core/test_match.py | 4 +- .../components/markdown/test_markdown.py | 8 +- tests/units/test_var.py | 141 ++++----------- 8 files changed, 202 insertions(+), 253 deletions(-) diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index 0ab543248e3..153953668c8 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -9,8 +9,7 @@ from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode from reflex.utils import types from reflex.utils.types import safe_issubclass -from reflex.vars.base import LiteralVar, ReflexCallable, Var -from reflex.vars.function import ArgsFunctionOperation +from reflex.vars.base import LiteralVar, Var from reflex.vars.number import ternary_operation @@ -53,23 +52,12 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var: if c2 is None: raise ValueError("For conditional vars, the second argument must be set.") - c1 = Var.create(c1) - c2 = Var.create(c2) - # Create the conditional var. return ternary_operation( cond_var.bool(), - ArgsFunctionOperation.create( - (), - c1, - _var_type=ReflexCallable[[], c1._var_type], - ), - ArgsFunctionOperation.create( - (), - c2, - _var_type=ReflexCallable[[], c2._var_type], - ), - ).call() + c1, + c2, + ) @overload diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 990a19e31e3..4502ec7bdbb 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -1788,7 +1788,7 @@ def var_operation( ```python @var_operation def add(a: Var[int], b: Var[int]): - return custom_var_operation(f"{a} + {b}") + return var_operation_return(f"{a} + {b}") ``` Args: @@ -1854,6 +1854,9 @@ def add(a: Var[int], b: Var[int]): custom_operation_return = func(*arg_vars) + def simplified_operation(*args): + return func(*args)._js_expr + args_operation = ArgsFunctionOperation.create( tuple(map(str, arg_vars)), custom_operation_return, @@ -1867,6 +1870,7 @@ def add(a: Var[int], b: Var[int]): function_name=func_name, type_computer=custom_operation_return._type_computer, _raw_js_function=custom_operation_return._raw_js_function, + _original_var_operation=simplified_operation, _var_type=ReflexCallable[ tuple( arg_python_type @@ -3222,15 +3226,7 @@ def and_operation(a: Var | Any, b: Var | Any) -> Var: Returns: The result of the logical AND operation. """ - from .function import ArgsFunctionOperation - - a = Var.create(a) - b = Var.create(b) - - return _and_func_operation( - ArgsFunctionOperation.create((), a, _var_type=ReflexCallable[[], a._var_type]), - ArgsFunctionOperation.create((), b, _var_type=ReflexCallable[[], b._var_type]), - ) + return _and_operation(a, b) def or_operation(a: Var | Any, b: Var | Any) -> Var: @@ -3243,99 +3239,7 @@ def or_operation(a: Var | Any, b: Var | Any) -> Var: Returns: The result of the logical OR operation. """ - from .function import ArgsFunctionOperation - - a = Var.create(a) - b = Var.create(b) - - return _or_func_operation( - ArgsFunctionOperation.create((), a, _var_type=ReflexCallable[[], a._var_type]), - ArgsFunctionOperation.create((), b, _var_type=ReflexCallable[[], b._var_type]), - ) - - -T_LOGICAL = TypeVar("T_LOGICAL") -U_LOGICAL = TypeVar("U_LOGICAL") - - -@var_operation -def _and_func_operation( - a: Var[ReflexCallable[[], T_LOGICAL]], b: Var[ReflexCallable[[], U_LOGICAL]] -) -> CustomVarOperationReturn[ReflexCallable[[], Union[T_LOGICAL, U_LOGICAL]]]: - """Perform a logical AND operation on two variables. - - Args: - a: The first variable. - b: The second variable. - - Returns: - The result of the logical AND operation. - """ - - def type_computer(*args: Var): - if not args: - return ( - ReflexCallable[[ReflexCallable[[], Any], ReflexCallable[[], Any]], Any], - type_computer, - ) - if len(args) == 1: - return ( - ReflexCallable[[ReflexCallable[[], Any]], Any], - functools.partial(type_computer, args[0]), - ) - - a_return_type = unwrap_reflex_callalbe(args[0]._var_type)[1] - b_return_type = unwrap_reflex_callalbe(args[1]._var_type)[1] - - return ( - ReflexCallable[[], unionize(a_return_type, b_return_type)], - None, - ) - - return var_operation_return( - js_expression=f"({a}() && {b}())", - type_computer=type_computer, - ) - - -@var_operation -def _or_func_operation( - a: Var[ReflexCallable[[], T_LOGICAL]], b: Var[ReflexCallable[[], U_LOGICAL]] -) -> CustomVarOperationReturn[ReflexCallable[[], Union[T_LOGICAL, U_LOGICAL]]]: - """Perform a logical OR operation on two variables. - - Args: - a: The first variable. - b: The second variable. - - Returns: - The result of the logical OR operation. - """ - - def type_computer(*args: Var): - if not args: - return ( - ReflexCallable[[ReflexCallable[[], Any], ReflexCallable[[], Any]], Any], - type_computer, - ) - if len(args) == 1: - return ( - ReflexCallable[[ReflexCallable[[], Any]], Any], - functools.partial(type_computer, args[0]), - ) - - a_return_type = unwrap_reflex_callalbe(args[0]._var_type)[1] - b_return_type = unwrap_reflex_callalbe(args[1]._var_type)[1] - - return ( - ReflexCallable[[], unionize(a_return_type, b_return_type)], - None, - ) - - return var_operation_return( - js_expression=f"({a}() || {b}())", - type_computer=type_computer, - ) + return _or_operation(a, b) def passthrough_unary_type_computer(no_args: GenericType) -> TypeComputer: @@ -3402,3 +3306,59 @@ def type_computer(*args: Var): ) return type_computer + + +T_LOGICAL = TypeVar("T_LOGICAL") +U_LOGICAL = TypeVar("U_LOGICAL") + + +@var_operation +def _and_operation( + a: Var[T_LOGICAL], b: Var[U_LOGICAL] +) -> CustomVarOperationReturn[Union[T_LOGICAL, U_LOGICAL]]: + """Perform a logical AND operation on two variables. + + Args: + a: The first variable. + b: The second variable. + + Returns: + The result of the logical AND operation. + """ + return var_operation_return( + js_expression=f"({a} && {b})", + type_computer=nary_type_computer( + ReflexCallable[[Any, Any], Any], + ReflexCallable[[Any], Any], + computer=lambda args: unionize( + args[0]._var_type, + args[1]._var_type, + ), + ), + ) + + +@var_operation +def _or_operation( + a: Var[T_LOGICAL], b: Var[U_LOGICAL] +) -> CustomVarOperationReturn[Union[T_LOGICAL, U_LOGICAL]]: + """Perform a logical OR operation on two variables. + + Args: + a: The first variable. + b: The second variable. + + Returns: + The result ocomputerf the logical OR operation. + """ + return var_operation_return( + js_expression=f"({a} || {b})", + type_computer=nary_type_computer( + ReflexCallable[[Any, Any], Any], + ReflexCallable[[Any], Any], + computer=lambda args: unionize( + args[0]._var_type, + args[1]._var_type, + ), + ), + ) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index c03c7c1d270..ba3b21c19a6 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -1219,19 +1219,26 @@ def call(self, *args: Var | Any) -> Var: # pyright: ignore [reportInconsistentO args = tuple(map(LiteralVar.create, args)) self._pre_check(*args) return_type = self._return_type(*args) - if ( - isinstance(self, (ArgsFunctionOperation, ArgsFunctionOperationBuilder)) - and self._raw_js_function + if arg_len == len(args) and isinstance( + self, (ArgsFunctionOperation, ArgsFunctionOperationBuilder) ): - return VarOperationCall.create( - FunctionStringVar.create( - self._raw_js_function, - _var_type=self._var_type, + if self._raw_js_function is not None: + return VarOperationCall.create( + FunctionStringVar.create( + self._raw_js_function, + _var_type=self._var_type, + _var_data=self._get_all_var_data(), + ), + *args, + _var_type=return_type, + ).guess_type() + if self._original_var_operation is not None: + return ExpressionCall.create( + self._original_var_operation, + *args, _var_data=self._get_all_var_data(), - ), - *args, - _var_type=return_type, - ).guess_type() + _var_type=return_type, + ).guess_type() return VarOperationCall.create(self, *args, _var_type=return_type).guess_type() @@ -1361,6 +1368,61 @@ def __get__(self, instance: Any, owner: Any): __call__ = call +@dataclasses.dataclass(frozen=True) +class ExpressionCall(CachedVarOperation, Var[R]): + """Class for expression calls.""" + + _original_var_operation: Callable = dataclasses.field(default=lambda *args: "") + _args: Tuple[Var, ...] = dataclasses.field(default_factory=tuple) + + @cached_property_no_lock + def _cached_var_name(self) -> str: + """The name of the var. + + Returns: + The name of the var. + """ + return self._original_var_operation(*self._args) + + @cached_property_no_lock + def _cached_get_all_var_data(self) -> VarData | None: + """Get all the var data associated with the var. + + Returns: + All the var data associated with the var. + """ + return VarData.merge( + *[arg._get_all_var_data() for arg in self._args], + self._var_data, + ) + + @classmethod + def create( + cls, + _original_var_operation: Callable, + *args: Var | Any, + _var_type: GenericType = Any, + _var_data: VarData | None = None, + ) -> ExpressionCall: + """Create a new expression call. + + Args: + _original_var_operation: The original var operation. + *args: The arguments to call the expression with. + _var_data: Additional hooks and imports associated with the Var. + + Returns: + The expression call var. + """ + return ExpressionCall( + _js_expr="", + _var_type=_var_type, + _var_data=_var_data, + _original_var_operation=_original_var_operation, + _args=args, + ) + + class BuilderFunctionVar( FunctionVar[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any] ): @@ -1600,6 +1662,7 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]): _type_computer: Optional[TypeComputer] = dataclasses.field(default=None) _explicit_return: bool = dataclasses.field(default=False) _raw_js_function: str | None = dataclasses.field(default=None) + _original_var_operation: Callable | None = dataclasses.field(default=None) _cached_var_name = cached_property_no_lock(format_args_function_operation) @@ -1619,6 +1682,7 @@ def create( explicit_return: bool = False, type_computer: Optional[TypeComputer] = None, _raw_js_function: str | None = None, + _original_var_operation: Callable | None = None, _var_type: GenericType = Callable, _var_data: VarData | None = None, ): @@ -1634,6 +1698,7 @@ def create( explicit_return: Whether to use explicit return syntax. type_computer: A function to compute the return type. _raw_js_function: If provided, it will be used when the operation is being called with all of its arguments at once. + _original_var_operation: The original var operation, if any. _var_type: The type of the var. _var_data: Additional hooks and imports associated with the Var. @@ -1647,6 +1712,7 @@ def create( _var_data=_var_data, _args=FunctionArgs(args=tuple(args_names), rest=rest), _raw_js_function=_raw_js_function, + _original_var_operation=_original_var_operation, _default_values=tuple(default_values), _function_name=function_name, _validators=tuple(validators), @@ -1678,6 +1744,7 @@ class ArgsFunctionOperationBuilder( _type_computer: Optional[TypeComputer] = dataclasses.field(default=None) _explicit_return: bool = dataclasses.field(default=False) _raw_js_function: str | None = dataclasses.field(default=None) + _original_var_operation: Callable | None = dataclasses.field(default=None) _cached_var_name = cached_property_no_lock(format_args_function_operation) @@ -1697,6 +1764,7 @@ def create( explicit_return: bool = False, type_computer: Optional[TypeComputer] = None, _raw_js_function: str | None = None, + _original_var_operation: Callable | None = None, _var_type: GenericType = Callable, _var_data: VarData | None = None, ): @@ -1711,9 +1779,10 @@ def create( function_name: The name of the function. explicit_return: Whether to use explicit return syntax. type_computer: A function to compute the return type. + _raw_js_function: If provided, it will be used when the operation is being called with all of its arguments at once. + _original_var_operation: The original var operation, if any. _var_type: The type of the var. _var_data: Additional hooks and imports associated with the Var. - _raw_js_function: If provided, it will be used when the operation is being called with all of its arguments at once. Returns: The function var. @@ -1725,6 +1794,7 @@ def create( _var_data=_var_data, _args=FunctionArgs(args=tuple(args_names), rest=rest), _raw_js_function=_raw_js_function, + _original_var_operation=_original_var_operation, _default_values=tuple(default_values), _function_name=function_name, _validators=tuple(validators), diff --git a/tests/units/components/core/test_colors.py b/tests/units/components/core/test_colors.py index 0a01bfcc046..c1295fb4158 100644 --- a/tests/units/components/core/test_colors.py +++ b/tests/units/components/core/test_colors.py @@ -39,7 +39,7 @@ def create_color_var(color): create_color_var( rx.color(ColorState.color, ColorState.shade, ColorState.alpha) # type: ignore ), - f'("var(--"+{color_state_name!s}.color+"-"+(((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))({color_state_name!s}.alpha, "a", ""))+(((__to_string) => __to_string.toString())({color_state_name!s}.shade))+")")', + f'("var(--"+{color_state_name!s}.color+"-"+({color_state_name!s}.alpha ? "a" : "")+(((__to_string) => __to_string.toString())({color_state_name!s}.shade))+")")', Color, ), ( @@ -78,11 +78,11 @@ def test_color(color, expected, expected_type: Union[Type[str], Type[Color]]): [ ( rx.cond(True, rx.color("mint"), rx.color("tomato", 5)), - '((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => "var(--mint-7)"), (() => "var(--tomato-5)")))())', + '(true ? "var(--mint-7)" : "var(--tomato-5)")', ), ( rx.cond(True, rx.color(ColorState.color), rx.color(ColorState.color, 5)), # type: ignore - f'((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => ("var(--"+{color_state_name!s}.color+"-7)")), (() => ("var(--"+{color_state_name!s}.color+"-5)"))))())', + f'(true ? ("var(--"+{color_state_name!s}.color+"-7)") : ("var(--"+{color_state_name!s}.color+"-5)"))', ), ( rx.match( diff --git a/tests/units/components/core/test_cond.py b/tests/units/components/core/test_cond.py index 06b10831735..98fa2e1218d 100644 --- a/tests/units/components/core/test_cond.py +++ b/tests/units/components/core/test_cond.py @@ -23,7 +23,7 @@ def test_f_string_cond_interpolation(): var = LiteralVar.create(f"x {cond(True, 'a', 'b')}") assert ( str(var) - == '("x "+((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => "a"), (() => "b")))()))' + == '("x "+(true ? "a" : "b"))' ) @@ -53,7 +53,7 @@ def test_validate_cond(cond_state: BaseState): assert isinstance(cond_var, Var) assert ( str(cond_var) - == f'((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))({cond_state.value.bool()!s}, (() => (jsx(RadixThemesText, ({{ ["as"] : "p" }}), (jsx(Fragment, ({{ }}), "cond is True"))))), (() => (jsx(RadixThemesText, ({{ ["as"] : "p" }}), (jsx(Fragment, ({{ }}), "cond is False")))))))())' + == f'({cond_state.value.bool()!s} ? (jsx(RadixThemesText, ({{ ["as"] : "p" }}), (jsx(Fragment, ({{ }}), "cond is True")))) : (jsx(RadixThemesText, ({{ ["as"] : "p" }}), (jsx(Fragment, ({{ }}), "cond is False")))))' ) var_data = cond_var._get_all_var_data() @@ -89,10 +89,7 @@ def test_prop_cond(c1: Any, c2: Any): c1 = json.dumps(c1) if not isinstance(c2, Var): c2 = json.dumps(c2) - assert ( - str(prop_cond) - == f"((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => {c1!s}), (() => {c2!s})))())" - ) + assert str(prop_cond) == f"(true ? {c1!s} : {c2!s})" def test_cond_mix(): @@ -101,7 +98,7 @@ def test_cond_mix(): assert isinstance(x, Var) assert ( str(x) - == '((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => "hello"), (() => (jsx(RadixThemesText, ({ ["as"] : "p" }), (jsx(Fragment, ({ }), "world")))))))())' + == '(true ? "hello" : (jsx(RadixThemesText, ({ ["as"] : "p" }), (jsx(Fragment, ({ }), "world")))))' ) @@ -112,7 +109,7 @@ def test_cond_no_else(): assert isinstance(comp, Var) assert ( str(comp) - == '((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => (jsx(RadixThemesText, ({ ["as"] : "p" }), (jsx(Fragment, ({ }), "hello"))))), (() => (jsx(Fragment, ({ }))))))())' + == '(true ? (jsx(RadixThemesText, ({ ["as"] : "p" }), (jsx(Fragment, ({ }), "hello")))) : (jsx(Fragment, ({ }))))' ) # Props do not support the use of cond without else @@ -139,8 +136,7 @@ def computed_str(self) -> str: state_name = format_state_name(CondStateComputed.get_full_name()) assert ( - str(comp) - == f"((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => {state_name}.computed_int), (() => {state_name}.computed_str)))())" + str(comp) == f"(true ? {state_name}.computed_int : {state_name}.computed_str)" ) assert comp._var_type == Union[int, str] diff --git a/tests/units/components/core/test_match.py b/tests/units/components/core/test_match.py index 129234c168c..b374b0f41dd 100644 --- a/tests/units/components/core/test_match.py +++ b/tests/units/components/core/test_match.py @@ -36,7 +36,7 @@ class MatchState(BaseState): f'(() => {{ switch (JSON.stringify({MatchState.get_name()}.value)) {{case JSON.stringify(1): return ("first"); break;case JSON.stringify(2): case JSON.stringify(3): return ' '("second value"); break;case JSON.stringify([1, 2]): return ("third-value"); break;case JSON.stringify("random"): ' 'return ("fourth_value"); break;case JSON.stringify(({ ["foo"] : "bar" })): return ("fifth value"); ' - f'break;case JSON.stringify((((_lhs, _rhs) => (_lhs + _rhs))({MatchState.get_name()}.num, 1))): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): ' + f'break;case JSON.stringify(({MatchState.get_name()}.num + 1)): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): ' f'return ({MatchState.get_name()}.string); break;case JSON.stringify({MatchState.get_name()}.string): return (({MatchState.get_name()}.value+" - string")); break;default: ' 'return ("default value"); break;};})()', ), @@ -55,7 +55,7 @@ class MatchState(BaseState): f'(() => {{ switch (JSON.stringify({MatchState.get_name()}.value)) {{case JSON.stringify(1): return ("first"); break;case JSON.stringify(2): case JSON.stringify(3): return ' '("second value"); break;case JSON.stringify([1, 2]): return ("third-value"); break;case JSON.stringify("random"): ' 'return ("fourth_value"); break;case JSON.stringify(({ ["foo"] : "bar" })): return ("fifth value"); ' - f'break;case JSON.stringify((((_lhs, _rhs) => (_lhs + _rhs))({MatchState.get_name()}.num, 1))): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): ' + f'break;case JSON.stringify(({MatchState.get_name()}.num + 1)): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): ' f'return ({MatchState.get_name()}.string); break;case JSON.stringify({MatchState.get_name()}.string): return (({MatchState.get_name()}.value+" - string")); break;default: ' f"return ({MatchState.get_name()}.string); break;}};}})()", ), diff --git a/tests/units/components/markdown/test_markdown.py b/tests/units/components/markdown/test_markdown.py index 93e30f0d378..6fddd0155e3 100644 --- a/tests/units/components/markdown/test_markdown.py +++ b/tests/units/components/markdown/test_markdown.py @@ -148,7 +148,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe ( "code", {}, - '(({node, inline, className, children, ...props}) => { const match = (className || \'\').match(/language-(?.*)/); const _language = match ? match[1] : \'\'; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( {children} ) : ( (_condition ? _if_true : _if_false))((Array.isArray(children)), (((...args) => (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\\n")), children))} css={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} customStyle={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} language={_language} style={((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))((((_lhs, _rhs) => (_lhs === _rhs))(resolvedColorMode, "light")), (() => oneLight), (() => oneDark)))())} wrapLongLines={true} {...props}/> ); })', + r"""(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( {children} ) : ( (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\n")) : children)} css={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} customStyle={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} language={_language} style={((resolvedColorMode === "light") ? oneLight : oneDark)} wrapLongLines={true} {...props}/> ); })""" ), ( "code", @@ -157,7 +157,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe value, **props ) }, - '(({node, inline, className, children, ...props}) => { const match = (className || \'\').match(/language-(?.*)/); const _language = match ? match[1] : \'\'; ; return inline ? ( {children} ) : ( (_condition ? _if_true : _if_false))((Array.isArray(children)), (((...args) => (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\\n")), children))} decorations={[]} language={_language} theme={((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))((((_lhs, _rhs) => (_lhs === _rhs))(resolvedColorMode, "light")), (() => "one-light"), (() => "one-dark-pro")))())} transformers={[]}/> ); })', + r"""(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; ; return inline ? ( {children} ) : ( (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\n")) : children)} decorations={[]} language={_language} theme={((resolvedColorMode === "light") ? "one-light" : "one-dark-pro")} transformers={[]}/> ); })""" ), ( "h1", @@ -171,7 +171,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe ( "code", {"codeblock": syntax_highlighter_memoized_component(CodeBlock)}, - "(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( {children} ) : ( (_condition ? _if_true : _if_false))((Array.isArray(children)), (((...args) => (((_array, _sep = \"\") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))(\"\\n\")), children))} language={_language} {...props}/> ); })", + r"""(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( {children} ) : ( (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\n")) : children)} language={_language} {...props}/> ); })""", ), ( "code", @@ -180,7 +180,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe ShikiHighLevelCodeBlock ) }, - """(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; ; return inline ? ( {children} ) : ( (_condition ? _if_true : _if_false))((Array.isArray(children)), (((...args) => (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\\n")), children))} language={_language} {...props}/> ); })""", + r"""(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; ; return inline ? ( {children} ) : ( (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\n")) : children)} language={_language} {...props}/> ); })""", ), ], ) diff --git a/tests/units/test_var.py b/tests/units/test_var.py index f395c4116a9..b66d2f17938 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -310,53 +310,21 @@ def test_basic_operations(TestObj): Args: TestObj: The test object. """ - assert str(v(1) == v(2)) == "(((_lhs, _rhs) => (_lhs === _rhs))(1, 2))" - assert str(v(1) != v(2)) == "(((_lhs, _rhs) => (_lhs !== _rhs))(1, 2))" - assert ( - str(LiteralNumberVar.create(1) < 2) == "(((_lhs, _rhs) => (_lhs < _rhs))(1, 2))" - ) - assert ( - str(LiteralNumberVar.create(1) <= 2) - == "(((_lhs, _rhs) => (_lhs <= _rhs))(1, 2))" - ) - assert ( - str(LiteralNumberVar.create(1) > 2) == "(((_lhs, _rhs) => (_lhs > _rhs))(1, 2))" - ) - assert ( - str(LiteralNumberVar.create(1) >= 2) - == "(((_lhs, _rhs) => (_lhs >= _rhs))(1, 2))" - ) - assert ( - str(LiteralNumberVar.create(1) + 2) == "(((_lhs, _rhs) => (_lhs + _rhs))(1, 2))" - ) - assert ( - str(LiteralNumberVar.create(1) - 2) == "(((_lhs, _rhs) => (_lhs - _rhs))(1, 2))" - ) - assert ( - str(LiteralNumberVar.create(1) * 2) == "(((_lhs, _rhs) => (_lhs * _rhs))(1, 2))" - ) - assert ( - str(LiteralNumberVar.create(1) / 2) == "(((_lhs, _rhs) => (_lhs / _rhs))(1, 2))" - ) - assert ( - str(LiteralNumberVar.create(1) // 2) - == "(((_lhs, _rhs) => Math.floor(_lhs / _rhs))(1, 2))" - ) - assert ( - str(LiteralNumberVar.create(1) % 2) == "(((_lhs, _rhs) => (_lhs % _rhs))(1, 2))" - ) - assert ( - str(LiteralNumberVar.create(1) ** 2) - == "(((_lhs, _rhs) => (_lhs ** _rhs))(1, 2))" - ) - assert ( - str(LiteralNumberVar.create(1) & v(2)) - == "(((_a, _b) => (_a() && _b()))((() => 1), (() => 2)))" - ) - assert ( - str(LiteralNumberVar.create(1) | v(2)) - == "(((_a, _b) => (_a() || _b()))((() => 1), (() => 2)))" - ) + assert str(v(1) == v(2)) == "(1 === 2)" + assert str(v(1) != v(2)) == "(1 !== 2)" + assert str(LiteralNumberVar.create(1) < 2) == "(1 < 2)" + assert str(LiteralNumberVar.create(1) <= 2) == "(1 <= 2)" + assert str(LiteralNumberVar.create(1) > 2) == "(1 > 2)" + assert str(LiteralNumberVar.create(1) >= 2) == "(1 >= 2)" + assert str(LiteralNumberVar.create(1) + 2) == "(1 + 2)" + assert str(LiteralNumberVar.create(1) - 2) == "(1 - 2)" + assert str(LiteralNumberVar.create(1) * 2) == "(1 * 2)" + assert str(LiteralNumberVar.create(1) / 2) == "(1 / 2)" + assert str(LiteralNumberVar.create(1) // 2) == "Math.floor(1 / 2)" + assert str(LiteralNumberVar.create(1) % 2) == "(1 % 2)" + assert str(LiteralNumberVar.create(1) ** 2) == "(1 ** 2)" + assert str(LiteralNumberVar.create(1) & v(2)) == "(1 && 2)" + assert str(LiteralNumberVar.create(1) | v(2)) == "(1 || 2)" assert ( str(LiteralArrayVar.create([1, 2, 3])[0]) == "(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3], ...args)))(0))" @@ -365,16 +333,10 @@ def test_basic_operations(TestObj): str(LiteralObjectVar.create({"a": 1, "b": 2})["a"]) == '({ ["a"] : 1, ["b"] : 2 })["a"]' ) + assert str(v("foo") == v("bar")) == '("foo" === "bar")' + assert str(Var(_js_expr="foo") == Var(_js_expr="bar")) == "(foo === bar)" assert ( - str(v("foo") == v("bar")) == '(((_lhs, _rhs) => (_lhs === _rhs))("foo", "bar"))' - ) - assert ( - str(Var(_js_expr="foo") == Var(_js_expr="bar")) - == "(((_lhs, _rhs) => (_lhs === _rhs))(foo, bar))" - ) - assert ( - str(LiteralVar.create("foo") == LiteralVar.create("bar")) - == '(((_lhs, _rhs) => (_lhs === _rhs))("foo", "bar"))' + str(LiteralVar.create("foo") == LiteralVar.create("bar")) == '("foo" === "bar")' ) print(Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state")) assert ( @@ -382,7 +344,7 @@ def test_basic_operations(TestObj): Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state").bar == LiteralVar.create("bar") ) - == '(((_lhs, _rhs) => (_lhs === _rhs))(state.foo["bar"], "bar"))' + == '(state.foo["bar"] === "bar")' ) assert ( str(Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state").bar) @@ -543,32 +505,17 @@ def test_str_contains(var, expected): ], ) def test_dict_contains(var, expected): - assert ( - str(var.contains(1)) - == f"(((_object, _key) => _object.hasOwnProperty(_key))({expected!s}, 1))" - ) - assert ( - str(var.contains("1")) - == f'(((_object, _key) => _object.hasOwnProperty(_key))({expected!s}, "1"))' - ) - assert ( - str(var.contains(v(1))) - == f"(((_object, _key) => _object.hasOwnProperty(_key))({expected!s}, 1))" - ) - assert ( - str(var.contains(v("1"))) - == f'(((_object, _key) => _object.hasOwnProperty(_key))({expected!s}, "1"))' - ) + assert str(var.contains(1)) == f"{expected!s}.hasOwnProperty(1)" + assert str(var.contains("1")) == f'{expected!s}.hasOwnProperty("1")' + assert str(var.contains(v(1))) == f"{expected!s}.hasOwnProperty(1)" + assert str(var.contains(v("1"))) == f'{expected!s}.hasOwnProperty("1")' other_state_var = Var(_js_expr="other")._var_set_state("state").to(str) other_var = Var(_js_expr="other").to(str) assert ( str(var.contains(other_state_var)) - == f"(((_object, _key) => _object.hasOwnProperty(_key))({expected!s}, state.other))" - ) - assert ( - str(var.contains(other_var)) - == f"(((_object, _key) => _object.hasOwnProperty(_key))({expected!s}, other))" + == f"{expected!s}.hasOwnProperty(state.other)" ) + assert str(var.contains(other_var)) == f"{expected!s}.hasOwnProperty(other)" @pytest.mark.parametrize( @@ -1108,8 +1055,8 @@ def test_var_operation(): def add(a: Var[int], b: Var[int]): return var_operation_return(js_expression=f"({a} + {b})", var_type=int) - assert str(add(1, 2)) == "(((_a, _b) => (_a + _b))(1, 2))" - assert str(add(4, -9)) == "(((_a, _b) => (_a + _b))(4, -9))" + assert str(add(1, 2)) == "(1 + 2)" + assert str(add(4, -9)) == "(4 + -9)" five = LiteralNumberVar.create(5) seven = add(2, five) @@ -1153,7 +1100,7 @@ def test_all_number_operations(): assert ( str(complicated_number) - == "(((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2))" + == "((Math.floor(((-((-5.4 + 1)) * 2) / 3) / 2) % 3) ** 2)" ) even_more_complicated_number = ~( @@ -1162,20 +1109,14 @@ def test_all_number_operations(): assert ( str(even_more_complicated_number) - == "(((_value) => !(_value))((isTrue((((_a, _b) => (_a() || _b()))((() => (Math.abs((Math.floor((((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2))))))), (() => (((_a, _b) => (_a() && _b()))((() => 2), (() => (((_value) => Math.round(_value))((((_lhs, _rhs) => (_lhs ** _rhs))((((_lhs, _rhs) => (_lhs % _rhs))((((_lhs, _rhs) => Math.floor(_lhs / _rhs))((((_lhs, _rhs) => (_lhs / _rhs))((((_lhs, _rhs) => (_lhs * _rhs))((((_value) => -(_value))((((_lhs, _rhs) => (_lhs + _rhs))(-5.4, 1)))), 2)), 3)), 2)), 3)), 2))))))))))))))" + == "!((isTrue(((Math.abs((Math.floor(((Math.floor(((-((-5.4 + 1)) * 2) / 3) / 2) % 3) ** 2))))) || (2 && Math.round(((Math.floor(((-((-5.4 + 1)) * 2) / 3) / 2) % 3) ** 2)))))))" ) - assert ( - str(LiteralNumberVar.create(5) > False) - == "(((_lhs, _rhs) => (_lhs > _rhs))(5, 0))" - ) - assert ( - str(LiteralBooleanVar.create(False) < 5) - == "(((_lhs, _rhs) => (_lhs < _rhs))((Number(false)), 5))" - ) + assert str(LiteralNumberVar.create(5) > False) == "(5 > 0)" + assert str(LiteralBooleanVar.create(False) < 5) == "((Number(false)) < 5)" assert ( str(LiteralBooleanVar.create(False) < LiteralBooleanVar.create(True)) - == "(((_lhs, _rhs) => (_lhs < _rhs))((Number(false)), (Number(true))))" + == "((Number(false)) < (Number(true)))" ) @@ -1218,7 +1159,7 @@ def test_index_operation(): ) assert ( str(array_var[0].to(NumberVar) + 9) - == "(((_lhs, _rhs) => (_lhs + _rhs))((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3, 4, 5], ...args)))(0)), 9))" + == "((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3, 4, 5], ...args)))(0)) + 9)" ) @@ -1261,14 +1202,8 @@ def test_array_operations(): str(ArrayVar.range(1, 10)) == "(((_e1, _e2 = null, _step = 1) => [...range(_e1, _e2, _step)])(1, 10))" ) - assert ( - str(ArrayVar.range(1, 10, 2)) - == "(((_e1, _e2 = null, _step = 1) => [...range(_e1, _e2, _step)])(1, 10, 2))" - ) - assert ( - str(ArrayVar.range(1, 10, -1)) - == "(((_e1, _e2 = null, _step = 1) => [...range(_e1, _e2, _step)])(1, 10, -1))" - ) + assert str(ArrayVar.range(1, 10, 2)) == "[...range(1, 10, 2)]" + assert str(ArrayVar.range(1, 10, -1)) == "[...range(1, 10, -1)]" def test_object_operations(): @@ -1289,7 +1224,7 @@ def test_object_operations(): assert str(object_var["a"]) == '({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["a"]' assert ( str(object_var.merge(LiteralObjectVar.create({"c": 4, "d": 5}))) - == '(((_lhs, _rhs) => ({..._lhs, ..._rhs}))(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }), ({ ["c"] : 4, ["d"] : 5 })))' + == '({...({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }), ...({ ["c"] : 4, ["d"] : 5 })})' ) @@ -1329,11 +1264,11 @@ def test_type_chains(): ) assert ( str(object_var.entries()[1][1] - 1) # type: ignore - == '(((_lhs, _rhs) => (_lhs - _rhs))((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((Object.entries(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))), ...args)))(1)), ...args)))(1)), 1))' + == '((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((Object.entries(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))), ...args)))(1)), ...args)))(1)) - 1)' ) assert ( str(object_var["c"] + object_var["b"]) # type: ignore - == '(((_lhs, _rhs) => (_lhs + _rhs))(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["c"], ({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["b"]))' + == '(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["c"] + ({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["b"])' ) From 0d746bf76217c88359606fd9b88f2bd1559d6d32 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 23 Jan 2025 13:09:47 -0800 Subject: [PATCH 81/86] handle default args --- reflex/vars/function.py | 35 ++++++++++++++++--- tests/units/components/core/test_cond.py | 5 +-- .../components/markdown/test_markdown.py | 4 +-- tests/units/test_var.py | 10 ++---- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index ba3b21c19a6..1e977331154 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -1219,9 +1219,20 @@ def call(self, *args: Var | Any) -> Var: # pyright: ignore [reportInconsistentO args = tuple(map(LiteralVar.create, args)) self._pre_check(*args) return_type = self._return_type(*args) - if arg_len == len(args) and isinstance( - self, (ArgsFunctionOperation, ArgsFunctionOperationBuilder) - ): + if isinstance(self, (ArgsFunctionOperation, ArgsFunctionOperationBuilder)): + default_args = self._default_args() + max_allowed_arguments = ( + arg_len if arg_len is not None else len(args) + len(default_args) + ) + provided_argument_count = len(args) + + # we skip default args which we provided + default_args_provided = len(default_args) - ( + max_allowed_arguments - provided_argument_count + ) + + full_args = args + tuple(default_args[default_args_provided:]) + if self._raw_js_function is not None: return VarOperationCall.create( FunctionStringVar.create( @@ -1229,13 +1240,13 @@ def call(self, *args: Var | Any) -> Var: # pyright: ignore [reportInconsistentO _var_type=self._var_type, _var_data=self._get_all_var_data(), ), - *args, + *full_args, _var_type=return_type, ).guess_type() if self._original_var_operation is not None: return ExpressionCall.create( self._original_var_operation, - *args, + *full_args, _var_data=self._get_all_var_data(), _var_type=return_type, ).guess_type() @@ -1316,6 +1327,20 @@ def _required_arg_len(self) -> int: ) return 0 + def _default_args(self) -> list[Any]: + """Get the default arguments of the function. + + Returns: + The default arguments of the function. + """ + if isinstance(self, (ArgsFunctionOperation, ArgsFunctionOperationBuilder)): + return [ + arg.default + for arg in self._default_values + if not isinstance(arg, inspect.Parameter.empty) + ] + return [] + def _return_type(self, *args: Var | Any) -> GenericType: """Override the type of the function call with the given arguments. diff --git a/tests/units/components/core/test_cond.py b/tests/units/components/core/test_cond.py index 98fa2e1218d..64ef88833f9 100644 --- a/tests/units/components/core/test_cond.py +++ b/tests/units/components/core/test_cond.py @@ -21,10 +21,7 @@ class CondState(BaseState): def test_f_string_cond_interpolation(): # make sure backticks inside interpolation don't get escaped var = LiteralVar.create(f"x {cond(True, 'a', 'b')}") - assert ( - str(var) - == '("x "+(true ? "a" : "b"))' - ) + assert str(var) == '("x "+(true ? "a" : "b"))' @pytest.mark.parametrize( diff --git a/tests/units/components/markdown/test_markdown.py b/tests/units/components/markdown/test_markdown.py index 6fddd0155e3..e6f310b3e60 100644 --- a/tests/units/components/markdown/test_markdown.py +++ b/tests/units/components/markdown/test_markdown.py @@ -148,7 +148,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe ( "code", {}, - r"""(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( {children} ) : ( (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\n")) : children)} css={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} customStyle={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} language={_language} style={((resolvedColorMode === "light") ? oneLight : oneDark)} wrapLongLines={true} {...props}/> ); })""" + r"""(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( {children} ) : ( (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\n")) : children)} css={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} customStyle={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} language={_language} style={((resolvedColorMode === "light") ? oneLight : oneDark)} wrapLongLines={true} {...props}/> ); })""", ), ( "code", @@ -157,7 +157,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe value, **props ) }, - r"""(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; ; return inline ? ( {children} ) : ( (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\n")) : children)} decorations={[]} language={_language} theme={((resolvedColorMode === "light") ? "one-light" : "one-dark-pro")} transformers={[]}/> ); })""" + r"""(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?.*)/); const _language = match ? match[1] : ''; ; return inline ? ( {children} ) : ( (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\n")) : children)} decorations={[]} language={_language} theme={((resolvedColorMode === "light") ? "one-light" : "one-dark-pro")} transformers={[]}/> ); })""", ), ( "h1", diff --git a/tests/units/test_var.py b/tests/units/test_var.py index b66d2f17938..4d2ffc8d318 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -1194,14 +1194,8 @@ def test_array_operations(): str(array_var.reverse()) == "(((...args) => (((_array) => _array.slice().reverse())([1, 2, 3, 4, 5], ...args)))())" ) - assert ( - str(ArrayVar.range(10)) - == "(((_e1, _e2 = null, _step = 1) => [...range(_e1, _e2, _step)])(10))" - ) - assert ( - str(ArrayVar.range(1, 10)) - == "(((_e1, _e2 = null, _step = 1) => [...range(_e1, _e2, _step)])(1, 10))" - ) + assert str(ArrayVar.range(10)) == "[...range(10, null, 1)]" + assert str(ArrayVar.range(1, 10)) == "[...range(1, 10, 1)]" assert str(ArrayVar.range(1, 10, 2)) == "[...range(1, 10, 2)]" assert str(ArrayVar.range(1, 10, -1)) == "[...range(1, 10, -1)]" From 2ffa698c6b57724b681170f3928aec787f3aabc7 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 23 Jan 2025 15:03:45 -0800 Subject: [PATCH 82/86] improve match and cond checking --- reflex/components/core/cond.py | 8 +++----- reflex/components/core/match.py | 7 +++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index 153953668c8..09f2ab213cd 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -2,13 +2,12 @@ from __future__ import annotations -from typing import Any, overload +from typing import Any, Union, overload from reflex.components.base.fragment import Fragment from reflex.components.component import BaseComponent, Component from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode from reflex.utils import types -from reflex.utils.types import safe_issubclass from reflex.vars.base import LiteralVar, Var from reflex.vars.number import ternary_operation @@ -41,9 +40,8 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var: # If the first component is a component, create a Fragment if the second component is not set. if isinstance(c1, BaseComponent) or ( isinstance(c1, Var) - and ( - safe_issubclass(c1._var_type, BaseComponent) - or types.safe_typehint_issubclass(c1._var_type, list[BaseComponent]) + and types.safe_typehint_issubclass( + c1._var_type, Union[BaseComponent, list[BaseComponent]] ) ): c2 = c2 if c2 is not None else Fragment.create() diff --git a/reflex/components/core/match.py b/reflex/components/core/match.py index a9e7e10c598..1b32c3033cf 100644 --- a/reflex/components/core/match.py +++ b/reflex/components/core/match.py @@ -1,6 +1,6 @@ """rx.match.""" -from typing import Any, cast +from typing import Any, Union, cast from typing_extensions import Unpack @@ -49,9 +49,8 @@ def _validate_return_types(match_cases: tuple[CASE_TYPE[VAR_TYPE], ...]) -> None def is_component_or_component_var(obj: Any) -> bool: return types._isinstance(obj, BaseComponent) or ( isinstance(obj, Var) - and ( - types.safe_typehint_issubclass(obj._var_type, BaseComponent) - or types.safe_typehint_issubclass(obj._var_type, list[BaseComponent]) + and types.safe_typehint_issubclass( + obj._var_type, Union[list[BaseComponent], BaseComponent] ) ) From 46c66b2adf195b2ce32bf2bd0be26782464e001d Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 31 Jan 2025 14:05:11 -0800 Subject: [PATCH 83/86] lock poetry --- poetry.lock | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0b4f489dce3..f8ed108d968 100644 --- a/poetry.lock +++ b/poetry.lock @@ -495,7 +495,6 @@ files = [ {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385"}, {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"}, {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"}, {file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"}, @@ -506,7 +505,6 @@ files = [ {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba"}, {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"}, {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"}, {file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"}, @@ -2582,42 +2580,56 @@ markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "SQLAlchemy-2.0.37-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da36c3b0e891808a7542c5c89f224520b9a16c7f5e4d6a1156955605e54aef0e"}, {file = "SQLAlchemy-2.0.37-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e7402ff96e2b073a98ef6d6142796426d705addd27b9d26c3b32dbaa06d7d069"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6f5d254a22394847245f411a2956976401e84da4288aa70cbcd5190744062c1"}, {file = "SQLAlchemy-2.0.37-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41296bbcaa55ef5fdd32389a35c710133b097f7b2609d8218c0eabded43a1d84"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bedee60385c1c0411378cbd4dc486362f5ee88deceea50002772912d798bb00f"}, {file = "SQLAlchemy-2.0.37-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6c67415258f9f3c69867ec02fea1bf6508153709ecbd731a982442a590f2b7e4"}, {file = "SQLAlchemy-2.0.37-cp310-cp310-win32.whl", hash = "sha256:650dcb70739957a492ad8acff65d099a9586b9b8920e3507ca61ec3ce650bb72"}, {file = "SQLAlchemy-2.0.37-cp310-cp310-win_amd64.whl", hash = "sha256:93d1543cd8359040c02b6614421c8e10cd7a788c40047dbc507ed46c29ae5636"}, {file = "SQLAlchemy-2.0.37-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:78361be6dc9073ed17ab380985d1e45e48a642313ab68ab6afa2457354ff692c"}, {file = "SQLAlchemy-2.0.37-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b661b49d0cb0ab311a189b31e25576b7ac3e20783beb1e1817d72d9d02508bf5"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d57bafbab289e147d064ffbd5cca2d7b1394b63417c0636cea1f2e93d16eb9e8"}, {file = "SQLAlchemy-2.0.37-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa2c0913f02341d25fb858e4fb2031e6b0813494cca1ba07d417674128ce11b"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9df21b8d9e5c136ea6cde1c50d2b1c29a2b5ff2b1d610165c23ff250e0704087"}, {file = "SQLAlchemy-2.0.37-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db18ff6b8c0f1917f8b20f8eca35c28bbccb9f83afa94743e03d40203ed83de9"}, {file = "SQLAlchemy-2.0.37-cp311-cp311-win32.whl", hash = "sha256:46954173612617a99a64aee103bcd3f078901b9a8dcfc6ae80cbf34ba23df989"}, {file = "SQLAlchemy-2.0.37-cp311-cp311-win_amd64.whl", hash = "sha256:7b7e772dc4bc507fdec4ee20182f15bd60d2a84f1e087a8accf5b5b7a0dcf2ba"}, {file = "SQLAlchemy-2.0.37-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2952748ecd67ed3b56773c185e85fc084f6bdcdec10e5032a7c25a6bc7d682ef"}, {file = "SQLAlchemy-2.0.37-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3151822aa1db0eb5afd65ccfafebe0ef5cda3a7701a279c8d0bf17781a793bb4"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaa8039b6d20137a4e02603aba37d12cd2dde7887500b8855356682fc33933f4"}, {file = "SQLAlchemy-2.0.37-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cdba1f73b64530c47b27118b7053b8447e6d6f3c8104e3ac59f3d40c33aa9fd"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1b2690456528a87234a75d1a1644cdb330a6926f455403c8e4f6cad6921f9098"}, {file = "SQLAlchemy-2.0.37-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf5ae8a9dcf657fd72144a7fd01f243236ea39e7344e579a121c4205aedf07bb"}, {file = "SQLAlchemy-2.0.37-cp312-cp312-win32.whl", hash = "sha256:ea308cec940905ba008291d93619d92edaf83232ec85fbd514dcb329f3192761"}, {file = "SQLAlchemy-2.0.37-cp312-cp312-win_amd64.whl", hash = "sha256:635d8a21577341dfe4f7fa59ec394b346da12420b86624a69e466d446de16aff"}, {file = "SQLAlchemy-2.0.37-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8c4096727193762e72ce9437e2a86a110cf081241919ce3fab8e89c02f6b6658"}, {file = "SQLAlchemy-2.0.37-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e4fb5ac86d8fe8151966814f6720996430462e633d225497566b3996966b9bdb"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e56a139bfe136a22c438478a86f8204c1eb5eed36f4e15c4224e4b9db01cb3e4"}, {file = "SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f95fc8e3f34b5f6b3effb49d10ac97c569ec8e32f985612d9b25dd12d0d2e94"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c505edd429abdfe3643fa3b2e83efb3445a34a9dc49d5f692dd087be966020e0"}, {file = "SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:12b0f1ec623cccf058cf21cb544f0e74656618165b083d78145cafde156ea7b6"}, {file = "SQLAlchemy-2.0.37-cp313-cp313-win32.whl", hash = "sha256:293f9ade06b2e68dd03cfb14d49202fac47b7bb94bffcff174568c951fbc7af2"}, {file = "SQLAlchemy-2.0.37-cp313-cp313-win_amd64.whl", hash = "sha256:d70f53a0646cc418ca4853da57cf3ddddbccb8c98406791f24426f2dd77fd0e2"}, {file = "SQLAlchemy-2.0.37-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:44f569d0b1eb82301b92b72085583277316e7367e038d97c3a1a899d9a05e342"}, + {file = "SQLAlchemy-2.0.37-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2eae3423e538c10d93ae3e87788c6a84658c3ed6db62e6a61bb9495b0ad16bb"}, {file = "SQLAlchemy-2.0.37-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfff7be361048244c3aa0f60b5e63221c5e0f0e509f4e47b8910e22b57d10ae7"}, + {file = "SQLAlchemy-2.0.37-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:5bc3339db84c5fb9130ac0e2f20347ee77b5dd2596ba327ce0d399752f4fce39"}, {file = "SQLAlchemy-2.0.37-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:84b9f23b0fa98a6a4b99d73989350a94e4a4ec476b9a7dfe9b79ba5939f5e80b"}, {file = "SQLAlchemy-2.0.37-cp37-cp37m-win32.whl", hash = "sha256:51bc9cfef83e0ac84f86bf2b10eaccb27c5a3e66a1212bef676f5bee6ef33ebb"}, {file = "SQLAlchemy-2.0.37-cp37-cp37m-win_amd64.whl", hash = "sha256:8e47f1af09444f87c67b4f1bb6231e12ba6d4d9f03050d7fc88df6d075231a49"}, {file = "SQLAlchemy-2.0.37-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6b788f14c5bb91db7f468dcf76f8b64423660a05e57fe277d3f4fad7b9dcb7ce"}, {file = "SQLAlchemy-2.0.37-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521ef85c04c33009166777c77e76c8a676e2d8528dc83a57836b63ca9c69dcd1"}, + {file = "SQLAlchemy-2.0.37-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75311559f5c9881a9808eadbeb20ed8d8ba3f7225bef3afed2000c2a9f4d49b9"}, {file = "SQLAlchemy-2.0.37-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cce918ada64c956b62ca2c2af59b125767097ec1dca89650a6221e887521bfd7"}, + {file = "SQLAlchemy-2.0.37-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9d087663b7e1feabea8c578d6887d59bb00388158e8bff3a76be11aa3f748ca2"}, {file = "SQLAlchemy-2.0.37-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cf95a60b36997dad99692314c4713f141b61c5b0b4cc5c3426faad570b31ca01"}, {file = "SQLAlchemy-2.0.37-cp38-cp38-win32.whl", hash = "sha256:d75ead7dd4d255068ea0f21492ee67937bd7c90964c8f3c2bea83c7b7f81b95f"}, {file = "SQLAlchemy-2.0.37-cp38-cp38-win_amd64.whl", hash = "sha256:74bbd1d0a9bacf34266a7907d43260c8d65d31d691bb2356f41b17c2dca5b1d0"}, {file = "SQLAlchemy-2.0.37-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:648ec5acf95ad59255452ef759054f2176849662af4521db6cb245263ae4aa33"}, {file = "SQLAlchemy-2.0.37-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:35bd2df269de082065d4b23ae08502a47255832cc3f17619a5cea92ce478b02b"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f581d365af9373a738c49e0c51e8b18e08d8a6b1b15cc556773bcd8a192fa8b"}, {file = "SQLAlchemy-2.0.37-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82df02816c14f8dc9f4d74aea4cb84a92f4b0620235daa76dde002409a3fbb5a"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94b564e38b344d3e67d2e224f0aec6ba09a77e4582ced41e7bfd0f757d926ec9"}, {file = "SQLAlchemy-2.0.37-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:955a2a765aa1bd81aafa69ffda179d4fe3e2a3ad462a736ae5b6f387f78bfeb8"}, {file = "SQLAlchemy-2.0.37-cp39-cp39-win32.whl", hash = "sha256:03f0528c53ca0b67094c4764523c1451ea15959bbf0a8a8a3096900014db0278"}, {file = "SQLAlchemy-2.0.37-cp39-cp39-win_amd64.whl", hash = "sha256:4b12885dc85a2ab2b7d00995bac6d967bffa8594123b02ed21e8eb2205a7584b"}, @@ -3171,4 +3183,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.10, <4.0" -content-hash = "822150bcbf41e5cbb61da0a059b41d8971e3c6c974c8af4be7ef55126648aea1" +content-hash = "8e0fa67f25242d1d0ca1875803a1e64f0615ae1ec7c9257f7a485f277e0f5085" From 749577f0bcca6754896acc090ce1893aed57d590 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 31 Jan 2025 14:19:13 -0800 Subject: [PATCH 84/86] precommit --- reflex/components/component.py | 13 +++--- reflex/components/core/banner.py | 2 +- reflex/components/core/foreach.py | 2 - reflex/components/radix/themes/color_mode.py | 2 +- reflex/utils/types.py | 4 +- reflex/vars/base.py | 42 +++++++++----------- reflex/vars/function.py | 13 +++--- reflex/vars/number.py | 2 +- reflex/vars/object.py | 1 - reflex/vars/sequence.py | 8 ++-- tests/integration/test_lifespan.py | 4 +- tests/units/test_var.py | 4 +- 12 files changed, 45 insertions(+), 52 deletions(-) diff --git a/reflex/components/component.py b/reflex/components/component.py index d3acf69abd0..ae06ea345dd 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -929,15 +929,16 @@ def validate_child(child: Any): valid_children = self._valid_children + allowed_components - def child_is_in_valid(child): - if type(child).__name__ in valid_children: + def child_is_in_valid(child_component: Any): + if type(child_component).__name__ in valid_children: return True if ( - not isinstance(child, Bare) - or child.contents is None - or not isinstance(child.contents, Var) - or (var_data := child.contents._get_all_var_data()) is None + not isinstance(child_component, Bare) + or child_component.contents is None + or not isinstance(child_component.contents, Var) + or (var_data := child_component.contents._get_all_var_data()) + is None ): return False diff --git a/reflex/components/core/banner.py b/reflex/components/core/banner.py index 64b7874b6c0..5f8ccf152d6 100644 --- a/reflex/components/core/banner.py +++ b/reflex/components/core/banner.py @@ -4,8 +4,8 @@ from typing import Optional -from reflex.components.base.fragment import Fragment from reflex import constants +from reflex.components.base.fragment import Fragment from reflex.components.component import Component from reflex.components.core.cond import cond from reflex.components.datadisplay.logo import svg_logo diff --git a/reflex/components/core/foreach.py b/reflex/components/core/foreach.py index e0eba7ef517..1678642ea1f 100644 --- a/reflex/components/core/foreach.py +++ b/reflex/components/core/foreach.py @@ -2,10 +2,8 @@ from __future__ import annotations -import functools from typing import Callable, Iterable -from reflex.utils.exceptions import UntypedVarError from reflex.vars.base import LiteralVar, Var from reflex.vars.object import ObjectVar from reflex.vars.sequence import ArrayVar diff --git a/reflex/components/radix/themes/color_mode.py b/reflex/components/radix/themes/color_mode.py index f370a5aa5f2..b1ab6f8c5cb 100644 --- a/reflex/components/radix/themes/color_mode.py +++ b/reflex/components/radix/themes/color_mode.py @@ -139,7 +139,7 @@ def create( if allow_system: - def color_mode_item(_color_mode: str): + def color_mode_item(_color_mode: Literal["light", "dark", "system"]): return dropdown_menu.item( _color_mode.title(), on_click=set_color_mode(_color_mode) ) diff --git a/reflex/utils/types.py b/reflex/utils/types.py index 81b052eac8e..90e2f566d41 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -880,7 +880,9 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo ): return all( typehint_issubclass(subclass, superclass) - for subclass, superclass in zip(possible_subclass, possible_superclass) + for subclass, superclass in zip( + possible_subclass, possible_superclass, strict=False + ) ) if possible_subclass is possible_superclass: return True diff --git a/reflex/vars/base.py b/reflex/vars/base.py index ce91f368e91..ba7906a2985 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -13,7 +13,7 @@ import re import string import warnings -from types import CodeType, FunctionType +from types import CodeType, EllipsisType, FunctionType from typing import ( TYPE_CHECKING, Any, @@ -54,7 +54,6 @@ from reflex.utils import console, exceptions, imports, serializers, types from reflex.utils.exceptions import ( UntypedComputedVarError, - VarAttributeError, VarDependencyError, VarTypeError, VarValueError, @@ -108,12 +107,7 @@ class ReflexCallable(Protocol[P, R]): __call__: Callable[P, R] -if sys.version_info >= (3, 10): - from types import EllipsisType - - ReflexCallableParams = Union[EllipsisType, Tuple[GenericType, ...]] -else: - ReflexCallableParams = Union[Any, Tuple[GenericType, ...]] +ReflexCallableParams = Union[EllipsisType, Tuple[GenericType, ...]] def unwrap_reflex_callalbe( @@ -1336,10 +1330,15 @@ def range( """ from .sequence import ArrayVar + if step is None: + return ArrayVar.range(first_endpoint, second_endpoint) + return ArrayVar.range(first_endpoint, second_endpoint, step) - def __bool__(self) -> bool: - """Raise exception if using Var in a boolean context. + if not TYPE_CHECKING: + + def __bool__(self) -> bool: + """Raise exception if using Var in a boolean context. Raises: VarTypeError: when attempting to bool-ify the Var. @@ -1924,12 +1923,12 @@ def simplified_operation(*args): _raw_js_function=custom_operation_return._raw_js_function, _original_var_operation=simplified_operation, _var_type=ReflexCallable[ - tuple( + tuple( # pyright: ignore [reportInvalidTypeArguments] arg_python_type if isinstance(arg_default_values[i], inspect.Parameter) else VarWithDefault[arg_python_type] for i, (_, arg_python_type) in enumerate(args_with_type_hints) - ), # type: ignore + ), custom_operation_return._var_type, ], ) @@ -2049,11 +2048,6 @@ def __hash__(self) -> int: RETURN_TYPE = TypeVar("RETURN_TYPE") -DICT_KEY = TypeVar("DICT_KEY") -DICT_VAL = TypeVar("DICT_VAL") - -LIST_INSIDE = TypeVar("LIST_INSIDE") - class FakeComputedVarBaseClass(property): """A fake base class for ComputedVar to avoid inheriting from property.""" @@ -2273,17 +2267,17 @@ def __get__( @overload def __get__( - self: ComputedVar[Mapping[DICT_KEY, DICT_VAL]], + self: ComputedVar[MAPPING_TYPE], instance: None, owner: Type, - ) -> ObjectVar[Mapping[DICT_KEY, DICT_VAL]]: ... + ) -> ObjectVar[MAPPING_TYPE]: ... @overload def __get__( - self: ComputedVar[Sequence[LIST_INSIDE]], + self: ComputedVar[SEQUENCE_TYPE], instance: None, owner: Type, - ) -> ArrayVar[Sequence[LIST_INSIDE]]: ... + ) -> ArrayVar[SEQUENCE_TYPE]: ... @overload def __get__(self, instance: None, owner: Type) -> ComputedVar[RETURN_TYPE]: ... @@ -2588,7 +2582,7 @@ def wrapper(fget: Callable[[BASE_STATE], Any]) -> ComputedVar: @dataclasses.dataclass( eq=False, frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, + slots=True, ) class CustomVarOperationReturn(Var[RETURN]): """Base class for custom var operations.""" @@ -3202,7 +3196,7 @@ def __get__(self: Field[bool], instance: None, owner: Any) -> BooleanVar: ... def __get__(self: Field[int], instance: None, owner: Any) -> NumberVar[int]: ... @overload - def __get__(self: Field[float], instance: None, owner) -> NumberVar[float]: ... + def __get__(self: Field[float], instance: None, owner: Any) -> NumberVar[float]: ... @overload def __get__(self: Field[str], instance: None, owner: Any) -> StringVar[str]: ... @@ -3251,7 +3245,7 @@ def field(value: FIELD_TYPE) -> Field[FIELD_TYPE]: Returns: The Field. """ - return value # type: ignore + return value # pyright: ignore [reportReturnType] def and_operation(a: Var | Any, b: Var | Any) -> Var: diff --git a/reflex/vars/function.py b/reflex/vars/function.py index 39c95dc9c50..9120370d3d8 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -1193,7 +1193,6 @@ def call( @overload def call(self: FunctionVar[NoReturn], *args: Var | Any) -> Var: ... - def call(self, *args: Var | Any) -> Var: # pyright: ignore [reportInconsistentOverload] def call(self, *args: Var | Any) -> Var: # pyright: ignore [reportInconsistentOverload] """Call the function with the given arguments. @@ -1299,7 +1298,7 @@ def _partial_type( """ args_types, return_type = unwrap_reflex_callalbe(self._var_type) if isinstance(args_types, tuple): - return ReflexCallable[[*args_types[len(args) :]], return_type], None # type: ignore + return ReflexCallable[[*args_types[len(args) :]], return_type], None return ReflexCallable[..., return_type], None def _arg_len(self) -> int | None: @@ -1637,7 +1636,7 @@ def pre_check_args( Raises: VarTypeError: If the arguments are invalid. """ - for i, (validator, arg) in enumerate(zip(self._validators, args)): + for i, (validator, arg) in enumerate(zip(self._validators, args, strict=False)): if (validation_message := validator(arg)) is not None: arg_name = self._args.args[i] if i < len(self._args.args) else None if arg_name is not None: @@ -1694,9 +1693,9 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]): _cached_var_name = cached_property_no_lock(format_args_function_operation) - _pre_check = pre_check_args # type: ignore + _pre_check = pre_check_args - _partial_type = figure_partial_type # type: ignore + _partial_type = figure_partial_type @classmethod def create( @@ -1776,9 +1775,9 @@ class ArgsFunctionOperationBuilder( _cached_var_name = cached_property_no_lock(format_args_function_operation) - _pre_check = pre_check_args # type: ignore + _pre_check = pre_check_args - _partial_type = figure_partial_type # type: ignore + _partial_type = figure_partial_type @classmethod def create( diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 5cdd2ba0007..d8fcc2e47f0 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -1080,7 +1080,7 @@ def ternary_operation( @dataclasses.dataclass( eq=False, frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, + slots=True, ) class MatchOperation(CachedVarOperation, Var[VAR_TYPE]): """Base class for immutable match operations.""" diff --git a/reflex/vars/object.py b/reflex/vars/object.py index 77518f0dbf4..1f395173ddd 100644 --- a/reflex/vars/object.py +++ b/reflex/vars/object.py @@ -142,7 +142,6 @@ def merge(self, other: ObjectVar): # NoReturn is used here to catch when key value is Any @overload - def __getitem__( # pyright: ignore [reportOverlappingOverload] def __getitem__( # pyright: ignore [reportOverlappingOverload] self: ObjectVar[Mapping[Any, NoReturn]], key: Var | Any, diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 0d869e856b7..a6f01634969 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -773,7 +773,7 @@ def type_computer(*args: Var): type_computer=nary_type_computer( ReflexCallable[[List[Any], ReflexCallable], List[Any]], ReflexCallable[[ReflexCallable], List[Any]], - computer=lambda args: List[unwrap_reflex_callalbe(args[1]._var_type)[1]], # type: ignore + computer=lambda args: List[unwrap_reflex_callalbe(args[1]._var_type)[1]], ), ) @@ -846,7 +846,7 @@ class SliceVar(Var[slice], python_types=slice): @dataclasses.dataclass( eq=False, frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, + slots=True, ) class LiteralSliceVar(CachedVarOperation, LiteralVar, SliceVar): """Base class for immutable literal slice vars.""" @@ -1245,7 +1245,7 @@ class StringVar(Var[STRING_TYPE], python_types=str): @dataclasses.dataclass( eq=False, frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, + slots=True, ) class LiteralStringVar(LiteralVar, StringVar[str]): """Base class for immutable literal string vars.""" @@ -1367,7 +1367,7 @@ def json(self) -> str: @dataclasses.dataclass( eq=False, frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, + slots=True, ) class ConcatVarOperation(CachedVarOperation, StringVar[str]): """Representing a concatenation of literal string vars.""" diff --git a/tests/integration/test_lifespan.py b/tests/integration/test_lifespan.py index 6b823329207..fd0cad95137 100644 --- a/tests/integration/test_lifespan.py +++ b/tests/integration/test_lifespan.py @@ -136,7 +136,7 @@ async def test_lifespan(lifespan_app: AppHarness): task_global = driver.find_element(By.ID, "task_global") assert context_global.text == "2" - assert lifespan_app.app_module.lifespan_context_global_getter() == 2 # type: ignore + assert lifespan_app.app_module.lifespan_context_global_getter() == 2 original_task_global_text = task_global.text original_task_global_value = int(original_task_global_text) @@ -145,7 +145,7 @@ async def test_lifespan(lifespan_app: AppHarness): assert ( lifespan_app.app_module.lifespan_task_global_getter() > original_task_global_value - ) # type: ignore + ) assert int(task_global.text) > original_task_global_value # Kill the backend diff --git a/tests/units/test_var.py b/tests/units/test_var.py index 84da0d9a04e..6902b81e26c 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -1249,11 +1249,11 @@ def test_type_chains(): List[int], ) assert ( - str(object_var.keys()[0].upper()) + str(object_var.keys()[0].upper()) # pyright: ignore [reportAttributeAccessIssue] == '(((...args) => (((_string) => String.prototype.toUpperCase.apply(_string))((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((Object.keys(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))), ...args)))(0)), ...args)))())' ) assert ( - str(object_var.entries()[1][1] - 1) + str(object_var.entries()[1][1] - 1) # pyright: ignore [reportCallIssue, reportOperatorIssue] == '((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((Object.entries(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))), ...args)))(1)), ...args)))(1)) - 1)' ) assert ( From 06b751f679ee7a7f529c8c0422ac6afc2f8575f0 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 31 Jan 2025 14:28:40 -0800 Subject: [PATCH 85/86] remove caching things --- reflex/components/base/bare.py | 7 ++----- reflex/vars/base.py | 22 ---------------------- 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index 0c62f900065..b8a65b3c75d 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -9,7 +9,7 @@ from reflex.components.tags.tagless import Tagless from reflex.utils.imports import ParsedImportDict from reflex.vars import BooleanVar, ObjectVar, Var -from reflex.vars.base import VarData, get_var_caching, set_var_caching +from reflex.vars.base import VarData class Bare(Component): @@ -160,10 +160,7 @@ def _add_style_recursive( for component in var_data.components: if isinstance(component, Component): component._add_style_recursive(style, theme) - if get_var_caching(): - set_var_caching(False) - str(new_self) - set_var_caching(True) + return new_self def _get_vars( diff --git a/reflex/vars/base.py b/reflex/vars/base.py index ba7906a2985..83836f2264b 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -148,28 +148,6 @@ def unwrap_reflex_callalbe( return args -_VAR_CACHING = True - - -def get_var_caching() -> bool: - """Get the var caching status. - - Returns: - The var caching status. - """ - return _VAR_CACHING - - -def set_var_caching(value: bool): - """Set the var caching status. - - Args: - value: The value to set the var caching status to. - """ - global _VAR_CACHING - _VAR_CACHING = value - - @dataclasses.dataclass( eq=False, frozen=True, From aef6aced035a16ca44dcdb69ae98d2979d6b6749 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 31 Jan 2025 14:38:23 -0800 Subject: [PATCH 86/86] clear cache on add style recursive --- reflex/compiler/compiler.py | 2 -- reflex/components/base/bare.py | 4 +++- tests/units/test_var.py | 11 ++++------- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index 54fb8b2f63a..dad57876944 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -689,8 +689,6 @@ def compile_unevaluated_page( component, enable_state = compile_unevaluated_page( route, cls.UNCOMPILED_PAGES[route] ) - component = component if isinstance(component, Component) else component() - component._add_style_recursive(style, theme) return route, component, compile_page(route, component, cls.STATE) @classmethod diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index b8a65b3c75d..5001b6f5051 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -9,7 +9,7 @@ from reflex.components.tags.tagless import Tagless from reflex.utils.imports import ParsedImportDict from reflex.vars import BooleanVar, ObjectVar, Var -from reflex.vars.base import VarData +from reflex.vars.base import GLOBAL_CACHE, VarData class Bare(Component): @@ -161,6 +161,8 @@ def _add_style_recursive( if isinstance(component, Component): component._add_style_recursive(style, theme) + GLOBAL_CACHE.clear() + return new_self def _get_vars( diff --git a/tests/units/test_var.py b/tests/units/test_var.py index 6902b81e26c..4ac05349514 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -1445,9 +1445,9 @@ def test_unsupported_types_for_reverse(var): Args: var: The base var. """ - with pytest.raises(TypeError) as err: + with pytest.raises(AttributeError) as err: var.reverse() - assert err.value.args[0] == "Cannot reverse non-list var." + assert err.value.args[0] == "'Var' object has no attribute 'reverse'" @pytest.mark.parametrize( @@ -1465,12 +1465,9 @@ def test_unsupported_types_for_contains(var: Var): Args: var: The base var. """ - with pytest.raises(TypeError) as err: + with pytest.raises(AttributeError) as err: assert var.contains(1) # pyright: ignore [reportAttributeAccessIssue] - assert ( - err.value.args[0] - == f"Var of type {var._var_type} does not support contains check." - ) + assert err.value.args[0] == "'Var' object has no attribute 'contains'" @pytest.mark.parametrize(