generate_pyi.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. # Copyright 2021-2024 Avaiga Private Limited
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  4. # the License. You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  9. # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  10. # specific language governing permissions and limitations under the License.
  11. import json
  12. import os
  13. import re
  14. import typing as t
  15. from markdownify import markdownify
  16. # ############################################################
  17. # Generate Python interface definition files
  18. # ############################################################
  19. from taipy.gui.config import Config
  20. # ############################################################
  21. # Generate gui pyi file (gui/gui.pyi)
  22. # ############################################################
  23. gui_py_file = "./taipy/gui/gui.py"
  24. gui_pyi_file = gui_py_file + "i"
  25. os.system(f"pipenv run stubgen {gui_py_file} --no-import --parse-only --export-less -o ./")
  26. gui_config = "".join(
  27. f", {k}: {v.__name__} = ..."
  28. if "<class" in str(v)
  29. else f", {k}: {str(v).replace('typing', 't').replace('taipy.gui.config.', '')} = ..."
  30. for k, v in Config.__annotations__.items()
  31. )
  32. replaced_content = ""
  33. with open(gui_pyi_file, "r") as file:
  34. for line in file:
  35. if "def run(" in line:
  36. replace_str = line[line.index(", run_server") : (line.index("**kwargs") + len("**kwargs"))]
  37. # ", run_server: bool = ..., run_in_thread: bool = ..., async_mode: str = ..., **kwargs"
  38. line = line.replace(replace_str, gui_config)
  39. replaced_content = replaced_content + line
  40. with open(gui_pyi_file, "w") as write_file:
  41. write_file.write(replaced_content)
  42. # ################
  43. # Read the version
  44. # ################
  45. current_version = "latest"
  46. with open("./taipy/gui/version.json", "r") as vfile:
  47. version = json.load(vfile)
  48. if "dev" in version.get("ext", ""):
  49. current_version = "develop"
  50. else:
  51. current_version = f'release-{version.get("major", 0)}.{version.get("minor", 0)}'
  52. taipy_doc_url = f"https://docs.taipy.io/en/{current_version}/manuals/userman/gui/viselements/standard-and-blocks/"
  53. # ############################################################
  54. # Generate Page Builder pyi file (gui/builder/__init__.pyi)
  55. # ############################################################
  56. builder_py_file = "./taipy/gui/builder/__init__.py"
  57. builder_pyi_file = builder_py_file + "i"
  58. with open("./taipy/gui/viselements.json", "r") as file:
  59. viselements = json.load(file)
  60. with open("./tools/gui/builder/block.txt", "r") as file:
  61. block_template = file.read()
  62. with open("./tools/gui/builder/control.txt", "r") as file:
  63. control_template = file.read()
  64. os.system(f"pipenv run stubgen {builder_py_file} --no-import --parse-only --export-less -o ./")
  65. with open(builder_pyi_file, "a") as file:
  66. file.write("from ._element import _Element, _Block, _Control\n")
  67. def get_properties(element, viselements) -> t.List[t.Dict[str, t.Any]]:
  68. properties = element["properties"]
  69. if "inherits" not in element:
  70. return properties
  71. for inherit in element["inherits"]:
  72. inherit_element = next((e for e in viselements["undocumented"] if e[0] == inherit), None)
  73. if inherit_element is None:
  74. inherit_element = next((e for e in viselements["blocks"] if e[0] == inherit), None)
  75. if inherit_element is None:
  76. inherit_element = next((e for e in viselements["controls"] if e[0] == inherit), None)
  77. if inherit_element is None:
  78. raise RuntimeError(f"Can't find element with name {inherit}")
  79. properties += get_properties(inherit_element[1], viselements)
  80. return properties
  81. def build_doc(name: str, element: t.Dict[str, t.Any]):
  82. if "doc" not in element:
  83. return ""
  84. doc = str(element["doc"]).replace("\n", f'\n{16*" "}')
  85. doc = re.sub(
  86. r"^(.*\..*\shref=\")([^h].*)(\".*\..*)$",
  87. r"\1" + taipy_doc_url + name + r"/\2\3",
  88. doc,
  89. )
  90. doc = re.sub(
  91. r"^(.*\.)(<br/>|\s)(See below((?!href=).)*\.)(.*)$",
  92. r"\1\3",
  93. doc,
  94. )
  95. doc = markdownify(doc, strip=["br"])
  96. return f"{element['name']} ({element['type']}): {doc} {'(default: '+markdownify(element['default_value']) + ')' if 'default_value' in element else ''}" # noqa: E501
  97. for control_element in viselements["controls"]:
  98. name = control_element[0]
  99. property_list: t.List[t.Dict[str, t.Any]] = []
  100. property_names: t.List[str] = []
  101. hidden_properties: t.List[str] = []
  102. for property in get_properties(control_element[1], viselements):
  103. if "hide" in property and property["hide"] is True:
  104. hidden_properties.append(property["name"])
  105. continue
  106. if (
  107. property["name"] not in property_names
  108. and "[" not in property["name"]
  109. and property["name"] not in hidden_properties
  110. ):
  111. if "default_property" in property and property["default_property"] is True:
  112. property_list.insert(0, property)
  113. property_names.insert(0, property["name"])
  114. continue
  115. property_list.append(property)
  116. property_names.append(property["name"])
  117. properties = ", ".join([f"{p} = ..." for p in property_names if p not in hidden_properties])
  118. doc_arguments = "\n".join([build_doc(name, p) for p in property_list if p["name"] not in hidden_properties])
  119. # append properties to __init__.pyi
  120. with open(builder_pyi_file, "a") as file:
  121. file.write(
  122. control_template.replace("{{name}}", name)
  123. .replace("{{properties}}", properties)
  124. .replace("{{doc_arguments}}", doc_arguments)
  125. )
  126. for block_element in viselements["blocks"]:
  127. name = block_element[0]
  128. property_list = []
  129. property_names = []
  130. for property in get_properties(block_element[1], viselements):
  131. if property["name"] not in property_names and "[" not in property["name"]:
  132. property_list.append(property)
  133. property_names.append(property["name"])
  134. properties = ", ".join([f"{p} = ..." for p in property_names])
  135. doc_arguments = "\n".join([build_doc(name, p) for p in property_list])
  136. # append properties to __init__.pyi
  137. with open(builder_pyi_file, "a") as file:
  138. file.write(
  139. block_template.replace("{{name}}", name)
  140. .replace("{{properties}}", properties)
  141. .replace("{{doc_arguments}}", doc_arguments)
  142. )
  143. os.system(f"pipenv run isort {gui_pyi_file}")
  144. os.system(f"pipenv run black {gui_pyi_file}")
  145. os.system(f"pipenv run isort {builder_pyi_file}")
  146. os.system(f"pipenv run black {builder_pyi_file}")