solara 1.30.1__py2.py3-none-any.whl → 1.31.0__py2.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 (438) hide show
  1. {solara-1.30.1.dist-info → solara-1.31.0.dist-info}/METADATA +7 -42
  2. solara-1.31.0.dist-info/RECORD +5 -0
  3. {solara-1.30.1.dist-info → solara-1.31.0.dist-info}/WHEEL +1 -1
  4. {solara/template/portal → solara-1.31.0.dist-info/licenses}/LICENSE +1 -1
  5. solara/__init__.py +0 -123
  6. solara/__main__.py +0 -734
  7. solara/alias.py +0 -6
  8. solara/autorouting.py +0 -545
  9. solara/cache.py +0 -294
  10. solara/checks.html +0 -71
  11. solara/checks.py +0 -224
  12. solara/comm.py +0 -28
  13. solara/components/__init__.py +0 -59
  14. solara/components/alert.py +0 -155
  15. solara/components/applayout.py +0 -393
  16. solara/components/button.py +0 -85
  17. solara/components/card.py +0 -87
  18. solara/components/checkbox.py +0 -50
  19. solara/components/code_highlight_css.py +0 -11
  20. solara/components/code_highlight_css.vue +0 -63
  21. solara/components/columns.py +0 -159
  22. solara/components/component_vue.py +0 -110
  23. solara/components/cross_filter.py +0 -335
  24. solara/components/dataframe.py +0 -546
  25. solara/components/datatable.py +0 -221
  26. solara/components/datatable.vue +0 -175
  27. solara/components/details.py +0 -21
  28. solara/components/download.vue +0 -35
  29. solara/components/echarts.py +0 -75
  30. solara/components/echarts.vue +0 -128
  31. solara/components/figure_altair.py +0 -39
  32. solara/components/file_browser.py +0 -182
  33. solara/components/file_download.py +0 -199
  34. solara/components/file_drop.py +0 -139
  35. solara/components/file_drop.vue +0 -83
  36. solara/components/file_list_widget.vue +0 -78
  37. solara/components/head.py +0 -27
  38. solara/components/head_tag.py +0 -49
  39. solara/components/head_tag.vue +0 -60
  40. solara/components/image.py +0 -173
  41. solara/components/input.py +0 -440
  42. solara/components/link.py +0 -55
  43. solara/components/markdown.py +0 -371
  44. solara/components/markdown_editor.py +0 -25
  45. solara/components/markdown_editor.vue +0 -362
  46. solara/components/matplotlib.py +0 -74
  47. solara/components/meta.py +0 -47
  48. solara/components/misc.py +0 -333
  49. solara/components/pivot_table.py +0 -258
  50. solara/components/pivot_table.vue +0 -158
  51. solara/components/progress.py +0 -47
  52. solara/components/select.py +0 -186
  53. solara/components/select.vue +0 -27
  54. solara/components/slider.py +0 -442
  55. solara/components/slider_date.vue +0 -56
  56. solara/components/spinner-solara.vue +0 -105
  57. solara/components/spinner.py +0 -30
  58. solara/components/sql_code.py +0 -33
  59. solara/components/sql_code.vue +0 -128
  60. solara/components/style.py +0 -105
  61. solara/components/switch.py +0 -68
  62. solara/components/tab_navigation.py +0 -37
  63. solara/components/title.py +0 -90
  64. solara/components/title.vue +0 -38
  65. solara/components/togglebuttons.py +0 -202
  66. solara/components/tooltip.py +0 -61
  67. solara/datatypes.py +0 -143
  68. solara/express.py +0 -241
  69. solara/hooks/__init__.py +0 -4
  70. solara/hooks/dataframe.py +0 -99
  71. solara/hooks/misc.py +0 -263
  72. solara/hooks/use_reactive.py +0 -129
  73. solara/hooks/use_thread.py +0 -129
  74. solara/kitchensink.py +0 -8
  75. solara/lab/__init__.py +0 -34
  76. solara/lab/components/__init__.py +0 -6
  77. solara/lab/components/chat.py +0 -203
  78. solara/lab/components/confirmation_dialog.py +0 -165
  79. solara/lab/components/cross_filter.py +0 -7
  80. solara/lab/components/input_date.py +0 -298
  81. solara/lab/components/menu.py +0 -181
  82. solara/lab/components/menu.vue +0 -38
  83. solara/lab/components/tabs.py +0 -274
  84. solara/lab/components/theming.py +0 -98
  85. solara/lab/components/theming.vue +0 -72
  86. solara/lab/hooks/__init__.py +0 -0
  87. solara/lab/hooks/dataframe.py +0 -12
  88. solara/lab/toestand.py +0 -3
  89. solara/lab/utils/__init__.py +0 -2
  90. solara/lab/utils/cookies.py +0 -5
  91. solara/lab/utils/dataframe.py +0 -115
  92. solara/lab/utils/headers.py +0 -5
  93. solara/layout.py +0 -44
  94. solara/minisettings.py +0 -133
  95. solara/py.typed +0 -0
  96. solara/reactive.py +0 -93
  97. solara/routing.py +0 -268
  98. solara/scope/__init__.py +0 -88
  99. solara/scope/types.py +0 -55
  100. solara/server/__init__.py +0 -0
  101. solara/server/app.py +0 -490
  102. solara/server/assets/custom.css +0 -1
  103. solara/server/assets/custom.js +0 -1
  104. solara/server/assets/favicon.png +0 -0
  105. solara/server/assets/favicon.svg +0 -5
  106. solara/server/assets/style.css +0 -1665
  107. solara/server/assets/theme-dark.css +0 -437
  108. solara/server/assets/theme-light.css +0 -420
  109. solara/server/assets/theme.js +0 -3
  110. solara/server/cdn_helper.py +0 -77
  111. solara/server/esm.py +0 -69
  112. solara/server/fastapi.py +0 -5
  113. solara/server/flask.py +0 -286
  114. solara/server/jupyter/__init__.py +0 -2
  115. solara/server/jupyter/cdn_handler.py +0 -28
  116. solara/server/jupyter/server_extension.py +0 -29
  117. solara/server/jupytertools.py +0 -46
  118. solara/server/kernel.py +0 -319
  119. solara/server/kernel_context.py +0 -396
  120. solara/server/patch.py +0 -552
  121. solara/server/reload.py +0 -242
  122. solara/server/server.py +0 -437
  123. solara/server/settings.py +0 -212
  124. solara/server/shell.py +0 -240
  125. solara/server/starlette.py +0 -597
  126. solara/server/static/ansi.js +0 -270
  127. solara/server/static/highlight-dark.css +0 -82
  128. solara/server/static/highlight.css +0 -43
  129. solara/server/static/main-vuetify.js +0 -260
  130. solara/server/static/main.js +0 -163
  131. solara/server/static/solara_bootstrap.py +0 -129
  132. solara/server/static/sun.svg +0 -23
  133. solara/server/static/webworker.js +0 -42
  134. solara/server/telemetry.py +0 -212
  135. solara/server/templates/index.html.j2 +0 -1
  136. solara/server/templates/loader-plain.css +0 -11
  137. solara/server/templates/loader-plain.html +0 -20
  138. solara/server/templates/loader-solara.css +0 -111
  139. solara/server/templates/loader-solara.html +0 -40
  140. solara/server/templates/plain.html +0 -82
  141. solara/server/templates/solara.html.j2 +0 -446
  142. solara/server/threaded.py +0 -75
  143. solara/server/utils.py +0 -30
  144. solara/server/websocket.py +0 -44
  145. solara/settings.py +0 -56
  146. solara/tasks.py +0 -847
  147. solara/template/button.py +0 -16
  148. solara/template/markdown.py +0 -42
  149. solara/template/portal/.flake8 +0 -6
  150. solara/template/portal/.pre-commit-config.yaml +0 -28
  151. solara/template/portal/Procfile +0 -7
  152. solara/template/portal/mypy.ini +0 -3
  153. solara/template/portal/pyproject.toml +0 -26
  154. solara/template/portal/solara_portal/__init__.py +0 -3
  155. solara/template/portal/solara_portal/components/__init__.py +0 -2
  156. solara/template/portal/solara_portal/components/article.py +0 -28
  157. solara/template/portal/solara_portal/components/data.py +0 -28
  158. solara/template/portal/solara_portal/components/header.py +0 -6
  159. solara/template/portal/solara_portal/components/layout.py +0 -6
  160. solara/template/portal/solara_portal/content/articles/equis-in-vidi.md +0 -85
  161. solara/template/portal/solara_portal/content/articles/substiterat-vati.md +0 -70
  162. solara/template/portal/solara_portal/data.py +0 -60
  163. solara/template/portal/solara_portal/pages/__init__.py +0 -67
  164. solara/template/portal/solara_portal/pages/article/__init__.py +0 -26
  165. solara/template/portal/solara_portal/pages/tabular.py +0 -29
  166. solara/template/portal/solara_portal/pages/viz/__init__.py +0 -69
  167. solara/template/portal/solara_portal/pages/viz/overview.py +0 -14
  168. solara/test/__init__.py +0 -0
  169. solara/test/pytest_plugin.py +0 -698
  170. solara/toestand.py +0 -772
  171. solara/util.py +0 -308
  172. solara/website/__init__.py +0 -0
  173. solara/website/assets/custom.css +0 -462
  174. solara/website/assets/images/logo-small.png +0 -0
  175. solara/website/assets/images/logo.svg +0 -17
  176. solara/website/assets/images/logo_white.svg +0 -50
  177. solara/website/assets/theme.js +0 -8
  178. solara/website/components/__init__.py +0 -5
  179. solara/website/components/algolia.vue +0 -24
  180. solara/website/components/algolia_api.vue +0 -157
  181. solara/website/components/docs.py +0 -118
  182. solara/website/components/header.py +0 -72
  183. solara/website/components/hero.py +0 -15
  184. solara/website/components/mailchimp.py +0 -12
  185. solara/website/components/mailchimp.vue +0 -47
  186. solara/website/components/markdown.py +0 -30
  187. solara/website/components/notebook.py +0 -172
  188. solara/website/pages/__init__.py +0 -575
  189. solara/website/pages/apps/__init__.py +0 -16
  190. solara/website/pages/apps/authorization/__init__.py +0 -118
  191. solara/website/pages/apps/authorization/admin.py +0 -12
  192. solara/website/pages/apps/authorization/users.py +0 -12
  193. solara/website/pages/apps/jupyter-dashboard-1.py +0 -116
  194. solara/website/pages/apps/layout-demo.py +0 -40
  195. solara/website/pages/apps/multipage/__init__.py +0 -38
  196. solara/website/pages/apps/multipage/page1.py +0 -26
  197. solara/website/pages/apps/multipage/page2.py +0 -34
  198. solara/website/pages/apps/scatter.py +0 -136
  199. solara/website/pages/apps/scrolling.py +0 -63
  200. solara/website/pages/apps/tutorial-streamlit.py +0 -18
  201. solara/website/pages/changelog/__init__.py +0 -8
  202. solara/website/pages/changelog/changelog.md +0 -195
  203. solara/website/pages/contact/__init__.py +0 -8
  204. solara/website/pages/contact/contact.md +0 -17
  205. solara/website/pages/doc_use_download.py +0 -85
  206. solara/website/pages/documentation/__init__.py +0 -184
  207. solara/website/pages/documentation/advanced/__init__.py +0 -9
  208. solara/website/pages/documentation/advanced/content/00-overview.md +0 -1
  209. solara/website/pages/documentation/advanced/content/10-howto/00-overview.md +0 -6
  210. solara/website/pages/documentation/advanced/content/10-howto/10-multipage.md +0 -196
  211. solara/website/pages/documentation/advanced/content/10-howto/20-layout.md +0 -125
  212. solara/website/pages/documentation/advanced/content/10-howto/30-testing.md +0 -154
  213. solara/website/pages/documentation/advanced/content/10-howto/31-debugging.md +0 -69
  214. solara/website/pages/documentation/advanced/content/10-howto/40-embed.md +0 -49
  215. solara/website/pages/documentation/advanced/content/10-howto/50-ipywidget_libraries.md +0 -124
  216. solara/website/pages/documentation/advanced/content/20-understanding/00-introduction.md +0 -10
  217. solara/website/pages/documentation/advanced/content/20-understanding/05-ipywidgets.md +0 -35
  218. solara/website/pages/documentation/advanced/content/20-understanding/06-ipyvuetify.md +0 -42
  219. solara/website/pages/documentation/advanced/content/20-understanding/10-reacton.md +0 -28
  220. solara/website/pages/documentation/advanced/content/20-understanding/12-reacton-basics.md +0 -108
  221. solara/website/pages/documentation/advanced/content/20-understanding/15-anatomy.md +0 -23
  222. solara/website/pages/documentation/advanced/content/20-understanding/17-rules-of-hooks.md +0 -7
  223. solara/website/pages/documentation/advanced/content/20-understanding/18-containers.md +0 -166
  224. solara/website/pages/documentation/advanced/content/20-understanding/20-solara.md +0 -18
  225. solara/website/pages/documentation/advanced/content/20-understanding/40-routing.md +0 -240
  226. solara/website/pages/documentation/advanced/content/20-understanding/50-solara-server.md +0 -86
  227. solara/website/pages/documentation/advanced/content/20-understanding/60-voila.md +0 -12
  228. solara/website/pages/documentation/advanced/content/30-enterprise/00-overview.md +0 -1
  229. solara/website/pages/documentation/advanced/content/30-enterprise/10-oauth.md +0 -171
  230. solara/website/pages/documentation/advanced/content/40-development/00-overview.md +0 -0
  231. solara/website/pages/documentation/advanced/content/40-development/01-contribute.md +0 -45
  232. solara/website/pages/documentation/advanced/content/40-development/10-setup.md +0 -76
  233. solara/website/pages/documentation/api/__init__.py +0 -19
  234. solara/website/pages/documentation/api/cross_filter/__init__.py +0 -9
  235. solara/website/pages/documentation/api/cross_filter/cross_filter_dataframe.py +0 -23
  236. solara/website/pages/documentation/api/cross_filter/cross_filter_report.py +0 -22
  237. solara/website/pages/documentation/api/cross_filter/cross_filter_select.py +0 -22
  238. solara/website/pages/documentation/api/cross_filter/cross_filter_slider.py +0 -22
  239. solara/website/pages/documentation/api/hooks/__init__.py +0 -9
  240. solara/website/pages/documentation/api/hooks/use_cross_filter.py +0 -25
  241. solara/website/pages/documentation/api/hooks/use_dark_effective.py +0 -13
  242. solara/website/pages/documentation/api/hooks/use_effect.md +0 -43
  243. solara/website/pages/documentation/api/hooks/use_effect.py +0 -9
  244. solara/website/pages/documentation/api/hooks/use_exception.py +0 -33
  245. solara/website/pages/documentation/api/hooks/use_memo.md +0 -16
  246. solara/website/pages/documentation/api/hooks/use_memo.py +0 -9
  247. solara/website/pages/documentation/api/hooks/use_previous.py +0 -32
  248. solara/website/pages/documentation/api/hooks/use_reactive.py +0 -15
  249. solara/website/pages/documentation/api/hooks/use_state.py +0 -11
  250. solara/website/pages/documentation/api/hooks/use_state_or_update.py +0 -68
  251. solara/website/pages/documentation/api/hooks/use_thread.md +0 -58
  252. solara/website/pages/documentation/api/hooks/use_thread.py +0 -44
  253. solara/website/pages/documentation/api/hooks/use_trait_observe.py +0 -13
  254. solara/website/pages/documentation/api/routing/__init__.py +0 -9
  255. solara/website/pages/documentation/api/routing/generate_routes.py +0 -11
  256. solara/website/pages/documentation/api/routing/generate_routes_directory.py +0 -11
  257. solara/website/pages/documentation/api/routing/resolve_path.py +0 -36
  258. solara/website/pages/documentation/api/routing/route.py +0 -32
  259. solara/website/pages/documentation/api/routing/use_route.py +0 -80
  260. solara/website/pages/documentation/api/routing/use_router.py +0 -15
  261. solara/website/pages/documentation/api/utilities/__init__.py +0 -9
  262. solara/website/pages/documentation/api/utilities/component_vue.py +0 -11
  263. solara/website/pages/documentation/api/utilities/computed.py +0 -16
  264. solara/website/pages/documentation/api/utilities/display.py +0 -15
  265. solara/website/pages/documentation/api/utilities/get_kernel_id.py +0 -16
  266. solara/website/pages/documentation/api/utilities/get_session_id.py +0 -16
  267. solara/website/pages/documentation/api/utilities/memoize.py +0 -36
  268. solara/website/pages/documentation/api/utilities/on_kernel_start.py +0 -27
  269. solara/website/pages/documentation/api/utilities/reactive.py +0 -15
  270. solara/website/pages/documentation/api/utilities/widget.py +0 -104
  271. solara/website/pages/documentation/components/__init__.py +0 -12
  272. solara/website/pages/documentation/components/advanced/__init__.py +0 -9
  273. solara/website/pages/documentation/components/advanced/link.py +0 -28
  274. solara/website/pages/documentation/components/advanced/meta.py +0 -21
  275. solara/website/pages/documentation/components/advanced/style.py +0 -44
  276. solara/website/pages/documentation/components/common.py +0 -9
  277. solara/website/pages/documentation/components/data/__init__.py +0 -9
  278. solara/website/pages/documentation/components/data/dataframe.py +0 -44
  279. solara/website/pages/documentation/components/data/pivot_table.py +0 -81
  280. solara/website/pages/documentation/components/enterprise/__init__.py +0 -9
  281. solara/website/pages/documentation/components/enterprise/avatar.py +0 -19
  282. solara/website/pages/documentation/components/enterprise/avatar_menu.py +0 -20
  283. solara/website/pages/documentation/components/input/__init__.py +0 -9
  284. solara/website/pages/documentation/components/input/button.py +0 -22
  285. solara/website/pages/documentation/components/input/checkbox.py +0 -12
  286. solara/website/pages/documentation/components/input/file_browser.py +0 -33
  287. solara/website/pages/documentation/components/input/file_drop.py +0 -75
  288. solara/website/pages/documentation/components/input/input.py +0 -18
  289. solara/website/pages/documentation/components/input/select.py +0 -21
  290. solara/website/pages/documentation/components/input/slider.py +0 -28
  291. solara/website/pages/documentation/components/input/switch.py +0 -12
  292. solara/website/pages/documentation/components/input/togglebuttons.py +0 -20
  293. solara/website/pages/documentation/components/lab/__init__.py +0 -9
  294. solara/website/pages/documentation/components/lab/chat.py +0 -108
  295. solara/website/pages/documentation/components/lab/confirmation_dialog.py +0 -55
  296. solara/website/pages/documentation/components/lab/cookies_headers.py +0 -48
  297. solara/website/pages/documentation/components/lab/input_date.py +0 -19
  298. solara/website/pages/documentation/components/lab/menu.py +0 -21
  299. solara/website/pages/documentation/components/lab/tab.py +0 -24
  300. solara/website/pages/documentation/components/lab/tabs.py +0 -44
  301. solara/website/pages/documentation/components/lab/task.py +0 -12
  302. solara/website/pages/documentation/components/lab/theming.py +0 -72
  303. solara/website/pages/documentation/components/lab/use_task.py +0 -12
  304. solara/website/pages/documentation/components/layout/__init__.py +0 -9
  305. solara/website/pages/documentation/components/layout/app_bar.py +0 -15
  306. solara/website/pages/documentation/components/layout/app_bar_title.py +0 -15
  307. solara/website/pages/documentation/components/layout/app_layout.py +0 -23
  308. solara/website/pages/documentation/components/layout/card.py +0 -14
  309. solara/website/pages/documentation/components/layout/card_actions.py +0 -15
  310. solara/website/pages/documentation/components/layout/column.py +0 -29
  311. solara/website/pages/documentation/components/layout/columns.py +0 -26
  312. solara/website/pages/documentation/components/layout/columns_responsive.py +0 -67
  313. solara/website/pages/documentation/components/layout/griddraggable.py +0 -61
  314. solara/website/pages/documentation/components/layout/gridfixed.py +0 -21
  315. solara/website/pages/documentation/components/layout/hbox.py +0 -17
  316. solara/website/pages/documentation/components/layout/row.py +0 -29
  317. solara/website/pages/documentation/components/layout/sidebar.py +0 -23
  318. solara/website/pages/documentation/components/layout/vbox.py +0 -19
  319. solara/website/pages/documentation/components/output/__init__.py +0 -9
  320. solara/website/pages/documentation/components/output/file_download.py +0 -12
  321. solara/website/pages/documentation/components/output/html.py +0 -22
  322. solara/website/pages/documentation/components/output/image.py +0 -12
  323. solara/website/pages/documentation/components/output/markdown.py +0 -59
  324. solara/website/pages/documentation/components/output/markdown_editor.py +0 -53
  325. solara/website/pages/documentation/components/output/sql_code.py +0 -84
  326. solara/website/pages/documentation/components/output/tooltip.py +0 -12
  327. solara/website/pages/documentation/components/page/__init__.py +0 -9
  328. solara/website/pages/documentation/components/page/head.py +0 -19
  329. solara/website/pages/documentation/components/page/title.py +0 -28
  330. solara/website/pages/documentation/components/status/__init__.py +0 -9
  331. solara/website/pages/documentation/components/status/error.py +0 -40
  332. solara/website/pages/documentation/components/status/info.py +0 -40
  333. solara/website/pages/documentation/components/status/progress.py +0 -10
  334. solara/website/pages/documentation/components/status/spinner.py +0 -10
  335. solara/website/pages/documentation/components/status/success.py +0 -40
  336. solara/website/pages/documentation/components/status/warning.py +0 -47
  337. solara/website/pages/documentation/components/viz/__init__.py +0 -9
  338. solara/website/pages/documentation/components/viz/altair.py +0 -44
  339. solara/website/pages/documentation/components/viz/echarts.py +0 -78
  340. solara/website/pages/documentation/components/viz/matplotlib.py +0 -32
  341. solara/website/pages/documentation/components/viz/plotly.py +0 -63
  342. solara/website/pages/documentation/components/viz/plotly_express.py +0 -41
  343. solara/website/pages/documentation/examples/__init__.py +0 -52
  344. solara/website/pages/documentation/examples/ai/__init__.py +0 -10
  345. solara/website/pages/documentation/examples/ai/chatbot.py +0 -96
  346. solara/website/pages/documentation/examples/ai/tokenizer.py +0 -106
  347. solara/website/pages/documentation/examples/basics/__init__.py +0 -2
  348. solara/website/pages/documentation/examples/basics/sine.py +0 -28
  349. solara/website/pages/documentation/examples/fullscreen/__init__.py +0 -9
  350. solara/website/pages/documentation/examples/fullscreen/authorization.py +0 -3
  351. solara/website/pages/documentation/examples/fullscreen/layout_demo.py +0 -3
  352. solara/website/pages/documentation/examples/fullscreen/multipage.py +0 -3
  353. solara/website/pages/documentation/examples/fullscreen/scatter.py +0 -3
  354. solara/website/pages/documentation/examples/fullscreen/scrolling.py +0 -3
  355. solara/website/pages/documentation/examples/fullscreen/tutorial_streamlit.py +0 -3
  356. solara/website/pages/documentation/examples/general/__init__.py +0 -9
  357. solara/website/pages/documentation/examples/general/custom_storage.py +0 -69
  358. solara/website/pages/documentation/examples/general/deploy_model.py +0 -114
  359. solara/website/pages/documentation/examples/general/live_update.py +0 -38
  360. solara/website/pages/documentation/examples/general/login_oauth.py +0 -76
  361. solara/website/pages/documentation/examples/general/mycard.vue +0 -58
  362. solara/website/pages/documentation/examples/general/pokemon_search.py +0 -51
  363. solara/website/pages/documentation/examples/general/vue_component.py +0 -50
  364. solara/website/pages/documentation/examples/ipycanvas.py +0 -49
  365. solara/website/pages/documentation/examples/libraries/__init__.py +0 -9
  366. solara/website/pages/documentation/examples/libraries/altair.py +0 -63
  367. solara/website/pages/documentation/examples/libraries/bqplot.py +0 -39
  368. solara/website/pages/documentation/examples/libraries/ipyleaflet.py +0 -32
  369. solara/website/pages/documentation/examples/libraries/ipyleaflet_advanced.py +0 -65
  370. solara/website/pages/documentation/examples/utilities/__init__.py +0 -9
  371. solara/website/pages/documentation/examples/utilities/calculator.py +0 -157
  372. solara/website/pages/documentation/examples/utilities/countdown_timer.py +0 -64
  373. solara/website/pages/documentation/examples/utilities/todo.py +0 -195
  374. solara/website/pages/documentation/examples/visualization/__init__.py +0 -6
  375. solara/website/pages/documentation/examples/visualization/annotator.py +0 -68
  376. solara/website/pages/documentation/examples/visualization/linked_views.py +0 -84
  377. solara/website/pages/documentation/examples/visualization/plotly.py +0 -43
  378. solara/website/pages/documentation/faq/__init__.py +0 -12
  379. solara/website/pages/documentation/faq/content/99-faq.md +0 -76
  380. solara/website/pages/documentation/getting_started/__init__.py +0 -9
  381. solara/website/pages/documentation/getting_started/content/00-quickstart.md +0 -89
  382. solara/website/pages/documentation/getting_started/content/01-introduction.md +0 -125
  383. solara/website/pages/documentation/getting_started/content/02-installing.md +0 -85
  384. solara/website/pages/documentation/getting_started/content/04-tutorials/00-overview.md +0 -14
  385. solara/website/pages/documentation/getting_started/content/04-tutorials/10_data_science.py +0 -13
  386. solara/website/pages/documentation/getting_started/content/04-tutorials/20-web-app.md +0 -89
  387. solara/website/pages/documentation/getting_started/content/04-tutorials/30-ipywidgets.md +0 -124
  388. solara/website/pages/documentation/getting_started/content/04-tutorials/40-streamlit.md +0 -146
  389. solara/website/pages/documentation/getting_started/content/04-tutorials/50-dash.md +0 -144
  390. solara/website/pages/documentation/getting_started/content/04-tutorials/60-jupyter-dashboard-part1.py +0 -64
  391. solara/website/pages/documentation/getting_started/content/04-tutorials/SF_crime_sample.csv.gz +0 -0
  392. solara/website/pages/documentation/getting_started/content/04-tutorials/_data_science.ipynb +0 -445
  393. solara/website/pages/documentation/getting_started/content/04-tutorials/_jupyter_dashboard_1.ipynb +0 -1000
  394. solara/website/pages/documentation/getting_started/content/05-fundamentals/00-overview.md +0 -11
  395. solara/website/pages/documentation/getting_started/content/05-fundamentals/10-components.md +0 -223
  396. solara/website/pages/documentation/getting_started/content/05-fundamentals/50-state-management.md +0 -88
  397. solara/website/pages/documentation/getting_started/content/06-reference/00-overview.md +0 -3
  398. solara/website/pages/documentation/getting_started/content/06-reference/40-static_files.md +0 -31
  399. solara/website/pages/documentation/getting_started/content/06-reference/41-asset-files.md +0 -36
  400. solara/website/pages/documentation/getting_started/content/06-reference/60-static-site-generation.md +0 -59
  401. solara/website/pages/documentation/getting_started/content/06-reference/70-search.md +0 -34
  402. solara/website/pages/documentation/getting_started/content/06-reference/80-reloading.md +0 -34
  403. solara/website/pages/documentation/getting_started/content/06-reference/90-notebook-support.md +0 -7
  404. solara/website/pages/documentation/getting_started/content/06-reference/95-caching.md +0 -148
  405. solara/website/pages/documentation/getting_started/content/07-deploying/00-overview.md +0 -7
  406. solara/website/pages/documentation/getting_started/content/07-deploying/10-self-hosted.md +0 -273
  407. solara/website/pages/documentation/getting_started/content/07-deploying/20-cloud-hosted.md +0 -80
  408. solara/website/pages/documentation/getting_started/content/80-what-is-lab.md +0 -7
  409. solara/website/pages/documentation/getting_started/content/90-troubleshoot.md +0 -26
  410. solara/website/pages/docutils.py +0 -38
  411. solara/website/pages/showcase/__init__.py +0 -105
  412. solara/website/pages/showcase/domino_code_assist.py +0 -60
  413. solara/website/pages/showcase/planeto_tessa.py +0 -19
  414. solara/website/pages/showcase/solara_dev.py +0 -54
  415. solara/website/pages/showcase/solarathon_2023_team_2.py +0 -22
  416. solara/website/pages/showcase/solarathon_2023_team_4.py +0 -22
  417. solara/website/pages/showcase/solarathon_2023_team_5.py +0 -23
  418. solara/website/pages/showcase/solarathon_2023_team_6.py +0 -34
  419. solara/website/pages/showcase/wanderlust.py +0 -27
  420. solara/website/public/beach.jpeg +0 -0
  421. solara/website/public/logo.svg +0 -6
  422. solara/website/public/social/discord.svg +0 -1
  423. solara/website/public/social/github.svg +0 -1
  424. solara/website/public/social/twitter.svg +0 -3
  425. solara/website/public/success.html +0 -25
  426. solara/website/templates/index.html.j2 +0 -117
  427. solara/website/utils.py +0 -51
  428. solara/widgets/__init__.py +0 -1
  429. solara/widgets/vue/gridlayout.vue +0 -110
  430. solara/widgets/vue/html.vue +0 -4
  431. solara/widgets/vue/navigator.vue +0 -104
  432. solara/widgets/vue/vegalite.vue +0 -115
  433. solara/widgets/widgets.py +0 -65
  434. solara-1.30.1.data/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -7
  435. solara-1.30.1.data/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -7
  436. solara-1.30.1.dist-info/RECORD +0 -437
  437. solara-1.30.1.dist-info/entry_points.txt +0 -5
  438. /solara-1.30.1.dist-info/licenses/LICENSE → /LICENSE +0 -0
@@ -1,698 +0,0 @@
1
- import contextlib
2
- import inspect
3
- import json
4
- import logging
5
- import os
6
- import shlex
7
- import subprocess
8
- import sys
9
- import textwrap
10
- import threading
11
- import typing
12
- import urllib.parse
13
- import uuid
14
- from io import BytesIO
15
- from pathlib import Path
16
- from typing import Any, Callable, Dict, Generator, List, Union
17
-
18
- import ipywidgets as widgets
19
- import pytest
20
- import requests
21
- from IPython.display import display
22
-
23
- import solara.server.app
24
- import solara.server.kernel_context
25
- import solara.server.server
26
- import solara.server.settings
27
- from solara.server import reload
28
- from solara.server.starlette import ServerStarlette
29
- from solara.server.threaded import ServerBase
30
-
31
- if typing.TYPE_CHECKING:
32
- import playwright.sync_api
33
-
34
-
35
- logger = logging.getLogger("solara.pytest_plugin")
36
-
37
- TEST_PORT_START = int(os.environ.get("PORT", "18765")) + 100 # do not interfere with the solara integration tests
38
- TEST_HOST = solara.server.settings.main.host
39
- TIMEOUT = float(os.environ.get("SOLARA_PW_TIMEOUT", "18"))
40
-
41
-
42
- @pytest.fixture(scope="session")
43
- def solara_server(pytestconfig: Any, request):
44
- port = pytestconfig.getoption("--solara-port")
45
- host = pytestconfig.getoption("--solara-host")
46
- webserver = ServerStarlette(port, host)
47
-
48
- try:
49
- webserver.serve_threaded()
50
- webserver.wait_until_serving()
51
- yield webserver
52
- finally:
53
- webserver.stop_serving()
54
-
55
-
56
- @pytest.fixture(scope="session")
57
- def context_session(
58
- browser: "playwright.sync_api.Browser",
59
- browser_context_args: Dict,
60
- pytestconfig: Any,
61
- request: pytest.FixtureRequest,
62
- ) -> Generator["playwright.sync_api.BrowserContext", None, None]:
63
- from playwright.sync_api import Error, Page
64
- from pytest_playwright.pytest_playwright import _build_artifact_test_folder
65
- from slugify import slugify # type: ignore
66
-
67
- pages: List[Page] = []
68
- context = browser.new_context(**browser_context_args)
69
- context.on("page", lambda page: pages.append(page))
70
-
71
- tracing_option = pytestconfig.getoption("--tracing")
72
- capture_trace = tracing_option in ["on", "retain-on-failure"]
73
- if capture_trace:
74
- context.tracing.start(
75
- title=slugify(request.node.nodeid),
76
- screenshots=True,
77
- snapshots=True,
78
- sources=True,
79
- )
80
-
81
- # decrease timeout from 30s to 10s
82
- context.set_default_timeout(TIMEOUT * 1000)
83
- yield context
84
-
85
- # If request.node is missing rep_call, then some error happened during execution
86
- # that prevented teardown, but should still be counted as a failure
87
- failed = request.node.rep_call.failed if hasattr(request.node, "rep_call") else True
88
-
89
- if capture_trace:
90
- retain_trace = tracing_option == "on" or (failed and tracing_option == "retain-on-failure")
91
- if retain_trace:
92
- trace_path = _build_artifact_test_folder(pytestconfig, request, "trace.zip")
93
- context.tracing.stop(path=trace_path)
94
- else:
95
- context.tracing.stop()
96
-
97
- screenshot_option = pytestconfig.getoption("--screenshot")
98
- capture_screenshot = screenshot_option == "on" or (failed and screenshot_option == "only-on-failure")
99
- if capture_screenshot:
100
- for index, page in enumerate(pages):
101
- human_readable_status = "failed" if failed else "finished"
102
- screenshot_path = _build_artifact_test_folder(pytestconfig, request, f"test-{human_readable_status}-{index+1}.png")
103
- try:
104
- page.screenshot(timeout=5000, path=screenshot_path)
105
- except Error:
106
- pass
107
-
108
- context.close()
109
-
110
- video_option = pytestconfig.getoption("--video")
111
- preserve_video = video_option == "on" or (failed and video_option == "retain-on-failure")
112
- if preserve_video:
113
- for page in pages:
114
- video = page.video
115
- if not video:
116
- continue
117
- try:
118
- video_path = video.path()
119
- file_name = os.path.basename(video_path)
120
- video.save_as(path=_build_artifact_test_folder(pytestconfig, request, file_name))
121
- except Error:
122
- # Silent catch empty videos.
123
- pass
124
-
125
-
126
- # page fixture that keeps open all the time, is faster
127
- @pytest.fixture(scope="session")
128
- def page_session(context_session: "playwright.sync_api.BrowserContext"):
129
- page = context_session.new_page()
130
-
131
- def log(msg):
132
- print("BROWSER LOG:", msg.text) # noqa
133
- logger.debug("PAGE LOG: %s", msg.text)
134
-
135
- page.on("console", log)
136
- yield page
137
- page.close()
138
-
139
-
140
- @pytest.fixture()
141
- def solara_app(solara_server):
142
- used_app = None
143
-
144
- @contextlib.contextmanager
145
- def run(app: Union[solara.server.app.AppScript, str]):
146
- nonlocal used_app
147
- if "__default__" in solara.server.app.apps:
148
- solara.server.app.apps["__default__"].close()
149
- if isinstance(app, str):
150
- app = solara.server.app.AppScript(app)
151
- used_app = app
152
- solara.server.app.apps["__default__"] = app
153
- try:
154
- yield
155
- finally:
156
- if app.type == solara.server.app.AppType.MODULE:
157
- if app.name in sys.modules and app.name.startswith("tests.integration.testapp"):
158
- del sys.modules[app.name]
159
- if app.name in reload.reloader.watched_modules:
160
- reload.reloader.watched_modules.remove(app.name)
161
-
162
- yield run
163
- if used_app:
164
- used_app.close()
165
-
166
-
167
- run_events: Dict[str, threading.Event] = {}
168
- used_contexts: Dict[str, solara.server.kernel_context.VirtualKernelContext] = {}
169
-
170
-
171
- @solara.component
172
- def SyncWrapper():
173
- global run_calls
174
- router = solara.use_router()
175
- values = urllib.parse.parse_qs(router.search, keep_blank_values=True)
176
- id = values.get("id", [None])[0] # type: ignore
177
- if id is None:
178
- solara.Error("No id found in url")
179
- else:
180
-
181
- import reacton.ipywidgets as w
182
-
183
- used_contexts[id] = solara.server.kernel_context.get_current_context()
184
- run_events[id].set()
185
-
186
- return w.VBox(children=[w.HTML(value="Test in solara"), w.VBox()])
187
-
188
-
189
- @contextlib.contextmanager
190
- def _solara_test(solara_server, solara_app, page_session: "playwright.sync_api.Page", require_vuetify_warmup: bool):
191
- with solara_app("solara.test.pytest_plugin:SyncWrapper"):
192
- id = str(uuid.uuid4())
193
- run_events[id] = run_event = threading.Event()
194
- page_session.goto(solara_server.base_url + f"?id={id}")
195
- try:
196
- assert run_event.wait(10)
197
- context = used_contexts[id]
198
- with context:
199
- test_output_warmup = widgets.Output()
200
- test_output = widgets.Output()
201
- try:
202
- page_session.locator("text=Test in solara").wait_for()
203
- assert context.container
204
- context.container.children[0].children[1].children[1].children = [test_output_warmup] # type: ignore
205
- with test_output_warmup:
206
- page_session.add_style_tag(
207
- content="""
208
- .solara-content-main {
209
- animation-duration: 0s !important;
210
- }
211
- """
212
- )
213
- if require_vuetify_warmup:
214
- warmup()
215
- button = page_session.locator(".solara-warmup-widget")
216
- button.wait_for()
217
- page_session.evaluate("document.fonts.ready")
218
- button.click()
219
- button.wait_for(state="detached")
220
- page_session.evaluate("document.fonts.ready")
221
- context.container.children[0].children[1].children[1].children = [test_output] # type: ignore
222
- with test_output:
223
- yield
224
- finally:
225
- test_output.close()
226
- test_output_warmup.close()
227
- finally:
228
- del run_events[id]
229
- page_session.goto("about:blank")
230
- if id in used_contexts:
231
- # handle when run_event.wait(10) fails
232
- del used_contexts[id]
233
- assert context.closed_event.wait(10)
234
-
235
-
236
- @pytest.fixture()
237
- def solara_test(solara_server, solara_app, page_session: "playwright.sync_api.Page", pytestconfig: Any):
238
- require_vuetify_warmup = pytestconfig.getoption("solara_vuetify_warmup")
239
- with _solara_test(solara_server, solara_app, page_session, require_vuetify_warmup):
240
- yield
241
-
242
-
243
- class ServerVoila(ServerBase):
244
- popen = None
245
-
246
- def __init__(self, notebook_path, port: int, host: str = "localhost", **kwargs):
247
- self.notebook_path = notebook_path
248
- super().__init__(port, host)
249
-
250
- def has_started(self):
251
- try:
252
- return requests.get(self.base_url).status_code // 100 in [2, 3]
253
- except requests.exceptions.ConnectionError:
254
- return False
255
-
256
- def signal_stop(self):
257
- if self.popen is None:
258
- return
259
- self.popen.terminate()
260
- self.popen.kill()
261
-
262
- def serve(self):
263
- if self.has_started():
264
- raise RuntimeError("Jupyter server already running, use lsof -i :{self.port} to find the process and kill it")
265
- cmd = (
266
- "voila --no-browser --VoilaTest.log_level=DEBUG --Voila.port_retries=0 --VoilaExecutor.timeout=240"
267
- f" --Voila.port={self.port} --show_tracebacks=True {self.notebook_path} --enable_nbextensions=True"
268
- )
269
- logger.info(f"Starting Voila server at {self.base_url} with command {cmd}")
270
- args = shlex.split(cmd)
271
- self.popen = subprocess.Popen(args, shell=False, stdout=sys.stdout, stderr=sys.stderr, stdin=None)
272
- self.started.set()
273
-
274
-
275
- class ServerJupyter(ServerBase):
276
- popen = None
277
-
278
- def __init__(self, notebook_path, port: int, host: str = "localhost", **kwargs):
279
- self.notebook_path = notebook_path
280
- super().__init__(port, host)
281
-
282
- def has_started(self):
283
- try:
284
- return requests.get(self.base_url).status_code // 100 in [2, 3]
285
- except requests.exceptions.ConnectionError:
286
- return False
287
-
288
- def signal_stop(self):
289
- if self.popen is None:
290
- return
291
- self.popen.terminate()
292
- self.popen.kill()
293
-
294
- def serve(self):
295
- if self.has_started():
296
- raise RuntimeError(f"Jupyter server already running, use lsof -i :{self.port} to find the process and kill it")
297
- cmd = f'jupyter lab --port={self.port} --no-browser --ServerApp.token="" --port-retries=0 {self.notebook_path}'
298
- logger.info(f"Starting Jupyter (lab) server at {self.base_url} with command {cmd}")
299
- args = shlex.split(cmd)
300
- self.popen = subprocess.Popen(args, shell=False, stdout=sys.stdout, stderr=sys.stderr, stdin=None)
301
- self.started.set()
302
-
303
-
304
- @pytest.fixture(scope="session")
305
- def voila_server(pytestconfig: Any, notebook_path):
306
- port = pytestconfig.getoption("--voila-port")
307
- host = pytestconfig.getoption("--solara-host")
308
- write_notebook(["print('hello')"], notebook_path)
309
- server = ServerVoila(notebook_path, port, host)
310
- try:
311
- server.serve_threaded()
312
- server.wait_until_serving()
313
- yield server
314
- finally:
315
- server.stop_serving()
316
-
317
-
318
- @pytest.fixture(scope="session")
319
- def jupyter_server(pytestconfig: Any, notebook_path):
320
- port = pytestconfig.getoption("--jupyter-port")
321
- host = pytestconfig.getoption("--solara-host")
322
- write_notebook(["print('hello')"], notebook_path)
323
- server = ServerJupyter(notebook_path, port, host)
324
- try:
325
- server.serve_threaded()
326
- server.wait_until_serving()
327
- yield server
328
- finally:
329
- server.stop_serving()
330
-
331
-
332
- def code_from_function(f) -> str:
333
- lines = inspect.getsourcelines(f)[0]
334
- lines = lines[1:]
335
- return textwrap.dedent("".join(lines))
336
-
337
-
338
- def write_notebook(codes: List[str], path: str):
339
- notebook = {
340
- "cells": [{"cell_type": "code", "execution_count": None, "id": "df77670d", "metadata": {}, "outputs": [], "source": [code]} for code in codes],
341
- "metadata": {
342
- "kernelspec": {"display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3"},
343
- "language_info": {
344
- "codemirror_mode": {"name": "ipython", "version": 3},
345
- "file_extension": ".py",
346
- "mimetype": "text/x-python",
347
- "name": "python",
348
- "nbconvert_exporter": "python",
349
- "pygments_lexer": "ipython3",
350
- "version": "3.9.16",
351
- },
352
- },
353
- "nbformat": 4,
354
- "nbformat_minor": 5,
355
- }
356
-
357
- with open(path, "w") as file:
358
- json.dump(notebook, file)
359
-
360
-
361
- @pytest.fixture(scope="session")
362
- def notebook_path(tmp_path_factory):
363
- path = tmp_path_factory.mktemp("notebooks") / "notebook.ipynb"
364
- yield str(path)
365
-
366
-
367
- def warmup():
368
- import ipyvuetify as v
369
-
370
- # give it a scope so we do not collide with the user's code
371
- def scoped():
372
- button = v.Btn(children=["Warmup js/css/fonts", v.Icon(children=["mdi-check"])], class_="solara-warmup-widget")
373
- container = v.Container(
374
- children=[button],
375
- class_="ma-2 snapshot-container",
376
- )
377
-
378
- def remove_button(*ignore):
379
- container.children = []
380
- container.style_ = "display: none"
381
-
382
- button.on_event("click", remove_button)
383
- return container
384
-
385
- display(scoped())
386
-
387
-
388
- def create_runner_voila(voila_server, notebook_path, page_session: "playwright.sync_api.Page", require_vuetify_warmup: bool):
389
- count = 0
390
- base_url = voila_server.base_url
391
-
392
- def run(f: Callable, locals={}):
393
- nonlocal count
394
- path = Path(f.__code__.co_filename)
395
- cwd = str(path.parent)
396
- code_setup = f"""
397
- import os
398
- os.chdir({cwd!r})
399
- \n"""
400
- for name, value in locals.items():
401
- code_setup += f"{name} = {value!r}\n"
402
- if require_vuetify_warmup:
403
- write_notebook([code_setup, code_from_function(warmup), code_from_function(f)], notebook_path)
404
- else:
405
- write_notebook([code_setup, code_from_function(f)], notebook_path)
406
- page_session.goto(base_url + f"?v={count}")
407
- if require_vuetify_warmup:
408
- button = page_session.locator(".solara-warmup-widget")
409
- button.wait_for()
410
- page_session.evaluate("document.fonts.ready")
411
- button.click()
412
- button.wait_for(state="detached")
413
- page_session.evaluate("document.fonts.ready")
414
- count += 1
415
-
416
- return run
417
-
418
-
419
- def create_runner_jupyter_lab(jupyter_server, notebook_path, page_session: "playwright.sync_api.Page", require_vuetify_warmup: bool):
420
- count = 0
421
- base_url = jupyter_server.base_url
422
-
423
- def run(f: Callable, locals={}):
424
- nonlocal count
425
- path = Path(f.__code__.co_filename)
426
- cwd = str(path.parent)
427
- code_setup = f"""
428
- import os
429
- os.chdir({cwd!r})
430
- \n"""
431
- if require_vuetify_warmup:
432
- code_setup += """
433
- import ipyvuetify as v;
434
- display(v.Btn(children=['Warmup js/css/fonts', v.Icon(children=["mdi-check"])], class_="solara-warmup-widget"))
435
- \n"""
436
- for name, value in locals.items():
437
- code_setup += f"{name} = {value!r}\n"
438
-
439
- write_notebook([code_setup, code_from_function(f)], notebook_path)
440
- page_session.goto(base_url + f"/lab/workspaces/solara-test/tree/notebook.ipynb?reset&v={count}")
441
- page_session.locator('css=[data-command="runmenu:run"]').wait_for()
442
- # close the file browser tab, it does not give a consistent width each time
443
- # which leads to fractional pixel x, which causes the screenshot to be different
444
- # by 1 pixel
445
- # first make sure the page is loaded
446
- page_session.locator('css=[data-id="filebrowser"]').wait_for()
447
- page_session.locator('css=[data-command="filebrowser:create-main-launcher"]').wait_for()
448
- # close
449
- page_session.locator('css=[data-id="filebrowser"]').click()
450
- # make sure it is closed
451
- page_session.locator('css=[data-command="filebrowser:create-main-launcher"]').wait_for(state="hidden")
452
-
453
- page_session.locator('button:has-text("No Kernel")').wait_for(state="detached")
454
- page_session.locator('css=[data-status="idle"]').wait_for()
455
- page_session.locator('css=[data-command="runmenu:run"]').click()
456
- if require_vuetify_warmup:
457
- page_session.locator(".solara-warmup-widget").wait_for()
458
- page_session.evaluate("document.fonts.ready")
459
- page_session.locator('css=[data-command="runmenu:run"]').click()
460
- count += 1
461
-
462
- return run
463
-
464
-
465
- def create_runner_jupyter_notebook(jupyter_server, notebook_path, page_session: "playwright.sync_api.Page", require_vuetify_warmup: bool):
466
- count = 0
467
- base_url = jupyter_server.base_url
468
-
469
- def run(f: Callable, locals={}):
470
- nonlocal count
471
- path = Path(f.__code__.co_filename)
472
- cwd = str(path.parent)
473
- code_setup = f"""
474
- import os
475
- os.chdir({cwd!r})
476
- """
477
- if require_vuetify_warmup:
478
- code_setup += """
479
- import ipyvuetify as v;
480
- display(v.Btn(children=['Warmup js/css/fonts', v.Icon(children=["mdi-check"])], class_="solara-warmup-widget"))
481
- \n"""
482
- for name, value in locals.items():
483
- code_setup += f"{name} = {value!r}\n"
484
- write_notebook([code_setup, code_from_function(f)], notebook_path)
485
- page_session.goto(base_url + f"/notebooks/notebook.ipynb?v={count}")
486
- page_session.locator(".prompt_container >> nth=0").wait_for()
487
- page_session.locator("text=Kernel starting, please wait...").wait_for(state="detached")
488
- page_session.locator("Kernel Ready").wait_for(state="detached")
489
- page_session.locator('css=[data-jupyter-action="jupyter-notebook:run-cell-and-select-next"]').click()
490
- if require_vuetify_warmup:
491
- page_session.locator(".solara-warmup-widget").wait_for()
492
- page_session.evaluate("document.fonts.ready")
493
- page_session.locator('css=[data-jupyter-action="jupyter-notebook:run-cell-and-select-next"]').click()
494
- count += 1
495
-
496
- return run
497
-
498
-
499
- @contextlib.contextmanager
500
- def create_runner_solara(solara_server, solara_app, page_session: "playwright.sync_api.Page", require_vuetify_warmup: bool):
501
- count = 0
502
-
503
- def run(f: Callable, locals={}):
504
- nonlocal count
505
- path = Path(f.__code__.co_filename)
506
- cwd = str(path.parent)
507
- current_dir = os.getcwd()
508
- os.chdir(cwd)
509
- import sys
510
-
511
- sys.path.append(cwd)
512
- try:
513
- f()
514
- finally:
515
- os.chdir(current_dir)
516
- sys.path.remove(cwd)
517
- count += 1
518
-
519
- with _solara_test(solara_server, solara_app, page_session, require_vuetify_warmup):
520
- yield run
521
-
522
-
523
- runners = os.environ.get("SOLARA_TEST_RUNNERS", "solara,voila,jupyter_lab,jupyter_notebook").split(",")
524
-
525
-
526
- @pytest.fixture(params=runners)
527
- def ipywidgets_runner(
528
- solara_server,
529
- solara_app,
530
- voila_server,
531
- jupyter_server,
532
- notebook_path,
533
- page_session: "playwright.sync_api.Page",
534
- request,
535
- pytestconfig: Any,
536
- ):
537
- runner = request.param
538
- require_vuetify_warmup = pytestconfig.getoption("solara_vuetify_warmup")
539
- if runner == "solara":
540
- with create_runner_solara(solara_server, solara_app, page_session, require_vuetify_warmup) as runner:
541
- yield runner
542
- elif runner == "voila":
543
- yield create_runner_voila(voila_server, notebook_path, page_session, require_vuetify_warmup)
544
- elif runner == "jupyter_lab":
545
- yield create_runner_jupyter_lab(jupyter_server, notebook_path, page_session, require_vuetify_warmup)
546
- elif runner == "jupyter_notebook":
547
- yield create_runner_jupyter_notebook(jupyter_server, notebook_path, page_session, require_vuetify_warmup)
548
- else:
549
- raise RuntimeError(f"Unknown runner {runner}")
550
-
551
-
552
- @pytest.fixture(scope="session")
553
- def solara_snapshots_directory(request: Any) -> Path:
554
- path = Path(request.config.rootpath) / "tests" / "ui" / "snapshots"
555
- if not path.exists():
556
- path.mkdir(exist_ok=True, parents=True)
557
- return path
558
-
559
-
560
- def compare_default(reference, result, threshold=0.1):
561
- from PIL import Image
562
- from pixelmatch.contrib.PIL import pixelmatch
563
-
564
- difference = Image.new("RGB", reference.size)
565
- diff = pixelmatch(reference, result, difference, threshold=threshold)
566
- return diff, difference
567
-
568
-
569
- @pytest.fixture
570
- def assert_solara_snapshot(pytestconfig: Any, request: Any, browser_name: str, solara_snapshots_directory) -> Callable:
571
- from PIL import Image
572
-
573
- testname = f"{str(Path(request.node.name))}".replace("[", "-").replace("]", "").replace(" ", "-").replace(",", "-")
574
- directory = solara_snapshots_directory / request.node.location[0]
575
- output_dir = Path(pytestconfig.getoption("--output")) / request.node.location[0]
576
- if not directory.exists():
577
- directory.mkdir(exist_ok=True, parents=True)
578
- if not output_dir.exists():
579
- output_dir.mkdir(exist_ok=True, parents=True)
580
-
581
- def assert_implementation(
582
- image: bytes,
583
- compare: Callable = compare_default,
584
- testname=testname,
585
- format="{prefix}{testname}-{platform}{postfix}-{type}.png",
586
- prefix="",
587
- postfix="",
588
- ):
589
- update_snapshot = pytestconfig.getoption("--solara-update-snapshots")
590
- format_kwargs = dict(testname=testname, platform=sys.platform, browser=browser_name, prefix=prefix, postfix=postfix)
591
- path_reference = directory / format.format(**format_kwargs, type="reference").format(**format_kwargs)
592
- path_reference_output = output_dir / format.format(**format_kwargs, type="reference").format(**format_kwargs)
593
- path_previous = output_dir / format.format(**format_kwargs, type="previous").format(**format_kwargs)
594
- path_diff = output_dir / format.format(**format_kwargs, type="diff").format(**format_kwargs)
595
- if not path_reference.exists():
596
- if update_snapshot:
597
- path_reference.write_bytes(image)
598
- else:
599
- # CI run, store the reference, but fail
600
- path_reference_output.write_bytes(image)
601
- raise AssertionError(
602
- f'Snapshot {path_reference} did not exist, file written. Run `cp "{path_reference_output}" "{path_reference}"` '
603
- "Commit this file and rerun the CI. Or run with --solara-update-snapshots to update it."
604
- )
605
- else:
606
- if update_snapshot:
607
- path_reference.write_bytes(image)
608
- else:
609
- reference = Image.open(path_reference)
610
- result = Image.open(BytesIO(image))
611
- difference = None
612
-
613
- def write():
614
- if update_snapshot:
615
- path_reference.write_bytes(image)
616
- else:
617
- # CI run, update the reference in the output dir, and store the previous run next to it
618
- path_reference_output.write_bytes(image)
619
- reference.save(path_previous)
620
- if difference is not None:
621
- difference.save(path_diff)
622
-
623
- # the error msg of the default compare is not very helpful
624
- if reference.size != result.size:
625
- write()
626
- raise AssertionError(
627
- f"Snapshot {path_reference} has a different size than the result {reference.size} != {result.size}."
628
- f'Run `cp "{path_reference_output}" "{path_reference}"` Commit this file and rerun the CI. '
629
- "Or run with --solara-update-snapshots to update it."
630
- )
631
- diff, difference = compare(reference, result)
632
- if diff > 0:
633
- write()
634
- raise AssertionError(
635
- f'Snapshot {path_reference} does not match, Run `cp "{path_reference_output}" "{path_reference}"` Commit this file and rerun the CI. '
636
- "Or run with --solara-update-snapshots to update it."
637
- )
638
-
639
- return assert_implementation
640
-
641
-
642
- def pytest_addoption(parser: Any) -> None:
643
- group = parser.getgroup("solara", "Solara")
644
- group.addoption(
645
- "--solara-update-snapshots",
646
- action="store_true",
647
- default=False,
648
- help="Do not compare, but store the snapshots.",
649
- )
650
- group.addoption(
651
- "--solara-update-snapshots-ci",
652
- action="store_true",
653
- default=False,
654
- help="On compare failure, store to the reference image. Useful for running in CI and downloading the snapshots.",
655
- )
656
- group.addoption(
657
- "--solara-host",
658
- default=TEST_HOST,
659
- help="Host or IP all servers will bind to (solara, jupyter, voila)",
660
- )
661
- group.addoption(
662
- "--solara-port",
663
- type=int,
664
- default=TEST_PORT_START + 0,
665
- help="Port the solara server is running on for the test",
666
- )
667
- group.addoption(
668
- "--jupyter-port",
669
- type=int,
670
- default=TEST_PORT_START + 1,
671
- help="Port the jupyter server is running on for the test (for classic notebook and juptyer lab)",
672
- )
673
- group.addoption(
674
- "--voila-port",
675
- type=int,
676
- default=TEST_PORT_START + 2,
677
- help="Port the voila server is running on for the test (for classic notebook and juptyer lab)",
678
- )
679
- vuetify_warmup = os.environ.get("SOLARA_TEST_VUETIFY_WARMUP", "true") in ["true", "True", "1", "on", "On"]
680
- help = (
681
- "Load/not load the vuetify fonts and css before running the test, leading to more stable screenshots. If (ipy)vuetify is not used this can be disabled."
682
- )
683
- if vuetify_warmup:
684
- group.addoption(
685
- "--no-solara-vuetify-warmup",
686
- action="store_false",
687
- default=vuetify_warmup,
688
- help=help,
689
- dest="solara_vuetify_warmup",
690
- )
691
- else:
692
- group.addoption(
693
- "--solara-vuetify-warmup",
694
- action="store_false",
695
- default=vuetify_warmup,
696
- help=help,
697
- dest="solara_vuetify_warmup",
698
- )