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"
|
asyncio_mode = "auto"
|
||||||
|
|
||||||
[tool.codespell]
|
[tool.codespell]
|
||||||
skip = "docs/*,*.html,examples/*, *.pyi"
|
skip = "docs/*,*.html,examples/*, *.pyi, *.lock"
|
||||||
ignore-words-list = "te, TreeE"
|
ignore-words-list = "te, TreeE"
|
||||||
|
@ -300,10 +300,7 @@ export const applyEvent = async (event, socket) => {
|
|||||||
|
|
||||||
// Send the event to the server.
|
// Send the event to the server.
|
||||||
if (socket) {
|
if (socket) {
|
||||||
socket.emit(
|
socket.emit("event", event);
|
||||||
"event",
|
|
||||||
event,
|
|
||||||
);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,7 +407,7 @@ export const connect = async (
|
|||||||
autoUnref: false,
|
autoUnref: false,
|
||||||
});
|
});
|
||||||
// Ensure undefined fields in events are sent as null instead of removed
|
// 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() {
|
function checkVisibility() {
|
||||||
if (document.visibilityState === "visible") {
|
if (document.visibilityState === "visible") {
|
||||||
@ -488,7 +485,7 @@ export const uploadFiles = async (
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const upload_ref_name = `__upload_controllers_${upload_id}`
|
const upload_ref_name = `__upload_controllers_${upload_id}`;
|
||||||
|
|
||||||
if (refs[upload_ref_name]) {
|
if (refs[upload_ref_name]) {
|
||||||
console.log("Upload already in progress for ", upload_id);
|
console.log("Upload already in progress for ", upload_id);
|
||||||
@ -924,6 +921,18 @@ export const atSlice = (arrayLike, slice) => {
|
|||||||
.filter((_, i) => i % step === 0);
|
.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.
|
* Get the value from a ref.
|
||||||
* @param ref The ref to get the value from.
|
* @param ref The ref to get the value from.
|
||||||
|
@ -61,13 +61,13 @@ class Bare(Component):
|
|||||||
hooks |= component._get_all_hooks()
|
hooks |= component._get_all_hooks()
|
||||||
return hooks
|
return hooks
|
||||||
|
|
||||||
def _get_all_imports(self) -> ParsedImportDict:
|
def _get_all_imports(self, collapse: bool = False) -> ParsedImportDict:
|
||||||
"""Include the imports for the component.
|
"""Include the imports for the component.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The imports for the component.
|
The imports for the component.
|
||||||
"""
|
"""
|
||||||
imports = super()._get_all_imports()
|
imports = super()._get_all_imports(collapse=collapse)
|
||||||
if isinstance(self.contents, Var):
|
if isinstance(self.contents, Var):
|
||||||
var_data = self.contents._get_all_var_data()
|
var_data = self.contents._get_all_var_data()
|
||||||
if var_data:
|
if var_data:
|
||||||
|
@ -930,6 +930,7 @@ class Component(BaseComponent, ABC):
|
|||||||
children: The children of the component.
|
children: The children of the component.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from reflex.components.base.bare import Bare
|
||||||
from reflex.components.base.fragment import Fragment
|
from reflex.components.base.fragment import Fragment
|
||||||
from reflex.components.core.cond import Cond
|
from reflex.components.core.cond import Cond
|
||||||
from reflex.components.core.foreach import Foreach
|
from reflex.components.core.foreach import Foreach
|
||||||
@ -960,6 +961,16 @@ class Component(BaseComponent, ABC):
|
|||||||
validate_child(child.comp1)
|
validate_child(child.comp1)
|
||||||
validate_child(child.comp2)
|
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):
|
if isinstance(child, Match):
|
||||||
for cases in child.match_cases:
|
for cases in child.match_cases:
|
||||||
validate_child(cases[-1])
|
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"
|
f"The component `{comp_name}` cannot have `{child_name}` as a child component"
|
||||||
)
|
)
|
||||||
|
|
||||||
if self._valid_children and child_name not in [
|
valid_children = self._valid_children + allowed_components
|
||||||
*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(
|
valid_child_list = ", ".join(
|
||||||
[f"`{v_child}`" for v_child in self._valid_children]
|
[f"`{v_child}`" for v_child in self._valid_children]
|
||||||
)
|
)
|
||||||
|
@ -175,6 +175,8 @@ class ConnectionBanner(Component):
|
|||||||
Returns:
|
Returns:
|
||||||
The connection banner component.
|
The connection banner component.
|
||||||
"""
|
"""
|
||||||
|
from reflex.components.base.bare import Bare
|
||||||
|
|
||||||
if not comp:
|
if not comp:
|
||||||
comp = Flex.create(
|
comp = Flex.create(
|
||||||
Text.create(
|
Text.create(
|
||||||
@ -189,7 +191,7 @@ class ConnectionBanner(Component):
|
|||||||
position="fixed",
|
position="fixed",
|
||||||
)
|
)
|
||||||
|
|
||||||
return cond(has_connection_errors, comp)
|
return Bare.create(cond(has_connection_errors, comp))
|
||||||
|
|
||||||
|
|
||||||
class ConnectionModal(Component):
|
class ConnectionModal(Component):
|
||||||
@ -205,18 +207,22 @@ class ConnectionModal(Component):
|
|||||||
Returns:
|
Returns:
|
||||||
The connection banner component.
|
The connection banner component.
|
||||||
"""
|
"""
|
||||||
|
from reflex.components.base.bare import Bare
|
||||||
|
|
||||||
if not comp:
|
if not comp:
|
||||||
comp = Text.create(*default_connection_error())
|
comp = Text.create(*default_connection_error())
|
||||||
return cond(
|
return Bare.create(
|
||||||
has_too_many_connection_errors,
|
cond(
|
||||||
DialogRoot.create(
|
has_too_many_connection_errors,
|
||||||
DialogContent.create(
|
DialogRoot.create(
|
||||||
DialogTitle.create("Connection Error"),
|
DialogContent.create(
|
||||||
comp,
|
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.imports import ImportDict, ImportVar
|
||||||
from reflex.utils.types import safe_issubclass
|
from reflex.utils.types import safe_issubclass
|
||||||
from reflex.vars import VarData
|
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
|
from reflex.vars.number import ternary_operation
|
||||||
|
|
||||||
_IS_TRUE_IMPORT: ImportDict = {
|
_IS_TRUE_IMPORT: ImportDict = {
|
||||||
@ -150,12 +151,23 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var:
|
|||||||
if c2 is None:
|
if c2 is None:
|
||||||
raise ValueError("For conditional vars, the second argument must be set.")
|
raise ValueError("For conditional vars, the second argument must be set.")
|
||||||
|
|
||||||
|
c1 = Var.create(c1)
|
||||||
|
c2 = Var.create(c2)
|
||||||
|
|
||||||
# Create the conditional var.
|
# Create the conditional var.
|
||||||
return ternary_operation(
|
return ternary_operation(
|
||||||
cond_var.bool(),
|
cond_var.bool(),
|
||||||
c1,
|
ArgsFunctionOperation.create(
|
||||||
c2,
|
(),
|
||||||
)
|
c1,
|
||||||
|
_var_type=ReflexCallable[[], c1._var_type],
|
||||||
|
),
|
||||||
|
ArgsFunctionOperation.create(
|
||||||
|
(),
|
||||||
|
c2,
|
||||||
|
_var_type=ReflexCallable[[], c2._var_type],
|
||||||
|
),
|
||||||
|
).call()
|
||||||
|
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
|
@ -184,11 +184,17 @@ class Match(MemoizationLeaf):
|
|||||||
return_type = Var
|
return_type = Var
|
||||||
|
|
||||||
for index, case in enumerate(match_cases):
|
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(
|
raise MatchTypeError(
|
||||||
f"Match cases should have the same return types. Case {index} with return "
|
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"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
|
@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
|
# 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.
|
# 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
|
props["theme"] = getattr(Theme, format.to_snake_case(props["theme"])) # type: ignore
|
||||||
console.deprecate(
|
console.deprecate(
|
||||||
feature_name="theme prop as string",
|
feature_name="theme prop as string",
|
||||||
|
@ -113,8 +113,8 @@ class MarkdownComponentMap:
|
|||||||
explicit_return = explicit_return or cls._explicit_return
|
explicit_return = explicit_return or cls._explicit_return
|
||||||
|
|
||||||
return ArgsFunctionOperation.create(
|
return ArgsFunctionOperation.create(
|
||||||
args_names=(DestructuredArg(fields=tuple(fn_args)),),
|
(DestructuredArg(fields=tuple(fn_args)),),
|
||||||
return_expr=fn_body,
|
fn_body,
|
||||||
explicit_return=explicit_return,
|
explicit_return=explicit_return,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -850,6 +850,22 @@ def safe_issubclass(cls: Any, class_or_tuple: Any, /) -> bool:
|
|||||||
) from e
|
) 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:
|
def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> bool:
|
||||||
"""Check if a type hint is a subclass of another type hint.
|
"""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 import constants
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.constants.compiler import Hooks
|
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 (
|
from reflex.utils.exceptions import (
|
||||||
VarAttributeError,
|
VarAttributeError,
|
||||||
VarDependencyError,
|
VarDependencyError,
|
||||||
@ -72,6 +72,7 @@ from reflex.utils.types import (
|
|||||||
_isinstance,
|
_isinstance,
|
||||||
get_origin,
|
get_origin,
|
||||||
has_args,
|
has_args,
|
||||||
|
infallible_issubclass,
|
||||||
typehint_issubclass,
|
typehint_issubclass,
|
||||||
unionize,
|
unionize,
|
||||||
)
|
)
|
||||||
@ -125,8 +126,25 @@ def unwrap_reflex_callalbe(
|
|||||||
"""
|
"""
|
||||||
if callable_type is ReflexCallable:
|
if callable_type is ReflexCallable:
|
||||||
return Ellipsis, Any
|
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
|
return Ellipsis, Any
|
||||||
|
|
||||||
args = get_args(callable_type)
|
args = get_args(callable_type)
|
||||||
if not args or len(args) != 2:
|
if not args or len(args) != 2:
|
||||||
return Ellipsis, Any
|
return Ellipsis, Any
|
||||||
@ -143,6 +161,7 @@ class VarSubclassEntry:
|
|||||||
var_subclass: Type[Var]
|
var_subclass: Type[Var]
|
||||||
to_var_subclass: Type[ToOperation]
|
to_var_subclass: Type[ToOperation]
|
||||||
python_types: Tuple[GenericType, ...]
|
python_types: Tuple[GenericType, ...]
|
||||||
|
is_subclass: Callable[[GenericType], bool] | None
|
||||||
|
|
||||||
|
|
||||||
_var_subclasses: List[VarSubclassEntry] = []
|
_var_subclasses: List[VarSubclassEntry] = []
|
||||||
@ -208,7 +227,7 @@ class VarData:
|
|||||||
object.__setattr__(self, "imports", immutable_imports)
|
object.__setattr__(self, "imports", immutable_imports)
|
||||||
object.__setattr__(self, "hooks", tuple(hooks or {}))
|
object.__setattr__(self, "hooks", tuple(hooks or {}))
|
||||||
object.__setattr__(
|
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, "deps", tuple(deps or []))
|
||||||
object.__setattr__(self, "position", position or None)
|
object.__setattr__(self, "position", position or None)
|
||||||
@ -444,6 +463,7 @@ class Var(Generic[VAR_TYPE]):
|
|||||||
cls,
|
cls,
|
||||||
python_types: Tuple[GenericType, ...] | GenericType = types.Unset(),
|
python_types: Tuple[GenericType, ...] | GenericType = types.Unset(),
|
||||||
default_type: GenericType = types.Unset(),
|
default_type: GenericType = types.Unset(),
|
||||||
|
is_subclass: Callable[[GenericType], bool] | types.Unset = types.Unset(),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""Initialize the subclass.
|
"""Initialize the subclass.
|
||||||
@ -451,11 +471,12 @@ class Var(Generic[VAR_TYPE]):
|
|||||||
Args:
|
Args:
|
||||||
python_types: The python types that the var represents.
|
python_types: The python types that the var represents.
|
||||||
default_type: The default type of the var. Defaults to the first python type.
|
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.
|
**kwargs: Additional keyword arguments.
|
||||||
"""
|
"""
|
||||||
super().__init_subclass__(**kwargs)
|
super().__init_subclass__(**kwargs)
|
||||||
|
|
||||||
if python_types or default_type:
|
if python_types or default_type or is_subclass:
|
||||||
python_types = (
|
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
|
if python_types
|
||||||
@ -480,7 +501,14 @@ class Var(Generic[VAR_TYPE]):
|
|||||||
|
|
||||||
ToVarOperation.__name__ = f'To{cls.__name__.removesuffix("Var")}Operation'
|
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):
|
def __post_init__(self):
|
||||||
"""Post-initialize the var."""
|
"""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.
|
# If the first argument is a python type, we map it to the corresponding Var type.
|
||||||
for var_subclass in _var_subclasses[::-1]:
|
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)
|
return self.to(var_subclass.var_subclass, output)
|
||||||
|
|
||||||
if fixed_output_type is None:
|
if fixed_output_type is None:
|
||||||
@ -801,12 +834,13 @@ class Var(Generic[VAR_TYPE]):
|
|||||||
Raises:
|
Raises:
|
||||||
TypeError: If the type is not supported for guessing.
|
TypeError: If the type is not supported for guessing.
|
||||||
"""
|
"""
|
||||||
from .number import NumberVar
|
|
||||||
from .object import ObjectVar
|
from .object import ObjectVar
|
||||||
|
|
||||||
var_type = self._var_type
|
var_type = self._var_type
|
||||||
|
|
||||||
if var_type is None:
|
if var_type is None:
|
||||||
return self.to(None)
|
return self.to(None)
|
||||||
|
|
||||||
if types.is_optional(var_type):
|
if types.is_optional(var_type):
|
||||||
var_type = types.get_args(var_type)[0]
|
var_type = types.get_args(var_type)[0]
|
||||||
|
|
||||||
@ -818,10 +852,15 @@ class Var(Generic[VAR_TYPE]):
|
|||||||
if fixed_type in types.UnionTypes:
|
if fixed_type in types.UnionTypes:
|
||||||
inner_types = get_args(var_type)
|
inner_types = get_args(var_type)
|
||||||
|
|
||||||
if all(
|
for var_subclass in _var_subclasses:
|
||||||
inspect.isclass(t) and issubclass(t, (int, float)) for t in inner_types
|
if all(
|
||||||
):
|
(
|
||||||
return self.to(NumberVar, self._var_type)
|
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):
|
if can_use_in_object_var(var_type):
|
||||||
return self.to(ObjectVar, self._var_type)
|
return self.to(ObjectVar, self._var_type)
|
||||||
@ -839,7 +878,9 @@ class Var(Generic[VAR_TYPE]):
|
|||||||
return self.to(None)
|
return self.to(None)
|
||||||
|
|
||||||
for var_subclass in _var_subclasses[::-1]:
|
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)
|
return self.to(var_subclass.var_subclass, self._var_type)
|
||||||
|
|
||||||
if can_use_in_object_var(fixed_type):
|
if can_use_in_object_var(fixed_type):
|
||||||
@ -1799,6 +1840,7 @@ def var_operation(
|
|||||||
),
|
),
|
||||||
function_name=func_name,
|
function_name=func_name,
|
||||||
type_computer=custom_operation_return._type_computer,
|
type_computer=custom_operation_return._type_computer,
|
||||||
|
_raw_js_function=custom_operation_return._raw_js_function,
|
||||||
_var_type=ReflexCallable[
|
_var_type=ReflexCallable[
|
||||||
tuple(
|
tuple(
|
||||||
arg_python_type
|
arg_python_type
|
||||||
@ -2541,15 +2583,17 @@ RETURN = TypeVar("RETURN")
|
|||||||
class CustomVarOperationReturn(Var[RETURN]):
|
class CustomVarOperationReturn(Var[RETURN]):
|
||||||
"""Base class for custom var operations."""
|
"""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
|
@classmethod
|
||||||
def create(
|
def create(
|
||||||
cls,
|
cls,
|
||||||
js_expression: str,
|
js_expression: str,
|
||||||
_var_type: Type[RETURN] | None = None,
|
_var_type: Type[RETURN] | None = None,
|
||||||
_type_computer: Optional[TypeComputer] = None,
|
_type_computer: TypeComputer | None = None,
|
||||||
_var_data: VarData | None = None,
|
_var_data: VarData | None = None,
|
||||||
|
_raw_js_function: str | None = None,
|
||||||
) -> CustomVarOperationReturn[RETURN]:
|
) -> CustomVarOperationReturn[RETURN]:
|
||||||
"""Create a CustomVarOperation.
|
"""Create a CustomVarOperation.
|
||||||
|
|
||||||
@ -2558,6 +2602,7 @@ class CustomVarOperationReturn(Var[RETURN]):
|
|||||||
_var_type: The type of the var.
|
_var_type: The type of the var.
|
||||||
_type_computer: A function to compute the type of the var given the arguments.
|
_type_computer: A function to compute the type of the var given the arguments.
|
||||||
_var_data: Additional hooks and imports associated with 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:
|
Returns:
|
||||||
The CustomVarOperation.
|
The CustomVarOperation.
|
||||||
@ -2567,6 +2612,7 @@ class CustomVarOperationReturn(Var[RETURN]):
|
|||||||
_var_type=_var_type or Any,
|
_var_type=_var_type or Any,
|
||||||
_type_computer=_type_computer,
|
_type_computer=_type_computer,
|
||||||
_var_data=_var_data,
|
_var_data=_var_data,
|
||||||
|
_raw_js_function=_raw_js_function,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -2575,6 +2621,7 @@ def var_operation_return(
|
|||||||
var_type: Type[RETURN] | None = None,
|
var_type: Type[RETURN] | None = None,
|
||||||
type_computer: Optional[TypeComputer] = None,
|
type_computer: Optional[TypeComputer] = None,
|
||||||
var_data: VarData | None = None,
|
var_data: VarData | None = None,
|
||||||
|
_raw_js_function: str | None = None,
|
||||||
) -> CustomVarOperationReturn[RETURN]:
|
) -> CustomVarOperationReturn[RETURN]:
|
||||||
"""Shortcut for creating a CustomVarOperationReturn.
|
"""Shortcut for creating a CustomVarOperationReturn.
|
||||||
|
|
||||||
@ -2583,6 +2630,7 @@ def var_operation_return(
|
|||||||
var_type: The type of the var.
|
var_type: The type of the var.
|
||||||
type_computer: A function to compute the type of the var given the arguments.
|
type_computer: A function to compute the type of the var given the arguments.
|
||||||
var_data: Additional hooks and imports associated with 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:
|
Returns:
|
||||||
The CustomVarOperationReturn.
|
The CustomVarOperationReturn.
|
||||||
@ -2592,6 +2640,7 @@ def var_operation_return(
|
|||||||
_var_type=var_type,
|
_var_type=var_type,
|
||||||
_type_computer=type_computer,
|
_type_computer=type_computer,
|
||||||
_var_data=var_data,
|
_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."""
|
"""Base class for immutable function vars."""
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
@ -304,15 +320,27 @@ class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]):
|
|||||||
if arg_len is not None:
|
if arg_len is not None:
|
||||||
if len(args) < required_arg_len:
|
if len(args) < required_arg_len:
|
||||||
raise VarTypeError(
|
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:
|
if len(args) > arg_len:
|
||||||
raise VarTypeError(
|
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))
|
args = tuple(map(LiteralVar.create, args))
|
||||||
self._pre_check(*args)
|
self._pre_check(*args)
|
||||||
return_type = self._return_type(*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()
|
return VarOperationCall.create(self, *args, _var_type=return_type).guess_type()
|
||||||
|
|
||||||
def chain(
|
def chain(
|
||||||
@ -412,7 +440,7 @@ class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]):
|
|||||||
Returns:
|
Returns:
|
||||||
True if the function can be called with the given arguments.
|
True if the function can be called with the given arguments.
|
||||||
"""
|
"""
|
||||||
return tuple()
|
return ()
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __get__(self, instance: None, owner: Any) -> FunctionVar[CALLABLE_TYPE]: ...
|
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())
|
(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)
|
if i < len(self._default_values)
|
||||||
and not isinstance(
|
and not isinstance(
|
||||||
(default_value := self._default_values[i]), inspect.Parameter.empty
|
(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
|
arg_name = self._args.args[i] if i < len(self._args.args) else None
|
||||||
if arg_name is not None:
|
if arg_name is not None:
|
||||||
raise VarTypeError(
|
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(
|
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) :]
|
return self._validators[len(args) :]
|
||||||
|
|
||||||
@ -679,6 +707,7 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]):
|
|||||||
_function_name: str = dataclasses.field(default="")
|
_function_name: str = dataclasses.field(default="")
|
||||||
_type_computer: Optional[TypeComputer] = dataclasses.field(default=None)
|
_type_computer: Optional[TypeComputer] = dataclasses.field(default=None)
|
||||||
_explicit_return: bool = dataclasses.field(default=False)
|
_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)
|
_cached_var_name = cached_property_no_lock(format_args_function_operation)
|
||||||
|
|
||||||
@ -698,6 +727,7 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]):
|
|||||||
function_name: str = "",
|
function_name: str = "",
|
||||||
explicit_return: bool = False,
|
explicit_return: bool = False,
|
||||||
type_computer: Optional[TypeComputer] = None,
|
type_computer: Optional[TypeComputer] = None,
|
||||||
|
_raw_js_function: str | None = None,
|
||||||
_var_type: GenericType = Callable,
|
_var_type: GenericType = Callable,
|
||||||
_var_data: VarData | None = None,
|
_var_data: VarData | None = None,
|
||||||
):
|
):
|
||||||
@ -712,6 +742,7 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]):
|
|||||||
function_name: The name of the function.
|
function_name: The name of the function.
|
||||||
explicit_return: Whether to use explicit return syntax.
|
explicit_return: Whether to use explicit return syntax.
|
||||||
type_computer: A function to compute the return type.
|
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_type: The type of the var.
|
||||||
_var_data: Additional hooks and imports associated with 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_type=_var_type,
|
||||||
_var_data=_var_data,
|
_var_data=_var_data,
|
||||||
_args=FunctionArgs(args=tuple(args_names), rest=rest),
|
_args=FunctionArgs(args=tuple(args_names), rest=rest),
|
||||||
|
_raw_js_function=_raw_js_function,
|
||||||
_default_values=tuple(default_values),
|
_default_values=tuple(default_values),
|
||||||
_function_name=function_name,
|
_function_name=function_name,
|
||||||
_validators=tuple(validators),
|
_validators=tuple(validators),
|
||||||
@ -753,6 +785,7 @@ class ArgsFunctionOperationBuilder(
|
|||||||
_function_name: str = dataclasses.field(default="")
|
_function_name: str = dataclasses.field(default="")
|
||||||
_type_computer: Optional[TypeComputer] = dataclasses.field(default=None)
|
_type_computer: Optional[TypeComputer] = dataclasses.field(default=None)
|
||||||
_explicit_return: bool = dataclasses.field(default=False)
|
_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)
|
_cached_var_name = cached_property_no_lock(format_args_function_operation)
|
||||||
|
|
||||||
@ -772,6 +805,7 @@ class ArgsFunctionOperationBuilder(
|
|||||||
function_name: str = "",
|
function_name: str = "",
|
||||||
explicit_return: bool = False,
|
explicit_return: bool = False,
|
||||||
type_computer: Optional[TypeComputer] = None,
|
type_computer: Optional[TypeComputer] = None,
|
||||||
|
_raw_js_function: str | None = None,
|
||||||
_var_type: GenericType = Callable,
|
_var_type: GenericType = Callable,
|
||||||
_var_data: VarData | None = None,
|
_var_data: VarData | None = None,
|
||||||
):
|
):
|
||||||
@ -788,6 +822,7 @@ class ArgsFunctionOperationBuilder(
|
|||||||
type_computer: A function to compute the return type.
|
type_computer: A function to compute the return type.
|
||||||
_var_type: The type of the var.
|
_var_type: The type of the var.
|
||||||
_var_data: Additional hooks and imports associated with 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:
|
Returns:
|
||||||
The function var.
|
The function var.
|
||||||
@ -797,6 +832,7 @@ class ArgsFunctionOperationBuilder(
|
|||||||
_var_type=_var_type,
|
_var_type=_var_type,
|
||||||
_var_data=_var_data,
|
_var_data=_var_data,
|
||||||
_args=FunctionArgs(args=tuple(args_names), rest=rest),
|
_args=FunctionArgs(args=tuple(args_names), rest=rest),
|
||||||
|
_raw_js_function=_raw_js_function,
|
||||||
_default_values=tuple(default_values),
|
_default_values=tuple(default_values),
|
||||||
_function_name=function_name,
|
_function_name=function_name,
|
||||||
_validators=tuple(validators),
|
_validators=tuple(validators),
|
||||||
|
@ -530,7 +530,9 @@ def number_abs_operation(
|
|||||||
The number absolute operation.
|
The number absolute operation.
|
||||||
"""
|
"""
|
||||||
return var_operation_return(
|
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:
|
Returns:
|
||||||
The number floor operation.
|
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
|
@var_operation
|
||||||
@ -763,7 +769,9 @@ def boolean_to_number_operation(value: Var[bool]):
|
|||||||
Returns:
|
Returns:
|
||||||
The boolean to number operation.
|
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(
|
def comparison_operator(
|
||||||
@ -1002,6 +1010,10 @@ _AT_SLICE_IMPORT: ImportDict = {
|
|||||||
f"$/{Dirs.STATE_PATH}": [ImportVar(tag="atSlice")],
|
f"$/{Dirs.STATE_PATH}": [ImportVar(tag="atSlice")],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_AT_SLICE_OR_INDEX: ImportDict = {
|
||||||
|
f"$/{Dirs.STATE_PATH}": [ImportVar(tag="atSliceOrIndex")],
|
||||||
|
}
|
||||||
|
|
||||||
_RANGE_IMPORT: ImportDict = {
|
_RANGE_IMPORT: ImportDict = {
|
||||||
f"$/{Dirs.UTILS}/helpers/range": [ImportVar(tag="range", is_default=True)],
|
f"$/{Dirs.UTILS}/helpers/range": [ImportVar(tag="range", is_default=True)],
|
||||||
}
|
}
|
||||||
@ -1021,6 +1033,7 @@ def boolify(value: Var):
|
|||||||
js_expression=f"isTrue({value})",
|
js_expression=f"isTrue({value})",
|
||||||
var_type=bool,
|
var_type=bool,
|
||||||
var_data=VarData(imports=_IS_TRUE_IMPORT),
|
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(
|
return var_operation_return(
|
||||||
js_expression=f"Object.keys({value})",
|
js_expression=f"Object.keys({value})",
|
||||||
var_type=List[str],
|
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()],
|
lambda x: List[x.to(ObjectVar)._value_type()],
|
||||||
),
|
),
|
||||||
var_type=List[Any],
|
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()]],
|
lambda x: List[Tuple[str, x.to(ObjectVar)._value_type()]],
|
||||||
),
|
),
|
||||||
var_type=List[Tuple[str, Any]],
|
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.constants.colors import Color
|
||||||
from reflex.utils.exceptions import VarTypeError
|
from reflex.utils.exceptions import VarTypeError
|
||||||
from reflex.utils.types import GenericType, get_origin
|
from reflex.utils.types import GenericType, get_origin
|
||||||
|
from reflex.vars.base import (
|
||||||
from .base import (
|
|
||||||
CachedVarOperation,
|
CachedVarOperation,
|
||||||
CustomVarOperationReturn,
|
CustomVarOperationReturn,
|
||||||
LiteralVar,
|
LiteralVar,
|
||||||
@ -51,8 +50,10 @@ from .base import (
|
|||||||
var_operation,
|
var_operation,
|
||||||
var_operation_return,
|
var_operation_return,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .number import (
|
from .number import (
|
||||||
_AT_SLICE_IMPORT,
|
_AT_SLICE_IMPORT,
|
||||||
|
_AT_SLICE_OR_INDEX,
|
||||||
_IS_TRUE_IMPORT,
|
_IS_TRUE_IMPORT,
|
||||||
_RANGE_IMPORT,
|
_RANGE_IMPORT,
|
||||||
LiteralNumberVar,
|
LiteralNumberVar,
|
||||||
@ -88,7 +89,7 @@ def string_lt_operation(lhs: Var[str], rhs: Var[str]):
|
|||||||
Returns:
|
Returns:
|
||||||
The string less than operation.
|
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
|
@var_operation
|
||||||
@ -102,7 +103,7 @@ def string_gt_operation(lhs: Var[str], rhs: Var[str]):
|
|||||||
Returns:
|
Returns:
|
||||||
The string greater than operation.
|
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
|
@var_operation
|
||||||
@ -116,7 +117,7 @@ def string_le_operation(lhs: Var[str], rhs: Var[str]):
|
|||||||
Returns:
|
Returns:
|
||||||
The string less than or equal operation.
|
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
|
@var_operation
|
||||||
@ -130,7 +131,7 @@ def string_ge_operation(lhs: Var[str], rhs: Var[str]):
|
|||||||
Returns:
|
Returns:
|
||||||
The string greater than or equal operation.
|
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
|
@var_operation
|
||||||
@ -143,7 +144,11 @@ def string_lower_operation(string: Var[str]):
|
|||||||
Returns:
|
Returns:
|
||||||
The lowercase string.
|
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
|
@var_operation
|
||||||
@ -156,7 +161,11 @@ def string_upper_operation(string: Var[str]):
|
|||||||
Returns:
|
Returns:
|
||||||
The uppercase string.
|
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
|
@var_operation
|
||||||
@ -169,7 +178,11 @@ def string_strip_operation(string: Var[str]):
|
|||||||
Returns:
|
Returns:
|
||||||
The stripped string.
|
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
|
@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)
|
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
|
@var_operation
|
||||||
def string_replace_operation(
|
def string_replace_operation(
|
||||||
string: Var[str], search_value: Var[str], new_value: Var[str]
|
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.
|
The item or slice from the array.
|
||||||
"""
|
"""
|
||||||
return var_operation_return(
|
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(
|
type_computer=nary_type_computer(
|
||||||
ReflexCallable[[Sequence, Union[int, slice]], Any],
|
ReflexCallable[[Sequence, Union[int, slice]], Any],
|
||||||
ReflexCallable[[Union[int, slice]], Any],
|
ReflexCallable[[Union[int, slice]], Any],
|
||||||
@ -465,7 +532,7 @@ def array_item_or_slice_operation(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
var_data=VarData(
|
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
|
__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
|
lower = string_lower_operation
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ def test_connection_banner():
|
|||||||
"react",
|
"react",
|
||||||
"$/utils/context",
|
"$/utils/context",
|
||||||
"$/utils/state",
|
"$/utils/state",
|
||||||
|
"@emotion/react",
|
||||||
RadixThemesComponent().library or "",
|
RadixThemesComponent().library or "",
|
||||||
"$/env.json",
|
"$/env.json",
|
||||||
)
|
)
|
||||||
@ -43,6 +44,7 @@ def test_connection_modal():
|
|||||||
"react",
|
"react",
|
||||||
"$/utils/context",
|
"$/utils/context",
|
||||||
"$/utils/state",
|
"$/utils/state",
|
||||||
|
"@emotion/react",
|
||||||
RadixThemesComponent().library or "",
|
RadixThemesComponent().library or "",
|
||||||
"$/env.json",
|
"$/env.json",
|
||||||
)
|
)
|
||||||
|
@ -846,7 +846,7 @@ def test_component_event_trigger_arbitrary_args():
|
|||||||
|
|
||||||
assert comp.render()["props"][0] == (
|
assert comp.render()["props"][0] == (
|
||||||
"onFoo={((__e, _alpha, _bravo, _charlie) => (addEvents("
|
"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], ({ }))))}"
|
"[__e, _alpha, _bravo, _charlie], ({ }))))}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -432,12 +432,15 @@ def test_default_setters(test_state):
|
|||||||
def test_class_indexing_with_vars():
|
def test_class_indexing_with_vars():
|
||||||
"""Test that we can index into a state var with another var."""
|
"""Test that we can index into a state var with another var."""
|
||||||
prop = TestState.array[TestState.num1]
|
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]
|
prop = TestState.mapping["a"][TestState.num1]
|
||||||
assert (
|
assert (
|
||||||
str(prop)
|
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]
|
prop = TestState.mapping[TestState.map_key]
|
||||||
|
@ -307,30 +307,65 @@ def test_basic_operations(TestObj):
|
|||||||
Args:
|
Args:
|
||||||
TestObj: The test object.
|
TestObj: The test object.
|
||||||
"""
|
"""
|
||||||
assert str(v(1) == v(2)) == "(1 === 2)"
|
assert str(v(1) == v(2)) == "(((_lhs, _rhs) => (_lhs === _rhs))(1, 2))"
|
||||||
assert str(v(1) != v(2)) == "(1 !== 2)"
|
assert str(v(1) != v(2)) == "(((_lhs, _rhs) => (_lhs !== _rhs))(1, 2))"
|
||||||
assert str(LiteralNumberVar.create(1) < 2) == "(1 < 2)"
|
assert (
|
||||||
assert str(LiteralNumberVar.create(1) <= 2) == "(1 <= 2)"
|
str(LiteralNumberVar.create(1) < 2) == "(((_lhs, _rhs) => (_lhs < _rhs))(1, 2))"
|
||||||
assert str(LiteralNumberVar.create(1) > 2) == "(1 > 2)"
|
)
|
||||||
assert str(LiteralNumberVar.create(1) >= 2) == "(1 >= 2)"
|
assert (
|
||||||
assert str(LiteralNumberVar.create(1) + 2) == "(1 + 2)"
|
str(LiteralNumberVar.create(1) <= 2)
|
||||||
assert str(LiteralNumberVar.create(1) - 2) == "(1 - 2)"
|
== "(((_lhs, _rhs) => (_lhs <= _rhs))(1, 2))"
|
||||||
assert str(LiteralNumberVar.create(1) * 2) == "(1 * 2)"
|
)
|
||||||
assert str(LiteralNumberVar.create(1) / 2) == "(1 / 2)"
|
assert (
|
||||||
assert str(LiteralNumberVar.create(1) // 2) == "Math.floor(1 / 2)"
|
str(LiteralNumberVar.create(1) > 2) == "(((_lhs, _rhs) => (_lhs > _rhs))(1, 2))"
|
||||||
assert str(LiteralNumberVar.create(1) % 2) == "(1 % 2)"
|
)
|
||||||
assert str(LiteralNumberVar.create(1) ** 2) == "(1 ** 2)"
|
assert (
|
||||||
assert str(LiteralNumberVar.create(1) & v(2)) == "(1 && 2)"
|
str(LiteralNumberVar.create(1) >= 2)
|
||||||
assert str(LiteralNumberVar.create(1) | v(2)) == "(1 || 2)"
|
== "(((_lhs, _rhs) => (_lhs >= _rhs))(1, 2))"
|
||||||
assert str(LiteralArrayVar.create([1, 2, 3])[0]) == "[1, 2, 3].at(0)"
|
)
|
||||||
|
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 (
|
assert (
|
||||||
str(LiteralObjectVar.create({"a": 1, "b": 2})["a"])
|
str(LiteralObjectVar.create({"a": 1, "b": 2})["a"])
|
||||||
== '({ ["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 (
|
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"))
|
print(Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state"))
|
||||||
assert (
|
assert (
|
||||||
@ -338,33 +373,39 @@ def test_basic_operations(TestObj):
|
|||||||
Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state").bar
|
Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state").bar
|
||||||
== LiteralVar.create("bar")
|
== LiteralVar.create("bar")
|
||||||
)
|
)
|
||||||
== '(state.foo["bar"] === "bar")'
|
== '(((_lhs, _rhs) => (_lhs === _rhs))(state.foo["bar"], "bar"))'
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
str(Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state").bar)
|
str(Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state").bar)
|
||||||
== 'state.foo["bar"]'
|
== 'state.foo["bar"]'
|
||||||
)
|
)
|
||||||
assert str(abs(LiteralNumberVar.create(1))) == "Math.abs(1)"
|
assert str(abs(LiteralNumberVar.create(1))) == "(Math.abs(1))"
|
||||||
assert str(LiteralArrayVar.create([1, 2, 3]).length()) == "[1, 2, 3].length"
|
assert (
|
||||||
|
str(LiteralArrayVar.create([1, 2, 3]).length())
|
||||||
|
== "(((...args) => (((_array) => _array.length)([1, 2, 3], ...args)))())"
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
str(LiteralArrayVar.create([1, 2]) + LiteralArrayVar.create([3, 4]))
|
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
|
# Tests for reverse operation
|
||||||
assert (
|
assert (
|
||||||
str(LiteralArrayVar.create([1, 2, 3]).reverse())
|
str(LiteralArrayVar.create([1, 2, 3]).reverse())
|
||||||
== "[1, 2, 3].slice().reverse()"
|
== "(((...args) => (((_array) => _array.slice().reverse())([1, 2, 3], ...args)))())"
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
str(LiteralArrayVar.create(["1", "2", "3"]).reverse())
|
str(LiteralArrayVar.create(["1", "2", "3"]).reverse())
|
||||||
== '["1", "2", "3"].slice().reverse()'
|
== '(((...args) => (((_array) => _array.slice().reverse())(["1", "2", "3"], ...args)))())'
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
str(Var(_js_expr="foo")._var_set_state("state").to(list).reverse())
|
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))"
|
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):
|
def test_list_tuple_contains(var, expected):
|
||||||
assert str(var.contains(1)) == f"{expected}.includes(1)"
|
assert (
|
||||||
assert str(var.contains("1")) == f'{expected}.includes("1")'
|
str(var.contains(1))
|
||||||
assert str(var.contains(v(1))) == f"{expected}.includes(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'{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))'
|
||||||
|
)
|
||||||
|
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_state_var = Var(_js_expr="other", _var_type=str)._var_set_state("state")
|
||||||
other_var = Var(_js_expr="other", _var_type=str)
|
other_var = Var(_js_expr="other", _var_type=str)
|
||||||
assert str(var.contains(other_state_var)) == f"{expected}.includes(state.other)"
|
assert (
|
||||||
assert str(var.contains(other_var)) == f"{expected}.includes(other)"
|
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):
|
class Foo(rx.Base):
|
||||||
@ -446,15 +505,27 @@ def test_var_types(var, var_type):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_str_contains(var, expected):
|
def test_str_contains(var, expected):
|
||||||
assert str(var.contains("1")) == f'{expected}.includes("1")'
|
assert (
|
||||||
assert str(var.contains(v("1"))) == f'{expected}.includes("1")'
|
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_state_var = Var(_js_expr="other")._var_set_state("state").to(str)
|
||||||
other_var = Var(_js_expr="other").to(str)
|
other_var = Var(_js_expr="other").to(str)
|
||||||
assert str(var.contains(other_state_var)) == f"{expected}.includes(state.other)"
|
assert (
|
||||||
assert str(var.contains(other_var)) == f"{expected}.includes(other)"
|
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 (
|
assert (
|
||||||
str(var.contains("1", "hello"))
|
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):
|
def test_dict_contains(var, expected):
|
||||||
assert str(var.contains(1)) == f"{expected}.hasOwnProperty(1)"
|
assert (
|
||||||
assert str(var.contains("1")) == f'{expected}.hasOwnProperty("1")'
|
str(var.contains(1))
|
||||||
assert str(var.contains(v(1))) == f"{expected}.hasOwnProperty(1)"
|
== f"(((_object, _key) => _object.hasOwnProperty(_key))({expected!s}, 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(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_state_var = Var(_js_expr="other")._var_set_state("state").to(str)
|
||||||
other_var = Var(_js_expr="other").to(str)
|
other_var = Var(_js_expr="other").to(str)
|
||||||
assert (
|
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(
|
@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="list", _var_type=List[int]).guess_type(),
|
||||||
Var(_js_expr="tuple", _var_type=Tuple[int, 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):
|
def test_var_indexing_lists(var):
|
||||||
@ -494,11 +580,20 @@ def test_var_indexing_lists(var):
|
|||||||
var : The str, list or tuple base var.
|
var : The str, list or tuple base var.
|
||||||
"""
|
"""
|
||||||
# Test basic indexing.
|
# Test basic indexing.
|
||||||
assert str(var[0]) == f"{var._js_expr}.at(0)"
|
assert (
|
||||||
assert str(var[1]) == f"{var._js_expr}.at(1)"
|
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.
|
# 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(
|
@pytest.mark.parametrize(
|
||||||
@ -532,11 +627,20 @@ def test_var_indexing_str():
|
|||||||
assert str_var[0]._var_type is str
|
assert str_var[0]._var_type is str
|
||||||
|
|
||||||
# Test basic indexing.
|
# Test basic indexing.
|
||||||
assert str(str_var[0]) == "str.at(0)"
|
assert (
|
||||||
assert str(str_var[1]) == "str.at(1)"
|
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.
|
# 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(
|
@pytest.mark.parametrize(
|
||||||
@ -651,9 +755,18 @@ def test_var_list_slicing(var):
|
|||||||
Args:
|
Args:
|
||||||
var : The str, list or tuple base var.
|
var : The str, list or tuple base var.
|
||||||
"""
|
"""
|
||||||
assert str(var[:1]) == f"{var._js_expr}.slice(undefined, 1)"
|
assert (
|
||||||
assert str(var[1:]) == f"{var._js_expr}.slice(1, undefined)"
|
str(var[:1])
|
||||||
assert str(var[:]) == f"{var._js_expr}.slice(undefined, undefined)"
|
== 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():
|
def test_str_var_slicing():
|
||||||
@ -665,16 +778,40 @@ def test_str_var_slicing():
|
|||||||
assert str_var[:1]._var_type is str
|
assert str_var[:1]._var_type is str
|
||||||
|
|
||||||
# Test basic slicing.
|
# Test basic slicing.
|
||||||
assert str(str_var[:1]) == 'str.split("").slice(undefined, 1).join("")'
|
assert (
|
||||||
assert str(str_var[1:]) == 'str.split("").slice(1, undefined).join("")'
|
str(str_var[:1])
|
||||||
assert str(str_var[:]) == 'str.split("").slice(undefined, undefined).join("")'
|
== "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([null, 1, null]))"
|
||||||
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)))([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.
|
# Test negative slicing.
|
||||||
assert str(str_var[:-1]) == 'str.split("").slice(undefined, -1).join("")'
|
assert (
|
||||||
assert str(str_var[-1:]) == 'str.split("").slice(-1, undefined).join("")'
|
str(str_var[:-1])
|
||||||
assert str(str_var[:-2]) == 'str.split("").slice(undefined, -2).join("")'
|
== "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([null, -1, null]))"
|
||||||
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)))([-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():
|
def test_dict_indexing():
|
||||||
@ -966,8 +1103,8 @@ def test_var_operation():
|
|||||||
def add(a: Var[int], b: Var[int]):
|
def add(a: Var[int], b: Var[int]):
|
||||||
return var_operation_return(js_expression=f"({a} + {b})", var_type=int)
|
return var_operation_return(js_expression=f"({a} + {b})", var_type=int)
|
||||||
|
|
||||||
assert str(add(1, 2)) == "(1 + 2)"
|
assert str(add(1, 2)) == "(((_a, _b) => (_a + _b))(1, 2))"
|
||||||
assert str(add(4, -9)) == "(4 + -9)"
|
assert str(add(4, -9)) == "(((_a, _b) => (_a + _b))(4, -9))"
|
||||||
|
|
||||||
five = LiteralNumberVar.create(5)
|
five = LiteralNumberVar.create(5)
|
||||||
seven = add(2, five)
|
seven = add(2, five)
|
||||||
@ -978,13 +1115,29 @@ def test_var_operation():
|
|||||||
def test_string_operations():
|
def test_string_operations():
|
||||||
basic_string = LiteralStringVar.create("Hello, World!")
|
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 (
|
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 (
|
assert (
|
||||||
str(complicated_number)
|
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 = ~(
|
even_more_complicated_number = ~(
|
||||||
@ -1004,14 +1157,20 @@ def test_all_number_operations():
|
|||||||
|
|
||||||
assert (
|
assert (
|
||||||
str(even_more_complicated_number)
|
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 (
|
||||||
assert str(LiteralBooleanVar.create(False) < 5) == "(Number(false) < 5)"
|
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 (
|
assert (
|
||||||
str(LiteralBooleanVar.create(False) < LiteralBooleanVar.create(True))
|
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(False), "false"),
|
||||||
(Var.create(True), "true"),
|
(Var.create(True), "true"),
|
||||||
(Var.create("false"), 'isTrue("false")'),
|
(Var.create("false"), '(isTrue("false"))'),
|
||||||
(Var.create([1, 2, 3]), "isTrue([1, 2, 3])"),
|
(Var.create([1, 2, 3]), "(isTrue([1, 2, 3]))"),
|
||||||
(Var.create({"a": 1, "b": 2}), 'isTrue(({ ["a"] : 1, ["b"] : 2 }))'),
|
(Var.create({"a": 1, "b": 2}), '(isTrue(({ ["a"] : 1, ["b"] : 2 })))'),
|
||||||
(Var("mysterious_var"), "isTrue(mysterious_var)"),
|
(Var("mysterious_var"), "(isTrue(mysterious_var))"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_boolify_operations(var, expected):
|
def test_boolify_operations(var, expected):
|
||||||
@ -1032,18 +1191,30 @@ def test_boolify_operations(var, expected):
|
|||||||
|
|
||||||
def test_index_operation():
|
def test_index_operation():
|
||||||
array_var = LiteralArrayVar.create([1, 2, 3, 4, 5])
|
array_var = LiteralArrayVar.create([1, 2, 3, 4, 5])
|
||||||
assert str(array_var[0]) == "[1, 2, 3, 4, 5].at(0)"
|
assert (
|
||||||
assert str(array_var[1:2]) == "[1, 2, 3, 4, 5].slice(1, 2)"
|
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 (
|
assert (
|
||||||
str(array_var[1:4:2])
|
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 (
|
assert (
|
||||||
str(array_var[::-1])
|
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(
|
@pytest.mark.parametrize(
|
||||||
@ -1065,24 +1236,33 @@ def test_inf_and_nan(var, expected_js):
|
|||||||
def test_array_operations():
|
def test_array_operations():
|
||||||
array_var = LiteralArrayVar.create([1, 2, 3, 4, 5])
|
array_var = LiteralArrayVar.create([1, 2, 3, 4, 5])
|
||||||
|
|
||||||
assert str(array_var.length()) == "[1, 2, 3, 4, 5].length"
|
assert (
|
||||||
assert str(array_var.contains(3)) == "[1, 2, 3, 4, 5].includes(3)"
|
str(array_var.length())
|
||||||
assert str(array_var.reverse()) == "[1, 2, 3, 4, 5].slice().reverse()"
|
== "(((...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 (
|
assert (
|
||||||
str(ArrayVar.range(10))
|
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 (
|
assert (
|
||||||
str(ArrayVar.range(1, 10))
|
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 (
|
assert (
|
||||||
str(ArrayVar.range(1, 10, 2))
|
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 (
|
assert (
|
||||||
str(ArrayVar.range(1, 10, -1))
|
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})
|
object_var = LiteralObjectVar.create({"a": 1, "b": 2, "c": 3})
|
||||||
|
|
||||||
assert (
|
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 (
|
assert (
|
||||||
str(object_var.values())
|
str(object_var.values())
|
||||||
== 'Object.values(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))'
|
== '(Object.values(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })))'
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
str(object_var.entries())
|
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["a"]) == '({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["a"]'
|
assert str(object_var["a"]) == '({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["a"]'
|
||||||
assert (
|
assert (
|
||||||
str(object_var.merge(LiteralObjectVar.create({"c": 4, "d": 5})))
|
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 (
|
assert (
|
||||||
str(object_var.keys()[0].upper()) # type: ignore
|
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 (
|
assert (
|
||||||
str(object_var.entries()[1][1] - 1) # type: ignore
|
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 (
|
assert (
|
||||||
str(object_var["c"] + object_var["b"]) # type: ignore
|
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():
|
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 (
|
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 Var(_js_expr="var").to(str).contains(other)
|
||||||
assert (
|
assert (
|
||||||
err.value.args[0]
|
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([10, 20]),
|
||||||
LiteralVar.create("5"),
|
LiteralVar.create("5"),
|
||||||
[
|
[
|
||||||
"+",
|
|
||||||
"-",
|
"-",
|
||||||
"/",
|
"/",
|
||||||
"//",
|
"//",
|
||||||
"*",
|
"*",
|
||||||
"%",
|
"%",
|
||||||
"**",
|
"**",
|
||||||
">",
|
|
||||||
"<",
|
|
||||||
"<=",
|
|
||||||
">=",
|
|
||||||
"^",
|
"^",
|
||||||
"<<",
|
"<<",
|
||||||
">>",
|
">>",
|
||||||
|
Loading…
Reference in New Issue
Block a user