__init__.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. """Import all classes and functions the end user will need to make an app.
  2. Anything imported here will be available in the default Reflex import as `rx.*`.
  3. To signal to typecheckers that something should be reexported,
  4. we use the Flask "import name as name" syntax.
  5. """
  6. from __future__ import annotations
  7. import importlib
  8. from typing import Type
  9. from reflex.page import page as page
  10. from reflex.utils import console
  11. from reflex.utils.format import to_snake_case
  12. _ALL_COMPONENTS = [
  13. # Core
  14. "color",
  15. "cond",
  16. "foreach",
  17. "html",
  18. "match",
  19. "color_mode_cond",
  20. "connection_banner",
  21. "connection_modal",
  22. "debounce_input",
  23. # Base
  24. "fragment",
  25. "Fragment",
  26. "image",
  27. "script",
  28. # Responsive
  29. "desktop_only",
  30. "mobile_and_tablet",
  31. "mobile_only",
  32. "tablet_and_desktop",
  33. "tablet_only",
  34. # Upload
  35. "cancel_upload",
  36. "clear_selected_files",
  37. "get_upload_dir",
  38. "get_upload_url",
  39. "selected_files",
  40. "upload",
  41. # Radix
  42. "accordion",
  43. "alert_dialog",
  44. "aspect_ratio",
  45. "avatar",
  46. "badge",
  47. "blockquote",
  48. "box",
  49. "button",
  50. "callout",
  51. "card",
  52. "center",
  53. "checkbox",
  54. "code",
  55. "container",
  56. "context_menu",
  57. "data_list",
  58. "dialog",
  59. "divider",
  60. "drawer",
  61. "flex",
  62. "form",
  63. "grid",
  64. "heading",
  65. "hover_card",
  66. "hstack",
  67. "icon_button",
  68. "inset",
  69. "input",
  70. "link",
  71. "menu",
  72. "popover",
  73. "progress",
  74. "radio",
  75. "scroll_area",
  76. "section",
  77. "select",
  78. "skeleton",
  79. "slider",
  80. "spacer",
  81. "spinner",
  82. "stack",
  83. "switch",
  84. "table",
  85. "tabs",
  86. "text",
  87. "text_area",
  88. "theme",
  89. "theme_panel",
  90. "tooltip",
  91. "vstack",
  92. # Other
  93. "code_block",
  94. "data_editor",
  95. "data_editor_theme",
  96. "data_table",
  97. "plotly",
  98. "audio",
  99. "video",
  100. "editor",
  101. "EditorButtonList",
  102. "EditorOptions",
  103. "icon",
  104. "markdown",
  105. "list",
  106. "list_item",
  107. "unordered_list",
  108. "ordered_list",
  109. "moment",
  110. "logo",
  111. # Toast is in experimental namespace initially
  112. # "toast",
  113. ]
  114. _MAPPING = {
  115. "reflex.experimental": ["_x"],
  116. "reflex.admin": ["admin", "AdminDash"],
  117. "reflex.app": ["app", "App", "UploadFile"],
  118. "reflex.base": ["base", "Base"],
  119. "reflex.compiler": ["compiler"],
  120. "reflex.components": _ALL_COMPONENTS,
  121. "reflex.components.component": ["Component", "NoSSRComponent", "memo"],
  122. "reflex.components.chakra": ["chakra"],
  123. "reflex.components.el": ["el"],
  124. "reflex.components.lucide": ["lucide"],
  125. "reflex.components.next": ["next"],
  126. "reflex.components.radix": ["radix", "color_mode"],
  127. "reflex.components.recharts": ["recharts"],
  128. "reflex.components.moment.moment": ["MomentDelta"],
  129. "reflex.config": ["config", "Config", "DBConfig"],
  130. "reflex.constants": ["constants", "Env"],
  131. "reflex.event": [
  132. "event",
  133. "EventChain",
  134. "EventHandler",
  135. "background",
  136. "call_script",
  137. "clear_local_storage",
  138. "console_log",
  139. "download",
  140. "prevent_default",
  141. "redirect",
  142. "remove_cookie",
  143. "remove_local_storage",
  144. "set_clipboard",
  145. "set_focus",
  146. "scroll_to",
  147. "set_value",
  148. "stop_propagation",
  149. "upload_files",
  150. "window_alert",
  151. ],
  152. "reflex.middleware": ["middleware", "Middleware"],
  153. "reflex.model": ["model", "session", "Model"],
  154. "reflex.page": ["page"],
  155. "reflex.route": ["route"],
  156. "reflex.state": [
  157. "state",
  158. "var",
  159. "Cookie",
  160. "LocalStorage",
  161. "ComponentState",
  162. "State",
  163. ],
  164. "reflex.style": ["style", "toggle_color_mode"],
  165. "reflex.testing": ["testing"],
  166. "reflex.utils": ["utils"],
  167. "reflex.utils.imports": ["ImportVar"],
  168. "reflex.vars": ["vars", "cached_var", "Var"],
  169. }
  170. def _reverse_mapping(mapping: dict[str, list]) -> dict[str, str]:
  171. """Reverse the mapping used to lazy loading, and check for conflicting name.
  172. Args:
  173. mapping: The mapping to reverse.
  174. Returns:
  175. The reversed mapping.
  176. """
  177. reversed_mapping = {}
  178. for key, values in mapping.items():
  179. for value in values:
  180. if value not in reversed_mapping:
  181. reversed_mapping[value] = key
  182. else:
  183. console.warn(
  184. f"Key {value} is present multiple times in the imports _MAPPING: {key} / {reversed_mapping[value]}"
  185. )
  186. return reversed_mapping
  187. # _MAPPING = {value: key for key, values in _MAPPING.items() for value in values}
  188. _MAPPING = _reverse_mapping(_MAPPING)
  189. def _removeprefix(text, prefix):
  190. return text[text.startswith(prefix) and len(prefix) :]
  191. __all__ = (_removeprefix(mod, "reflex.") for mod in _MAPPING)
  192. def __getattr__(name: str) -> Type:
  193. """Lazy load all modules.
  194. Args:
  195. name: name of the module to load.
  196. Returns:
  197. The module or the attribute of the module.
  198. Raises:
  199. AttributeError: If the module or the attribute does not exist.
  200. """
  201. try:
  202. # Check for import of a module that is not in the mapping.
  203. if name not in _MAPPING:
  204. # If the name does not start with reflex, add it.
  205. if not name.startswith("reflex") and name != "__all__":
  206. name = f"reflex.{name}"
  207. return importlib.import_module(name)
  208. # Import the module.
  209. module = importlib.import_module(_MAPPING[name])
  210. # Get the attribute from the module if the name is not the module itself.
  211. return (
  212. getattr(module, name) if name != _MAPPING[name].rsplit(".")[-1] else module
  213. )
  214. except ModuleNotFoundError:
  215. raise AttributeError(f"module 'reflex' has no attribute {name}") from None