From 39f5a454c8b716aab80d539b8f92ddb528952f77 Mon Sep 17 00:00:00 2001
From: Fabien Lelaquais <86590727+FabienLelaquais@users.noreply.github.com>
Date: Sat, 10 Aug 2024 12:23:21 +0200
Subject: [PATCH] Improve content of generated pyi for the Page Builder API
(#1653)
- Change Page Builder API pyi generation script
- Updated actions
- Details in element examples
---
.github/actions/gui-test/pyi/action.yml | 2 +-
.../build-and-release-single-package.yml | 2 +-
.github/workflows/build-and-release.yml | 2 +-
doc/gui/examples/controls/chat-discuss.py | 3 +-
doc/gui/examples/controls/metric-color-map.py | 30 +--
doc/gui/examples/controls/metric-layout.py | 2 +-
doc/gui/examples/controls/metric-simple.py | 9 +-
taipy/gui/viselements.json | 122 +++++-----
tools/gui/builder/block.txt | 8 -
tools/gui/builder/control.txt | 8 -
tools/gui/generate_pyi.py | 218 ++++++++++--------
11 files changed, 209 insertions(+), 197 deletions(-)
delete mode 100644 tools/gui/builder/block.txt
delete mode 100644 tools/gui/builder/control.txt
diff --git a/.github/actions/gui-test/pyi/action.yml b/.github/actions/gui-test/pyi/action.yml
index a4ddddfeab..e80c9e7912 100644
--- a/.github/actions/gui-test/pyi/action.yml
+++ b/.github/actions/gui-test/pyi/action.yml
@@ -8,7 +8,7 @@ runs:
run: pipenv run pip install mypy black isort
- name: Generate pyi
shell: bash
- run: cp tools/gui/generate_pyi.py pyi_temp.py && pipenv run python pyi_temp.py && rm pyi_temp.py
+ run: pipenv run python tools/gui/generate_pyi.py
- name: Cleanup any untracked files
shell: bash
run: git clean -f
diff --git a/.github/workflows/build-and-release-single-package.yml b/.github/workflows/build-and-release-single-package.yml
index 2a4a7cac57..87159a7e29 100644
--- a/.github/workflows/build-and-release-single-package.yml
+++ b/.github/workflows/build-and-release-single-package.yml
@@ -128,7 +128,7 @@ jobs:
- name: Generate GUI pyi file
if: github.event.inputs.target_package == 'gui'
run: |
- cp tools/gui/generate_pyi.py pyi_temp.py && pipenv run python pyi_temp.py && rm pyi_temp.py
+ pipenv run python tools/gui/generate_pyi.py
- name: Build frontends
if: github.event.inputs.target_package == 'gui'
diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml
index 978cd924cf..76fdb9d605 100644
--- a/.github/workflows/build-and-release.yml
+++ b/.github/workflows/build-and-release.yml
@@ -126,7 +126,7 @@ jobs:
- name: Generate GUI pyi file
if: matrix.package == 'gui'
run: |
- cp tools/gui/generate_pyi.py pyi_temp.py && pipenv run python pyi_temp.py && rm pyi_temp.py
+ pipenv run python tools/gui/generate_pyi.py
- name: Build frontends
if: matrix.package == 'gui'
diff --git a/doc/gui/examples/controls/chat-discuss.py b/doc/gui/examples/controls/chat-discuss.py
index 858962fa94..180ba3da71 100644
--- a/doc/gui/examples/controls/chat-discuss.py
+++ b/doc/gui/examples/controls/chat-discuss.py
@@ -19,12 +19,13 @@
# incognito windows so a given user's context is not reused.
# -----------------------------------------------------------------------------------------
from os import path
+from typing import Union
from taipy.gui import Gui, Icon
from taipy.gui.gui_actions import navigate, notify
username = ""
-users: list[str | Icon] = []
+users: list[Union[str, Icon]] = []
messages: list[tuple[str, str, str]] = []
Gui.add_shared_variables("messages", "users")
diff --git a/doc/gui/examples/controls/metric-color-map.py b/doc/gui/examples/controls/metric-color-map.py
index c33c5979fe..2735bbcc05 100644
--- a/doc/gui/examples/controls/metric-color-map.py
+++ b/doc/gui/examples/controls/metric-color-map.py
@@ -15,23 +15,23 @@
# -----------------------------------------------------------------------------------------
from taipy.gui import Gui
-# color_map = {
-# # 0-20 - Let Taipy decide
-# # 20-40 - red
-# 20: "red",
-# # 40-60 - Let Taipy decide
-# 40: None,
-# # 60-80 - blue
-# 60: "blue",
-# # 80-100 - Let Taipy decide
-# 80: None
-# }
-
-value = 50
-color_map = {20: "red", 40: None, 60: "blue", 80: None}
+# Color wavelength
+color_wl = 530
+# Color ranges by wavelength
+color_map = {
+ 200: None,
+ 380: "violet",
+ 435: "blue",
+ 500: "cyan",
+ 520: "green",
+ 565: "yellow",
+ 590: "orange",
+ 625: "red",
+ 740: None,
+}
page = """
-<|{value}|metric|color_map={color_map}|>
+<|{color_wl}|metric|color_map={color_map}|format=%d nm|min=200|max=800|bar_color=gray|>
"""
Gui(page).run()
diff --git a/doc/gui/examples/controls/metric-layout.py b/doc/gui/examples/controls/metric-layout.py
index 33c220901d..09451a8dd0 100644
--- a/doc/gui/examples/controls/metric-layout.py
+++ b/doc/gui/examples/controls/metric-layout.py
@@ -15,7 +15,7 @@
# -----------------------------------------------------------------------------------------
from taipy.gui import Gui
-value = 50
+value = 45
# The layout object reference can be found in Plotly's documentation:
# https://plotly.com/python/reference/layout/
layout = {
diff --git a/doc/gui/examples/controls/metric-simple.py b/doc/gui/examples/controls/metric-simple.py
index 0448f89428..ed4793a1fb 100644
--- a/doc/gui/examples/controls/metric-simple.py
+++ b/doc/gui/examples/controls/metric-simple.py
@@ -15,13 +15,12 @@
# -----------------------------------------------------------------------------------------
from taipy.gui import Gui
-value = 50
-max_value = 150
-delta_value = 20
-threshold = 100
+value = 72
+delta = 15
+threshold = 60
page = """
-<|{value}|metric|max={max_value}|delta={delta_value}|threshold={threshold}|>
+<|{value}|metric|delta={delta}|threshold={threshold}|>
"""
Gui(page).run()
diff --git a/taipy/gui/viselements.json b/taipy/gui/viselements.json
index c0fcdbbb9b..9b392d6d4a 100644
--- a/taipy/gui/viselements.json
+++ b/taipy/gui/viselements.json
@@ -44,14 +44,14 @@
{
"name": "label",
"default_property": true,
- "type": "dynamic(str|Icon)",
+ "type": "dynamic(Union[str,Icon])",
"default_value": "\"\"",
"doc": "The label displayed in the button."
},
{
"name": "on_action",
"type": "Callback",
- "doc": "The name of a function that is triggered when the button is pressed.
The parameters of that function are all optional:\n
\n- state (
State^
): the state instance. \n- id (str|None): the identifier of the button.
\n- payload (dict): a dictionary that contains the key \"action\" set to the name of the action that triggered this callback.
\n
",
+ "doc": "The name of a function that is triggered when the button is pressed.
The parameters of that function are all optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the button it it has one.
\n- payload (dict): a dictionary that contains the key \"action\" set to the name of the action that triggered this callback.
\n
",
"signature": [
[
"state",
@@ -142,24 +142,24 @@
},
{
"name": "step",
- "type": "dynamic(int|float)",
+ "type": "dynamic(Union[int,float])",
"default_value": "1",
"doc": "The amount by which the value is incremented or decremented when the user clicks one of the arrow buttons."
},
{
"name": "step_multiplier",
- "type": "dynamic(int|float)",
+ "type": "dynamic(Union[int,float])",
"default_value": "10",
"doc": "A factor that multiplies step when the user presses the Shift key while clicking one of the arrow buttons."
},
{
"name": "min",
- "type": "dynamic(int|float)",
+ "type": "dynamic(Union[int,float])",
"doc": "The minimum value to accept for this input."
},
{
"name": "max",
- "type": "dynamic(int|float)",
+ "type": "dynamic(Union[int,float])",
"doc": "The maximum value to accept for this input."
}
]
@@ -176,26 +176,26 @@
{
"name": "value",
"default_property": true,
- "type": "dynamic(int|float|int[]|float[]|str|str[])",
+ "type": "dynamic(Union[int,float,str,list[int],list[float],list[str]])",
"doc": "The value that is set for this slider.
If this slider is based on a lov then this property can be set to the lov element.
This value can also hold an array of numbers to indicate that the slider reflects a range (within the [min,max] domain) defined by several knobs that the user can set independently.
If this slider is based on a lov 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": "int|float",
+ "type": "Union[int,float]",
"default_value": "0",
"doc": "The minimum value.
This is ignored when lov is defined."
},
{
"name": "max",
- "type": "int|float",
+ "type": "Union[int,float]",
"default_value": "100",
"doc": "The maximum value.
This is ignored when lov is defined."
},
{
"name": "step",
- "type": "int|float",
+ "type": "Union[int,float]",
"default_value": "1",
- "doc": "The step value: the gap between two consecutive values the slider set. It is a good practice to have (max-min) being divisible by step.
This property is ignored when lov is defined."
+ "doc": "The step value, which is the gap between two consecutive values the slider set. It is a good practice to have (max-min) being divisible by step.
This property is ignored when lov is defined."
},
{
"name": "text_anchor",
@@ -205,7 +205,7 @@
},
{
"name": "labels",
- "type": "bool|dict",
+ "type": "Union[bool,dict[str,str]]",
"doc": "The labels for specific points of the slider.
If set to True, this slider uses the labels of the lov if there are any.
If set to a dictionary, the slider uses the dictionary keys as a lov key or index, and the associated value as the label."
},
{
@@ -501,7 +501,7 @@
{
"name": "on_range_change",
"type": "Callback",
- "doc": "The callback function that is invoked when the visible part of the x axis changes.
The function receives three parameters:\n\n- state (
State^
): the state instance. \n- id (str|None): the identifier of the chart control.
\n- payload (dict[str, any]): the full details on this callback's invocation, as emitted by Plotly.
\n
",
+ "doc": "The callback function that is invoked when the visible part of the x axis changes.
The function receives three parameters:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the chart control if it has one.
\n- payload (dict[str, any]): the full details on this callback's invocation, as emitted by Plotly.
\n
",
"signature": [
[
"state",
@@ -519,7 +519,7 @@
},
{
"name": "columns",
- "type": "str|list[str]|dict[str, dict[str, str]]",
+ "type": "Union[str,list[str],dict[str,dict[str,str]]]",
"default_value": "All columns",
"doc": "The list of column names\n\n- str: ;-separated list of column names
\n- list[str]: list of names
\n- 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
\n
"
},
@@ -535,7 +535,7 @@
},
{
"name": "selected",
- "type": "indexed(dynamic(list[int]|str))",
+ "type": "indexed(dynamic(Union[list[int],str]))",
"doc": "The list of the selected point indices ."
},
{
@@ -555,7 +555,7 @@
},
{
"name": "line",
- "type": "indexed(str|dict[str, any])",
+ "type": "indexed(Union[str,dict[str,any]])",
"doc": "The configuration of the line used for the indicated trace.
See line for details.
If the value is a string, it must be a dash type or pattern (see dash style of lines for details)."
},
{
@@ -600,13 +600,13 @@
},
{
"name": "width",
- "type": "str|int|float",
+ "type": "Union[str,int,float]",
"default_value": "\"100%\"",
"doc": "The width of this chart, in CSS units."
},
{
"name": "height",
- "type": "str|int|float",
+ "type": "Union[str,int,float]",
"doc": "The height of this chart, in CSS units."
},
{
@@ -689,18 +689,18 @@
},
{
"name": "selected",
- "type": "dynamic(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": "list[int]|str",
- "default_value": "[50, 100, 500]",
+ "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": "str|list[str]|dict[str, dict[str, str|int]]",
+ "type": "Union[str,list[str],dict[str,dict[str,Union[str,int]]]]",
"default_value": "shows all columns when empty",
"doc": "The list of the column names to display.\n\n- str: Semicolon (';')-separated list of column names.
\n- list[str]: The list of column names.
\n- dict: A dictionary with entries matching: {\"col name\": {format: \"format\", index: 1}}.
\nif index is specified, it represents the display order of the columns.\nIf index is not specified, the list order defines the index.
\nIf format is specified, it is used for numbers or dates. \n
"
},
@@ -885,12 +885,12 @@
},
{
"name": "lov[column_name]",
- "type": "list[str]|str",
+ "type": "Union[list[str],str]",
"doc": "The list of values of the indicated column."
},
{
"name": "downloadable",
- "type": "boolean",
+ "type": "bool",
"doc": "If True, a clickable icon is shown so the user can download the data as CSV."
},
{
@@ -954,13 +954,13 @@
},
{
"name": "width",
- "type": "str|int",
+ "type": "Union[str,int]",
"default_value": "\"360px\"",
"doc": "The width of this selector, in CSS units."
},
{
"name": "height",
- "type": "str|int",
+ "type": "Union[str,int]",
"doc": "The height of this selector, in CSS units."
}
]
@@ -977,7 +977,7 @@
{
"name": "content",
"default_property": true,
- "type": "dynamic(path|file|URL|ReadableBuffer|None)",
+ "type": "dynamic(Union[path,file,URL,ReadableBuffer,None])",
"doc": "The content to transfer.
If this is a string, a URL, or a file, then the content is read from this source.
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 data_url_max_size parameter of the application configuration (which is set to 50kB by default):\n\n- If the buffer size is smaller than this setting, then the raw content is generated as a data URL, encoded using base64 (i.e.
\"data:<mimetype>;base64,<data>\"
). \n- If the buffer size exceeds this setting, then it is transferred through a temporary file.
\n
If this property is set to None, that indicates that dynamic content is generated. Please take a look at the examples below for details on dynamic generation."
},
{
@@ -988,7 +988,7 @@
{
"name": "on_action",
"type": "Callback",
- "doc": "The name of a function that is triggered when the download is terminated (or on user action if content is None).
All the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (str|None): the identifier of the button.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has two keys:\n\n- action: the name of the action that triggered this callback.
\n- args: A list of two elements: args[0] reflects the name property and args[1] holds the file URL.
\n
\n \n
",
+ "doc": "The name of a function that is triggered when the download is terminated (or on user action if content is None).
All the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the button if it has one.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has two keys:\n\n- action: the name of the action that triggered this callback.
\n- args: A list of two elements: args[0] reflects the name property and args[1] holds the file URL.
\n
\n \n
",
"signature": [
[
"state",
@@ -1052,7 +1052,7 @@
{
"name": "on_action",
"type": "Callback",
- "doc": "The name of the function that will be triggered.
All the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (str|None): the identifier of the button.
\n- payload (dict): a dictionary that contains the key \"action\" set to the name of the action that triggered this callback.
\n
",
+ "doc": "The name of the function that will be triggered.
All the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the button if it has one.
\n- payload (dict): a dictionary that contains the key \"action\" set to the name of the action that triggered this callback.
\n
",
"signature": [
[
"state",
@@ -1106,7 +1106,7 @@
{
"name": "content",
"default_property": true,
- "type": "dynamic(path|URL|file|ReadableBuffer)",
+ "type": "dynamic(Union[path,URL,file,ReadableBuffer])",
"doc": "The image source.
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 data_url_max_size parameter of the application configuration (which is set to 50kB by default):\n\n- 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.
\"data:<mimetype>;base64,<data>\"
). \n- If the size of the buffer is greater than this setting, then it is transferred through a temporary\n file.
\n
"
},
{
@@ -1117,7 +1117,7 @@
{
"name": "on_action",
"type": "Callback",
- "doc": "The name of a function that is triggered when the user clicks on the image.
All the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (str|None): the identifier of the button.
\n- payload (dict): a dictionary that contains the key \"action\" set to the name of the action that triggered this callback.
\n
",
+ "doc": "The name of a function that is triggered when the user clicks on the image.
All the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the button if it has one.
\n- payload (dict): a dictionary that contains the key \"action\" set to the name of the action that triggered this callback.
\n
",
"signature": [
[
"state",
@@ -1135,13 +1135,13 @@
},
{
"name": "width",
- "type": "str|int|float",
+ "type": "Union[str,int,float]",
"default_value": "\"300px\"",
"doc": "The width of this image control, in CSS units."
},
{
"name": "height",
- "type": "str|int|float",
+ "type": "Union[str,int,float]",
"doc": "The height of this image control, in CSS units."
}
]
@@ -1157,7 +1157,7 @@
{
"name": "value",
"default_property": true,
- "type": "dynamic(int|float)",
+ "type": "dynamic(Union[int,float])",
"doc": "The value to represent."
},
{
@@ -1168,19 +1168,19 @@
},
{
"name": "min",
- "type": "int|float",
+ "type": "Union[int,float]",
"default_value": "0",
"doc": "The minimum value of this metric control's gauge."
},
{
"name": "max",
- "type": "int|float",
+ "type": "Union[int,float]",
"default_value": "100",
"doc": "The maximum value of this metric control's gauge."
},
{
"name": "delta",
- "type": "dynamic(int|float)",
+ "type": "dynamic(Union[int,float])",
"doc": "The delta value to display."
},
{
@@ -1201,7 +1201,7 @@
},
{
"name": "threshold",
- "type": "dynamic(int|float)",
+ "type": "dynamic(Union[int,float])",
"doc": "The threshold value to display."
},
{
@@ -1232,13 +1232,13 @@
},
{
"name": "width",
- "type": "str|number",
+ "type": "Union[str,number]",
"default_value": "None",
"doc": "The width of the metric control, in CSS units."
},
{
"name": "height",
- "type": "str|number",
+ "type": "Union[str,number]",
"default_value": "None",
"doc": "The height of the metric control, in CSS units."
},
@@ -1317,13 +1317,13 @@
},
{
"name": "min",
- "type": "int|float",
+ "type": "Union[int,float]",
"default_value": "0",
"doc": "The minimum value of the range."
},
{
"name": "max",
- "type": "int|float",
+ "type": "Union[int,float]",
"default_value": "100",
"doc": "The maximum value of the range."
},
@@ -1367,14 +1367,14 @@
{
"name": "lov",
"default_property": true,
- "type": "dynamic(str|list[str|Icon|any])",
+ "type": "dynamic(Union[str,list[Union[str,Icon,any]]])",
"doc": "The list of menu option values."
},
{
"name": "adapter",
"type": "Function",
"default_value": "`\"lambda x: str(x)\"`",
- "doc": "The function that transforms an element of lov into a tuple(id:str, label:str|Icon)."
+ "doc": "The function that transforms an element of lov into a tuple(id:str, label:Union[str,Icon])."
},
{
"name": "type",
@@ -1389,7 +1389,7 @@
},
{
"name": "inactive_ids",
- "type": "dynamic(str|list[str])",
+ "type": "dynamic(Union[str,list[str]])",
"doc": "Semicolon (';')-separated list or a list of menu items identifiers that are disabled."
},
{
@@ -1407,7 +1407,7 @@
{
"name": "on_action",
"type": "Callback",
- "doc": "The name of the function that is triggered when a menu option is selected.
All the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the button.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- action: the name of the action that triggered this callback.
\n- args: List where the first element contains the id of the selected option.
\n
\n \n
",
+ "doc": "The name of the function that is triggered when a menu option is selected.
All the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the button if it has one.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- action: the name of the action that triggered this callback.
\n- args: List where the first element contains the id of the selected option.
\n
\n \n
",
"signature": [
[
"state",
@@ -1453,7 +1453,7 @@
{
"name": "value",
"default_property": true,
- "type": "tuple|dict|list[dict]|list[tuple]",
+ "type": "Union[tuple,dict,list[dict],list[tuple]]",
"doc": "The different status items to represent. See below."
},
{
@@ -1482,7 +1482,7 @@
{
"name": "on_action",
"type": "Callback",
- "doc": "The name of the function that is triggered when the dialog button is pressed.
All the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the button.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- action: the name of the action that triggered this callback.
\n- args: a list with three elements:\n
- The first element is the username
- The second element is the password
- The third element is the current page name
\n
\n\n
When the button is pressed, and if this property is not set, Taipy will try to find a callback function called on_login() and invoke it with the parameters listed above.",
+ "doc": "The name of the function that is triggered when the dialog button is pressed.
All the parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the button if it has one.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- action: the name of the action that triggered this callback.
\n- args: a list with three elements:\n
- The first element is the username
- The second element is the password
- The third element is the current page name
\n
\n\n
When the button is pressed, and if this property is not set, Taipy will try to find a callback function called on_login() and invoke it with the parameters listed above.",
"signature": [
[
"state",
@@ -1523,7 +1523,7 @@
},
{
"name": "users",
- "type": "dynamic(list[str|Icon])",
+ "type": "dynamic(list[Union[str,Icon]])",
"doc": "The list of users. See the section on List of Values for details."
},
{
@@ -1565,7 +1565,7 @@
},
{
"name": "height",
- "type": "str|int|float",
+ "type": "Union[str,int,float]",
"doc": "The maximum height of this chat control, in CSS units."
}
]
@@ -1580,7 +1580,7 @@
"properties": [
{
"name": "expanded",
- "type": "dynamic(bool|str[])",
+ "type": "dynamic(Union[bool,list[str]])",
"default_value": "True",
"doc": "If Boolean and False, only one node can be expanded at one given level. Otherwise this should be set to an array of the node identifiers that need to be expanded."
},
@@ -1687,7 +1687,7 @@
{
"name": "on_action",
"type": "Callback",
- "doc": "Name of a function triggered when a button is pressed.
The parameters of that function are all optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the dialog.
\n- payload (dict): the details on this callback's invocation.
This dictionary has the following keys:\n\n- action: the name of the action that triggered this callback.
\n- args: a list where the first element contains the index of the selected label.
\n
\n \n
",
+ "doc": "Name of a function triggered when a button is pressed.
The parameters of that function are all optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the dialog if it has one.
\n- payload (dict): the details on this callback's invocation.
This dictionary has the following keys:\n\n- action: the name of the action that triggered this callback.
\n- args: a list where the first element contains the index of the selected label.
\n
\n \n
",
"signature": [
[
"state",
@@ -1711,17 +1711,17 @@
},
{
"name": "labels",
- "type": " str|list[str]",
+ "type": "Union[str,list[str]]",
"doc": "A list of labels to show in a row of buttons at the bottom of the dialog. The index of the button in the list is reported as args in the on_action callback (that index is -1 for the close icon)."
},
{
"name": "width",
- "type": "str|int|float",
+ "type": "Union[str,int,float]",
"doc": "The width of this dialog, in CSS units."
},
{
"name": "height",
- "type": "str|int|float",
+ "type": "Union[str,int,float]",
"doc": "The height of this dialog, in CSS units."
}
]
@@ -1776,7 +1776,7 @@
{
"name": "on_close",
"type": "Callback",
- "doc": "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).
All parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (str|None): the identifier of the button.
\n
If this property is not set, no function is called when this pane is closed.",
+ "doc": "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).
All parameters of that function are optional:\n\n- state (
State^
): the state instance. \n- id (optional[str]): the identifier of the close button if it has one.
\n
If this property is not set, no function is called when this pane is closed.",
"signature": [
[
"state",
@@ -1852,7 +1852,7 @@
"name": "adapter",
"type": "Function",
"default_value": "`lambda x: str(x)`",
- "doc": "The function that transforms an element of lov into a tuple(id:str, label:str|Icon)."
+ "doc": "The function that transforms an element of lov into a tuple(id:str, label:Union[str,Icon])."
},
{
"name": "type",
@@ -1901,7 +1901,7 @@
"properties": [
{
"name": "partial",
- "type": "Partial",
+ "type": "taipy.gui.Partial",
"doc": "A Partial object that holds the content of the block.
This should not be defined if page is set."
},
{
@@ -1942,7 +1942,7 @@
{
"name": "on_action",
"type": "Callback",
- "doc": "Name of a function that is triggered when a specific key is pressed.
The parameters of that function are all optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the input.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- action: the name of the action that triggered this callback.
\n- args (list):\n
- key name
- variable name
- current value
\n \n
\n \n
",
+ "doc": "Name of a function that is triggered when a specific key is pressed.
The parameters of that function are all optional:\n\n- state (
State^
): the state instance. \n- id (str): the identifier of the control if it has one.
\n- payload (dict): the details on this callback's invocation.
\nThis dictionary has the following keys:\n\n- action: the name of the action that triggered this callback.
\n- args (list):\n
- key name
- variable name
- current value
\n \n
\n \n
",
"signature": [
[
"state",
@@ -1974,25 +1974,21 @@
{
"name": "id",
"type": "str",
- "default_value": "None",
"doc": "The identifier that is assigned to the rendered HTML component."
},
{
"name": "properties",
"type": "dict[str, any]",
- "default_value": "None",
"doc": "Bound to a dictionary that contains additional properties for this element."
},
{
"name": "class_name",
"type": "dynamic(str)",
- "default_value": "None",
"doc": "The list of CSS class names that are associated with the generated HTML Element.
These class names are added to the default taipy-<element_type>
class name."
},
{
"name": "hover_text",
"type": "dynamic(str)",
- "default_value": "None",
"doc": "The information that is displayed when the user hovers over this element."
}
]
diff --git a/tools/gui/builder/block.txt b/tools/gui/builder/block.txt
deleted file mode 100644
index e37bde2246..0000000000
--- a/tools/gui/builder/block.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-
-class {{name}}(_Block):
- _ELEMENT_NAME: str
- def __init__(self, {{properties}}) -> None:
- """### Arguments:
-{{doc_arguments}}
- """
- ...
diff --git a/tools/gui/builder/control.txt b/tools/gui/builder/control.txt
deleted file mode 100644
index 7d0a2ce5e8..0000000000
--- a/tools/gui/builder/control.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-
-class {{name}}(_Control):
- _ELEMENT_NAME: str
- def __init__(self, {{properties}}) -> None:
- """### Arguments:
-{{doc_arguments}}
- """
- ...
diff --git a/tools/gui/generate_pyi.py b/tools/gui/generate_pyi.py
index 531b8d1ca6..8fd235142f 100644
--- a/tools/gui/generate_pyi.py
+++ b/tools/gui/generate_pyi.py
@@ -12,24 +12,25 @@
import json
import os
import re
-import typing as t
+import sys
from markdownify import markdownify
-# ############################################################
-# Generate Python interface definition files
-# ############################################################
-from taipy.gui.config import Config
+# Make sure we can import the mandatory packages
+script_dir = os.path.dirname(os.path.realpath(__file__))
+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)))
-# ############################################################
+# ##################################################################################################
# Generate gui pyi file (gui/gui.pyi)
-# ############################################################
+# ##################################################################################################
gui_py_file = "./taipy/gui/gui.py"
-gui_pyi_file = gui_py_file + "i"
+gui_pyi_file = f"{gui_py_file}i"
+from taipy.config import Config # noqa: E402
+# Generate Python interface definition files
os.system(f"pipenv run stubgen {gui_py_file} --no-import --parse-only --export-less -o ./")
-
gui_config = "".join(
f", {k}: {v.__name__} = ..."
if " t.List[t.Dict[str, t.Any]]:
- properties = element["properties"]
- if "inherits" not in element:
+def resolve_inherit(name: str, properties, inherits, viselements) -> list[dict[str, any]]:
+ if not inherits:
return properties
- for inherit in element["inherits"]:
- inherit_element = next((e for e in viselements["undocumented"] if e[0] == inherit), None)
- if inherit_element is None:
- inherit_element = next((e for e in viselements["blocks"] if e[0] == inherit), None)
- if inherit_element is None:
- inherit_element = next((e for e in viselements["controls"] if e[0] == inherit), None)
- if inherit_element is None:
- raise RuntimeError(f"Can't find element with name {inherit}")
- properties += get_properties(inherit_element[1], viselements)
+ for inherit_name in inherits:
+ inherited_desc = next((e for e in viselements["undocumented"] if e[0] == inherit_name), None)
+ if inherited_desc is None:
+ inherited_desc = next((e for e in viselements["blocks"] if e[0] == inherit_name), None)
+ if inherited_desc is None:
+ inherited_desc = next((e for e in viselements["controls"] if e[0] == inherit_name), None)
+ if inherited_desc is None:
+ raise RuntimeError(f"Element type '{name}' inherits from unknown element type '{inherit_name}'")
+ inherited_desc = inherited_desc[1]
+ for inherit_prop in inherited_desc["properties"]:
+ prop_desc = next((p for p in properties if p["name"] == inherit_prop["name"]), None)
+ if prop_desc: # Property exists
+ def override(current, inherits, p: str):
+ if p not in current and (inherited := inherits.get(p, None)):
+ current[p] = inherited
+ override(prop_desc, inherit_prop, "type")
+ override(prop_desc, inherit_prop, "default_value")
+ override(prop_desc, inherit_prop, "doc")
+ override(prop_desc, inherit_prop, "signature")
+ else:
+ properties.append(inherit_prop)
+ properties = resolve_inherit(inherit_name, properties, inherited_desc.get("inherits", None), viselements)
return properties
+def format_as_parameter(property):
+ type = property["type"]
+ if m := re.match(r"indexed\((.*)\)", type):
+ type = m[1]
+ property["indexed"] = " (indexed)"
+ else:
+ property["indexed"] = ""
+ if m := re.match(r"dynamic\((.*)\)", type):
+ type = m[1]
+ property["dynamic"] = " (dynamic)"
+ else:
+ property["dynamic"] = ""
+ if type == "Callback" or type == "Function":
+ type = ""
+ else:
+ type = f": {type}"
+ default_value = property.get("default_value", None)
+ if default_value is not None:
+ try:
+ eval(default_value)
+ default_value = f" = {default_value}"
+ except Exception:
+ default_value = ""
+ else:
+ default_value = ""
+ return f"{property['name']}{type}{default_value}"
-def build_doc(name: str, element: t.Dict[str, t.Any]):
- if "doc" not in element:
+def build_doc(name: str, desc: dict[str, any]):
+ if "doc" not in desc:
return ""
- doc = str(element["doc"]).replace("\n", f'\n{16*" "}')
- doc = re.sub(
- r"^(.*\..*\shref=\")([^h].*)(\".*\..*)$",
- r"\1" + taipy_doc_url + name + r"/\2\3",
- doc,
- )
- doc = re.sub(
- r"^(.*\.)(
|\s)(See below((?!href=).)*\.)(.*)$",
- r"\1\3",
- doc,
- )
- doc = markdownify(doc, strip=["br"])
- return f"{element['name']} ({element['type']}): {doc} {'(default: '+markdownify(element['default_value']) + ')' if 'default_value' in element else ''}" # noqa: E501
-
-
-for control_element in viselements["controls"]:
- name = control_element[0]
- property_list: t.List[t.Dict[str, t.Any]] = []
- property_names: t.List[str] = []
- hidden_properties: t.List[str] = []
- for property in get_properties(control_element[1], viselements):
- if "hide" in property and property["hide"] is True:
- hidden_properties.append(property["name"])
- continue
- if (
- property["name"] not in property_names
- and "[" not in property["name"]
- and property["name"] not in hidden_properties
- ):
+ doc = desc["doc"]
+ if desc["name"] == "class_name":
+ doc = doc.replace("", name)
+ # This won't work for Scenartio Management and Block elements
+ doc = re.sub(r"(href=\")\.\.((?:.*?)\")", r"\1" + taipy_doc_url + name + r"/../..\2", doc)
+ doc = "\n ".join(markdownify(doc).split("\n"))
+ doc = doc.replace(" \n", " \\n")
+ doc = re.sub(r"(?:\s+\\n)?\s+See below(?:[^\.]*)?\.", "", doc).replace("\n", "\\n")
+ return f"{desc['name']}{desc['dynamic']}{desc['indexed']}\\n {doc}\\n\\n"
+
+
+element_template = """
+
+class {{name}}(_{{base_class}}):
+ _ELEMENT_NAME: str
+ def __init__(self, {{properties_decl}}) -> None:
+ \"\"\"Creates a{{n}} {{name}} element.\\n\\nParameters\\n----------\\n\\n{{properties_doc}}\"\"\" # noqa: E501
+ ...
+"""
+
+def generate_elements(category: str, base_class: str):
+ for element in viselements[category]:
+ name = element[0]
+ desc = element[1]
+ properties_doc = ""
+ property_list: list[dict[str, any]] = []
+ property_names: list[str] = []
+ properties = resolve_inherit(name, desc["properties"], desc.get("inherits", None), viselements)
+ # Remove hidden properties and indexed properties (TODO?)
+ properties = [p for p in properties if not p.get("hide", False) and "[" not in p["name"]]
+ # Generate function parameters
+ properties_decl = [format_as_parameter(p) for p in properties]
+ # Generate properties doc
+ for property in properties:
if "default_property" in property and property["default_property"] is True:
property_list.insert(0, property)
property_names.insert(0, property["name"])
continue
property_list.append(property)
property_names.append(property["name"])
- properties = ", ".join([f"{p} = ..." for p in property_names if p not in hidden_properties])
- doc_arguments = "\n".join([build_doc(name, p) for p in property_list if p["name"] not in hidden_properties])
- # append properties to __init__.pyi
- with open(builder_pyi_file, "a") as file:
- file.write(
- control_template.replace("{{name}}", name)
- .replace("{{properties}}", properties)
- .replace("{{doc_arguments}}", doc_arguments)
- )
-
-for block_element in viselements["blocks"]:
- name = block_element[0]
- property_list = []
- property_names = []
- for property in get_properties(block_element[1], viselements):
- if property["name"] not in property_names and "[" not in property["name"]:
- property_list.append(property)
- property_names.append(property["name"])
- properties = ", ".join([f"{p} = ..." for p in property_names])
- doc_arguments = "\n".join([build_doc(name, p) for p in property_list])
- # append properties to __init__.pyi
- with open(builder_pyi_file, "a") as file:
- file.write(
- block_template.replace("{{name}}", name)
- .replace("{{properties}}", properties)
- .replace("{{doc_arguments}}", doc_arguments)
- )
+ # Append properties doc to element doc (once ordered)
+ for property in property_list:
+ property_doc = build_doc(name, property)
+ properties_doc += property_doc
+ if (len(properties_decl) > 1):
+ properties_decl.insert(1, "*")
+ # Append element to __init__.pyi
+ with open(builder_pyi_file, "a") as file:
+ n = "n" if name[0] in ["a", "e", "i", "o"] else ""
+ file.write(
+ element_template.replace("{{name}}", name).replace("{{n}}", n)
+ .replace("{{base_class}}", base_class)
+ .replace("{{properties_decl}}", ", ".join(properties_decl))
+ .replace("{{properties_doc}}", properties_doc)
+ )
+
+
+
+generate_elements("controls", "Control")
+generate_elements("blocks", "Block")
os.system(f"pipenv run isort {gui_pyi_file}")
os.system(f"pipenv run black {gui_pyi_file}")