Jelajahi Sumber

lambda in multiline context (#1526)

* lambda in multiline context
resolves #1522

* python 3.8 keyword.lineno undefined

* test multiline

---------

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide 10 bulan lalu
induk
melakukan
a1bb3604bc

+ 3 - 2
taipy/gui/builder/_element.py

@@ -98,9 +98,10 @@ class _Element(ABC):
                 return value.__name__
 
             try:
-                st = ast.parse(inspect.getsource(value.__code__).strip())
+                source = inspect.findsource(value)
+                st = ast.parse("".join(source[0]))
                 lambda_by_name: t.Dict[str, ast.Lambda] = {}
-                _LambdaByName(self._ELEMENT_NAME, lambda_by_name).visit(st)
+                _LambdaByName(self._ELEMENT_NAME, source[1], 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,

+ 14 - 4
taipy/gui/builder/_utils.py

@@ -49,17 +49,27 @@ class _TransformVarToValue(ast.NodeTransformer):
 class _LambdaByName(ast.NodeVisitor):
     _DEFAULT_NAME = "<default>"
 
-    def __init__(self, element_name: str, lambdas: t.Dict[str, ast.Lambda]) -> None:
+    def __init__(self, element_name: str, lineno: int, lambdas: t.Dict[str, ast.Lambda]) -> None:
         super().__init__()
         self.element_name = element_name
         self.lambdas = lambdas
+        self.lineno = lineno + 1
 
     def visit_Call(self, node):
-        if node.func.attr == self.element_name:
+        if getattr(node.func, "attr", None) == self.element_name:
             if self.lambdas.get(_LambdaByName._DEFAULT_NAME, None) is None:
                 self.lambdas[_LambdaByName._DEFAULT_NAME] = next(
-                    (arg for arg in node.args if isinstance(arg, ast.Lambda)), None
+                    (
+                        arg
+                        for arg in node.args
+                        if isinstance(arg, ast.Lambda) and self.lineno >= arg.lineno and self.lineno <= arg.end_lineno
+                    ),
+                    None,
                 )
             for kwd in node.keywords:
-                if isinstance(kwd.value, ast.Lambda):
+                if (
+                    isinstance(kwd.value, ast.Lambda)
+                    and self.lineno >= kwd.value.lineno
+                    and self.lineno <= kwd.value.end_lineno
+                ):
                     self.lambdas[kwd.arg] = kwd.value

+ 9 - 0
tests/gui/builder/test_lambda.py

@@ -47,3 +47,12 @@ def test_builder_simple_lambda(gui: Gui, test_client, helpers):
         tgb.text(lambda value: value)  # type: ignore[attr-defined] # noqa: B023
     expected_list = ['defaultValue="10"']
     helpers.test_control_builder(gui, page, expected_list)
+
+def test_builder_multiline_lambda(gui: Gui, test_client, helpers):
+    value = 10
+    gui._bind_var_val("value", value)
+    with tgb.Page(frame=None) as page:
+        tgb.text(  # type: ignore[attr-defined] # noqa: B023
+            lambda value: value)
+    expected_list = ['defaultValue="10"']
+    helpers.test_control_builder(gui, page, expected_list)