Răsfoiți Sursa

fix hold_control action support (#2400)

* fix hold_control action support
resolves #2399

* lint

* lint

* lint

* allow return without action

---------

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide 4 luni în urmă
părinte
comite
9db15e4d15
3 a modificat fișierele cu 76 adăugiri și 16 ștergeri
  1. 17 14
      taipy/gui/gui.py
  2. 1 2
      taipy/gui/utils/callable.py
  3. 58 0
      tests/gui/gui_specific/test_callable.py

+ 17 - 14
taipy/gui/gui.py

@@ -1948,10 +1948,11 @@ class Gui:
         return _getscopeattr(self, Gui.__UI_BLOCK_NAME, False)
 
     def __get_on_cancel_block_ui(self, callback: t.Optional[str]):
-        def _taipy_on_cancel_block_ui(guiApp, id: t.Optional[str], payload: t.Any):
-            if _hasscopeattr(guiApp, Gui.__UI_BLOCK_NAME):
-                _setscopeattr(guiApp, Gui.__UI_BLOCK_NAME, False)
-            guiApp.__on_action(id, {"action": callback})
+        def _taipy_on_cancel_block_ui(a_state: State, id: t.Optional[str], payload: t.Any):
+            gui_app = a_state.get_gui()
+            if _hasscopeattr(gui_app, Gui.__UI_BLOCK_NAME):
+                _setscopeattr(gui_app, Gui.__UI_BLOCK_NAME, False)
+            gui_app.__on_action(id, {"action": callback})
 
         return _taipy_on_cancel_block_ui
 
@@ -2391,15 +2392,13 @@ class Gui:
         callback: t.Optional[t.Union[str, t.Callable]] = None,
         message: t.Optional[str] = "Work in Progress...",
     ):  # pragma: no cover
-        action_name = (
-            callback
-            if isinstance(callback, str)
-            else _get_lambda_id(t.cast(LambdaType, callback))
-            if _is_unnamed_function(callback)
-            else callback.__name__
-            if callback is not None
-            else None
-        )
+        if _is_unnamed_function(callback):
+            action_name = _get_lambda_id(t.cast(LambdaType, callback))
+            self._bind_var_val(action_name, callback)
+        else:
+            action_name = (
+                callback if isinstance(callback, str) else (callback.__name__ if callback is not None else None)
+            )
         func = self.__get_on_cancel_block_ui(action_name)
         def_action_name = func.__name__
         _setscopeattr(self, def_action_name, func)
@@ -2579,7 +2578,11 @@ class Gui:
             with self._set_locals_context(context):
                 self._call_on_page_load(nav_page)
             return self._server._render(
-                page._rendered_jsx, page._script_paths if page._script_paths is not None else [], page._style if page._style is not None else "", page._head, context # noqa: E501
+                page._rendered_jsx,
+                page._script_paths if page._script_paths is not None else [],
+                page._style if page._style is not None else "",
+                page._head,
+                context,  # noqa: E501
             )
         else:
             return ("No page template", 404)

+ 1 - 2
taipy/gui/utils/callable.py

@@ -11,7 +11,6 @@
 
 import typing as t
 from inspect import isclass
-from types import LambdaType
 
 
 def _is_function(s: t.Any) -> bool:
@@ -28,4 +27,4 @@ def _function_name(s: t.Any) -> str:
 
 
 def _is_unnamed_function(s: t.Any):
-    return isinstance(s, LambdaType) or (callable(s) and not hasattr(s, "__name__"))
+    return (hasattr(s, "__name__") and s.__name__ == "<lambda>") or (callable(s) and not hasattr(s, "__name__"))

+ 58 - 0
tests/gui/gui_specific/test_callable.py

@@ -0,0 +1,58 @@
+# Copyright 2021-2025 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.
+
+from taipy.gui.utils.callable import _function_name, _is_function, _is_unnamed_function
+
+
+def my_function():
+    pass
+
+
+class my_class:
+    pass
+
+
+class my_callable_class:
+    def __call__(self):
+        pass
+
+
+def test__is_unnamed_function():
+    assert _is_unnamed_function(my_function) is False
+    assert _is_unnamed_function(lambda x: x) is True
+    assert _is_unnamed_function("a") is False
+
+
+def test__is_function():
+    assert _is_function(my_function) is True
+    assert _is_function(lambda x: x) is True
+    assert _is_function("a") is False
+
+
+def test__function_name():
+    assert _function_name(my_function) == "my_function"
+    assert _function_name(lambda x: x) == "<lambda>"
+    assert _function_name("a") == "a"
+    assert _function_name(1) == "1"
+    assert _function_name(1.0) == "1.0"
+    assert _function_name(True) == "True"
+    assert _function_name(False) == "False"
+    assert _function_name(None) == "None"
+    assert _function_name([]) == "[]"
+    assert _function_name({}) == "{}"
+    assert _function_name(set()) == "set()"
+    assert _function_name(tuple()) == "()"  # noqa C408
+    assert _function_name(object) == "object"
+    assert _function_name(object()).startswith("<object ")
+    assert _function_name(my_class) == "my_class"
+    assert _function_name(my_class()).startswith("<tests.gui.gui_specific.test_callable.my_class ")
+    assert _function_name(my_callable_class) == "my_callable_class"
+    assert _function_name(my_callable_class()) == "<instance of my_callable_class>"