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/__main__.py ADDED
@@ -0,0 +1,765 @@
1
+ import os
2
+ import shutil
3
+ import site
4
+ import sys
5
+ import threading
6
+ import time
7
+ import typing
8
+ import webbrowser
9
+ from enum import Enum
10
+ from pathlib import Path
11
+
12
+ import rich
13
+ import rich_click as click
14
+ import uvicorn
15
+ from rich import print as rprint
16
+ from uvicorn.main import LEVEL_CHOICES, LOOP_CHOICES
17
+
18
+ import solara
19
+ from solara.server import settings
20
+ import solara.server.threaded
21
+
22
+ from .server import telemetry
23
+
24
+ print_mutex = threading.Lock()
25
+
26
+ try:
27
+ from solara_enterprise.ssg import ssg_crawl
28
+ except ImportError:
29
+
30
+ def ssg_crawl(*args, **kwargs): # type: ignore
31
+ raise RuntimeError('SSG not available, please install solara-enterprise (pip install "solara-enterprise[ssg]"')
32
+
33
+
34
+ HERE = Path(__file__).parent
35
+
36
+ LOGGING_CONFIG: dict = {
37
+ "version": 1,
38
+ "disable_existing_loggers": False,
39
+ "formatters": {
40
+ "default": {
41
+ "()": "uvicorn.logging.DefaultFormatter",
42
+ "fmt": "%(levelprefix)s %(message)s",
43
+ "use_colors": None,
44
+ },
45
+ "access": {
46
+ "()": "uvicorn.logging.AccessFormatter",
47
+ "fmt": '%(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s', # noqa: E501
48
+ },
49
+ },
50
+ "handlers": {
51
+ "default": {
52
+ "formatter": "default",
53
+ "class": "logging.StreamHandler",
54
+ "stream": "ext://sys.stderr",
55
+ },
56
+ "rich": {
57
+ "class": "rich.logging.RichHandler",
58
+ },
59
+ "access": {
60
+ "formatter": "access",
61
+ "class": "logging.StreamHandler",
62
+ "stream": "ext://sys.stdout",
63
+ },
64
+ },
65
+ "loggers": {
66
+ "solara": {"handlers": ["default"], "level": "ERROR"},
67
+ "reacton": {"handlers": ["default"], "level": "ERROR"},
68
+ # "react": {"handlers": ["rich"], "level": "DEBUG"},
69
+ "uvicorn": {"handlers": ["default"], "level": "ERROR"},
70
+ "uvicorn.error": {"level": "ERROR"},
71
+ "uvicorn.access": {"handlers": ["access"], "level": "ERROR", "propagate": False},
72
+ },
73
+ }
74
+
75
+
76
+ def _check_version():
77
+ import requests
78
+
79
+ try:
80
+ response = requests.get("https://pypi.org/pypi/solara/json", timeout=0.5)
81
+ latest_version = response.json()["info"]["version"]
82
+ except: # noqa: E722
83
+ # in case of a firewall, or timeout, we just abort
84
+ return
85
+ if latest_version != solara.__version__:
86
+ with print_mutex:
87
+ print(f"New version of Solara available: {latest_version}. You have {solara.__version__}. Please upgrade using:") # noqa: T201
88
+ print(f'\t$ pip install "solara=={latest_version}"') # noqa: T201
89
+
90
+
91
+ def find_all_packages_paths():
92
+ paths = []
93
+ # sitepackages = set([os.path.dirname(k) for k in site.getsitepackages()])
94
+ sitepackages = {k for k in site.getsitepackages()}
95
+ paths.extend(list(sitepackages))
96
+ for name, module in sys.modules.items():
97
+ if hasattr(module, "__path__"):
98
+ try:
99
+ path = module.__path__[0]
100
+ except: # noqa: E722
101
+ pass # happens for namespace packages it seems
102
+ # print(f"Error for {name}")
103
+ # if path:
104
+ # skip = False
105
+ # for sitepackage in sitepackages:
106
+ # if path.startswith(sitepackage):
107
+ # skip = True
108
+ # if not skip:
109
+ # print(name, path, skip)
110
+ paths.append(str(path))
111
+ # print("PATHS", paths)
112
+ return paths
113
+
114
+
115
+ class EnumType(click.Choice):
116
+ def __init__(self, enum: typing.Type[Enum], case_sensitive=False):
117
+ self._enum = enum
118
+ super().__init__(choices=[item.name for item in enum], case_sensitive=case_sensitive)
119
+
120
+
121
+ @click.group()
122
+ def cli():
123
+ pass
124
+
125
+
126
+ production_default = False
127
+ if "SOLARA_MODE" in os.environ:
128
+ # settings.main.mode by default is set to production,
129
+ # which is a good default for when you embed in a flask
130
+ # app for instance, but not for the CLI, which app developers
131
+ # usually run.
132
+ production_default = settings.main.mode == "production"
133
+ # Note that in the CLI we do set this value to "development"
134
+ # or "production" based on the --production flag
135
+
136
+
137
+ @cli.command()
138
+ @click.option(
139
+ "--port",
140
+ default=int(os.environ.get("PORT", 8765)),
141
+ help="Port to run the server on, 0 for a random free port, default to $PORT or 8765.",
142
+ )
143
+ @click.option(
144
+ "--host",
145
+ default=settings.main.host,
146
+ help="Host to listen on. Defaults to the $HOST environment or $SOLARA_HOST when available or localhost when not given.",
147
+ )
148
+ @click.option("--dev/--no-dev", default=None, help="Deprecated: use --auto-restart/-a", hidden=True)
149
+ @click.option("--production", is_flag=True, default=production_default, help="Run in production mode: https://solara.dev/docs/understanding/solara-server")
150
+ @click.option("--reload", is_flag=True, default=None, help="Deprecated: use --auto-restart/-a", hidden=True)
151
+ @click.option("-a", "--auto-restart", is_flag=True, default=False, help="Enable auto-restarting of server when the solara server code changes.")
152
+ @click.option("--tracer/--no-tracer", default=False)
153
+ @click.option("--timing/--no-timing", default=False)
154
+ @click.option("--open/--no-open", default=True)
155
+ @click.option(
156
+ "--restart-dir",
157
+ "restart_dirs",
158
+ multiple=True,
159
+ help="Set restart directories explicitly, instead of using the current working directory.",
160
+ type=click.Path(exists=True),
161
+ )
162
+ @click.option(
163
+ "--restart-exclude",
164
+ "restart_excludes",
165
+ multiple=True,
166
+ help="Set glob patterns to exclude while watching for files. Includes "
167
+ "'.*, .py[cod], .sw.*, ~*' by default; these defaults can be overridden "
168
+ "with `--restart-include`. This option has no effect unless watchgod is "
169
+ "installed.",
170
+ )
171
+ @click.option(
172
+ "--workers",
173
+ default=None,
174
+ type=int,
175
+ help="Number of worker processes. Defaults to the $WEB_CONCURRENCY environment variable if available, or 1. Not valid with --auto-restart/-a.",
176
+ )
177
+ @click.option(
178
+ "--env-file",
179
+ type=click.Path(exists=True),
180
+ default=None,
181
+ help="Environment configuration file.",
182
+ show_default=True,
183
+ )
184
+ @click.option(
185
+ "--log-config",
186
+ type=click.Path(exists=True),
187
+ default=None,
188
+ help="Logging configuration file. Supported formats: .ini, .json, .yaml.",
189
+ show_default=True,
190
+ )
191
+ @click.option(
192
+ "--log-level",
193
+ type=LEVEL_CHOICES,
194
+ default=None,
195
+ help="Log level. [default: info]",
196
+ show_default=True,
197
+ )
198
+ @click.option(
199
+ "--log-level-uvicorn",
200
+ type=LEVEL_CHOICES,
201
+ default="error",
202
+ help="Log level. [default: error]",
203
+ show_default=True,
204
+ )
205
+ @click.option(
206
+ "--access-log/--no-access-log",
207
+ is_flag=True,
208
+ default=True,
209
+ help="Enable/Disable access log.",
210
+ )
211
+ @click.option(
212
+ "--root-path",
213
+ type=str,
214
+ default="",
215
+ help="Set the ASGI 'root_path' for applications submounted below a given URL path.",
216
+ )
217
+ @click.option(
218
+ "--theme-loader",
219
+ type=str,
220
+ default=settings.theme.loader,
221
+ help=f"Loader to use when the app is not yet shown to the user. [default: {settings.theme.loader!r}]",
222
+ )
223
+ @click.option(
224
+ "--theme-variant",
225
+ type=settings.ThemeVariant,
226
+ default=settings.theme.variant.name,
227
+ help=f"Use light or dark variant, or auto detect (auto). [default: {settings.theme.variant.name}",
228
+ )
229
+ @click.option(
230
+ "--dark",
231
+ type=bool,
232
+ default=settings.theme.variant == settings.ThemeVariant.dark,
233
+ help="Use dark theme. Shorthand for --theme-variant=dark",
234
+ )
235
+ @click.option(
236
+ "--theme-variant-user-selectable/--no-theme-variant-user-selectable",
237
+ type=bool,
238
+ hidden=True,
239
+ help="Deprecated.",
240
+ )
241
+ @click.option("--pdb/--no-pdb", "use_pdb", default=False, help="Enter debugger on error")
242
+ @click.argument("app")
243
+ @click.option(
244
+ "--loop",
245
+ type=LOOP_CHOICES,
246
+ default="auto",
247
+ help="Event loop implementation.",
248
+ show_default=True,
249
+ )
250
+ @click.option(
251
+ "--ssg/--no-ssg",
252
+ is_flag=True,
253
+ default=settings.ssg.enabled,
254
+ help="(pre) Render static pages.",
255
+ )
256
+ @click.option(
257
+ "--search/--no-search",
258
+ is_flag=True,
259
+ default=settings.search.enabled,
260
+ help="Enable search (requires ssg generated pages).",
261
+ )
262
+ @click.option(
263
+ "--check-version/--no-check-version",
264
+ is_flag=True,
265
+ default=True,
266
+ help="Check installed version again pypi version.",
267
+ )
268
+ @click.option(
269
+ "--qt",
270
+ is_flag=True,
271
+ default=False,
272
+ help="Instead of opening a browser, open a Qt window. Will also stop the server when the window is closed. (experimental)",
273
+ )
274
+ def run(
275
+ app,
276
+ host,
277
+ port,
278
+ open,
279
+ auto_restart: bool,
280
+ reload: bool,
281
+ restart_dirs: typing.Optional[typing.List[str]],
282
+ restart_excludes: typing.List[str],
283
+ dev: bool,
284
+ production: bool,
285
+ tracer: bool,
286
+ timing: bool,
287
+ loop: str,
288
+ workers: int,
289
+ env_file: str,
290
+ root_path: str,
291
+ log_config: str,
292
+ log_level: str,
293
+ log_level_uvicorn: str,
294
+ access_log: bool,
295
+ use_pdb: bool,
296
+ theme_loader: str,
297
+ theme_variant: settings.ThemeVariant,
298
+ dark: bool,
299
+ theme_variant_user_selectable: bool,
300
+ ssg: bool,
301
+ search: bool,
302
+ check_version: bool = True,
303
+ qt=False,
304
+ ):
305
+ """Run a Solara app."""
306
+ if dev is not None:
307
+ print("solara: --dev is deprecated, use --auto-restart/-a instead", file=sys.stderr) # noqa: T201
308
+ auto_restart = dev
309
+ if reload is not None:
310
+ print("solara: --reload is deprecated, use --auto-restart/-a instead", file=sys.stderr) # noqa: T201
311
+ auto_restart = reload
312
+ if check_version:
313
+ threading.Thread(target=_check_version, daemon=True).run()
314
+
315
+ # uvicorn calls it reload, we call it auto restart
316
+ reload = auto_restart
317
+ del auto_restart
318
+ settings.ssg.enabled = ssg
319
+ settings.search.enabled = search
320
+ reload_dirs = restart_dirs if restart_dirs else None
321
+ if port == 0:
322
+ port = solara.server.threaded.get_free_port()
323
+ del restart_dirs
324
+ url = f"http://{host}:{port}"
325
+
326
+ failed = False
327
+ if reload:
328
+ telemetry._auto_restart_enabled = True
329
+ solara_root = Path(solara.__file__).parent
330
+
331
+ reload_dirs = list(reload_dirs if reload_dirs else [])
332
+
333
+ # we restart the server when solara or react changes, in principle we should do
334
+ # that for all dependencies of the server, but these are changing most often
335
+ # during development
336
+ # We exclude the website, that will be handled by solara/server/reload.py
337
+ reload_dirs = [str(solara_root), str(Path(solara.__file__).parent)]
338
+ try:
339
+ import solara_enterprise
340
+
341
+ reload_dirs.append(str(Path(solara_enterprise.__file__).parent))
342
+ del solara_enterprise
343
+ except ImportError:
344
+ pass
345
+ reload_excludes = restart_excludes if restart_excludes else []
346
+ del restart_excludes
347
+ reload_excludes = [str(solara_root / "website"), str(solara_root / "template")]
348
+ app_path = Path(app)
349
+ if app_path.exists():
350
+ # if app is not a child of the current working directory
351
+ # uvicorn crashes
352
+ if not str(app_path.resolve()).startswith(str(Path.cwd().resolve())):
353
+ reload_excludes.append(str(app_path.resolve()))
354
+ del app_path
355
+ del solara_root
356
+ reload = True
357
+ # avoid sending many restarts
358
+ settings.telemetry.mixpanel_enable = False
359
+ else:
360
+ del restart_excludes
361
+
362
+ if production:
363
+ settings.main.mode = "production"
364
+ else:
365
+ settings.main.mode = "development"
366
+ server = None
367
+
368
+ # TODO: we might want to support this, but it needs to be started from the main thread
369
+ # and then uvicorn needs to be started from a thread
370
+ # def open_webview():
371
+ # import webview
372
+
373
+ # while not failed and (server is None or not server.started):
374
+ # time.sleep(0.1)
375
+ # if not failed:
376
+ # window = webview.create_window("Hello world", url, resizable=True)
377
+ # window.on_top = True
378
+ # # window.show()
379
+ # webview.start(debug=True)
380
+
381
+ def open_browser():
382
+ while not failed and (server is None or not server.started):
383
+ time.sleep(0.1)
384
+ if not failed:
385
+ if qt:
386
+ from .server.qt import run_qt
387
+
388
+ run_qt(url)
389
+ else:
390
+ webbrowser.open(url)
391
+
392
+ # with qt, we open the browser in the main thread (qt wants that)
393
+ # otherwise, we open the browser in a separate thread
394
+ if open and not qt:
395
+ threading.Thread(target=open_browser, daemon=True).start()
396
+
397
+ with print_mutex:
398
+ rich.print(f"Solara server is starting at {url}")
399
+
400
+ if log_level is not None:
401
+ LOGGING_CONFIG["loggers"]["solara"]["level"] = log_level.upper()
402
+ # LOGGING_CONFIG["loggers"]["reacton"]["level"] = log_level.upper()
403
+
404
+ log_level = log_level_uvicorn
405
+ del log_level_uvicorn
406
+
407
+ kwargs = locals().copy()
408
+ # cgi vars: https://datatracker.ietf.org/doc/html/rfc3875
409
+ os.environ["SOLARA_APP"] = app
410
+ os.environ["SERVER_NAME"] = host
411
+ os.environ["SERVER_PORT"] = str(port)
412
+
413
+ kwargs["app"] = "solara.server.starlette:app"
414
+ kwargs["log_config"] = LOGGING_CONFIG if log_config is None else log_config
415
+ kwargs["loop"] = loop
416
+ settings.main.use_pdb = use_pdb
417
+ settings.theme.loader = theme_loader
418
+ if dark:
419
+ theme_variant = settings.ThemeVariant.dark
420
+ settings.theme.variant = theme_variant
421
+ settings.main.tracer = tracer
422
+ settings.main.timing = timing
423
+ items = (
424
+ "theme_variant_user_selectable dark theme_variant theme_loader use_pdb server open_browser open url failed dev tracer"
425
+ " timing ssg search check_version production qt".split()
426
+ )
427
+ for item in items:
428
+ del kwargs[item]
429
+
430
+ def start_server():
431
+ nonlocal server
432
+ nonlocal failed
433
+ try:
434
+ # we manually create the server instead of calling uvicorn.run
435
+ # because we can then access the server variable and check if it is
436
+ # running.
437
+ config = uvicorn.Config(**kwargs)
438
+ server = uvicorn.Server(config=config)
439
+ if reload:
440
+ sock = config.bind_socket()
441
+ from uvicorn.supervisors import ChangeReload
442
+
443
+ ChangeReload(
444
+ config,
445
+ target=run_with_settings(
446
+ server, main=settings.main.dict(), theme=settings.theme.dict(), ssg=settings.ssg.dict(), search=settings.search.dict()
447
+ ),
448
+ sockets=[sock],
449
+ ).run()
450
+ else:
451
+ server.run()
452
+ except: # noqa
453
+ failed = True
454
+ raise
455
+
456
+ def ssg_run():
457
+ while not failed and (server is None or not server.started):
458
+ time.sleep(0.1)
459
+ if not failed:
460
+ assert server is not None
461
+ base_url = f"http://{server.config.host}:{server.config.port}"
462
+ rprint("Running Static Site Generator pre-render background task")
463
+ ssg_crawl(base_url)
464
+ if settings.search.enabled:
465
+ from solara_enterprise.search.index import build_index
466
+
467
+ build_index("")
468
+
469
+ # in dev mode we run the ssg in the child process (see run_with_settings)
470
+ if not dev and settings.ssg.enabled:
471
+ threading.Thread(target=ssg_run, daemon=True).start()
472
+
473
+ # if we don't have to wait for SSG, we can build the index right away
474
+ if not settings.ssg.enabled and settings.search.enabled:
475
+ from solara_enterprise.search.index import build_index
476
+
477
+ build_index("")
478
+
479
+ # TODO: if we want to use webview, it should be sth like this
480
+ if qt:
481
+ server_thread = threading.Thread(target=start_server, daemon=True)
482
+ server_thread.start()
483
+ open_browser()
484
+ else:
485
+ start_server()
486
+ # server_thread.join()
487
+
488
+
489
+ @cli.command()
490
+ @click.argument("app")
491
+ @click.option("--port", default=int(os.environ.get("PORT", 8765)))
492
+ @click.option("--host", default=settings.main.host)
493
+ @click.option(
494
+ "--headed/--no-headed",
495
+ is_flag=True,
496
+ default=settings.ssg.headed,
497
+ help="Show browser window if true.",
498
+ )
499
+ def ssg(app: str, port: int, host: str, headed: bool):
500
+ """Static site generation"""
501
+ settings.ssg.headed = headed
502
+ settings.ssg.enabled = True
503
+ settings.main.mode = "production" # always override this
504
+ os.environ["SOLARA_APP"] = app
505
+ from solara.server.starlette import ServerStarlette
506
+
507
+ server = ServerStarlette(port=port, host=host)
508
+ server.serve_threaded()
509
+ server.wait_until_serving()
510
+
511
+ base_url = f"http://{server.server.config.host}:{server.server.config.port}"
512
+
513
+ ssg_crawl(base_url)
514
+
515
+
516
+ @cli.command()
517
+ def deploy():
518
+ rprint("...")
519
+ import time
520
+
521
+ time.sleep(1)
522
+ rprint("Want your app to run instantly on awesomeapp-mystartup-gh.solara.run?")
523
+ rprint("\tCheck out https://solara.dev/documentation/getting_started/deploying/cloud-hosted")
524
+
525
+
526
+ @cli.command()
527
+ @click.argument("app")
528
+ def search(app: str):
529
+ """Build search index based on ssg output"""
530
+ os.environ["SOLARA_APP"] = app
531
+ from solara_enterprise.search.index import build_index
532
+
533
+ build_index("")
534
+
535
+
536
+ class run_with_settings:
537
+ """This cross a process boundary, and takes the serialized settings with it"""
538
+
539
+ def __init__(self, server: uvicorn.Server, main: typing.Dict, theme: typing.Dict, ssg: typing.Dict, search: typing.Dict):
540
+ self.server = server
541
+ self.main = main
542
+ self.theme = theme
543
+ self.ssg = ssg
544
+ self.search = search
545
+
546
+ def __call__(self, *args, **kwargs):
547
+ # this is now in the new process, where we need to re-apply the settings
548
+ failed = False
549
+
550
+ def ssg_run():
551
+ while not failed and (self.server is None or not self.server.started):
552
+ time.sleep(0.1)
553
+ if not failed:
554
+ assert self.server is not None
555
+ base_url = f"http://{self.server.config.host}:{self.server.config.port}"
556
+ rprint("Running Static Site Generator pre-render background task")
557
+ ssg_crawl(base_url)
558
+ if settings.search.enabled:
559
+ from solara_enterprise.search.index import build_index
560
+
561
+ build_index("")
562
+
563
+ settings.main = settings.MainSettings(**self.main)
564
+ settings.theme = settings.ThemeSettings(**self.theme)
565
+ settings.ssg = settings.SSG(**self.ssg)
566
+ settings.search = settings.Search(**self.search)
567
+ if settings.ssg.enabled:
568
+ threading.Thread(target=ssg_run, daemon=True).start()
569
+ try:
570
+ return self.server.run(*args, **kwargs)
571
+ except: # noqa
572
+ failed = True
573
+ raise
574
+
575
+
576
+ @cli.command()
577
+ @click.option("--port", default=int(os.environ.get("PORT", 8000)))
578
+ def staticserve(port):
579
+ """Experimental static serving"""
580
+ import http.server
581
+ import os
582
+ from functools import partial
583
+
584
+ print(f"http://localhost:{port}/") # noqa
585
+ wk_dir = os.getcwd() + "/staticbuild"
586
+ http.server.SimpleHTTPRequestHandler.extensions_map[".js"] = "application/javascript"
587
+ http.server.SimpleHTTPRequestHandler.extensions_map = {k: v + ";charset=UTF-8" for k, v in http.server.SimpleHTTPRequestHandler.extensions_map.items()}
588
+ http.server.test(HandlerClass=partial(http.server.SimpleHTTPRequestHandler, directory=wk_dir), port=port, bind="") # type: ignore
589
+
590
+
591
+ @cli.command()
592
+ def staticbuild():
593
+ """Experimental static build"""
594
+ # imports locals, otherwise .run() does not use the cli arguments
595
+ import solara.server
596
+ import solara.server.server
597
+
598
+ server_path = HERE / "server"
599
+ assets_path = server_path / "assets"
600
+ static_path = server_path / "static"
601
+
602
+ target_dir = Path("staticbuild")
603
+ target_dir.mkdir(exist_ok=True)
604
+
605
+ from .server import settings
606
+
607
+ copytree(settings.assets.proxy_cache_dir, target_dir / "_solara/cdn/")
608
+
609
+ static_dir_target = target_dir / "static"
610
+ static_dir_target.mkdir(exist_ok=True)
611
+
612
+ assets_dir_target = target_dir / "static" / "assets"
613
+ assets_dir_target.mkdir(exist_ok=True)
614
+ copytree(assets_path, assets_dir_target)
615
+
616
+ for path in (
617
+ list(static_path.glob("*.css"))
618
+ + list(static_path.glob("*.js"))
619
+ + list(static_path.glob("*.ps"))
620
+ + list(static_path.glob("*.py"))
621
+ + list(static_path.glob("*.png"))
622
+ + list(static_path.glob("*.svg"))
623
+ ):
624
+ shutil.copy(path, static_dir_target)
625
+
626
+ include_nbextensions = True
627
+ if include_nbextensions:
628
+ directories = solara.server.server.get_nbextensions_directories()
629
+ nbextensions, ignore = solara.server.server.get_nbextensions()
630
+ for name in nbextensions:
631
+ for directory in directories:
632
+ if (directory / (name + ".js")).exists():
633
+ src = (directory / (name + ".js")).parent
634
+ dst: Path = (static_dir_target / "nbextensions" / (name + ".js")).parent
635
+ dst.mkdir(parents=True, exist_ok=True)
636
+ # shutil.copytree(src, dst)
637
+ copytree(src, dst)
638
+
639
+ target_dir_wheels = target_dir / "wheels"
640
+ target_dir_wheels.mkdir(exist_ok=True)
641
+ version = solara.__version__
642
+ for path in [Path(f"dist/solara-{version}-py2.py3-none-any.whl")]:
643
+ shutil.copy(path, target_dir_wheels)
644
+
645
+ target_dir_static_dist = target_dir / "static/dist"
646
+ target_dir_static_dist.mkdir(exist_ok=True)
647
+ voila = server_path / "dist"
648
+ for path in list(voila.glob("*.js")) + list(voila.glob("*.woff")):
649
+ shutil.copy(path, target_dir_static_dist)
650
+
651
+ index_html = solara.server.server.read_root("", render_kwargs={"for_pyodide": True}, use_nbextensions=True)
652
+ assert index_html is not None
653
+ (target_dir / "index.html").write_text(index_html)
654
+
655
+
656
+ @click.group()
657
+ def create():
658
+ """Quickly create a solara script or project."""
659
+ pass
660
+
661
+
662
+ @create.command()
663
+ @click.argument(
664
+ "target",
665
+ type=click.Path(exists=False),
666
+ default="sol.py",
667
+ required=False,
668
+ )
669
+ def button(target: typing.Optional[Path]):
670
+ """Create a button with a click counter."""
671
+ write_script("button", target)
672
+
673
+
674
+ @create.command()
675
+ @click.argument(
676
+ "target",
677
+ type=click.Path(exists=False),
678
+ default="sol.py",
679
+ required=False,
680
+ )
681
+ def markdown(target: typing.Optional[Path] = None):
682
+ """Create a markdown editor."""
683
+ write_script("markdown", target)
684
+
685
+
686
+ def write_script(name: str, target: typing.Optional[Path]):
687
+ code = (HERE / "template" / f"{name}.py").read_text(encoding="utf-8")
688
+ if target is None:
689
+ target = Path("sol.py")
690
+ else:
691
+ target = Path(target)
692
+ target.parent.mkdir(exist_ok=True)
693
+ target.write_text(code)
694
+ rprint(f"Wrote: {target.resolve()}")
695
+ rprint(f"Run as:\n\t $ solara run {target.resolve()}")
696
+
697
+
698
+ # recursively copy a directory and allow for existing directories
699
+ def copytree(src: Path, dst: Path, copy_function=shutil.copy2, ignore: typing.Callable[[Path], bool] = lambda x: False, rename=lambda x: x):
700
+ print(src, " -> ", dst) # noqa
701
+ if not src.exists():
702
+ return
703
+ if not dst.exists():
704
+ dst.mkdir(parents=True, exist_ok=True)
705
+ for item in src.iterdir():
706
+ if ignore(item):
707
+ continue
708
+ if item.is_dir():
709
+ copytree(item, dst / rename(item).name, ignore=ignore, copy_function=copy_function, rename=rename)
710
+ else:
711
+ copy_function(item, dst / rename(item).name)
712
+
713
+
714
+ @create.command()
715
+ @click.argument(
716
+ "target",
717
+ type=click.Path(exists=False),
718
+ default="solara_portal",
719
+ required=False,
720
+ )
721
+ def portal(target: Path):
722
+ """Create a full Python project template for a data portal"""
723
+ target = Path(target)
724
+ name = target.name
725
+ package_name = name.replace("-", "_")
726
+
727
+ def copy_function(src: Path, dst: Path):
728
+ dst.write_bytes(
729
+ src.read_bytes().replace(b"solara-portal", name.encode("utf8")).replace(b"solara_portal", package_name.encode("utf8")),
730
+ )
731
+
732
+ def rename(path: Path):
733
+ if path.name == "solara_portal":
734
+ return path.parent / package_name
735
+ else:
736
+ return path
737
+
738
+ copytree(
739
+ HERE / "template" / "portal",
740
+ target,
741
+ ignore=lambda p: p.name.startswith("__") and p.name != "__init__.py",
742
+ copy_function=copy_function,
743
+ rename=rename,
744
+ )
745
+ rprint(f"Wrote: {target.resolve()}")
746
+ rprint(f"Install as:\n\t $ (cd {target}; pip install -e .)")
747
+ rprint(f"Run as:\n\t $ solara run {package_name}.pages")
748
+
749
+
750
+ cli.add_command(run)
751
+ cli.add_command(staticbuild)
752
+ cli.add_command(create)
753
+ cli.add_command(ssg)
754
+
755
+
756
+ def main():
757
+ args = sys.argv
758
+ # skip everything after -- if it exists
759
+ if "--" in args:
760
+ args = args[: args.index("--")]
761
+ cli(args[1:])
762
+
763
+
764
+ if __name__ == "__main__":
765
+ main()