use ArgsFunctionOperation
This commit is contained in:
parent
dc18101118
commit
ec5f751f3f
@ -6,13 +6,14 @@ import textwrap
|
||||
from functools import lru_cache
|
||||
from hashlib import md5
|
||||
from typing import Any, Callable, Dict, Union
|
||||
import dataclasses
|
||||
|
||||
from reflex.components.component import Component, CustomComponent
|
||||
from reflex.components.tags.tag import Tag
|
||||
from reflex.utils import types
|
||||
from reflex.utils.imports import ImportDict, ImportVar
|
||||
from reflex.vars.base import LiteralVar, Var
|
||||
from reflex.vars.function import ARRAY_ISARRAY
|
||||
from reflex.vars.function import ARRAY_ISARRAY, ArgsFunctionOperation
|
||||
from reflex.vars.number import ternary_operation
|
||||
|
||||
# Special vars used in the component map.
|
||||
@ -75,8 +76,10 @@ def get_base_component_map() -> dict[str, Callable]:
|
||||
}
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class MarkdownComponentMap:
|
||||
"""Mixin class for handling custom component maps in Markdown components."""
|
||||
_explicit_return: bool = dataclasses.field(default=False)
|
||||
|
||||
@classmethod
|
||||
def get_component_map_custom_code(cls) -> str:
|
||||
@ -89,22 +92,23 @@ class MarkdownComponentMap:
|
||||
|
||||
@classmethod
|
||||
def create_map_fn_var(
|
||||
cls, fn_body: str | None = None, fn_args: list[str] | None = None
|
||||
cls, fn_body: Var | None = None, fn_args: tuple[str, ...] | None = None, explicit_return: bool | None = None
|
||||
) -> Var:
|
||||
"""Create a function Var for the component map.
|
||||
|
||||
Args:
|
||||
fn_body: The formatted component as a string.
|
||||
fn_args: The function arguments.
|
||||
explicit_return: Whether to use explicit return syntax.
|
||||
|
||||
Returns:
|
||||
The function Var for the component map.
|
||||
"""
|
||||
fn_args = fn_args or cls.get_fn_args()
|
||||
fn_body = fn_body or cls.get_fn_body()
|
||||
fn_args_str = ", ".join(fn_args)
|
||||
fn_body = fn_body if fn_body is not None else cls.get_fn_body()
|
||||
explicit_return = explicit_return or cls._explicit_return
|
||||
|
||||
return Var(_js_expr=f"(({{{fn_args_str}}}) => {fn_body})")
|
||||
return ArgsFunctionOperation.create(args_names=fn_args, return_expr=fn_body, destructure_args=True, explicit_return=explicit_return)
|
||||
|
||||
@classmethod
|
||||
def get_fn_args(cls) -> list[str]:
|
||||
@ -116,13 +120,13 @@ class MarkdownComponentMap:
|
||||
return ["node", _CHILDREN._js_expr, _PROPS._js_expr]
|
||||
|
||||
@classmethod
|
||||
def get_fn_body(cls) -> str:
|
||||
def get_fn_body(cls) -> Var:
|
||||
"""Get the function body for the component map.
|
||||
|
||||
Returns:
|
||||
The function body as a string.
|
||||
"""
|
||||
return "()"
|
||||
return Var(_js_expr="", _var_type=str)
|
||||
|
||||
|
||||
class Markdown(Component):
|
||||
@ -269,23 +273,24 @@ class Markdown(Component):
|
||||
codeblock_custom_code = "\n".join(custom_code_list)
|
||||
|
||||
# Format the code to handle inline and block code.
|
||||
formatted_code = f"""{{{codeblock_custom_code};
|
||||
formatted_code = f"""{codeblock_custom_code};
|
||||
return inline ? (
|
||||
{self.format_component("code")}
|
||||
) : (
|
||||
{self.format_component("codeblock", language=_LANGUAGE)}
|
||||
);
|
||||
}}""".replace("\n", " ")
|
||||
""".replace("\n", " ")
|
||||
|
||||
return MarkdownComponentMap.create_map_fn_var(
|
||||
fn_args=[
|
||||
fn_args=(
|
||||
"node",
|
||||
"inline",
|
||||
"className",
|
||||
_CHILDREN._js_expr,
|
||||
_PROPS._js_expr,
|
||||
],
|
||||
fn_body=formatted_code,
|
||||
),
|
||||
fn_body=Var(_js_expr=formatted_code),
|
||||
explicit_return=True
|
||||
)
|
||||
|
||||
def get_component(self, tag: str, **props) -> Component:
|
||||
@ -354,11 +359,12 @@ class Markdown(Component):
|
||||
Returns:
|
||||
The function Var for the component map.
|
||||
"""
|
||||
formatted_component = Var(_js_expr=f"({self.format_component(tag)})", _var_type=str)
|
||||
if isinstance(component, MarkdownComponentMap):
|
||||
return component.create_map_fn_var(f"({self.format_component(tag)})")
|
||||
return component.create_map_fn_var(fn_body=formatted_component)
|
||||
|
||||
# fallback to the default fn Var creation if the component is not a MarkdownComponentMap.
|
||||
return MarkdownComponentMap.create_map_fn_var(f"({self.format_component(tag)})")
|
||||
return MarkdownComponentMap.create_map_fn_var(fn_body=formatted_component)
|
||||
|
||||
def _get_map_fn_custom_code_from_children(self, component) -> list[str]:
|
||||
"""Recursively get markdown custom code from children components.
|
||||
|
@ -3,6 +3,7 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
import dataclasses
|
||||
from functools import lru_cache
|
||||
from typing import Any, Callable, Dict, Optional, Union, overload
|
||||
|
||||
@ -28,18 +29,21 @@ NO_PROPS_TAGS = ("ul", "ol", "li")
|
||||
|
||||
@lru_cache
|
||||
def get_base_component_map() -> dict[str, Callable]: ...
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class MarkdownComponentMap:
|
||||
@classmethod
|
||||
def get_component_map_custom_code(cls) -> str: ...
|
||||
@classmethod
|
||||
def create_map_fn_var(
|
||||
cls, fn_body: str | None = None, fn_args: list[str] | None = None
|
||||
cls,
|
||||
fn_body: Var | None = None,
|
||||
fn_args: tuple[str, ...] | None = None,
|
||||
explicit_return: bool | None = None,
|
||||
) -> Var: ...
|
||||
@classmethod
|
||||
def get_fn_args(cls) -> list[str]: ...
|
||||
@classmethod
|
||||
def get_fn_body(cls) -> str: ...
|
||||
def get_fn_body(cls) -> Var: ...
|
||||
|
||||
class Markdown(Component):
|
||||
@overload
|
||||
|
@ -7,6 +7,7 @@ import sys
|
||||
from typing import Any, Callable, Optional, Tuple, Type, Union
|
||||
|
||||
from reflex.utils.types import GenericType
|
||||
from reflex.utils import format
|
||||
|
||||
from .base import CachedVarOperation, LiteralVar, Var, VarData, cached_property_no_lock
|
||||
|
||||
@ -136,6 +137,8 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar):
|
||||
|
||||
_args_names: Tuple[str, ...] = dataclasses.field(default_factory=tuple)
|
||||
_return_expr: Union[Var, Any] = dataclasses.field(default=None)
|
||||
_destructure_args: bool = dataclasses.field(default=False)
|
||||
_explicit_return: bool = dataclasses.field(default=True)
|
||||
|
||||
@cached_property_no_lock
|
||||
def _cached_var_name(self) -> str:
|
||||
@ -144,21 +147,36 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar):
|
||||
Returns:
|
||||
The name of the var.
|
||||
"""
|
||||
return f"(({', '.join(self._args_names)}) => ({str(LiteralVar.create(self._return_expr))}))"
|
||||
arg_names_str = ", ".join(self._args_names)
|
||||
return_expr_str = str(LiteralVar.create(self._return_expr))
|
||||
|
||||
if self._destructure_args:
|
||||
arg_names_str = format.wrap(arg_names_str, "{", "}")
|
||||
|
||||
# Wrap return expression in curly braces if explicit return syntax is used.
|
||||
return_expr_str = format.wrap(return_expr_str, "{", "}") if self._explicit_return else format.wrap(return_expr_str, "(", ")")
|
||||
|
||||
return f"(({arg_names_str}) => {return_expr_str})"
|
||||
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
cls,
|
||||
args_names: Tuple[str, ...],
|
||||
return_expr: Var | Any,
|
||||
destructure_args: bool = False,
|
||||
explicit_return: bool = False,
|
||||
_var_type: GenericType = Callable,
|
||||
_var_data: VarData | None = None,
|
||||
|
||||
) -> ArgsFunctionOperation:
|
||||
"""Create a new function var.
|
||||
|
||||
Args:
|
||||
args_names: The names of the arguments.
|
||||
return_expr: The return expression of the function.
|
||||
destructure_args: Whether to destructure the arguments.
|
||||
explicit_return: Whether to use explicit return syntax.
|
||||
_var_data: Additional hooks and imports associated with the Var.
|
||||
|
||||
Returns:
|
||||
@ -170,6 +188,8 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar):
|
||||
_var_data=_var_data,
|
||||
_args_names=args_names,
|
||||
_return_expr=return_expr,
|
||||
_destructure_args=destructure_args,
|
||||
_explicit_return=explicit_return
|
||||
)
|
||||
|
||||
|
||||
|
@ -18,22 +18,22 @@ class CustomMarkdownComponent(Component, MarkdownComponentMap):
|
||||
library = "custom"
|
||||
|
||||
@classmethod
|
||||
def get_fn_args(cls) -> list[str]:
|
||||
def get_fn_args(cls) -> tuple[str, ...]:
|
||||
"""Return the function arguments.
|
||||
|
||||
Returns:
|
||||
The function arguments.
|
||||
"""
|
||||
return ["custom_node", "custom_children", "custom_props"]
|
||||
return ("custom_node", "custom_children", "custom_props")
|
||||
|
||||
@classmethod
|
||||
def get_fn_body(cls) -> str:
|
||||
def get_fn_body(cls) -> Var:
|
||||
"""Return the function body.
|
||||
|
||||
Returns:
|
||||
The function body.
|
||||
"""
|
||||
return "{return custom_node + custom_children + custom_props;}"
|
||||
return Var(_js_expr="{return custom_node + custom_children + custom_props}")
|
||||
|
||||
|
||||
def syntax_highlighter_memoized_component(codeblock: Type[Component]):
|
||||
@ -58,67 +58,74 @@ def syntax_highlighter_memoized_component(codeblock: Type[Component]):
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"fn_body, fn_args, expected",
|
||||
"fn_body, fn_args, explicit_return, expected",
|
||||
[
|
||||
(None, None, Var(_js_expr="(({node, children, ...props}) => ())")),
|
||||
("{return node;}", ["node"], Var(_js_expr="(({node}) => {return node;})")),
|
||||
(None, None, False, Var(_js_expr="(({node, children, ...props}) => ())")),
|
||||
("return node", ("node", ), True, Var(_js_expr="(({node}) => {return node})")),
|
||||
(
|
||||
"{return node + children;}",
|
||||
["node", "children"],
|
||||
Var(_js_expr="(({node, children}) => {return node + children;})"),
|
||||
"return node + children",
|
||||
("node", "children"),
|
||||
True,
|
||||
Var(_js_expr="(({node, children}) => {return node + children})"),
|
||||
),
|
||||
(
|
||||
"{return node + props;}",
|
||||
["node", "...props"],
|
||||
Var(_js_expr="(({node, ...props}) => {return node + props;})"),
|
||||
"return node + props",
|
||||
("node", "...props"),
|
||||
True,
|
||||
Var(_js_expr="(({node, ...props}) => {return node + props})"),
|
||||
),
|
||||
(
|
||||
"{return node + children + props;}",
|
||||
["node", "children", "...props"],
|
||||
"return node + children + props",
|
||||
("node", "children", "...props"),
|
||||
True,
|
||||
Var(
|
||||
_js_expr="(({node, children, ...props}) => {return node + children + props;})"
|
||||
_js_expr="(({node, children, ...props}) => {return node + children + props})"
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_create_map_fn_var(fn_body, fn_args, expected):
|
||||
result = MarkdownComponentMap.create_map_fn_var(fn_body, fn_args)
|
||||
def test_create_map_fn_var(fn_body, fn_args, explicit_return, expected):
|
||||
result = MarkdownComponentMap.create_map_fn_var(fn_body= Var(_js_expr=fn_body,_var_type=str) if fn_body else None, fn_args=fn_args, explicit_return=explicit_return)
|
||||
assert result._js_expr == expected._js_expr
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"cls, fn_body, fn_args, expected",
|
||||
"cls, fn_body, fn_args, explicit_return, expected",
|
||||
[
|
||||
(
|
||||
MarkdownComponentMap,
|
||||
None,
|
||||
None,
|
||||
False,
|
||||
Var(_js_expr="(({node, children, ...props}) => ())"),
|
||||
),
|
||||
(
|
||||
MarkdownComponentMap,
|
||||
"{return node};",
|
||||
["node"],
|
||||
Var(_js_expr="(({node}) => {return node};)"),
|
||||
"return node",
|
||||
("node", ),
|
||||
True,
|
||||
Var(_js_expr="(({node}) => {return node})"),
|
||||
),
|
||||
(
|
||||
CustomMarkdownComponent,
|
||||
None,
|
||||
None,
|
||||
True,
|
||||
Var(
|
||||
_js_expr="(({custom_node, custom_children, custom_props}) => {return custom_node + custom_children + custom_props;})"
|
||||
_js_expr="(({custom_node, custom_children, custom_props}) => {return custom_node + custom_children + custom_props})"
|
||||
),
|
||||
),
|
||||
(
|
||||
CustomMarkdownComponent,
|
||||
"{return custom_node;}",
|
||||
["custom_node"],
|
||||
Var(_js_expr="(({custom_node}) => {return custom_node;})"),
|
||||
"return custom_node",
|
||||
("custom_node",),
|
||||
True,
|
||||
Var(_js_expr="(({custom_node}) => {return custom_node})"),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_create_map_fn_var_subclass(cls, fn_body, fn_args, expected):
|
||||
result = cls.create_map_fn_var(fn_body, fn_args)
|
||||
def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expected):
|
||||
result = cls.create_map_fn_var(fn_body= Var(_js_expr=fn_body, _var_type=int) if fn_body else None, fn_args=fn_args, explicit_return=explicit_return)
|
||||
assert result._js_expr == expected._js_expr
|
||||
|
||||
|
||||
|
@ -940,6 +940,18 @@ def test_function_var():
|
||||
== '(((name) => (("Hello, "+name+"!")))("Steven Universe"))'
|
||||
)
|
||||
|
||||
# Test with destructured arguments
|
||||
destructured_func = ArgsFunctionOperation.create(
|
||||
("a","b"), Var(_js_expr="a + b"), destructure_args=True
|
||||
)
|
||||
assert str(destructured_func.call({"a": 1, "b": 2})) == '(({a, b}) => (a + b))({"a": 1, "b": 2})'
|
||||
|
||||
# Test with explicit return
|
||||
explicit_return_func = ArgsFunctionOperation.create(
|
||||
("a", "b"), Var(_js_expr="return a + b"), explicit_return=True
|
||||
)
|
||||
assert str(explicit_return_func.call(1, 2)) == '((a, b) => {return a + b})(1, 2)'
|
||||
|
||||
|
||||
def test_var_operation():
|
||||
@var_operation
|
||||
|
Loading…
Reference in New Issue
Block a user