fix some tests
This commit is contained in:
parent
45dde0072e
commit
f0f84d5410
@ -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"
|
||||
|
@ -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.
|
||||
|
@ -61,13 +61,13 @@ class Bare(Component):
|
||||
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:
|
||||
|
@ -930,6 +930,7 @@ class Component(BaseComponent, ABC):
|
||||
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 @@ class Component(BaseComponent, ABC):
|
||||
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 @@ class Component(BaseComponent, ABC):
|
||||
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]
|
||||
)
|
||||
|
@ -175,6 +175,8 @@ class ConnectionBanner(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 @@ class ConnectionBanner(Component):
|
||||
position="fixed",
|
||||
)
|
||||
|
||||
return cond(has_connection_errors, comp)
|
||||
return Bare.create(cond(has_connection_errors, comp))
|
||||
|
||||
|
||||
class ConnectionModal(Component):
|
||||
@ -205,18 +207,22 @@ class ConnectionModal(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,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
@ -12,7 +12,8 @@ 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.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
|
||||
|
@ -184,11 +184,17 @@ class Match(MemoizationLeaf):
|
||||
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
|
||||
|
@ -447,7 +447,7 @@ class CodeBlock(Component, MarkdownComponentMap):
|
||||
|
||||
# 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",
|
||||
|
@ -113,8 +113,8 @@ class MarkdownComponentMap:
|
||||
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,
|
||||
)
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -51,7 +51,7 @@ from typing_extensions import (
|
||||
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 @@ from reflex.utils.types import (
|
||||
_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 @@ class VarData:
|
||||
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 @@ class Var(Generic[VAR_TYPE]):
|
||||
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 @@ class Var(Generic[VAR_TYPE]):
|
||||
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 Var(Generic[VAR_TYPE]):
|
||||
|
||||
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 @@ class Var(Generic[VAR_TYPE]):
|
||||
|
||||
# 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 @@ class Var(Generic[VAR_TYPE]):
|
||||
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 @@ class Var(Generic[VAR_TYPE]):
|
||||
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 @@ class Var(Generic[VAR_TYPE]):
|
||||
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 var_operation(
|
||||
),
|
||||
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 @@ RETURN = TypeVar("RETURN")
|
||||
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 @@ class CustomVarOperationReturn(Var[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 CustomVarOperation.
|
||||
@ -2567,6 +2612,7 @@ class CustomVarOperationReturn(Var[RETURN]):
|
||||
_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,
|
||||
)
|
||||
|
||||
|
||||
|
@ -52,7 +52,23 @@ OTHER_CALLABLE_TYPE = TypeVar(
|
||||
)
|
||||
|
||||
|
||||
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 @@ class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]):
|
||||
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 @@ class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]):
|
||||
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 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]):
|
||||
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 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]):
|
||||
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 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]):
|
||||
_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 @@ class ArgsFunctionOperationBuilder(
|
||||
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 @@ class ArgsFunctionOperationBuilder(
|
||||
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 @@ class ArgsFunctionOperationBuilder(
|
||||
_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),
|
||||
|
@ -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 @@ _AT_SLICE_IMPORT: ImportDict = {
|
||||
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",
|
||||
)
|
||||
|
||||
|
||||
|
@ -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",
|
||||
)
|
||||
|
||||
|
||||
|
@ -30,8 +30,7 @@ from reflex.constants.base import REFLEX_VAR_OPENING_TAG
|
||||
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 @@ from .base import (
|
||||
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
|
||||
|
||||
|
@ -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",
|
||||
)
|
||||
|
@ -846,7 +846,7 @@ def test_component_event_trigger_arbitrary_args():
|
||||
|
||||
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], ({ }))))}"
|
||||
)
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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 test_var_operation():
|
||||
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 <class 'str'> 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"),
|
||||
[
|
||||
"+",
|
||||
"-",
|
||||
"/",
|
||||
"//",
|
||||
"*",
|
||||
"%",
|
||||
"**",
|
||||
">",
|
||||
"<",
|
||||
"<=",
|
||||
">=",
|
||||
"^",
|
||||
"<<",
|
||||
">>",
|
||||
|
Loading…
Reference in New Issue
Block a user