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/hooks/misc.py
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import io
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
# import tempfile
|
|
8
|
+
import threading
|
|
9
|
+
import time
|
|
10
|
+
import urllib.request
|
|
11
|
+
import uuid
|
|
12
|
+
from typing import IO, Any, Callable, Tuple, TypeVar, Union, cast
|
|
13
|
+
|
|
14
|
+
import solara
|
|
15
|
+
from solara.datatypes import FileContentResult, Result
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger("react-ipywidgets.extra.hooks")
|
|
18
|
+
chunk_size_default = 1024**2
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"use_download",
|
|
22
|
+
"use_fetch",
|
|
23
|
+
"use_json_load",
|
|
24
|
+
"use_json",
|
|
25
|
+
"use_file_content",
|
|
26
|
+
"use_uuid4",
|
|
27
|
+
"use_unique_key",
|
|
28
|
+
"use_state_or_update",
|
|
29
|
+
"use_previous",
|
|
30
|
+
"use_trait_observe",
|
|
31
|
+
]
|
|
32
|
+
T = TypeVar("T")
|
|
33
|
+
U = TypeVar("U")
|
|
34
|
+
|
|
35
|
+
MaybeResult = Union[T, Result[T]]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def use_retry(*actions: Callable[[], Any]):
|
|
39
|
+
counter, set_counter = solara.use_state(0)
|
|
40
|
+
|
|
41
|
+
def retry():
|
|
42
|
+
for action in actions:
|
|
43
|
+
action()
|
|
44
|
+
set_counter(lambda counter: counter + 1)
|
|
45
|
+
|
|
46
|
+
return counter, retry
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def use_download(
|
|
50
|
+
f: MaybeResult[Union[str, os.PathLike, IO]], url, expected_size=None, delay=None, return_content=False, chunk_size=chunk_size_default
|
|
51
|
+
) -> Result:
|
|
52
|
+
from .use_thread import use_thread
|
|
53
|
+
|
|
54
|
+
if not isinstance(f, Result):
|
|
55
|
+
f = Result(value=f)
|
|
56
|
+
assert isinstance(f, Result)
|
|
57
|
+
content_length, set_content_length = solara.use_state(expected_size, key="content_length")
|
|
58
|
+
downloaded_length = 0
|
|
59
|
+
file_object = hasattr(f.value, "tell")
|
|
60
|
+
if not file_object:
|
|
61
|
+
file_path = cast(Union[str, os.PathLike], f.value)
|
|
62
|
+
if os.path.exists(file_path) and expected_size is not None:
|
|
63
|
+
file_size = os.path.getsize(file_path)
|
|
64
|
+
if file_size == expected_size:
|
|
65
|
+
downloaded_length = file_size
|
|
66
|
+
downloaded_length, set_downloaded_length = solara.use_state(downloaded_length, key="downloaded_length")
|
|
67
|
+
|
|
68
|
+
def download(cancel: threading.Event):
|
|
69
|
+
assert isinstance(f, Result)
|
|
70
|
+
nonlocal downloaded_length
|
|
71
|
+
if expected_size is not None and downloaded_length == expected_size:
|
|
72
|
+
return # we already downloaded, but hooks cannot be conditional
|
|
73
|
+
|
|
74
|
+
context: Any = None
|
|
75
|
+
if file_object:
|
|
76
|
+
context = solara.util.nullcontext()
|
|
77
|
+
output_file = cast(IO, f.value)
|
|
78
|
+
else:
|
|
79
|
+
# f = cast(Result[Union[str, os.PathLike]], f)
|
|
80
|
+
output_file = context = open(f.value, "wb") # type: ignore
|
|
81
|
+
|
|
82
|
+
with context:
|
|
83
|
+
with urllib.request.urlopen(url) as response:
|
|
84
|
+
content_length = int(response.info()["Content-Length"])
|
|
85
|
+
logger.info("content_length for %r = %r", url, content_length)
|
|
86
|
+
set_content_length(content_length)
|
|
87
|
+
bytes_read = 0
|
|
88
|
+
while not cancel.is_set():
|
|
89
|
+
chunk = response.read(chunk_size)
|
|
90
|
+
|
|
91
|
+
if delay:
|
|
92
|
+
time.sleep(delay)
|
|
93
|
+
if not chunk:
|
|
94
|
+
break
|
|
95
|
+
bytes_read += len(chunk)
|
|
96
|
+
output_file.write(chunk)
|
|
97
|
+
set_downloaded_length(bytes_read)
|
|
98
|
+
return bytes_read
|
|
99
|
+
|
|
100
|
+
result: Result[Any] = use_thread(download, [f, url])
|
|
101
|
+
# maybe we wanna check this
|
|
102
|
+
# download_is_done = downloaded_length == content_length
|
|
103
|
+
|
|
104
|
+
if content_length is not None:
|
|
105
|
+
progress = downloaded_length / content_length
|
|
106
|
+
else:
|
|
107
|
+
progress = 0
|
|
108
|
+
|
|
109
|
+
return dataclasses.replace(result, progress=progress)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def use_fetch(url, chunk_size=chunk_size_default):
|
|
113
|
+
# reuse the same file like object
|
|
114
|
+
f = solara.use_memo(io.BytesIO, [url])
|
|
115
|
+
result = use_download(f, url, return_content=True, chunk_size=chunk_size)
|
|
116
|
+
return dataclasses.replace(result, value=f.getvalue() if result.progress == 1 else None)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def compose_result(head, *tail):
|
|
120
|
+
return head
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def ensure_result(input: MaybeResult[T]) -> Result[T]:
|
|
124
|
+
if isinstance(input, Result):
|
|
125
|
+
return input
|
|
126
|
+
else:
|
|
127
|
+
return Result(value=input)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def make_use_thread(f: Callable[[T], U]):
|
|
131
|
+
from .use_thread import use_thread
|
|
132
|
+
|
|
133
|
+
def use_result(input: MaybeResult[T]) -> Result[U]:
|
|
134
|
+
input_result = ensure_result(input)
|
|
135
|
+
|
|
136
|
+
def in_thread(cancel: threading.Event):
|
|
137
|
+
if input_result.value:
|
|
138
|
+
return f(input_result.value)
|
|
139
|
+
|
|
140
|
+
return use_thread(in_thread, dependencies=[input_result.value])
|
|
141
|
+
|
|
142
|
+
return use_result
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@make_use_thread
|
|
146
|
+
def use_json_load(value: bytes):
|
|
147
|
+
return json.loads(value)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def use_json(path):
|
|
151
|
+
return use_fetch(path) | use_json_load
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def use_file_content(path, watch=False) -> FileContentResult[bytes]:
|
|
155
|
+
counter, retry = use_retry()
|
|
156
|
+
|
|
157
|
+
def read_file(*ignore):
|
|
158
|
+
try:
|
|
159
|
+
with open(path, "rb") as f:
|
|
160
|
+
return f.read()
|
|
161
|
+
except Exception as e:
|
|
162
|
+
return e
|
|
163
|
+
|
|
164
|
+
result = None
|
|
165
|
+
try:
|
|
166
|
+
mtime = os.path.getmtime(path)
|
|
167
|
+
except Exception:
|
|
168
|
+
mtime = None
|
|
169
|
+
|
|
170
|
+
content = solara.use_memo(read_file, dependencies=[path, mtime, counter])
|
|
171
|
+
if result is not None:
|
|
172
|
+
return result
|
|
173
|
+
if isinstance(content, Exception):
|
|
174
|
+
return FileContentResult[bytes](error=content, _retry=retry)
|
|
175
|
+
else:
|
|
176
|
+
return FileContentResult[bytes](value=content, _retry=retry)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def use_force_update() -> Callable[[], None]:
|
|
180
|
+
"""Returns a callable that can be used to force an update of a component.
|
|
181
|
+
|
|
182
|
+
This is used when external state has change, and we need to re-render out component.
|
|
183
|
+
"""
|
|
184
|
+
_counter, set_counter = solara.use_state(0, "force update counter")
|
|
185
|
+
|
|
186
|
+
def updater():
|
|
187
|
+
set_counter(lambda count: count + 1)
|
|
188
|
+
|
|
189
|
+
return updater
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def use_uuid4(dependencies=[]) -> str:
|
|
193
|
+
"""Generate a unique string using the uuid4 algorithm. Will only change when the dependencies change."""
|
|
194
|
+
|
|
195
|
+
def make_uuid(*_ignore):
|
|
196
|
+
return str(uuid.uuid4())
|
|
197
|
+
|
|
198
|
+
return solara.use_memo(make_uuid, dependencies)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def use_unique_key(key: str = None, prefix: str = "", dependencies=[]) -> str:
|
|
202
|
+
"""Generate a unique string, or use key when not None. Dependencies are forwarded to `use_uuid4`."""
|
|
203
|
+
uuid = use_uuid4(dependencies=dependencies)
|
|
204
|
+
return prefix + (key or uuid)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def use_state_or_update(
|
|
208
|
+
initial_or_updated: T, key: str = None, eq: Callable[[Any, Any], bool] = None
|
|
209
|
+
) -> Tuple[T, Callable[[Union[T, Callable[[T], T]]], None]]:
|
|
210
|
+
"""This is useful for situations where a prop can change from a parent
|
|
211
|
+
component, which should be respected, and otherwise the internal
|
|
212
|
+
state should be kept.
|
|
213
|
+
"""
|
|
214
|
+
value, set_value = solara.use_state(initial_or_updated, key=key, eq=eq)
|
|
215
|
+
|
|
216
|
+
def possibly_update():
|
|
217
|
+
nonlocal value
|
|
218
|
+
# only gets called when initial_or_updated changes
|
|
219
|
+
set_value(initial_or_updated)
|
|
220
|
+
# this make sure the return value gets updated directly
|
|
221
|
+
value = initial_or_updated
|
|
222
|
+
|
|
223
|
+
solara.use_memo(possibly_update, [initial_or_updated])
|
|
224
|
+
return value, set_value
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def use_previous(value: T, condition=True) -> T:
|
|
228
|
+
ref = solara.use_ref(value)
|
|
229
|
+
|
|
230
|
+
def assign():
|
|
231
|
+
if condition:
|
|
232
|
+
ref.current = value
|
|
233
|
+
|
|
234
|
+
solara.use_effect(assign, [value])
|
|
235
|
+
return ref.current
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def use_trait_observe(has_trait_object, name):
|
|
239
|
+
"""Observe a trait on an object, and return its value.
|
|
240
|
+
|
|
241
|
+
This is useful when you want your component to be in sync with a trait
|
|
242
|
+
of a widget or [HasTraits object](https://traitlets.readthedocs.io/en/stable/).
|
|
243
|
+
|
|
244
|
+
When the trait changes, your component will be re-rendered.
|
|
245
|
+
|
|
246
|
+
See [use_dark_effective](/api/use_dark_effective) for an example.
|
|
247
|
+
"""
|
|
248
|
+
counter = solara.use_reactive(0)
|
|
249
|
+
counter.get() # make the main component depend on this counter
|
|
250
|
+
|
|
251
|
+
def connect():
|
|
252
|
+
def update(change):
|
|
253
|
+
counter.value += 1
|
|
254
|
+
|
|
255
|
+
has_trait_object.observe(update, name)
|
|
256
|
+
|
|
257
|
+
def cleanup():
|
|
258
|
+
has_trait_object.unobserve(update, name)
|
|
259
|
+
|
|
260
|
+
return cleanup
|
|
261
|
+
|
|
262
|
+
solara.use_effect(connect, [has_trait_object, name])
|
|
263
|
+
return getattr(has_trait_object, name)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from typing import Any, Callable, Optional, TypeVar, Union
|
|
2
|
+
|
|
3
|
+
import solara
|
|
4
|
+
import solara.settings
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def use_reactive(
|
|
10
|
+
value: Union[T, solara.Reactive[T]],
|
|
11
|
+
on_change: Optional[Callable[[T], None]] = None,
|
|
12
|
+
equals: Callable[[Any, Any], bool] = solara.util.equals_extra,
|
|
13
|
+
) -> solara.Reactive[T]:
|
|
14
|
+
"""Creates a reactive variable with the a local component scope.
|
|
15
|
+
|
|
16
|
+
It is a useful alternative to `use_state` when you want to use a
|
|
17
|
+
reactive variable for the component state.
|
|
18
|
+
See also [our documentation on state management](/documentation/getting_started/fundamentals/state-management).
|
|
19
|
+
|
|
20
|
+
If the variable passed is a reactive variable, it will be returned instead and no
|
|
21
|
+
new reactive variable will be created. This is useful for implementing component
|
|
22
|
+
that accept either a reactive variable or a normal value along with an optional `on_change`
|
|
23
|
+
callback.
|
|
24
|
+
|
|
25
|
+
Note that that on each call, if the value changes, the reactive variable will be updated.
|
|
26
|
+
For objects that do no implement equality comparison, the will lead to an infinite loop.
|
|
27
|
+
|
|
28
|
+
In that case, combine the `use_reactive` with `use_memo` to never trigger an update from
|
|
29
|
+
the render function.
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
@solara.component
|
|
33
|
+
def Page():
|
|
34
|
+
data = solara.use_reactive(solara.use_memo(lambda: MyDataObject()))
|
|
35
|
+
...
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Arguments:
|
|
39
|
+
|
|
40
|
+
* value (Union[T, solara.Reactive[T]]): The value of the
|
|
41
|
+
reactive variable. If a reactive variable is provided, it will be
|
|
42
|
+
used directly. Otherwise, a new reactive variable will be created
|
|
43
|
+
with the provided initial value. If the argument passed changes
|
|
44
|
+
the reactive variable will be updated.
|
|
45
|
+
|
|
46
|
+
* on_change (Optional[Callable[[T], None]]): An optional callback function
|
|
47
|
+
that will be called when the reactive variable's value changes.
|
|
48
|
+
|
|
49
|
+
* equals: A function that returns True if two values are considered equal, and False otherwise.
|
|
50
|
+
The default function is `solara.util.equals`, which performs a deep comparison of the two values
|
|
51
|
+
and is more forgiving than the default `==` operator.
|
|
52
|
+
You can provide a custom function if you need to define a different notion of equality.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
solara.Reactive[T]: A reactive variable with the specified initial value
|
|
57
|
+
or the provided reactive variable.
|
|
58
|
+
|
|
59
|
+
## Examples
|
|
60
|
+
|
|
61
|
+
### Replacement for use_state
|
|
62
|
+
```solara
|
|
63
|
+
import solara
|
|
64
|
+
|
|
65
|
+
@solara.component
|
|
66
|
+
def ReusableComponent():
|
|
67
|
+
color = solara.use_reactive("red") # another possibility
|
|
68
|
+
solara.Select(label="Color",values=["red", "green", "blue", "orange"],
|
|
69
|
+
value=color)
|
|
70
|
+
solara.Markdown("### Solara is awesome", style={"color": color.value})
|
|
71
|
+
|
|
72
|
+
@solara.component
|
|
73
|
+
def Page():
|
|
74
|
+
# this component is used twice, but each instance has its own state
|
|
75
|
+
ReusableComponent()
|
|
76
|
+
ReusableComponent()
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Flexible arguments
|
|
81
|
+
|
|
82
|
+
The `MyComponent` component can be passed a reactive variable or a normal
|
|
83
|
+
Python variable and a `on_value` callback.
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
import solara
|
|
87
|
+
from typing import Union, Optional, Callable
|
|
88
|
+
|
|
89
|
+
@solara.component
|
|
90
|
+
def MyComponent(value: Union[T, solara.Reactive[T]],
|
|
91
|
+
on_value: Optional[Callable[[T], None]] = None,
|
|
92
|
+
):
|
|
93
|
+
reactive_value = solara.use_reactive(value, on_value)
|
|
94
|
+
# Use the `reactive_value` in the component
|
|
95
|
+
```
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
on_change_ref = solara.use_ref(on_change)
|
|
100
|
+
except RuntimeError as e:
|
|
101
|
+
raise RuntimeError(
|
|
102
|
+
"use_reactive must be called from a component function, inside the render function.\n"
|
|
103
|
+
"Do not call it top level, use [solara.reactive()](https://solara.dev/documentation/api/utilities/reactive) instead."
|
|
104
|
+
) from e
|
|
105
|
+
on_change_ref.current = on_change
|
|
106
|
+
|
|
107
|
+
def create():
|
|
108
|
+
if not isinstance(value, solara.Reactive):
|
|
109
|
+
from solara._stores import SharedStore, MutateDetectorStore, StoreValue, _PublicValueNotSet, _SetValueNotSet
|
|
110
|
+
from solara.toestand import ValueBase
|
|
111
|
+
|
|
112
|
+
store: ValueBase[T]
|
|
113
|
+
|
|
114
|
+
if solara.settings.storage.mutation_detection is True:
|
|
115
|
+
shared_store = SharedStore[StoreValue[T]](
|
|
116
|
+
StoreValue[T](private=value, public=_PublicValueNotSet(), get_traceback=None, set_value=_SetValueNotSet(), set_traceback=None),
|
|
117
|
+
unwrap=lambda x: x.private,
|
|
118
|
+
)
|
|
119
|
+
store = MutateDetectorStore[T](shared_store, equals=equals)
|
|
120
|
+
else:
|
|
121
|
+
store = SharedStore(value, equals=equals)
|
|
122
|
+
|
|
123
|
+
return solara.Reactive(store)
|
|
124
|
+
|
|
125
|
+
reactive_value = solara.use_memo(create, dependencies=[])
|
|
126
|
+
if isinstance(value, solara.Reactive):
|
|
127
|
+
reactive_value = value
|
|
128
|
+
assert reactive_value is not None
|
|
129
|
+
updating = solara.use_ref(False)
|
|
130
|
+
|
|
131
|
+
def forward_on_change():
|
|
132
|
+
def forward(value):
|
|
133
|
+
if on_change_ref.current and not updating.current:
|
|
134
|
+
on_change_ref.current(value)
|
|
135
|
+
|
|
136
|
+
return reactive_value.subscribe(forward)
|
|
137
|
+
|
|
138
|
+
def update():
|
|
139
|
+
updating.current = True
|
|
140
|
+
try:
|
|
141
|
+
if not isinstance(value, solara.Reactive):
|
|
142
|
+
reactive_value.value = value
|
|
143
|
+
finally:
|
|
144
|
+
updating.current = False
|
|
145
|
+
|
|
146
|
+
solara.use_memo(update, [value])
|
|
147
|
+
# if value is a reactive variable, and it changes, we need to subscribe to the latest
|
|
148
|
+
# reactive variable, otherwise we only link to it once
|
|
149
|
+
solara.use_effect(forward_on_change, [value] if isinstance(value, solara.Reactive) else [])
|
|
150
|
+
|
|
151
|
+
return reactive_value
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import inspect
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import threading
|
|
6
|
+
from typing import Callable, Iterator, Optional, TypeVar, Union, cast
|
|
7
|
+
|
|
8
|
+
import solara
|
|
9
|
+
from solara.datatypes import Result, ResultState
|
|
10
|
+
from solara.util import cancel_guard, nullcontext
|
|
11
|
+
|
|
12
|
+
SOLARA_ALLOW_OTHER_TRACER = os.environ.get("SOLARA_ALLOW_OTHER_TRACER", False) in (True, "True", "true", "1")
|
|
13
|
+
T = TypeVar("T")
|
|
14
|
+
logger = logging.getLogger("solara.hooks.use_thread")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def use_thread(
|
|
18
|
+
callback: Union[
|
|
19
|
+
Callable[[threading.Event], T],
|
|
20
|
+
Iterator[Callable[[threading.Event], T]],
|
|
21
|
+
Callable[[], T],
|
|
22
|
+
Iterator[Callable[[], T]],
|
|
23
|
+
],
|
|
24
|
+
dependencies=[],
|
|
25
|
+
intrusive_cancel=True,
|
|
26
|
+
) -> Result[T]:
|
|
27
|
+
from .misc import use_force_update, use_retry
|
|
28
|
+
|
|
29
|
+
def make_event(*_ignore_dependencies):
|
|
30
|
+
return threading.Event()
|
|
31
|
+
|
|
32
|
+
def make_lock():
|
|
33
|
+
return threading.Lock()
|
|
34
|
+
|
|
35
|
+
lock: threading.Lock = solara.use_memo(make_lock, [])
|
|
36
|
+
updater = use_force_update()
|
|
37
|
+
result_state, set_result_state = solara.use_state(ResultState.INITIAL)
|
|
38
|
+
error = solara.use_ref(cast(Optional[Exception], None))
|
|
39
|
+
result = solara.use_ref(cast(Optional[T], None))
|
|
40
|
+
running_thread = solara.use_ref(cast(Optional[threading.Thread], None))
|
|
41
|
+
counter, retry = use_retry()
|
|
42
|
+
cancel: threading.Event = solara.use_memo(make_event, [*dependencies, counter])
|
|
43
|
+
|
|
44
|
+
def run():
|
|
45
|
+
set_result_state(ResultState.STARTING)
|
|
46
|
+
|
|
47
|
+
def runner():
|
|
48
|
+
wait_for_thread = None
|
|
49
|
+
with lock:
|
|
50
|
+
# if there is a current thread already, we'll need
|
|
51
|
+
# to wait for it. copy the ref, and set ourselves
|
|
52
|
+
# as the current one
|
|
53
|
+
if running_thread.current:
|
|
54
|
+
wait_for_thread = running_thread.current
|
|
55
|
+
running_thread.current = threading.current_thread()
|
|
56
|
+
if wait_for_thread is not None:
|
|
57
|
+
set_result_state(ResultState.WAITING)
|
|
58
|
+
# don't start before the previous is stopped
|
|
59
|
+
try:
|
|
60
|
+
wait_for_thread.join()
|
|
61
|
+
except: # noqa
|
|
62
|
+
pass
|
|
63
|
+
if threading.current_thread() != running_thread.current:
|
|
64
|
+
# in case a new thread was started that also was waiting for the previous
|
|
65
|
+
# thread to st stop, we can finish this
|
|
66
|
+
return
|
|
67
|
+
# we previously set current to None, but if we do not do that, we can still render the old value
|
|
68
|
+
# while we can still show a loading indicator using the .state
|
|
69
|
+
# result.current = None
|
|
70
|
+
set_result_state(ResultState.RUNNING)
|
|
71
|
+
|
|
72
|
+
sig = inspect.signature(callback) # type: ignore
|
|
73
|
+
if sig.parameters:
|
|
74
|
+
f = functools.partial(callback, cancel) # type: ignore
|
|
75
|
+
else:
|
|
76
|
+
f = callback # type: ignore
|
|
77
|
+
try:
|
|
78
|
+
try:
|
|
79
|
+
# we only use the cancel_guard context manager around
|
|
80
|
+
# the function calls to f. We don't want to guard around
|
|
81
|
+
# a call to react, since that might slow down rendering
|
|
82
|
+
# during rendering
|
|
83
|
+
with cancel_guard(cancel) if intrusive_cancel else nullcontext():
|
|
84
|
+
value = f()
|
|
85
|
+
if inspect.isgenerator(value):
|
|
86
|
+
while True:
|
|
87
|
+
try:
|
|
88
|
+
with cancel_guard(cancel) if intrusive_cancel else nullcontext():
|
|
89
|
+
result.current = next(value)
|
|
90
|
+
error.current = None
|
|
91
|
+
except StopIteration:
|
|
92
|
+
break
|
|
93
|
+
# assigning to the ref doesn't trigger a rerender, so do it manually
|
|
94
|
+
updater()
|
|
95
|
+
if threading.current_thread() == running_thread.current:
|
|
96
|
+
set_result_state(ResultState.FINISHED)
|
|
97
|
+
else:
|
|
98
|
+
result.current = value
|
|
99
|
+
error.current = None
|
|
100
|
+
if threading.current_thread() == running_thread.current:
|
|
101
|
+
set_result_state(ResultState.FINISHED)
|
|
102
|
+
except Exception as e:
|
|
103
|
+
error.current = e
|
|
104
|
+
if threading.current_thread() == running_thread.current:
|
|
105
|
+
logger.exception(e)
|
|
106
|
+
set_result_state(ResultState.ERROR)
|
|
107
|
+
return
|
|
108
|
+
except solara.util.CancelledError:
|
|
109
|
+
pass
|
|
110
|
+
# this means this thread is cancelled not be request, but because
|
|
111
|
+
# a new thread is running, we can ignore this
|
|
112
|
+
finally:
|
|
113
|
+
if threading.current_thread() == running_thread.current:
|
|
114
|
+
running_thread.current = None
|
|
115
|
+
logger.info("thread done!")
|
|
116
|
+
if cancel.is_set():
|
|
117
|
+
set_result_state(ResultState.CANCELLED)
|
|
118
|
+
|
|
119
|
+
logger.info("starting thread: %r", runner)
|
|
120
|
+
thread = threading.Thread(target=runner, daemon=True)
|
|
121
|
+
thread.start()
|
|
122
|
+
|
|
123
|
+
def cleanup():
|
|
124
|
+
cancel.set() # cleanup for use effect
|
|
125
|
+
|
|
126
|
+
return cleanup
|
|
127
|
+
|
|
128
|
+
solara.use_effect(run, dependencies + [counter])
|
|
129
|
+
return Result[T](value=result.current, error=error.current, state=result_state, cancel=cancel.set, _retry=retry)
|
solara/kitchensink.py
ADDED
solara/lab/__init__.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# isort: skip_file
|
|
2
|
+
from .components import * # noqa: F401, F403
|
|
3
|
+
from .utils import cookies, headers # noqa: F401, F403
|
|
4
|
+
from ..lifecycle import on_kernel_start # noqa: F401
|
|
5
|
+
from ..tasks import task, use_task, Task, TaskResult # noqa: F401, F403
|
|
6
|
+
from ..toestand import computed # noqa: F401
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def __getattr__(name):
|
|
10
|
+
# for backwards compatibility
|
|
11
|
+
from solara.components.cross_filter import ( # noqa: F401
|
|
12
|
+
CrossFilterDataFrame,
|
|
13
|
+
CrossFilterReport,
|
|
14
|
+
CrossFilterSelect,
|
|
15
|
+
CrossFilterSlider,
|
|
16
|
+
)
|
|
17
|
+
from solara.toestand import Reactive, Ref, State # noqa: F401
|
|
18
|
+
|
|
19
|
+
if name == "CrossFilterDataFrame":
|
|
20
|
+
return CrossFilterDataFrame
|
|
21
|
+
elif name == "CrossFilterReport":
|
|
22
|
+
return CrossFilterReport
|
|
23
|
+
elif name == "CrossFilterSelect":
|
|
24
|
+
return CrossFilterSelect
|
|
25
|
+
elif name == "CrossFilterSlider":
|
|
26
|
+
return CrossFilterSlider
|
|
27
|
+
elif name == "Reactive":
|
|
28
|
+
return Reactive
|
|
29
|
+
elif name == "Ref":
|
|
30
|
+
return Ref
|
|
31
|
+
elif name == "State":
|
|
32
|
+
return State
|
|
33
|
+
else:
|
|
34
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from .chat import ChatBox, ChatInput, ChatMessage # noqa: F401
|
|
2
|
+
from .confirmation_dialog import ConfirmationDialog # noqa: F401
|
|
3
|
+
from .input_date import InputDate, InputDateRange # noqa: F401
|
|
4
|
+
from .input_time import InputTime as InputTime
|
|
5
|
+
from .menu import ClickMenu, ContextMenu, Menu # noqa: F401 F403
|
|
6
|
+
from .tabs import Tab, Tabs # noqa: F401
|
|
7
|
+
from .theming import ThemeToggle, theme, use_dark_effective # noqa: F401
|