Explorar o código

Toggle control code examples (#2482)

- Renamed examples markdown.my/builder.py to main_[md|tgb].py
- Tons of warnings removed
- Change Optional[Union[,,,]] type hints to Union[,,,, None]
- generate_pyi removes Optional[] and augments Union[] when possible
- Allow Page.set_content() to return a string (-> Markdown)
- Improvement in potential error ignore directives with precise definition


* Wrong binding

* Tune error codes.

* Linter

* More errors hidden.
Fabien Lelaquais hai 2 meses
pai
achega
7af14bdcb5
Modificáronse 79 ficheiros con 507 adicións e 489 borrados
  1. 1 1
      doc/gui/examples/controls/chat_discuss.py
  2. 0 0
      doc/gui/examples/controls/progress_styling_circular/main_md.py
  3. 0 0
      doc/gui/examples/controls/progress_styling_circular/main_tgb.py
  4. 0 0
      doc/gui/examples/controls/progress_styling_linear/main_md.py
  5. 0 0
      doc/gui/examples/controls/progress_styling_linear/main_tgb.py
  6. 0 0
      doc/gui/examples/controls/table_edit_action/main_md.py
  7. 0 0
      doc/gui/examples/controls/table_edit_action/main_tgb.py
  8. 0 0
      doc/gui/examples/controls/table_styling_cells/main_md.py
  9. 0 0
      doc/gui/examples/controls/table_styling_cells/main_tgb.py
  10. 0 0
      doc/gui/examples/controls/table_styling_rows/main_md.py
  11. 0 0
      doc/gui/examples/controls/table_styling_rows/main_tgb.py
  12. 0 0
      doc/gui/examples/controls/toggle_images/__init__.py
  13. 6 5
      doc/gui/examples/controls/toggle_images/main_md.py
  14. 6 8
      doc/gui/examples/controls/toggle_images/main_tgb.py
  15. 4 0
      doc/gui/examples/controls/toggle_objects.py
  16. 3 1
      doc/gui/examples/controls/toggle_simple.py
  17. 10 9
      doc/gui/examples/controls/toggle_styling.py
  18. 0 26
      doc/gui/examples/controls/toggle_switch.py
  19. 8 1
      doc/gui/examples/controls/toggle_theme.py
  20. 2 1
      doc/gui/examples/controls/toggle_unselect.py
  21. 3 2
      doc/gui/examples/grocery_store.py
  22. 2 5
      doc/gui/examples/grocery_store/sales.py
  23. 12 12
      taipy/common/config/config.pyi
  24. 5 5
      taipy/core/_entity/_migrate/_migrate_mongo.py
  25. 1 1
      taipy/core/_entity/submittable.py
  26. 2 2
      taipy/core/_orchestrator/_abstract_orchestrator.py
  27. 3 3
      taipy/core/_orchestrator/_orchestrator.py
  28. 1 1
      taipy/core/_version/_version_manager.py
  29. 2 2
      taipy/core/config/data_node_config.py
  30. 2 2
      taipy/core/config/job_config.py
  31. 2 2
      taipy/core/config/scenario_config.py
  32. 10 10
      taipy/core/config/task_config.py
  33. 5 7
      taipy/core/data/_abstract_sql.py
  34. 1 1
      taipy/core/data/excel.py
  35. 2 2
      taipy/core/data/mongo.py
  36. 1 1
      taipy/core/scenario/_scenario_manager.py
  37. 3 3
      taipy/core/scenario/scenario.py
  38. 1 1
      taipy/core/sequence/_sequence_manager.py
  39. 1 1
      taipy/core/sequence/sequence.py
  40. 1 1
      taipy/core/submission/submission.py
  41. 2 2
      taipy/core/taipy.py
  42. 1 1
      taipy/core/task/_task_manager.py
  43. 1 1
      taipy/core/task/task.py
  44. 6 6
      taipy/gui/_page.py
  45. 5 5
      taipy/gui/_renderers/__init__.py
  46. 4 2
      taipy/gui/_renderers/_html/factory.py
  47. 4 4
      taipy/gui/_renderers/_html/parser.py
  48. 2 2
      taipy/gui/_renderers/_markdown/control.py
  49. 47 50
      taipy/gui/_renderers/builder.py
  50. 2 2
      taipy/gui/_renderers/factory.py
  51. 2 2
      taipy/gui/_renderers/json.py
  52. 2 2
      taipy/gui/builder/_factory.py
  53. 3 3
      taipy/gui/config.py
  54. 5 5
      taipy/gui/data/comparison.py
  55. 3 3
      taipy/gui/data/data_accessor.py
  56. 4 4
      taipy/gui/data/decimator/rdp.py
  57. 1 1
      taipy/gui/data/decimator/scatter_decimator.py
  58. 2 2
      taipy/gui/data/pandas_based_data_accessor.py
  59. 10 10
      taipy/gui/data/pandas_data_accessor.py
  60. 6 6
      taipy/gui/extension/library.py
  61. 63 63
      taipy/gui/gui.py
  62. 23 25
      taipy/gui/gui_actions.py
  63. 18 11
      taipy/gui/page.py
  64. 2 2
      taipy/gui/partial.py
  65. 13 13
      taipy/gui/server.py
  66. 16 16
      taipy/gui/state.py
  67. 4 4
      taipy/gui/types.py
  68. 1 1
      taipy/gui/utils/_adapter.py
  69. 11 11
      taipy/gui/utils/_attributes.py
  70. 5 5
      taipy/gui/utils/_bindings.py
  71. 12 12
      taipy/gui/utils/_evaluator.py
  72. 2 2
      taipy/gui/utils/_map_dict.py
  73. 2 2
      taipy/gui/utils/chart_config_builder.py
  74. 1 1
      taipy/gui/utils/varnamefromcontent.py
  75. 34 34
      taipy/gui/viselements.json
  76. 2 2
      taipy/gui_core/_adapters.py
  77. 36 36
      taipy/gui_core/_context.py
  78. 2 2
      tests/gui/gui_specific/test_gui.py
  79. 48 18
      tools/gui/generate_pyi.py

+ 1 - 1
doc/gui/examples/controls/chat_discuss.py

@@ -84,4 +84,4 @@ discuss_page = """
 pages = {"register": register_page, "discuss": discuss_page}
 
 if __name__ == "__main__":
-    gui = Gui(pages=pages).run(title="Chat - Discuss")
+    Gui(pages=pages).run(title="Chat - Discuss")

+ 0 - 0
doc/gui/examples/controls/progress_styling_circular/markdown.py → doc/gui/examples/controls/progress_styling_circular/main_md.py


+ 0 - 0
doc/gui/examples/controls/progress_styling_circular/builder.py → doc/gui/examples/controls/progress_styling_circular/main_tgb.py


+ 0 - 0
doc/gui/examples/controls/progress_styling_linear/markdown.py → doc/gui/examples/controls/progress_styling_linear/main_md.py


+ 0 - 0
doc/gui/examples/controls/progress_styling_linear/builder.py → doc/gui/examples/controls/progress_styling_linear/main_tgb.py


+ 0 - 0
doc/gui/examples/controls/table_edit_action/markdown.py → doc/gui/examples/controls/table_edit_action/main_md.py


+ 0 - 0
doc/gui/examples/controls/table_edit_action/builder.py → doc/gui/examples/controls/table_edit_action/main_tgb.py


+ 0 - 0
doc/gui/examples/controls/table_styling_cells/markdown.py → doc/gui/examples/controls/table_styling_cells/main_md.py


+ 0 - 0
doc/gui/examples/controls/table_styling_cells/builder.py → doc/gui/examples/controls/table_styling_cells/main_tgb.py


+ 0 - 0
doc/gui/examples/controls/table_styling_rows/markdown.py → doc/gui/examples/controls/table_styling_rows/main_md.py


+ 0 - 0
doc/gui/examples/controls/table_styling_rows/builder.py → doc/gui/examples/controls/table_styling_rows/main_tgb.py


+ 0 - 0
doc/gui/examples/controls/toggle_list_tuples/__init__.py → doc/gui/examples/controls/toggle_images/__init__.py


+ 6 - 5
doc/gui/examples/controls/toggle_list_tuples/markdown.py → doc/gui/examples/controls/toggle_images/main_md.py

@@ -16,16 +16,17 @@
 from taipy.gui import Gui, Icon
 
 lov = [
-    ("id1", "Label 1"),
-    ("id2", Icon("https://docs.taipy.io/en/latest/assets/images/favicon.png", "Taipy Logo"), "Label 2"),
-    ("id3", "Label 3"),
+    "Label 1",
+    Icon("https://docs.taipy.io/en/latest/assets/images/favicon.png", "Taipy Logo"),
+    "Label 3",
 ]
 value = lov[0]
 
 page = """<|{value}|toggle|lov={lov}|>
-Value: <|"{value[1].text if isinstance(value[1], Icon) else value[1]}"|>
+
+Value: <|{value.text if isinstance(value, Icon) else value}|>
 """
 
 
 if __name__ == "__main__":
-    Gui(page).run(title="Toggle - List tuples")
+    Gui(page).run(title="Toggle - Images")

+ 6 - 8
doc/gui/examples/controls/toggle_list_tuples/builder.py → doc/gui/examples/controls/toggle_images/main_tgb.py

@@ -17,17 +17,15 @@ from taipy.gui import Gui, Icon
 from taipy.gui import builder as tgb
 
 lov = [
-    ("id1", "Label 1"),
-    ("id2", Icon("https://docs.taipy.io/en/latest/assets/images/favicon.png", "Taipy Logo"), "Label 2"),
-    ("id3", "Label 3"),
+    "Label 1",
+    Icon("https://docs.taipy.io/en/latest/assets/images/favicon.png", "Taipy Logo"),
+    "Label 3",
 ]
 value = lov[0]
 
 with tgb.Page() as page:
-    tgb.toggle("{value}", lov="{lov}")  # type: ignore[attr-defined]
-    tgb.html(None, "Value: ")
-    tgb.text("{value[1].text if isinstance(value[1], Icon) else value[1]}", inline=True)  # type: ignore[attr-defined]
-
+    tgb.toggle("{value}", lov="{lov}")
+    tgb.text("{value.text if isinstance(value, Icon) else value}")
 
 if __name__ == "__main__":
-    Gui(page).run(title="Toggle - List tuples")
+    Gui(page).run(title="Toggle - Images")

+ 4 - 0
doc/gui/examples/controls/toggle_objects.py

@@ -18,12 +18,14 @@ from dataclasses import dataclass
 from taipy.gui import Gui
 
 
+# Define a User class with attributes for ID, name, and birth year
 @dataclass
 class User:
     id: int
     name: str
     birth_year: int
 
+# Create a list of users to be managed
 users = [
     User(231, "Johanna", 1987),
     User(125, "John", 1979),
@@ -31,7 +33,9 @@ users = [
     User(31,  "Mary", 1974)
     ]
 
+# Initialize the selected user to the third user in the list (Peter)
 user_sel = users[2]
+
 page ="""
 <|{user_sel}|toggle|lov={users}|type=User|adapter={lambda u: (u.id, u.name)}|>
 

+ 3 - 1
doc/gui/examples/controls/toggle_simple.py

@@ -15,11 +15,13 @@
 # -----------------------------------------------------------------------------------------
 from taipy.gui import Gui
 
+# Initial selected value
 value = "Item 2"
 
 page = """
 <|{value}|toggle|lov=Item 1;Item 2;Item 3|>
-Value: <|{value}|>
+
+Selected value: <|{value}|>
 """
 
 if __name__ == "__main__":

+ 10 - 9
doc/gui/examples/controls/toggle_styling.py

@@ -17,22 +17,23 @@ from taipy.gui import Gui, Markdown
 
 value = "Item 2"
 
-page = Markdown("<|{value}|toggle|lov=Item 1;Item 2;Item 3|>", style={
+page = Markdown(
+    "<|{value}|toggle|lov=Item 1;Item 2;Item 3;Item 4;Item 5|>",
+    style={
         ".taipy-toggle": {
-            ".MuiList-root": {  # list
-                "height": "70vh",  # limit height
-                "overflow-y": "auto",  # show vertical scroll if necessary
-                ".MuiListItemButton-root:nth-child(even)": {  # change colors
+            ".MuiToggleButtonGroup-root": {  # Select the list
+                ".MuiToggleButton-root:nth-child(even)": {  # Even button colors
                     "background-color": "lightgrey",
-                    "color": "darkgrey",
+                    "color": "black",
                 },
-                ".MuiListItemButton-root:nth-child(odd)": {
+                ".MuiToggleButton-root:nth-child(odd)": {  # Odd button colors
                     "background-color": "darkgrey",
-                    "color": "lightgrey",
+                    "color": "white",
                 },
             },
         }
-    },)
+    },
+)
 
 if __name__ == "__main__":
     Gui(page).run(title="Toggle - Styling")

+ 0 - 26
doc/gui/examples/controls/toggle_switch.py

@@ -1,26 +0,0 @@
-# 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.
-# -----------------------------------------------------------------------------------------
-# To execute this script, make sure that the taipy-gui package is installed in your
-# Python environment and run:
-#     python <script>
-# -----------------------------------------------------------------------------------------
-from taipy.gui import Gui
-
-value = True
-
-page = """
-<|{value}|toggle|label=Is this True?|>
-Value: <|{str(value)}|>
-"""
-
-if __name__ == "__main__":
-    Gui(page).run(title="Toggle - Switch")

+ 8 - 1
doc/gui/examples/controls/toggle_theme.py

@@ -15,7 +15,14 @@
 # -----------------------------------------------------------------------------------------
 from taipy.gui import Gui
 
-page = "<|toggle|theme|>"
+dark_theme = True
+
+
+def on_change(state):
+    print(f"Dark theme set: {state.dark_theme}")  # noqa: T201
+
+
+page = "<|{dark_theme}|toggle|theme|>"
 
 if __name__ == "__main__":
     Gui(page).run(title="Toggle - Theme")

+ 2 - 1
doc/gui/examples/controls/toggle_unselect.py

@@ -19,7 +19,8 @@ value = "Item 2"
 
 page = """
 <|{value}|toggle|lov=Item 1;Item 2;Item 3|unselected_value=No Value|allow_unselect|>
-Value: <|{value}|>
+
+Selected value: <|{value}|>
 """
 
 if __name__ == "__main__":

+ 3 - 2
doc/gui/examples/grocery_store.py

@@ -29,8 +29,9 @@ data = {
     "Sales Q1": [120, 200, 90, 50, 75],
     "Sales Q2": [140, 180, 110, 60, 85],
     "Sales Q3": [100, 190, 95, 55, 80],
-    "Stock":  [500, 600, 400, 300, 250]
+    "Stock": [500, 600, 400, 300, 250],
 }
 
 # Initialize and run the GUI application with Sales and Stock pages
-Gui(pages={ "sales": SalesPage(), "stock": StockPage }).run(title="Grocery Store")
+if __name__ == "__main__":
+    Gui(pages={"sales": SalesPage(), "stock": StockPage}).run(title="Grocery Store")

+ 2 - 5
doc/gui/examples/grocery_store/sales.py

@@ -17,12 +17,9 @@ from taipy.gui import Markdown, Page
 
 # SalesPage inherits from taipy.gui.Page
 class SalesPage(Page):
-    # Available quarters for sales data selection
-    quarters: list[str] = ["Q1", "Q2", "Q3"]
-
     def __init__(self) -> None:
-        self.quarter = "Q1"  # Default selected quarter
-        self.currency = "$"  # Default currency
+        self.quarter = "Q1"  # Initial selected quarter
+        self.currency = "$"  # Initial currency
         super().__init__()
 
     @staticmethod

+ 12 - 12
taipy/common/config/config.pyi

@@ -537,7 +537,7 @@ class Config:
         id: str,
         default_path: Optional[str] = None,
         has_header: Optional[bool] = None,
-        sheet_name: Optional[Union[List[str], str]] = None,
+        sheet_name: Union[List[str], str, None] = None,
         exposed_type: Optional[str] = None,
         scope: Optional[Scope] = None,
         validity_period: Optional[timedelta] = None,
@@ -549,7 +549,7 @@ class Config:
             id (str): The unique identifier of the new Excel data node configuration.
             default_path (Optional[str]): The path of the Excel file.
             has_header (Optional[bool]): If True, indicates that the Excel file has a header.
-            sheet_name (Optional[Union[List[str], str]]): The list of sheet names to be used.
+            sheet_name (Union[List[str], str]): The list of sheet names to be used.
                 This can be a unique name.
             exposed_type (Optional[str]): The exposed type of the data read from Excel file.<br/>
                 The default value is `pandas`.
@@ -904,8 +904,8 @@ class Config:
     def configure_task(
         id: str,
         function: Optional[Callable],
-        input: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
-        output: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
+        input: Union[DataNodeConfig, List[DataNodeConfig], None] = None,
+        output: Union[DataNodeConfig, List[DataNodeConfig], None] = None,
         skippable: bool = False,
         **properties,
     ) -> "TaskConfig":
@@ -914,10 +914,10 @@ class Config:
         Arguments:
             id (str): The unique identifier of this task configuration.
             function (Callable): The python function called by Taipy to run the task.
-            input (Optional[Union[DataNodeConfig^, List[DataNodeConfig^]]]): The list of the
+            input (Union[DataNodeConfig^, List[DataNodeConfig^], None]): The list of the
                 function input data node configurations. This can be a unique data node
                 configuration if there is a single input data node, or None if there are none.
-            output (Optional[Union[DataNodeConfig^, List[DataNodeConfig^]]]): The list of the
+            output (Union[DataNodeConfig^, List[DataNodeConfig^], None]): The list of the
                 function output data node configurations. This can be a unique data node
                 configuration if there is a single output data node, or None if there are none.
             skippable (bool): If True, indicates that the task can be skipped if no change has
@@ -932,8 +932,8 @@ class Config:
     @staticmethod
     def set_default_task_configuration(
         function: Optional[Callable],
-        input: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
-        output: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
+        input: Union[DataNodeConfig, List[DataNodeConfig], None] = None,
+        output: Union[DataNodeConfig, List[DataNodeConfig], None] = None,
         skippable: bool = False,
         **properties,
     ) -> "TaskConfig":
@@ -945,10 +945,10 @@ class Config:
 
         Arguments:
             function (Callable): The python function called by Taipy to run the task.
-            input (Optional[Union[DataNodeConfig^, List[DataNodeConfig^]]]): The list of the
+            input (Union[DataNodeConfig^, List[DataNodeConfig^], None]): The list of the
                 input data node configurations. This can be a unique data node
                 configuration if there is a single input data node, or None if there are none.
-            output (Optional[Union[DataNodeConfig^, List[DataNodeConfig^]]]): The list of the
+            output (Union[DataNodeConfig^, List[DataNodeConfig^], None]): The list of the
                 output data node configurations. This can be a unique data node
                 configuration if there is a single output data node, or None if there are none.
             skippable (bool): If True, indicates that the task can be skipped if no change has
@@ -963,14 +963,14 @@ class Config:
 
     @staticmethod
     def configure_job_executions(
-        mode: Optional[str] = None, max_nb_of_workers: Optional[Union[int, str]] = None, **properties
+        mode: Optional[str] = None, max_nb_of_workers: Union[int, str, None] = None, **properties
     ) -> "JobConfig":
         """Configure job execution.
 
         Arguments:
             mode (Optional[str]): The job execution mode.
                 Possible values are: *"standalone"* or *"development"*.
-            max_nb_of_workers (Optional[int, str]): Parameter used only in *"standalone"* mode.
+            max_nb_of_workers (Union[int, str]): Parameter used only in *"standalone"* mode.
                 This indicates the maximum number of jobs able to run in parallel.<br/>
                 The default value is 2.<br/>
                 A string can be provided to dynamically set the value using an environment

+ 5 - 5
taipy/core/_entity/_migrate/_migrate_mongo.py

@@ -151,11 +151,11 @@ def _migrate_mongo_entities(
     """Migrate entities from mongodb to the current version.
 
     Args:
-        hostname (str, optional): The hostname of the mongodb. Defaults to "localhost".
-        port (int, optional): The port of the mongodb. Defaults to 27017.
-        user (str, optional): The username of the mongodb. Defaults to "".
-        password (str, optional): The password of the mongodb. Defaults to "".
-        backup (bool, optional): Whether to backup the entities before migrating. Defaults to True.
+        hostname (Optional[str]): The hostname of the mongodb. Defaults to "localhost".
+        port (Optional[int]): The port of the mongodb. Defaults to 27017.
+        user (Optional[str]): The username of the mongodb. Defaults to "".
+        password (Optional[str]): The password of the mongodb. Defaults to "".
+        backup (Optional[bool]): Whether to backup the entities before migrating. Defaults to True.
 
     Returns:
         bool: True if the migration was successful, False otherwise.

+ 1 - 1
taipy/core/_entity/submittable.py

@@ -96,7 +96,7 @@ class Submittable:
         callbacks: Optional[List[Callable]] = None,
         force: bool = False,
         wait: bool = False,
-        timeout: Optional[Union[float, int]] = None,
+        timeout: Union[float, int, None] = None,
     ) -> Submission:
         raise NotImplementedError
 

+ 2 - 2
taipy/core/_orchestrator/_abstract_orchestrator.py

@@ -44,7 +44,7 @@ class _AbstractOrchestrator:
         callbacks: Optional[Iterable[Callable]],
         force: bool = False,
         wait: bool = False,
-        timeout: Optional[Union[float, int]] = None,
+        timeout: Union[float, int, None] = None,
         **properties,
     ) -> Submission:
         raise NotImplementedError
@@ -57,7 +57,7 @@ class _AbstractOrchestrator:
         callbacks: Optional[Iterable[Callable]] = None,
         force: bool = False,
         wait: bool = False,
-        timeout: Optional[Union[float, int]] = None,
+        timeout: Union[float, int, None] = None,
         **properties,
     ) -> Submission:
         raise NotImplementedError

+ 3 - 3
taipy/core/_orchestrator/_orchestrator.py

@@ -52,7 +52,7 @@ class _Orchestrator(_AbstractOrchestrator):
         callbacks: Optional[Iterable[Callable]] = None,
         force: bool = False,
         wait: bool = False,
-        timeout: Optional[Union[float, int]] = None,
+        timeout: Union[float, int, None] = None,
         **properties,
     ) -> Submission:
         """Submit the given `Scenario^` or `Sequence^` for an execution.
@@ -109,7 +109,7 @@ class _Orchestrator(_AbstractOrchestrator):
         callbacks: Optional[Iterable[Callable]] = None,
         force: bool = False,
         wait: bool = False,
-        timeout: Optional[Union[float, int]] = None,
+        timeout: Union[float, int, None] = None,
         **properties,
     ) -> Submission:
         """Submit the given `Task^` for an execution.
@@ -203,7 +203,7 @@ class _Orchestrator(_AbstractOrchestrator):
             cls.jobs_to_run.put(job)
 
     @classmethod
-    def _wait_until_job_finished(cls, jobs: Union[List[Job], Job], timeout: Optional[Union[float, int]] = None) -> None:
+    def _wait_until_job_finished(cls, jobs: Union[List[Job], Job], timeout: Union[float, int, None] = None) -> None:
         #  Note: this method should be prefixed by two underscores, but it has only one, so it can be mocked in tests.
         def __check_if_timeout(st, to):
             if to is None:

+ 1 - 1
taipy/core/_version/_version_manager.py

@@ -72,7 +72,7 @@ class _VersionManager(_Manager[_Version]):
         return version
 
     @classmethod
-    def _get_all(cls, version_number: Optional[Union[str, List]] = "all") -> List[_Version]:
+    def _get_all(cls, version_number: Union[str, List, None] = "all") -> List[_Version]:
         """
         Returns all entities.
         """

+ 2 - 2
taipy/core/config/data_node_config.py

@@ -772,7 +772,7 @@ class DataNodeConfig(Section):
         id: str,
         default_path: Optional[str] = None,
         has_header: Optional[bool] = None,
-        sheet_name: Optional[Union[List[str], str]] = None,
+        sheet_name: Union[List[str], str, None] = None,
         exposed_type: Optional[str] = None,
         scope: Optional[Scope] = None,
         validity_period: Optional[timedelta] = None,
@@ -784,7 +784,7 @@ class DataNodeConfig(Section):
             id (str): The unique identifier of the new Excel data node configuration.
             default_path (Optional[str]): The path of the Excel file.
             has_header (Optional[bool]): If True, indicates that the Excel file has a header.
-            sheet_name (Optional[Union[List[str], str]]): The list of sheet names to be used.
+            sheet_name (Union[List[str], str]): The list of sheet names to be used.
                 This can be a unique name.
             exposed_type (Optional[str]): The exposed type of the data read from Excel file.<br/>
                 The default value is `pandas`.

+ 2 - 2
taipy/core/config/job_config.py

@@ -97,14 +97,14 @@ class JobConfig(UniqueSection):
 
     @staticmethod
     def _configure(
-        mode: Optional[str] = None, max_nb_of_workers: Optional[Union[int, str]] = None, **properties
+        mode: Optional[str] = None, max_nb_of_workers: Union[int, str, None] = None, **properties
     ) -> "JobConfig":
         """Configure job execution.
 
         Arguments:
             mode (Optional[str]): The job execution mode.
                 Possible values are: *"standalone"* or *"development"*.
-            max_nb_of_workers (Optional[int, str]): Parameter used only in *"standalone"* mode.
+            max_nb_of_workers (Union[int, str]): Parameter used only in *"standalone"* mode.
                 This indicates the maximum number of jobs able to run in parallel.<br/>
                 The default value is 2.<br/>
                 A string can be provided to dynamically set the value using an environment

+ 2 - 2
taipy/core/config/scenario_config.py

@@ -55,8 +55,8 @@ class ScenarioConfig(Section):
     def __init__(
         self,
         id: str,
-        tasks: Optional[Union[TaskConfig, List[TaskConfig]]] = None,
-        additional_data_nodes: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
+        tasks: Union[TaskConfig, List[TaskConfig], None] = None,
+        additional_data_nodes: Union[DataNodeConfig, List[DataNodeConfig], None] = None,
         frequency: Optional[Frequency] = None,
         comparators: Optional[Dict[str, Union[List[Callable], Callable]]] = None,
         sequences: Optional[Dict[str, List[TaskConfig]]] = None,

+ 10 - 10
taipy/core/config/task_config.py

@@ -57,8 +57,8 @@ class TaskConfig(Section):
         self,
         id: str,
         function: Optional[Callable],
-        inputs: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
-        outputs: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
+        inputs: Union[DataNodeConfig, List[DataNodeConfig], None] = None,
+        outputs: Union[DataNodeConfig, List[DataNodeConfig], None] = None,
         skippable: bool = False,
         **properties,
     ) -> None:
@@ -168,8 +168,8 @@ class TaskConfig(Section):
     def _configure(
         id: str,
         function: Optional[Callable],
-        input: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
-        output: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
+        input: Union[DataNodeConfig, List[DataNodeConfig], None] = None,
+        output: Union[DataNodeConfig, List[DataNodeConfig], None] = None,
         skippable: bool = False,
         **properties,
     ) -> "TaskConfig":
@@ -178,10 +178,10 @@ class TaskConfig(Section):
         Arguments:
             id (str): The unique identifier of this task configuration.
             function (Callable): The python function called by Taipy to run the task.
-            input (Optional[Union[DataNodeConfig^, List[DataNodeConfig^]]]): The list of the
+            input (Union[DataNodeConfig^, List[DataNodeConfig^], None]): The list of the
                 function input data node configurations. This can be a unique data node
                 configuration if there is a single input data node, or None if there are none.
-            output (Optional[Union[DataNodeConfig^, List[DataNodeConfig^]]]): The list of the
+            output (Union[DataNodeConfig^, List[DataNodeConfig^], None]): The list of the
                 function output data node configurations. This can be a unique data node
                 configuration if there is a single output data node, or None if there are none.
             skippable (bool): If True, indicates that the task can be skipped if no change has
@@ -199,8 +199,8 @@ class TaskConfig(Section):
     @staticmethod
     def _set_default_configuration(
         function: Optional[Callable],
-        input: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
-        output: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
+        input: Union[DataNodeConfig, List[DataNodeConfig], None] = None,
+        output: Union[DataNodeConfig, List[DataNodeConfig], None] = None,
         skippable: bool = False,
         **properties,
     ) -> "TaskConfig":
@@ -212,10 +212,10 @@ class TaskConfig(Section):
 
         Arguments:
             function (Callable): The python function called by Taipy to run the task.
-            input (Optional[Union[DataNodeConfig^, List[DataNodeConfig^]]]): The list of the
+            input (Union[DataNodeConfig^, List[DataNodeConfig^], None]): The list of the
                 input data node configurations. This can be a unique data node
                 configuration if there is a single input data node, or None if there are none.
-            output (Optional[Union[DataNodeConfig^, List[DataNodeConfig^]]]): The list of the
+            output (Union[DataNodeConfig^, List[DataNodeConfig^], None]): The list of the
                 output data node configurations. This can be a unique data node
                 configuration if there is a single output data node, or None if there are none.
             skippable (bool): If True, indicates that the task can be skipped if no change has

+ 5 - 7
taipy/core/data/_abstract_sql.py

@@ -139,7 +139,7 @@ class _AbstractSQLDataNode(DataNode, _TabularDataNodeMixin):
             self._engine = None
         return super().__setattr__(key, value)
 
-    def filter(self, operators: Optional[Union[List, Tuple]] = None, join_operator=JoinOperator.AND):
+    def filter(self, operators: Union[List, Tuple, None] = None, join_operator=JoinOperator.AND):
         properties = self.properties
         if properties[self._EXPOSED_TYPE_PROPERTY] == self._EXPOSED_TYPE_PANDAS:
             return self._read_as_pandas_dataframe(operators=operators, join_operator=join_operator)
@@ -212,21 +212,19 @@ class _AbstractSQLDataNode(DataNode, _TabularDataNodeMixin):
             return self._read_as_numpy()
         return self._read_as()
 
-    def _read_as(self, operators: Optional[Union[List, Tuple]] = None, join_operator=JoinOperator.AND):
+    def _read_as(self, operators: Union[List, Tuple, None] = None, join_operator=JoinOperator.AND):
         custom_class = self.properties[self._EXPOSED_TYPE_PROPERTY]
         with self._get_engine().connect() as connection:
             query_result = connection.execute(text(self._get_read_query(operators, join_operator)))
         return [custom_class(**row) for row in query_result]
 
-    def _read_as_numpy(
-        self, operators: Optional[Union[List, Tuple]] = None, join_operator=JoinOperator.AND
-    ) -> np.ndarray:
+    def _read_as_numpy(self, operators: Union[List, Tuple, None] = None, join_operator=JoinOperator.AND) -> np.ndarray:
         return self._read_as_pandas_dataframe(operators=operators, join_operator=join_operator).to_numpy()
 
     def _read_as_pandas_dataframe(
         self,
         columns: Optional[List[str]] = None,
-        operators: Optional[Union[List, Tuple]] = None,
+        operators: Union[List, Tuple, None] = None,
         join_operator=JoinOperator.AND,
     ):
         with self._get_engine().connect() as conn:
@@ -240,7 +238,7 @@ class _AbstractSQLDataNode(DataNode, _TabularDataNodeMixin):
             return pd.DataFrame(result, columns=keys)
 
     @abstractmethod
-    def _get_read_query(self, operators: Optional[Union[List, Tuple]] = None, join_operator=JoinOperator.AND):
+    def _get_read_query(self, operators: Union[List, Tuple, None] = None, join_operator=JoinOperator.AND):
         query = self._get_base_read_query()
 
         if not operators:

+ 1 - 1
taipy/core/data/excel.py

@@ -161,7 +161,7 @@ class ExcelDataNode(DataNode, _FileDataNodeMixin, _TabularDataNodeMixin):
 
     def _read_sheet_with_exposed_type(
         self, path: str, sheet_exposed_type: str, sheet_name: str
-    ) -> Optional[Union[np.ndarray, pd.DataFrame]]:
+    ) -> Union[np.ndarray, pd.DataFrame, None]:
         if sheet_exposed_type in [self._EXPOSED_TYPE_NUMPY, self._EXPOSED_TYPE_NUMPY_NDARRAY]:
             return self._read_as_numpy(path, sheet_name)
         elif sheet_exposed_type in [self._EXPOSED_TYPE_PANDAS, self._EXPOSED_TYPE_PANDAS_DATAFRAME]:

+ 2 - 2
taipy/core/data/mongo.py

@@ -157,7 +157,7 @@ class MongoCollectionDataNode(DataNode):
         """Return the storage type of the data node: "mongo_collection"."""
         return cls.__STORAGE_TYPE
 
-    def filter(self, operators: Optional[Union[List, Tuple]] = None, join_operator=JoinOperator.AND) -> List:
+    def filter(self, operators: Union[List, Tuple, None] = None, join_operator=JoinOperator.AND) -> List:
         cursor = self._read_by_query(operators, join_operator)
         return [self._decoder(row) for row in cursor]
 
@@ -165,7 +165,7 @@ class MongoCollectionDataNode(DataNode):
         cursor = self._read_by_query()
         return [self._decoder(row) for row in cursor]
 
-    def _read_by_query(self, operators: Optional[Union[List, Tuple]] = None, join_operator=JoinOperator.AND):
+    def _read_by_query(self, operators: Union[List, Tuple, None] = None, join_operator=JoinOperator.AND):
         """Query from a Mongo collection, exclude the _id field"""
         if not operators:
             return self.collection.find()

+ 1 - 1
taipy/core/scenario/_scenario_manager.py

@@ -233,7 +233,7 @@ class _ScenarioManager(_Manager[Scenario], _VersionMixin):
         callbacks: Optional[List[Callable]] = None,
         force: bool = False,
         wait: bool = False,
-        timeout: Optional[Union[float, int]] = None,
+        timeout: Union[float, int, None] = None,
         check_inputs_are_ready: bool = True,
         **properties,
     ) -> Submission:

+ 3 - 3
taipy/core/scenario/scenario.py

@@ -112,9 +112,9 @@ class Scenario(_Entity, Submittable, _Labeled):
     def __init__(
         self,
         config_id: str,
-        tasks: Optional[Union[Set[TaskId], Set[Task]]],
+        tasks: Union[Set[TaskId], Set[Task], None],
         properties: Dict[str, Any],
-        additional_data_nodes: Optional[Union[Set[DataNodeId], Set[DataNode]]] = None,
+        additional_data_nodes: Union[Set[DataNodeId], Set[DataNode], None] = None,
         scenario_id: Optional[ScenarioId] = None,
         creation_date: Optional[datetime] = None,
         is_primary: bool = False,
@@ -389,7 +389,7 @@ class Scenario(_Entity, Submittable, _Labeled):
         callbacks: Optional[List[Callable]] = None,
         force: bool = False,
         wait: bool = False,
-        timeout: Optional[Union[float, int]] = None,
+        timeout: Union[float, int, None] = None,
         **properties,
     ) -> Submission:
         """Submit this scenario for execution.

+ 1 - 1
taipy/core/sequence/_sequence_manager.py

@@ -365,7 +365,7 @@ class _SequenceManager(_Manager[Sequence], _VersionMixin):
         callbacks: Optional[List[Callable]] = None,
         force: bool = False,
         wait: bool = False,
-        timeout: Optional[Union[float, int]] = None,
+        timeout: Union[float, int, None] = None,
         check_inputs_are_ready: bool = True,
         **properties,
     ) -> Submission:

+ 1 - 1
taipy/core/sequence/sequence.py

@@ -295,7 +295,7 @@ class Sequence(_Entity, Submittable, _Labeled):
         callbacks: Optional[List[Callable]] = None,
         force: bool = False,
         wait: bool = False,
-        timeout: Optional[Union[float, int]] = None,
+        timeout: Union[float, int, None] = None,
         **properties,
     ) -> Submission:
         """Submit the sequence for execution.

+ 1 - 1
taipy/core/submission/submission.py

@@ -80,7 +80,7 @@ class Submission(_Entity, _Labeled):
         entity_type: str,
         entity_config_id: Optional[str] = None,
         id: Optional[SubmissionId] = None,
-        jobs: Optional[Union[List[Job], List[JobId]]] = None,
+        jobs: Union[List[Job], List[JobId], None] = None,
         properties: Optional[Dict[str, Any]] = None,
         creation_date: Optional[datetime] = None,
         submission_status: Optional[SubmissionStatus] = None,

+ 2 - 2
taipy/core/taipy.py

@@ -221,7 +221,7 @@ def submit(
     entity: Union[Scenario, Sequence, Task],
     force: bool = False,
     wait: bool = False,
-    timeout: Optional[Union[float, int]] = None,
+    timeout: Union[float, int, None] = None,
     **properties,
 ) -> Submission:
     """Submit a scenario, sequence or task entity for execution.
@@ -864,7 +864,7 @@ def get_cycles() -> List[Cycle]:
     return _CycleManagerFactory._build_manager()._get_all()
 
 
-def can_create(config: Optional[Union[ScenarioConfig, DataNodeConfig]] = None) -> ReasonCollection:
+def can_create(config: Union[ScenarioConfig, DataNodeConfig, None] = None) -> ReasonCollection:
     """Indicate if a config section can be used to instantiate a scenario or a data node.
 
     If no config is provided, the function indicates if any scenario or data node config can be created.

+ 1 - 1
taipy/core/task/_task_manager.py

@@ -204,7 +204,7 @@ class _TaskManager(_Manager[Task], _VersionMixin):
         callbacks: Optional[List[Callable]] = None,
         force: bool = False,
         wait: bool = False,
-        timeout: Optional[Union[float, int]] = None,
+        timeout: Union[float, int, None] = None,
         check_inputs_are_ready: bool = True,
         **properties,
     ) -> Submission:

+ 1 - 1
taipy/core/task/task.py

@@ -233,7 +233,7 @@ class Task(_Entity, _Labeled):
         callbacks: Optional[List[Callable]] = None,
         force: bool = False,
         wait: bool = False,
-        timeout: Optional[Union[float, int]] = None,
+        timeout: Union[float, int, None] = None,
         **properties,
     ) -> Submission:
         """Submit the task for execution.

+ 6 - 6
taipy/gui/_page.py

@@ -9,7 +9,7 @@
 # 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.
 
-# _Page for multipage support
+# _Page for multi-page support
 from __future__ import annotations
 
 import logging
@@ -32,10 +32,10 @@ class _Page(object):
     def __init__(self) -> None:
         self._rendered_jsx: t.Optional[str] = None
         self._renderer: t.Optional[Page] = None
-        self._style: t.Optional[t.Union[str, t.Dict[str, t.Any]]] = None
+        self._style: t.Union[str, t.Dict[str, t.Any], None] = None
         self._route: t.Optional[str] = None
         self._head: t.Optional[list] = None
-        self._script_paths: t.Optional[t.Union[str, Path, t.List[t.Union[str, Path]]]] = None
+        self._script_paths: t.Union[str, Path, t.List[t.Union[str, Path]], None] = None
 
     def render(self, gui: Gui, silent: t.Optional[bool] = False):
         if self._renderer is None:
@@ -43,7 +43,7 @@ class _Page(object):
         with warnings.catch_warnings(record=True) as w:
             warnings.resetwarnings()
             module_name = self._renderer._get_module_name()
-            with gui._set_locals_context(module_name):
+            with gui._set_locals_context(module_name):  # type: ignore[attr-defined]
                 self._rendered_jsx = self._renderer.render(gui)
             if silent:
                 s = ""
@@ -69,7 +69,7 @@ class _Page(object):
                     s = "\033[1;31m\n"
                     s += (
                         message
-                        := f"--- {len(w)} warning(s) were found for page '{'/' if self._route == gui._get_root_page_name() else self._route}' {self._renderer._get_content_detail(gui)} ---\n"  # noqa: E501
+                        := f"--- {len(w)} warning(s) were found for page '{'/' if self._route == gui._get_root_page_name() else self._route}' {self._renderer._get_content_detail(gui)} ---\n"  # type: ignore[reportAttributeAccessIssue] # noqa: E501
                     )
                     for i, wm in enumerate(w):
                         s += f" - Warning {i + 1}: {wm.message}\n"
@@ -77,6 +77,6 @@ class _Page(object):
                     s += "\033[0m\n"
                     logging.warning(s)
         if hasattr(self._renderer, "head"):
-            self._head = list(self._renderer.head)  # type: ignore
+            self._head = list(self._renderer.head)  # type: ignore[attr-defined]
         # return renderer module_name from frame
         return module_name

+ 5 - 5
taipy/gui/_renderers/__init__.py

@@ -41,7 +41,7 @@ class _Renderer(Page, ABC):
         from ..builder._element import _Element  # noqa: F811
 
         super().__init__(**kwargs)
-        content: t.Optional[t.Union[str, _Element]] = kwargs.get("content", None)
+        content: t.Union[str, _Element, None] = kwargs.get("content", None)
         if content is None:
             raise ValueError("'content' argument is missing for class '_Renderer'")
         self._content = ""
@@ -107,10 +107,10 @@ class _Renderer(Page, ABC):
             raise RuntimeError("'set_content()' must be used in an IPython notebook context")
         self.__process_content(content)
         if self._notebook_gui is not None and self._notebook_page is not None:
-            if self._notebook_gui._config.root_page is self._notebook_page:
-                self._notebook_gui._navigate("/", {"tp_reload_all": "true"})
+            if self._notebook_gui._config.root_page is self._notebook_page:  # type: ignore[attr-defined]
+                self._notebook_gui._navigate("/", {"tp_reload_all": "true"})  # type: ignore[attr-defined]
                 return
-            self._notebook_gui._navigate(self._notebook_page._route, {"tp_reload_same_route_only": "true"})
+            self._notebook_gui._navigate(self._notebook_page._route, {"tp_reload_same_route_only": "true"})  # type: ignore[attr-defined]
 
     def _get_content_detail(self, gui: "Gui") -> str:
         if self._filepath:
@@ -161,7 +161,7 @@ class Markdown(_Renderer):
 
     # Generate JSX from Markdown
     def render(self, gui: "Gui") -> str:
-        return gui._markdown.convert(self._content)
+        return gui._markdown.convert(self._content)  # type: ignore[attr-defined]
 
 
 class Html(_Renderer):

+ 4 - 2
taipy/gui/_renderers/_html/factory.py

@@ -16,8 +16,10 @@ from ..factory import _Factory
 
 class _HtmlFactory(_Factory):
     @staticmethod
-    def create_element(gui, namespace: str, control_type: str, all_properties: t.Dict[str, str]) -> t.Tuple[str, str]:
+    def create_element(
+        gui, namespace: str, control_type: str, all_properties: t.Dict[str, str]
+    ) -> t.Union[t.Tuple[str, str], str]:
         builder_html = _Factory.call_builder(gui, f"{namespace}.{control_type}", all_properties, True)
         if builder_html is None:
             return f"<div>INVALID SYNTAX - Control is '{namespace}:{control_type}'</div>", "div"
-        return builder_html  # type: ignore
+        return builder_html

+ 4 - 4
taipy/gui/_renderers/_html/parser.py

@@ -33,7 +33,7 @@ class _TaipyHTMLParser(HTMLParser):
         self._tag_stack = []
 
     # @override
-    def handle_starttag(self, tag, props) -> None:
+    def handle_starttag(self, tag, props) -> None: # type: ignore[misc]
         self._tag_stack.append((tag, self._line_count))
         if tag == "html":
             return
@@ -91,9 +91,9 @@ class _TaipyHTMLParser(HTMLParser):
             self.body += data
 
     def parse_taipy_tag(self) -> None:
-        tp_string, tp_element_name = self.taipy_tag.parse(self._gui)
+        tp_string, tp_element_name = self.taipy_tag.parse(self._gui)  # type: ignore[misc]
         self.append_data(tp_string)
-        self.tag_mapping[f"{self.taipy_tag.namespace}:{self.taipy_tag.control_type}"] = tp_element_name
+        self.tag_mapping[f"{self.taipy_tag.namespace}:{self.taipy_tag.control_type}"] = tp_element_name  # type: ignore[misc]
         self.taipy_tag = None
 
     def get_jsx(self) -> str:
@@ -132,4 +132,4 @@ class _TaipyTag(object):
         # allow usage of 'class' property in html taipy tag
         if "class" in self.properties and "class_name" not in self.properties:
             self.properties["class_name"] = self.properties["class"]
-        return _HtmlFactory.create_element(gui, self.namespace, self.control_type, self.properties)
+        return _HtmlFactory.create_element(gui, self.namespace, self.control_type, self.properties)  # type: ignore[return-value]

+ 2 - 2
taipy/gui/_renderers/_markdown/control.py

@@ -21,7 +21,7 @@ class _ControlPattern(InlineProcessor):
     def extend(md, gui, priority):
         instance = _ControlPattern(_ControlPattern.__PATTERN, md)
         md.inlinePatterns.register(instance, "taipy", priority)
-        instance._gui = gui  # type: ignore[reportAttributeAccessIssue]
+        instance._gui = gui  # type: ignore[attr-defined]
 
     def handleMatch(self, m, data):
-        return _MarkdownFactory.create_element(self._gui, m.group(1), m.group(2)), m.start(0), m.end(0)  # type: ignore[reportAttributeAccessIssue]
+        return _MarkdownFactory.create_element(self._gui, m.group(1), m.group(2)), m.start(0), m.end(0)  # type: ignore[attr-defined]

+ 47 - 50
taipy/gui/_renderers/builder.py

@@ -117,12 +117,12 @@ class _Builder:
             (prop_dict, prop_hash) = _Builder.__parse_attribute_value(gui, self.__prop_values["properties"])
             if prop_hash is None:
                 prop_hash = prop_dict
-                prop_hash = self.__gui._bind_var(prop_hash)
-                if hasattr(self.__gui._bindings(), prop_hash):
+                prop_hash = self.__gui._bind_var(prop_hash)  # type: ignore[attr-defined]
+                if hasattr(self.__gui._bindings(), prop_hash):  # type: ignore[attr-defined]
                     prop_dict = _getscopeattr(self.__gui, prop_hash)
             if isinstance(prop_dict, (dict, _MapDict)):
                 # Iterate through prop_dict and append to self.attributes
-                var_name, _ = gui._get_real_var_name(prop_hash)
+                var_name, _ = gui._get_real_var_name(prop_hash)  # type: ignore[attr-defined]
                 for k, v in prop_dict.items():
                     (val, key_hash) = _Builder.__parse_attribute_value(gui, v)
                     self.__prop_values[k] = (
@@ -141,10 +141,10 @@ class _Builder:
 
     @staticmethod
     def __parse_attribute_value(gui: "Gui", value) -> t.Tuple:
-        if isinstance(value, str) and gui._is_expression(value):
-            hash_value = gui._evaluate_expr(value)
+        if isinstance(value, str) and gui._is_expression(value):  # type: ignore[attr-defined]
+            hash_value = gui._evaluate_expr(value)  # type: ignore[attr-defined]
             try:
-                func = gui._get_user_function(hash_value)
+                func = gui._get_user_function(hash_value)  # type: ignore[attr-defined]
                 if _is_function(func):
                     return (func, hash_value)
                 return (_getscopeattr_drill(gui, hash_value), hash_value)
@@ -330,9 +330,7 @@ class _Builder:
     ):
         value = self.__prop_values.get(name, default_value)
         if value is not None:
-            self.set_attribute(
-                _to_camel_case(f"default_{name}" if dynamic_property_name is None else name), str(value)
-            )
+            self.set_attribute(_to_camel_case(f"default_{name}" if dynamic_property_name is None else name), str(value))
         if hash := self.__hashes.get(name):
             prop_name = _to_camel_case(name if dynamic_property_name is None else dynamic_property_name)
             if with_update:
@@ -460,7 +458,7 @@ class _Builder:
             return self.__set_react_attribute(_to_camel_case(name), False)
         elif value:
             value = str(value)
-            func = self.__gui._get_user_function(value)
+            func = self.__gui._get_user_function(value)  # type: ignore[attr-defined]
             if func == value:
                 _warn(f"{self.__control_type}.{name}: {value} is not a function.")
         return self.set_attribute(_to_camel_case(name), value) if value else self
@@ -477,7 +475,7 @@ class _Builder:
     ):
         property_name = var_name if property_name is None else property_name
         lov_name = self.__hashes.get(var_name)
-        real_var_name = self.__gui._get_real_var_name(lov_name)[0] if lov_name else None
+        real_var_name = self.__gui._get_real_var_name(lov_name)[0] if lov_name else None  # type: ignore[attr-defined]
         lov = self.__prop_values.get(var_name)
         adapter: t.Any = None
         var_type: t.Optional[str] = None
@@ -498,7 +496,7 @@ class _Builder:
 
         adapter = self.__prop_values.get("adapter", adapter)
         if adapter and isinstance(adapter, str):
-            adapter = self.__gui._get_user_function(adapter)
+            adapter = self.__gui._get_user_function(adapter)  # type: ignore[attr-defined]
         if adapter and not _is_function(adapter):
             _warn(f"{self.__element_name}: adapter property value is invalid.")
             adapter = None
@@ -518,31 +516,31 @@ class _Builder:
                             elt = value[0]
                     else:
                         elt = value
-                var_type = self.__gui._get_unique_type_adapter(type(elt).__name__)
+                var_type = self.__gui._get_unique_type_adapter(type(elt).__name__)  # type: ignore[attr-defined]
             if adapter is None:
-                adapter = self.__gui._get_adapter_for_type(var_type)
+                adapter = self.__gui._get_adapter_for_type(var_type)  # type: ignore[attr-defined]
             elif var_type == str.__name__ and _is_function(adapter):
                 var_type += (
-                    _get_lambda_id(t.cast(LambdaType, adapter))
+                    _get_lambda_id(t.cast(LambdaType, adapter))  # type: ignore[attr-defined]
                     if _is_unnamed_function(adapter)
                     else _get_expr_var_name(adapter.__name__)
                 )
             if lov_name:
                 if adapter is None:
-                    adapter = self.__gui._get_adapter_for_type(lov_name)
+                    adapter = self.__gui._get_adapter_for_type(lov_name)  # type: ignore[attr-defined]
                 else:
-                    self.__gui._add_type_for_var(lov_name, t.cast(str, var_type))
+                    self.__gui._add_type_for_var(lov_name, t.cast(str, var_type))  # type: ignore[attr-defined]
             if value_name := self.__hashes.get("value"):
                 if adapter is None:
-                    adapter = self.__gui._get_adapter_for_type(value_name)
+                    adapter = self.__gui._get_adapter_for_type(value_name)  # type: ignore[attr-defined]
                 else:
-                    self.__gui._add_type_for_var(value_name, t.cast(str, var_type))
+                    self.__gui._add_type_for_var(value_name, t.cast(str, var_type))  # type: ignore[attr-defined]
             if adapter is not None:
                 self.__gui._add_adapter_for_type(var_type, adapter)  # type: ignore[arg-type]
 
             if default_lov is not None and lov:
                 for elt in lov:
-                    ret = self.__gui._run_adapter(
+                    ret = self.__gui._run_adapter(  # type: ignore[attr-defined]
                         t.cast(t.Callable, adapter),
                         elt,
                         adapter.__name__ if hasattr(adapter, "__name__") else "adapter",
@@ -554,7 +552,7 @@ class _Builder:
             value = self.__prop_values.get("value")
             val_list = value if isinstance(value, list) else [value]
             for val in val_list:
-                ret = self.__gui._run_adapter(
+                ret = self.__gui._run_adapter(  # type: ignore[attr-defined]
                     t.cast(t.Callable, adapter),
                     val,
                     adapter.__name__ if hasattr(adapter, "__name__") else "adapter",
@@ -577,8 +575,8 @@ class _Builder:
         # LoV expression binding
         if lov_name and real_var_name:
             typed_lov_hash = (
-                self.__gui._evaluate_expr(
-                    f"{{{self.__gui._get_call_method_name('_get_adapted_lov')}({real_var_name},'{var_type}')}}"
+                self.__gui._evaluate_expr(  # type: ignore[attr-defined]
+                    f"{{{self.__gui._get_call_method_name('_get_adapted_lov')}({real_var_name},'{var_type}')}}"  # type: ignore[attr-defined]
                 )
                 if var_type
                 else lov_name
@@ -614,16 +612,16 @@ class _Builder:
         rebuild_hash = self.__hashes.get("rebuild")
         if rebuild_hash or _is_true(rebuild):
             attributes, hashes = self.__filter_attributes_hashes(self.__filter_attribute_names(attribute_names))
-            rebuild_name = f"bool({self.__gui._get_real_var_name(rebuild_hash)[0]})" if rebuild_hash else "None"
+            rebuild_name = f"bool({self.__gui._get_real_var_name(rebuild_hash)[0]})" if rebuild_hash else "None"  # type: ignore[attr-defined]
             try:
-                self.__gui._set_building(True)
-                return self.__gui._evaluate_expr(
+                self.__gui._set_building(True)  # type: ignore[attr-defined]
+                return self.__gui._evaluate_expr(  # type: ignore[attr-defined]
                     "{"
-                    + f'{fn_name}({rebuild}, {rebuild_name}, "{quote(json.dumps(attributes))}", "{quote(json.dumps(hashes))}", {", ".join([f"{k}={v2}" for k, v2 in {v: self.__gui._get_real_var_name(t.cast(str, v))[0] for v in hashes.values()}.items()])})'  # noqa: E501
+                    + f'{fn_name}({rebuild}, {rebuild_name}, "{quote(json.dumps(attributes))}", "{quote(json.dumps(hashes))}", {", ".join([f"{k}={v2}" for k, v2 in {v: self.__gui._get_real_var_name(t.cast(str, v))[0] for v in hashes.values()}.items()])})'  # type: ignore[attr-defined] # noqa: E501
                     + "}"
                 )
             finally:
-                self.__gui._set_building(False)
+                self.__gui._set_building(False)  # type: ignore[attr-defined]
         return None
 
     def _get_dataframe_attributes(self) -> "_Builder":
@@ -637,17 +635,17 @@ class _Builder:
             cmp_datas_hash = []
             while cmp_data := self.__hashes.get(f"data[{cmp_idx}]"):
                 cmp_idx += 1
-                cmp_datas.append(self.__gui._get_real_var_name(cmp_data)[0])
+                cmp_datas.append(self.__gui._get_real_var_name(cmp_data)[0])  # type: ignore[attr-defined]
                 cmp_datas_hash.append(cmp_data)
             if cmp_datas:
-                cmp_hash = self.__gui._evaluate_expr(
+                cmp_hash = self.__gui._evaluate_expr(  # type: ignore[attr-defined]
                     "{"
-                    + f"{self.__gui._get_call_method_name('_compare_data')}"
-                    + f"({self.__gui._get_real_var_name(data_hash)[0]},{','.join(cmp_datas)})"
+                    + f"{self.__gui._get_call_method_name('_compare_data')}"  # type: ignore[attr-defined]
+                    + f"({self.__gui._get_real_var_name(data_hash)[0]},{','.join(cmp_datas)})"  # type: ignore[attr-defined]
                     + "}"
                 )
                 self.__update_vars.append(f"comparedatas={','.join(cmp_datas_hash)}")
-        cols_description = self.__gui._get_accessor().get_cols_description(data_hash, _TaipyData(data, data_hash))
+        cols_description = self.__gui._get_accessor().get_cols_description(data_hash, _TaipyData(data, data_hash))  # type: ignore[attr-defined]
         col_dict = _get_columns_dict(
             self.__prop_values.get("columns", {}),
             cols_description,
@@ -656,7 +654,8 @@ class _Builder:
         )
 
         rebuild_fn_hash = self.__build_rebuild_fn(
-            self.__gui._get_call_method_name("_tbl_cols"), _Builder.__TABLE_COLUMNS_DEPS
+            self.__gui._get_call_method_name("_tbl_cols"),  # type: ignore[attr-defined]
+            _Builder.__TABLE_COLUMNS_DEPS,  # type: ignore[attr-defined]
         )
         if rebuild_fn_hash:
             self.__set_react_attribute("columns", rebuild_fn_hash)
@@ -703,7 +702,7 @@ class _Builder:
         self.__prop_values["_default_type"] = default_type
         self.__prop_values["_default_mode"] = default_mode
         rebuild_fn_hash = self.__build_rebuild_fn(
-            self.__gui._get_call_method_name("_chart_conf"),
+            self.__gui._get_call_method_name("_chart_conf"),  # type: ignore[attr-defined]
             _CHART_NAMES + ("_default_type", "_default_mode", "data"),
         )
         if rebuild_fn_hash:
@@ -712,7 +711,7 @@ class _Builder:
         # read column definitions
         data = self.__prop_values.get("data")
         data_hash = self.__hashes.get("data", "")
-        cols_description = [self.__gui._get_accessor().get_cols_description(data_hash, _TaipyData(data, data_hash))]
+        cols_description = [self.__gui._get_accessor().get_cols_description(data_hash, _TaipyData(data, data_hash))]  # type: ignore[attr-defined]
 
         if data_hash:
             data_updates: t.List[str] = []
@@ -726,7 +725,7 @@ class _Builder:
                 data_idx += 1
                 name_idx = f"data[{data_idx}]"
                 cols_description.append(
-                    self.__gui._get_accessor().get_cols_description(add_data_hash, _TaipyData(add_data, add_data_hash))
+                    self.__gui._get_accessor().get_cols_description(add_data_hash, _TaipyData(add_data, add_data_hash))  # type: ignore[attr-defined]
                 )
             self.set_attribute("dataVarNames", ";".join(data_updates))
 
@@ -834,7 +833,7 @@ class _Builder:
         hash_name = self.__hashes.get(var_name)
         if content is None and hash_name is None:
             return self
-        value = self.__gui._get_content(hash_name or var_name, content, image)
+        value = self.__gui._get_content(hash_name or var_name, content, image)  # type: ignore[attr-defined]
         if hash_name:
             hash_name = self.__get_typed_hash_name(hash_name, PropertyType.image if image else PropertyType.content)
         if hash_name:
@@ -849,7 +848,7 @@ class _Builder:
         var_name: str,
         value: t.Optional[t.Any] = None,
         native_type: bool = False,
-        var_type: t.Optional[t.Union[PropertyType, t.Type[_TaipyBase]]] = None,
+        var_type: t.Union[PropertyType, t.Type[_TaipyBase], None] = None,
     ):
         if value is None:
             value = self.__prop_values.get(var_name)
@@ -879,7 +878,7 @@ class _Builder:
         with_update=True,
         with_default=True,
         native_type=False,
-        var_type: t.Optional[t.Union[PropertyType, t.Type[_TaipyBase]]] = None,
+        var_type: t.Union[PropertyType, t.Type[_TaipyBase], None] = None,
         default_val: t.Any = None,
     ):
         """
@@ -967,7 +966,7 @@ class _Builder:
         return self
 
     def _set_propagate(self):
-        val = self.__get_boolean_attribute("propagate", t.cast(bool, self.__gui._config.config.get("propagate")))
+        val = self.__get_boolean_attribute("propagate", t.cast(bool, self.__gui._config.config.get("propagate")))  # type: ignore[attr-defined]
         return self if val else self.__set_boolean_attribute("propagate", False)
 
     def __set_refresh_on_update(self):
@@ -1000,12 +999,10 @@ class _Builder:
             self.set_attribute("mode", "theme")
         return self
 
-    def __get_typed_hash_name(
-        self, hash_name: str, var_type: t.Optional[t.Union[PropertyType, t.Type[_TaipyBase]]]
-    ) -> str:
+    def __get_typed_hash_name(self, hash_name: str, var_type: t.Union[PropertyType, t.Type[_TaipyBase], None]) -> str:
         if taipy_type := _get_taipy_type(var_type):
-            expr = self.__gui._get_expr_from_hash(hash_name)
-            hash_name = self.__gui._evaluate_bind_holder(t.cast(t.Type[_TaipyBase], taipy_type), expr)
+            expr = self.__gui._get_expr_from_hash(hash_name)  # type: ignore[attr-defined]
+            hash_name = self.__gui._evaluate_bind_holder(t.cast(t.Type[_TaipyBase], taipy_type), expr)  # type: ignore[attr-defined]
         return hash_name
 
     def __set_dynamic_property_without_default(
@@ -1028,11 +1025,11 @@ class _Builder:
         front_var = self.__get_typed_hash_name(hash_name, property_type)
         self.set_attribute(
             _to_camel_case(f"default_{property_name}"),
-            self.__gui._get_user_content_url(
+            self.__gui._get_user_content_url(  # type: ignore[attr-defined]
                 None,
                 {
                     "variable_name": front_var,
-                    self.__gui._HTML_CONTENT_KEY: str(_time.time()),
+                    self.__gui._HTML_CONTENT_KEY: str(_time.time()),  # type: ignore[attr-defined]
                 },
             ),
         )
@@ -1163,8 +1160,8 @@ class _Builder:
             elif isclass(var_type) and issubclass(var_type, _TaipyBase):
                 prop_name = _to_camel_case(attr[0])
                 if hash_name := self.__hashes.get(attr[0]):
-                    expr = self.__gui._get_expr_from_hash(hash_name)
-                    hash_name = self.__gui._evaluate_bind_holder(var_type, expr)
+                    expr = self.__gui._get_expr_from_hash(hash_name)  # type: ignore[attr-defined]
+                    hash_name = self.__gui._evaluate_bind_holder(var_type, expr)  # type: ignore[attr-defined]
                     self.__update_vars.append(f"{prop_name}={hash_name}")
                     self.__set_react_attribute(prop_name, hash_name)
                 else:

+ 2 - 2
taipy/gui/_renderers/factory.py

@@ -732,12 +732,12 @@ class _Factory:
     @staticmethod
     def call_builder(
         gui: "Gui", name: str, all_properties: t.Optional[t.Dict[str, t.Any]] = None, is_html: t.Optional[bool] = False
-    ) -> t.Optional[t.Union[t.Any, t.Tuple[str, str]]]:
+    ) -> t.Union[t.Any, t.Tuple[str, str], None]:
         name = name[len(_Factory.__TAIPY_NAME_SPACE) :] if name.startswith(_Factory.__TAIPY_NAME_SPACE) else name
         builder = _Factory.__CONTROL_BUILDERS.get(name)
         built = None
         _Factory.__COUNTER += 1
-        with gui._get_authorization():
+        with gui._get_authorization():  # type: ignore[attr-defined]
             if builder is None:
                 lib, element_name, element = _Factory.__get_library_element(name)
                 if lib:

+ 2 - 2
taipy/gui/_renderers/json.py

@@ -18,7 +18,7 @@ from pathlib import Path
 
 import numpy
 import pandas
-from flask.json.provider import DefaultJSONProvider
+from flask.json.provider import DefaultJSONProvider  # type: ignore[reportMissingImports]
 
 from .._warnings import _warn
 from ..icon import Icon
@@ -81,5 +81,5 @@ class _TaipyJsonEncoder(JSONEncoder):
 
 
 class _TaipyJsonProvider(DefaultJSONProvider):
-    default = staticmethod(_TaipyJsonAdapter().parse)  # type: ignore
+    default = staticmethod(_TaipyJsonAdapter().parse)
     sort_keys = False

+ 2 - 2
taipy/gui/builder/_factory.py

@@ -16,8 +16,8 @@ from .._renderers.factory import _Factory
 
 class _BuilderFactory(_Factory):
     @staticmethod
-    def create_element(gui, element_type: str, properties: t.Dict[str, t.Any]) -> t.Tuple[str, str]:
+    def create_element(gui, element_type: str, properties: t.Dict[str, t.Any]) -> t.Union[t.Tuple[str, str], str]:
         builder_html = _Factory.call_builder(gui, element_type, properties, True)
         if builder_html is None:
             return f"<div>INVALID SYNTAX - Element is '{element_type}'", "div"
-        return builder_html  # type: ignore
+        return builder_html

+ 3 - 3
taipy/gui/config.py

@@ -93,9 +93,9 @@ Stylekit = t.TypedDict(
 ServerConfig = t.TypedDict(
     "ServerConfig",
     {
-        "cors": t.Optional[t.Union[bool, t.Dict[str, t.Any]]],
+        "cors": t.Union[bool, t.Dict[str, t.Any], None],
         "socketio": t.Optional[t.Dict[str, t.Any]],
-        "ssl_context": t.Optional[t.Union[str, t.Tuple[str, str]]],
+        "ssl_context": t.Union[str, t.Tuple[str, str], None],
         "flask": t.Optional[t.Dict[str, t.Any]],
     },
     total=False,
@@ -270,7 +270,7 @@ class _Config(object):
                         if key == "port" and str(value).strip() == "auto":
                             config["port"] = "auto"
                         else:
-                            config[key] = value if config[key] is None else type(config[key])(value)  # type: ignore
+                            config[key] = value if config[key] is None else type(config[key])(value)  # type: ignore[reportCallIssue]
                     except Exception as e:
                         _warn(
                             f"Invalid env value in Gui.run(): {key} - {value}. Unable to parse value to the correct type",  # noqa: E501

+ 5 - 5
taipy/gui/data/comparison.py

@@ -25,16 +25,16 @@ def _compare_function(
         names = datanames.split(",")
         if not names:
             return None
-        compare_fn = gui._get_user_function(compare_name) if compare_name else None
+        compare_fn = gui._get_user_function(compare_name) if compare_name else None  # type: ignore[attr-defined]
         if callable(compare_fn):
-            return gui._get_accessor().to_pandas(
-                gui._call_function_with_state(compare_fn, [name, [gui._get_real_var_name(n) for n in names]])
+            return gui._get_accessor().to_pandas(  # type: ignore[attr-defined]
+                gui._call_function_with_state(compare_fn, [name, [gui._get_real_var_name(n) for n in names]])  # type: ignore[attr-defined]
             )
         elif compare_fn is not None:
             _warn(f"{compare_name}(): compare function name is not valid.")
-        dfs = [gui._get_accessor().to_pandas(_getscopeattr(gui, n)) for n in names]
+        dfs = [gui._get_accessor().to_pandas(_getscopeattr(gui, n)) for n in names]  # type: ignore[attr-defined]
         return value.compare(dfs[0], keep_shape=True)
     except Exception as e:
-        if not gui._call_on_exception(compare_name or "Gui._compare_function", e):
+        if not gui._call_on_exception(compare_name or "Gui._compare_function", e):  # type: ignore[attr-defined]
             _warn(f"{compare_name or 'Gui._compare_function'}(): compare function raised an exception", e)
         return None

+ 3 - 3
taipy/gui/data/data_accessor.py

@@ -132,19 +132,19 @@ class _DataAccessors(object):
                 raise TypeError(f"Class {cls.__name__} cannot be instantiated") from e
             if inst:
                 for cl in classes:
-                    self.__access_4_type[cl] = inst  # type: ignore
+                    self.__access_4_type[cl] = inst
 
     def _unregister(self, cls: t.Type[_DataAccessor]) -> None:
         """Unregister a DataAccessor type."""
         if cls in self.__access_4_type:
             del self.__access_4_type[cls]
 
-    def _get_instance(self, value: _TaipyData) -> _DataAccessor:  # type: ignore
+    def _get_instance(self, value: _TaipyData) -> _DataAccessor:
         value = value.get() if isinstance(value, _TaipyData) else value
         access = self.__access_4_type.get(type(value))
         if access is None:
             if value is not None:
-                converted_value = type(self.__gui)._convert_unsupported_data(value)
+                converted_value = type(self.__gui)._convert_unsupported_data(value)  # type: ignore[attr-defined]
                 if converted_value is not None:
                     access = self.__access_4_type.get(type(converted_value))
                     if access is not None:

+ 4 - 4
taipy/gui/data/decimator/rdp.py

@@ -80,7 +80,7 @@ class RDP(Decimator):
         mask.fill(True)
 
         # The stack to select start and end index
-        stack: t.List[t.Tuple[int, int]] = [(0, data.shape[0] - 1)]  # type: ignore
+        stack: t.List[t.Tuple[int, int]] = [(0, data.shape[0] - 1)]
 
         while stack:
             # Pop the last item
@@ -103,8 +103,8 @@ class RDP(Decimator):
                 # Also include the start index to get absolute index
                 # And not relative
                 mid = np.argmax(dsq) + 1 + start
-                stack.append((start, mid))  # type: ignore
-                stack.append((mid, end))  # type: ignore
+                stack.append((start, mid))  # type: ignore[arg-type]
+                stack.append((mid, end))  # type: ignore[arg-type]
             else:
                 # Points in between are redundant
                 mask[start + 1 : end] = False
@@ -133,7 +133,7 @@ class RDP(Decimator):
             max_dist_index = np.argmax(dsq) + start + 1
             weights[max_dist_index] = np.amax(dsq)
             stack.append((start, max_dist_index))
-            stack.append((max_dist_index, end))
+            stack.append((max_dist_index, end))  # type: ignore[arg-type]
         maxTolerance = np.sort(weights)[M_len - n_out]
 
         return weights >= maxTolerance

+ 1 - 1
taipy/gui/data/decimator/scatter_decimator.py

@@ -81,7 +81,7 @@ class ScatterDecimator(Decimator):
         grid_shape = (grid_x + 1, grid_y + 1)
         if len(data[0]) == 3:
             grid_z = grid_x
-            grid_shape = (grid_x + 1, grid_y + 1, grid_z + 1)  # type: ignore
+            grid_shape = (grid_x + 1, grid_y + 1, grid_z + 1)  # type: ignore[assignment]
             z_col = data[:, 2]
             min_z: float = np.amin(z_col)
             max_z: float = np.amax(z_col)

+ 2 - 2
taipy/gui/data/pandas_based_data_accessor.py

@@ -26,14 +26,14 @@ class _PandasBasedDataAccessor(_DataAccessor):
 
     def _get_pandas_accessor(self):
         if self.__accessor_instance is None:
-            self.__accessor_instance = self._gui._get_accessor()._get_instance(pd.DataFrame({}))  # type: ignore[arg-type, assignment]
+            self.__accessor_instance = self._gui._get_accessor()._get_instance(pd.DataFrame({}))  # type: ignore[reportAttributeAccessIssue ]
         return t.cast(_PandasDataAccessor, self.__accessor_instance)
 
     @abstractmethod
     def _from_pandas(self, value: pd.DataFrame, data_type: t.Type) -> t.Any:
         pass
 
-    def get_cols_description(self, var_name: str, value: t.Any) -> t.Union[None, t.Dict[str, t.Dict[str, str]]]:  # type: ignore
+    def get_cols_description(self, var_name: str, value: t.Any) -> t.Union[None, t.Dict[str, t.Dict[str, str]]]:  # type: ignore[override ]
         return self._get_pandas_accessor().get_cols_description(var_name, self.to_pandas(value))
 
     def get_data(

+ 10 - 10
taipy/gui/data/pandas_data_accessor.py

@@ -70,7 +70,7 @@ class _PandasDataAccessor(_DataAccessor):
         if column_name:
             args.append(column_name)  # type: ignore[arg-type]
         try:
-            return str(gui._call_function_with_state(user_function, args))
+            return str(gui._call_function_with_state(user_function, args))  # type: ignore[attr-defined]
         except Exception as e:
             _warn(f"Exception raised when calling user function {function_name}()", e)
         return ""
@@ -104,7 +104,7 @@ class _PandasDataAccessor(_DataAccessor):
             for k, v in styles.items():
                 col_applied = ""
                 new_data = None
-                func = self._gui._get_user_function(v)
+                func = self._gui._get_user_function(v)  # type: ignore[attr-defined]
                 if callable(func):
                     col_applied, new_data = self.__apply_user_function(
                         func, k if k in cols else None, v, dataframe, "tps__"
@@ -112,7 +112,7 @@ class _PandasDataAccessor(_DataAccessor):
                 new_cols[col_applied or v] = new_data if col_applied else v
         if tooltips:
             for k, v in tooltips.items():
-                func = self._gui._get_user_function(v)
+                func = self._gui._get_user_function(v)  # type: ignore[attr-defined]
                 if callable(func):
                     col_applied, new_data = self.__apply_user_function(
                         func, k if k in cols else None, v, dataframe, "tpt__"
@@ -121,7 +121,7 @@ class _PandasDataAccessor(_DataAccessor):
                         new_cols[col_applied] = new_data
         if formats:
             for k, v in formats.items():
-                func = self._gui._get_user_function(v)
+                func = self._gui._get_user_function(v)  # type: ignore[attr-defined]
                 if callable(func):
                     col_applied, new_data = self.__apply_user_function(
                         func, k if k in cols else None, v, dataframe, "tpf__"
@@ -134,7 +134,7 @@ class _PandasDataAccessor(_DataAccessor):
             if not is_copied:
                 # copy the df so that we don't "mess" with the user's data
                 dataframe = dataframe.copy()
-            tz = Gui._get_timezone()
+            tz = Gui._get_timezone()  # type: ignore[attr-defined]
             for col in date_cols:
                 col_name = self.__get_column_names(dataframe, col)
                 new_col = _get_date_col_str_name(cols, col)
@@ -338,7 +338,7 @@ class _PandasDataAccessor(_DataAccessor):
                 applies_with_fn = {
                     self.__get_column_names(df, k): v
                     if v in _PandasDataAccessor.__AGGREGATE_FUNCTIONS
-                    else self._gui._get_user_function(v)
+                    else self._gui._get_user_function(v)  # type: ignore[attr-defined]
                     for k, v in applies.items()
                 }
 
@@ -401,9 +401,9 @@ class _PandasDataAccessor(_DataAccessor):
                         raise Exception()
                 except Exception:
                     _warn(f"Cannot sort {var_name} on columns {order_by}.")
-                    new_indexes = slice(start, end + 1)  # type: ignore
+                    new_indexes = slice(start, end + 1)
             else:
-                new_indexes = slice(start, end + 1)  # type: ignore
+                new_indexes = slice(start, end + 1)
             df = self.__build_transferred_cols(
                 columns + optional_columns,
                 t.cast(pd.DataFrame, df),
@@ -460,7 +460,7 @@ class _PandasDataAccessor(_DataAccessor):
                     decimated_dfs.append(decimated_df)
                     continue
                 decimator_instance = (
-                    self._gui._get_user_instance(decimator, PropertyType.decimator.value)
+                    self._gui._get_user_instance(decimator, PropertyType.decimator.value)  # type: ignore[attr-defined]
                     if decimator is not None
                     else None
                 )
@@ -473,7 +473,7 @@ class _PandasDataAccessor(_DataAccessor):
                     # add decimated dataframe to the list of decimated
                     decimated_dfs.append(decimated_df)
                     if is_decimator_applied:
-                        self._gui._call_on_change(f"{var_name}.{decimator}.nb_rows", len(decimated_df))
+                        self._gui._call_on_change(f"{var_name}.{decimator}.nb_rows", len(decimated_df))  # type: ignore[attr-defined]
             # merge the decimated dataFrames
             if len(decimated_dfs) > 1:
                 # get the unique columns from all decimated dataFrames

+ 6 - 6
taipy/gui/extension/library.py

@@ -307,7 +307,7 @@ class ElementLibrary(ABC):
     def __get_class_folder(self):
         if not hasattr(self, "_class_folder"):
             module_obj = sys.modules.get(self.__class__.__module__)
-            base = (Path(".") if module_obj is None else Path(module_obj.__file__).parent).resolve()  # type: ignore
+            base = (Path(".") if module_obj is None else Path(module_obj.__file__).parent).resolve()  # type: ignore[arg-type]
             self._class_folder = base if base.exists() else Path(".").resolve()
         return self._class_folder
 
@@ -372,7 +372,7 @@ class ElementLibrary(ABC):
         """TODO"""
         from ..gui import Gui
 
-        return f"/{Gui._EXTENSION_ROOT}/{self.get_name()}/{resource}{self.get_query(resource)}"
+        return f"/{Gui._EXTENSION_ROOT}/{self.get_name()}/{resource}{self.get_query(resource)}"  # type: ignore[attr-defined]
 
     def get_data(self, library_name: str, payload: t.Dict, var_name: str, value: t.Any) -> t.Optional[t.Dict]:
         """
@@ -491,9 +491,9 @@ class _ElementWithInnerProps(Element):
                     # handling property replacement in inner properties <tp:prop:...>
                     while m := _ElementWithInnerProps.__RE_PROP_VAR.search(val):
                         var = attributes.get(m.group(1))
-                        hash_value = None if var is None else gui._evaluate_expr(var)
+                        hash_value = None if var is None else gui._evaluate_expr(var)  # type: ignore[attr-defined]
                         if hash_value:
-                            names = gui._get_real_var_name(hash_value)
+                            names = gui._get_real_var_name(hash_value)  # type: ignore[attr-defined]
                             hash_value = names[0] if isinstance(names, tuple) else names
                         else:
                             hash_value = "None"
@@ -507,7 +507,7 @@ class _ElementWithInnerProps(Element):
                             id = len(uniques) + 1
                             uniques[m.group(1)] = id
                         val = f"{val[: m.start()]}{counter}{id}{val[m.end() :]}"
-                    if has_uniq and gui._is_expression(val):
-                        gui._evaluate_expr(val, True)
+                    if has_uniq and gui._is_expression(val):  # type: ignore[attr-defined]
+                        gui._evaluate_expr(val, True)  # type: ignore[attr-defined]
 
                 attributes[prop] = val

+ 63 - 63
taipy/gui/gui.py

@@ -60,7 +60,7 @@ from ._renderers.factory import _Factory
 from ._renderers.json import _TaipyJsonEncoder
 from ._renderers.utils import _get_columns_dict
 from ._warnings import TaipyGuiWarning, _warn
-from .builder import _ElementApiGenerator
+from .builder._api_generator import _ElementApiGenerator
 from .config import Config, ConfigParameter, _Config
 from .custom import Page as CustomPage
 from .custom.utils import get_current_resource_handler, is_in_custom_page_context
@@ -179,19 +179,19 @@ class Gui:
 
     def __init__(
         self,
-        page: t.Optional[t.Union[str, Page]] = None,
+        page: t.Union[str, Page, None] = None,
         pages: t.Optional[dict] = None,
         css_file: t.Optional[str] = None,
         path_mapping: t.Optional[dict] = None,
         env_filename: t.Optional[str] = None,
         libraries: t.Optional[t.List[ElementLibrary]] = None,
         flask: t.Optional[Flask] = None,
-        script_paths: t.Optional[t.Union[str, Path, t.List[t.Union[str, Path]]]] = None,
+        script_paths: t.Union[str, Path, t.List[t.Union[str, Path]], None] = None,
     ):
         """Initialize a new Gui instance.
 
         Arguments:
-            page (Optional[Union[str, Page^]]): An optional `Page^` instance that is used
+            page (Union[str, Page^]): An optional `Page^` instance that is used
                 when there is a single page in this interface, referenced as the *root*
                 page (located at `/`).<br/>
                 If *page* is a raw string and if it holds a path to a readable file then
@@ -231,7 +231,7 @@ class Gui:
                 If this argument is set, this `Gui` instance will use the value of this argument
                 as the underlying server. If omitted or set to None, this `Gui` will create its
                 own Flask application instance and use it to serve the pages.
-            script_paths (Optional[Union[str, Path, List[Union[str, Path]]]]):
+            script_paths (Union[str, Path, List[Union[str, Path]], None]):
                 Specifies the path(s) to the JavaScript files or external resources used by the application.
                 It can be a single URL or path, or a list containing multiple URLs and/or paths.
         """
@@ -254,14 +254,14 @@ class Gui:
         self.__content_accessor = None
         self.__accessors: t.Optional[_DataAccessors] = None
         self.__state: t.Optional[State] = None
-        self.__bindings = _Bindings(self)
+        self.__bindings = _Bindings(self)  # type: ignore[arg-type]
         self.__locals_context = _LocalsContext()
         self.__var_dir = _VariableDirectory(self.__locals_context)
 
         self.__evaluator: _Evaluator = None  # type: ignore[assignment]
         self.__adapter = _Adapter()
         self.__directory_name_of_pages: t.List[str] = []
-        self.__favicon: t.Optional[t.Union[str, Path]] = None
+        self.__favicon: t.Union[str, Path, None] = None
 
         # default actions
         self.on_action: t.Optional[t.Callable] = None
@@ -418,7 +418,7 @@ class Gui:
             for library in libraries:
                 Gui.add_library(library)
 
-    def __load_scripts(self, script_paths: t.Optional[t.Union[str, Path, t.List[t.Union[str, Path]]]]):
+    def __load_scripts(self, script_paths: t.Union[str, Path, t.List[t.Union[str, Path]], None]):
         if script_paths is None:
             return
         else:
@@ -499,7 +499,7 @@ class Gui:
         variable_name = query.get("variable_name")
         content = None
         if variable_name:
-            content = _getscopeattr(self, variable_name)
+            content = _getscopeattr(self, variable_name)  # type: ignore[arg-type]
             if isinstance(content, _TaipyContentHtml):
                 content = content.get()
             provider_fn = Gui.__content_providers.get(type(content))
@@ -776,11 +776,11 @@ class Gui:
         if not var_name:
             return
         # Check if Variable is a managed type
-        current_value = _getscopeattr_drill(self, self.__evaluator.get_hash_from_expr(var_name))
+        current_value = _getscopeattr_drill(self, self.__evaluator.get_hash_from_expr(var_name))  # type: ignore[arg-type]
         if isinstance(current_value, _TaipyData):
             return
         elif rel_var and isinstance(current_value, _TaipyLovValue):  # pragma: no cover
-            lov_holder = _getscopeattr_drill(self, self.__evaluator.get_hash_from_expr(rel_var))
+            lov_holder = _getscopeattr_drill(self, self.__evaluator.get_hash_from_expr(rel_var))  # type: ignore[arg-type]
             if isinstance(lov_holder, _TaipyLov):
                 if isinstance(value, (str, list)):
                     val = value if isinstance(value, list) else [value]
@@ -814,7 +814,7 @@ class Gui:
             self._set_broadcast()
         # Use custom attrsetter function to allow value binding for _MapDict
         if propagate:
-            _setscopeattr_drill(self, hash_expr, value)
+            _setscopeattr_drill(self, hash_expr, value)  # type: ignore[arg-type]
             # In case expression == hash (which is when there is only a single variable in expression)
             if var_name == hash_expr or hash_expr.startswith("tpec_"):
                 derived_vars.update(self._re_evaluate_expr(var_name))
@@ -921,7 +921,7 @@ class Gui:
         q_args: t.Dict[str, str] = {}
         q_args.update(request.args)
         q_args.pop(Gui.__ARG_CLIENT_ID, None)
-        cb_function: t.Optional[t.Union[t.Callable, str]] = None
+        cb_function: t.Union[t.Callable, str, None] = None
         cb_function_name = None
         if q_args.get(Gui._HTML_CONTENT_KEY):
             cb_function = self.__process_content_provider
@@ -933,7 +933,7 @@ class Gui:
                 if not _is_function(cb_function):
                     parts = cb_function_name.split(".", 1)
                     if len(parts) > 1:
-                        base = _getscopeattr(self, parts[0], None)
+                        base = _getscopeattr(self, parts[0], None)  # type: ignore[arg-type]
                         if base and (meth := getattr(base, parts[1], None)):
                             cb_function = meth
                         else:
@@ -1087,10 +1087,10 @@ class Gui:
                 except EnvironmentError as ee:  # pragma: no cover
                     _warn(f"Cannot group file after chunk upload for {file.filename}", ee)
                     return (f"Cannot group file after chunk upload for {file.filename}", 500)
-            # notify the file is uploaded
+            # Notify when file is uploaded
             newvalue = str(file_path)
             if multiple and var_name:
-                value = _getscopeattr(self, var_name)
+                value = _getscopeattr(self, var_name)  # type: ignore[arg-type]
                 if not isinstance(value, t.List):
                     value = [] if value is None else [value]
                 value.append(newvalue)
@@ -1106,7 +1106,7 @@ class Gui:
                     data["path"] = file_path
                     file_fn = self._get_user_function(on_upload_action)
                     if not _is_function(file_fn):
-                        file_fn = _getscopeattr(self, on_upload_action)
+                        file_fn = _getscopeattr(self, on_upload_action)  # type: ignore[arg-type]
                     if _is_function(file_fn):
                         self._call_function_with_state(t.cast(t.Callable, file_fn), ["file_upload", {"args": [data]}])
                 else:
@@ -1120,7 +1120,7 @@ class Gui:
     ):
         ws_dict = {}
         is_custom_page = is_in_custom_page_context()
-        values = {v: _getscopeattr_drill(self, v) for v in modified_vars if is_custom_page or _is_moduled_variable(v)}
+        values = {v: _getscopeattr_drill(self, v) for v in modified_vars if is_custom_page or _is_moduled_variable(v)}  # type: ignore[arg-type]
         if not values:
             return
         for k, v in values.items():
@@ -1217,14 +1217,14 @@ class Gui:
             The transformed data or None if no transformation is possible.
         """
         try:
-            return Gui.__unsupported_data_converter(value) if _is_function(Gui.__unsupported_data_converter) else None  # type: ignore
+            return Gui.__unsupported_data_converter(value) if _is_function(Gui.__unsupported_data_converter) else None  # type: ignore[misc]
         except Exception as e:
             _warn(f"Error transforming data: {str(e)}")
             return None
 
     def __request_data_update(self, var_name: str, payload: t.Any) -> None:
         # Use custom attrgetter function to allow value binding for _MapDict
-        newvalue = _getscopeattr_drill(self, var_name)
+        newvalue = _getscopeattr_drill(self, var_name)  # type: ignore[arg-type]
         resource_handler = get_current_resource_handler()
         custom_page_filtered_types = resource_handler.data_layer_supported_types if resource_handler else ()
         if not isinstance(newvalue, _TaipyData) and isinstance(newvalue, custom_page_filtered_types):
@@ -1260,7 +1260,7 @@ class Gui:
             if payload.get("refresh", False):
                 # refresh vars
                 for _var in t.cast(list, payload.get("names")):
-                    val = _getscopeattr_drill(self, _var)
+                    val = _getscopeattr_drill(self, _var)  # type: ignore[arg-type]
                     self._refresh_expr(
                         val.get_name() if isinstance(val, _TaipyBase) else _var,
                         val if isinstance(val, _TaipyBase) else None,
@@ -1387,7 +1387,7 @@ class Gui:
             if value is not None and scope_meta_ls.get(key) != value:
                 scope_meta_ls[key] = value
 
-    def _query_local_storage(self, *keys: str) -> t.Optional[t.Union[str, t.Dict[str, str]]]:
+    def _query_local_storage(self, *keys: str) -> t.Union[str, t.Dict[str, str], None]:
         if not keys:
             return None
         if len(keys) == 1:
@@ -1552,8 +1552,8 @@ class Gui:
 
     def __get_message_grouping(self):
         return (
-            _getscopeattr(self, Gui.__MESSAGE_GROUPING_NAME)
-            if _hasscopeattr(self, Gui.__MESSAGE_GROUPING_NAME)
+            _getscopeattr(self, Gui.__MESSAGE_GROUPING_NAME)  # type: ignore[arg-type]
+            if _hasscopeattr(self, Gui.__MESSAGE_GROUPING_NAME)  # type: ignore[arg-type]
             else None
         )
 
@@ -1578,7 +1578,7 @@ class Gui:
     def __send_messages(self):
         grouping_message = self.__get_message_grouping()
         if grouping_message is not None:
-            _delscopeattr(self, Gui.__MESSAGE_GROUPING_NAME)
+            _delscopeattr(self, Gui.__MESSAGE_GROUPING_NAME)  # type: ignore[arg-type]
             if len(grouping_message):
                 self.__send_ws({"type": _WsType.MULTIPLE_MESSAGE.value, "payload": grouping_message})
 
@@ -1587,7 +1587,7 @@ class Gui:
             getattr(self, func_name.split(".", 2)[1], func_name) if func_name.startswith(f"{Gui.__SELF_VAR}.") else None
         )
         if not _is_function(func):
-            func = _getscopeattr(self, func_name, None)
+            func = _getscopeattr(self, func_name, None)  # type: ignore[arg-type]
         if not _is_function(func):
             func = self._get_locals_bind().get(func_name)
         if not _is_function(func):
@@ -1595,7 +1595,7 @@ class Gui:
         return t.cast(t.Callable, func) if _is_function(func) else func_name
 
     def _get_user_instance(self, class_name: str, class_type: type) -> t.Union[object, str]:
-        cls = _getscopeattr(self, class_name, None)
+        cls = _getscopeattr(self, class_name, None)  # type: ignore[arg-type]
         if not isinstance(cls, class_type):
             cls = self._get_locals_bind().get(class_name)
         if not isinstance(cls, class_type):
@@ -1607,7 +1607,7 @@ class Gui:
         try:
             csv_path = self._get_accessor().to_csv(
                 holder_name,
-                _getscopeattr(self, holder_name, None),
+                _getscopeattr(self, holder_name, None),  # type: ignore[arg-type]
             )
             if csv_path:
                 self._download(csv_path, "data.csv", Gui.__DOWNLOAD_DELETE_ACTION)
@@ -1806,22 +1806,22 @@ class Gui:
     def _evaluate_expr(
         self, expr: str, lazy_declare: t.Optional[bool] = False, lambda_expr: t.Optional[bool] = False
     ) -> t.Any:
-        return self.__evaluator.evaluate_expr(self, expr, lazy_declare, lambda_expr)
+        return self.__evaluator.evaluate_expr(self, expr, lazy_declare, lambda_expr)  # type: ignore[arg-type]
 
     def _re_evaluate_expr(self, var_name: str) -> t.Set[str]:
-        return self.__evaluator.re_evaluate_expr(self, var_name)
+        return self.__evaluator.re_evaluate_expr(self, var_name)  # type: ignore[arg-type]
 
     def _refresh_expr(self, var_name: str, holder: t.Optional[_TaipyBase]):
-        return self.__evaluator.refresh_expr(self, var_name, holder)
+        return self.__evaluator.refresh_expr(self, var_name, holder)  # type: ignore[arg-type]
 
     def _get_expr_from_hash(self, hash_val: str) -> str:
         return self.__evaluator.get_expr_from_hash(hash_val)
 
     def _evaluate_bind_holder(self, holder: t.Type[_TaipyBase], expr: str) -> str:
-        return self.__evaluator.evaluate_bind_holder(self, holder, expr)
+        return self.__evaluator.evaluate_bind_holder(self, holder, expr)  # type: ignore[arg-type]
 
     def _evaluate_holders(self, expr: str) -> t.List[str]:
-        return self.__evaluator.evaluate_holders(self, expr)
+        return self.__evaluator.evaluate_holders(self, expr)  # type: ignore[arg-type]
 
     def _is_expression(self, expr: str) -> bool:
         if self.__evaluator is None:
@@ -1947,7 +1947,7 @@ class Gui:
                         data_hashes.append(data_hash)
                         idx += 1
                     config = _build_chart_config(
-                        self,
+                        self,  # type: ignore[arg-type]
                         attributes,
                         [
                             self._get_accessor().get_cols_description(
@@ -1985,14 +1985,14 @@ class Gui:
         return self.__adapter._get_valid_result(value, id_only)
 
     def _is_ui_blocked(self):
-        return _getscopeattr(self, Gui.__UI_BLOCK_NAME, False)
+        return _getscopeattr(self, Gui.__UI_BLOCK_NAME, False)  # type: ignore[arg-type]
 
     def __get_on_cancel_block_ui(self, callback: t.Optional[str]):
         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})
+            gui_app.__on_action(id, {"action": callback})  # type: ignore[attr-defined]
 
         return _taipy_on_cancel_block_ui
 
@@ -2148,7 +2148,7 @@ class Gui:
         self._add_page_context(page)
         # Special case needed for page to access gui to trigger reload in notebook
         if _is_in_notebook():
-            page._notebook_gui = self
+            page._notebook_gui = self  # type: ignore[attr-defined]
             page._notebook_page = new_page
         # add page to hook
         _Hooks().add_page(self, page)
@@ -2163,7 +2163,7 @@ class Gui:
             self.__var_dir.add_frame(page._frame)
         return module_name
 
-    def add_pages(self, pages: t.Optional[t.Union[t.Mapping[str, t.Union[str, Page]], str]] = None) -> None:
+    def add_pages(self, pages: t.Union[t.Mapping[str, t.Union[str, Page]], str, None] = None) -> None:
         """Add several pages to the Graphical User Interface.
 
         Arguments:
@@ -2293,18 +2293,18 @@ class Gui:
         return new_partial
 
     def _update_partial(self, partial: Partial):
-        partials = _getscopeattr(self, Partial._PARTIALS, {})
+        partials = _getscopeattr(self, Partial._PARTIALS, {})  # type: ignore[arg-type]
         partials[partial._route] = partial
-        _setscopeattr(self, Partial._PARTIALS, partials)
+        _setscopeattr(self, Partial._PARTIALS, partials)  # type: ignore[arg-type]
         self.__send_ws_partial(str(partial._route))
 
     def _get_partial(self, route: str) -> t.Optional[Partial]:
-        partials = _getscopeattr(self, Partial._PARTIALS, {})
+        partials = _getscopeattr(self, Partial._PARTIALS, {})  # type: ignore[arg-type]
         partial = partials.get(route)
         if partial is None:
             partial = next((p for p in self._config.partials if p._route == route), None)
             partials[route] = partial
-            _setscopeattr(self, Partial._PARTIALS, partials)
+            _setscopeattr(self, Partial._PARTIALS, partials)  # type: ignore[arg-type]
         return partial
 
     # Main binding method (bind in markdown declaration)
@@ -2386,9 +2386,7 @@ class Gui:
         except RuntimeError:
             return False
 
-    def _download(
-        self, content: t.Any, name: t.Optional[str] = "", on_action: t.Optional[t.Union[str, t.Callable]] = ""
-    ):
+    def _download(self, content: t.Any, name: t.Optional[str] = "", on_action: t.Union[str, t.Callable, None] = ""):
         if _is_function(on_action):
             on_action_name = (
                 _get_lambda_id(t.cast(LambdaType, on_action))
@@ -2438,7 +2436,7 @@ class Gui:
 
     def _hold_actions(
         self,
-        callback: t.Optional[t.Union[str, t.Callable]] = None,
+        callback: t.Union[str, t.Callable, None] = None,
         message: t.Optional[str] = "Work in Progress...",
     ):  # pragma: no cover
         if _is_unnamed_function(callback):
@@ -2450,17 +2448,17 @@ class Gui:
             )
         func = self.__get_on_cancel_block_ui(action_name)
         def_action_name = func.__name__
-        _setscopeattr(self, def_action_name, func)
+        _setscopeattr(self, def_action_name, func)  # type: ignore[arg-type]
 
-        if _hasscopeattr(self, Gui.__UI_BLOCK_NAME):
-            _setscopeattr(self, Gui.__UI_BLOCK_NAME, True)
+        if _hasscopeattr(self, Gui.__UI_BLOCK_NAME):  # type: ignore[arg-type]
+            _setscopeattr(self, Gui.__UI_BLOCK_NAME, True)  # type: ignore[arg-type]
         else:
             self._bind(Gui.__UI_BLOCK_NAME, True)
         self.__send_ws_block(action=def_action_name, message=message, cancel=bool(action_name))
 
     def _resume_actions(self):  # pragma: no cover
-        if _hasscopeattr(self, Gui.__UI_BLOCK_NAME):
-            _setscopeattr(self, Gui.__UI_BLOCK_NAME, False)
+        if _hasscopeattr(self, Gui.__UI_BLOCK_NAME):  # type: ignore[arg-type]
+            _setscopeattr(self, Gui.__UI_BLOCK_NAME, False)  # type: ignore[arg-type]
         self.__send_ws_block(close=True)
 
     def _navigate(
@@ -2490,8 +2488,8 @@ class Gui:
 
     def __init_route(self):
         self.__set_client_id_in_context(force=True)
-        if not _hasscopeattr(self, Gui.__ON_INIT_NAME):
-            _setscopeattr(self, Gui.__ON_INIT_NAME, True)
+        if not _hasscopeattr(self, Gui.__ON_INIT_NAME):  # type: ignore[arg-type]
+            _setscopeattr(self, Gui.__ON_INIT_NAME, True)  # type: ignore[arg-type]
             self.__pre_render_pages()
             self.__init_libs()
             if hasattr(self, "on_init") and _is_function(self.on_init):
@@ -2533,7 +2531,7 @@ class Gui:
                     if isinstance(page._renderer, CustomPage):
                         self._bind_custom_page_variables(page._renderer, self._get_client_id())
                     else:
-                        page.render(self, silent=True)
+                        page.render(self, silent=True)  # type: ignore[arg-type]
         if additional_pages := _Hooks()._get_additional_pages():
             for page in additional_pages:
                 if isinstance(page, Page):
@@ -2543,7 +2541,7 @@ class Gui:
                         else:
                             new_page = _Page()
                             new_page._renderer = page
-                            new_page.render(self, silent=True)
+                            new_page.render(self, silent=True)  # type: ignore[arg-type]
 
         scope_metadata[_DataScopes._META_PRE_RENDER] = True
 
@@ -2631,7 +2629,7 @@ class Gui:
                 return ("Successfully redirect to custom resource handler", 200)
             return ("Failed to navigate to custom resource handler", 500)
         # Handle page rendering
-        context = page.render(self)
+        context = page.render(self)  # type: ignore[arg-type]
         if (
             nav_page == Gui.__root_page_name
             and page._rendered_jsx is not None
@@ -2748,7 +2746,7 @@ class Gui:
         # Init server if there is no server
         if not hasattr(self, "_server"):
             self._server = _Server(
-                self,
+                self,  # type: ignore[arg-type]
                 path_mapping=self._path_mapping,
                 flask=self._flask,
                 async_mode=app_config.get("async_mode"),
@@ -2761,7 +2759,7 @@ class Gui:
             self.stop()
             self._flask_blueprint = []
             self._server = _Server(
-                self,
+                self,  # type: ignore[arg-type]
                 path_mapping=self._path_mapping,
                 flask=self._flask,
                 async_mode=app_config.get("async_mode"),
@@ -2846,7 +2844,7 @@ class Gui:
             for lib in libs
             for s in (lib._do_get_relative_paths(lib.get_styles()))
         ]
-        if self._get_config("stylekit", True):
+        if self._get_config("stylekit", True):  # noqa codespell
             styles.append("stylekit/stylekit.css")
         else:
             styles.append(Gui.__ROBOTO_FONT)
@@ -2891,7 +2889,7 @@ class Gui:
 
     def _get_accessor(self):
         if self.__accessors is None:
-            self.__accessors = _DataAccessors(self)
+            self.__accessors = _DataAccessors(self)  # type: ignore[arg-type]
         return self.__accessors
 
     def run(
@@ -2941,7 +2939,7 @@ class Gui:
         """
         # --------------------------------------------------------------------------------
         # The ssl_context argument was removed just after 1.1. It was defined as:
-        # t.Optional[t.Union[ssl.SSLContext, t.Tuple[str, t.Optional[str]], t.Literal["adhoc"]]] = None
+        # t.Union[ssl.SSLContext, t.Tuple[str, t.Optional[str]], t.Literal["adhoc"], None] = None
         #
         # With the doc:
         #     ssl_context (Optional[Union[ssl.SSLContext, Tuple[str, Optional[str]], t.Literal['adhoc']]]):
@@ -2994,7 +2992,9 @@ class Gui:
 
         if self.__state is None or is_reloading:
             self.__state = _GuiState(
-                self, self.__locals_context.get_all_keys(), self.__locals_context.get_all_context()
+                self,  # type: ignore[arg-type]
+                self.__locals_context.get_all_keys(),
+                self.__locals_context.get_all_context(),
             )
 
         if _is_in_notebook():
@@ -3018,7 +3018,7 @@ class Gui:
                 if not isinstance(lib, ElementLibrary):
                     continue
                 try:
-                    lib_context = lib.on_init(self)
+                    lib_context = lib.on_init(self)  # type: ignore[arg-type]
                     if (
                         isinstance(lib_context, tuple)
                         and len(lib_context) > 1

+ 23 - 25
taipy/gui/gui_actions.py

@@ -18,9 +18,7 @@ from .state import State
 from .utils.callable import _is_function
 
 
-def download(
-    state: State, content: t.Any, name: t.Optional[str] = "", on_action: t.Optional[t.Union[str, t.Callable]] = ""
-):
+def download(state: State, content: t.Any, name: t.Optional[str] = "", on_action: t.Union[str, t.Callable, None] = ""):
     """Download content to the client.
 
     Arguments:
@@ -57,7 +55,7 @@ def download(
                   and the second element reflects the server-side URL where the file is located.
     """
     if state and isinstance(state._gui, Gui):
-        state._gui._download(content, name, on_action)
+        state._gui._download(content, name, on_action)  # type: ignore[attr-defined]
     else:
         _warn("'download()' must be called in the context of a callback.")
 
@@ -102,7 +100,7 @@ def notify(
     displayed, but the in-app notification will still function.
     """
     if state and isinstance(state._gui, Gui):
-        return state._gui._notify(notification_type, message, system_notification, duration, id)
+        return state._gui._notify(notification_type, message, system_notification, duration, id)  # type: ignore[attr-defined]
     else:
         _warn("'notify()' must be called in the context of a callback.")
         return None
@@ -124,14 +122,14 @@ def close_notification(state: State, id: str) -> None:
     """
     if state and isinstance(state._gui, Gui):
         # Send the close command with the notification_id
-        state._gui._close_notification(id)
+        state._gui._close_notification(id)  # type: ignore[attr-defined]
     else:
         _warn("'close_notification()' must be called in the context of a callback.")
 
 
 def hold_control(
     state: State,
-    callback: t.Optional[t.Union[str, t.Callable]] = None,
+    callback: t.Union[str, t.Callable, None] = None,
     message: t.Optional[str] = "Work in Progress...",
 ):
     """Hold the User Interface actions.
@@ -146,7 +144,7 @@ def hold_control(
 
     Arguments:
         state (State^): The current user state received in any callback.
-        callback (Optional[Union[str, Callable]]): The function to be called if the user
+        callback (Union[str, Callable]): The function to be called if the user
             chooses to cancel.<br/>
             If empty or None, no cancel action is provided to the user.<br/>
             The signature of this function is:
@@ -159,7 +157,7 @@ def hold_control(
         message: The message to show. The default value is the string "Work in Progress...".
     """
     if state and isinstance(state._gui, Gui):
-        state._gui._hold_actions(callback, message)
+        state._gui._hold_actions(callback, message)  # type: ignore[attr-defined]
     else:
         _warn("'hold_actions()' must be called in the context of a callback.")
 
@@ -174,7 +172,7 @@ def resume_control(state: State):
         state (State^): The current user state as received in any callback.
     """
     if state and isinstance(state._gui, Gui):
-        state._gui._resume_actions()
+        state._gui._resume_actions()  # type: ignore[attr-defined]
     else:
         _warn("'resume_actions()' must be called in the context of a callback.")
 
@@ -200,7 +198,7 @@ def navigate(
         force: When navigating to a known page, the content is refreshed even it the page is already shown.
     """
     if state and isinstance(state._gui, Gui):
-        state._gui._navigate(to, params, tab, force)
+        state._gui._navigate(to, params, tab, force)  # type: ignore[attr-defined]
     else:
         _warn("'navigate()' must be called in the context of a callback.")
 
@@ -225,7 +223,7 @@ def get_user_content_url(
         An URL that, when queried, triggers the *on_user_content* callback.
     """
     if state and isinstance(state._gui, Gui):
-        return state._gui._get_user_content_url(path, params)
+        return state._gui._get_user_content_url(path, params)  # type: ignore[attr-defined]
     _warn("'get_user_content_url()' must be called in the context of a callback.")
     return None
 
@@ -247,7 +245,7 @@ def get_state_id(state: State) -> t.Optional[str]:
             If this value None, it indicates that *state* is not handled by a `Gui^` instance.
     """
     if state and isinstance(state._gui, Gui):
-        return state._gui._get_client_id()
+        return state._gui._get_client_id()  # type: ignore[attr-defined]
     return None
 
 
@@ -261,7 +259,7 @@ def get_module_context(state: State) -> t.Optional[str]:
         The name of the current module.
     """
     if state and isinstance(state._gui, Gui):
-        return state._gui._get_locals_context()
+        return state._gui._get_locals_context()  # type: ignore[attr-defined]
     return None
 
 
@@ -291,7 +289,7 @@ def get_module_name_from_state(state: State) -> t.Optional[str]:
             that triggered the callback that was provided the *state* object.
     """
     if state and isinstance(state._gui, Gui):
-        return state._gui._get_locals_context()
+        return state._gui._get_locals_context()  # type: ignore[attr-defined]
     return None
 
 
@@ -353,9 +351,9 @@ def invoke_state_callback(gui: Gui, state_id: str, callback: t.Callable, args: t
 def invoke_long_callback(
     state: State,
     user_function: t.Callable,
-    user_function_args: t.Optional[t.Union[t.Tuple, t.List]] = None,
+    user_function_args: t.Union[t.Tuple, t.List, None] = None,
     user_status_function: t.Optional[t.Callable] = None,
-    user_status_function_args: t.Optional[t.Union[t.Tuple, t.List]] = None,
+    user_status_function_args: t.Union[t.Tuple, t.List, None] = None,
     period=0,
 ):
     """Invoke a long-running user callback.
@@ -374,7 +372,7 @@ def invoke_long_callback(
         state (State^): The `State^` instance, as received in any callback.
         user_function (Callable[[...], None]): The function that will be run independently of Taipy GUI. Note
             that this function must not use *state*, which is not persisted across threads.
-        user_function_args (Optional[List|Tuple]): The arguments to send to *user_function*.
+        user_function_args (Union[List, Tuple]): The arguments to send to *user_function*.
         user_status_function (Optional(Callable[[State^, bool, ...], None])): The optional user-defined status
             function that is invoked at the end of and possibly during the runtime of *user_function*:
 
@@ -391,7 +389,7 @@ def invoke_long_callback(
                - If this parameter is set to an int value, then this value indicates
                  how many periods (as lengthy as indicated in *period*) have elapsed since *user_function* was
                  started.
-        user_status_function_args (Optional[List|Tuple]): The remaining arguments of the user status function.
+        user_status_function_args (Union[List, Tuple]): The remaining arguments of the user status function.
         period (int): The interval, in milliseconds, at which *user_status_function* is called.<br/>
             The default value is 0, meaning no call to *user_status_function* will happen until *user_function*
             terminates (then the second parameter of that call will be ).</br>
@@ -409,13 +407,13 @@ def invoke_long_callback(
 
     this_gui = state.get_gui()
 
-    state_id = this_gui._get_client_id()
-    module_context = this_gui._get_locals_context()
+    state_id = this_gui._get_client_id()  # type: ignore[attr-defined]
+    module_context = this_gui._get_locals_context()  # type: ignore[attr-defined]
     if not isinstance(state_id, str) or not isinstance(module_context, str):
         return
 
     def callback_on_exception(state: State, function_name: str, e: Exception):
-        if not this_gui._call_on_exception(function_name, e):
+        if not this_gui._call_on_exception(function_name, e):  # type: ignore[attr-defined]
             _warn(f"invoke_long_callback(): Exception raised in function {function_name}()", e)
 
     def callback_on_status(
@@ -428,7 +426,7 @@ def invoke_long_callback(
             this_gui.invoke_callback(
                 str(state_id),
                 t.cast(t.Callable, user_status_function),
-                [status] + list(user_status_function_args) + [function_result],  # type: ignore
+                [status] + list(user_status_function_args) + [function_result],
                 str(module_context),
             )
         if e:
@@ -461,7 +459,7 @@ def invoke_long_callback(
         thread_status(thread.name, period / 1000.0, 0)
 
 
-def query_local_storage(state: State, *keys: str) -> t.Optional[t.Union[str, t.Dict[str, str]]]:
+def query_local_storage(state: State, *keys: str) -> t.Union[str, t.Dict[str, str], None]:
     """Retrieve values from the browser's local storage.
 
     This function queries the local storage of the client identified by *state* and returns the
@@ -482,6 +480,6 @@ def query_local_storage(state: State, *keys: str) -> t.Optional[t.Union[str, t.D
             - If no value is found for a key, that key will not appear in the dictionary.
     """
     if state and isinstance(state._gui, Gui):
-        return state._gui._query_local_storage(*keys)
+        return state._gui._query_local_storage(*keys)  # type: ignore[attr-defined]
     _warn("'query_local_storage()' must be called in the context of a callback.")
     return None

+ 18 - 11
taipy/gui/page.py

@@ -45,7 +45,13 @@ class Page:
         self._class_module_name = ""
         self._class_locals: t.Dict[str, t.Any] = {}
         self._frame: t.Optional[FrameType] = None
-        self._renderer: t.Optional[Page] = self.create_page()
+        page = self.create_page()
+        if isinstance(page, str):
+            from ._renderers import Markdown
+
+            page = Markdown(page)
+        self._renderer: t.Optional[Page] = page
+
         if "frame" in kwargs:
             self._frame = kwargs.get("frame")
         elif self._renderer:
@@ -63,11 +69,7 @@ class Page:
             # Extract the page module's attributes and methods
             cls = type(self)
             cls_locals = dict(vars(self))
-            functions = [
-                i[0]
-                for i in inspect.getmembers(cls)
-                if not i[0].startswith("_") and inspect.isroutine(i[1])
-            ]
+            functions = [i[0] for i in inspect.getmembers(cls) if not i[0].startswith("_") and inspect.isroutine(i[1])]
             for f in functions:
                 func = getattr(self, f)
                 if hasattr(func, "__func__") and func.__func__ is not None:
@@ -80,7 +82,7 @@ class Page:
         self.set_style(kwargs.get("style", None))
         self._script_paths(kwargs.get("script_paths", None))
 
-    def create_page(self) -> t.Optional[Page]:
+    def create_page(self) -> t.Union[Page, str, None]:
         """Create the page content for page modules.
 
         If this page is a page module, this method must be overloaded and return the page content.
@@ -91,7 +93,8 @@ class Page:
         a page module.
 
         Returns:
-            The page content for this Page subclass, making it a page module.
+            The page content for this Page subclass, making it a page module.<br/>
+            This can be a string, then Taipy will interpret it as Markdown content.
         """
         return None
 
@@ -185,7 +188,7 @@ class Page:
     def _get_style(self):
         return self.__style
 
-    def _script_paths(self, script_paths: t.Optional[t.Union[str, Path, t.List[t.Union[str, Path]]]]) -> Page:
+    def _script_paths(self, script_paths: t.Union[str, Path, t.List[t.Union[str, Path]], None]) -> Page:
         """
         Load a script or a list of scripts to be used in the page.
 
@@ -205,8 +208,12 @@ class Page:
                         continue
                     script_path = Path(script_path)
 
-                if isinstance(script_path,
-                              Path) and script_path.exists() and script_path.is_file() and script_path.suffix == ".js":
+                if (
+                    isinstance(script_path, Path)
+                    and script_path.exists()
+                    and script_path.is_file()
+                    and script_path.suffix == ".js"
+                ):
                     continue
                 else:
                     _warn(f"Script path '{script_path}' does not exist, is not a file, or is not a JavaScript file.")

+ 2 - 2
taipy/gui/partial.py

@@ -60,8 +60,8 @@ class Partial(_Page):
             state (State^): The current user state as received in any callback.
             content (str): The new content to use and display.
         """
-        if state and state._gui and callable(state._gui._update_partial):
-            state._gui._update_partial(self.__copy(content))
+        if state and state._gui and callable(state._gui._update_partial):  # type: ignore[attr-defined]
+            state._gui._update_partial(self.__copy(content))  # type: ignore[attr-defined]
         else:
             _warn("'Partial.update_content()' must be called in the context of a callback.")
 

+ 13 - 13
taipy/gui/server.py

@@ -106,7 +106,7 @@ class _Server:
 
         # set json encoder (for Taipy specific types)
         self._flask.json_provider_class = _TaipyJsonProvider
-        self._flask.json = self._flask.json_provider_class(self._flask)  # type: ignore
+        self._flask.json = self._flask.json_provider_class(self._flask)
 
         self.__path_mapping = path_mapping or {}
         self.__ssl_context = server_config.get("ssl_context", None)
@@ -119,15 +119,15 @@ class _Server:
             if "status" in message:
                 _TaipyLogger._get_logger().info(message["status"])
             elif "type" in message:
-                gui._manage_message(message["type"], message)
+                gui._manage_message(message["type"], message)  # type: ignore[attr-defined]
 
         @self._ws.on("connect")
         def handle_connect():
-            gui._handle_connect()
+            gui._handle_connect()  # type: ignore[attr-defined]
 
         @self._ws.on("disconnect")
         def handle_disconnect():
-            gui._handle_disconnect()
+            gui._handle_disconnect()  # type: ignore[attr-defined]
 
     def __is_ignored(self, file_path: str) -> bool:
         if not hasattr(self, "_ignore_matches"):
@@ -136,7 +136,7 @@ class _Server:
                 (pathlib.Path(__main__.__file__).parent / __IGNORE_FILE) if hasattr(__main__, "__file__") else None
             )
             if not ignore_file or not ignore_file.is_file():
-                ignore_file = pathlib.Path(self._gui._root_dir) / __IGNORE_FILE
+                ignore_file = pathlib.Path(self._gui._root_dir) / __IGNORE_FILE  # type: ignore[attr-defined]
             self._ignore_matches = (
                 parse_gitignore(ignore_file) if ignore_file.is_file() and os.access(ignore_file, os.R_OK) else None
             )
@@ -201,7 +201,7 @@ class _Server:
                     ) from None
 
             if path == "taipy.status.json":
-                return self._direct_render_json(self._gui._serve_status(pathlib.Path(template_folder) / path))
+                return self._direct_render_json(self._gui._serve_status(pathlib.Path(template_folder) / path))  # type: ignore[attr-defined]
             if (file_path := str(os.path.normpath((base_path := static_folder + os.path.sep) + path))).startswith(
                 base_path
             ) and os.path.isfile(file_path):
@@ -229,7 +229,7 @@ class _Server:
                 return send_from_directory(base_path, path)
             if (
                 (
-                    file_path := str(os.path.normpath((base_path := self._gui._root_dir + os.path.sep) + path))
+                    file_path := str(os.path.normpath((base_path := self._gui._root_dir + os.path.sep) + path))  # type: ignore[attr-defined]
                 ).startswith(base_path)
                 and os.path.isfile(file_path)
                 and not self.__is_ignored(file_path)
@@ -251,8 +251,8 @@ class _Server:
                 "jsx": template_str,
                 "style": (style + os.linesep) if style else "",
                 "head": head or [],
-                "context": context or self._gui._get_default_module_name(),
-                "scriptPaths": script_paths
+                "context": context or self._gui._get_default_module_name(),  # type: ignore[attr-defined]
+                "scriptPaths": script_paths,
             }
         )
 
@@ -273,7 +273,7 @@ class _Server:
         self._ws.run(self._flask, host=self._host, port=self._port, debug=False, use_reloader=False)
 
     def _get_async_mode(self) -> str:
-        return self._ws.async_mode  # type: ignore[reportAttributeAccessIssue]
+        return self._ws.async_mode  # type: ignore[attr-defined]
 
     def _apply_patch(self):
         if self._get_async_mode() == "gevent" and util.find_spec("gevent"):
@@ -344,7 +344,7 @@ class _Server:
             if client_url is not None:
                 client_url = client_url.format(port=port)
                 _TaipyLogger._get_logger().info(f" * Application is accessible at {client_url}")
-        if not is_running_from_reloader() and self._gui._get_config("run_browser", False):
+        if not is_running_from_reloader() and self._gui._get_config("run_browser", False):  # type: ignore[attr-defined]
             webbrowser.open(client_url or server_url, new=2)
         if _is_in_notebook() or run_in_thread:
             self._thread = KThread(target=self._run_notebook)
@@ -373,8 +373,8 @@ class _Server:
             self._is_running = False
             with contextlib.suppress(Exception):
                 if self._get_async_mode() == "gevent":
-                    if self._ws.wsgi_server is not None:  # type: ignore[reportAttributeAccessIssue]
-                        self._ws.wsgi_server.stop()  # type: ignore[reportAttributeAccessIssue]
+                    if self._ws.wsgi_server is not None:  # type: ignore[attr-defined]
+                        self._ws.wsgi_server.stop()  # type: ignore[attr-defined]
                     else:
                         self._thread.kill()
                 else:

+ 16 - 16
taipy/gui/state.py

@@ -195,8 +195,8 @@ class _GuiState(State):
 
     def broadcast(self, name: str, value: t.Any):
         with self._set_context(self._gui):
-            encoded_name = self._gui._bind_var(name)
-            self._gui._broadcast_all_clients(encoded_name, value)
+            encoded_name = self._gui._bind_var(name)  # type: ignore[attr-defined]
+            self._gui._broadcast_all_clients(encoded_name, value)  # type: ignore[attr-defined]
 
     def __getattribute__(self, name: str) -> t.Any:
         if name == "__class__":
@@ -208,33 +208,33 @@ class _GuiState(State):
             return gui
         if name in _GuiState.__excluded_attrs:
             raise AttributeError(f"Variable '{name}' is protected and is not accessible.")
-        if gui._is_in_brdcst_callback() and (
-            name not in gui._get_shared_variables() and not gui._bindings()._is_single_client()
+        if gui._is_in_brdcst_callback() and (  # type: ignore[attr-defined]
+            name not in gui._get_shared_variables() and not gui._bindings()._is_single_client()  # type: ignore[attr-defined]
         ):
             raise AttributeError(f"Variable '{name}' is not available to be accessed in shared callback.")
         if not name.startswith("__") and name not in super().__getattribute__(_GuiState.__attrs[1]):
             raise AttributeError(f"Variable '{name}' is not defined.")
         with self._notebook_context(gui), self._set_context(gui):
-            encoded_name = gui._bind_var(name)
-            return getattr(gui._bindings(), encoded_name)
+            encoded_name = gui._bind_var(name)  # type: ignore[attr-defined]
+            return getattr(gui._bindings(), encoded_name)  # type: ignore[attr-defined]
 
     def __setattr__(self, name: str, value: t.Any) -> None:
         gui: "Gui" = super().__getattribute__(_GuiState.__gui_attr)
-        if gui._is_in_brdcst_callback() and (
-            name not in gui._get_shared_variables() and not gui._bindings()._is_single_client()
+        if gui._is_in_brdcst_callback() and (  # type: ignore[attr-defined]
+            name not in gui._get_shared_variables() and not gui._bindings()._is_single_client()  # type: ignore[attr-defined]
         ):
             raise AttributeError(f"Variable '{name}' is not available to be accessed in shared callback.")
         if not name.startswith("__") and name not in super().__getattribute__(_GuiState.__attrs[1]):
             raise AttributeError(f"Variable '{name}' is not accessible.")
         with self._notebook_context(gui), self._set_context(gui):
-            encoded_name = gui._bind_var(name)
-            setattr(gui._bindings(), encoded_name, value)
+            encoded_name = gui._bind_var(name)  # type: ignore[attr-defined]
+            setattr(gui._bindings(), encoded_name, value)  # type: ignore[attr-defined]
 
     def __getitem__(self, key: str):
         context = key if key in super().__getattribute__(_GuiState.__attrs[2]) else None
         if context is None:
             gui: "Gui" = super().__getattribute__(_GuiState.__gui_attr)
-            page_ctx = gui._get_page_context(key)
+            page_ctx = gui._get_page_context(key)  # type: ignore[attr-defined]
             context = page_ctx if page_ctx is not None else None
         if context is None:
             raise RuntimeError(f"Can't resolve context '{key}' from state object")
@@ -244,14 +244,14 @@ class _GuiState(State):
     def _set_context(self, gui: "Gui") -> t.ContextManager[None]:
         if (pl_ctx := self._get_placeholder(_GuiState.__placeholder_attrs[1])) is not None:
             self._set_placeholder(_GuiState.__placeholder_attrs[1], None)
-            if pl_ctx != gui._get_locals_context():
-                return gui._set_locals_context(pl_ctx)
+            if pl_ctx != gui._get_locals_context():  # type: ignore[attr-defined]
+                return gui._set_locals_context(pl_ctx)  # type: ignore[attr-defined]
         if len(inspect.stack()) > 1:
             ctx = _get_module_name_from_frame(t.cast(FrameType, t.cast(FrameType, inspect.stack()[2].frame)))
-            current_context = gui._get_locals_context()
+            current_context = gui._get_locals_context()  # type: ignore[attr-defined]
             # ignore context if the current one starts with the new one (to resolve for class modules)
             if ctx != current_context and not current_context.startswith(str(ctx)):
-                return gui._set_locals_context(ctx)
+                return gui._set_locals_context(ctx)  # type: ignore[attr-defined]
         return nullcontext()
 
     def _notebook_context(self, gui: "Gui"):
@@ -284,7 +284,7 @@ class _GuiState(State):
 class _AsyncState(_GuiState):
     def __init__(self, state: State) -> None:
         super().__init__(state.get_gui(), [], [])
-        self._set_placeholder("__state_id", state.get_gui()._get_client_id())
+        self._set_placeholder("__state_id", state.get_gui()._get_client_id())  # type: ignore[attr-defined]
 
     @staticmethod
     def __set_var_in_state(state: State, var_name: str, value: t.Any):

+ 4 - 4
taipy/gui/types.py

@@ -186,13 +186,13 @@ def _get_taipy_type(a_type: PropertyType) -> t.Type[_TaipyBase]:  # noqa: F811
 
 @t.overload
 def _get_taipy_type(  # noqa: F811
-    a_type: t.Optional[t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType]],
-) -> t.Optional[t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType]]: ...
+    a_type: t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType, None],
+) -> t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType, None]: ...
 
 
 def _get_taipy_type(  # noqa: F811
-    a_type: t.Optional[t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType]],
-) -> t.Optional[t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType]]:
+    a_type: t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType, None],
+) -> t.Union[t.Type[_TaipyBase], t.Type[Decimator], PropertyType, None]:
     if a_type is None:
         return None
     if isinstance(a_type, PropertyType) and not isinstance(a_type.value, str):

+ 1 - 1
taipy/gui/utils/_adapter.py

@@ -164,7 +164,7 @@ class _Adapter:
         if label is None:
             return None
         children = self.__get_children(value)
-        return (id, label) if children is None else (id, label, children)  # type: ignore
+        return (id, label) if children is None else (id, label, children)  # type: ignore[return-value]
 
     def __get_id(self, value: t.Any, dig=True) -> str:
         if isinstance(value, str):

+ 11 - 11
taipy/gui/utils/_attributes.py

@@ -18,36 +18,36 @@ if t.TYPE_CHECKING:
 
 def _getscopeattr(gui: "Gui", name: str, *more) -> t.Any:
     if more:
-        return getattr(gui._get_data_scope(), name, more[0])
-    return getattr(gui._get_data_scope(), name)
+        return getattr(gui._get_data_scope(), name, more[0])  # type: ignore[attr-defined]
+    return getattr(gui._get_data_scope(), name)  # type: ignore[attr-defined]
 
 
 def _getscopeattr_drill(gui: "Gui", name: str) -> t.Any:
-    return attrgetter(name)(gui._get_data_scope())
+    return attrgetter(name)(gui._get_data_scope())  # type: ignore[attr-defined]
 
 
 def _setscopeattr(gui: "Gui", name: str, value: t.Any):
-    if gui._is_broadcasting():
-        for scope in gui._get_all_data_scopes().values():
+    if gui._is_broadcasting():  # type: ignore[attr-defined]
+        for scope in gui._get_all_data_scopes().values():  # type: ignore[attr-defined]
             setattr(scope, name, value)
     else:
-        setattr(gui._get_data_scope(), name, value)
+        setattr(gui._get_data_scope(), name, value)  # type: ignore[attr-defined]
 
 
 def _setscopeattr_drill(gui: "Gui", name: str, value: t.Any):
-    if gui._is_broadcasting():
-        for scope in gui._get_all_data_scopes().values():
+    if gui._is_broadcasting():  # type: ignore[attr-defined]
+        for scope in gui._get_all_data_scopes().values():  # type: ignore[attr-defined]
             _attrsetter(scope, name, value)
     else:
-        _attrsetter(gui._get_data_scope(), name, value)
+        _attrsetter(gui._get_data_scope(), name, value)  # type: ignore[attr-defined]
 
 
 def _hasscopeattr(gui: "Gui", name: str) -> bool:
-    return hasattr(gui._get_data_scope(), name)
+    return hasattr(gui._get_data_scope(), name)  # type: ignore[attr-defined]
 
 
 def _delscopeattr(gui: "Gui", name: str):
-    delattr(gui._get_data_scope(), name)
+    delattr(gui._get_data_scope(), name)  # type: ignore[attr-defined]
 
 
 def _attrsetter(obj: object, attr_str: str, value: object) -> None:

+ 5 - 5
taipy/gui/utils/_bindings.py

@@ -43,12 +43,12 @@ class _Bindings:
                 value._update_var = None  # type: ignore[assignment]
             elif isinstance(value, dict):
                 value = _MapDict(value, None)
-            ud.__gui._update_var(name, value)
+            ud.__gui._update_var(name, value)  # type: ignore[attr-defined]
 
         def __getter(ud: _Bindings) -> t.Any:
             value = getattr(ud._get_data_scope(), name)
             if isinstance(value, _MapDict):
-                return _MapDict(value._dict, lambda k, v: ud.__gui._update_var(f"{name}.{k}", v))
+                return _MapDict(value._dict, lambda k, v: ud.__gui._update_var(f"{name}.{k}", v))  # type: ignore[attr-defined]
             else:
                 return value
 
@@ -64,7 +64,7 @@ class _Bindings:
         create = not id
         if create:
             id = f"{datetime.now().strftime('%Y%m%d%H%M%S%f')}-{random()}"
-            self.__gui._send_ws_id(id)
+            self.__gui._send_ws_id(id)  # type: ignore[attr-defined]
         self.__scopes.create_scope(id)
         return id, create
 
@@ -75,10 +75,10 @@ class _Bindings:
         self.__scopes = _DataScopes(self.__gui)
 
     def _get_data_scope(self):
-        return self.__scopes.get_scope(self.__gui._get_client_id())[0]
+        return self.__scopes.get_scope(self.__gui._get_client_id())[0]  # type: ignore[attr-defined]
 
     def _get_data_scope_metadata(self):
-        return self.__scopes.get_scope(self.__gui._get_client_id())[1]
+        return self.__scopes.get_scope(self.__gui._get_client_id())[1]  # type: ignore[attr-defined]
 
     def _get_all_scopes(self):
         return self.__scopes.get_all_scopes()

+ 12 - 12
taipy/gui/utils/_evaluator.py

@@ -125,11 +125,11 @@ class _Evaluator:
                             if lazy_declare and var_name.startswith("__"):
                                 with warnings.catch_warnings(record=True) as warns:
                                     warnings.resetwarnings()
-                                    encoded_var_name = gui._bind_var(var_name)
+                                    encoded_var_name = gui._bind_var(var_name)  # type: ignore[attr-defined]
                                     if next((w for w in warns if w.category is TaipyGuiWarning), None):
-                                        gui._bind_var_val(var_name, None)
+                                        gui._bind_var_val(var_name, None)  # type: ignore[attr-defined]
                             else:
-                                encoded_var_name = gui._bind_var(var_name)
+                                encoded_var_name = gui._bind_var(var_name)  # type: ignore[attr-defined]
                             var_val[var_name] = _getscopeattr_drill(gui, encoded_var_name)
                             var_map[var_name] = encoded_var_name
                         except AttributeError as e:
@@ -147,7 +147,7 @@ class _Evaluator:
     ):
         if expr in self.__expr_to_hash:
             expr_hash = self.__expr_to_hash[expr]
-            gui._bind_var_val(expr_hash, expr_evaluated)
+            gui._bind_var_val(expr_hash, expr_evaluated)  # type: ignore[attr-defined]
             return expr_hash
         if expr_hash is None:
             expr_hash = _get_expr_var_name(expr)
@@ -156,7 +156,7 @@ class _Evaluator:
             # edge case, only a single variable
             expr_hash = f"tpec_{_get_client_var_name(expr)}"
         self.__expr_to_hash[expr] = expr_hash
-        gui._bind_var_val(expr_hash, expr_evaluated)
+        gui._bind_var_val(expr_hash, expr_evaluated)  # type: ignore[attr-defined]
         self.__hash_to_expr[expr_hash] = expr
         for var in var_map.values():
             if var not in self.__global_ctx.keys():
@@ -171,7 +171,7 @@ class _Evaluator:
         for encoded_var_name in var_map.values():
             var_name, module_name = _variable_decode(encoded_var_name)
             # only variables in the main module with be taken into account
-            if module_name is not None and module_name != gui._get_default_module_name():
+            if module_name is not None and module_name != gui._get_default_module_name():  # type: ignore[attr-defined]
                 continue
             if var_name in self.__shared_variable:
                 self.__shared_variable.append(expr_hash)
@@ -248,7 +248,7 @@ class _Evaluator:
             expr_hash = expr if _Evaluator.__EXPR_VALID_VAR_EDGE_CASE.match(expr) else None
             is_edge_case = True
         # validate whether expression has already been evaluated
-        module_name = gui._get_locals_context()
+        module_name = gui._get_locals_context()  # type: ignore[attr-defined]
         not_encoded_expr = expr
         expr = f"TpExPr_{_variable_encode(expr, module_name)}"
         if expr in self.__expr_to_hash and _hasscopeattr(gui, self.__expr_to_hash[expr]):
@@ -258,10 +258,10 @@ class _Evaluator:
             ctx: t.Dict[str, t.Any] = {}
             ctx.update(self.__global_ctx)
             if lambda_expr:
-                ctx.update(gui._get_locals_bind())
+                ctx.update(gui._get_locals_bind())  # type: ignore[attr-defined]
             # entries in var_val are not always seen (NameError) when passed as locals
             ctx.update(var_val)
-            with gui._get_authorization():
+            with gui._get_authorization():  # type: ignore[attr-defined]
                 expr_evaluated = eval(not_encoded_expr if is_edge_case else expr_string, ctx)
         except Exception as e:
             exception_str = not_encoded_expr if is_edge_case else expr_string
@@ -272,7 +272,7 @@ class _Evaluator:
             )
             expr_evaluated = None
         if lambda_expr and callable(expr_evaluated):
-            expr_hash = _get_lambda_id(expr_evaluated, module=module_name)  # type: ignore[reportArgumentType]
+            expr_hash = _get_lambda_id(expr_evaluated, module=module_name)  # type: ignore[arg-type]
         # save the expression if it needs to be re-evaluated
         return self.__save_expression(gui, expr, expr_hash, expr_evaluated, var_map, lambda_expr)
 
@@ -286,7 +286,7 @@ class _Evaluator:
 
         expr_decoded, _ = _variable_decode(expr)
         var_map = self.__expr_to_var_map.get(expr, {})
-        eval_dict = {k: _getscopeattr_drill(gui, gui._bind_var(v)) for k, v in var_map.items()}
+        eval_dict = {k: _getscopeattr_drill(gui, gui._bind_var(v)) for k, v in var_map.items()}  # type: ignore[attr-defined]
         if self._is_expression(expr_decoded):
             expr_string = 'f"' + _variable_decode(expr)[0].replace('"', '\\"') + '"'
         else:
@@ -363,7 +363,7 @@ class _Evaluator:
                 if expr_var_map is None:
                     _warn(f"Something is amiss with expression list for {expr}.")
                 else:
-                    eval_dict = {k: _getscopeattr_drill(gui, gui._bind_var(v)) for k, v in expr_var_map.items()}
+                    eval_dict = {k: _getscopeattr_drill(gui, gui._bind_var(v)) for k, v in expr_var_map.items()}  # type: ignore[attr-defined]
                     if self._is_expression(expr_decoded):
                         expr_string = 'f"' + _variable_decode(expr)[0].replace('"', '\\"') + '"'
                     else:

+ 2 - 2
taipy/gui/utils/_map_dict.py

@@ -31,7 +31,7 @@ class _MapDict(object):
         return self._dict.__len__()
 
     def __length_hint__(self):
-        return self._dict.__length_hint__() # type: ignore[reportAttributeAccessIssue]
+        return self._dict.__length_hint__() # type: ignore[attr-defined]
 
     def __getitem__(self, key):
         value = self._dict.__getitem__(key)
@@ -52,7 +52,7 @@ class _MapDict(object):
         self._dict.__delitem__(key)
 
     def __missing__(self, key):
-        return self._dict.__missing__(key) # type: ignore[reportAttributeAccessIssue]
+        return self._dict.__missing__(key) # type: ignore[attr-defined]
 
     def __iter__(self):
         return self._dict.__iter__()

+ 2 - 2
taipy/gui/utils/chart_config_builder.py

@@ -99,7 +99,7 @@ def __check_dict(values: t.List[t.Any], properties: t.Iterable[_Chart_iprops]) -
 def __get_multiple_indexed_attributes(
     attributes: t.Dict[str, t.Any], names: t.Iterable[str], index: t.Optional[int] = None
 ) -> t.List[t.Optional[str]]:
-    names = names if index is None else [f"{n}[{index}]" for n in names]  # type: ignore
+    names = names if index is None else [f"{n}[{index}]" for n in names]
     return [attributes.get(name) for name in names]
 
 
@@ -214,7 +214,7 @@ def _build_chart_config(  # noqa: C901
     decimators: t.List[t.Optional[str]] = []
     for tr in traces:
         if tr[_Chart_iprops.decimator.value]:
-            cls = gui._get_user_instance(
+            cls = gui._get_user_instance(  # type: ignore[attr-defined]
                 class_name=str(tr[_Chart_iprops.decimator.value]), class_type=PropertyType.decimator.value
             )
             if isinstance(cls, PropertyType.decimator.value):

+ 1 - 1
taipy/gui/utils/varnamefromcontent.py

@@ -18,4 +18,4 @@ if t.TYPE_CHECKING:
 
 
 def _varname_from_content(gui: Gui, content: str) -> t.Optional[str]:
-    return next((k for k, v in gui._get_locals_bind().items() if isinstance(v, str) and v == content), None)
+    return next((k for k, v in gui._get_locals_bind().items() if isinstance(v, str) and v == content), None)  # type: ignore[attr-defined]

+ 34 - 34
taipy/gui/viselements.json

@@ -32,7 +32,7 @@
                     },
                     {
                         "name": "width",
-                        "type": "Union[str,int]",
+                        "type": "Union[str, int]",
                         "default_value": "None",
                         "doc": "The width of the text element, in CSS units."
                     }
@@ -50,7 +50,7 @@
                     {
                         "name": "label",
                         "default_property": true,
-                        "type": "dynamic(Union[str,Icon])",
+                        "type": "dynamic(Union[str, Icon])",
                         "default_value": "\"\"",
                         "doc": "The label displayed in the button."
                     },
@@ -87,7 +87,7 @@
                     },
                     {
                         "name": "width",
-                        "type": "Union[str,int]",
+                        "type": "Union[str, int]",
                         "default_value": "None",
                         "doc": "The width of the button element."
                     }
@@ -172,24 +172,24 @@
                     },
                     {
                         "name": "step",
-                        "type": "dynamic(Union[int,float])",
+                        "type": "dynamic(Union[int, float])",
                         "default_value": "1",
                         "doc": "The increment or decrement applied to the value when the user clicks the arrow buttons."
                     },
                     {
                         "name": "step_multiplier",
-                        "type": "dynamic(Union[int,float])",
+                        "type": "dynamic(Union[int, float])",
                         "default_value": "10",
                         "doc": "The factor by which the step value is multiplied when the user holds the Shift key while clicking the arrow buttons."
                     },
                     {
                         "name": "min",
-                        "type": "dynamic(Union[int,float])",
+                        "type": "dynamic(Union[int, float])",
                         "doc": "The minimum acceptable value.<br/>Values below this threshold are invalid."
                     },
                     {
                         "name": "max",
-                        "type": "dynamic(Union[int,float])",
+                        "type": "dynamic(Union[int, float])",
                         "doc": "The maximum acceptable value.<br/>Values above this threshold are invalid."
                     },
                     {
@@ -212,24 +212,24 @@
                     {
                         "name": "value",
                         "default_property": true,
-                        "type": "dynamic(Union[int,float,str,list[int],list[float],list[str]])",
+                        "type": "dynamic(Union[int, float, str, list[int], list[float], list[str]])",
                         "doc": "The value that is set for this slider.<br/>If this slider is based on a <i>lov</i> then this property can be set to the lov element.<br/>This value can also hold an array of numbers to indicate that the slider reflects a range (within the [<i>min</i>,<i>max</i>] domain) defined by several knobs that the user can set independently.<br/>If this slider is based on a <i>lov</i> then this property can be set to an array of lov elements. The slider is then represented with several knobs, one for each lov value."
                     },
                     {
                         "name": "min",
-                        "type": "Union[int,float]",
+                        "type": "Union[int, float]",
                         "default_value": "0",
                         "doc": "The minimum value.<br/>This is ignored when <i>lov</i> is defined."
                     },
                     {
                         "name": "max",
-                        "type": "Union[int,float]",
+                        "type": "Union[int, float]",
                         "default_value": "100",
                         "doc": "The maximum value.<br/>This is ignored when <i>lov</i> is defined."
                     },
                     {
                         "name": "step",
-                        "type": "Union[int,float]",
+                        "type": "Union[int, float]",
                         "default_value": "1",
                         "doc": "The step value, which is the gap between two consecutive values the slider set. It is a good practice to have (<i>max</i>-<i>min</i>) being divisible by <i>step</i>.<br/>This property is ignored when <i>lov</i> is defined."
                     },
@@ -241,7 +241,7 @@
                     },
                     {
                         "name": "labels",
-                        "type": "Union[bool,dict[str,str]]",
+                        "type": "Union[bool, dict[str, str]]",
                         "doc": "The labels for specific points of the slider.<br/>If set to True, this slider uses the labels of the <i>lov</i> if there are any.<br/>If set to a dictionary, the slider uses the dictionary keys as a <i>lov</i> key or index, and the associated value as the label."
                     },
                     {
@@ -312,7 +312,7 @@
                     },
                     {
                         "name": "width",
-                        "type": "Union[str,int]",
+                        "type": "Union[str, int]",
                         "default_value": "None",
                         "doc": "The width of the element."
                     }
@@ -367,7 +367,7 @@
                     },
                     {
                         "name": "width",
-                        "type": "Union[str,int]",
+                        "type": "Union[str, int]",
                         "default_value": "None",
                         "doc": "The width of the date element."
                     },
@@ -418,7 +418,7 @@
                     },
                     {
                         "name": "width",
-                        "type": "Union[str,int]",
+                        "type": "Union[str, int]",
                         "default_value": "None",
                         "doc": "The width of the time element."
                     }
@@ -468,7 +468,7 @@
                     },
                     {
                         "name": "width",
-                        "type": "Union[str,int]",
+                        "type": "Union[str, int]",
                         "default_value": "None",
                         "doc": "The width of the date_range element."
                     },
@@ -630,7 +630,7 @@
                     },
                     {
                         "name": "columns",
-                        "type": "Union[str,list[str],dict[str,dict[str,str]]]",
+                        "type": "Union[str, list[str], dict[str, dict[str, str]]]",
                         "default_value": "<i>All columns</i>",
                         "doc": "The list of column names to represent.\n<ul>\n<li>str: ;-separated list of column names</li><li>list[str]: list of names</li><li>dict: {\"column_name\": {format: \"format\", index: 1}} if index is specified, it represents the display order of the columns.\nIf not, the list order defines the index</li></ul><br/>If <i>columns</i> is omitted or set to None, all columns of <i>data</i> are represented."
                     },
@@ -646,7 +646,7 @@
                     },
                     {
                         "name": "selected",
-                        "type": "indexed(dynamic(Union[list[int],str]))",
+                        "type": "indexed(dynamic(Union[list[int], str]))",
                         "doc": "The list of the selected point indices  ."
                     },
                     {
@@ -666,7 +666,7 @@
                     },
                     {
                         "name": "line",
-                        "type": "indexed(Union[str,dict[str,Any]])",
+                        "type": "indexed(Union[str, dict[str, Any]])",
                         "doc": "The configuration of the line used for the indicated trace.<br/>See <a href=\"https://plotly.com/javascript/reference/scatter/#scatter-line\">line</a> for more details.<br/>If the value is a string, it must be a dash type or pattern (see <a href=\"https://plotly.com/python/reference/scatter/#scatter-line-dash\">dash style of lines</a> for more details)."
                     },
                     {
@@ -711,13 +711,13 @@
                     },
                     {
                         "name": "width",
-                        "type": "Union[str,int,float]",
+                        "type": "Union[str, int, float]",
                         "default_value": "\"100%\"",
                         "doc": "The width of the chart, in CSS units."
                     },
                     {
                         "name": "height",
-                        "type": "Union[str,int,float]",
+                        "type": "Union[str, int, float]",
                         "doc": "The height of the chart, in CSS units."
                     },
                     {
@@ -819,18 +819,18 @@
                     },
                     {
                         "name": "selected",
-                        "type": "dynamic(Union[list[int],str])",
+                        "type": "dynamic(Union[list[int], str])",
                         "doc": "The list of the indices of the rows to be displayed as selected."
                     },
                     {
                         "name": "page_size_options",
-                        "type": "Union[list[int],str]",
+                        "type": "Union[list[int], str]",
                         "default_value": "[50, 100, 500]",
                         "doc": "The list of available page sizes that users can choose from."
                     },
                     {
                         "name": "columns",
-                        "type": "Union[str,list[str],dict[str,dict[str,Union[str,int]]]]",
+                        "type": "Union[str, list[str], dict[str, dict[str, Union[str, int]]]]",
                         "default_value": "<i>All columns</i>",
                         "doc": "The list of the column names to display.\n<ul>\n<li>str: semicolon (';')-separated list of column names.</li><li>list[str]: the list of column names.</li><li>dict: a dictionary with entries matching: {\"&lt;column_name&gt;\": {\"format\": \"&lt;format&gt;\", \"index\": 1}}.<br/>\nif <i>index</i> is specified, it represents the display order of this column.\nIf <i>index</i> is not specified, the list order defines the index.<br/>\nIf <i>format</i> is specified, it is used for numbers or dates.</li></ul><br/>If <i>columns</i> is omitted or set to None, all columns of <i>data</i> are represented."
                     },
@@ -1044,7 +1044,7 @@
                     },
                     {
                         "name": "lov[<i>column_name</i>]",
-                        "type": "Union[list[str],str]",
+                        "type": "Union[list[str], str]",
                         "doc": "The list of values of the indicated column."
                     },
                     {
@@ -1137,13 +1137,13 @@
                     },
                     {
                         "name": "width",
-                        "type": "Union[str,int]",
+                        "type": "Union[str, int]",
                         "default_value": "\"360px\"",
                         "doc": "The width of the selector, in CSS units."
                     },
                     {
                         "name": "height",
-                        "type": "Union[str,int]",
+                        "type": "Union[str, int]",
                         "doc": "The height of the selector, in CSS units."
                     }
                 ]
@@ -1160,7 +1160,7 @@
                     {
                         "name": "content",
                         "default_property": true,
-                        "type": "dynamic(Union[path,file,URL,ReadableBuffer,None])",
+                        "type": "dynamic(Union[path, file, URL, ReadableBuffer])",
                         "doc": "The content to transfer.<br/>If this is a string, a URL, or a file, then the content is read from this source.<br/>If a readable buffer is provided (such as an array of bytes...), and to prevent the bandwidth from being consumed too much, the way the data is transferred depends on the <i>data_url_max_size</i> parameter of the application configuration (which is set to 50kB by default):\n<ul>\n<li>If the buffer size is smaller than this setting, then the raw content is generated as a data URL, encoded using base64 (i.e. <code>\"data:&lt;mimetype&gt;;base64,&lt;data&gt;\"</code>).</li><li>If the buffer size exceeds this setting, then it is transferred through a temporary file.</li></ul>If this property is set to None, that indicates that dynamic content is generated. Please take a look at the examples below for more details on dynamic generation."
                     },
                     {
@@ -1212,7 +1212,7 @@
                     },
                     {
                         "name": "width",
-                        "type": "Union[str,int]",
+                        "type": "Union[str, int]",
                         "default_value": "None",
                         "doc": "The width of the element."
                     }
@@ -1289,7 +1289,7 @@
                     },
                     {
                         "name": "width",
-                        "type": "Union[str,int]",
+                        "type": "Union[str, int]",
                         "default_value": "None",
                         "doc": "The width of the element."
                     }
@@ -1307,7 +1307,7 @@
                     {
                         "name": "content",
                         "default_property": true,
-                        "type": "dynamic(Union[path,URL,file,ReadableBuffer])",
+                        "type": "dynamic(Union[path, file, URL, ReadableBuffer])",
                         "doc": "The image source.<br/>If a buffer is provided (string, array of bytes...), and in order to prevent the bandwidth to be consumed too much, the way the image data is transferred depends on the <i>data_url_max_size</i> parameter of the application configuration (which is set to 50kB by default):\n<ul>\n<li>If the size of the buffer is smaller than this setting, then the raw content is generated as a\n  data URL, encoded using base64 (i.e. <code>\"data:&lt;mimetype&gt;;base64,&lt;data&gt;\"</code>).</li><li>If the size of the buffer is greater than this setting, then it is transferred through a temporary\n  file.</li></ul>"
                     },
                     {
@@ -1433,13 +1433,13 @@
                     },
                     {
                         "name": "width",
-                        "type": "Union[str,number]",
+                        "type": "Union[str,int,float]",
                         "default_value": "\"20vw\"",
                         "doc": "The width of the metric control, in CSS units"
                     },
                     {
                         "name": "height",
-                        "type": "Union[str,number]",
+                        "type": "Union[str,int,float]",
                         "default_value": "\"20vh\"",
                         "doc": "The height of the metric control, in CSS units"
                     },
@@ -2079,7 +2079,7 @@
                     {
                         "name": "on_close",
                         "type": "Union[str, Callable]",
-                        "doc": "A function or the name of a function that is triggered when this pane is closed (if the user clicks outside of it or presses the Esc key).<br/>This function is invoked with the following parameters:<ul>\n<li>state (<code>State^</code>): the state instance.</li><li>id (optional[str]): the identifier of the <i>close</i> button if it has one.</li></ul><br/>If this property is not set, no function is called when this pane is closed.",
+                        "doc": "A function or the name of a function that is triggered when this pane is closed (if the user clicks outside of it or presses the Esc key).<br/>This function is invoked with the following parameters:<ul>\n<li>state (<code>State^</code>): the state instance.</li><li>id (Optional[str]): the identifier of the <i>close</i> button if it has one.</li></ul><br/>If this property is not set, no function is called when this pane is closed.",
                         "signature": [
                             [
                                 "state",

+ 2 - 2
taipy/gui_core/_adapters.py

@@ -50,7 +50,7 @@ from .filters import DataNodeFilter, ParamType, ScenarioFilter, _Filter
 # prevent gui from trying to push scenario instances to the front-end
 class _GuiCoreDoNotUpdate(_DoNotUpdate):
     def __repr__(self):
-        return self.get_label() if hasattr(self, "get_label") else super().__repr__()  # type: ignore[reportAttributeAccessIssue]
+        return self.get_label() if hasattr(self, "get_label") else super().__repr__()  # type: ignore[attr-defined]
 
 
 class _EntityType(Enum):
@@ -416,7 +416,7 @@ class _GuiCoreProperties(ABC):
         return {}
 
     def get(self):
-        data = super().get()  # type: ignore[reportAttributeAccessIssue]
+        data = super().get()  # type: ignore[attr-defined]
         if _is_boolean(data):
             if _is_true(data):
                 data = self.get_default_list()

+ 36 - 36
taipy/gui_core/_context.py

@@ -59,7 +59,7 @@ from taipy.core.reason import ReasonCollection
 from taipy.core.submission.submission_status import SubmissionStatus
 from taipy.gui import Gui, State, get_state_id
 from taipy.gui._warnings import _warn
-from taipy.gui.gui import _DoNotUpdate
+from taipy.gui.utils import _DoNotUpdate
 from taipy.gui.utils._map_dict import _MapDict
 
 from ._adapters import (
@@ -101,12 +101,12 @@ class _GuiCoreContext(CoreEventConsumerBase):
         # lazy_start
         self.__started = False
         # Gui event listener
-        gui._add_event_listener("authorization", self._auth_listener, with_state=True)
+        gui._add_event_listener("authorization", self._auth_listener, with_state=True) # type: ignore
         # super
         super().__init__(reg_id, reg_queue)
 
     def on_user_init(self, state: State):
-        self.gui._fire_event("authorization", get_state_id(state), {})
+        self.gui._fire_event("authorization", get_state_id(state), {}) # type: ignore
 
     def __lazy_start(self):
         if self.__started:
@@ -117,7 +117,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
     def process_event(self, event: Event):
         self.__lazy_start()
         if event.entity_type is EventEntityType.SCENARIO:
-            with self.gui._get_authorization(system=True):
+            with self.gui._get_authorization(system=True): # type: ignore
                 self.scenario_refresh(
                     event.entity_id
                     if event.operation is EventOperation.DELETION or is_readable(t.cast(ScenarioId, event.entity_id))
@@ -126,7 +126,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
         elif event.entity_type is EventEntityType.SEQUENCE and event.entity_id:
             sequence = None
             try:
-                with self.gui._get_authorization(system=True):
+                with self.gui._get_authorization(system=True): # type: ignore
                     sequence = (
                         core_get(event.entity_id)
                         if event.operation is not EventOperation.DELETION
@@ -153,7 +153,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
             )
 
     def broadcast_core_changed(self, payload: t.Dict[str, t.Any], client_id: t.Optional[str] = None):
-        self.gui._broadcast(_GuiCoreContext._CORE_CHANGED_NAME, payload, client_id)
+        self.gui._broadcast(_GuiCoreContext._CORE_CHANGED_NAME, payload, client_id) # type: ignore
 
     def scenario_refresh(self, scenario_id: t.Optional[str]):
         with self.lock:
@@ -194,8 +194,8 @@ class _GuiCoreContext(CoreEventConsumerBase):
                 else:
                     last_client_status.submission_status = new_status
 
-            if client_id:= submission.properties.get("client_id"):
-                with self.gui._get_authorization(client_id):
+            if client_id := submission.properties.get("client_id"):
+                with self.gui._get_authorization(client_id): # type: ignore
                     if payload is not None:
                         running_tasks = {}
                         for job in submission.jobs:
@@ -339,8 +339,8 @@ class _GuiCoreContext(CoreEventConsumerBase):
             match_case = fd.get("matchCase", False) is not False
             customs = CustomScenarioFilter._get_custom(col)
             if customs:
-                with self.gui._set_locals_context(customs[0] or None):
-                    fn = self.gui._get_user_function(customs[1])
+                with self.gui._set_locals_context(customs[0] or None): # type: ignore
+                    fn = self.gui._get_user_function(customs[1]) # type: ignore
                     if callable(fn):
                         col = fn
             if (
@@ -474,10 +474,10 @@ class _GuiCoreContext(CoreEventConsumerBase):
             gui = state.get_gui()
             try:
                 on_creation = args[0] if isinstance(args[0], str) else None
-                on_creation_function = gui._get_user_function(on_creation) if on_creation else None
+                on_creation_function = gui._get_user_function(on_creation) if on_creation else None # type: ignore
                 if callable(on_creation_function) and on_creation:
                     try:
-                        res = gui._call_function_with_state(
+                        res = gui._call_function_with_state( # type: ignore
                             on_creation_function,
                             [
                                 id,
@@ -501,7 +501,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
                             state.assign(error_var, f"{res}")
                             return
                     except Exception as e:  # pragma: no cover
-                        if not gui._call_on_exception(on_creation, e):
+                        if not gui._call_on_exception(on_creation, e): # type: ignore
                             _warn(f"on_creation(): Exception raised in '{on_creation}()'", e)
                         state.assign(
                             error_var,
@@ -527,7 +527,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
             finally:
                 self.scenario_refresh(scenario_id)
                 if (scenario or user_scenario) and (sel_scenario_var := args[1] if isinstance(args[1], str) else None):
-                    self.gui._update_var(
+                    self.gui._update_var( # type: ignore
                         sel_scenario_var[6:] if sel_scenario_var.startswith("_TpLv_") else sel_scenario_var,
                         scenario or user_scenario,
                         on_change=args[2],
@@ -632,10 +632,10 @@ class _GuiCoreContext(CoreEventConsumerBase):
                 submission_entity = core_submit(
                     entity,
                     on_submission=on_submission,
-                    client_id=self.gui._get_client_id(),
-                    module_context=self.gui._get_locals_context(),
+                    client_id=self.gui._get_client_id(), # type: ignore
+                    module_context=self.gui._get_locals_context(), # type: ignore
                 )
-                client_status = _ClientStatus(self.gui._get_client_id(), None)
+                client_status = _ClientStatus(self.gui._get_client_id(), None) # type: ignore
                 with self.submissions_lock:
                     self.client_submission[submission_entity.id] = client_status
                 if Config.core.mode == "development":
@@ -662,8 +662,8 @@ class _GuiCoreContext(CoreEventConsumerBase):
             match_case = fd.get("matchCase", False) is not False
             customs = CustomScenarioFilter._get_custom(col)
             if customs:
-                with self.gui._set_locals_context(customs[0] or None):
-                    fn = self.gui._get_user_function(customs[1])
+                with self.gui._set_locals_context(customs[0] or None):  # type: ignore
+                    fn = self.gui._get_user_function(customs[1])  # type: ignore
                     if callable(fn):
                         col = fn
             if (
@@ -742,7 +742,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
 
     def get_datanodes_tree(
         self,
-        scenarios: t.Optional[t.Union[Scenario, t.List[Scenario]]],
+        scenarios: t.Union[Scenario, t.List[Scenario], None],
         datanodes: t.Optional[t.List[DataNode]],
         filters: t.Optional[t.List[t.Dict[str, t.Any]]],
         sorts: t.Optional[t.List[t.Dict[str, t.Any]]],
@@ -984,9 +984,9 @@ class _GuiCoreContext(CoreEventConsumerBase):
         if isinstance(entity, DataNode):
             try:
                 if lock:
-                    entity.lock_edit(self.gui._get_client_id())
+                    entity.lock_edit(self.gui._get_client_id()) # type: ignore
                 else:
-                    entity.unlock_edit(self.gui._get_client_id())
+                    entity.unlock_edit(self.gui._get_client_id()) # type: ignore
                 _GuiCoreContext.__assign_var(state, error_var, "")
             except Exception as e:
                 _GuiCoreContext.__assign_var(state, error_var, f"Error locking Data node. {e}")
@@ -1092,7 +1092,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
                     else float(val)
                     if data.get("type") == "float"
                     else data.get("value"),
-                    editor_id=self.gui._get_client_id(),
+                    editor_id=self.gui._get_client_id(), # type: ignore
                     comment=t.cast(str, data.get(_GuiCoreContext.__PROP_ENTITY_COMMENT)),
                 )
                 _GuiCoreContext.__assign_var(state, error_var, "")
@@ -1180,7 +1180,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
                 if new_data is not None:
                     datanode.write(
                         new_data,
-                        editor_id=self.gui._get_client_id(),
+                        editor_id=self.gui._get_client_id(), # type: ignore
                         comment=user_data.get(_GuiCoreContext.__PROP_ENTITY_COMMENT),
                     )
                     _GuiCoreContext.__assign_var(state, error_var, "")
@@ -1205,7 +1205,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
     def get_data_node_tabular_data(self, id: str):
         self.__lazy_start()
         if id and is_readable(t.cast(DataNodeId, id)) and (dn := core_get(id)) and isinstance(dn, DataNode):
-            if dn.is_ready_for_reading or (dn.edit_in_progress and dn.editor_id == self.gui._get_client_id()):
+            if dn.is_ready_for_reading or (dn.edit_in_progress and dn.editor_id == self.gui._get_client_id()): # type: ignore
                 try:
                     value = self.__read_tabular_data(dn)
                     if _GuiCoreDatanodeAdapter._is_tabular_data(dn, value):
@@ -1217,11 +1217,11 @@ class _GuiCoreContext(CoreEventConsumerBase):
     def get_data_node_tabular_columns(self, id: str):
         self.__lazy_start()
         if id and is_readable(t.cast(DataNodeId, id)) and (dn := core_get(id)) and isinstance(dn, DataNode):
-            if dn.is_ready_for_reading or (dn.edit_in_progress and dn.editor_id == self.gui._get_client_id()):
+            if dn.is_ready_for_reading or (dn.edit_in_progress and dn.editor_id == self.gui._get_client_id()): # type: ignore
                 try:
                     value = self.__read_tabular_data(dn)
                     if _GuiCoreDatanodeAdapter._is_tabular_data(dn, value):
-                        return self.gui._tbl_cols(
+                        return self.gui._tbl_cols( # type: ignore
                             True, True, "{}", json.dumps({"data": "tabular_data"}), tabular_data=value
                         )
                 except Exception:
@@ -1231,9 +1231,9 @@ class _GuiCoreContext(CoreEventConsumerBase):
     def get_data_node_chart_config(self, id: str):
         self.__lazy_start()
         if id and is_readable(t.cast(DataNodeId, id)) and (dn := core_get(id)) and isinstance(dn, DataNode):
-            if dn.is_ready_for_reading or (dn.edit_in_progress and dn.editor_id == self.gui._get_client_id()):
+            if dn.is_ready_for_reading or (dn.edit_in_progress and dn.editor_id == self.gui._get_client_id()): # type: ignore
                 try:
-                    return self.gui._chart_conf(
+                    return self.gui._chart_conf( # type: ignore
                         True,
                         True,
                         "{}",
@@ -1249,7 +1249,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
         args = payload.get("args")
         if args is None or not isinstance(args, list) or len(args) < 2:
             return
-        on_action_function = self.gui._get_user_function(args[1]) if args[1] else None
+        on_action_function = self.gui._get_user_function(args[1]) if args[1] else None # type: ignore
         if callable(on_action_function):
             try:
                 entity = (
@@ -1257,12 +1257,12 @@ class _GuiCoreContext(CoreEventConsumerBase):
                     if (reason := is_readable(t.cast(ScenarioId, args[0])))
                     else f"{args[0]} is not readable: {_get_reason(reason)}"
                 )
-                self.gui._call_function_with_state(
+                self.gui._call_function_with_state( # type: ignore
                     on_action_function,
                     [entity],
                 )
             except Exception as e:
-                if not self.gui._call_on_exception(args[1], e):
+                if not self.gui._call_on_exception(args[1], e): # type: ignore
                     _warn(f"dag.on_action(): Exception raised in '{args[1]}()' with '{args[0]}'", e)
         elif args[1]:
             _warn(f"dag.on_action(): Invalid function '{args[1]}()'.")
@@ -1280,7 +1280,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
                 if act_payload.get("action") == "export":
                     if reason := dn.is_downloadable():
                         path = dn._get_downloadable_path()
-                        self.gui._download(Path(path), dn_id)
+                        self.gui._download(Path(path), dn_id) # type: ignore
                     else:
                         state.assign(
                             error_id,
@@ -1289,12 +1289,12 @@ class _GuiCoreContext(CoreEventConsumerBase):
                         )
                 else:
                     checker_name = act_payload.get("upload_check")
-                    checker = self.gui._get_user_function(checker_name) if checker_name else None
+                    checker = self.gui._get_user_function(checker_name) if checker_name else None # type: ignore
                     if not (
                         reason := dn._upload(
                             act_payload.get("path", ""),
                             t.cast(t.Callable[[str, t.Any], bool], checker) if callable(checker) else None,
-                            editor_id=self.gui._get_client_id(),
+                            editor_id=self.gui._get_client_id(), # type: ignore
                             comment=None,
                         )
                     ):
@@ -1306,7 +1306,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
             state.assign(error_id, reason.reasons)
 
     def _auth_listener(self, state: State, client_id: t.Optional[str], payload: t.Dict[str, t.Any]):
-        self.gui._broadcast(
+        self.gui._broadcast( # type: ignore
             _GuiCoreContext._AUTH_CHANGED_NAME,
             payload.get("override", "")
             if (reason := can_create())

+ 2 - 2
tests/gui/gui_specific/test_gui.py

@@ -107,5 +107,5 @@ def test_on_action_call(gui:Gui):
 
     gui.run(run_server=False)
     with gui.get_flask_app().app_context():
-        gui._Gui__on_action(an_id, a_non_action_payload) # type: ignore[reportAttributeAccessIssue]
-        gui._Gui__on_action(an_id, an_action_payload) # type: ignore[reportAttributeAccessIssue]
+        gui._Gui__on_action(an_id, a_non_action_payload) # type: ignore[attr-defined]
+        gui._Gui__on_action(an_id, an_action_payload) # type: ignore[attr-defined]

+ 48 - 18
tools/gui/generate_pyi.py

@@ -13,14 +13,17 @@ import json
 import os
 import re
 import sys
-from typing import Any, Dict, List
+from typing import Any, Dict, List, Union, get_args, get_origin
 
 from markdownify import markdownify
 
 __RE_INDEXED_PROPERTY = re.compile(r"^([\w_]+)\[(<\w+>)?([\w]+)(</\w+>)?\]$")
 
-# Make sure we can import the mandatory packages
+# Script should be located in <taipy_root>/tools
 script_dir = os.path.dirname(os.path.realpath(__file__))
+# Move to <taipy_root>
+os.chdir(os.path.dirname(os.path.dirname(script_dir)))
+# Make sure we can import the mandatory packages
 if not os.path.isdir(os.path.abspath(os.path.join(script_dir, "taipy"))):
     sys.path.append(os.path.abspath(os.path.join(script_dir, os.pardir, os.pardir)))
 
@@ -44,7 +47,7 @@ gui_config = "".join(
 )
 
 replaced_content = ""
-with open(gui_pyi_file, "r") as file:
+with open(gui_pyi_file, "r", encoding="utf-8") as file:
     for line in file:
         if "def run(" in line:
             replace_str = line[line.index(", run_server") : (line.index("**kwargs") + len("**kwargs"))]
@@ -52,15 +55,20 @@ with open(gui_pyi_file, "r") as file:
             line = line.replace(replace_str, gui_config)
         replaced_content += line
 
-with open(gui_pyi_file, "w") as write_file:
+with open(gui_pyi_file, "w", encoding="utf-8") as write_file:
     write_file.write(replaced_content)
 
 # ##################################################################################################
 # Generate Page Builder pyi file (gui/builder/__init__.pyi)
 # ##################################################################################################
+# Types that appear in viselements.json
+from taipy.gui import Icon  # noqa: E402
+from taipy.core import Cycle, DataNode, Job, Scenario  # noqa: E402
+from datetime import datetime
+
 # Read the version
 current_version = "latest"
-with open("./taipy/gui/version.json", "r") as vfile:
+with open("./taipy/gui/version.json", "r", encoding="utf-8") as vfile:
     version = json.load(vfile)
     if "dev" in version.get("ext", ""):
         current_version = "develop"
@@ -74,12 +82,12 @@ builder_pyi_file = f"{builder_py_file}i"
 controls: Dict[str, List] = {}
 blocks: Dict[str, List] = {}
 undocumented: Dict[str, List] = {}
-with open("./taipy/gui/viselements.json", "r") as file:
+with open("./taipy/gui/viselements.json", "r", encoding="utf-8") as file:
     viselements: Dict[str, List] = json.load(file)
     controls[""] = viselements.get("controls", [])
     blocks[""] = viselements.get("blocks", [])
     undocumented[""] = viselements.get("undocumented", [])
-with open("./taipy/gui_core/viselements.json", "r") as file:
+with open("./taipy/gui_core/viselements.json", "r", encoding="utf-8") as file:
     core_viselements: Dict[str, List] = json.load(file)
     controls['if find_spec("taipy.core"):'] = core_viselements.get("controls", [])
     blocks['if find_spec("taipy.core"):'] = core_viselements.get("blocks", [])
@@ -87,7 +95,7 @@ with open("./taipy/gui_core/viselements.json", "r") as file:
 
 os.system(f"pipenv run stubgen {builder_py_file} --no-import --parse-only --export-less -o ./")
 
-with open(builder_pyi_file, "a") as file:
+with open(builder_pyi_file, "a", encoding="utf-8") as file:
     file.write("from datetime import datetime\n")
     file.write("from importlib.util import find_spec\n")
     file.write("from typing import Any, Callable, Optional, Union\n")
@@ -132,7 +140,7 @@ def resolve_inherit(
     return properties
 
 
-def format_as_parameter(property: Dict[str, str]):
+def format_as_parameter(property: Dict[str, str], element_name: str):
     name = property["name"]
     if match := __RE_INDEXED_PROPERTY.match(name):
         name = f"{match.group(1)}__{match.group(3)}"
@@ -147,14 +155,27 @@ def format_as_parameter(property: Dict[str, str]):
         property["dynamic"] = " (dynamic)"
     else:
         property["dynamic"] = ""
-    if type == "Callback" or type == "Function":
-        type = "Callable"
-    else:
-        type = re.sub(r"((plotly|taipy)\.[\w\.]*)", r'"\1"', type)
     default_value = property.get("default_value", None)
+    type, _ = re.subn(r"\bCallable|Callback|Function\b", "callable", type)
+    type = re.sub(r"((plotly|taipy)\.[\w\.]*)", r'"\1"', type)
+    try:
+        type_desc = eval(type)
+        if get_origin(type_desc) is Union:
+            types = get_args(type_desc)
+            if not any(t.__name__ in ["str", "Any"] for t in types):
+                type = type.rpartition("]")
+                type = type[0] + ", str]"
+        elif hasattr(type_desc, "__name__") and type_desc.__name__ not in ["str", "Any"]:
+            type = f"Union[{type}, str]"
+    except NameError:
+        print(f"WARNING - Couldn't parse type '{type}' in {element_name}.{name}")
+    
     if default_value is None or default_value == "None":
         default_value = " = None"
-        if type:
+        if type.startswith("Union["):
+            type = type.rpartition("]")
+            type = ": " + type[0] + ", None]"
+        else:
             type = f": Optional[{type}]"
     else:
         try:
@@ -164,7 +185,10 @@ def format_as_parameter(property: Dict[str, str]):
                 type = f": {type}"
         except Exception:
             default_value = " = None"
-            if type:
+            if type.startswith("Union["):
+                type = type.rpartition("]")
+                type = ": " + type[0] + ", None]"
+            else:
                 type = f": Optional[{type}]"
     return f"{name}{type}{default_value}"
 
@@ -209,7 +233,7 @@ def generate_elements(elements_by_prefix: Dict[str, List], base_class: str):
         indent = ""
         if prefix:
             indent = "    "
-            with open(builder_pyi_file, "a") as file:
+            with open(builder_pyi_file, "a", encoding="utf-8") as file:
                 file.write(prefix + "\n")
         for element in elements:
             name = element[0]
@@ -228,7 +252,10 @@ def generate_elements(elements_by_prefix: Dict[str, List], base_class: str):
             # Remove hidden properties
             properties = [p for p in properties if not p.get("hide", False)]
             # Generate function parameters
-            properties_decl = [format_as_parameter(p) for p in properties]
+            properties_decl = [format_as_parameter(p, name) for p in properties]
+            # Manually add the 'inline' property for the text control
+            if name == "text":
+                properties_decl.append("inline: bool = False")
             # Generate properties doc
             for property in properties:
                 if "default_property" in property and property["default_property"] is True:
@@ -241,10 +268,13 @@ def generate_elements(elements_by_prefix: Dict[str, List], base_class: str):
             for property in property_list:
                 property_doc = build_doc(name, property)
                 properties_doc += property_doc
+            if name == "text":
+                properties_doc += ("inline\n  If True, the text is created next to "
+                                 + "the previous element and not on a new line.\n\n")
             if len(properties_decl) > 1:
                 properties_decl.insert(1, "*")
             # Append element to __init__.pyi
-            with open(builder_pyi_file, "a") as file:
+            with open(builder_pyi_file, "a", encoding="utf-8") as file:
                 file.write(
                     element_template(
                         name,