__init__.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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. Dynamic Imports
  4. ---------------
  5. Reflex utilizes dynamic imports, or lazy loading, to reduce startup/import times.
  6. With this approach, imports are delayed until they are actually needed. We use
  7. the `lazy_loader` library(https://github.com/scientific-python/lazy_loader) to achieve this.
  8. How it works
  9. --------------
  10. `lazy_loader.attach` takes two optional arguments: `submodules` and `submod_attrs`.
  11. - `submodules` typically points to directories or files to be accessed.
  12. - `submod_attrs` defines a mapping of directory or file names as keys with a list
  13. of attributes or modules to access.
  14. Example directory structure:
  15. reflex/
  16. |_ components/
  17. |_ radix/
  18. |_ themes/
  19. |_ components/
  20. |_ box.py
  21. To add `box` under the `rx` namespace (`rx.box`), add the relative path to `submod_attrs` in
  22. `reflex/__init__.py` (this file):
  23. ```python
  24. lazy_loader.attach(
  25. submodules={"components"},
  26. submod_attrs={
  27. "components.radix.themes.components.box": ["box"]
  28. }
  29. )
  30. ```
  31. This implies that `box` will be imported from `reflex/components/radix/themes/components/box.py`.
  32. To add box under the `rx.radix` namespace (`rx.radix.box`), add the relative path to the
  33. submod_attrs argument in `reflex/components/radix/__init__.py`:
  34. ```python
  35. lazy_loader.attach(
  36. submodules = {"themes"},
  37. submod_attrs = {
  38. "themes.components.box": ["box"]
  39. }
  40. )
  41. ```
  42. Note: It is important to specify the immediate submodules of a directory in the submodules
  43. argument to ensure they are registered at runtime. For example, 'components' for reflex,
  44. 'radix' for components, 'themes' for radix, etc.
  45. Pyi_generator
  46. --------------
  47. To generate `.pyi` files for `__init__.py` files, we read the `_SUBMODULES` and `_SUBMOD_ATTRS`
  48. attributes to generate the import statements. It is highly recommended to define these with
  49. the provided annotations to facilitate their generation.
  50. Aliases
  51. ------------
  52. This is a special case to specify an alias for a component.
  53. As an example, we use this typically for `rx.list` where defining `list` attribute in the list.py
  54. overshadows python's list object which messes up the pyi generation for `list.pyi`. As a result, aliases
  55. should be used for similar cases like this. Note that this logic is employed to fix the pyi generation and alias
  56. should still be defined or accessible. Check out the __getattr__ logic in `reflex/components/radix/themes/layouts/list.py`
  57. ```python
  58. lazy_loader.attach(
  59. submodules={"components"},
  60. submod_attrs={
  61. "components.radix.themes.layouts": [("list_ns", "list")]
  62. }
  63. )
  64. ```
  65. In the example above, you will be able to do `rx.list`
  66. """
  67. from __future__ import annotations
  68. from types import ModuleType
  69. from typing import Any
  70. from reflex.utils import (
  71. compat, # for side-effects
  72. lazy_loader,
  73. )
  74. from .event import event as event
  75. # import this here explicitly to avoid returning the page module since page attr has the
  76. # same name as page module(page.py)
  77. from .page import page as page
  78. # Remove the `compat` name from the namespace, it was imported for side-effects only.
  79. del compat
  80. RADIX_THEMES_MAPPING: dict = {
  81. "components.radix.themes.base": ["color_mode", "theme", "theme_panel"],
  82. "components.radix.themes.color_mode": ["color_mode"],
  83. }
  84. RADIX_THEMES_COMPONENTS_MAPPING: dict = {
  85. **{
  86. f"components.radix.themes.components.{mod}": [mod]
  87. for mod in [
  88. "alert_dialog",
  89. "aspect_ratio",
  90. "avatar",
  91. "badge",
  92. "button",
  93. "callout",
  94. "card",
  95. "checkbox",
  96. "context_menu",
  97. "data_list",
  98. "dialog",
  99. "hover_card",
  100. "icon_button",
  101. "input",
  102. "inset",
  103. "popover",
  104. "scroll_area",
  105. "select",
  106. "skeleton",
  107. "slider",
  108. "spinner",
  109. "switch",
  110. "table",
  111. "tabs",
  112. "text_area",
  113. "tooltip",
  114. "segmented_control",
  115. "radio_cards",
  116. "checkbox_cards",
  117. "checkbox_group",
  118. ]
  119. },
  120. "components.radix.themes.components.text_field": ["text_field", "input"],
  121. "components.radix.themes.components.radio_group": ["radio", "radio_group"],
  122. "components.radix.themes.components.dropdown_menu": ["menu", "dropdown_menu"],
  123. "components.radix.themes.components.separator": ["divider", "separator"],
  124. "components.radix.themes.components.progress": ["progress"],
  125. }
  126. RADIX_THEMES_LAYOUT_MAPPING: dict = {
  127. "components.radix.themes.layout.box": [
  128. "box",
  129. ],
  130. "components.radix.themes.layout.center": [
  131. "center",
  132. ],
  133. "components.radix.themes.layout.container": [
  134. "container",
  135. ],
  136. "components.radix.themes.layout.flex": [
  137. "flex",
  138. ],
  139. "components.radix.themes.layout.grid": [
  140. "grid",
  141. ],
  142. "components.radix.themes.layout.section": [
  143. "section",
  144. ],
  145. "components.radix.themes.layout.spacer": [
  146. "spacer",
  147. ],
  148. "components.radix.themes.layout.stack": [
  149. "stack",
  150. "hstack",
  151. "vstack",
  152. ],
  153. "components.radix.themes.layout.list": [
  154. ("list_ns", "list"),
  155. "list_item",
  156. "ordered_list",
  157. "unordered_list",
  158. ],
  159. }
  160. RADIX_THEMES_TYPOGRAPHY_MAPPING: dict = {
  161. "components.radix.themes.typography.blockquote": [
  162. "blockquote",
  163. ],
  164. "components.radix.themes.typography.code": [
  165. "code",
  166. ],
  167. "components.radix.themes.typography.heading": [
  168. "heading",
  169. ],
  170. "components.radix.themes.typography.link": [
  171. "link",
  172. ],
  173. "components.radix.themes.typography.text": [
  174. "text",
  175. ],
  176. }
  177. RADIX_PRIMITIVES_MAPPING: dict = {
  178. "components.radix.primitives.accordion": [
  179. "accordion",
  180. ],
  181. "components.radix.primitives.drawer": [
  182. "drawer",
  183. ],
  184. "components.radix.primitives.form": [
  185. "form",
  186. ],
  187. "components.radix.primitives.progress": [
  188. "progress",
  189. ],
  190. }
  191. RADIX_PRIMITIVES_SHORTCUT_MAPPING: dict = {
  192. k: v for k, v in RADIX_PRIMITIVES_MAPPING.items() if "progress" not in k
  193. }
  194. COMPONENTS_CORE_MAPPING: dict = {
  195. "components.core.banner": [
  196. "connection_banner",
  197. "connection_modal",
  198. ],
  199. "components.core.cond": ["cond", "color_mode_cond"],
  200. "components.core.foreach": ["foreach"],
  201. "components.core.debounce": ["debounce_input"],
  202. "components.core.html": ["html"],
  203. "components.core.match": ["match"],
  204. "components.core.clipboard": ["clipboard"],
  205. "components.core.colors": ["color"],
  206. "components.core.breakpoints": ["breakpoints"],
  207. "components.core.responsive": [
  208. "desktop_only",
  209. "mobile_and_tablet",
  210. "mobile_only",
  211. "tablet_and_desktop",
  212. "tablet_only",
  213. ],
  214. "components.core.upload": [
  215. "cancel_upload",
  216. "clear_selected_files",
  217. "get_upload_dir",
  218. "get_upload_url",
  219. "selected_files",
  220. "upload",
  221. ],
  222. "components.core.auto_scroll": ["auto_scroll"],
  223. }
  224. COMPONENTS_BASE_MAPPING: dict = {
  225. "components.base.fragment": ["fragment", "Fragment"],
  226. "components.base.script": ["script", "Script"],
  227. }
  228. RADIX_MAPPING: dict = {
  229. **RADIX_THEMES_MAPPING,
  230. **RADIX_THEMES_COMPONENTS_MAPPING,
  231. **RADIX_THEMES_TYPOGRAPHY_MAPPING,
  232. **RADIX_THEMES_LAYOUT_MAPPING,
  233. **RADIX_PRIMITIVES_SHORTCUT_MAPPING,
  234. }
  235. _MAPPING: dict = {
  236. "experimental": ["_x"],
  237. "admin": ["AdminDash"],
  238. "app": ["App", "UploadFile"],
  239. "assets": ["asset"],
  240. "base": ["Base"],
  241. "components.component": [
  242. "Component",
  243. "NoSSRComponent",
  244. "memo",
  245. "ComponentNamespace",
  246. ],
  247. "components.el.elements.media": ["image"],
  248. "components.lucide": ["icon"],
  249. **COMPONENTS_BASE_MAPPING,
  250. "components.suneditor": [
  251. "editor",
  252. "EditorButtonList",
  253. "EditorOptions",
  254. ],
  255. "components": ["el", "radix", "lucide", "recharts", "next"],
  256. "components.markdown": ["markdown"],
  257. **RADIX_MAPPING,
  258. "components.plotly": ["plotly"],
  259. "components.react_player": ["audio", "video"],
  260. **COMPONENTS_CORE_MAPPING,
  261. "components.datadisplay.code": [
  262. "code_block",
  263. ],
  264. "components.datadisplay.dataeditor": [
  265. "data_editor",
  266. "data_editor_theme",
  267. ],
  268. "components.sonner.toast": ["toast"],
  269. "components.datadisplay.logo": ["logo"],
  270. "components.gridjs": ["data_table"],
  271. "components.moment": ["MomentDelta", "moment"],
  272. "config": ["Config", "DBConfig"],
  273. "constants": ["Env"],
  274. "constants.colors": ["Color"],
  275. "event": [
  276. "EventChain",
  277. "EventHandler",
  278. "call_script",
  279. "call_function",
  280. "run_script",
  281. "clear_local_storage",
  282. "clear_session_storage",
  283. "console_log",
  284. "download",
  285. "noop",
  286. "prevent_default",
  287. "redirect",
  288. "remove_cookie",
  289. "remove_local_storage",
  290. "remove_session_storage",
  291. "set_clipboard",
  292. "set_focus",
  293. "scroll_to",
  294. "set_value",
  295. "stop_propagation",
  296. "upload_files",
  297. "window_alert",
  298. ],
  299. "istate.storage": [
  300. "Cookie",
  301. "LocalStorage",
  302. "SessionStorage",
  303. ],
  304. "middleware": ["middleware", "Middleware"],
  305. "model": ["asession", "session", "Model"],
  306. "state": [
  307. "var",
  308. "ComponentState",
  309. "State",
  310. "dynamic",
  311. ],
  312. "istate.wrappers": ["get_state"],
  313. "style": ["Style", "toggle_color_mode"],
  314. "utils.imports": ["ImportDict", "ImportVar"],
  315. "utils.misc": ["run_in_thread"],
  316. "utils.serializers": ["serializer"],
  317. "vars": ["Var", "field", "Field"],
  318. }
  319. _SUBMODULES: set[str] = {
  320. "components",
  321. "app",
  322. "style",
  323. "admin",
  324. "base",
  325. "model",
  326. "testing",
  327. "utils",
  328. "vars",
  329. "config",
  330. "compiler",
  331. }
  332. _SUBMOD_ATTRS: dict = _MAPPING
  333. getattr, __dir__, __all__ = lazy_loader.attach(
  334. __name__,
  335. submodules=_SUBMODULES,
  336. submod_attrs=_SUBMOD_ATTRS,
  337. )
  338. def __getattr__(name: str):
  339. return getattr(name)