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/lifecycle.py ADDED
@@ -0,0 +1,46 @@
1
+ from types import FrameType, ModuleType
2
+ from typing import Callable, List, NamedTuple, Optional
3
+ from pathlib import Path
4
+ import inspect
5
+
6
+
7
+ class _on_kernel_callback_entry(NamedTuple):
8
+ callback: Callable[[], Optional[Callable[[], None]]]
9
+ callpoint: Optional[Path]
10
+ module: Optional[ModuleType]
11
+ cleanup: Callable[[], None]
12
+
13
+
14
+ _on_kernel_start_callbacks: List[_on_kernel_callback_entry] = []
15
+
16
+
17
+ def _find_root_module_frame() -> Optional[FrameType]:
18
+ # basically the module where the call stack origined from
19
+ current_frame = inspect.currentframe()
20
+ root_module_frame = None
21
+
22
+ while current_frame is not None:
23
+ if current_frame.f_code.co_name == "<module>":
24
+ root_module_frame = current_frame
25
+ break
26
+ current_frame = current_frame.f_back
27
+
28
+ return root_module_frame
29
+
30
+
31
+ def on_kernel_start(f: Callable[[], Optional[Callable[[], None]]]) -> Callable[[], None]:
32
+ root = _find_root_module_frame()
33
+ path: Optional[Path] = None
34
+ module: Optional[ModuleType] = None
35
+ if root is not None:
36
+ path_str = inspect.getsourcefile(root)
37
+ module = inspect.getmodule(root)
38
+ if path_str is not None:
39
+ path = Path(path_str)
40
+
41
+ def cleanup():
42
+ return _on_kernel_start_callbacks.remove(kce)
43
+
44
+ kce = _on_kernel_callback_entry(f, path, module, cleanup)
45
+ _on_kernel_start_callbacks.append(kce)
46
+ return cleanup
solara/minisettings.py ADDED
@@ -0,0 +1,141 @@
1
+ import inspect
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Any, Optional, List, Type
5
+
6
+ # similar API to pydantic/pydantic-settings but we prefer not to have a dependency on pydantic
7
+ # since we cannot be compatible with pydantic1 and 2
8
+ # NOTE: not a public api
9
+
10
+
11
+ def _get_type(annotation):
12
+ check_optional_types = [str, int, float, bool, dict, list]
13
+ for check_type in check_optional_types:
14
+ if annotation == Optional[check_type]:
15
+ return check_type
16
+ if hasattr(annotation, "__origin__"):
17
+ if annotation.__origin__ is dict:
18
+ return dict
19
+ return annotation
20
+
21
+
22
+ class _Field:
23
+ def __init__(self, default=None, env=None, title=None, default_factory=None, gt=None, alias=None) -> None:
24
+ self.default = default
25
+ self.env = env
26
+ self.fullenv = None
27
+ self.title = title
28
+ self.annotation = None
29
+ self.default_factory = default_factory
30
+ self.gt = gt
31
+ self.alias = alias
32
+ self.field_info = self
33
+ self.extra = {"env_names": [env] if env else []}
34
+
35
+ def __set_name__(self, owner, name):
36
+ prefix = "SOLARA_"
37
+ config = getattr(owner, "Config")
38
+ if config:
39
+ prefix = getattr(config, "env_prefix", prefix).upper()
40
+ if hasattr(config, "fields"):
41
+ fields = config.fields
42
+ if name in fields:
43
+ self.alias = fields[name]
44
+ self.name = name
45
+ self.alias = self.alias or self.name
46
+ self.title = self.title or self.name
47
+ if self.env is None:
48
+ self.env = f"{prefix}{self.name.upper()}"
49
+ else:
50
+ self.env = self.env
51
+ self.annotation = owner.__annotations__.get(self.name)
52
+ assert self.annotation is not None, f"Field {self.name} must have a type annotation"
53
+ self.type_ = _get_type(self.annotation)
54
+
55
+ def __get__(self, instance, owner):
56
+ if instance is None:
57
+ return self
58
+ return instance._values[self.name]
59
+
60
+ def __set__(self, instance, value):
61
+ instance._values[self.name] = value
62
+
63
+
64
+ def convert(annotation, value: str) -> Any:
65
+ check_sub_types: List[Type] = [str, int, float, bool, Path]
66
+ for sub_type in check_sub_types:
67
+ if annotation == Optional[sub_type]:
68
+ annotation = sub_type
69
+ return convert(annotation, value)
70
+ for sub_type in check_sub_types:
71
+ if annotation == List[sub_type]: # type: ignore
72
+ annotation = sub_type
73
+ values = value.split(",")
74
+ return [convert(sub_type, k) for k in values]
75
+ if annotation is str:
76
+ return value
77
+ elif annotation is int:
78
+ return int(value)
79
+ elif annotation is float:
80
+ return float(value)
81
+ elif annotation is bool:
82
+ if value in ("True", "true", "1"):
83
+ return True
84
+ elif value in ("False", "false", "0"):
85
+ return False
86
+ else:
87
+ raise ValueError(f"Invalid boolean value {value}")
88
+ else:
89
+ # raise TypeError(f"Unsupported type {annotation}")
90
+ return annotation(value)
91
+
92
+
93
+ def Field(*args, **kwargs) -> Any:
94
+ return _Field(*args, **kwargs)
95
+
96
+
97
+ class BaseSettings:
98
+ __fields__: dict
99
+
100
+ def __init__(self, **kwargs) -> None:
101
+ cls = type(self)
102
+ self._values = {**kwargs}
103
+ keys = {k.upper() for k in os.environ.keys()}
104
+ for key, field in cls.__dict__.items():
105
+ if key in kwargs:
106
+ continue
107
+ if isinstance(field, _Field):
108
+ value = field.default
109
+ if field.default_factory:
110
+ value = field.default_factory()
111
+
112
+ if field.env:
113
+ env_key = field.env.upper()
114
+ if env_key in keys:
115
+ # do a case-insensitive lookup
116
+ for env_var_cased in os.environ.keys():
117
+ if env_key.upper() == env_var_cased.upper():
118
+ value = convert(field.annotation, os.environ[env_var_cased])
119
+ self._values[key] = value
120
+
121
+ def __init_subclass__(cls) -> None:
122
+ cls.__fields__ = {}
123
+ for key, field in cls.__dict__.items():
124
+ if key.startswith("_"):
125
+ continue
126
+ if key == "Config":
127
+ continue
128
+ if not isinstance(field, _Field):
129
+ if inspect.isfunction(field):
130
+ continue
131
+ field = Field(field)
132
+ setattr(cls, key, field)
133
+ field.__set_name__(cls, key)
134
+ cls.__fields__[key] = field
135
+
136
+ def dict(self, by_alias=True):
137
+ values = self._values.copy()
138
+ for key, value in values.items():
139
+ if isinstance(value, BaseSettings):
140
+ values[key] = value.dict(by_alias=by_alias)
141
+ return values
solara/py.typed ADDED
File without changes
solara/reactive.py ADDED
@@ -0,0 +1,99 @@
1
+ from typing import Any, Callable, TypeVar
2
+
3
+ from solara.toestand import Reactive
4
+ import solara.util
5
+
6
+ __all__ = ["reactive", "Reactive"]
7
+
8
+ T = TypeVar("T")
9
+
10
+
11
+ def reactive(value: T, equals: Callable[[Any, Any], bool] = solara.util.equals_extra) -> Reactive[T]:
12
+ """Creates a new Reactive object with the given initial value.
13
+
14
+ Reactive objects are mostly used to manage global or application-wide state in
15
+ Solara web applications. They provide an easy-to-use mechanism for keeping
16
+ track of the changing state of data and for propagating those changes to
17
+ the appropriate UI components. For managing local or component-specific
18
+ state, consider using the [`solara.use_state()`](/documentation/api/hooks/use_state) function.
19
+
20
+
21
+ Reactive variables can be accessed using the `.value` attribute. To modify
22
+ the value, you can either set the `.value` property directly or use the
23
+ `.set()` method. While both approaches are equivalent, the `.set()` method
24
+ is particularly useful when you need to pass it as a callback function to
25
+ other components, such as a slider's `on_value` callback.
26
+
27
+ When a component uses a reactive variable, it
28
+ automatically listens for changes to the variable's value. If the value
29
+ changes, the component will automatically re-render to reflect the updated
30
+ state, without the need to explicitly subscribe to the variable.
31
+
32
+ Reactive objects in Solara are also context-aware, meaning that they can
33
+ maintain separate values for each browser tab or user session. This enables
34
+ each user to have their own independent state, allowing them to interact
35
+ with the web application without affecting the state of other users.
36
+
37
+ Args:
38
+ value (T): The initial value of the reactive variable.
39
+ equals: A function that returns True if two values are considered equal, and False otherwise.
40
+ The default function is `solara.util.equals`, which performs a deep comparison of the two values
41
+ and is more forgiving than the default `==` operator.
42
+ You can provide a custom function if you need to define a different notion of equality.
43
+
44
+
45
+ Returns:
46
+ Reactive[T]: A new Reactive object with the specified initial value.
47
+
48
+ Example:
49
+
50
+ ```python
51
+ >>> counter = solara.reactive(0)
52
+ >>> counter.value
53
+ 0
54
+ >>> counter.set(1)
55
+ >>> counter.value
56
+ 1
57
+ >>> counter.value += 1
58
+ >>> counter.value
59
+ 2
60
+ ```
61
+
62
+
63
+ ## Solara example
64
+
65
+ Here's an example that demonstrates the use of reactive variables in Solara components:
66
+
67
+ ```solara
68
+ import solara
69
+
70
+ counter = solara.reactive(0)
71
+
72
+ def increment():
73
+ counter.value += 1
74
+
75
+
76
+ @solara.component
77
+ def CounterDisplay():
78
+ solara.Info(f"Counter: {counter.value}")
79
+
80
+
81
+ @solara.component
82
+ def IncrementButton():
83
+
84
+ solara.Button("Increment", on_click=increment)
85
+
86
+
87
+ @solara.component
88
+ def Page():
89
+ IncrementButton()
90
+ CounterDisplay()
91
+ ```
92
+
93
+ In this example, we create a reactive variable counter with an initial value of 0.
94
+ We define two components: `CounterDisplay` and `IncrementButton`. `CounterDisplay` renders the current value of counter,
95
+ while `IncrementButton` increments the value of counter when clicked.
96
+ Whenever the counter value changes, `CounterDisplay` automatically updates to display the new value.
97
+
98
+ """
99
+ return Reactive(value, equals=equals)
solara/routing.py ADDED
@@ -0,0 +1,268 @@
1
+ import abc
2
+ import logging
3
+ from typing import Callable, List, Optional, Tuple, Union, cast
4
+
5
+ import solara
6
+ from solara import _using_solara_server
7
+
8
+ logger = logging.getLogger("solara.router")
9
+
10
+
11
+ class _LocationBase(abc.ABC):
12
+ @property
13
+ def pathname(self):
14
+ pass
15
+
16
+ @pathname.setter
17
+ # mypy does not accept this
18
+ # @abc.abstractmethod
19
+ def pathname(self):
20
+ pass
21
+
22
+
23
+ class _Location(_LocationBase):
24
+ def __init__(self, pathname, setter: Callable[[str], None]) -> None:
25
+ self._pathname = pathname
26
+ self.setter = setter
27
+
28
+ @property
29
+ def pathname(self):
30
+ return self._pathname
31
+
32
+ @pathname.setter
33
+ def pathname(self, value):
34
+ # import pdb
35
+
36
+ # pdb.set_trace()
37
+ self._pathname = value
38
+ self.setter(self._pathname)
39
+
40
+
41
+ class Router:
42
+ search: Optional[str]
43
+
44
+ def __init__(self, path: str, routes: List[solara.Route], set_path: Callable[[str], None] = None):
45
+ # see https://developer.mozilla.org/en-US/docs/Web/API/Location for anatomy/nomenclature
46
+ if "?" in path:
47
+ self.path, self.search = path.split("?", 1)
48
+ else:
49
+ self.path = path
50
+ self.search = None
51
+ del path
52
+ self.set_path = set_path
53
+ self.parts = (self.path or "").strip("/").split("/")
54
+ self.routes = routes
55
+ self.root_path = ""
56
+ if _using_solara_server():
57
+ import solara.server.settings
58
+
59
+ self.root_path = solara.server.settings.main.root_path or ""
60
+ # each route in this list corresponds to a part in self.parts
61
+ self.path_routes: List["solara.Route"] = []
62
+ self.path_routes_siblings: List[List["solara.Route"]] = [] # siblings including itself
63
+ # routes = routes.copy()
64
+ route = None
65
+ for part in self.parts:
66
+ for route in routes:
67
+ if (route.path == part) or (route.path == "/" and not part):
68
+ self.path_routes.append(route)
69
+ self.path_routes_siblings.append(routes)
70
+ routes = route.children
71
+ break
72
+ if len(self.parts) == len(self.path_routes):
73
+ # e.g. '/foo/bar' -> ['foo', 'bar'] and bar has a default route
74
+ # but if '' -> [''] we should not
75
+ route = self.path_routes[-1]
76
+ if route:
77
+ default_routes = [k for k in route.children if k.path == "/"]
78
+ if self.parts and self.parts[0] and default_routes:
79
+ self.path_routes.append(default_routes[0])
80
+ self.path_routes_siblings.append(route.children)
81
+
82
+ assert len(self.path_routes) == len(self.path_routes_siblings)
83
+ self.possible_match = (len(self.path_routes[-1].children) == 0) if self.path_routes else False
84
+
85
+ def push(self, path: str):
86
+ assert self.set_path is not None
87
+ self.set_path(path)
88
+
89
+
90
+ router_context = solara.create_context(Router("", []))
91
+ _location_context = solara.create_context(cast(_LocationBase, _Location("", lambda x: None)))
92
+
93
+ route_level_context = solara.create_context(0)
94
+
95
+
96
+ def use_route_level():
97
+ route_level = solara.use_context(route_level_context)
98
+ return route_level
99
+
100
+
101
+ def use_router() -> Router:
102
+ """Returns the current router object.
103
+
104
+ See also [Understanding Routing](/documentation/advanced/understanding/routing).
105
+
106
+ `use_router` returns the current router object. This is useful to build custom routing.
107
+
108
+ the router object contains the following properties/methods:
109
+
110
+ * `path` - the current pathname (e.g. `/fruit/banana`)
111
+ * `parts` - the current pathname split into parts (e.g. `['fruit', 'banana']`)
112
+ * `search` - the query parameters string (e.g. `color=yellow`).
113
+ * `push(path: str)` - navigate to path (e.g. `router.push('/fruit/banana')`)
114
+
115
+ ## Typical usage:
116
+
117
+ ```python
118
+ import solara
119
+
120
+
121
+ @solara.component
122
+ def Page():
123
+ router = solara.use_router()
124
+
125
+ def redirect():
126
+ router.push(f"/documentation/api/routing/use_route")
127
+
128
+ solara.Button("Navigate using an event", on_click=redirect)
129
+ ```
130
+
131
+ """
132
+ return solara.use_context(router_context)
133
+
134
+
135
+ def use_route(
136
+ level=0,
137
+ peek=False,
138
+ ) -> Tuple[Optional[solara.Route], List[solara.Route]]:
139
+ """Returns (if found) the current route that matches the pathname, or None
140
+
141
+ See also [Understanding Routing](/documentation/advanced/understanding/routing).
142
+
143
+ `use_route` returns (if found) the current route that matches the pathname, or None. It also returns all resolved routes of that level
144
+ (i.e. all siblings and itself). This return tuple is useful to build custom navigation (e.g. using tabs or buttons).
145
+
146
+
147
+ Routing starts with declaring a set of `routes` in your app (solara picks up the `routes` variable if it exists,
148
+ and it should be in the same namespace as `Page`).
149
+ In the demo below, we declared the following routes.
150
+
151
+ ```python
152
+ routes = [
153
+ solara.Route(path="/"),
154
+ solara.Route(
155
+ path="fruit",
156
+ component=Fruit,
157
+ children=[
158
+ solara.Route(path="/"),
159
+ solara.Route(path="kiwi"),
160
+ solara.Route(path="banana"),
161
+ solara.Route(path="apple"),
162
+ ],
163
+ ),
164
+ ]
165
+ ```
166
+
167
+ Note that all routes are relative, since a component does not know if it is embedded into a larger application, which may also do routing.
168
+ Therefore you should never use the `route.path` for navigation since the route object has no knowledge of the full url
169
+ (e.g. `/documentation/api/routing/use_route/fruit/banana`) but only knows its small piece of the pathname (e.g. `banana`)
170
+
171
+ Use [`resolve_path`](/documentation/api/routing/resolve_path) to request the full url for navigation,
172
+ or simply use the `Link` component that can do this for us.
173
+
174
+ If the current route has children, any child component that calls `use_route` will return the matched route and its siblings of our children.
175
+
176
+
177
+ ## Arguments
178
+
179
+ * `level`: the level of the route to return. 0 is the current route, -1 is the parent route, 1 the child route, etc.
180
+ * `peek`: if True, the route level is not incremented. This is useful to peek at the next route level without changing the current route level.
181
+ """
182
+
183
+ router = solara.use_context(router_context)
184
+ route_level = solara.use_context(route_level_context)
185
+ if not peek:
186
+ route_level_context.provide(route_level + 1)
187
+ route_level += level
188
+ if route_level < len(router.path_routes):
189
+ return router.path_routes[route_level], router.path_routes_siblings[route_level]
190
+ else:
191
+ return None, []
192
+
193
+
194
+ def find_route(path: str) -> Optional[solara.Route]:
195
+ router = solara.get_context(router_context)
196
+ route_level = min(solara.get_context(route_level_context), len(router.path_routes_siblings) - 1)
197
+ for route in router.path_routes_siblings[route_level]:
198
+ if path.startswith(route.path) or (not path and route.path == "/"):
199
+ return route
200
+ return None
201
+
202
+
203
+ def use_pathname():
204
+ location_proxy = solara.use_context(_location_context)
205
+
206
+ def setter(value):
207
+ location_proxy.pathname = value
208
+
209
+ return location_proxy.pathname, setter
210
+
211
+
212
+ def resolve_path(path_or_route: Union[str, solara.Route], level=0) -> str:
213
+ """Resolve a relative path or a route to an absolute path.
214
+
215
+ If the path is a string and starts with a `/'`, it is returned as is.
216
+
217
+
218
+ ## Typical usage:
219
+
220
+ ```python
221
+ ...
222
+ route_current, routes_current_level = solara.routes()
223
+ # route_current.path == "banana"
224
+ path = solara.resolve_path(route_current)
225
+ # path == "/fruit/banana"
226
+ path_same = solara.resolve_path("banana")
227
+ # path_same == path == "/fruit/banana"
228
+ ...
229
+ ```
230
+
231
+ ## Arguments
232
+
233
+ * path_or_route: a path string or a [`solara.Route`](/documentation/api/routing/route) object to resolve.
234
+
235
+ ## See also
236
+
237
+ * [Multipage](/documentation/advanced/howto/multipage).
238
+ * [Understanding Routing](/documentation/advanced/understanding/routing).
239
+
240
+
241
+ """
242
+ router = solara.get_context(router_context)
243
+ if isinstance(path_or_route, str):
244
+ path = path_or_route
245
+ if path.startswith("/"):
246
+ return path
247
+ route_level = solara.get_context(route_level_context) + level
248
+ parts = [*router.parts[:route_level], path]
249
+ path = "/" + "/".join(parts)
250
+ if path.startswith("//"):
251
+ path = path[1:]
252
+ return path
253
+ elif isinstance(path_or_route, solara.Route):
254
+ route: solara.Route = path_or_route
255
+ path = _resolve_path("/", route, router.routes)
256
+ if path.startswith("//"):
257
+ path = path[1:]
258
+ return path
259
+
260
+
261
+ def _resolve_path(prefix: str, findroute: solara.Route, routes: List[solara.Route]):
262
+ for route in routes:
263
+ path = (prefix + "/" + route.path) if route.path != "/" else prefix
264
+ if findroute is route:
265
+ return path
266
+ possible_path = _resolve_path(path, findroute=findroute, routes=route.children)
267
+ if possible_path is not None:
268
+ return possible_path
@@ -0,0 +1,88 @@
1
+ import re
2
+ import sys
3
+ import threading
4
+ from typing import MutableMapping
5
+
6
+ from .types import ObservableMutableMapping
7
+
8
+
9
+ def _in_solara_server():
10
+ return "solara.server" in sys.modules
11
+
12
+
13
+ class ConnectionScope(ObservableMutableMapping):
14
+ def __init__(self, name="connection"):
15
+ super().__init__()
16
+ self._global_dict = {}
17
+ self.name = name
18
+ self.lock = threading.Lock()
19
+
20
+ def _get_dict(self) -> MutableMapping:
21
+ if _in_solara_server():
22
+ import solara.server.kernel_context
23
+
24
+ context = solara.server.kernel_context.get_current_context()
25
+ if self.name not in context.user_dicts:
26
+ with self.lock:
27
+ if self.name not in context.user_dicts:
28
+ context.user_dicts[self.name] = {}
29
+ return context.user_dicts[self.name]
30
+ else:
31
+ return self._global_dict
32
+
33
+
34
+ class ObservableDict(ObservableMutableMapping):
35
+ def __init__(self):
36
+ super().__init__()
37
+ self._global_dict = {}
38
+
39
+ def _get_dict(self) -> MutableMapping:
40
+ return self._global_dict
41
+
42
+
43
+ worker = ObservableDict()
44
+ connection = ConnectionScope()
45
+
46
+
47
+ def get_kernel_id(ipython_fallback=True) -> str:
48
+ """Returns the kernel id, a unique string for each virtual kernel.
49
+
50
+ See [Understanding solara server](/docs/understanding/solara-server) for understanding the concept of virtual kernels
51
+ and their lifetime.
52
+
53
+ This unique ID can be useful to to implement storing state, scoped to a kernel. See [the scope example](/examples/general/custom_storage) for an example.
54
+
55
+ If `ipython_fallback` is `True` (default), this function will also work in IPython notebooks, where it will return the IPython kernel id.
56
+
57
+ """
58
+ import solara.server.kernel_context
59
+
60
+ try:
61
+ context = solara.server.kernel_context.get_current_context()
62
+ except RuntimeError as e:
63
+ if not ipython_fallback:
64
+ raise
65
+ import IPython
66
+
67
+ ipython = IPython.get_ipython()
68
+ if not ipython or not hasattr(ipython, "kernel"):
69
+ raise RuntimeError("Not in a kernel") from e
70
+ kernel = ipython.kernel
71
+ regex = r"[\\/]kernel-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.json$"
72
+ connection_file = kernel.config["IPKernelApp"]["connection_file"]
73
+ return re.compile(regex).search(connection_file).group(1) # type: ignore
74
+ return context.id
75
+
76
+
77
+ def get_session_id() -> str:
78
+ """Returns the session id, which is stored using a browser cookie.
79
+
80
+ See [Understanding solara server](/docs/understanding/solara-server#session) for more information about the Solara sessions.
81
+
82
+ This unique ID can be useful to to implement storing state, scoped to a browser session. See [the scope example](/examples/general/custom_storage)
83
+ for an example.
84
+ """
85
+ import solara.server.kernel_context
86
+
87
+ context = solara.server.kernel_context.get_current_context()
88
+ return context.session_id
solara/scope/types.py ADDED
@@ -0,0 +1,55 @@
1
+ import threading
2
+ from abc import abstractmethod
3
+ from typing import Any, Callable, Dict, List, MutableMapping
4
+ from warnings import warn
5
+
6
+ ObserverCallback = Callable[[Any, Any], None]
7
+
8
+
9
+ class MutableMappingBase(MutableMapping):
10
+ @abstractmethod
11
+ def _get_dict(self) -> MutableMapping:
12
+ pass
13
+
14
+ def __delitem__(self, key) -> None:
15
+ self._get_dict().__delitem__(key)
16
+
17
+ def __getitem__(self, key):
18
+ return self._get_dict().__getitem__(key)
19
+
20
+ def __iter__(self):
21
+ return self._get_dict().__iter__()
22
+
23
+ def __len__(self):
24
+ return self._get_dict().__len__()
25
+
26
+ def __setitem__(self, key, value):
27
+ self._get_dict().__setitem__(key, value)
28
+
29
+
30
+ class ObservableMutableMapping(MutableMappingBase):
31
+ # list of observers for each key
32
+ observers: Dict[Any, List[ObserverCallback]]
33
+
34
+ def __init__(self) -> None:
35
+ super().__init__()
36
+ self.observers = {}
37
+ self.lock = threading.Lock()
38
+
39
+ def subscribe_key(self, key, callback: ObserverCallback):
40
+ if key not in self.observers:
41
+ with self.lock:
42
+ if key not in self.observers:
43
+ self.observers[key] = []
44
+ self.observers[key].append(callback)
45
+ if self.observers[key].count(callback) > 1:
46
+ warn("Callback already subscribed")
47
+
48
+ def unsubscribe():
49
+ self.observers[key].remove(callback)
50
+
51
+ return unsubscribe
52
+
53
+ def trigger_key(self, key):
54
+ for callback in self.observers[key]:
55
+ callback(key, self[key])
File without changes