1
0
Эх сурвалжийг харах

support on_action in tgb (with lambda) (#1497)

resolves #1496

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide 10 сар өмнө
parent
commit
fcfd101318

+ 40 - 38
taipy/gui/builder/_element.py

@@ -86,45 +86,47 @@ class _Element(ABC):
             return value
         if isinstance(value, FunctionType):
             if key.startswith("on_"):
-                return value
-            else:
-                try:
-                    st = ast.parse(inspect.getsource(value.__code__).strip())
-                    lambda_by_name: t.Dict[str, ast.Lambda] = {}
-                    _LambdaByName(self._ELEMENT_NAME, lambda_by_name).visit(st)
-                    lambda_fn = lambda_by_name.get(
-                        key,
-                        lambda_by_name.get(_LambdaByName._DEFAULT_NAME, None)
-                        if key == self._DEFAULT_PROPERTY
-                        else None,
+                if value.__name__.startswith("<"):
+                    return value
+                return value.__name__
+
+            try:
+                st = ast.parse(inspect.getsource(value.__code__).strip())
+                lambda_by_name: t.Dict[str, ast.Lambda] = {}
+                _LambdaByName(self._ELEMENT_NAME, lambda_by_name).visit(st)
+                lambda_fn = lambda_by_name.get(
+                    key,
+                    lambda_by_name.get(_LambdaByName._DEFAULT_NAME, None)
+                    if key == self._DEFAULT_PROPERTY
+                    else None,
+                )
+                if lambda_fn is not None:
+                    args = [arg.arg for arg in lambda_fn.args.args]
+                    targets = [
+                        compr.target.id  # type: ignore[attr-defined]
+                        for node in ast.walk(lambda_fn.body)
+                        if isinstance(node, ast.ListComp)
+                        for compr in node.generators
+                    ]
+                    tree = _TransformVarToValue(self.__calling_frame, args + targets + _python_builtins).visit(
+                        lambda_fn
                     )
-                    if lambda_fn is not None:
-                        args = [arg.arg for arg in lambda_fn.args.args]
-                        targets = [
-                            compr.target.id  # type: ignore[attr-defined]
-                            for node in ast.walk(lambda_fn.body)
-                            if isinstance(node, ast.ListComp)
-                            for compr in node.generators
-                        ]
-                        tree = _TransformVarToValue(self.__calling_frame, args + targets + _python_builtins).visit(
-                            lambda_fn
-                        )
-                        ast.fix_missing_locations(tree)
-                        if sys.version_info < (3, 9):  # python 3.8 ast has no unparse
-                            string_fd = io.StringIO()
-                            _Unparser(tree, string_fd)
-                            string_fd.seek(0)
-                            lambda_text = string_fd.read()
-                        else:
-                            lambda_text = ast.unparse(tree)
-                        new_code = compile(f"{_Element._NEW_LAMBDA_NAME} = {lambda_text}", "<ast>", "exec")
-                        namespace: t.Dict[str, FunctionType] = {}
-                        exec(new_code, namespace)
-                        var_name = f"__lambda_{id(namespace[_Element._NEW_LAMBDA_NAME])}"
-                        self.__variables[var_name] = namespace[_Element._NEW_LAMBDA_NAME]
-                        return f'{{{var_name}({", ".join(args)})}}'
-                except Exception as e:
-                    _warn("Error in lambda expression", e)
+                    ast.fix_missing_locations(tree)
+                    if sys.version_info < (3, 9):  # python 3.8 ast has no unparse
+                        string_fd = io.StringIO()
+                        _Unparser(tree, string_fd)
+                        string_fd.seek(0)
+                        lambda_text = string_fd.read()
+                    else:
+                        lambda_text = ast.unparse(tree)
+                    new_code = compile(f"{_Element._NEW_LAMBDA_NAME} = {lambda_text}", "<ast>", "exec")
+                    namespace: t.Dict[str, FunctionType] = {}
+                    exec(new_code, namespace)
+                    var_name = f"__lambda_{id(namespace[_Element._NEW_LAMBDA_NAME])}"
+                    self.__variables[var_name] = namespace[_Element._NEW_LAMBDA_NAME]
+                    return f'{{{var_name}({", ".join(args)})}}'
+            except Exception as e:
+                _warn("Error in lambda expression", e)
         if hasattr(value, "__name__"):
             return str(getattr(value, "__name__"))  # noqa: B009
         return str(value)

+ 30 - 0
tests/gui/builder/test_on_action.py

@@ -0,0 +1,30 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+import taipy.gui.builder as tgb
+from taipy.gui import Gui, notify
+
+
+def test_builder_on_function(gui: Gui, test_client, helpers):
+    def on_slider(state):
+        notify(state, "success", f"Value: {state.value}")
+    gui._bind_var_val("on_slider", on_slider)
+    with tgb.Page(frame=None) as page:
+        tgb.slider(value="{value}", on_change=on_slider)  # type: ignore[attr-defined] # noqa: B023
+    expected_list = ['<Slider','onChange="on_slider"']
+    helpers.test_control_builder(gui, page, expected_list)
+
+
+def test_builder_on_lambda(gui: Gui, test_client, helpers):
+    with tgb.Page(frame=None) as page:
+        tgb.slider(value="{value}", on_change=lambda s: notify(s, "success", f"Lambda Value: {s.value}"))  # type: ignore[attr-defined] # noqa: B023
+    expected_list = ['<Slider','onChange="__lambda_']
+    helpers.test_control_builder(gui, page, expected_list)