solara-ui 1.45.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) hide show
  1. prefix/etc/jupyter/jupyter_notebook_config.d/solara.json +7 -0
  2. prefix/etc/jupyter/jupyter_server_config.d/solara.json +7 -0
  3. solara/__init__.py +124 -0
  4. solara/__main__.py +765 -0
  5. solara/_stores.py +297 -0
  6. solara/alias.py +6 -0
  7. solara/autorouting.py +555 -0
  8. solara/cache.py +305 -0
  9. solara/checks.html +71 -0
  10. solara/checks.py +227 -0
  11. solara/comm.py +28 -0
  12. solara/components/__init__.py +77 -0
  13. solara/components/alert.py +155 -0
  14. solara/components/applayout.py +397 -0
  15. solara/components/button.py +85 -0
  16. solara/components/card.py +87 -0
  17. solara/components/checkbox.py +50 -0
  18. solara/components/code_highlight_css.py +11 -0
  19. solara/components/code_highlight_css.vue +63 -0
  20. solara/components/columns.py +159 -0
  21. solara/components/component_vue.py +134 -0
  22. solara/components/cross_filter.py +335 -0
  23. solara/components/dataframe.py +546 -0
  24. solara/components/datatable.py +214 -0
  25. solara/components/datatable.vue +175 -0
  26. solara/components/details.py +56 -0
  27. solara/components/download.vue +35 -0
  28. solara/components/echarts.py +86 -0
  29. solara/components/echarts.vue +139 -0
  30. solara/components/figure_altair.py +39 -0
  31. solara/components/file_browser.py +181 -0
  32. solara/components/file_download.py +199 -0
  33. solara/components/file_drop.py +159 -0
  34. solara/components/file_drop.vue +83 -0
  35. solara/components/file_list_widget.vue +78 -0
  36. solara/components/head.py +27 -0
  37. solara/components/head_tag.py +49 -0
  38. solara/components/head_tag.vue +60 -0
  39. solara/components/image.py +173 -0
  40. solara/components/input.py +456 -0
  41. solara/components/input_text_area.py +86 -0
  42. solara/components/link.py +55 -0
  43. solara/components/markdown.py +441 -0
  44. solara/components/markdown_editor.py +33 -0
  45. solara/components/markdown_editor.vue +359 -0
  46. solara/components/matplotlib.py +74 -0
  47. solara/components/meta.py +47 -0
  48. solara/components/misc.py +333 -0
  49. solara/components/pivot_table.py +258 -0
  50. solara/components/pivot_table.vue +158 -0
  51. solara/components/progress.py +47 -0
  52. solara/components/select.py +182 -0
  53. solara/components/select.vue +27 -0
  54. solara/components/slider.py +442 -0
  55. solara/components/slider_date.vue +56 -0
  56. solara/components/spinner-solara.vue +105 -0
  57. solara/components/spinner.py +45 -0
  58. solara/components/sql_code.py +41 -0
  59. solara/components/sql_code.vue +125 -0
  60. solara/components/style.py +105 -0
  61. solara/components/switch.py +71 -0
  62. solara/components/tab_navigation.py +37 -0
  63. solara/components/title.py +90 -0
  64. solara/components/title.vue +38 -0
  65. solara/components/togglebuttons.py +200 -0
  66. solara/components/tooltip.py +61 -0
  67. solara/core.py +42 -0
  68. solara/datatypes.py +143 -0
  69. solara/express.py +241 -0
  70. solara/hooks/__init__.py +4 -0
  71. solara/hooks/dataframe.py +99 -0
  72. solara/hooks/misc.py +263 -0
  73. solara/hooks/use_reactive.py +151 -0
  74. solara/hooks/use_thread.py +129 -0
  75. solara/kitchensink.py +8 -0
  76. solara/lab/__init__.py +34 -0
  77. solara/lab/components/__init__.py +7 -0
  78. solara/lab/components/chat.py +215 -0
  79. solara/lab/components/confirmation_dialog.py +163 -0
  80. solara/lab/components/cross_filter.py +7 -0
  81. solara/lab/components/input_date.py +339 -0
  82. solara/lab/components/input_time.py +133 -0
  83. solara/lab/components/menu.py +181 -0
  84. solara/lab/components/menu.vue +38 -0
  85. solara/lab/components/tabs.py +274 -0
  86. solara/lab/components/theming.py +98 -0
  87. solara/lab/components/theming.vue +72 -0
  88. solara/lab/hooks/__init__.py +0 -0
  89. solara/lab/hooks/dataframe.py +2 -0
  90. solara/lab/toestand.py +3 -0
  91. solara/lab/utils/__init__.py +2 -0
  92. solara/lab/utils/cookies.py +5 -0
  93. solara/lab/utils/dataframe.py +165 -0
  94. solara/lab/utils/headers.py +5 -0
  95. solara/layout.py +44 -0
  96. solara/lifecycle.py +46 -0
  97. solara/minisettings.py +141 -0
  98. solara/py.typed +0 -0
  99. solara/reactive.py +99 -0
  100. solara/routing.py +268 -0
  101. solara/scope/__init__.py +88 -0
  102. solara/scope/types.py +55 -0
  103. solara/server/__init__.py +0 -0
  104. solara/server/app.py +527 -0
  105. solara/server/assets/custom.css +1 -0
  106. solara/server/assets/custom.js +1 -0
  107. solara/server/assets/favicon.png +0 -0
  108. solara/server/assets/favicon.svg +5 -0
  109. solara/server/assets/style.css +1681 -0
  110. solara/server/assets/theme-dark.css +437 -0
  111. solara/server/assets/theme-light.css +420 -0
  112. solara/server/assets/theme.js +3 -0
  113. solara/server/cdn_helper.py +91 -0
  114. solara/server/esm.py +71 -0
  115. solara/server/fastapi.py +5 -0
  116. solara/server/flask.py +297 -0
  117. solara/server/jupyter/__init__.py +2 -0
  118. solara/server/jupyter/cdn_handler.py +28 -0
  119. solara/server/jupyter/server_extension.py +40 -0
  120. solara/server/jupyter/solara.py +91 -0
  121. solara/server/jupytertools.py +46 -0
  122. solara/server/kernel.py +388 -0
  123. solara/server/kernel_context.py +467 -0
  124. solara/server/patch.py +564 -0
  125. solara/server/pyinstaller/__init__.py +9 -0
  126. solara/server/pyinstaller/hook-ipyreact.py +5 -0
  127. solara/server/pyinstaller/hook-ipyvuetify.py +5 -0
  128. solara/server/pyinstaller/hook-solara.py +9 -0
  129. solara/server/qt.py +113 -0
  130. solara/server/reload.py +251 -0
  131. solara/server/server.py +484 -0
  132. solara/server/settings.py +249 -0
  133. solara/server/shell.py +269 -0
  134. solara/server/starlette.py +770 -0
  135. solara/server/static/ansi.js +270 -0
  136. solara/server/static/highlight-dark.css +82 -0
  137. solara/server/static/highlight.css +43 -0
  138. solara/server/static/main-vuetify.js +272 -0
  139. solara/server/static/main.js +163 -0
  140. solara/server/static/solara_bootstrap.py +129 -0
  141. solara/server/static/sun.svg +23 -0
  142. solara/server/static/webworker.js +42 -0
  143. solara/server/telemetry.py +212 -0
  144. solara/server/templates/index.html.j2 +1 -0
  145. solara/server/templates/loader-plain.css +11 -0
  146. solara/server/templates/loader-plain.html +20 -0
  147. solara/server/templates/loader-solara.css +111 -0
  148. solara/server/templates/loader-solara.html +40 -0
  149. solara/server/templates/plain.html +82 -0
  150. solara/server/templates/solara.html.j2 +486 -0
  151. solara/server/threaded.py +84 -0
  152. solara/server/utils.py +44 -0
  153. solara/server/websocket.py +45 -0
  154. solara/settings.py +86 -0
  155. solara/tasks.py +893 -0
  156. solara/template/button.py +16 -0
  157. solara/template/markdown.py +42 -0
  158. solara/template/portal/.flake8 +6 -0
  159. solara/template/portal/.pre-commit-config.yaml +28 -0
  160. solara/template/portal/LICENSE +21 -0
  161. solara/template/portal/Procfile +7 -0
  162. solara/template/portal/mypy.ini +3 -0
  163. solara/template/portal/pyproject.toml +26 -0
  164. solara/template/portal/solara_portal/__init__.py +4 -0
  165. solara/template/portal/solara_portal/components/__init__.py +2 -0
  166. solara/template/portal/solara_portal/components/article.py +28 -0
  167. solara/template/portal/solara_portal/components/data.py +28 -0
  168. solara/template/portal/solara_portal/components/header.py +6 -0
  169. solara/template/portal/solara_portal/components/layout.py +6 -0
  170. solara/template/portal/solara_portal/content/articles/equis-in-vidi.md +85 -0
  171. solara/template/portal/solara_portal/content/articles/substiterat-vati.md +70 -0
  172. solara/template/portal/solara_portal/data.py +60 -0
  173. solara/template/portal/solara_portal/pages/__init__.py +67 -0
  174. solara/template/portal/solara_portal/pages/article/__init__.py +26 -0
  175. solara/template/portal/solara_portal/pages/tabular.py +29 -0
  176. solara/template/portal/solara_portal/pages/viz/__init__.py +70 -0
  177. solara/template/portal/solara_portal/pages/viz/overview.py +14 -0
  178. solara/test/__init__.py +0 -0
  179. solara/test/pytest_plugin.py +783 -0
  180. solara/toestand.py +998 -0
  181. solara/util.py +348 -0
  182. solara/validate_hooks.py +258 -0
  183. solara/website/__init__.py +0 -0
  184. solara/website/assets/custom.css +444 -0
  185. solara/website/assets/images/logo-small.png +0 -0
  186. solara/website/assets/images/logo.svg +17 -0
  187. solara/website/assets/images/logo_white.svg +50 -0
  188. solara/website/assets/theme.js +8 -0
  189. solara/website/components/__init__.py +5 -0
  190. solara/website/components/algolia.py +6 -0
  191. solara/website/components/algolia.vue +24 -0
  192. solara/website/components/algolia_api.vue +202 -0
  193. solara/website/components/breadcrumbs.py +28 -0
  194. solara/website/components/contact.py +144 -0
  195. solara/website/components/docs.py +143 -0
  196. solara/website/components/header.py +75 -0
  197. solara/website/components/mailchimp.py +12 -0
  198. solara/website/components/mailchimp.vue +47 -0
  199. solara/website/components/markdown.py +99 -0
  200. solara/website/components/markdown_nav.vue +34 -0
  201. solara/website/components/notebook.py +171 -0
  202. solara/website/components/sidebar.py +105 -0
  203. solara/website/pages/__init__.py +370 -0
  204. solara/website/pages/about/__init__.py +9 -0
  205. solara/website/pages/about/about.md +3 -0
  206. solara/website/pages/apps/__init__.py +16 -0
  207. solara/website/pages/apps/authorization/__init__.py +119 -0
  208. solara/website/pages/apps/authorization/admin.py +12 -0
  209. solara/website/pages/apps/authorization/users.py +12 -0
  210. solara/website/pages/apps/jupyter-dashboard-1.py +116 -0
  211. solara/website/pages/apps/layout-demo.py +40 -0
  212. solara/website/pages/apps/multipage/__init__.py +38 -0
  213. solara/website/pages/apps/multipage/page1.py +26 -0
  214. solara/website/pages/apps/multipage/page2.py +34 -0
  215. solara/website/pages/apps/scatter.py +136 -0
  216. solara/website/pages/apps/scrolling.py +63 -0
  217. solara/website/pages/apps/tutorial-streamlit.py +18 -0
  218. solara/website/pages/careers/__init__.py +27 -0
  219. solara/website/pages/changelog/__init__.py +10 -0
  220. solara/website/pages/changelog/changelog.md +372 -0
  221. solara/website/pages/contact/__init__.py +34 -0
  222. solara/website/pages/doc_use_download.py +85 -0
  223. solara/website/pages/documentation/__init__.py +90 -0
  224. solara/website/pages/documentation/advanced/__init__.py +9 -0
  225. solara/website/pages/documentation/advanced/content/00-overview.md +1 -0
  226. solara/website/pages/documentation/advanced/content/10-howto/00-overview.md +6 -0
  227. solara/website/pages/documentation/advanced/content/10-howto/10-multipage.md +196 -0
  228. solara/website/pages/documentation/advanced/content/10-howto/20-layout.md +125 -0
  229. solara/website/pages/documentation/advanced/content/10-howto/30-testing.md +417 -0
  230. solara/website/pages/documentation/advanced/content/10-howto/31-debugging.md +69 -0
  231. solara/website/pages/documentation/advanced/content/10-howto/40-embed.md +50 -0
  232. solara/website/pages/documentation/advanced/content/10-howto/50-ipywidget_libraries.md +124 -0
  233. solara/website/pages/documentation/advanced/content/15-reference/00-overview.md +3 -0
  234. solara/website/pages/documentation/advanced/content/15-reference/40-static_files.md +31 -0
  235. solara/website/pages/documentation/advanced/content/15-reference/41-asset-files.md +72 -0
  236. solara/website/pages/documentation/advanced/content/15-reference/60-static-site-generation.md +59 -0
  237. solara/website/pages/documentation/advanced/content/15-reference/70-search.md +34 -0
  238. solara/website/pages/documentation/advanced/content/15-reference/80-reloading.md +34 -0
  239. solara/website/pages/documentation/advanced/content/15-reference/90-notebook-support.md +7 -0
  240. solara/website/pages/documentation/advanced/content/15-reference/95-caching.md +148 -0
  241. solara/website/pages/documentation/advanced/content/20-understanding/00-introduction.md +10 -0
  242. solara/website/pages/documentation/advanced/content/20-understanding/05-ipywidgets.md +35 -0
  243. solara/website/pages/documentation/advanced/content/20-understanding/06-ipyvuetify.md +42 -0
  244. solara/website/pages/documentation/advanced/content/20-understanding/10-reacton.md +28 -0
  245. solara/website/pages/documentation/advanced/content/20-understanding/12-reacton-basics.md +108 -0
  246. solara/website/pages/documentation/advanced/content/20-understanding/15-anatomy.md +23 -0
  247. solara/website/pages/documentation/advanced/content/20-understanding/17-rules-of-hooks.md +192 -0
  248. solara/website/pages/documentation/advanced/content/20-understanding/18-containers.md +166 -0
  249. solara/website/pages/documentation/advanced/content/20-understanding/20-solara.md +18 -0
  250. solara/website/pages/documentation/advanced/content/20-understanding/40-routing.md +256 -0
  251. solara/website/pages/documentation/advanced/content/20-understanding/50-solara-server.md +108 -0
  252. solara/website/pages/documentation/advanced/content/20-understanding/60-voila.md +12 -0
  253. solara/website/pages/documentation/advanced/content/30-enterprise/00-overview.md +7 -0
  254. solara/website/pages/documentation/advanced/content/30-enterprise/10-oauth.md +187 -0
  255. solara/website/pages/documentation/advanced/content/40-development/00-overview.md +0 -0
  256. solara/website/pages/documentation/advanced/content/40-development/01-contribute.md +45 -0
  257. solara/website/pages/documentation/advanced/content/40-development/10-setup.md +76 -0
  258. solara/website/pages/documentation/api/__init__.py +19 -0
  259. solara/website/pages/documentation/api/cross_filter/__init__.py +9 -0
  260. solara/website/pages/documentation/api/cross_filter/cross_filter_dataframe.py +22 -0
  261. solara/website/pages/documentation/api/cross_filter/cross_filter_report.py +20 -0
  262. solara/website/pages/documentation/api/cross_filter/cross_filter_select.py +20 -0
  263. solara/website/pages/documentation/api/cross_filter/cross_filter_slider.py +20 -0
  264. solara/website/pages/documentation/api/hooks/__init__.py +9 -0
  265. solara/website/pages/documentation/api/hooks/use_cross_filter.py +23 -0
  266. solara/website/pages/documentation/api/hooks/use_dark_effective.py +12 -0
  267. solara/website/pages/documentation/api/hooks/use_effect.md +43 -0
  268. solara/website/pages/documentation/api/hooks/use_effect.py +9 -0
  269. solara/website/pages/documentation/api/hooks/use_exception.py +31 -0
  270. solara/website/pages/documentation/api/hooks/use_memo.md +16 -0
  271. solara/website/pages/documentation/api/hooks/use_memo.py +9 -0
  272. solara/website/pages/documentation/api/hooks/use_previous.py +30 -0
  273. solara/website/pages/documentation/api/hooks/use_reactive.py +16 -0
  274. solara/website/pages/documentation/api/hooks/use_state.py +10 -0
  275. solara/website/pages/documentation/api/hooks/use_state_or_update.py +66 -0
  276. solara/website/pages/documentation/api/hooks/use_thread.md +64 -0
  277. solara/website/pages/documentation/api/hooks/use_thread.py +42 -0
  278. solara/website/pages/documentation/api/hooks/use_trait_observe.py +12 -0
  279. solara/website/pages/documentation/api/routing/__init__.py +9 -0
  280. solara/website/pages/documentation/api/routing/generate_routes.py +10 -0
  281. solara/website/pages/documentation/api/routing/generate_routes_directory.py +10 -0
  282. solara/website/pages/documentation/api/routing/resolve_path.py +35 -0
  283. solara/website/pages/documentation/api/routing/route.py +29 -0
  284. solara/website/pages/documentation/api/routing/use_route.py +76 -0
  285. solara/website/pages/documentation/api/routing/use_router.py +16 -0
  286. solara/website/pages/documentation/api/utilities/__init__.py +9 -0
  287. solara/website/pages/documentation/api/utilities/component_vue.py +10 -0
  288. solara/website/pages/documentation/api/utilities/computed.py +16 -0
  289. solara/website/pages/documentation/api/utilities/display.py +16 -0
  290. solara/website/pages/documentation/api/utilities/get_kernel_id.py +16 -0
  291. solara/website/pages/documentation/api/utilities/get_session_id.py +16 -0
  292. solara/website/pages/documentation/api/utilities/memoize.py +35 -0
  293. solara/website/pages/documentation/api/utilities/on_kernel_start.py +44 -0
  294. solara/website/pages/documentation/api/utilities/reactive.py +16 -0
  295. solara/website/pages/documentation/api/utilities/widget.py +104 -0
  296. solara/website/pages/documentation/components/__init__.py +12 -0
  297. solara/website/pages/documentation/components/advanced/__init__.py +9 -0
  298. solara/website/pages/documentation/components/advanced/link.py +25 -0
  299. solara/website/pages/documentation/components/advanced/meta.py +17 -0
  300. solara/website/pages/documentation/components/advanced/style.py +43 -0
  301. solara/website/pages/documentation/components/common.py +9 -0
  302. solara/website/pages/documentation/components/data/__init__.py +9 -0
  303. solara/website/pages/documentation/components/data/dataframe.py +44 -0
  304. solara/website/pages/documentation/components/data/pivot_table.py +81 -0
  305. solara/website/pages/documentation/components/enterprise/__init__.py +9 -0
  306. solara/website/pages/documentation/components/enterprise/avatar.py +24 -0
  307. solara/website/pages/documentation/components/enterprise/avatar_menu.py +25 -0
  308. solara/website/pages/documentation/components/input/__init__.py +9 -0
  309. solara/website/pages/documentation/components/input/button.py +23 -0
  310. solara/website/pages/documentation/components/input/checkbox.py +10 -0
  311. solara/website/pages/documentation/components/input/file_browser.py +30 -0
  312. solara/website/pages/documentation/components/input/file_drop.py +76 -0
  313. solara/website/pages/documentation/components/input/input.py +43 -0
  314. solara/website/pages/documentation/components/input/select.py +22 -0
  315. solara/website/pages/documentation/components/input/slider.py +29 -0
  316. solara/website/pages/documentation/components/input/switch.py +10 -0
  317. solara/website/pages/documentation/components/input/togglebuttons.py +21 -0
  318. solara/website/pages/documentation/components/lab/__init__.py +9 -0
  319. solara/website/pages/documentation/components/lab/chat.py +109 -0
  320. solara/website/pages/documentation/components/lab/confirmation_dialog.py +55 -0
  321. solara/website/pages/documentation/components/lab/cookies_headers.py +48 -0
  322. solara/website/pages/documentation/components/lab/input_date.py +20 -0
  323. solara/website/pages/documentation/components/lab/input_time.py +15 -0
  324. solara/website/pages/documentation/components/lab/menu.py +22 -0
  325. solara/website/pages/documentation/components/lab/tab.py +25 -0
  326. solara/website/pages/documentation/components/lab/tabs.py +45 -0
  327. solara/website/pages/documentation/components/lab/task.py +11 -0
  328. solara/website/pages/documentation/components/lab/theming.py +74 -0
  329. solara/website/pages/documentation/components/lab/use_task.py +11 -0
  330. solara/website/pages/documentation/components/layout/__init__.py +9 -0
  331. solara/website/pages/documentation/components/layout/app_bar.py +16 -0
  332. solara/website/pages/documentation/components/layout/app_bar_title.py +16 -0
  333. solara/website/pages/documentation/components/layout/app_layout.py +24 -0
  334. solara/website/pages/documentation/components/layout/card.py +15 -0
  335. solara/website/pages/documentation/components/layout/card_actions.py +16 -0
  336. solara/website/pages/documentation/components/layout/column.py +30 -0
  337. solara/website/pages/documentation/components/layout/columns.py +27 -0
  338. solara/website/pages/documentation/components/layout/columns_responsive.py +66 -0
  339. solara/website/pages/documentation/components/layout/details.py +13 -0
  340. solara/website/pages/documentation/components/layout/griddraggable.py +62 -0
  341. solara/website/pages/documentation/components/layout/gridfixed.py +19 -0
  342. solara/website/pages/documentation/components/layout/hbox.py +18 -0
  343. solara/website/pages/documentation/components/layout/row.py +30 -0
  344. solara/website/pages/documentation/components/layout/sidebar.py +24 -0
  345. solara/website/pages/documentation/components/layout/vbox.py +19 -0
  346. solara/website/pages/documentation/components/output/__init__.py +9 -0
  347. solara/website/pages/documentation/components/output/file_download.py +11 -0
  348. solara/website/pages/documentation/components/output/html.py +19 -0
  349. solara/website/pages/documentation/components/output/image.py +11 -0
  350. solara/website/pages/documentation/components/output/markdown.py +57 -0
  351. solara/website/pages/documentation/components/output/markdown_editor.py +51 -0
  352. solara/website/pages/documentation/components/output/sql_code.py +83 -0
  353. solara/website/pages/documentation/components/output/tooltip.py +11 -0
  354. solara/website/pages/documentation/components/page/__init__.py +9 -0
  355. solara/website/pages/documentation/components/page/head.py +15 -0
  356. solara/website/pages/documentation/components/page/title.py +25 -0
  357. solara/website/pages/documentation/components/status/__init__.py +9 -0
  358. solara/website/pages/documentation/components/status/error.py +39 -0
  359. solara/website/pages/documentation/components/status/info.py +39 -0
  360. solara/website/pages/documentation/components/status/progress.py +10 -0
  361. solara/website/pages/documentation/components/status/spinner.py +11 -0
  362. solara/website/pages/documentation/components/status/success.py +40 -0
  363. solara/website/pages/documentation/components/status/warning.py +47 -0
  364. solara/website/pages/documentation/components/viz/__init__.py +9 -0
  365. solara/website/pages/documentation/components/viz/altair.py +42 -0
  366. solara/website/pages/documentation/components/viz/echarts.py +77 -0
  367. solara/website/pages/documentation/components/viz/matplotlib.py +30 -0
  368. solara/website/pages/documentation/components/viz/plotly.py +63 -0
  369. solara/website/pages/documentation/components/viz/plotly_express.py +41 -0
  370. solara/website/pages/documentation/examples/__init__.py +54 -0
  371. solara/website/pages/documentation/examples/ai/__init__.py +11 -0
  372. solara/website/pages/documentation/examples/ai/chatbot.py +113 -0
  373. solara/website/pages/documentation/examples/ai/tokenizer.py +107 -0
  374. solara/website/pages/documentation/examples/basics/__init__.py +10 -0
  375. solara/website/pages/documentation/examples/basics/sine.py +28 -0
  376. solara/website/pages/documentation/examples/fullscreen/__init__.py +10 -0
  377. solara/website/pages/documentation/examples/fullscreen/authorization.py +3 -0
  378. solara/website/pages/documentation/examples/fullscreen/layout_demo.py +3 -0
  379. solara/website/pages/documentation/examples/fullscreen/multipage.py +3 -0
  380. solara/website/pages/documentation/examples/fullscreen/scatter.py +3 -0
  381. solara/website/pages/documentation/examples/fullscreen/scrolling.py +3 -0
  382. solara/website/pages/documentation/examples/fullscreen/tutorial_streamlit.py +3 -0
  383. solara/website/pages/documentation/examples/general/__init__.py +10 -0
  384. solara/website/pages/documentation/examples/general/custom_storage.py +70 -0
  385. solara/website/pages/documentation/examples/general/deploy_model.py +115 -0
  386. solara/website/pages/documentation/examples/general/live_update.py +32 -0
  387. solara/website/pages/documentation/examples/general/login_oauth.py +81 -0
  388. solara/website/pages/documentation/examples/general/mycard.vue +58 -0
  389. solara/website/pages/documentation/examples/general/pokemon_search.py +51 -0
  390. solara/website/pages/documentation/examples/general/vue_component.py +50 -0
  391. solara/website/pages/documentation/examples/ipycanvas.py +49 -0
  392. solara/website/pages/documentation/examples/libraries/__init__.py +10 -0
  393. solara/website/pages/documentation/examples/libraries/altair.py +65 -0
  394. solara/website/pages/documentation/examples/libraries/bqplot.py +39 -0
  395. solara/website/pages/documentation/examples/libraries/ipyleaflet.py +33 -0
  396. solara/website/pages/documentation/examples/libraries/ipyleaflet_advanced.py +66 -0
  397. solara/website/pages/documentation/examples/utilities/__init__.py +10 -0
  398. solara/website/pages/documentation/examples/utilities/calculator.py +157 -0
  399. solara/website/pages/documentation/examples/utilities/countdown_timer.py +62 -0
  400. solara/website/pages/documentation/examples/utilities/todo.py +196 -0
  401. solara/website/pages/documentation/examples/visualization/__init__.py +6 -0
  402. solara/website/pages/documentation/examples/visualization/annotator.py +67 -0
  403. solara/website/pages/documentation/examples/visualization/linked_views.py +81 -0
  404. solara/website/pages/documentation/examples/visualization/plotly.py +44 -0
  405. solara/website/pages/documentation/faq/__init__.py +12 -0
  406. solara/website/pages/documentation/faq/content/99-faq.md +112 -0
  407. solara/website/pages/documentation/getting_started/__init__.py +9 -0
  408. solara/website/pages/documentation/getting_started/content/00-quickstart.md +107 -0
  409. solara/website/pages/documentation/getting_started/content/01-introduction.md +125 -0
  410. solara/website/pages/documentation/getting_started/content/02-installing.md +134 -0
  411. solara/website/pages/documentation/getting_started/content/04-tutorials/00-overview.md +14 -0
  412. solara/website/pages/documentation/getting_started/content/04-tutorials/10_data_science.py +13 -0
  413. solara/website/pages/documentation/getting_started/content/04-tutorials/20-web-app.md +89 -0
  414. solara/website/pages/documentation/getting_started/content/04-tutorials/30-ipywidgets.md +124 -0
  415. solara/website/pages/documentation/getting_started/content/04-tutorials/40-streamlit.md +146 -0
  416. solara/website/pages/documentation/getting_started/content/04-tutorials/50-dash.md +144 -0
  417. solara/website/pages/documentation/getting_started/content/04-tutorials/60-jupyter-dashboard-part1.py +65 -0
  418. solara/website/pages/documentation/getting_started/content/04-tutorials/SF_crime_sample.csv.gz +0 -0
  419. solara/website/pages/documentation/getting_started/content/04-tutorials/_data_science.ipynb +445 -0
  420. solara/website/pages/documentation/getting_started/content/04-tutorials/_jupyter_dashboard_1.ipynb +1021 -0
  421. solara/website/pages/documentation/getting_started/content/05-fundamentals/00-overview.md +11 -0
  422. solara/website/pages/documentation/getting_started/content/05-fundamentals/10-components.md +228 -0
  423. solara/website/pages/documentation/getting_started/content/05-fundamentals/50-state-management.md +278 -0
  424. solara/website/pages/documentation/getting_started/content/07-deploying/00-overview.md +7 -0
  425. solara/website/pages/documentation/getting_started/content/07-deploying/10-self-hosted.md +305 -0
  426. solara/website/pages/documentation/getting_started/content/07-deploying/20-cloud-hosted.md +80 -0
  427. solara/website/pages/documentation/getting_started/content/80-what-is-lab.md +7 -0
  428. solara/website/pages/documentation/getting_started/content/90-troubleshoot.md +26 -0
  429. solara/website/pages/docutils.py +38 -0
  430. solara/website/pages/home.vue +1199 -0
  431. solara/website/pages/our_team/__init__.py +83 -0
  432. solara/website/pages/pricing/__init__.py +31 -0
  433. solara/website/pages/roadmap/__init__.py +11 -0
  434. solara/website/pages/roadmap/roadmap.md +47 -0
  435. solara/website/pages/scale_ipywidgets.py +45 -0
  436. solara/website/pages/showcase/__init__.py +105 -0
  437. solara/website/pages/showcase/domino_code_assist.py +60 -0
  438. solara/website/pages/showcase/planeto_tessa.py +19 -0
  439. solara/website/pages/showcase/solara_dev.py +54 -0
  440. solara/website/pages/showcase/solarathon_2023_team_2.py +22 -0
  441. solara/website/pages/showcase/solarathon_2023_team_4.py +22 -0
  442. solara/website/pages/showcase/solarathon_2023_team_5.py +23 -0
  443. solara/website/pages/showcase/solarathon_2023_team_6.py +34 -0
  444. solara/website/pages/showcase/wanderlust.py +27 -0
  445. solara/website/public/beach.jpeg +0 -0
  446. solara/website/public/logo.svg +6 -0
  447. solara/website/public/social/discord.svg +1 -0
  448. solara/website/public/social/github.svg +1 -0
  449. solara/website/public/social/twitter.svg +3 -0
  450. solara/website/public/success.html +25 -0
  451. solara/website/templates/index.html.j2 +117 -0
  452. solara/website/utils.py +51 -0
  453. solara/widgets/__init__.py +1 -0
  454. solara/widgets/vue/gridlayout.vue +107 -0
  455. solara/widgets/vue/html.vue +4 -0
  456. solara/widgets/vue/navigator.vue +134 -0
  457. solara/widgets/vue/vegalite.vue +130 -0
  458. solara/widgets/widgets.py +74 -0
  459. solara_ui-1.45.0.data/data/etc/jupyter/jupyter_notebook_config.d/solara.json +7 -0
  460. solara_ui-1.45.0.data/data/etc/jupyter/jupyter_server_config.d/solara.json +7 -0
  461. solara_ui-1.45.0.dist-info/METADATA +162 -0
  462. solara_ui-1.45.0.dist-info/RECORD +464 -0
  463. solara_ui-1.45.0.dist-info/WHEEL +4 -0
  464. solara_ui-1.45.0.dist-info/licenses/LICENSE +21 -0
solara/util.py ADDED
@@ -0,0 +1,348 @@
1
+ import base64
2
+ import contextlib
3
+ import functools
4
+ import gzip
5
+ import hashlib
6
+ import json
7
+ import os
8
+ import sys
9
+ import threading
10
+ from collections import abc
11
+ from pathlib import Path
12
+ from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union
13
+ from urllib.parse import urlencode
14
+
15
+ if TYPE_CHECKING:
16
+ import numpy as np
17
+
18
+ import ipyvuetify
19
+ import ipywidgets
20
+ import reacton
21
+
22
+ import solara
23
+
24
+ SOLARA_ALLOW_OTHER_TRACER = os.environ.get("SOLARA_ALLOW_OTHER_TRACER", False) in (True, "True", "true", "1")
25
+ ipyvuetify_major_version = int(ipyvuetify.__version__.split(".")[0])
26
+ ipywidgets_major = int(ipywidgets.__version__.split(".")[0])
27
+
28
+ try:
29
+ threading.Thread(target=lambda: None).start()
30
+ has_threads = True
31
+ except RuntimeError:
32
+ has_threads = False
33
+
34
+
35
+ from reacton.utils import equals as equals_extra
36
+
37
+
38
+ def equals_pickle(a, b):
39
+ """Compare two values for equality.
40
+
41
+ Avoid false negative, e.g. when comparing dataframes, we want to compare
42
+ the data, not the object identity.
43
+
44
+ """
45
+ if equals_extra(a, b):
46
+ return True
47
+ import pickle
48
+
49
+ try:
50
+ if pickle.dumps(a) == pickle.dumps(b):
51
+ return True
52
+ except Exception:
53
+ pass
54
+ return False
55
+
56
+
57
+ def github_url(file):
58
+ rel_path = os.path.relpath(file, Path(solara.__file__).parent.parent)
59
+ github_url = solara.github_url + f"/blob/{solara.git_branch}/" + rel_path
60
+ return github_url
61
+
62
+
63
+ def pycafe_url(*, path: Path, requirements: Optional[List[str]] = None):
64
+ json_object = {"code": path.read_text("utf8")}
65
+ if requirements:
66
+ json_object["requirements"] = "\n".join(requirements)
67
+ json_text = json.dumps(json_object)
68
+ # gzip -> base64
69
+ compressed_json_text = gzip.compress(json_text.encode("utf8"))
70
+ base64_text = base64.b64encode(compressed_json_text).decode("utf8")
71
+ query = urlencode({"c": base64_text})
72
+ base_url = "https://app.py.cafe"
73
+ return f"{base_url}/snippet/solara?{query}"
74
+
75
+
76
+ def github_edit_url(file):
77
+ # e.g. https://github.com/widgetti/solara/edit/master/solara/__init__.py
78
+ rel_path = os.path.relpath(file, Path(solara.__file__).parent.parent)
79
+ github_url = solara.github_url + f"/edit/{solara.git_branch}/" + rel_path
80
+ return github_url
81
+
82
+
83
+ def load_file_as_data_url(file_name, mime):
84
+ with open(file_name, "rb") as f:
85
+ data = f.read()
86
+ return f"data:{mime};base64," + base64.b64encode(data).decode("utf-8")
87
+
88
+
89
+ def isinstanceof(object, spec: str):
90
+ """Check if object is instance of type '<modulename>:<classname>'
91
+
92
+ This can avoid a runtime dependency, since we do not need to import `modulename`.
93
+
94
+ >>> import numpy as np
95
+ >>> isinstanceof(np.arange(2), "numpy:ndarray")
96
+ True
97
+ """
98
+ module_name, classname = spec.split(":")
99
+ module = sys.modules.get(module_name)
100
+ if module:
101
+ cls = getattr(module, classname)
102
+ return isinstance(object, cls)
103
+ return False
104
+
105
+
106
+ def numpy_to_image(data: "np.ndarray", format="png"):
107
+ import io
108
+
109
+ if data.ndim == 3:
110
+ try:
111
+ import PIL.Image
112
+ except ModuleNotFoundError:
113
+ raise ModuleNotFoundError("Pillow is required to convert numpy array to image, use pip install pillow to install it.")
114
+ if data.shape[2] == 3:
115
+ im = PIL.Image.fromarray(data[::], "RGB")
116
+ elif data.shape[2] == 4:
117
+ im = PIL.Image.fromarray(data[::], "RGBA")
118
+ else:
119
+ raise ValueError(f"Expected last dimension to have 3 or 4 dimensions, total shape we got was {data.shape}")
120
+ f = io.BytesIO()
121
+ im.save(f, format)
122
+ return f.getvalue()
123
+ else:
124
+ raise ValueError(f"Expected an image with 3 dimensions (height, width, channel), not {data.shape}")
125
+
126
+
127
+ @contextlib.contextmanager
128
+ def cwd(path):
129
+ cwd = os.getcwd()
130
+ try:
131
+ os.chdir(path)
132
+ yield
133
+ finally:
134
+ os.chdir(cwd)
135
+
136
+
137
+ def numpy_equals(a, b):
138
+ import numpy as np
139
+
140
+ if a is b:
141
+ return True
142
+ if a is None or b is None:
143
+ return False
144
+ if np.all(a == b):
145
+ return True
146
+ return False
147
+
148
+
149
+ def _combine_classes(class_list: List[str]) -> str:
150
+ return " ".join(class_list)
151
+
152
+
153
+ def _flatten_style(style: Union[str, Dict, None] = None) -> str:
154
+ if style is None:
155
+ return ""
156
+ elif isinstance(style, str):
157
+ return style
158
+ elif isinstance(style, dict):
159
+ return ";".join(f"{k}:{v}" for k, v in style.items()) + ";"
160
+ else:
161
+ raise ValueError(f"Expected style to be a string or dict, got {type(style)}")
162
+
163
+
164
+ def import_item(name: str):
165
+ """Import an object by name like solara.cache.LRU"""
166
+ parts = name.rsplit(".", 2)
167
+ if len(parts) == 1:
168
+ return __import__(name)
169
+ else:
170
+ module = __import__(".".join(parts[:-1]), fromlist=[parts[-1]])
171
+ return getattr(module, parts[-1])
172
+
173
+
174
+ def get_solara_home() -> Path:
175
+ """Get solara home directory, defaults to ~/.solara.
176
+
177
+ The $SOLARA_HOME environment variable can be set to override this default.
178
+
179
+ If $SOLARA_HOME is not defined and ~ cannot be expanded, the current working directory + ".solara" is used.
180
+ """
181
+ os_home = None
182
+ try:
183
+ os_home = Path.home()
184
+ except Exception:
185
+ pass
186
+ if "SOLARA_HOME" in os.environ:
187
+ return Path(os.environ["SOLARA_HOME"])
188
+ elif os_home:
189
+ return os_home / ".solara"
190
+ else:
191
+ return Path(os.getcwd()) / ".solara"
192
+
193
+
194
+ def parse_size(size: str) -> int:
195
+ """Given a human readable size, return the number of bytes.
196
+
197
+ Supports GB, MB, KB, and bytes. E.g. 10GB, 500MB, 1KB, 1000
198
+
199
+ Commas and _ are ignored, e.g. 1,000,000 is the same as 1000000.
200
+ """
201
+ size = size.replace(",", "").replace("_", "").upper()
202
+ if size.endswith("GB"):
203
+ return int(float(size[:-2]) * 1024 * 1024 * 1024)
204
+ elif size.endswith("MB"):
205
+ return int(float(size[:-2]) * 1024 * 1024)
206
+ elif size.endswith("KB"):
207
+ return int(float(size[:-2]) * 1024)
208
+ elif size.endswith("B"):
209
+ return int(float(size[:-1]))
210
+ else:
211
+ return int(size)
212
+
213
+
214
+ def nested_get(object, dotted_name: str, default=None):
215
+ names = dotted_name.split(".")
216
+ for name in names:
217
+ if isinstance(object, abc.Mapping):
218
+ if name == names[-1]:
219
+ object = object.get(name, default)
220
+ else:
221
+ object = object.get(name)
222
+ else:
223
+ if name == names[-1]:
224
+ object = getattr(object, name, default)
225
+ else:
226
+ object = getattr(object, name)
227
+ return object
228
+
229
+
230
+ # inherit from BaseException so less change of being caught
231
+ # in an except
232
+ class CancelledError(BaseException):
233
+ pass
234
+
235
+
236
+ # not available in python 3.6
237
+ class nullcontext(contextlib.AbstractContextManager):
238
+ def __init__(self, enter_result=None):
239
+ self.enter_result = enter_result
240
+
241
+ def __enter__(self):
242
+ return self.enter_result
243
+
244
+ def __exit__(self, *excinfo):
245
+ pass
246
+
247
+
248
+ @contextlib.contextmanager
249
+ def cancel_guard(cancelled: threading.Event):
250
+ def tracefunc(frame, event, arg):
251
+ # this gets called at least for every line executed
252
+ if cancelled.is_set():
253
+ rc = reacton.core.get_render_context(required=False)
254
+ # we do not want to cancel the rendering cycle
255
+ if rc is None or not rc._is_rendering:
256
+ # this will bubble up
257
+ raise CancelledError()
258
+ if prev and SOLARA_ALLOW_OTHER_TRACER:
259
+ prev(frame, event, arg)
260
+ # keep tracing:
261
+ return tracefunc
262
+
263
+ # see https://docs.python.org/3/library/sys.html#sys.settrace
264
+ # it is for the calling thread only
265
+ # not every Python implementation has it
266
+ prev = None
267
+ if hasattr(sys, "gettrace"):
268
+ prev = sys.gettrace()
269
+ if hasattr(sys, "settrace"):
270
+ sys.settrace(tracefunc)
271
+ try:
272
+ yield
273
+ finally:
274
+ if hasattr(sys, "settrace"):
275
+ sys.settrace(prev)
276
+
277
+
278
+ def parse_timedelta(size: str) -> float:
279
+ """Turn a human readable time delta into seconds.
280
+ Supports days(d), hours (h), minutes (m) and seconds (s).
281
+ If not unit is specified, seconds is assumed.
282
+ >>> parse_timedelta("1d")
283
+ 86400
284
+ >>> parse_timedelta("1h")
285
+ 3600
286
+ >>> parse_timedelta("30m")
287
+ 1800
288
+ >>> parse_timedelta("10s")
289
+ 10
290
+ >>> parse_timedelta("10")
291
+ 10
292
+ """
293
+ if size.endswith("d"):
294
+ return float(size[:-1]) * 24 * 60 * 60
295
+ elif size.endswith("h"):
296
+ return float(size[:-1]) * 60 * 60
297
+ elif size.endswith("m"):
298
+ return float(size[:-1]) * 60
299
+ elif size.endswith("s"):
300
+ return float(size[:-1])
301
+ else:
302
+ return float(size)
303
+
304
+
305
+ def get_file_hash(path: Path, algorithm="md5") -> Tuple[bytes, str]:
306
+ """Compute the hash of a file. Note that we also return the file content as bytes."""
307
+ data = path.read_bytes()
308
+ if sys.version_info[:2] < (3, 9):
309
+ # usedforsecurity is only available in Python 3.9+
310
+ h = hashlib.new(algorithm)
311
+ else:
312
+ h = hashlib.new(algorithm, usedforsecurity=False) # type: ignore
313
+ h.update(data)
314
+ return data, h.hexdigest()
315
+
316
+
317
+ def is_running_in_colab():
318
+ try:
319
+ import google.colab # noqa
320
+
321
+ return True
322
+ except ImportError:
323
+ return False
324
+
325
+
326
+ def is_running_in_vscode():
327
+ return "VSCODE_PID" in os.environ or "VSCODE_CWD" in os.environ
328
+
329
+
330
+ def is_running_in_voila():
331
+ return os.environ.get("SERVER_SOFTWARE", "").startswith("voila")
332
+
333
+
334
+ def once(f):
335
+ called = False
336
+ return_value = None
337
+
338
+ @functools.wraps(f)
339
+ def wrapper():
340
+ nonlocal called
341
+ nonlocal return_value
342
+ if called:
343
+ return return_value
344
+ called = True
345
+ return_value = f()
346
+ return return_value
347
+
348
+ return wrapper
@@ -0,0 +1,258 @@
1
+ import ast
2
+ import inspect
3
+ import re
4
+ import typing as t
5
+ from enum import Enum
6
+ import sys
7
+ import warnings
8
+
9
+ DEFAULT_USE_FUNCTIONS = ("^use_.*$",)
10
+
11
+
12
+ class InvalidReactivityCause(Enum):
13
+ USE_AFTER_RETURN = "early return"
14
+ CONDITIONAL_USE = "conditional"
15
+ LOOP_USE = "loop"
16
+ NESTED_FUNCTION_USE = "nested function"
17
+ VARIABLE_ASSIGNMENT = "assignment"
18
+ EXCEPTION_USE = "exception"
19
+
20
+
21
+ noqa_code_to_cause = {
22
+ "SH101": InvalidReactivityCause.USE_AFTER_RETURN,
23
+ "SH102": InvalidReactivityCause.CONDITIONAL_USE,
24
+ "SH103": InvalidReactivityCause.LOOP_USE,
25
+ "SH104": InvalidReactivityCause.NESTED_FUNCTION_USE,
26
+ "SH105": InvalidReactivityCause.VARIABLE_ASSIGNMENT,
27
+ "SH106": InvalidReactivityCause.EXCEPTION_USE,
28
+ }
29
+ cause_to_noqa_code = {v: k for k, v in noqa_code_to_cause.items()}
30
+ noqa_pattern = re.compile(r".*# noqa(?::\s*([A-Z]{2,3}\d{2,3})(?:,\s*([A-Z]{2,3}\d{2,3}))*)?")
31
+
32
+
33
+ if sys.version_info < (3, 11):
34
+ ScopeNodeType = t.Union[ast.For, ast.While, ast.If, ast.Try, ast.FunctionDef]
35
+ TryNodes = (ast.Try,)
36
+ else:
37
+ # except* nodes are only standardized in 3.11+
38
+ ScopeNodeType = t.Union[ast.For, ast.While, ast.If, ast.Try, ast.TryStar, ast.FunctionDef]
39
+ TryNodes = (ast.Try, ast.TryStar)
40
+
41
+
42
+ def line_to_noqa(line: str) -> t.Optional[t.Set[InvalidReactivityCause]]:
43
+ """Return set of noqa code for which to not do quality assurance checks, or None when to do all qa."""
44
+ if "#" in line:
45
+ no_qa = set()
46
+ match = noqa_pattern.match(line)
47
+ if match is not None:
48
+ # not sure why we get some None values
49
+ groups = [k for k in match.groups() if k is not None]
50
+ if groups: # `noqa: SHXXX`` found
51
+ for group in groups:
52
+ if group not in noqa_code_to_cause:
53
+ if group.startswith("SH"):
54
+ raise ValueError(f"Unknown noqa code {group}")
55
+ else:
56
+ no_qa.add(noqa_code_to_cause[group])
57
+ else: # we found `noqa`
58
+ # skip all qa
59
+ no_qa = set(noqa_code_to_cause.values())
60
+ return no_qa
61
+ else:
62
+ return None # no noqa found
63
+ return None # fast path, no comment found
64
+
65
+
66
+ def should_skip_qa(global_no_qa: t.Optional[t.Set[InvalidReactivityCause]], cause: InvalidReactivityCause, line: str) -> bool:
67
+ line_qa = line_to_noqa(line)
68
+ if global_no_qa is None and line_qa is None:
69
+ # there is not a noqa on the function or line
70
+ return False
71
+ else:
72
+ noqa = global_no_qa or set()
73
+ if line_qa is not None:
74
+ noqa |= line_qa
75
+ return cause in noqa
76
+
77
+
78
+ class HookValidationError(Exception):
79
+ def __init__(self, cause: InvalidReactivityCause, message: str):
80
+ self.cause = cause
81
+ super().__init__(message)
82
+
83
+
84
+ class HookValidator(ast.NodeVisitor):
85
+ def __init__(self, component: t.Callable, use_functions=DEFAULT_USE_FUNCTIONS):
86
+ self.use_functions = [re.compile(use_func) for use_func in use_functions]
87
+
88
+ self.root_function_return: t.Optional[ast.Return] = None
89
+ self.outer_scope: t.Optional[ScopeNodeType] = None
90
+
91
+ self.filename = component.__code__.co_filename
92
+ self.line_offset = component.__code__.co_firstlineno - 1
93
+ self.component = component
94
+
95
+ self.source = inspect.getsource(self.component)
96
+ # lines before we dedent
97
+ self.lines = self.source.split("\n")
98
+ # dedent the source code to avoid indentation errors
99
+ parsed_source = inspect.cleandoc(self.source)
100
+ parsed = ast.parse(parsed_source)
101
+ # Get nodes from inside the function body
102
+ func_definition = t.cast(ast.FunctionDef, parsed.body[0])
103
+ self.function_scope: ast.FunctionDef = func_definition
104
+ self._root_function_scope = self.function_scope
105
+ # None means, *DO* qa
106
+ self.no_qa: t.Optional[t.Set[InvalidReactivityCause]] = None
107
+
108
+ def run(self):
109
+ import solara.settings
110
+
111
+ if solara.settings.main.check_hooks == "off":
112
+ return
113
+ func_def_line = self.lines[self._root_function_scope.lineno - 1]
114
+ self.no_qa = line_to_noqa(func_def_line)
115
+ try:
116
+ for node in self._root_function_scope.body:
117
+ self.visit(node)
118
+ except HookValidationError as e:
119
+ if solara.settings.main.check_hooks == "error":
120
+ raise e
121
+ elif solara.settings.main.check_hooks == "warn":
122
+ warnings.warn(str(e))
123
+
124
+ def matches_use_function(self, name: str) -> bool:
125
+ return any(use_func.match(name) for use_func in self.use_functions)
126
+
127
+ def node_to_scope_cause(self, node: ScopeNodeType) -> InvalidReactivityCause:
128
+ if isinstance(node, ast.If):
129
+ return InvalidReactivityCause.CONDITIONAL_USE
130
+ elif isinstance(node, (ast.For, ast.While)):
131
+ return InvalidReactivityCause.LOOP_USE
132
+ elif isinstance(node, ast.FunctionDef):
133
+ return InvalidReactivityCause.NESTED_FUNCTION_USE
134
+ elif isinstance(node, TryNodes):
135
+ return InvalidReactivityCause.EXCEPTION_USE
136
+ else:
137
+ warnings.warn(f"Unexpected scope node type: {node}, line={node.lineno}")
138
+
139
+ def visit_Call(self, node: ast.Call):
140
+ """Records calls of use functions, i.e. solara.use_state(...)"""
141
+ func = node.func
142
+ if isinstance(func, ast.Call):
143
+ # Nested function, it will appear in another node later
144
+ return
145
+ id_ = None
146
+ if isinstance(func, ast.Name):
147
+ id_ = func.id
148
+ elif isinstance(func, ast.Attribute):
149
+ id_ = func.attr
150
+ if id_ is not None and self.matches_use_function(id_):
151
+ self.error_on_early_return(node, id_)
152
+ self.error_on_invalid_scope(node, id_)
153
+ self.generic_visit(node)
154
+
155
+ def visit_If(self, node: ast.If):
156
+ self._visit_children_using_scope(node)
157
+
158
+ def visit_For(self, node: ast.For):
159
+ self._visit_children_using_scope(node)
160
+
161
+ def visit_While(self, node: ast.While):
162
+ self._visit_children_using_scope(node)
163
+
164
+ def visit_Try(self, node: ast.Try) -> t.Any:
165
+ self._visit_children_using_scope(node)
166
+
167
+ if sys.version_info >= (3, 11):
168
+
169
+ def visit_TryStar(self, node: ast.TryStar) -> t.Any:
170
+ self._visit_children_using_scope(node)
171
+
172
+ def visit_FunctionDef(self, node: ast.FunctionDef):
173
+ old_function_scope = self.function_scope
174
+ self.function_scope = node
175
+ self._visit_children_using_scope(node)
176
+ self.function_scope = old_function_scope
177
+
178
+ def _visit_children_using_scope(self, node: ScopeNodeType):
179
+ outer_scope = self.outer_scope
180
+ self.outer_scope = node
181
+ for child in node.body:
182
+ self.visit(child)
183
+ self.outer_scope = outer_scope
184
+
185
+ def visit_Assign(self, node: ast.Assign):
186
+ self.error_on_invalid_assign(node)
187
+ self.generic_visit(node)
188
+
189
+ def visit_Return(self, node: ast.Return):
190
+ """
191
+ Records the earliest return statement in the function
192
+ """
193
+ # Returns are valid in nested functions
194
+ if self.function_scope != self._root_function_scope:
195
+ return
196
+ self.root_function_return = node
197
+ self.generic_visit(node)
198
+
199
+ def error_on_invalid_assign(self, node: ast.Assign):
200
+ if isinstance(node.value, ast.Attribute) and self.matches_use_function(node.value.attr):
201
+ line = node.lineno + self.line_offset
202
+ cause = InvalidReactivityCause.VARIABLE_ASSIGNMENT
203
+ message = f"Assigning the hook `{node.value.attr}` to a variable is not allowed since it complicates the tracking of valid hook use {_hint_supress(line, cause)}"
204
+
205
+ raise HookValidationError(
206
+ cause,
207
+ f"{self.get_source_context(line)}: {message}",
208
+ )
209
+
210
+ def error_on_early_return(self, use_node: ast.Call, use_node_id: str):
211
+ """
212
+ Checks if the latest use of a reactive function occurs after the earliest return
213
+ """
214
+ if self.root_function_return and self.root_function_return.lineno <= use_node.lineno:
215
+ offset_return = self.root_function_return.lineno + self.line_offset
216
+ offset_use = use_node.lineno + self.line_offset
217
+ cause = InvalidReactivityCause.USE_AFTER_RETURN
218
+ line = self.lines[use_node.lineno - 1]
219
+ if should_skip_qa(self.no_qa, cause, line):
220
+ return
221
+
222
+ raise HookValidationError(
223
+ cause,
224
+ f"""{self.get_source_context(offset_use)}: `{use_node_id}` found despite early return on line {offset_return}
225
+ {_hint_supress(line, cause)}""",
226
+ )
227
+
228
+ def error_on_invalid_scope(self, use_node: ast.Call, use_node_id: str):
229
+ """
230
+ Checks if the latest use of a reactive function occurs after the earliest return or in an invalid scope
231
+ such as try-except blocks.
232
+ """
233
+ if self.outer_scope is None:
234
+ return
235
+ offset_use = use_node.lineno + self.line_offset
236
+ cause = self.node_to_scope_cause(self.outer_scope)
237
+
238
+ scope_line = self.outer_scope.lineno + self.line_offset
239
+ line = self.lines[use_node.lineno - 1]
240
+ if should_skip_qa(self.no_qa, cause, line):
241
+ return
242
+ raise HookValidationError(
243
+ cause,
244
+ f"""{self.get_source_context(offset_use)}: `{use_node_id}` found within a {cause.value} created on line {scope_line}
245
+ {_hint_supress(line, cause)}""",
246
+ )
247
+
248
+ def get_source_context(self, lineno):
249
+ return f"{self.filename}:{lineno}: {self.component.__qualname__}"
250
+
251
+
252
+ def _hint_supress(line, cause):
253
+ return f"""To suppress this check, replace the line with:
254
+ {line} # noqa: {cause_to_noqa_code[cause]}
255
+
256
+ Make sure you understand the consequences of this, by reading about the rules of hooks at:
257
+ https://solara.dev/documentation/advanced/understanding/rules-of-hooks
258
+ """
File without changes