solara 1.30.0__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.
- {solara-1.30.0.dist-info → solara-1.31.0.dist-info}/METADATA +7 -42
- solara-1.31.0.dist-info/RECORD +5 -0
- {solara-1.30.0.dist-info → solara-1.31.0.dist-info}/WHEEL +1 -1
- {solara/template/portal → solara-1.31.0.dist-info/licenses}/LICENSE +1 -1
- solara/__init__.py +0 -123
- solara/__main__.py +0 -734
- solara/alias.py +0 -6
- solara/autorouting.py +0 -554
- solara/cache.py +0 -294
- solara/checks.html +0 -71
- solara/checks.py +0 -224
- solara/comm.py +0 -28
- solara/components/__init__.py +0 -59
- solara/components/alert.py +0 -155
- solara/components/applayout.py +0 -393
- solara/components/button.py +0 -85
- solara/components/card.py +0 -87
- solara/components/checkbox.py +0 -50
- solara/components/code_highlight_css.py +0 -11
- solara/components/code_highlight_css.vue +0 -63
- solara/components/columns.py +0 -159
- solara/components/component_vue.py +0 -109
- solara/components/cross_filter.py +0 -335
- solara/components/dataframe.py +0 -546
- solara/components/datatable.py +0 -221
- solara/components/datatable.vue +0 -175
- solara/components/details.py +0 -21
- solara/components/download.vue +0 -35
- solara/components/echarts.py +0 -75
- solara/components/echarts.vue +0 -128
- solara/components/figure_altair.py +0 -39
- solara/components/file_browser.py +0 -182
- solara/components/file_download.py +0 -199
- solara/components/file_drop.py +0 -139
- solara/components/file_drop.vue +0 -83
- solara/components/file_list_widget.vue +0 -78
- solara/components/head.py +0 -27
- solara/components/head_tag.py +0 -49
- solara/components/head_tag.vue +0 -60
- solara/components/image.py +0 -173
- solara/components/input.py +0 -440
- solara/components/link.py +0 -55
- solara/components/markdown.py +0 -371
- solara/components/markdown_editor.py +0 -25
- solara/components/markdown_editor.vue +0 -362
- solara/components/matplotlib.py +0 -74
- solara/components/meta.py +0 -47
- solara/components/misc.py +0 -333
- solara/components/pivot_table.py +0 -258
- solara/components/pivot_table.vue +0 -158
- solara/components/progress.py +0 -47
- solara/components/select.py +0 -186
- solara/components/select.vue +0 -27
- solara/components/slider.py +0 -442
- solara/components/slider_date.vue +0 -56
- solara/components/spinner-solara.vue +0 -105
- solara/components/spinner.py +0 -30
- solara/components/sql_code.py +0 -33
- solara/components/sql_code.vue +0 -128
- solara/components/style.py +0 -105
- solara/components/switch.py +0 -68
- solara/components/tab_navigation.py +0 -37
- solara/components/title.py +0 -90
- solara/components/title.vue +0 -38
- solara/components/togglebuttons.py +0 -202
- solara/components/tooltip.py +0 -61
- solara/datatypes.py +0 -143
- solara/express.py +0 -241
- solara/hooks/__init__.py +0 -4
- solara/hooks/dataframe.py +0 -99
- solara/hooks/misc.py +0 -263
- solara/hooks/use_reactive.py +0 -129
- solara/hooks/use_thread.py +0 -129
- solara/kitchensink.py +0 -8
- solara/lab/__init__.py +0 -34
- solara/lab/components/__init__.py +0 -6
- solara/lab/components/chat.py +0 -203
- solara/lab/components/confirmation_dialog.py +0 -165
- solara/lab/components/cross_filter.py +0 -7
- solara/lab/components/input_date.py +0 -298
- solara/lab/components/menu.py +0 -181
- solara/lab/components/menu.vue +0 -38
- solara/lab/components/tabs.py +0 -274
- solara/lab/components/theming.py +0 -98
- solara/lab/components/theming.vue +0 -72
- solara/lab/hooks/__init__.py +0 -0
- solara/lab/hooks/dataframe.py +0 -12
- solara/lab/toestand.py +0 -3
- solara/lab/utils/__init__.py +0 -2
- solara/lab/utils/cookies.py +0 -5
- solara/lab/utils/dataframe.py +0 -115
- solara/lab/utils/headers.py +0 -5
- solara/layout.py +0 -44
- solara/minisettings.py +0 -133
- solara/py.typed +0 -0
- solara/reactive.py +0 -93
- solara/routing.py +0 -268
- solara/scope/__init__.py +0 -87
- solara/scope/types.py +0 -55
- solara/server/__init__.py +0 -0
- solara/server/app.py +0 -490
- solara/server/assets/custom.css +0 -1
- solara/server/assets/custom.js +0 -1
- solara/server/assets/favicon.png +0 -0
- solara/server/assets/favicon.svg +0 -5
- solara/server/assets/style.css +0 -1665
- solara/server/assets/theme-dark.css +0 -437
- solara/server/assets/theme-light.css +0 -420
- solara/server/assets/theme.js +0 -3
- solara/server/cdn_helper.py +0 -77
- solara/server/esm.py +0 -69
- solara/server/fastapi.py +0 -5
- solara/server/flask.py +0 -286
- solara/server/jupyter/__init__.py +0 -2
- solara/server/jupyter/cdn_handler.py +0 -28
- solara/server/jupyter/server_extension.py +0 -29
- solara/server/jupytertools.py +0 -46
- solara/server/kernel.py +0 -319
- solara/server/kernel_context.py +0 -393
- solara/server/patch.py +0 -552
- solara/server/reload.py +0 -242
- solara/server/server.py +0 -437
- solara/server/settings.py +0 -212
- solara/server/shell.py +0 -240
- solara/server/starlette.py +0 -597
- solara/server/static/ansi.js +0 -270
- solara/server/static/highlight-dark.css +0 -82
- solara/server/static/highlight.css +0 -43
- solara/server/static/main-vuetify.js +0 -260
- solara/server/static/main.js +0 -163
- solara/server/static/solara_bootstrap.py +0 -129
- solara/server/static/sun.svg +0 -23
- solara/server/static/webworker.js +0 -42
- solara/server/telemetry.py +0 -212
- solara/server/templates/index.html.j2 +0 -1
- solara/server/templates/loader-plain.css +0 -11
- solara/server/templates/loader-plain.html +0 -20
- solara/server/templates/loader-solara.css +0 -111
- solara/server/templates/loader-solara.html +0 -40
- solara/server/templates/plain.html +0 -82
- solara/server/templates/solara.html.j2 +0 -446
- solara/server/threaded.py +0 -75
- solara/server/utils.py +0 -30
- solara/server/websocket.py +0 -44
- solara/settings.py +0 -56
- solara/tasks.py +0 -847
- solara/template/button.py +0 -16
- solara/template/markdown.py +0 -42
- solara/template/portal/.flake8 +0 -6
- solara/template/portal/.pre-commit-config.yaml +0 -28
- solara/template/portal/Procfile +0 -7
- solara/template/portal/mypy.ini +0 -3
- solara/template/portal/pyproject.toml +0 -26
- solara/template/portal/solara_portal/__init__.py +0 -3
- solara/template/portal/solara_portal/components/__init__.py +0 -2
- solara/template/portal/solara_portal/components/article.py +0 -28
- solara/template/portal/solara_portal/components/data.py +0 -28
- solara/template/portal/solara_portal/components/header.py +0 -6
- solara/template/portal/solara_portal/components/layout.py +0 -6
- solara/template/portal/solara_portal/content/articles/equis-in-vidi.md +0 -85
- solara/template/portal/solara_portal/content/articles/substiterat-vati.md +0 -70
- solara/template/portal/solara_portal/data.py +0 -60
- solara/template/portal/solara_portal/pages/__init__.py +0 -67
- solara/template/portal/solara_portal/pages/article/__init__.py +0 -26
- solara/template/portal/solara_portal/pages/tabular.py +0 -29
- solara/template/portal/solara_portal/pages/viz/__init__.py +0 -69
- solara/template/portal/solara_portal/pages/viz/overview.py +0 -14
- solara/test/__init__.py +0 -0
- solara/test/pytest_plugin.py +0 -698
- solara/toestand.py +0 -772
- solara/util.py +0 -308
- solara/website/__init__.py +0 -0
- solara/website/assets/custom.css +0 -462
- solara/website/assets/images/logo-small.png +0 -0
- solara/website/assets/images/logo.svg +0 -17
- solara/website/assets/images/logo_white.svg +0 -50
- solara/website/assets/theme.js +0 -8
- solara/website/components/__init__.py +0 -5
- solara/website/components/algolia.vue +0 -24
- solara/website/components/algolia_api.vue +0 -157
- solara/website/components/docs.py +0 -118
- solara/website/components/header.py +0 -72
- solara/website/components/hero.py +0 -15
- solara/website/components/mailchimp.py +0 -12
- solara/website/components/mailchimp.vue +0 -47
- solara/website/components/notebook.py +0 -172
- solara/website/pages/__init__.py +0 -564
- solara/website/pages/apps/__init__.py +0 -16
- solara/website/pages/apps/authorization/__init__.py +0 -118
- solara/website/pages/apps/authorization/admin.py +0 -12
- solara/website/pages/apps/authorization/users.py +0 -12
- solara/website/pages/apps/jupyter-dashboard-1.py +0 -116
- solara/website/pages/apps/layout-demo.py +0 -40
- solara/website/pages/apps/multipage/__init__.py +0 -38
- solara/website/pages/apps/multipage/page1.py +0 -26
- solara/website/pages/apps/multipage/page2.py +0 -34
- solara/website/pages/apps/scatter.py +0 -136
- solara/website/pages/apps/scrolling.py +0 -63
- solara/website/pages/apps/tutorial-streamlit.py +0 -18
- solara/website/pages/changelog/__init__.py +0 -8
- solara/website/pages/changelog/changelog.md +0 -180
- solara/website/pages/contact/__init__.py +0 -8
- solara/website/pages/contact/contact.md +0 -17
- solara/website/pages/doc_use_download.py +0 -85
- solara/website/pages/documentation/__init__.py +0 -184
- solara/website/pages/documentation/advanced/__init__.py +0 -36
- solara/website/pages/documentation/advanced/content/00-overview.md +0 -1
- solara/website/pages/documentation/advanced/content/10-howto/00-overview.md +0 -1
- solara/website/pages/documentation/advanced/content/10-howto/10-multipage.md +0 -191
- solara/website/pages/documentation/advanced/content/10-howto/20-layout.md +0 -120
- solara/website/pages/documentation/advanced/content/10-howto/30-testing.md +0 -149
- solara/website/pages/documentation/advanced/content/10-howto/31-debugging.md +0 -66
- solara/website/pages/documentation/advanced/content/10-howto/40-embed.md +0 -43
- solara/website/pages/documentation/advanced/content/10-howto/50-ipywidget_libraries.md +0 -120
- solara/website/pages/documentation/advanced/content/20-understanding/00-introduction.md +0 -4
- solara/website/pages/documentation/advanced/content/20-understanding/05-ipywidgets.md +0 -30
- solara/website/pages/documentation/advanced/content/20-understanding/06-ipyvuetify.md +0 -38
- solara/website/pages/documentation/advanced/content/20-understanding/10-reacton.md +0 -24
- solara/website/pages/documentation/advanced/content/20-understanding/12-reacton-basics.md +0 -104
- solara/website/pages/documentation/advanced/content/20-understanding/15-anatomy.md +0 -19
- solara/website/pages/documentation/advanced/content/20-understanding/17-rules-of-hooks.md +0 -3
- solara/website/pages/documentation/advanced/content/20-understanding/18-containers.md +0 -160
- solara/website/pages/documentation/advanced/content/20-understanding/20-solara.md +0 -13
- solara/website/pages/documentation/advanced/content/20-understanding/40-routing.md +0 -236
- solara/website/pages/documentation/advanced/content/20-understanding/50-solara-server.md +0 -81
- solara/website/pages/documentation/advanced/content/20-understanding/60-voila.md +0 -7
- solara/website/pages/documentation/advanced/content/30-enterprise/00-overview.md +0 -1
- solara/website/pages/documentation/advanced/content/30-enterprise/10-oauth.md +0 -167
- solara/website/pages/documentation/advanced/content/40-development/00-overview.md +0 -0
- solara/website/pages/documentation/advanced/content/40-development/01-contribute.md +0 -41
- solara/website/pages/documentation/advanced/content/40-development/10-setup.md +0 -72
- solara/website/pages/documentation/advanced/content/__init__.py +0 -0
- solara/website/pages/documentation/api/__init__.py +0 -19
- solara/website/pages/documentation/api/cross_filter/__init__.py +0 -9
- solara/website/pages/documentation/api/cross_filter/cross_filter_dataframe.py +0 -23
- solara/website/pages/documentation/api/cross_filter/cross_filter_report.py +0 -22
- solara/website/pages/documentation/api/cross_filter/cross_filter_select.py +0 -22
- solara/website/pages/documentation/api/cross_filter/cross_filter_slider.py +0 -22
- solara/website/pages/documentation/api/hooks/__init__.py +0 -9
- solara/website/pages/documentation/api/hooks/use_cross_filter.py +0 -25
- solara/website/pages/documentation/api/hooks/use_dark_effective.py +0 -13
- solara/website/pages/documentation/api/hooks/use_effect.md +0 -43
- solara/website/pages/documentation/api/hooks/use_effect.py +0 -9
- solara/website/pages/documentation/api/hooks/use_exception.py +0 -33
- solara/website/pages/documentation/api/hooks/use_memo.md +0 -16
- solara/website/pages/documentation/api/hooks/use_memo.py +0 -9
- solara/website/pages/documentation/api/hooks/use_previous.py +0 -32
- solara/website/pages/documentation/api/hooks/use_reactive.py +0 -15
- solara/website/pages/documentation/api/hooks/use_state.py +0 -11
- solara/website/pages/documentation/api/hooks/use_state_or_update.py +0 -68
- solara/website/pages/documentation/api/hooks/use_thread.md +0 -58
- solara/website/pages/documentation/api/hooks/use_thread.py +0 -44
- solara/website/pages/documentation/api/hooks/use_trait_observe.py +0 -13
- solara/website/pages/documentation/api/routing/__init__.py +0 -9
- solara/website/pages/documentation/api/routing/generate_routes.py +0 -11
- solara/website/pages/documentation/api/routing/generate_routes_directory.py +0 -11
- solara/website/pages/documentation/api/routing/resolve_path.py +0 -36
- solara/website/pages/documentation/api/routing/route.py +0 -32
- solara/website/pages/documentation/api/routing/use_route.py +0 -80
- solara/website/pages/documentation/api/routing/use_router.py +0 -15
- solara/website/pages/documentation/api/utilities/__init__.py +0 -9
- solara/website/pages/documentation/api/utilities/component_vue.py +0 -11
- solara/website/pages/documentation/api/utilities/computed.py +0 -16
- solara/website/pages/documentation/api/utilities/display.py +0 -15
- solara/website/pages/documentation/api/utilities/get_kernel_id.py +0 -16
- solara/website/pages/documentation/api/utilities/get_session_id.py +0 -16
- solara/website/pages/documentation/api/utilities/memoize.py +0 -36
- solara/website/pages/documentation/api/utilities/on_kernel_start.py +0 -27
- solara/website/pages/documentation/api/utilities/reactive.py +0 -15
- solara/website/pages/documentation/api/utilities/widget.py +0 -104
- solara/website/pages/documentation/components/__init__.py +0 -12
- solara/website/pages/documentation/components/advanced/__init__.py +0 -9
- solara/website/pages/documentation/components/advanced/link.py +0 -28
- solara/website/pages/documentation/components/advanced/meta.py +0 -21
- solara/website/pages/documentation/components/advanced/style.py +0 -44
- solara/website/pages/documentation/components/common.py +0 -9
- solara/website/pages/documentation/components/data/__init__.py +0 -9
- solara/website/pages/documentation/components/data/dataframe.py +0 -44
- solara/website/pages/documentation/components/data/pivot_table.py +0 -81
- solara/website/pages/documentation/components/enterprise/__init__.py +0 -9
- solara/website/pages/documentation/components/enterprise/avatar.py +0 -19
- solara/website/pages/documentation/components/enterprise/avatar_menu.py +0 -20
- solara/website/pages/documentation/components/input/__init__.py +0 -9
- solara/website/pages/documentation/components/input/button.py +0 -22
- solara/website/pages/documentation/components/input/checkbox.py +0 -12
- solara/website/pages/documentation/components/input/file_browser.py +0 -33
- solara/website/pages/documentation/components/input/file_drop.py +0 -75
- solara/website/pages/documentation/components/input/input.py +0 -18
- solara/website/pages/documentation/components/input/select.py +0 -21
- solara/website/pages/documentation/components/input/slider.py +0 -28
- solara/website/pages/documentation/components/input/switch.py +0 -12
- solara/website/pages/documentation/components/input/togglebuttons.py +0 -20
- solara/website/pages/documentation/components/lab/__init__.py +0 -9
- solara/website/pages/documentation/components/lab/chat.py +0 -108
- solara/website/pages/documentation/components/lab/confirmation_dialog.py +0 -55
- solara/website/pages/documentation/components/lab/cookies_headers.py +0 -48
- solara/website/pages/documentation/components/lab/input_date.py +0 -19
- solara/website/pages/documentation/components/lab/menu.py +0 -21
- solara/website/pages/documentation/components/lab/tab.py +0 -24
- solara/website/pages/documentation/components/lab/tabs.py +0 -44
- solara/website/pages/documentation/components/lab/task.py +0 -12
- solara/website/pages/documentation/components/lab/theming.py +0 -72
- solara/website/pages/documentation/components/lab/use_task.py +0 -12
- solara/website/pages/documentation/components/layout/__init__.py +0 -9
- solara/website/pages/documentation/components/layout/app_bar.py +0 -15
- solara/website/pages/documentation/components/layout/app_bar_title.py +0 -15
- solara/website/pages/documentation/components/layout/app_layout.py +0 -23
- solara/website/pages/documentation/components/layout/card.py +0 -14
- solara/website/pages/documentation/components/layout/card_actions.py +0 -15
- solara/website/pages/documentation/components/layout/column.py +0 -29
- solara/website/pages/documentation/components/layout/columns.py +0 -26
- solara/website/pages/documentation/components/layout/columns_responsive.py +0 -67
- solara/website/pages/documentation/components/layout/griddraggable.py +0 -61
- solara/website/pages/documentation/components/layout/gridfixed.py +0 -21
- solara/website/pages/documentation/components/layout/hbox.py +0 -17
- solara/website/pages/documentation/components/layout/row.py +0 -29
- solara/website/pages/documentation/components/layout/sidebar.py +0 -23
- solara/website/pages/documentation/components/layout/vbox.py +0 -19
- solara/website/pages/documentation/components/output/__init__.py +0 -9
- solara/website/pages/documentation/components/output/file_download.py +0 -12
- solara/website/pages/documentation/components/output/html.py +0 -22
- solara/website/pages/documentation/components/output/image.py +0 -12
- solara/website/pages/documentation/components/output/markdown.py +0 -59
- solara/website/pages/documentation/components/output/markdown_editor.py +0 -53
- solara/website/pages/documentation/components/output/sql_code.py +0 -84
- solara/website/pages/documentation/components/output/tooltip.py +0 -12
- solara/website/pages/documentation/components/page/__init__.py +0 -9
- solara/website/pages/documentation/components/page/head.py +0 -19
- solara/website/pages/documentation/components/page/title.py +0 -28
- solara/website/pages/documentation/components/status/__init__.py +0 -9
- solara/website/pages/documentation/components/status/error.py +0 -40
- solara/website/pages/documentation/components/status/info.py +0 -40
- solara/website/pages/documentation/components/status/progress.py +0 -10
- solara/website/pages/documentation/components/status/spinner.py +0 -10
- solara/website/pages/documentation/components/status/success.py +0 -40
- solara/website/pages/documentation/components/status/warning.py +0 -47
- solara/website/pages/documentation/components/viz/__init__.py +0 -9
- solara/website/pages/documentation/components/viz/altair.py +0 -44
- solara/website/pages/documentation/components/viz/echarts.py +0 -78
- solara/website/pages/documentation/components/viz/matplotlib.py +0 -32
- solara/website/pages/documentation/components/viz/plotly.py +0 -63
- solara/website/pages/documentation/components/viz/plotly_express.py +0 -41
- solara/website/pages/documentation/examples/__init__.py +0 -52
- solara/website/pages/documentation/examples/ai/__init__.py +0 -10
- solara/website/pages/documentation/examples/ai/chatbot.py +0 -96
- solara/website/pages/documentation/examples/ai/tokenizer.py +0 -106
- solara/website/pages/documentation/examples/basics/__init__.py +0 -2
- solara/website/pages/documentation/examples/basics/sine.py +0 -28
- solara/website/pages/documentation/examples/fullscreen/__init__.py +0 -9
- solara/website/pages/documentation/examples/fullscreen/authorization.py +0 -3
- solara/website/pages/documentation/examples/fullscreen/layout_demo.py +0 -3
- solara/website/pages/documentation/examples/fullscreen/multipage.py +0 -3
- solara/website/pages/documentation/examples/fullscreen/scatter.py +0 -3
- solara/website/pages/documentation/examples/fullscreen/scrolling.py +0 -3
- solara/website/pages/documentation/examples/fullscreen/tutorial_streamlit.py +0 -3
- solara/website/pages/documentation/examples/general/__init__.py +0 -9
- solara/website/pages/documentation/examples/general/custom_storage.py +0 -69
- solara/website/pages/documentation/examples/general/deploy_model.py +0 -114
- solara/website/pages/documentation/examples/general/live_update.py +0 -38
- solara/website/pages/documentation/examples/general/login_oauth.py +0 -76
- solara/website/pages/documentation/examples/general/mycard.vue +0 -58
- solara/website/pages/documentation/examples/general/pokemon_search.py +0 -51
- solara/website/pages/documentation/examples/general/vue_component.py +0 -50
- solara/website/pages/documentation/examples/ipycanvas.py +0 -49
- solara/website/pages/documentation/examples/libraries/__init__.py +0 -9
- solara/website/pages/documentation/examples/libraries/altair.py +0 -63
- solara/website/pages/documentation/examples/libraries/bqplot.py +0 -39
- solara/website/pages/documentation/examples/libraries/ipyleaflet.py +0 -32
- solara/website/pages/documentation/examples/libraries/ipyleaflet_advanced.py +0 -65
- solara/website/pages/documentation/examples/utilities/__init__.py +0 -9
- solara/website/pages/documentation/examples/utilities/calculator.py +0 -157
- solara/website/pages/documentation/examples/utilities/countdown_timer.py +0 -64
- solara/website/pages/documentation/examples/utilities/todo.py +0 -195
- solara/website/pages/documentation/examples/visualization/__init__.py +0 -6
- solara/website/pages/documentation/examples/visualization/annotator.py +0 -68
- solara/website/pages/documentation/examples/visualization/linked_views.py +0 -84
- solara/website/pages/documentation/examples/visualization/plotly.py +0 -43
- solara/website/pages/documentation/faq/__init__.py +0 -11
- solara/website/pages/documentation/faq/content/99-faq.md +0 -73
- solara/website/pages/documentation/getting_started/__init__.py +0 -15
- solara/website/pages/documentation/getting_started/content/00-quickstart.md +0 -84
- solara/website/pages/documentation/getting_started/content/01-introduction.md +0 -121
- solara/website/pages/documentation/getting_started/content/02-installing.md +0 -81
- solara/website/pages/documentation/getting_started/content/04-tutorials/00-overview.md +0 -9
- solara/website/pages/documentation/getting_started/content/04-tutorials/10_data_science.py +0 -13
- solara/website/pages/documentation/getting_started/content/04-tutorials/20-web-app.md +0 -84
- solara/website/pages/documentation/getting_started/content/04-tutorials/30-ipywidgets.md +0 -120
- solara/website/pages/documentation/getting_started/content/04-tutorials/40-streamlit.md +0 -141
- solara/website/pages/documentation/getting_started/content/04-tutorials/50-dash.md +0 -140
- solara/website/pages/documentation/getting_started/content/04-tutorials/60-jupyter-dashboard-part1.py +0 -64
- solara/website/pages/documentation/getting_started/content/04-tutorials/SF_crime_sample.csv.gz +0 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/_data_science.ipynb +0 -445
- solara/website/pages/documentation/getting_started/content/04-tutorials/_jupyter_dashboard_1.ipynb +0 -1000
- solara/website/pages/documentation/getting_started/content/05-fundamentals/00-overview.md +0 -7
- solara/website/pages/documentation/getting_started/content/05-fundamentals/10-components.md +0 -218
- solara/website/pages/documentation/getting_started/content/05-fundamentals/50-state-management.md +0 -83
- solara/website/pages/documentation/getting_started/content/06-reference/00-overview.md +0 -3
- solara/website/pages/documentation/getting_started/content/06-reference/40-static_files.md +0 -27
- solara/website/pages/documentation/getting_started/content/06-reference/41-asset-files.md +0 -32
- solara/website/pages/documentation/getting_started/content/06-reference/60-static-site-generation.md +0 -55
- solara/website/pages/documentation/getting_started/content/06-reference/70-search.md +0 -30
- solara/website/pages/documentation/getting_started/content/06-reference/80-reloading.md +0 -30
- solara/website/pages/documentation/getting_started/content/06-reference/90-notebook-support.md +0 -4
- solara/website/pages/documentation/getting_started/content/06-reference/95-caching.md +0 -143
- solara/website/pages/documentation/getting_started/content/07-deploying/00-overview.md +0 -3
- solara/website/pages/documentation/getting_started/content/07-deploying/10-self-hosted.md +0 -269
- solara/website/pages/documentation/getting_started/content/07-deploying/20-cloud-hosted.md +0 -76
- solara/website/pages/documentation/getting_started/content/08-lab/00-what-is-lab.md +0 -3
- solara/website/pages/documentation/getting_started/content/90-troubleshoot.md +0 -22
- solara/website/pages/documentation/getting_started/content/__init__.py +0 -0
- solara/website/pages/docutils.py +0 -38
- solara/website/pages/showcase/__init__.py +0 -105
- solara/website/pages/showcase/domino_code_assist.py +0 -60
- solara/website/pages/showcase/planeto_tessa.py +0 -19
- solara/website/pages/showcase/solara_dev.py +0 -54
- solara/website/pages/showcase/solarathon_2023_team_2.py +0 -22
- solara/website/pages/showcase/solarathon_2023_team_4.py +0 -22
- solara/website/pages/showcase/solarathon_2023_team_5.py +0 -23
- solara/website/pages/showcase/solarathon_2023_team_6.py +0 -34
- solara/website/pages/showcase/wanderlust.py +0 -27
- solara/website/public/beach.jpeg +0 -0
- solara/website/public/logo.svg +0 -6
- solara/website/public/social/discord.svg +0 -1
- solara/website/public/social/github.svg +0 -1
- solara/website/public/social/twitter.svg +0 -3
- solara/website/public/success.html +0 -25
- solara/website/templates/index.html.j2 +0 -117
- solara/website/utils.py +0 -51
- solara/widgets/__init__.py +0 -1
- solara/widgets/vue/gridlayout.vue +0 -110
- solara/widgets/vue/html.vue +0 -4
- solara/widgets/vue/navigator.vue +0 -104
- solara/widgets/vue/vegalite.vue +0 -115
- solara/widgets/widgets.py +0 -65
- solara-1.30.0.data/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -7
- solara-1.30.0.data/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -7
- solara-1.30.0.dist-info/RECORD +0 -438
- solara-1.30.0.dist-info/entry_points.txt +0 -5
- /solara-1.30.0.dist-info/licenses/LICENSE → /LICENSE +0 -0
solara/server/reload.py
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import contextlib
|
|
2
|
-
import errno
|
|
3
|
-
import importlib
|
|
4
|
-
import inspect
|
|
5
|
-
import logging
|
|
6
|
-
import os
|
|
7
|
-
import sys
|
|
8
|
-
import threading
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
from typing import Any, Callable, Dict, List, Optional, Set, Type
|
|
11
|
-
|
|
12
|
-
import solara.server.settings as settings
|
|
13
|
-
|
|
14
|
-
NO_WATCHDOG = False
|
|
15
|
-
try:
|
|
16
|
-
from watchdog.events import FileSystemEventHandler
|
|
17
|
-
from watchdog.observers import Observer
|
|
18
|
-
except ImportError:
|
|
19
|
-
pass
|
|
20
|
-
NO_WATCHDOG = True
|
|
21
|
-
logger = logging.getLogger("solara.server.reload")
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class Watcher:
|
|
25
|
-
def __init__(self, files, on_change: Callable[[str], None]):
|
|
26
|
-
pass
|
|
27
|
-
|
|
28
|
-
def close(self):
|
|
29
|
-
pass
|
|
30
|
-
|
|
31
|
-
def add_file(self, file):
|
|
32
|
-
pass
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class WatcherDummy:
|
|
36
|
-
def __init__(self, files, on_change: Callable[[str], None]):
|
|
37
|
-
pass
|
|
38
|
-
|
|
39
|
-
def close(self):
|
|
40
|
-
pass
|
|
41
|
-
|
|
42
|
-
def add_file(self, file):
|
|
43
|
-
pass
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if NO_WATCHDOG:
|
|
47
|
-
WatcherType: Type[Watcher] = WatcherDummy # type: ignore
|
|
48
|
-
|
|
49
|
-
else:
|
|
50
|
-
|
|
51
|
-
class WatcherWatchdog(FileSystemEventHandler):
|
|
52
|
-
def __init__(self, files, on_change: Callable[[str], None]):
|
|
53
|
-
self.on_change = on_change
|
|
54
|
-
self.observers: List[Any] = []
|
|
55
|
-
self.directories: Set[str] = set()
|
|
56
|
-
self.files: List[str] = []
|
|
57
|
-
self.mtimes: Dict[str, float] = dict()
|
|
58
|
-
for file in files:
|
|
59
|
-
self.add_file(file)
|
|
60
|
-
|
|
61
|
-
def close(self):
|
|
62
|
-
for o in self.observers:
|
|
63
|
-
o.unschedule_all()
|
|
64
|
-
o.stop()
|
|
65
|
-
# this makes testing slow
|
|
66
|
-
# for o in self.observers:
|
|
67
|
-
# o.join()
|
|
68
|
-
|
|
69
|
-
def add_file(self, file):
|
|
70
|
-
file = os.path.abspath(os.path.realpath(file))
|
|
71
|
-
if file not in self.files:
|
|
72
|
-
logger.debug("Watching file %s", file)
|
|
73
|
-
if file in self.files:
|
|
74
|
-
raise RuntimeError(f"{file} was already added")
|
|
75
|
-
self.files.append(file)
|
|
76
|
-
self._watch_file(file)
|
|
77
|
-
|
|
78
|
-
def _watch_file(self, file):
|
|
79
|
-
self.mtimes[file] = os.path.getmtime(file)
|
|
80
|
-
directory = os.path.realpath(os.path.dirname(file))
|
|
81
|
-
self.watch_directory(directory)
|
|
82
|
-
|
|
83
|
-
def watch_directory(self, directory):
|
|
84
|
-
if directory not in self.directories:
|
|
85
|
-
logger.debug("Watching directory %s", directory)
|
|
86
|
-
observer = Observer()
|
|
87
|
-
observer.schedule(self, directory, recursive=False)
|
|
88
|
-
try:
|
|
89
|
-
observer.start()
|
|
90
|
-
except OSError as e:
|
|
91
|
-
if e.errno == errno.ENOSPC:
|
|
92
|
-
# from rich import print()
|
|
93
|
-
from .utils import start_error
|
|
94
|
-
|
|
95
|
-
start_error(
|
|
96
|
-
"inotify watch limit reached", "Read https://github.com/widgetti/solara/#how-to-fix-inotify-watch-limit-reached", exception=e
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
self.observers.append(observer)
|
|
100
|
-
self.directories.add(directory)
|
|
101
|
-
|
|
102
|
-
def on_modified(self, event):
|
|
103
|
-
super().on_modified(event)
|
|
104
|
-
logger.debug("Watch event: %s", event)
|
|
105
|
-
if not event.is_directory:
|
|
106
|
-
if event.src_path in self.files:
|
|
107
|
-
mtime_new = os.path.getmtime(event.src_path)
|
|
108
|
-
changed = mtime_new > self.mtimes[event.src_path]
|
|
109
|
-
self.mtimes[event.src_path] = mtime_new
|
|
110
|
-
if changed:
|
|
111
|
-
logger.debug("File modified: %s", event.src_path)
|
|
112
|
-
try:
|
|
113
|
-
self.on_change(event.src_path)
|
|
114
|
-
except: # noqa
|
|
115
|
-
# we are in the watchdog thread here, all we can do is report
|
|
116
|
-
# and continue running (otherwise reload stops working)
|
|
117
|
-
logger.exception("Error while executing on change handler")
|
|
118
|
-
else:
|
|
119
|
-
logger.debug("File reported modified, but mtime did not change: %s", event.src_path)
|
|
120
|
-
else:
|
|
121
|
-
logger.debug("Ignore file modification: %s", event.src_path)
|
|
122
|
-
|
|
123
|
-
WatcherType = WatcherWatchdog # type: ignore
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
class Reloader:
|
|
127
|
-
def __init__(self, on_change: Optional[Callable[[str], None]] = None) -> None:
|
|
128
|
-
self.watched_modules: Set[str] = set()
|
|
129
|
-
self._first = True
|
|
130
|
-
self.on_change: Optional[Callable[[str], None]] = on_change
|
|
131
|
-
WatcherCls = WatcherType if settings.main.mode == "development" else WatcherDummy
|
|
132
|
-
self.watcher = WatcherCls([], self._on_change)
|
|
133
|
-
self.requires_reload = False
|
|
134
|
-
self.ignore_modules: Set[str] = set()
|
|
135
|
-
self.reload_event_next = threading.Event()
|
|
136
|
-
# should be set at app.directory
|
|
137
|
-
self.root_path: Optional[Path] = None
|
|
138
|
-
# maybe we want this mode enabled in the future via configuration
|
|
139
|
-
# this is useful if you have some packages installed in editable mode
|
|
140
|
-
# and you want to quick reload. However this does not always work:
|
|
141
|
-
# * https://github.com/widgetti/solara/issues/177
|
|
142
|
-
# * https://github.com/widgetti/solara/issues/148
|
|
143
|
-
self.aggresive_reload = False
|
|
144
|
-
|
|
145
|
-
def start(self):
|
|
146
|
-
if self._first:
|
|
147
|
-
# although we try to keep track of modules loaded (which we will watch)
|
|
148
|
-
# it can happen that an import is done at runtime, that we miss (could be in a thread)
|
|
149
|
-
# so we always reload all modules except the ignore_modules
|
|
150
|
-
self.ignore_modules = set(sys.modules)
|
|
151
|
-
logger.debug("Ignoring reloading modules: %s", self.ignore_modules)
|
|
152
|
-
self._first = False
|
|
153
|
-
|
|
154
|
-
def _on_change(self, name):
|
|
155
|
-
# flag that we need to reload all modules next time
|
|
156
|
-
self.requires_reload = True
|
|
157
|
-
# and forward callback
|
|
158
|
-
if self.on_change:
|
|
159
|
-
self.on_change(name)
|
|
160
|
-
# used for testing
|
|
161
|
-
self.reload_event_next.set()
|
|
162
|
-
|
|
163
|
-
def close(self):
|
|
164
|
-
self.watcher.close()
|
|
165
|
-
|
|
166
|
-
def get_reload_module_names(self):
|
|
167
|
-
if self.aggresive_reload:
|
|
168
|
-
# not sure why, but if we reload pandas, the integration/reload_test.py fails
|
|
169
|
-
return {
|
|
170
|
-
k for k in set(sys.modules) - set(self.ignore_modules) if not (k.startswith("solara.server") or k.startswith("anyio") or k.startswith("pandas"))
|
|
171
|
-
}
|
|
172
|
-
else:
|
|
173
|
-
reload = []
|
|
174
|
-
for name in sorted(sys.modules):
|
|
175
|
-
mod = sys.modules[name]
|
|
176
|
-
if name.startswith("solara.server"):
|
|
177
|
-
continue # this will break everything
|
|
178
|
-
if name in self.ignore_modules:
|
|
179
|
-
continue # nothing we imported from solara itself like starlette etc
|
|
180
|
-
# we only reload modules that are in the root path
|
|
181
|
-
if (getattr(mod, "__file__", None) or "").startswith(str(self.root_path)):
|
|
182
|
-
reload.append(name)
|
|
183
|
-
return reload
|
|
184
|
-
|
|
185
|
-
def reload(self):
|
|
186
|
-
# before we did this:
|
|
187
|
-
# # don't reload modules like solara.server and react
|
|
188
|
-
# # that may cause issues (like 2 Element classes existing)
|
|
189
|
-
reload_modules = self.get_reload_module_names()
|
|
190
|
-
# which picks up import that are done in threads etc, but it will also reload starlette, httptools etc
|
|
191
|
-
# which causes issues with exceptions and isinstance checks.
|
|
192
|
-
# reload_modules = self.watched_modules
|
|
193
|
-
logger.info("Reloading modules... %s", reload_modules)
|
|
194
|
-
# not sure if this is needed
|
|
195
|
-
importlib.invalidate_caches()
|
|
196
|
-
for mod_name in sorted(reload_modules):
|
|
197
|
-
# don't reload modules like solara.server and react
|
|
198
|
-
# that may cause issues (like 2 Element classes existing)
|
|
199
|
-
logger.debug("Reloading module %s", mod_name)
|
|
200
|
-
sys.modules.pop(mod_name, None)
|
|
201
|
-
# if all successful...
|
|
202
|
-
self.requires_reload = False
|
|
203
|
-
|
|
204
|
-
@contextlib.contextmanager
|
|
205
|
-
def watch(self):
|
|
206
|
-
"""Use this context manager to execute code so that we can track loaded modules and reload them when needed"""
|
|
207
|
-
if self.requires_reload:
|
|
208
|
-
self.reload()
|
|
209
|
-
|
|
210
|
-
modules_before = set(sys.modules)
|
|
211
|
-
try:
|
|
212
|
-
yield
|
|
213
|
-
finally:
|
|
214
|
-
modules_after = set(sys.modules)
|
|
215
|
-
# these are imported during the 'yield'
|
|
216
|
-
modules_added = modules_after - modules_before
|
|
217
|
-
modules_to_consider = modules_added - set(self.watched_modules)
|
|
218
|
-
# TODO: libraries that solara uses need special care
|
|
219
|
-
# modules_always = {k for k in sys.modules if k.startswith("reacton")}
|
|
220
|
-
# modules_to_consider = modules_to_consider | modules_always
|
|
221
|
-
modules_watching = set()
|
|
222
|
-
if modules_to_consider:
|
|
223
|
-
logger.debug("Found modules %s", modules_to_consider)
|
|
224
|
-
for modname in modules_to_consider:
|
|
225
|
-
module = sys.modules[modname]
|
|
226
|
-
path = None
|
|
227
|
-
try:
|
|
228
|
-
path = inspect.getfile(module)
|
|
229
|
-
except Exception:
|
|
230
|
-
pass
|
|
231
|
-
if path and Path(path).exists():
|
|
232
|
-
if not path.startswith(sys.prefix):
|
|
233
|
-
self.watcher.add_file(path)
|
|
234
|
-
self.watched_modules.add(modname)
|
|
235
|
-
modules_watching.add(modname)
|
|
236
|
-
if modules_watching:
|
|
237
|
-
logger.debug("Watching modules: %s for reload", modules_watching)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
# there is only a reloader, and there should be only 1 app
|
|
241
|
-
# that connect to the on_change
|
|
242
|
-
reloader = Reloader()
|
solara/server/server.py
DELETED
|
@@ -1,437 +0,0 @@
|
|
|
1
|
-
import contextlib
|
|
2
|
-
import hashlib
|
|
3
|
-
import json
|
|
4
|
-
import logging
|
|
5
|
-
import os
|
|
6
|
-
import sys
|
|
7
|
-
import time
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from typing import Dict, List, Optional, Tuple, TypeVar
|
|
10
|
-
|
|
11
|
-
import ipykernel
|
|
12
|
-
import ipyvue
|
|
13
|
-
import ipywidgets
|
|
14
|
-
import jinja2
|
|
15
|
-
import requests
|
|
16
|
-
import solara
|
|
17
|
-
import solara.routing
|
|
18
|
-
import solara.settings
|
|
19
|
-
from solara.lab import cookies as solara_cookies
|
|
20
|
-
from solara.lab import headers as solara_headers
|
|
21
|
-
|
|
22
|
-
from . import app, jupytertools, patch, settings, websocket
|
|
23
|
-
from .kernel import Kernel, deserialize_binary_message
|
|
24
|
-
from .kernel_context import initialize_virtual_kernel
|
|
25
|
-
|
|
26
|
-
COOKIE_KEY_SESSION_ID = "solara-session-id"
|
|
27
|
-
|
|
28
|
-
T = TypeVar("T")
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
directory = Path(__file__).parent
|
|
32
|
-
template_name = "index.html.j2"
|
|
33
|
-
ipykernel_major = int(ipykernel.__version__.split(".")[0])
|
|
34
|
-
ipywidgets_major = int(ipywidgets.__version__.split(".")[0])
|
|
35
|
-
cache_memory = solara.cache.Memory(max_items=128)
|
|
36
|
-
vue3 = ipyvue.__version__.startswith("3")
|
|
37
|
-
_redirects: Dict[str, str] = {}
|
|
38
|
-
|
|
39
|
-
# first look at the project directory, then the builtin solara directory
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
@solara.memoize(storage=cache_memory)
|
|
43
|
-
def get_jinja_env(app_name: str) -> jinja2.Environment:
|
|
44
|
-
jinja_loader = jinja2.FileSystemLoader(
|
|
45
|
-
[
|
|
46
|
-
app.apps["__default__"].directory.parent / "templates",
|
|
47
|
-
str(directory / "templates"),
|
|
48
|
-
]
|
|
49
|
-
)
|
|
50
|
-
jinja_env = jinja2.Environment(loader=jinja_loader, autoescape=True)
|
|
51
|
-
return jinja_env
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
logger = logging.getLogger("solara.server.server")
|
|
55
|
-
nbextensions_ignorelist = [
|
|
56
|
-
"jupytext/index",
|
|
57
|
-
"nbextensions_configurator/config_menu/main",
|
|
58
|
-
"jupytext/index",
|
|
59
|
-
"nbdime/index",
|
|
60
|
-
"voila/extension",
|
|
61
|
-
"contrib_nbextensions_help_item/main",
|
|
62
|
-
"execute_time/ExecuteTime",
|
|
63
|
-
"dominocode/extension",
|
|
64
|
-
"low-code-assistant/extension",
|
|
65
|
-
"domino-code-assist/extension",
|
|
66
|
-
"jupyter-js/extension",
|
|
67
|
-
"jupyter-js-widgets/extension",
|
|
68
|
-
"jupyter_dash/main",
|
|
69
|
-
]
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def readyz():
|
|
73
|
-
return {"status": "ok"}, 200
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def wait_ready(url, timeout=10) -> None:
|
|
77
|
-
"""Wait for a solara server at root url to be ready, or throw a TimeoutError
|
|
78
|
-
|
|
79
|
-
This uses the /readyz endpoint to check if the server is ready.
|
|
80
|
-
|
|
81
|
-
>>> solara.server.server.wait_ready("http://localhost:8888")
|
|
82
|
-
...
|
|
83
|
-
|
|
84
|
-
"""
|
|
85
|
-
t0 = time.time()
|
|
86
|
-
while True:
|
|
87
|
-
try:
|
|
88
|
-
r = requests.get(url + "/readyz")
|
|
89
|
-
if r.status_code == 200:
|
|
90
|
-
return
|
|
91
|
-
except Exception:
|
|
92
|
-
pass
|
|
93
|
-
time.sleep(0.1)
|
|
94
|
-
if time.time() - t0 > timeout:
|
|
95
|
-
raise TimeoutError(f"Timeout waiting for {url}")
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def is_ready(url) -> bool:
|
|
99
|
-
"""Returns whether a solara server at root url is ready.
|
|
100
|
-
|
|
101
|
-
This uses the /readyz endpoint to check if the server is ready.
|
|
102
|
-
|
|
103
|
-
>>> solara.server.server.is_ready("http://localhost:8888")
|
|
104
|
-
True
|
|
105
|
-
|
|
106
|
-
"""
|
|
107
|
-
try:
|
|
108
|
-
r = requests.get(url + "/readyz")
|
|
109
|
-
if r.status_code == 200:
|
|
110
|
-
return True
|
|
111
|
-
except Exception:
|
|
112
|
-
pass
|
|
113
|
-
return False
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
async def app_loop(ws: websocket.WebsocketWrapper, cookies: Dict[str, str], headers, session_id: str, kernel_id: str, page_id: str, user: dict = None):
|
|
117
|
-
context = initialize_virtual_kernel(session_id, kernel_id, ws)
|
|
118
|
-
if context is None:
|
|
119
|
-
logging.warning("invalid kernel id: %r", kernel_id)
|
|
120
|
-
# to avoid very fast reconnects (we are in a thread anyway)
|
|
121
|
-
time.sleep(0.5)
|
|
122
|
-
return
|
|
123
|
-
|
|
124
|
-
if settings.main.tracer:
|
|
125
|
-
import viztracer
|
|
126
|
-
|
|
127
|
-
output_file = f"viztracer-{page_id}.html"
|
|
128
|
-
run_context = viztracer.VizTracer(output_file=output_file, max_stack_depth=10)
|
|
129
|
-
logger.warning(f"Running with tracer: {output_file}")
|
|
130
|
-
else:
|
|
131
|
-
run_context = solara.util.nullcontext()
|
|
132
|
-
|
|
133
|
-
kernel = context.kernel
|
|
134
|
-
try:
|
|
135
|
-
context.page_connect(page_id)
|
|
136
|
-
with run_context, context:
|
|
137
|
-
if user:
|
|
138
|
-
from solara_enterprise.auth import user as solara_user
|
|
139
|
-
|
|
140
|
-
solara_user.set(user)
|
|
141
|
-
|
|
142
|
-
solara_cookies.set(cookies) # type: ignore
|
|
143
|
-
solara_headers.set(headers) # type: ignore
|
|
144
|
-
|
|
145
|
-
while True:
|
|
146
|
-
if settings.main.timing:
|
|
147
|
-
widgets_ids = set(patch.widgets)
|
|
148
|
-
try:
|
|
149
|
-
message = await ws.receive()
|
|
150
|
-
except websocket.WebSocketDisconnect:
|
|
151
|
-
try:
|
|
152
|
-
context.kernel.session.websockets.remove(ws)
|
|
153
|
-
except KeyError:
|
|
154
|
-
pass
|
|
155
|
-
logger.debug("Disconnected")
|
|
156
|
-
break
|
|
157
|
-
t0 = time.time()
|
|
158
|
-
if isinstance(message, str):
|
|
159
|
-
msg = json.loads(message)
|
|
160
|
-
else:
|
|
161
|
-
msg = deserialize_binary_message(message)
|
|
162
|
-
t1 = time.time()
|
|
163
|
-
if not process_kernel_messages(kernel, msg):
|
|
164
|
-
# if we shut down the kernel, we do not keep the page session alive
|
|
165
|
-
context.close()
|
|
166
|
-
return
|
|
167
|
-
t2 = time.time()
|
|
168
|
-
if settings.main.timing:
|
|
169
|
-
widgets_ids_after = set(patch.widgets)
|
|
170
|
-
created_widgets_count = len(widgets_ids_after - widgets_ids)
|
|
171
|
-
close_widgets_count = len(widgets_ids - widgets_ids_after)
|
|
172
|
-
print( # noqa: T201
|
|
173
|
-
f"timing: total={t2-t0:.3f}s, deserialize={t1-t0:.3f}s, kernel={t2-t1:.3f}s"
|
|
174
|
-
f" widget: created: {created_widgets_count} closed: {close_widgets_count}"
|
|
175
|
-
)
|
|
176
|
-
finally:
|
|
177
|
-
context.page_disconnect(page_id)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
def process_kernel_messages(kernel: Kernel, msg: Dict) -> bool:
|
|
181
|
-
session = kernel.session
|
|
182
|
-
assert session is not None
|
|
183
|
-
comm_manager = kernel.comm_manager
|
|
184
|
-
|
|
185
|
-
def send_status(status, parent):
|
|
186
|
-
session.send(
|
|
187
|
-
kernel.iopub_socket,
|
|
188
|
-
"status",
|
|
189
|
-
{"execution_state": status},
|
|
190
|
-
parent=parent,
|
|
191
|
-
ident=None,
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
@contextlib.contextmanager
|
|
195
|
-
def busy_idle(parent):
|
|
196
|
-
send_status("busy", parent=parent)
|
|
197
|
-
try:
|
|
198
|
-
yield
|
|
199
|
-
finally:
|
|
200
|
-
send_status("idle", parent=parent)
|
|
201
|
-
|
|
202
|
-
msg_type = msg["header"]["msg_type"]
|
|
203
|
-
|
|
204
|
-
if ipykernel_major < 6:
|
|
205
|
-
# the channel argument was added in 6.0
|
|
206
|
-
kernel.set_parent(None, msg)
|
|
207
|
-
else:
|
|
208
|
-
kernel.set_parent(None, msg, msg["channel"])
|
|
209
|
-
if msg_type == "kernel_info_request":
|
|
210
|
-
content = {
|
|
211
|
-
"status": "ok",
|
|
212
|
-
"protocol_version": "5.3",
|
|
213
|
-
"implementation": Kernel.implementation,
|
|
214
|
-
"implementation_version": Kernel.implementation_version,
|
|
215
|
-
"language_info": {},
|
|
216
|
-
"banner": Kernel.banner,
|
|
217
|
-
"help_links": [],
|
|
218
|
-
}
|
|
219
|
-
with busy_idle(msg["header"]):
|
|
220
|
-
msg = kernel.session.send(kernel.shell_stream, "kernel_info_reply", content, msg["header"], None)
|
|
221
|
-
return True
|
|
222
|
-
elif msg_type in ["comm_open", "comm_msg", "comm_close"]:
|
|
223
|
-
with busy_idle(msg["header"]):
|
|
224
|
-
getattr(comm_manager, msg_type)(kernel.shell_stream, None, msg)
|
|
225
|
-
return True
|
|
226
|
-
elif msg_type in ["comm_info_request"]:
|
|
227
|
-
content = msg["content"]
|
|
228
|
-
target_name = msg.get("target_name", None)
|
|
229
|
-
|
|
230
|
-
comms = {
|
|
231
|
-
k: dict(target_name=v.target_name) for (k, v) in comm_manager.comms.items() if v.target_name == target_name or target_name is None # type: ignore
|
|
232
|
-
}
|
|
233
|
-
reply_content = dict(comms=comms, status="ok")
|
|
234
|
-
with busy_idle(msg["header"]):
|
|
235
|
-
msg = session.send(
|
|
236
|
-
kernel.shell_stream,
|
|
237
|
-
"comm_info_reply",
|
|
238
|
-
reply_content,
|
|
239
|
-
msg["header"],
|
|
240
|
-
None,
|
|
241
|
-
)
|
|
242
|
-
return True
|
|
243
|
-
elif msg_type == "shutdown_request":
|
|
244
|
-
send_status("dead", parent=msg["header"])
|
|
245
|
-
return False
|
|
246
|
-
else:
|
|
247
|
-
logger.error("Unsupported msg with msg_type %r", msg_type)
|
|
248
|
-
return False
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
def read_root(path: str, root_path: str = "", render_kwargs={}, use_nbextensions=True, ssg_data=None) -> Optional[str]:
|
|
252
|
-
if settings.ssg.enabled and ssg_data is None:
|
|
253
|
-
# simply return the pre-rendered html
|
|
254
|
-
from solara_enterprise import ssg
|
|
255
|
-
|
|
256
|
-
content = ssg.ssg_content(path)
|
|
257
|
-
if content is not None:
|
|
258
|
-
return content
|
|
259
|
-
|
|
260
|
-
default_app = app.apps["__default__"]
|
|
261
|
-
routes = default_app.routes
|
|
262
|
-
router = solara.routing.Router(path, routes)
|
|
263
|
-
if not router.possible_match:
|
|
264
|
-
return None
|
|
265
|
-
if use_nbextensions:
|
|
266
|
-
nbextensions, nbextensions_hashes = get_nbextensions()
|
|
267
|
-
else:
|
|
268
|
-
nbextensions, nbextensions_hashes = [], {}
|
|
269
|
-
|
|
270
|
-
from markupsafe import Markup
|
|
271
|
-
|
|
272
|
-
def resolve_static_path(path: str) -> Path:
|
|
273
|
-
# this solve a similar problem as the starlette and flask endpoints
|
|
274
|
-
# maybe this can be common code for all of them.
|
|
275
|
-
if path.startswith("/static/public/"):
|
|
276
|
-
directories = [default_app.directory.parent / "public"]
|
|
277
|
-
filename = path[len("/static/public/") :]
|
|
278
|
-
elif path.startswith("/static/assets/"):
|
|
279
|
-
directories = [default_app.directory.parent / "assets", solara_static.parent / "assets"]
|
|
280
|
-
filename = path[len("/static/assets/") :]
|
|
281
|
-
elif path.startswith("/static/"):
|
|
282
|
-
directories = [solara_static.parent / "static"]
|
|
283
|
-
filename = path[len("/static/") :]
|
|
284
|
-
else:
|
|
285
|
-
raise ValueError(f"invalid static path: {path}")
|
|
286
|
-
for directory in directories:
|
|
287
|
-
full_path = directory / filename
|
|
288
|
-
if full_path.exists():
|
|
289
|
-
return full_path
|
|
290
|
-
raise ValueError(f"static path not found: {filename} (path={path}), looked in {directories}")
|
|
291
|
-
|
|
292
|
-
def include_css(path: str) -> Markup:
|
|
293
|
-
filepath = resolve_static_path(path)
|
|
294
|
-
content, hash = solara.util.get_file_hash(filepath)
|
|
295
|
-
url = f"{root_path}{path}?v={hash}"
|
|
296
|
-
# when < 10k we embed, also when we use a url, it can be relative, which can break the url
|
|
297
|
-
embed = len(content) < 1024 * 10 and b"url" not in content
|
|
298
|
-
# Always embed the jupyterlab theme CSS to make theme switching possible (see solara.html.j2 template)
|
|
299
|
-
# TODO: Prevent browser from caching the theme CSS files
|
|
300
|
-
if path.endswith("theme-dark.css") or path.endswith("theme-light.css"):
|
|
301
|
-
content_utf8 = content.decode("utf-8")
|
|
302
|
-
code = content_utf8
|
|
303
|
-
elif embed:
|
|
304
|
-
content_utf8 = content.decode("utf-8")
|
|
305
|
-
code = f'<style class="solara-template-css">/*\npath={path}\n*/\n{content_utf8}</style>'
|
|
306
|
-
else:
|
|
307
|
-
code = f'<link rel="stylesheet" type="text/css" href="{url}" class="solara-template-css">'
|
|
308
|
-
return Markup(code)
|
|
309
|
-
|
|
310
|
-
def include_js(path: str, module=False) -> Markup:
|
|
311
|
-
filepath = resolve_static_path(path)
|
|
312
|
-
content, hash = solara.util.get_file_hash(filepath)
|
|
313
|
-
content_utf8 = content.decode("utf-8")
|
|
314
|
-
url = f"{root_path}{path}?v={hash}"
|
|
315
|
-
# when < 10k we embed, but if we use currentScript, it can break things
|
|
316
|
-
embed = len(content) < 1024 * 10 and b"currentScript" not in content
|
|
317
|
-
if embed:
|
|
318
|
-
if module:
|
|
319
|
-
code = f'<script type="module">/*\npath={path}\n*/{content_utf8}</script>'
|
|
320
|
-
else:
|
|
321
|
-
code = f"<script>/*\npath={path}\n*/{content_utf8}</script>"
|
|
322
|
-
else:
|
|
323
|
-
if module:
|
|
324
|
-
code = f'<script type="module" src="{url}"></script>'
|
|
325
|
-
else:
|
|
326
|
-
code = f'<script src="{url}"></script>'
|
|
327
|
-
return Markup(code)
|
|
328
|
-
|
|
329
|
-
resources = {
|
|
330
|
-
"theme": "light",
|
|
331
|
-
"nbextensions": nbextensions,
|
|
332
|
-
"nbextensions_hashes": nbextensions_hashes,
|
|
333
|
-
"include_css": include_css,
|
|
334
|
-
"include_js": include_js,
|
|
335
|
-
}
|
|
336
|
-
template: jinja2.Template = get_jinja_env(app_name="__default__").get_template(template_name)
|
|
337
|
-
pre_rendered_html = ""
|
|
338
|
-
pre_rendered_css = ""
|
|
339
|
-
pre_rendered_metas = ""
|
|
340
|
-
title = "Solara ☀️"
|
|
341
|
-
if ssg_data is not None:
|
|
342
|
-
pre_rendered_html = ssg_data["html"]
|
|
343
|
-
pre_rendered_css = "\n".join(ssg_data["styles"])
|
|
344
|
-
pre_rendered_metas = "\n ".join(ssg_data["metas"])
|
|
345
|
-
title = ssg_data["title"]
|
|
346
|
-
|
|
347
|
-
if solara.settings.assets.proxy:
|
|
348
|
-
# solara acts as a proxy
|
|
349
|
-
cdn = f"{root_path}/_solara/cdn"
|
|
350
|
-
else:
|
|
351
|
-
cdn = solara.settings.assets.cdn
|
|
352
|
-
|
|
353
|
-
render_settings = {
|
|
354
|
-
"title": title,
|
|
355
|
-
"path": path,
|
|
356
|
-
"root_path": root_path,
|
|
357
|
-
"resources": resources,
|
|
358
|
-
"theme": settings.theme.dict(),
|
|
359
|
-
"production": settings.main.mode == "production",
|
|
360
|
-
"pre_rendered_html": pre_rendered_html,
|
|
361
|
-
"pre_rendered_css": pre_rendered_css,
|
|
362
|
-
"pre_rendered_metas": pre_rendered_metas,
|
|
363
|
-
"assets": settings.assets.dict(),
|
|
364
|
-
"cdn": cdn,
|
|
365
|
-
"ipywidget_major_version": ipywidgets_major,
|
|
366
|
-
"solara_version": solara.__version__,
|
|
367
|
-
"platform": settings.main.platform,
|
|
368
|
-
"vue3": vue3,
|
|
369
|
-
"perform_check": settings.main.mode != "production" and solara.checks.should_perform_solara_check(),
|
|
370
|
-
**render_kwargs,
|
|
371
|
-
}
|
|
372
|
-
response = template.render(**render_settings)
|
|
373
|
-
return response
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
def find_prefixed_directory(path):
|
|
377
|
-
prefixes = [sys.prefix, os.path.expanduser("~/.local")]
|
|
378
|
-
for prefix in prefixes:
|
|
379
|
-
directory = f"{prefix}{path}"
|
|
380
|
-
if Path(directory).exists():
|
|
381
|
-
return directory
|
|
382
|
-
else:
|
|
383
|
-
raise RuntimeError(f"{path} not found at prefixes: {prefixes}")
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
@solara.memoize(storage=cache_memory)
|
|
387
|
-
def get_nbextensions_directories() -> List[Path]:
|
|
388
|
-
from jupyter_core.paths import jupyter_path
|
|
389
|
-
|
|
390
|
-
all_nb_directories = [Path(k) for k in jupyter_path("nbextensions")]
|
|
391
|
-
# FIXME: remove IPython nbextensions path after a migration period
|
|
392
|
-
try:
|
|
393
|
-
from IPython.paths import get_ipython_dir
|
|
394
|
-
except ImportError:
|
|
395
|
-
pass
|
|
396
|
-
else:
|
|
397
|
-
all_nb_directories.append(Path(get_ipython_dir()) / "nbextensions")
|
|
398
|
-
return [Path(k) for k in all_nb_directories]
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
@solara.memoize(storage=cache_memory)
|
|
402
|
-
def get_nbextensions() -> Tuple[List[str], Dict[str, Optional[str]]]:
|
|
403
|
-
from jupyter_core.paths import jupyter_config_path
|
|
404
|
-
|
|
405
|
-
paths = [Path(p) / "nbconfig" for p in jupyter_config_path()]
|
|
406
|
-
|
|
407
|
-
load_extensions = jupytertools.get_config(paths, "notebook")["load_extensions"]
|
|
408
|
-
|
|
409
|
-
def exists(name):
|
|
410
|
-
for directory in nbextensions_directories:
|
|
411
|
-
if (directory / (name + ".js")).exists():
|
|
412
|
-
return True
|
|
413
|
-
logger.info(f"nbextension {name} not found")
|
|
414
|
-
return False
|
|
415
|
-
|
|
416
|
-
def hash_extension(name):
|
|
417
|
-
if sys.version_info[:2] < (3, 9):
|
|
418
|
-
# usedforsecurity is only available in Python 3.9+
|
|
419
|
-
h = hashlib.new("md5")
|
|
420
|
-
else:
|
|
421
|
-
h = hashlib.new("md5", usedforsecurity=False) # type: ignore
|
|
422
|
-
|
|
423
|
-
for directory in nbextensions_directories:
|
|
424
|
-
if (directory / (name + ".js")).exists():
|
|
425
|
-
for file in directory.glob("**/*.*"):
|
|
426
|
-
data = file.read_bytes()
|
|
427
|
-
h.update(data)
|
|
428
|
-
|
|
429
|
-
return h.hexdigest()
|
|
430
|
-
|
|
431
|
-
nbextensions: List[str] = [name for name, enabled in load_extensions.items() if enabled and (name not in nbextensions_ignorelist) and exists(name)]
|
|
432
|
-
nbextensions_hashes = {name: hash_extension(name) for name in nbextensions}
|
|
433
|
-
return nbextensions, nbextensions_hashes
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
nbextensions_directories = get_nbextensions_directories()
|
|
437
|
-
solara_static = Path(__file__).parent / "static"
|