solara-ui 1.45.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prefix/etc/jupyter/jupyter_notebook_config.d/solara.json +7 -0
- prefix/etc/jupyter/jupyter_server_config.d/solara.json +7 -0
- solara/__init__.py +124 -0
- solara/__main__.py +765 -0
- solara/_stores.py +297 -0
- solara/alias.py +6 -0
- solara/autorouting.py +555 -0
- solara/cache.py +305 -0
- solara/checks.html +71 -0
- solara/checks.py +227 -0
- solara/comm.py +28 -0
- solara/components/__init__.py +77 -0
- solara/components/alert.py +155 -0
- solara/components/applayout.py +397 -0
- solara/components/button.py +85 -0
- solara/components/card.py +87 -0
- solara/components/checkbox.py +50 -0
- solara/components/code_highlight_css.py +11 -0
- solara/components/code_highlight_css.vue +63 -0
- solara/components/columns.py +159 -0
- solara/components/component_vue.py +134 -0
- solara/components/cross_filter.py +335 -0
- solara/components/dataframe.py +546 -0
- solara/components/datatable.py +214 -0
- solara/components/datatable.vue +175 -0
- solara/components/details.py +56 -0
- solara/components/download.vue +35 -0
- solara/components/echarts.py +86 -0
- solara/components/echarts.vue +139 -0
- solara/components/figure_altair.py +39 -0
- solara/components/file_browser.py +181 -0
- solara/components/file_download.py +199 -0
- solara/components/file_drop.py +159 -0
- solara/components/file_drop.vue +83 -0
- solara/components/file_list_widget.vue +78 -0
- solara/components/head.py +27 -0
- solara/components/head_tag.py +49 -0
- solara/components/head_tag.vue +60 -0
- solara/components/image.py +173 -0
- solara/components/input.py +456 -0
- solara/components/input_text_area.py +86 -0
- solara/components/link.py +55 -0
- solara/components/markdown.py +441 -0
- solara/components/markdown_editor.py +33 -0
- solara/components/markdown_editor.vue +359 -0
- solara/components/matplotlib.py +74 -0
- solara/components/meta.py +47 -0
- solara/components/misc.py +333 -0
- solara/components/pivot_table.py +258 -0
- solara/components/pivot_table.vue +158 -0
- solara/components/progress.py +47 -0
- solara/components/select.py +182 -0
- solara/components/select.vue +27 -0
- solara/components/slider.py +442 -0
- solara/components/slider_date.vue +56 -0
- solara/components/spinner-solara.vue +105 -0
- solara/components/spinner.py +45 -0
- solara/components/sql_code.py +41 -0
- solara/components/sql_code.vue +125 -0
- solara/components/style.py +105 -0
- solara/components/switch.py +71 -0
- solara/components/tab_navigation.py +37 -0
- solara/components/title.py +90 -0
- solara/components/title.vue +38 -0
- solara/components/togglebuttons.py +200 -0
- solara/components/tooltip.py +61 -0
- solara/core.py +42 -0
- solara/datatypes.py +143 -0
- solara/express.py +241 -0
- solara/hooks/__init__.py +4 -0
- solara/hooks/dataframe.py +99 -0
- solara/hooks/misc.py +263 -0
- solara/hooks/use_reactive.py +151 -0
- solara/hooks/use_thread.py +129 -0
- solara/kitchensink.py +8 -0
- solara/lab/__init__.py +34 -0
- solara/lab/components/__init__.py +7 -0
- solara/lab/components/chat.py +215 -0
- solara/lab/components/confirmation_dialog.py +163 -0
- solara/lab/components/cross_filter.py +7 -0
- solara/lab/components/input_date.py +339 -0
- solara/lab/components/input_time.py +133 -0
- solara/lab/components/menu.py +181 -0
- solara/lab/components/menu.vue +38 -0
- solara/lab/components/tabs.py +274 -0
- solara/lab/components/theming.py +98 -0
- solara/lab/components/theming.vue +72 -0
- solara/lab/hooks/__init__.py +0 -0
- solara/lab/hooks/dataframe.py +2 -0
- solara/lab/toestand.py +3 -0
- solara/lab/utils/__init__.py +2 -0
- solara/lab/utils/cookies.py +5 -0
- solara/lab/utils/dataframe.py +165 -0
- solara/lab/utils/headers.py +5 -0
- solara/layout.py +44 -0
- solara/lifecycle.py +46 -0
- solara/minisettings.py +141 -0
- solara/py.typed +0 -0
- solara/reactive.py +99 -0
- solara/routing.py +268 -0
- solara/scope/__init__.py +88 -0
- solara/scope/types.py +55 -0
- solara/server/__init__.py +0 -0
- solara/server/app.py +527 -0
- solara/server/assets/custom.css +1 -0
- solara/server/assets/custom.js +1 -0
- solara/server/assets/favicon.png +0 -0
- solara/server/assets/favicon.svg +5 -0
- solara/server/assets/style.css +1681 -0
- solara/server/assets/theme-dark.css +437 -0
- solara/server/assets/theme-light.css +420 -0
- solara/server/assets/theme.js +3 -0
- solara/server/cdn_helper.py +91 -0
- solara/server/esm.py +71 -0
- solara/server/fastapi.py +5 -0
- solara/server/flask.py +297 -0
- solara/server/jupyter/__init__.py +2 -0
- solara/server/jupyter/cdn_handler.py +28 -0
- solara/server/jupyter/server_extension.py +40 -0
- solara/server/jupyter/solara.py +91 -0
- solara/server/jupytertools.py +46 -0
- solara/server/kernel.py +388 -0
- solara/server/kernel_context.py +467 -0
- solara/server/patch.py +564 -0
- solara/server/pyinstaller/__init__.py +9 -0
- solara/server/pyinstaller/hook-ipyreact.py +5 -0
- solara/server/pyinstaller/hook-ipyvuetify.py +5 -0
- solara/server/pyinstaller/hook-solara.py +9 -0
- solara/server/qt.py +113 -0
- solara/server/reload.py +251 -0
- solara/server/server.py +484 -0
- solara/server/settings.py +249 -0
- solara/server/shell.py +269 -0
- solara/server/starlette.py +770 -0
- solara/server/static/ansi.js +270 -0
- solara/server/static/highlight-dark.css +82 -0
- solara/server/static/highlight.css +43 -0
- solara/server/static/main-vuetify.js +272 -0
- solara/server/static/main.js +163 -0
- solara/server/static/solara_bootstrap.py +129 -0
- solara/server/static/sun.svg +23 -0
- solara/server/static/webworker.js +42 -0
- solara/server/telemetry.py +212 -0
- solara/server/templates/index.html.j2 +1 -0
- solara/server/templates/loader-plain.css +11 -0
- solara/server/templates/loader-plain.html +20 -0
- solara/server/templates/loader-solara.css +111 -0
- solara/server/templates/loader-solara.html +40 -0
- solara/server/templates/plain.html +82 -0
- solara/server/templates/solara.html.j2 +486 -0
- solara/server/threaded.py +84 -0
- solara/server/utils.py +44 -0
- solara/server/websocket.py +45 -0
- solara/settings.py +86 -0
- solara/tasks.py +893 -0
- solara/template/button.py +16 -0
- solara/template/markdown.py +42 -0
- solara/template/portal/.flake8 +6 -0
- solara/template/portal/.pre-commit-config.yaml +28 -0
- solara/template/portal/LICENSE +21 -0
- solara/template/portal/Procfile +7 -0
- solara/template/portal/mypy.ini +3 -0
- solara/template/portal/pyproject.toml +26 -0
- solara/template/portal/solara_portal/__init__.py +4 -0
- solara/template/portal/solara_portal/components/__init__.py +2 -0
- solara/template/portal/solara_portal/components/article.py +28 -0
- solara/template/portal/solara_portal/components/data.py +28 -0
- solara/template/portal/solara_portal/components/header.py +6 -0
- solara/template/portal/solara_portal/components/layout.py +6 -0
- solara/template/portal/solara_portal/content/articles/equis-in-vidi.md +85 -0
- solara/template/portal/solara_portal/content/articles/substiterat-vati.md +70 -0
- solara/template/portal/solara_portal/data.py +60 -0
- solara/template/portal/solara_portal/pages/__init__.py +67 -0
- solara/template/portal/solara_portal/pages/article/__init__.py +26 -0
- solara/template/portal/solara_portal/pages/tabular.py +29 -0
- solara/template/portal/solara_portal/pages/viz/__init__.py +70 -0
- solara/template/portal/solara_portal/pages/viz/overview.py +14 -0
- solara/test/__init__.py +0 -0
- solara/test/pytest_plugin.py +783 -0
- solara/toestand.py +998 -0
- solara/util.py +348 -0
- solara/validate_hooks.py +258 -0
- solara/website/__init__.py +0 -0
- solara/website/assets/custom.css +444 -0
- solara/website/assets/images/logo-small.png +0 -0
- solara/website/assets/images/logo.svg +17 -0
- solara/website/assets/images/logo_white.svg +50 -0
- solara/website/assets/theme.js +8 -0
- solara/website/components/__init__.py +5 -0
- solara/website/components/algolia.py +6 -0
- solara/website/components/algolia.vue +24 -0
- solara/website/components/algolia_api.vue +202 -0
- solara/website/components/breadcrumbs.py +28 -0
- solara/website/components/contact.py +144 -0
- solara/website/components/docs.py +143 -0
- solara/website/components/header.py +75 -0
- solara/website/components/mailchimp.py +12 -0
- solara/website/components/mailchimp.vue +47 -0
- solara/website/components/markdown.py +99 -0
- solara/website/components/markdown_nav.vue +34 -0
- solara/website/components/notebook.py +171 -0
- solara/website/components/sidebar.py +105 -0
- solara/website/pages/__init__.py +370 -0
- solara/website/pages/about/__init__.py +9 -0
- solara/website/pages/about/about.md +3 -0
- solara/website/pages/apps/__init__.py +16 -0
- solara/website/pages/apps/authorization/__init__.py +119 -0
- solara/website/pages/apps/authorization/admin.py +12 -0
- solara/website/pages/apps/authorization/users.py +12 -0
- solara/website/pages/apps/jupyter-dashboard-1.py +116 -0
- solara/website/pages/apps/layout-demo.py +40 -0
- solara/website/pages/apps/multipage/__init__.py +38 -0
- solara/website/pages/apps/multipage/page1.py +26 -0
- solara/website/pages/apps/multipage/page2.py +34 -0
- solara/website/pages/apps/scatter.py +136 -0
- solara/website/pages/apps/scrolling.py +63 -0
- solara/website/pages/apps/tutorial-streamlit.py +18 -0
- solara/website/pages/careers/__init__.py +27 -0
- solara/website/pages/changelog/__init__.py +10 -0
- solara/website/pages/changelog/changelog.md +372 -0
- solara/website/pages/contact/__init__.py +34 -0
- solara/website/pages/doc_use_download.py +85 -0
- solara/website/pages/documentation/__init__.py +90 -0
- solara/website/pages/documentation/advanced/__init__.py +9 -0
- solara/website/pages/documentation/advanced/content/00-overview.md +1 -0
- solara/website/pages/documentation/advanced/content/10-howto/00-overview.md +6 -0
- solara/website/pages/documentation/advanced/content/10-howto/10-multipage.md +196 -0
- solara/website/pages/documentation/advanced/content/10-howto/20-layout.md +125 -0
- solara/website/pages/documentation/advanced/content/10-howto/30-testing.md +417 -0
- solara/website/pages/documentation/advanced/content/10-howto/31-debugging.md +69 -0
- solara/website/pages/documentation/advanced/content/10-howto/40-embed.md +50 -0
- solara/website/pages/documentation/advanced/content/10-howto/50-ipywidget_libraries.md +124 -0
- solara/website/pages/documentation/advanced/content/15-reference/00-overview.md +3 -0
- solara/website/pages/documentation/advanced/content/15-reference/40-static_files.md +31 -0
- solara/website/pages/documentation/advanced/content/15-reference/41-asset-files.md +72 -0
- solara/website/pages/documentation/advanced/content/15-reference/60-static-site-generation.md +59 -0
- solara/website/pages/documentation/advanced/content/15-reference/70-search.md +34 -0
- solara/website/pages/documentation/advanced/content/15-reference/80-reloading.md +34 -0
- solara/website/pages/documentation/advanced/content/15-reference/90-notebook-support.md +7 -0
- solara/website/pages/documentation/advanced/content/15-reference/95-caching.md +148 -0
- solara/website/pages/documentation/advanced/content/20-understanding/00-introduction.md +10 -0
- solara/website/pages/documentation/advanced/content/20-understanding/05-ipywidgets.md +35 -0
- solara/website/pages/documentation/advanced/content/20-understanding/06-ipyvuetify.md +42 -0
- solara/website/pages/documentation/advanced/content/20-understanding/10-reacton.md +28 -0
- solara/website/pages/documentation/advanced/content/20-understanding/12-reacton-basics.md +108 -0
- solara/website/pages/documentation/advanced/content/20-understanding/15-anatomy.md +23 -0
- solara/website/pages/documentation/advanced/content/20-understanding/17-rules-of-hooks.md +192 -0
- solara/website/pages/documentation/advanced/content/20-understanding/18-containers.md +166 -0
- solara/website/pages/documentation/advanced/content/20-understanding/20-solara.md +18 -0
- solara/website/pages/documentation/advanced/content/20-understanding/40-routing.md +256 -0
- solara/website/pages/documentation/advanced/content/20-understanding/50-solara-server.md +108 -0
- solara/website/pages/documentation/advanced/content/20-understanding/60-voila.md +12 -0
- solara/website/pages/documentation/advanced/content/30-enterprise/00-overview.md +7 -0
- solara/website/pages/documentation/advanced/content/30-enterprise/10-oauth.md +187 -0
- solara/website/pages/documentation/advanced/content/40-development/00-overview.md +0 -0
- solara/website/pages/documentation/advanced/content/40-development/01-contribute.md +45 -0
- solara/website/pages/documentation/advanced/content/40-development/10-setup.md +76 -0
- solara/website/pages/documentation/api/__init__.py +19 -0
- solara/website/pages/documentation/api/cross_filter/__init__.py +9 -0
- solara/website/pages/documentation/api/cross_filter/cross_filter_dataframe.py +22 -0
- solara/website/pages/documentation/api/cross_filter/cross_filter_report.py +20 -0
- solara/website/pages/documentation/api/cross_filter/cross_filter_select.py +20 -0
- solara/website/pages/documentation/api/cross_filter/cross_filter_slider.py +20 -0
- solara/website/pages/documentation/api/hooks/__init__.py +9 -0
- solara/website/pages/documentation/api/hooks/use_cross_filter.py +23 -0
- solara/website/pages/documentation/api/hooks/use_dark_effective.py +12 -0
- solara/website/pages/documentation/api/hooks/use_effect.md +43 -0
- solara/website/pages/documentation/api/hooks/use_effect.py +9 -0
- solara/website/pages/documentation/api/hooks/use_exception.py +31 -0
- solara/website/pages/documentation/api/hooks/use_memo.md +16 -0
- solara/website/pages/documentation/api/hooks/use_memo.py +9 -0
- solara/website/pages/documentation/api/hooks/use_previous.py +30 -0
- solara/website/pages/documentation/api/hooks/use_reactive.py +16 -0
- solara/website/pages/documentation/api/hooks/use_state.py +10 -0
- solara/website/pages/documentation/api/hooks/use_state_or_update.py +66 -0
- solara/website/pages/documentation/api/hooks/use_thread.md +64 -0
- solara/website/pages/documentation/api/hooks/use_thread.py +42 -0
- solara/website/pages/documentation/api/hooks/use_trait_observe.py +12 -0
- solara/website/pages/documentation/api/routing/__init__.py +9 -0
- solara/website/pages/documentation/api/routing/generate_routes.py +10 -0
- solara/website/pages/documentation/api/routing/generate_routes_directory.py +10 -0
- solara/website/pages/documentation/api/routing/resolve_path.py +35 -0
- solara/website/pages/documentation/api/routing/route.py +29 -0
- solara/website/pages/documentation/api/routing/use_route.py +76 -0
- solara/website/pages/documentation/api/routing/use_router.py +16 -0
- solara/website/pages/documentation/api/utilities/__init__.py +9 -0
- solara/website/pages/documentation/api/utilities/component_vue.py +10 -0
- solara/website/pages/documentation/api/utilities/computed.py +16 -0
- solara/website/pages/documentation/api/utilities/display.py +16 -0
- solara/website/pages/documentation/api/utilities/get_kernel_id.py +16 -0
- solara/website/pages/documentation/api/utilities/get_session_id.py +16 -0
- solara/website/pages/documentation/api/utilities/memoize.py +35 -0
- solara/website/pages/documentation/api/utilities/on_kernel_start.py +44 -0
- solara/website/pages/documentation/api/utilities/reactive.py +16 -0
- solara/website/pages/documentation/api/utilities/widget.py +104 -0
- solara/website/pages/documentation/components/__init__.py +12 -0
- solara/website/pages/documentation/components/advanced/__init__.py +9 -0
- solara/website/pages/documentation/components/advanced/link.py +25 -0
- solara/website/pages/documentation/components/advanced/meta.py +17 -0
- solara/website/pages/documentation/components/advanced/style.py +43 -0
- solara/website/pages/documentation/components/common.py +9 -0
- solara/website/pages/documentation/components/data/__init__.py +9 -0
- solara/website/pages/documentation/components/data/dataframe.py +44 -0
- solara/website/pages/documentation/components/data/pivot_table.py +81 -0
- solara/website/pages/documentation/components/enterprise/__init__.py +9 -0
- solara/website/pages/documentation/components/enterprise/avatar.py +24 -0
- solara/website/pages/documentation/components/enterprise/avatar_menu.py +25 -0
- solara/website/pages/documentation/components/input/__init__.py +9 -0
- solara/website/pages/documentation/components/input/button.py +23 -0
- solara/website/pages/documentation/components/input/checkbox.py +10 -0
- solara/website/pages/documentation/components/input/file_browser.py +30 -0
- solara/website/pages/documentation/components/input/file_drop.py +76 -0
- solara/website/pages/documentation/components/input/input.py +43 -0
- solara/website/pages/documentation/components/input/select.py +22 -0
- solara/website/pages/documentation/components/input/slider.py +29 -0
- solara/website/pages/documentation/components/input/switch.py +10 -0
- solara/website/pages/documentation/components/input/togglebuttons.py +21 -0
- solara/website/pages/documentation/components/lab/__init__.py +9 -0
- solara/website/pages/documentation/components/lab/chat.py +109 -0
- solara/website/pages/documentation/components/lab/confirmation_dialog.py +55 -0
- solara/website/pages/documentation/components/lab/cookies_headers.py +48 -0
- solara/website/pages/documentation/components/lab/input_date.py +20 -0
- solara/website/pages/documentation/components/lab/input_time.py +15 -0
- solara/website/pages/documentation/components/lab/menu.py +22 -0
- solara/website/pages/documentation/components/lab/tab.py +25 -0
- solara/website/pages/documentation/components/lab/tabs.py +45 -0
- solara/website/pages/documentation/components/lab/task.py +11 -0
- solara/website/pages/documentation/components/lab/theming.py +74 -0
- solara/website/pages/documentation/components/lab/use_task.py +11 -0
- solara/website/pages/documentation/components/layout/__init__.py +9 -0
- solara/website/pages/documentation/components/layout/app_bar.py +16 -0
- solara/website/pages/documentation/components/layout/app_bar_title.py +16 -0
- solara/website/pages/documentation/components/layout/app_layout.py +24 -0
- solara/website/pages/documentation/components/layout/card.py +15 -0
- solara/website/pages/documentation/components/layout/card_actions.py +16 -0
- solara/website/pages/documentation/components/layout/column.py +30 -0
- solara/website/pages/documentation/components/layout/columns.py +27 -0
- solara/website/pages/documentation/components/layout/columns_responsive.py +66 -0
- solara/website/pages/documentation/components/layout/details.py +13 -0
- solara/website/pages/documentation/components/layout/griddraggable.py +62 -0
- solara/website/pages/documentation/components/layout/gridfixed.py +19 -0
- solara/website/pages/documentation/components/layout/hbox.py +18 -0
- solara/website/pages/documentation/components/layout/row.py +30 -0
- solara/website/pages/documentation/components/layout/sidebar.py +24 -0
- solara/website/pages/documentation/components/layout/vbox.py +19 -0
- solara/website/pages/documentation/components/output/__init__.py +9 -0
- solara/website/pages/documentation/components/output/file_download.py +11 -0
- solara/website/pages/documentation/components/output/html.py +19 -0
- solara/website/pages/documentation/components/output/image.py +11 -0
- solara/website/pages/documentation/components/output/markdown.py +57 -0
- solara/website/pages/documentation/components/output/markdown_editor.py +51 -0
- solara/website/pages/documentation/components/output/sql_code.py +83 -0
- solara/website/pages/documentation/components/output/tooltip.py +11 -0
- solara/website/pages/documentation/components/page/__init__.py +9 -0
- solara/website/pages/documentation/components/page/head.py +15 -0
- solara/website/pages/documentation/components/page/title.py +25 -0
- solara/website/pages/documentation/components/status/__init__.py +9 -0
- solara/website/pages/documentation/components/status/error.py +39 -0
- solara/website/pages/documentation/components/status/info.py +39 -0
- solara/website/pages/documentation/components/status/progress.py +10 -0
- solara/website/pages/documentation/components/status/spinner.py +11 -0
- solara/website/pages/documentation/components/status/success.py +40 -0
- solara/website/pages/documentation/components/status/warning.py +47 -0
- solara/website/pages/documentation/components/viz/__init__.py +9 -0
- solara/website/pages/documentation/components/viz/altair.py +42 -0
- solara/website/pages/documentation/components/viz/echarts.py +77 -0
- solara/website/pages/documentation/components/viz/matplotlib.py +30 -0
- solara/website/pages/documentation/components/viz/plotly.py +63 -0
- solara/website/pages/documentation/components/viz/plotly_express.py +41 -0
- solara/website/pages/documentation/examples/__init__.py +54 -0
- solara/website/pages/documentation/examples/ai/__init__.py +11 -0
- solara/website/pages/documentation/examples/ai/chatbot.py +113 -0
- solara/website/pages/documentation/examples/ai/tokenizer.py +107 -0
- solara/website/pages/documentation/examples/basics/__init__.py +10 -0
- solara/website/pages/documentation/examples/basics/sine.py +28 -0
- solara/website/pages/documentation/examples/fullscreen/__init__.py +10 -0
- solara/website/pages/documentation/examples/fullscreen/authorization.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/layout_demo.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/multipage.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/scatter.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/scrolling.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/tutorial_streamlit.py +3 -0
- solara/website/pages/documentation/examples/general/__init__.py +10 -0
- solara/website/pages/documentation/examples/general/custom_storage.py +70 -0
- solara/website/pages/documentation/examples/general/deploy_model.py +115 -0
- solara/website/pages/documentation/examples/general/live_update.py +32 -0
- solara/website/pages/documentation/examples/general/login_oauth.py +81 -0
- solara/website/pages/documentation/examples/general/mycard.vue +58 -0
- solara/website/pages/documentation/examples/general/pokemon_search.py +51 -0
- solara/website/pages/documentation/examples/general/vue_component.py +50 -0
- solara/website/pages/documentation/examples/ipycanvas.py +49 -0
- solara/website/pages/documentation/examples/libraries/__init__.py +10 -0
- solara/website/pages/documentation/examples/libraries/altair.py +65 -0
- solara/website/pages/documentation/examples/libraries/bqplot.py +39 -0
- solara/website/pages/documentation/examples/libraries/ipyleaflet.py +33 -0
- solara/website/pages/documentation/examples/libraries/ipyleaflet_advanced.py +66 -0
- solara/website/pages/documentation/examples/utilities/__init__.py +10 -0
- solara/website/pages/documentation/examples/utilities/calculator.py +157 -0
- solara/website/pages/documentation/examples/utilities/countdown_timer.py +62 -0
- solara/website/pages/documentation/examples/utilities/todo.py +196 -0
- solara/website/pages/documentation/examples/visualization/__init__.py +6 -0
- solara/website/pages/documentation/examples/visualization/annotator.py +67 -0
- solara/website/pages/documentation/examples/visualization/linked_views.py +81 -0
- solara/website/pages/documentation/examples/visualization/plotly.py +44 -0
- solara/website/pages/documentation/faq/__init__.py +12 -0
- solara/website/pages/documentation/faq/content/99-faq.md +112 -0
- solara/website/pages/documentation/getting_started/__init__.py +9 -0
- solara/website/pages/documentation/getting_started/content/00-quickstart.md +107 -0
- solara/website/pages/documentation/getting_started/content/01-introduction.md +125 -0
- solara/website/pages/documentation/getting_started/content/02-installing.md +134 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/00-overview.md +14 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/10_data_science.py +13 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/20-web-app.md +89 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/30-ipywidgets.md +124 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/40-streamlit.md +146 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/50-dash.md +144 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/60-jupyter-dashboard-part1.py +65 -0
- 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 +445 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/_jupyter_dashboard_1.ipynb +1021 -0
- solara/website/pages/documentation/getting_started/content/05-fundamentals/00-overview.md +11 -0
- solara/website/pages/documentation/getting_started/content/05-fundamentals/10-components.md +228 -0
- solara/website/pages/documentation/getting_started/content/05-fundamentals/50-state-management.md +278 -0
- solara/website/pages/documentation/getting_started/content/07-deploying/00-overview.md +7 -0
- solara/website/pages/documentation/getting_started/content/07-deploying/10-self-hosted.md +305 -0
- solara/website/pages/documentation/getting_started/content/07-deploying/20-cloud-hosted.md +80 -0
- solara/website/pages/documentation/getting_started/content/80-what-is-lab.md +7 -0
- solara/website/pages/documentation/getting_started/content/90-troubleshoot.md +26 -0
- solara/website/pages/docutils.py +38 -0
- solara/website/pages/home.vue +1199 -0
- solara/website/pages/our_team/__init__.py +83 -0
- solara/website/pages/pricing/__init__.py +31 -0
- solara/website/pages/roadmap/__init__.py +11 -0
- solara/website/pages/roadmap/roadmap.md +47 -0
- solara/website/pages/scale_ipywidgets.py +45 -0
- solara/website/pages/showcase/__init__.py +105 -0
- solara/website/pages/showcase/domino_code_assist.py +60 -0
- solara/website/pages/showcase/planeto_tessa.py +19 -0
- solara/website/pages/showcase/solara_dev.py +54 -0
- solara/website/pages/showcase/solarathon_2023_team_2.py +22 -0
- solara/website/pages/showcase/solarathon_2023_team_4.py +22 -0
- solara/website/pages/showcase/solarathon_2023_team_5.py +23 -0
- solara/website/pages/showcase/solarathon_2023_team_6.py +34 -0
- solara/website/pages/showcase/wanderlust.py +27 -0
- solara/website/public/beach.jpeg +0 -0
- solara/website/public/logo.svg +6 -0
- solara/website/public/social/discord.svg +1 -0
- solara/website/public/social/github.svg +1 -0
- solara/website/public/social/twitter.svg +3 -0
- solara/website/public/success.html +25 -0
- solara/website/templates/index.html.j2 +117 -0
- solara/website/utils.py +51 -0
- solara/widgets/__init__.py +1 -0
- solara/widgets/vue/gridlayout.vue +107 -0
- solara/widgets/vue/html.vue +4 -0
- solara/widgets/vue/navigator.vue +134 -0
- solara/widgets/vue/vegalite.vue +130 -0
- solara/widgets/widgets.py +74 -0
- solara_ui-1.45.0.data/data/etc/jupyter/jupyter_notebook_config.d/solara.json +7 -0
- solara_ui-1.45.0.data/data/etc/jupyter/jupyter_server_config.d/solara.json +7 -0
- solara_ui-1.45.0.dist-info/METADATA +162 -0
- solara_ui-1.45.0.dist-info/RECORD +464 -0
- solara_ui-1.45.0.dist-info/WHEEL +4 -0
- solara_ui-1.45.0.dist-info/licenses/LICENSE +21 -0
solara/server/app.py
ADDED
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import importlib.util
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import pickle
|
|
6
|
+
import sys
|
|
7
|
+
import threading
|
|
8
|
+
import traceback
|
|
9
|
+
import warnings
|
|
10
|
+
import weakref
|
|
11
|
+
from enum import Enum
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Dict, List, Optional, cast
|
|
14
|
+
|
|
15
|
+
import ipywidgets as widgets
|
|
16
|
+
import reacton
|
|
17
|
+
from reacton.core import Element, render
|
|
18
|
+
|
|
19
|
+
import solara
|
|
20
|
+
import solara.lifecycle
|
|
21
|
+
from solara.util import nested_get
|
|
22
|
+
|
|
23
|
+
from . import kernel_context, patch, reload, settings
|
|
24
|
+
from .kernel import Kernel
|
|
25
|
+
from .utils import pdb_guard
|
|
26
|
+
|
|
27
|
+
WebSocket = Any
|
|
28
|
+
apps: Dict[str, "AppScript"] = {}
|
|
29
|
+
thread_lock = threading.Lock()
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger("solara.server.app")
|
|
32
|
+
|
|
33
|
+
reload.reloader.start()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AppType(str, Enum):
|
|
37
|
+
SCRIPT = "script"
|
|
38
|
+
NOTEBOOK = "notebook"
|
|
39
|
+
MODULE = "module"
|
|
40
|
+
DIRECTORY = "directory"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def display(*args, **kwargs):
|
|
44
|
+
print("display not implemented", args, kwargs) # noqa
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AppScript:
|
|
48
|
+
directory: Path
|
|
49
|
+
routes: List[solara.Route]
|
|
50
|
+
|
|
51
|
+
def __init__(self, name, default_app_name="Page"):
|
|
52
|
+
self.fullname = name
|
|
53
|
+
if reload.reloader.on_change:
|
|
54
|
+
raise RuntimeError("Previous reloader still had a on_change attached, no cleanup?")
|
|
55
|
+
reload.reloader.on_change = self.on_file_change
|
|
56
|
+
|
|
57
|
+
self.app_name = default_app_name
|
|
58
|
+
if ":" in self.fullname:
|
|
59
|
+
self.name, self.app_name = self.fullname.rsplit(":", 1)
|
|
60
|
+
if len(self.name) == 1: # a windows drive letter, restore
|
|
61
|
+
self.name = self.fullname
|
|
62
|
+
self.app_name = default_app_name
|
|
63
|
+
else:
|
|
64
|
+
self.name = name
|
|
65
|
+
self.path: Path = Path(self.name).resolve()
|
|
66
|
+
if self.path.is_dir():
|
|
67
|
+
self.type = AppType.DIRECTORY
|
|
68
|
+
# resolve the directory, because Path("file").parent.parent == "." != ".."
|
|
69
|
+
self.directory = self.path.resolve()
|
|
70
|
+
elif self.name.endswith(".py"):
|
|
71
|
+
self.type = AppType.SCRIPT
|
|
72
|
+
self.directory = self.path.parent.resolve()
|
|
73
|
+
elif self.name.endswith(".ipynb"):
|
|
74
|
+
self.type = AppType.NOTEBOOK
|
|
75
|
+
self.directory = self.path.parent.resolve()
|
|
76
|
+
else:
|
|
77
|
+
self.type = AppType.MODULE
|
|
78
|
+
try:
|
|
79
|
+
spec = importlib.util.find_spec(self.name)
|
|
80
|
+
except ValueError:
|
|
81
|
+
if self.name not in sys.modules:
|
|
82
|
+
raise ImportError(f"Module {self.name} not found")
|
|
83
|
+
spec = importlib.util.spec_from_file_location(self.name, sys.modules[self.name].__file__)
|
|
84
|
+
if spec is None:
|
|
85
|
+
raise ImportError(f"Module {self.name} cannot be found")
|
|
86
|
+
assert spec is not None
|
|
87
|
+
if spec.origin is None:
|
|
88
|
+
raise ImportError(f"Module {self.name} cannot be found, or is a namespace package")
|
|
89
|
+
assert spec.origin is not None
|
|
90
|
+
self.path = Path(spec.origin)
|
|
91
|
+
self.directory = self.path.parent
|
|
92
|
+
self._initialized = False
|
|
93
|
+
self._lock = threading.Lock()
|
|
94
|
+
|
|
95
|
+
def init(self):
|
|
96
|
+
try:
|
|
97
|
+
context = kernel_context.get_current_context()
|
|
98
|
+
except RuntimeError:
|
|
99
|
+
context = None
|
|
100
|
+
if context is not None:
|
|
101
|
+
raise RuntimeError(f"We should not have an existing Solara app context when running an app for the first time: {context}")
|
|
102
|
+
dummy_kernel_context = kernel_context.create_dummy_context()
|
|
103
|
+
with dummy_kernel_context:
|
|
104
|
+
app = self._execute()
|
|
105
|
+
|
|
106
|
+
# We now ran the app, now we can check for patches that require heavy imports
|
|
107
|
+
patch.patch_heavy_imports()
|
|
108
|
+
|
|
109
|
+
self._first_execute_app = app
|
|
110
|
+
reload.reloader.root_path = self.directory
|
|
111
|
+
if self.type == AppType.MODULE:
|
|
112
|
+
package_name = self.name.split(".")[0]
|
|
113
|
+
mod = importlib.import_module(package_name)
|
|
114
|
+
if mod.__file__ is not None:
|
|
115
|
+
package_root_path = Path(mod.__file__).parent
|
|
116
|
+
reload.reloader.root_path = package_root_path
|
|
117
|
+
dummy_kernel_context.close()
|
|
118
|
+
self._initialized = True
|
|
119
|
+
|
|
120
|
+
def _execute(self):
|
|
121
|
+
logger.info("Executing %s", self.name)
|
|
122
|
+
app = None
|
|
123
|
+
routes: Optional[List[solara.Route]] = None
|
|
124
|
+
|
|
125
|
+
def add_path():
|
|
126
|
+
# this is not expected for modules, similar to `python script.py and python -m package.mymodule`
|
|
127
|
+
if self.type in [AppType.SCRIPT, AppType.NOTEBOOK]:
|
|
128
|
+
working_directory = str(self.path.parent)
|
|
129
|
+
if working_directory not in sys.path:
|
|
130
|
+
sys.path.insert(0, working_directory)
|
|
131
|
+
|
|
132
|
+
if self.type == AppType.DIRECTORY:
|
|
133
|
+
# resolve the directory, because Path("file").parent.parent == "." != ".."
|
|
134
|
+
routes = solara.generate_routes_directory(self.path)
|
|
135
|
+
|
|
136
|
+
if any(name for name in sys.modules.keys() if name.startswith(self.name)):
|
|
137
|
+
logger.warn(
|
|
138
|
+
f"Directory {self.name} is also used as a package. This can cause modules to be loaded twice, and might "
|
|
139
|
+
"cause unexpected behavior. If you run solara from a different directory (e.g. the parent directory) you "
|
|
140
|
+
"can avoid this ambiguity."
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
elif self.type == AppType.SCRIPT:
|
|
144
|
+
add_path()
|
|
145
|
+
# manually add the script to the watcher
|
|
146
|
+
reload.reloader.watcher.add_file(self.path)
|
|
147
|
+
initial_namespace = {
|
|
148
|
+
"__name__": "__main__",
|
|
149
|
+
}
|
|
150
|
+
with reload.reloader.watch():
|
|
151
|
+
routes = [solara.autorouting._generate_route_path(self.path, first=True, initial_namespace=initial_namespace)]
|
|
152
|
+
elif self.type == AppType.NOTEBOOK:
|
|
153
|
+
add_path()
|
|
154
|
+
# manually add the notebook to the watcher
|
|
155
|
+
reload.reloader.watcher.add_file(self.path)
|
|
156
|
+
with reload.reloader.watch():
|
|
157
|
+
routes = [solara.autorouting._generate_route_path(self.path, first=True)]
|
|
158
|
+
else:
|
|
159
|
+
# the module itself will be added by reloader
|
|
160
|
+
# automatically
|
|
161
|
+
with kernel_context.without_context(), reload.reloader.watch():
|
|
162
|
+
self.type = AppType.MODULE
|
|
163
|
+
mod = importlib.import_module(self.name)
|
|
164
|
+
routes = solara.generate_routes(mod)
|
|
165
|
+
|
|
166
|
+
app = solara.autorouting.RenderPage(self.app_name)
|
|
167
|
+
|
|
168
|
+
# when the root moduled defined routes, skip the enclosing route object
|
|
169
|
+
if len(routes) == 1 and routes[0].module and hasattr(routes[0].module, "routes"):
|
|
170
|
+
if routes[0].component:
|
|
171
|
+
warnings.warn(
|
|
172
|
+
f"{self.name} has a component defined, but you are also defining routes."
|
|
173
|
+
" To avoid confusing, consider renaming the {self.app_name} component."
|
|
174
|
+
)
|
|
175
|
+
routes = routes[0].children
|
|
176
|
+
|
|
177
|
+
if self.app_name != "Page":
|
|
178
|
+
# if we specified the app name, we replace the component
|
|
179
|
+
if len(routes) > 1:
|
|
180
|
+
raise ValueError(f"App {self.name} has multiple routes, but a default app name was given: {self.app_name}")
|
|
181
|
+
assert len(routes) == 1
|
|
182
|
+
component = nested_get(routes[0].module.__dict__, self.app_name, None)
|
|
183
|
+
routes = [dataclasses.replace(routes[0], component=component)]
|
|
184
|
+
|
|
185
|
+
if settings.ssg.build_path is None:
|
|
186
|
+
settings.ssg.build_path = self.directory.parent.resolve() / "build"
|
|
187
|
+
|
|
188
|
+
# auto enable search if search.json exists
|
|
189
|
+
search_index_file = self.directory.parent / "assets" / "search.json"
|
|
190
|
+
if search_index_file.exists():
|
|
191
|
+
settings.search.enabled = True
|
|
192
|
+
|
|
193
|
+
# this might be useful for development
|
|
194
|
+
# but requires reloading of react in solara itself
|
|
195
|
+
# for name, module in sys.modules.items():
|
|
196
|
+
# if name.startswith("reacton"):
|
|
197
|
+
# file = inspect.getfile(module)
|
|
198
|
+
# self.watcher.add_file(file)
|
|
199
|
+
|
|
200
|
+
# cgi vars: https://datatracker.ietf.org/doc/html/rfc3875
|
|
201
|
+
# we cannot set script name, because gunicorn uses it (and will crash)
|
|
202
|
+
# os.environ["SCRIPT_NAME"] = self.name
|
|
203
|
+
os.environ["PATH_TRANSLATED"] = str(self.path.resolve())
|
|
204
|
+
|
|
205
|
+
self.routes = routes
|
|
206
|
+
|
|
207
|
+
# this might be useful for development
|
|
208
|
+
# but requires reloading of react in solara itself
|
|
209
|
+
# for name, module in sys.modules.items():
|
|
210
|
+
# if name.startswith("reacton"):
|
|
211
|
+
# file = inspect.getfile(module)
|
|
212
|
+
# self.watcher.add_file(file)
|
|
213
|
+
|
|
214
|
+
# cgi vars: https://datatracker.ietf.org/doc/html/rfc3875
|
|
215
|
+
# we cannot set script name, because gunicorn uses it (and will crash)
|
|
216
|
+
# os.environ["SCRIPT_NAME"] = self.name
|
|
217
|
+
os.environ["PATH_TRANSLATED"] = str(self.path.resolve())
|
|
218
|
+
return app
|
|
219
|
+
|
|
220
|
+
def close(self):
|
|
221
|
+
reload.reloader.on_change = None
|
|
222
|
+
context_values = list(kernel_context.contexts.values())
|
|
223
|
+
kernel_context.contexts.clear()
|
|
224
|
+
for context in context_values:
|
|
225
|
+
context.close()
|
|
226
|
+
|
|
227
|
+
def check(self):
|
|
228
|
+
if not self._initialized:
|
|
229
|
+
with self._lock:
|
|
230
|
+
if not self._initialized:
|
|
231
|
+
self.init()
|
|
232
|
+
|
|
233
|
+
def run(self):
|
|
234
|
+
self.check()
|
|
235
|
+
if reload.reloader.requires_reload or self._first_execute_app is None:
|
|
236
|
+
with thread_lock:
|
|
237
|
+
if reload.reloader.requires_reload or self._first_execute_app is None:
|
|
238
|
+
self._first_execute_app = None
|
|
239
|
+
self._first_execute_app = self._execute()
|
|
240
|
+
print("Re-executed app", self.name) # noqa
|
|
241
|
+
# We now ran the app again, might contain new imports
|
|
242
|
+
patch.patch_heavy_imports()
|
|
243
|
+
|
|
244
|
+
return self._first_execute_app
|
|
245
|
+
|
|
246
|
+
def on_file_change(self, name):
|
|
247
|
+
path = Path(name)
|
|
248
|
+
if path.suffix == ".vue":
|
|
249
|
+
logger.info("Vue file changed: %s", name)
|
|
250
|
+
template_content = path.read_text(encoding="utf-8")
|
|
251
|
+
for context in list(kernel_context.contexts.values()):
|
|
252
|
+
with context:
|
|
253
|
+
for filepath, widget in context.templates.items():
|
|
254
|
+
if filepath == str(path):
|
|
255
|
+
widget.template = template_content
|
|
256
|
+
else:
|
|
257
|
+
logger.info("Reload requires due to change in module: %s", name)
|
|
258
|
+
self.reload()
|
|
259
|
+
|
|
260
|
+
def reload(self):
|
|
261
|
+
# if multiple files change in a short time, we want to do this
|
|
262
|
+
# not concurrently. Even better would be to do a debounce?
|
|
263
|
+
with thread_lock:
|
|
264
|
+
# TODO: clearing the type_counter is a bit of a hack
|
|
265
|
+
# and we should introduce reload 'hooks', so there is
|
|
266
|
+
# less interdependency between modules
|
|
267
|
+
import solara.lab.toestand
|
|
268
|
+
|
|
269
|
+
solara.lab.toestand.ConnectionStore._type_counter.clear()
|
|
270
|
+
|
|
271
|
+
# we need to remove callbacks that are added in the app code
|
|
272
|
+
# which will be re-executed after the reload and we do not
|
|
273
|
+
# want to keep executing the old ones.
|
|
274
|
+
for kc in solara.lifecycle._on_kernel_start_callbacks.copy():
|
|
275
|
+
callback, path, module, cleanup = kc
|
|
276
|
+
will_reload = False
|
|
277
|
+
if module is not None:
|
|
278
|
+
module_name = module.__name__
|
|
279
|
+
if module_name in reload.reloader.get_reload_module_names():
|
|
280
|
+
will_reload = True
|
|
281
|
+
elif path is not None:
|
|
282
|
+
if str(path.resolve()).startswith(str(self.directory)):
|
|
283
|
+
will_reload = True
|
|
284
|
+
else:
|
|
285
|
+
logger.warning(
|
|
286
|
+
"script %s is not in the same directory as the app %s but is using on_kernel_start, "
|
|
287
|
+
"this might lead to multiple entries, and might indicate a bug.",
|
|
288
|
+
path,
|
|
289
|
+
self.directory,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
if will_reload:
|
|
293
|
+
logger.info("reload: Removing on_kernel_start callback: %s (since it will be added when reloaded)", callback)
|
|
294
|
+
cleanup()
|
|
295
|
+
|
|
296
|
+
context_values = list(kernel_context.contexts.values())
|
|
297
|
+
# save states into the context so the hot reload will
|
|
298
|
+
# keep the same state
|
|
299
|
+
for context in context_values:
|
|
300
|
+
render_context = cast(reacton.core._RenderContext, context.app_object)
|
|
301
|
+
if render_context:
|
|
302
|
+
with context:
|
|
303
|
+
# we save the state for when the app reruns, so we stay in the same state.
|
|
304
|
+
# (e.g. button clicks, chosen options etc)
|
|
305
|
+
# for instance a dataframe, needs to be pickled, because after the pandas
|
|
306
|
+
# module is reloaded, it's a different class type
|
|
307
|
+
logger.info("pickling state: %s", render_context.state_get())
|
|
308
|
+
try:
|
|
309
|
+
context.state = pickle.dumps(render_context.state_get())
|
|
310
|
+
except Exception as e:
|
|
311
|
+
logger.warning("Could not pickle state, next render the state will be lost: %s", e)
|
|
312
|
+
# clear/cleanup the render_context, so during reload we start
|
|
313
|
+
# from scratch
|
|
314
|
+
context.app_object = None
|
|
315
|
+
# we want to reuse the container
|
|
316
|
+
render_context.container = None
|
|
317
|
+
try:
|
|
318
|
+
render_context.close()
|
|
319
|
+
except Exception as e:
|
|
320
|
+
logger.exception("Could not close render context: %s", e)
|
|
321
|
+
|
|
322
|
+
# ask all contexts/users to reload
|
|
323
|
+
for context in context_values:
|
|
324
|
+
with context:
|
|
325
|
+
context.reload()
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _run_app(
|
|
329
|
+
app_state,
|
|
330
|
+
app_script: AppScript,
|
|
331
|
+
pathname: str,
|
|
332
|
+
render_context: reacton.core._RenderContext = None,
|
|
333
|
+
):
|
|
334
|
+
# app.signal_hook_install()
|
|
335
|
+
main_object = app_script.run()
|
|
336
|
+
app_state = pickle.loads(app_state) if app_state is not None else None
|
|
337
|
+
if app_state:
|
|
338
|
+
logger.info("Restoring state: %r", app_state)
|
|
339
|
+
|
|
340
|
+
context = kernel_context.get_current_context()
|
|
341
|
+
container = context.container
|
|
342
|
+
if isinstance(main_object, widgets.Widget):
|
|
343
|
+
return main_object, render_context
|
|
344
|
+
elif isinstance(main_object, Element) or isinstance(main_object, reacton.core.Component):
|
|
345
|
+
if isinstance(main_object, Element):
|
|
346
|
+
children = [main_object]
|
|
347
|
+
else:
|
|
348
|
+
children = [main_object()]
|
|
349
|
+
solara_context = solara.RoutingProvider(children=children, routes=app_script.routes, pathname=pathname)
|
|
350
|
+
if render_context is None:
|
|
351
|
+
result = render(solara_context, container, handle_error=True, initial_state=app_state)
|
|
352
|
+
# support older versions of react
|
|
353
|
+
if isinstance(result, tuple):
|
|
354
|
+
container, render_context = result
|
|
355
|
+
else:
|
|
356
|
+
render_context = result
|
|
357
|
+
else:
|
|
358
|
+
if app_state:
|
|
359
|
+
render_context.state_set(render_context.context_root, app_state)
|
|
360
|
+
result = render_context.render(solara_context)
|
|
361
|
+
container = render_context.container
|
|
362
|
+
# return container, render_context
|
|
363
|
+
else:
|
|
364
|
+
extra = ""
|
|
365
|
+
dotted = []
|
|
366
|
+
for key, value in vars(main_object).items():
|
|
367
|
+
if isinstance(value, (Element, widgets.Widget)):
|
|
368
|
+
dotted.append(f"{app_script.app_name}.{key}")
|
|
369
|
+
if dotted:
|
|
370
|
+
extra = " We did find that sub objects that might work: " + ", ".join(dotted)
|
|
371
|
+
raise ValueError(
|
|
372
|
+
f"Main object (with name {app_script.app_name} in {app_script.path}) is not a Widget, Element or Component, but {type(main_object)}." + extra
|
|
373
|
+
)
|
|
374
|
+
return container, render_context
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def load_app_widget(app_state, app_script: AppScript, pathname: str):
|
|
378
|
+
# load the app, and set it at the child of the context's container
|
|
379
|
+
app_state_initial = app_state
|
|
380
|
+
context = kernel_context.get_current_context()
|
|
381
|
+
container = context.container
|
|
382
|
+
assert container is not None
|
|
383
|
+
try:
|
|
384
|
+
import ipyreact
|
|
385
|
+
|
|
386
|
+
del ipyreact
|
|
387
|
+
except ModuleNotFoundError:
|
|
388
|
+
pass
|
|
389
|
+
else:
|
|
390
|
+
import solara.server.esm
|
|
391
|
+
|
|
392
|
+
# will create widgets, but will clean itself up when the kernel closes
|
|
393
|
+
solara.server.esm.create_modules()
|
|
394
|
+
solara.server.esm.create_import_map()
|
|
395
|
+
|
|
396
|
+
try:
|
|
397
|
+
render_context = context.app_object
|
|
398
|
+
app_state = app_state_initial
|
|
399
|
+
with pdb_guard():
|
|
400
|
+
widget, render_context = _run_app(
|
|
401
|
+
app_state,
|
|
402
|
+
app_script,
|
|
403
|
+
pathname,
|
|
404
|
+
render_context=render_context,
|
|
405
|
+
)
|
|
406
|
+
if render_context is None:
|
|
407
|
+
assert context.container is not None
|
|
408
|
+
context.container.children = [widget]
|
|
409
|
+
|
|
410
|
+
if render_context:
|
|
411
|
+
context.app_object = render_context
|
|
412
|
+
|
|
413
|
+
except BaseException as e:
|
|
414
|
+
error = ""
|
|
415
|
+
error = "".join(traceback.format_exception(None, e, e.__traceback__))
|
|
416
|
+
print(error, file=sys.stdout, flush=True) # noqa
|
|
417
|
+
# widget = widgets.Label(value="Error, see server logs")
|
|
418
|
+
import html
|
|
419
|
+
|
|
420
|
+
error = html.escape(error)
|
|
421
|
+
with context:
|
|
422
|
+
widget = widgets.HTML(f"<pre>{error}</pre>", layout=widgets.Layout(overflow="auto"))
|
|
423
|
+
container.children = [widget]
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def load_themes(themes: Dict[str, Dict[str, str]], dark: bool):
|
|
427
|
+
# While these usually gets set from the frontend, in solara (server) we want to know theme information directly at the first
|
|
428
|
+
# render. Also, using the same trait allows us to write code which works on all widgets platforms, instead
|
|
429
|
+
# or using something different when running under solara server
|
|
430
|
+
from solara.lab.components.theming import _set_theme, theme
|
|
431
|
+
|
|
432
|
+
_set_theme(themes)
|
|
433
|
+
theme.dark_effective = dark
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def solara_comm_target(comm, msg_first):
|
|
437
|
+
app: Optional[AppScript] = None
|
|
438
|
+
|
|
439
|
+
def on_msg(msg):
|
|
440
|
+
nonlocal app
|
|
441
|
+
comm = comm_ref()
|
|
442
|
+
assert comm is not None
|
|
443
|
+
context = kernel_context.get_current_context()
|
|
444
|
+
data = msg["content"]["data"]
|
|
445
|
+
method = data["method"]
|
|
446
|
+
if method == "run":
|
|
447
|
+
args = data["args"]
|
|
448
|
+
path = args.get("path", "")
|
|
449
|
+
app_name = args.get("appName") or "__default__"
|
|
450
|
+
app = apps[app_name]
|
|
451
|
+
context = kernel_context.get_current_context()
|
|
452
|
+
import ipyvuetify
|
|
453
|
+
|
|
454
|
+
container = ipyvuetify.Html(tag="div")
|
|
455
|
+
context.container = container
|
|
456
|
+
themes = args.get("themes")
|
|
457
|
+
dark = args.get("dark")
|
|
458
|
+
load_themes(themes, dark)
|
|
459
|
+
try:
|
|
460
|
+
load_app_widget(None, app, path)
|
|
461
|
+
except Exception as e:
|
|
462
|
+
msg = f"Error loading app: from path {path} and app {app_name}"
|
|
463
|
+
logger.exception(msg)
|
|
464
|
+
raise RuntimeError(msg) from e
|
|
465
|
+
comm.send({"method": "finished", "widget_id": context.container._model_id})
|
|
466
|
+
elif method == "app-status":
|
|
467
|
+
context = kernel_context.get_current_context()
|
|
468
|
+
# if there is no container, we never ran the app
|
|
469
|
+
if context.container is not None:
|
|
470
|
+
logger.info("app-status check: %s app started", context.id)
|
|
471
|
+
comm.send({"method": "app-status", "started": True})
|
|
472
|
+
else:
|
|
473
|
+
logger.info("app-status check: %s app not started", context.id)
|
|
474
|
+
comm.send({"method": "app-status", "started": False})
|
|
475
|
+
|
|
476
|
+
elif method == "reload":
|
|
477
|
+
from solara.lab.components.theming import _get_theme, theme
|
|
478
|
+
|
|
479
|
+
assert app is not None
|
|
480
|
+
context = kernel_context.get_current_context()
|
|
481
|
+
path = data.get("path", "")
|
|
482
|
+
current_theme = theme._instance.value
|
|
483
|
+
theme_dict = _get_theme(current_theme)
|
|
484
|
+
|
|
485
|
+
with context:
|
|
486
|
+
context.restart()
|
|
487
|
+
load_themes(theme_dict, current_theme.dark_effective)
|
|
488
|
+
load_app_widget(context.state, app, path)
|
|
489
|
+
comm.send({"method": "finished"})
|
|
490
|
+
else:
|
|
491
|
+
logger.error("Unknown comm method called on solara.control comm: %s", method)
|
|
492
|
+
|
|
493
|
+
def reload():
|
|
494
|
+
comm = comm_ref()
|
|
495
|
+
assert comm is not None
|
|
496
|
+
context = kernel_context.get_current_context()
|
|
497
|
+
# we don't reload the app ourself, we send a message to the client
|
|
498
|
+
# this ensures that we don't run code of any client that for some reason is connected
|
|
499
|
+
# but not working anymore. And it indirectly passes a message from the current thread
|
|
500
|
+
# (which is that of the Reloader/watchdog), to the thread of the client
|
|
501
|
+
logger.debug(f"Send reload to client: {context.id}")
|
|
502
|
+
comm.send({"method": "reload"})
|
|
503
|
+
|
|
504
|
+
comm.on_msg(on_msg)
|
|
505
|
+
comm_ref = weakref.ref(comm)
|
|
506
|
+
del comm
|
|
507
|
+
|
|
508
|
+
kernel_context.get_current_context().reload = reload
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def register_solara_comm_target(kernel: Kernel):
|
|
512
|
+
kernel.comm_manager.register_target("solara.control", solara_comm_target)
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
from . import patch # noqa
|
|
516
|
+
|
|
517
|
+
patch.patch()
|
|
518
|
+
# the default app (used in solara-server)
|
|
519
|
+
if "SOLARA_APP" in os.environ:
|
|
520
|
+
with pdb_guard():
|
|
521
|
+
apps["__default__"] = AppScript(os.environ.get("SOLARA_APP", "solara.website.pages:Page"))
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
@solara.util.once
|
|
525
|
+
def ensure_apps_initialized():
|
|
526
|
+
for app in apps.values():
|
|
527
|
+
app.init()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/* not empty *
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/* not empty */
|
|
Binary file
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="65" height="65"><svg width="65" height="65" viewBox="0 0 65 65" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M57.11 30.64L61.47 17.87L48.7 13.51L42.76 1.39999L30.65 7.34999L17.87 2.97999L13.51 15.75L1.40002 21.7L7.35002 33.81L2.99002 46.58L15.76 50.94L21.71 63.06L33.82 57.11L46.59 61.47L50.95 48.7L63.06 42.75L57.11 30.64ZM54.26 34.39L34.39 54.26C33.19 55.46 31.25 55.46 30.05 54.26L10.2 34.4C9.00002 33.2 9.00002 31.26 10.2 30.07L30.06 10.2C31.26 8.99999 33.2 8.99999 34.4 10.2L54.27 30.07C55.47 31.27 55.47 33.21 54.27 34.4L54.26 34.39Z" fill="#FFCF64"></path>
|
|
3
|
+
<path d="M53.62 19.42L51.65 6.07L38.3 8.04L27.46 0L19.42 10.84L6.07 12.82L8.04 26.17L0 37L10.84 45.04L12.81 58.39L26.16 56.42L37 64.46L45.04 53.62L58.39 51.64L56.42 38.29L64.46 27.45L53.62 19.4V19.42ZM52.8 24.06L44.24 50.82C43.72 52.43 42 53.32 40.39 52.81L13.63 44.25C12.02 43.74 11.13 42.01 11.64 40.4L20.21 13.64C20.72 12.03 22.45 11.14 24.06 11.65L50.82 20.21C52.43 20.72 53.32 22.45 52.81 24.06H52.8Z" fill="#FF8C3E"></path>
|
|
4
|
+
</svg><style>@media (prefers-color-scheme: light) { :root { filter: none; } }
|
|
5
|
+
</style></svg>
|