1
0

_api_generator.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  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 inspect
  12. import json
  13. import os
  14. import sys
  15. import types
  16. import typing as t
  17. from taipy.logger._taipy_logger import _TaipyLogger
  18. from ..utils.singleton import _Singleton
  19. from ..utils.viselements import VisElements, resolve_inherits
  20. from ._element import _Block, _Control
  21. if t.TYPE_CHECKING:
  22. from ..extension.library import ElementLibrary
  23. class _ElementApiGenerator(object, metaclass=_Singleton):
  24. def __init__(self) -> None:
  25. self.__module: t.Optional[types.ModuleType] = None
  26. @staticmethod
  27. def find_default_property(property_list: t.List[t.Dict[str, t.Any]]) -> str:
  28. for property in property_list:
  29. if "default_property" in property and property["default_property"] is True:
  30. return property["name"]
  31. return ""
  32. def add_default(self):
  33. current_frame = inspect.currentframe()
  34. error_message = "Cannot generate elements API for the current module"
  35. if current_frame is None:
  36. raise RuntimeError(f"{error_message}: No frame found.")
  37. if current_frame.f_back is None:
  38. raise RuntimeError(f"{error_message}: taipy-gui module not found.")
  39. module_name = current_frame.f_back.f_globals["__name__"]
  40. self.__module = module = sys.modules[module_name]
  41. with open(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "viselements.json"))) as f:
  42. viselements_json = json.load(f)
  43. if "blocks" not in viselements_json or "controls" not in viselements_json:
  44. raise RuntimeError(f"{error_message}: Invalid viselements.json file.")
  45. viselements = resolve_inherits(t.cast(VisElements, viselements_json))
  46. for blockElement in viselements["blocks"]:
  47. default_property = _ElementApiGenerator.find_default_property(blockElement[1]["properties"])
  48. setattr(
  49. module,
  50. blockElement[0],
  51. _ElementApiGenerator.create_block_api(blockElement[0], blockElement[0], default_property),
  52. )
  53. for controlElement in viselements["controls"]:
  54. default_property = _ElementApiGenerator.find_default_property(controlElement[1]["properties"])
  55. setattr(
  56. module,
  57. controlElement[0],
  58. _ElementApiGenerator.create_control_api(controlElement[0], controlElement[0], default_property),
  59. )
  60. def add_library(self, library: "ElementLibrary"):
  61. library_name = library.get_name()
  62. if self.__module is None:
  63. _TaipyLogger._get_logger().info(
  64. f"Python API for extension library '{library_name}' is not available. To fix this, import 'taipy.gui.builder' before importing the extension library." # noqa: E501
  65. )
  66. return
  67. library_module = getattr(self.__module, library_name, None)
  68. if library_module is None:
  69. library_module = types.ModuleType(library_name)
  70. setattr(self.__module, library_name, library_module)
  71. for element_name, element in library.get_elements().items():
  72. setattr(
  73. library_module,
  74. element_name,
  75. _ElementApiGenerator().create_control_api(
  76. element_name, f"{library_name}.{element_name}", element.default_attribute
  77. ),
  78. )
  79. # Allow element to be accessed from the root module
  80. if hasattr(self.__module, element_name):
  81. _TaipyLogger()._get_logger().info(
  82. f"Can't add element `{element_name}` of library `{library_name}` to the root of Builder API as another element with the same name already exists." # noqa: E501
  83. )
  84. continue
  85. setattr(self.__module, element_name, getattr(library_module, element_name))
  86. @staticmethod
  87. def create_block_api(
  88. classname: str,
  89. element_name: str,
  90. default_property: str,
  91. ):
  92. return _ElementApiGenerator.create_element_api(classname, element_name, default_property, _Block)
  93. @staticmethod
  94. def create_control_api(
  95. classname: str,
  96. element_name: str,
  97. default_property: str,
  98. ):
  99. return _ElementApiGenerator.create_element_api(classname, element_name, default_property, _Control)
  100. @staticmethod
  101. def create_element_api(
  102. classname: str,
  103. element_name: str,
  104. default_property: str,
  105. ElementBaseClass: t.Union[t.Type[_Block], t.Type[_Control]],
  106. ):
  107. return type(
  108. classname,
  109. (ElementBaseClass,),
  110. {
  111. "_ELEMENT_NAME": element_name,
  112. "_DEFAULT_PROPERTY": default_property,
  113. },
  114. )