solara 1.29.1__py2.py3-none-any.whl → 1.30.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- solara/__init__.py +5 -5
- solara/__main__.py +6 -2
- solara/autorouting.py +79 -38
- solara/cache.py +2 -2
- solara/checks.html +1 -1
- solara/components/__init__.py +1 -1
- solara/components/applayout.py +5 -5
- solara/components/button.py +4 -4
- solara/components/card.py +1 -2
- solara/components/component_vue.py +1 -1
- solara/components/cross_filter.py +6 -7
- solara/components/datatable.py +2 -3
- solara/components/figure_altair.py +1 -1
- solara/components/file_download.py +2 -2
- solara/components/file_drop.py +76 -33
- solara/components/file_drop.vue +24 -10
- solara/components/head.py +1 -2
- solara/components/head_tag.py +2 -3
- solara/components/link.py +3 -4
- solara/components/meta.py +1 -1
- solara/components/misc.py +5 -9
- solara/datatypes.py +2 -2
- solara/hooks/use_reactive.py +2 -2
- solara/lab/components/confirmation_dialog.py +1 -1
- solara/lab/components/tabs.py +6 -6
- solara/reactive.py +1 -1
- solara/routing.py +9 -8
- solara/server/app.py +44 -1
- solara/server/assets/style.css +6 -0
- solara/server/esm.py +28 -4
- solara/server/kernel_context.py +75 -7
- solara/server/patch.py +3 -0
- solara/server/reload.py +2 -2
- solara/server/server.py +3 -3
- solara/server/settings.py +1 -0
- solara/server/starlette.py +66 -33
- solara/server/static/solara_bootstrap.py +1 -1
- solara/server/templates/solara.html.j2 +62 -42
- solara/tasks.py +1 -6
- solara/util.py +23 -1
- solara/website/assets/custom.css +56 -0
- solara/website/components/__init__.py +1 -0
- solara/website/components/algolia_api.vue +157 -0
- solara/website/components/docs.py +118 -0
- solara/website/components/header.py +20 -10
- solara/website/components/hero.py +1 -1
- solara/website/pages/__init__.py +223 -20
- solara/website/pages/apps/jupyter-dashboard-1.py +1 -1
- solara/website/pages/apps/multipage/__init__.py +1 -1
- solara/website/pages/apps/multipage/page2.py +1 -1
- solara/website/pages/apps/scatter.py +21 -7
- solara/website/pages/changelog/__init__.py +8 -0
- solara/website/pages/{docs/content/95-changelog.md → changelog/changelog.md} +28 -2
- solara/website/pages/contact/__init__.py +8 -0
- solara/website/pages/documentation/__init__.py +184 -0
- solara/website/pages/documentation/advanced/__init__.py +36 -0
- solara/website/pages/documentation/advanced/content/00-overview.md +1 -0
- solara/website/pages/{docs/content/10-howto/20-multipage.md → documentation/advanced/content/10-howto/10-multipage.md} +4 -5
- solara/website/pages/{docs/content/10-howto/30-layout.md → documentation/advanced/content/10-howto/20-layout.md} +21 -21
- solara/website/pages/{docs/content/10-howto/80-embed.md → documentation/advanced/content/10-howto/40-embed.md} +1 -1
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/06-ipyvuetify.md +1 -1
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/12-reacton-basics.md +1 -1
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/15-anatomy.md +2 -2
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/18-containers.md +3 -3
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/40-routing.md +9 -9
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/50-solara-server.md +1 -1
- solara/website/pages/{docs/content/50-enterprise → documentation/advanced/content/30-enterprise}/10-oauth.md +3 -3
- solara/website/pages/{docs/content/10-howto → documentation/advanced/content/40-development}/01-contribute.md +5 -5
- solara/website/pages/{docs/content/90-development → documentation/advanced/content/40-development}/10-setup.md +2 -2
- solara/website/pages/documentation/advanced/content/__init__.py +0 -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/hooks/__init__.py +9 -0
- solara/website/pages/{api → documentation/api/hooks}/use_effect.md +3 -3
- solara/website/pages/{api → documentation/api/hooks}/use_effect.py +2 -1
- solara/website/pages/{api → documentation/api/hooks}/use_memo.md +2 -2
- solara/website/pages/{api → documentation/api/hooks}/use_memo.py +2 -1
- solara/website/pages/{api → documentation/api/hooks}/use_reactive.py +1 -2
- solara/website/pages/{api → documentation/api/hooks}/use_state.py +1 -2
- solara/website/pages/documentation/api/routing/__init__.py +9 -0
- solara/website/pages/{api → documentation/api/routing}/generate_routes.py +1 -2
- solara/website/pages/{api → documentation/api/routing}/generate_routes_directory.py +1 -2
- solara/website/pages/{api → documentation/api/routing}/use_route.py +2 -2
- solara/website/pages/{api → documentation/api/routing}/use_router.py +1 -2
- solara/website/pages/documentation/api/utilities/__init__.py +9 -0
- solara/website/pages/{api → documentation/api/utilities}/component_vue.py +1 -2
- solara/website/pages/{api → documentation/api/utilities}/computed.py +2 -2
- solara/website/pages/{api → documentation/api/utilities}/display.py +1 -2
- solara/website/pages/{api → documentation/api/utilities}/get_kernel_id.py +2 -2
- solara/website/pages/{api → documentation/api/utilities}/get_session_id.py +2 -2
- solara/website/pages/{api → documentation/api/utilities}/on_kernel_start.py +9 -3
- solara/website/pages/{api → documentation/api/utilities}/reactive.py +1 -2
- solara/website/pages/{api → documentation/api/utilities}/widget.py +2 -2
- solara/website/pages/documentation/components/__init__.py +12 -0
- solara/website/pages/documentation/components/advanced/__init__.py +9 -0
- solara/website/pages/documentation/components/data/__init__.py +9 -0
- solara/website/pages/documentation/components/enterprise/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/enterprise}/avatar.py +1 -2
- solara/website/pages/{api → documentation/components/enterprise}/avatar_menu.py +1 -2
- solara/website/pages/documentation/components/input/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/input}/checkbox.py +1 -2
- solara/website/pages/documentation/components/input/file_drop.py +75 -0
- solara/website/pages/{api → documentation/components/input}/input.py +1 -2
- solara/website/pages/{api → documentation/components/input}/select.py +1 -2
- solara/website/pages/{api → documentation/components/input}/slider.py +1 -2
- solara/website/pages/{api → documentation/components/input}/switch.py +1 -2
- solara/website/pages/{api → documentation/components/input}/togglebuttons.py +1 -2
- solara/website/pages/documentation/components/lab/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/lab}/chat.py +2 -3
- solara/website/pages/{api → documentation/components/lab}/cookies_headers.py +1 -1
- solara/website/pages/{api → documentation/components/lab}/input_date.py +1 -2
- solara/website/pages/{api → documentation/components/lab}/menu.py +1 -2
- solara/website/pages/{api → documentation/components/lab}/task.py +1 -2
- solara/website/pages/{api → documentation/components/lab}/theming.py +1 -2
- solara/website/pages/{api → documentation/components/lab}/use_task.py +1 -2
- solara/website/pages/documentation/components/layout/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/layout}/app_bar.py +1 -2
- solara/website/pages/{api → documentation/components/layout}/app_bar_title.py +1 -2
- solara/website/pages/{api → documentation/components/layout}/card.py +1 -2
- solara/website/pages/{api → documentation/components/layout}/card_actions.py +1 -2
- solara/website/pages/{api → documentation/components/layout}/griddraggable.py +1 -1
- solara/website/pages/{api → documentation/components/layout}/gridfixed.py +1 -1
- solara/website/pages/{api → documentation/components/layout}/hbox.py +1 -1
- solara/website/pages/{api → documentation/components/layout}/vbox.py +1 -1
- solara/website/pages/documentation/components/output/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/output}/file_download.py +1 -2
- solara/website/pages/{api → documentation/components/output}/image.py +1 -2
- solara/website/pages/{api → documentation/components/output}/tooltip.py +1 -2
- solara/website/pages/documentation/components/page/__init__.py +9 -0
- solara/website/pages/documentation/components/status/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/status}/error.py +3 -3
- solara/website/pages/{api → documentation/components/status}/info.py +3 -3
- solara/website/pages/{api → documentation/components/status}/progress.py +1 -2
- solara/website/pages/{api → documentation/components/status}/spinner.py +1 -2
- solara/website/pages/{api → documentation/components/status}/success.py +3 -3
- solara/website/pages/{api → documentation/components/status}/warning.py +3 -3
- solara/website/pages/documentation/components/viz/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/viz}/plotly.py +1 -0
- solara/website/pages/{examples → documentation/examples}/__init__.py +3 -43
- solara/website/pages/{examples → documentation/examples}/general/deploy_model.py +3 -3
- solara/website/pages/{examples → documentation/examples}/general/login_oauth.py +1 -1
- solara/website/pages/{examples → documentation/examples}/general/vue_component.py +1 -2
- solara/website/pages/{examples → documentation/examples}/libraries/altair.py +2 -3
- solara/website/pages/{examples → documentation/examples}/libraries/ipyleaflet_advanced.py +1 -1
- solara/website/pages/{examples → documentation/examples}/utilities/countdown_timer.py +1 -1
- solara/website/pages/{examples → documentation/examples}/visualization/plotly.py +1 -2
- solara/website/pages/documentation/faq/__init__.py +11 -0
- solara/website/pages/{docs → documentation/getting_started}/__init__.py +7 -1
- solara/website/pages/{docs/content/03-quickstart.md → documentation/getting_started/content/00-quickstart.md} +2 -2
- solara/website/pages/{docs/content/00-introduction.md → documentation/getting_started/content/01-introduction.md} +16 -16
- solara/website/pages/{docs → documentation/getting_started}/content/02-installing.md +1 -1
- solara/website/pages/documentation/getting_started/content/04-tutorials/00-overview.md +9 -0
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/20-web-app.md +4 -4
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/30-ipywidgets.md +9 -9
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/40-streamlit.md +13 -13
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/50-dash.md +3 -3
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/_data_science.ipynb +5 -5
- solara/website/pages/documentation/getting_started/content/05-fundamentals/00-overview.md +7 -0
- solara/website/pages/{docs/content/07-fundamentals → documentation/getting_started/content/05-fundamentals}/10-components.md +2 -2
- solara/website/pages/{docs/content/07-fundamentals → documentation/getting_started/content/05-fundamentals}/50-state-management.md +3 -3
- solara/website/pages/documentation/getting_started/content/06-reference/00-overview.md +3 -0
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/41-asset-files.md +1 -1
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/60-static-site-generation.md +1 -1
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/70-search.md +1 -1
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/80-reloading.md +3 -3
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/95-caching.md +1 -1
- solara/website/pages/documentation/getting_started/content/07-deploying/00-overview.md +3 -0
- solara/website/pages/{docs/content/30-deploying → documentation/getting_started/content/07-deploying}/10-self-hosted.md +3 -3
- solara/website/pages/{docs/content/30-deploying → documentation/getting_started/content/07-deploying}/20-cloud-hosted.md +1 -1
- solara/website/pages/documentation/getting_started/content/__init__.py +0 -0
- solara/website/pages/showcase/__init__.py +1 -1
- solara/website/pages/showcase/domino_code_assist.py +1 -1
- solara/website/pages/showcase/solara_dev.py +1 -1
- 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-1.29.1.dist-info → solara-1.30.0.dist-info}/METADATA +8 -7
- solara-1.30.0.dist-info/RECORD +438 -0
- {solara-1.29.1.dist-info → solara-1.30.0.dist-info}/WHEEL +1 -1
- solara/website/pages/api/__init__.py +0 -292
- solara/website/pages/api/default_layout.py +0 -16
- solara/website/pages/api/file_drop.py +0 -36
- solara/website/pages/docs/content/04-tutorial/00-overview.md +0 -9
- solara/website/pages/docs/content/07-fundamentals/00-overview.md +0 -7
- solara/website/pages/docs/content/15-reference/00-overview.md +0 -6
- solara/website/pages/docs/content/30-deploying/00-overview.md +0 -3
- solara-1.29.1.dist-info/RECORD +0 -411
- /solara/website/pages/{docs/content/99-contact.md → contact/contact.md} +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/10-howto/00-overview.md +0 -0
- /solara/website/pages/{docs/content/10-howto/50-testing.md → documentation/advanced/content/10-howto/30-testing.md} +0 -0
- /solara/website/pages/{docs/content/10-howto/51-debugging.md → documentation/advanced/content/10-howto/31-debugging.md} +0 -0
- /solara/website/pages/{docs/content/10-howto/ipywidget_libraries.md → documentation/advanced/content/10-howto/50-ipywidget_libraries.md} +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/00-introduction.md +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/05-ipywidgets.md +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/10-reacton.md +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/17-rules-of-hooks.md +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/20-solara.md +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/60-voila.md +0 -0
- /solara/website/pages/{docs/content/50-enterprise → documentation/advanced/content/30-enterprise}/00-overview.md +0 -0
- /solara/website/pages/{docs/content/__init__.py → documentation/advanced/content/40-development/00-overview.md} +0 -0
- /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_dataframe.py +0 -0
- /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_report.py +0 -0
- /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_select.py +0 -0
- /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_slider.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_cross_filter.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_dark_effective.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_exception.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_previous.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_state_or_update.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_thread.md +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_thread.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_trait_observe.py +0 -0
- /solara/website/pages/{api → documentation/api/routing}/resolve_path.py +0 -0
- /solara/website/pages/{api → documentation/api/routing}/route.py +0 -0
- /solara/website/pages/{api → documentation/api/utilities}/memoize.py +0 -0
- /solara/website/pages/{api → documentation/components/advanced}/link.py +0 -0
- /solara/website/pages/{api → documentation/components/advanced}/meta.py +0 -0
- /solara/website/pages/{api → documentation/components/advanced}/style.py +0 -0
- /solara/website/pages/{api → documentation/components}/common.py +0 -0
- /solara/website/pages/{api → documentation/components/data}/dataframe.py +0 -0
- /solara/website/pages/{api → documentation/components/data}/pivot_table.py +0 -0
- /solara/website/pages/{api → documentation/components/input}/button.py +0 -0
- /solara/website/pages/{api → documentation/components/input}/file_browser.py +0 -0
- /solara/website/pages/{api → documentation/components/lab}/confirmation_dialog.py +0 -0
- /solara/website/pages/{api → documentation/components/lab}/tab.py +0 -0
- /solara/website/pages/{api → documentation/components/lab}/tabs.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/app_layout.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/column.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/columns.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/columns_responsive.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/row.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/sidebar.py +0 -0
- /solara/website/pages/{api → documentation/components/output}/html.py +0 -0
- /solara/website/pages/{api → documentation/components/output}/markdown.py +0 -0
- /solara/website/pages/{api → documentation/components/output}/markdown_editor.py +0 -0
- /solara/website/pages/{api → documentation/components/output}/sql_code.py +0 -0
- /solara/website/pages/{api → documentation/components/page}/head.py +0 -0
- /solara/website/pages/{api → documentation/components/page}/title.py +0 -0
- /solara/website/pages/{api → documentation/components/viz}/altair.py +0 -0
- /solara/website/pages/{api → documentation/components/viz}/echarts.py +0 -0
- /solara/website/pages/{api → documentation/components/viz}/matplotlib.py +0 -0
- /solara/website/pages/{api → documentation/components/viz}/plotly_express.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/ai/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/ai/chatbot.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/ai/tokenizer.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/basics/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/basics/sine.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/authorization.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/layout_demo.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/multipage.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/scatter.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/scrolling.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/tutorial_streamlit.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/custom_storage.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/live_update.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/mycard.vue +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/pokemon_search.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/ipycanvas.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/libraries/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/libraries/bqplot.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/libraries/ipyleaflet.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/utilities/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/utilities/calculator.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/utilities/todo.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/visualization/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/visualization/annotator.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/visualization/linked_views.py +0 -0
- /solara/website/pages/{docs → documentation/faq}/content/99-faq.md +0 -0
- /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/10_data_science.py +0 -0
- /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/60-jupyter-dashboard-part1.py +0 -0
- /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/SF_crime_sample.csv.gz +0 -0
- /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/_jupyter_dashboard_1.ipynb +0 -0
- /solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/40-static_files.md +0 -0
- /solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/90-notebook-support.md +0 -0
- /solara/website/pages/{docs/content/lab → documentation/getting_started/content/08-lab}/00-what-is-lab.md +0 -0
- /solara/website/pages/{docs → documentation/getting_started}/content/90-troubleshoot.md +0 -0
- {solara-1.29.1.data → solara-1.30.0.data}/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -0
- {solara-1.29.1.data → solara-1.30.0.data}/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -0
- {solara-1.29.1.dist-info → solara-1.30.0.dist-info}/entry_points.txt +0 -0
- {solara-1.29.1.dist-info → solara-1.30.0.dist-info}/licenses/LICENSE +0 -0
solara/components/file_drop.vue
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div ref="dropzone" class="solara-file-drop" effectAllowed="move">
|
|
3
|
-
|
|
3
|
+
<template v-if="file_info && file_info.length > 0">
|
|
4
|
+
<template v-if="multiple">
|
|
5
|
+
<div v-for="file in file_info">
|
|
6
|
+
{{ file.name }}
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
<template v-else>
|
|
10
|
+
{{ file_info[0].name }}
|
|
11
|
+
</template>
|
|
12
|
+
</template>
|
|
13
|
+
<template v-else>
|
|
14
|
+
{{ label }}
|
|
15
|
+
</template>
|
|
4
16
|
</div>
|
|
5
17
|
</template>
|
|
6
18
|
|
|
@@ -16,16 +28,17 @@ module.exports = {
|
|
|
16
28
|
event.preventDefault();
|
|
17
29
|
const items = await Promise.all([...event.dataTransfer.items]);
|
|
18
30
|
const files = items.map(i => i.webkitGetAsEntry())
|
|
19
|
-
const
|
|
20
|
-
const
|
|
31
|
+
const fileHolders = files.filter(f => f.isFile)
|
|
32
|
+
const nativeFilesPromises = fileHolders.map(fileHolder => new Promise((rs, rj) => fileHolder.file(rs, rj)))
|
|
33
|
+
const nativeFiles = await Promise.all(nativeFilesPromises)
|
|
21
34
|
|
|
22
|
-
this.native_file_info =
|
|
35
|
+
this.native_file_info = nativeFiles
|
|
23
36
|
this.file_info = this.native_file_info.map(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
({name, size}) => ({
|
|
38
|
+
name,
|
|
39
|
+
isFile: true,
|
|
40
|
+
size,
|
|
41
|
+
}));
|
|
29
42
|
});
|
|
30
43
|
},
|
|
31
44
|
methods: {
|
|
@@ -64,6 +77,7 @@ module.exports = {
|
|
|
64
77
|
height: 100px;
|
|
65
78
|
border: 1px dashed gray;
|
|
66
79
|
margin: 8px 0;
|
|
67
|
-
padding: 8px
|
|
80
|
+
padding: 8px;
|
|
81
|
+
overflow: auto;
|
|
68
82
|
}
|
|
69
83
|
</style>
|
solara/components/head.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
3
|
import reacton
|
|
4
|
-
|
|
5
4
|
import solara
|
|
6
5
|
|
|
7
6
|
|
|
@@ -9,7 +8,7 @@ import solara
|
|
|
9
8
|
def Head(children: List[reacton.core.Element] = []):
|
|
10
9
|
"""A component that manager the "head" tag of the page to avoid duplicate tags, such as titles.
|
|
11
10
|
|
|
12
|
-
Currently only supports the [title](/
|
|
11
|
+
Currently only supports the [title](/documentation/components/page/title) tag as child, e.g.:
|
|
13
12
|
|
|
14
13
|
```python
|
|
15
14
|
import solara
|
solara/components/head_tag.py
CHANGED
|
@@ -2,9 +2,8 @@ from typing import Optional
|
|
|
2
2
|
|
|
3
3
|
import ipyvuetify as vy
|
|
4
4
|
import reacton.core
|
|
5
|
-
import traitlets
|
|
6
|
-
|
|
7
5
|
import solara
|
|
6
|
+
import traitlets
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
class HeadTagWidget(vy.VuetifyTemplate):
|
|
@@ -19,7 +18,7 @@ class HeadTagWidget(vy.VuetifyTemplate):
|
|
|
19
18
|
def HeadTag(tagname: str, key=None, attributes: Optional[dict] = None):
|
|
20
19
|
"""Add a child element to head element, or replace a meta tag with the same tagname and key.
|
|
21
20
|
|
|
22
|
-
This component should be used inside a [Head](/
|
|
21
|
+
This component should be used inside a [Head](/documentation/components/page/head) component, e.g.:
|
|
23
22
|
|
|
24
23
|
```python
|
|
25
24
|
import solara
|
solara/components/link.py
CHANGED
|
@@ -2,7 +2,6 @@ from typing import Dict, List, Union
|
|
|
2
2
|
|
|
3
3
|
import ipyvue as vue
|
|
4
4
|
import reacton.ipyvue as vuer
|
|
5
|
-
|
|
6
5
|
import solara
|
|
7
6
|
|
|
8
7
|
|
|
@@ -18,8 +17,8 @@ def Link(
|
|
|
18
17
|
|
|
19
18
|
See also:
|
|
20
19
|
|
|
21
|
-
* [Multipage](/
|
|
22
|
-
* [Understanding Routing](/
|
|
20
|
+
* [Multipage](/documentation/advanced/howto/multipage).
|
|
21
|
+
* [Understanding Routing](/documentation/advanced/understanding/routing).
|
|
23
22
|
|
|
24
23
|
Most common usage is in combination with a button, e.g.:
|
|
25
24
|
|
|
@@ -32,7 +31,7 @@ def Link(
|
|
|
32
31
|
## Arguments
|
|
33
32
|
|
|
34
33
|
* path_or_route: the path or route to navigate to. Paths should be absolute, e.g. '/fruit/banana'.
|
|
35
|
-
If a route is given, [`resolve_path`](/api/resolve_path)] will be used to resolve to the absolute path.
|
|
34
|
+
If a route is given, [`resolve_path`](/documentation/api/routing/resolve_path)] will be used to resolve to the absolute path.
|
|
36
35
|
* children: the children of the link. If a child is clicked, the link will be followed.
|
|
37
36
|
* nofollow: If True, the link will not be followed by web crawlers (such as google).
|
|
38
37
|
* style: CSS styles to apply to the HTML link element. Either a string or a dictionary.
|
solara/components/meta.py
CHANGED
|
@@ -9,7 +9,7 @@ from .head_tag import HeadTag
|
|
|
9
9
|
def Meta(name: Optional[str] = None, property: Optional[str] = None, content: Optional[str] = None):
|
|
10
10
|
"""Add a meta tag to the head element, or replace a meta tag with the same name and or property.
|
|
11
11
|
|
|
12
|
-
This component should be used inside a [Head](/
|
|
12
|
+
This component should be used inside a [Head](/documentation/components/page/head) component, e.g.:
|
|
13
13
|
|
|
14
14
|
```python
|
|
15
15
|
import solara
|
solara/components/misc.py
CHANGED
|
@@ -3,7 +3,6 @@ from typing import Any, Callable, Dict, List, Union
|
|
|
3
3
|
|
|
4
4
|
import reacton
|
|
5
5
|
import reacton.ipyvuetify as v
|
|
6
|
-
|
|
7
6
|
import solara
|
|
8
7
|
import solara.widgets
|
|
9
8
|
from solara.util import _combine_classes
|
|
@@ -159,7 +158,7 @@ def HBox(children=[], grow=True, align_items="stretch", classes: List[str] = [])
|
|
|
159
158
|
def Row(children=[], gap="12px", justify="start", margin: int = 0, classes: List[str] = [], style: Union[str, Dict[str, str], None] = None):
|
|
160
159
|
"""Lays out children in a row, side by side, with the given gap between them.
|
|
161
160
|
|
|
162
|
-
See also [Column](/
|
|
161
|
+
See also [Column](/documentation/components/layout/column).
|
|
163
162
|
|
|
164
163
|
Example with three children side by side:
|
|
165
164
|
|
|
@@ -200,7 +199,7 @@ def Row(children=[], gap="12px", justify="start", margin: int = 0, classes: List
|
|
|
200
199
|
def Column(children=[], gap="12px", align="stretch", margin: int = 0, classes: List[str] = [], style: Union[str, Dict[str, str], None] = None):
|
|
201
200
|
"""Lays out children in a column on top of each other, with the given gap between them.
|
|
202
201
|
|
|
203
|
-
See also [Row](/
|
|
202
|
+
See also [Row](/documentation/components/layout/row).
|
|
204
203
|
|
|
205
204
|
Example with three children on top of each other:
|
|
206
205
|
|
|
@@ -287,17 +286,14 @@ def FigurePlotly(
|
|
|
287
286
|
"plotly_hover": on_hover,
|
|
288
287
|
"plotly_unhover": on_unhover,
|
|
289
288
|
"plotly_selected": on_selection,
|
|
290
|
-
"plotly_deselect": on_deselect
|
|
289
|
+
"plotly_deselect": on_deselect,
|
|
291
290
|
}
|
|
292
|
-
|
|
291
|
+
|
|
293
292
|
callback = event_mapping.get(event_type)
|
|
294
293
|
if callback:
|
|
295
294
|
callback(data)
|
|
296
295
|
|
|
297
|
-
fig_element = FigureWidget.element(
|
|
298
|
-
on__js2py_pointsCallback=on_points_callback,
|
|
299
|
-
on__js2py_relayout=on_relayout
|
|
300
|
-
)
|
|
296
|
+
fig_element = FigureWidget.element(on__js2py_pointsCallback=on_points_callback, on__js2py_relayout=on_relayout)
|
|
301
297
|
|
|
302
298
|
def update_data():
|
|
303
299
|
fig_widget: FigureWidget = solara.get_widget(fig_element)
|
solara/datatypes.py
CHANGED
|
@@ -114,8 +114,8 @@ class Route:
|
|
|
114
114
|
|
|
115
115
|
## See also
|
|
116
116
|
|
|
117
|
-
* [Multipage](/
|
|
118
|
-
* [Understanding Routing](/
|
|
117
|
+
* [Multipage](/documentation/advanced/howto/multipage).
|
|
118
|
+
* [Understanding Routing](/documentation/advanced/understanding/routing).
|
|
119
119
|
|
|
120
120
|
"""
|
|
121
121
|
|
solara/hooks/use_reactive.py
CHANGED
|
@@ -13,7 +13,7 @@ def use_reactive(
|
|
|
13
13
|
|
|
14
14
|
It is a useful alternative to `use_state` when you want to use a
|
|
15
15
|
reactive variable for the component state.
|
|
16
|
-
See also [our documentation on state management](/
|
|
16
|
+
See also [our documentation on state management](/documentation/getting_started/fundamentals/state-management).
|
|
17
17
|
|
|
18
18
|
If the variable passed is a reactive variable, it will be returned instead and no
|
|
19
19
|
new reactive variable will be created. This is useful for implementing component
|
|
@@ -92,7 +92,7 @@ def use_reactive(
|
|
|
92
92
|
except RuntimeError as e:
|
|
93
93
|
raise RuntimeError(
|
|
94
94
|
"use_reactive must be called from a component function, inside the render function.\n"
|
|
95
|
-
"Do not call it top level, use [solara.reactive()](https://solara.dev/api/reactive) instead."
|
|
95
|
+
"Do not call it top level, use [solara.reactive()](https://solara.dev/documentation/api/utilities/reactive) instead."
|
|
96
96
|
) from e
|
|
97
97
|
on_change_ref.current = on_change
|
|
98
98
|
|
|
@@ -60,7 +60,7 @@ def ConfirmationDialog(
|
|
|
60
60
|
):
|
|
61
61
|
"""A dialog used to confirm a user action.
|
|
62
62
|
|
|
63
|
-
(*Note: [This component is experimental and its API may change in the future](/
|
|
63
|
+
(*Note: [This component is experimental and its API may change in the future](/documentation/getting_started/lab).*)
|
|
64
64
|
|
|
65
65
|
By default, has a title, a text explaining the
|
|
66
66
|
decision to be made, and two buttons "OK" and "Cancel".
|
solara/lab/components/tabs.py
CHANGED
|
@@ -17,9 +17,9 @@ def Tab(
|
|
|
17
17
|
):
|
|
18
18
|
"""An item in a Tabs component.
|
|
19
19
|
|
|
20
|
-
(*Note: [This component is experimental and its API may change in the future](/
|
|
20
|
+
(*Note: [This component is experimental and its API may change in the future](/documentation/getting_started/lab).*)
|
|
21
21
|
|
|
22
|
-
Should be a direct child of a [Tabs](/
|
|
22
|
+
Should be a direct child of a [Tabs](/documentation/components/lab/tabs).
|
|
23
23
|
|
|
24
24
|
## Arguments
|
|
25
25
|
* `label`: The label of the tab.
|
|
@@ -62,12 +62,12 @@ def Tabs(
|
|
|
62
62
|
):
|
|
63
63
|
"""A tabbed container showing one tab at a time.
|
|
64
64
|
|
|
65
|
-
(*Note: [This component is experimental and its API may change in the future](/
|
|
65
|
+
(*Note: [This component is experimental and its API may change in the future](/documentation/getting_started/lab).*)
|
|
66
66
|
|
|
67
|
-
Note that if Tabs are used as a child of the [AppBar](/
|
|
67
|
+
Note that if Tabs are used as a child of the [AppBar](/documentation/components/layout/app_bar) component, the tabs
|
|
68
68
|
will be placed under the app bar. See our [authorization app](/apps/authorization) for an example.
|
|
69
69
|
|
|
70
|
-
If the children [Tab](/
|
|
70
|
+
If the children [Tab](/documentation/components/lab/tab) elements are passed a `path_or_route` argument, the active tab
|
|
71
71
|
will be based on the path of the current page.
|
|
72
72
|
|
|
73
73
|
|
|
@@ -89,7 +89,7 @@ def Tabs(
|
|
|
89
89
|
|
|
90
90
|
### Tabs with content
|
|
91
91
|
|
|
92
|
-
This is usually only used when the tabs are placed in the [AppBar](/
|
|
92
|
+
This is usually only used when the tabs are placed in the [AppBar](/documentation/components/layout/app_bar) component.
|
|
93
93
|
|
|
94
94
|
```solara
|
|
95
95
|
import solara
|
solara/reactive.py
CHANGED
|
@@ -14,7 +14,7 @@ def reactive(value: T) -> Reactive[T]:
|
|
|
14
14
|
Solara web applications. They provide an easy-to-use mechanism for keeping
|
|
15
15
|
track of the changing state of data and for propagating those changes to
|
|
16
16
|
the appropriate UI components. For managing local or component-specific
|
|
17
|
-
state, consider using the [`solara.use_state()`](/api/use_state) function.
|
|
17
|
+
state, consider using the [`solara.use_state()`](/documentation/api/hooks/use_state) function.
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
Reactive variables can be accessed using the `.value` attribute. To modify
|
solara/routing.py
CHANGED
|
@@ -101,7 +101,7 @@ def use_route_level():
|
|
|
101
101
|
def use_router() -> Router:
|
|
102
102
|
"""Returns the current router object.
|
|
103
103
|
|
|
104
|
-
See also [Understanding Routing](/
|
|
104
|
+
See also [Understanding Routing](/documentation/advanced/understanding/routing).
|
|
105
105
|
|
|
106
106
|
`use_router` returns the current router object. This is useful to build custom routing.
|
|
107
107
|
|
|
@@ -123,7 +123,7 @@ def use_router() -> Router:
|
|
|
123
123
|
router = solara.use_router()
|
|
124
124
|
|
|
125
125
|
def redirect():
|
|
126
|
-
router.push(f"/api/use_route")
|
|
126
|
+
router.push(f"/documentation/api/routing/use_route")
|
|
127
127
|
|
|
128
128
|
solara.Button("Navigate using an event", on_click=redirect)
|
|
129
129
|
```
|
|
@@ -138,7 +138,7 @@ def use_route(
|
|
|
138
138
|
) -> Tuple[Optional[solara.Route], List[solara.Route]]:
|
|
139
139
|
"""Returns (if found) the current route that matches the pathname, or None
|
|
140
140
|
|
|
141
|
-
See also [Understanding Routing](/
|
|
141
|
+
See also [Understanding Routing](/documentation/advanced/understanding/routing).
|
|
142
142
|
|
|
143
143
|
`use_route` returns (if found) the current route that matches the pathname, or None. It also returns all resolved routes of that level
|
|
144
144
|
(i.e. all siblings and itself). This return tuple is useful to build custom navigation (e.g. using tabs or buttons).
|
|
@@ -166,9 +166,10 @@ def use_route(
|
|
|
166
166
|
|
|
167
167
|
Note that all routes are relative, since a component does not know if it is embedded into a larger application, which may also do routing.
|
|
168
168
|
Therefore you should never use the `route.path` for navigation since the route object has no knowledge of the full url
|
|
169
|
-
(e.g. `/api/use_route/fruit/banana`) but only knows its small piece of the pathname (e.g. `banana`)
|
|
169
|
+
(e.g. `/documentation/api/routing/use_route/fruit/banana`) but only knows its small piece of the pathname (e.g. `banana`)
|
|
170
170
|
|
|
171
|
-
Use [`resolve_path`](/api/resolve_path) to request the full url for navigation,
|
|
171
|
+
Use [`resolve_path`](/documentation/api/routing/resolve_path) to request the full url for navigation,
|
|
172
|
+
or simply use the `Link` component that can do this for us.
|
|
172
173
|
|
|
173
174
|
If the current route has children, any child component that calls `use_route` will return the matched route and its siblings of our children.
|
|
174
175
|
|
|
@@ -229,12 +230,12 @@ def resolve_path(path_or_route: Union[str, solara.Route], level=0) -> str:
|
|
|
229
230
|
|
|
230
231
|
## Arguments
|
|
231
232
|
|
|
232
|
-
* path_or_route: a path string or a [`solara.Route`](/api/route) object to resolve.
|
|
233
|
+
* path_or_route: a path string or a [`solara.Route`](/documentation/api/routing/route) object to resolve.
|
|
233
234
|
|
|
234
235
|
## See also
|
|
235
236
|
|
|
236
|
-
* [Multipage](/
|
|
237
|
-
* [Understanding Routing](/
|
|
237
|
+
* [Multipage](/documentation/advanced/howto/multipage).
|
|
238
|
+
* [Understanding Routing](/documentation/advanced/understanding/routing).
|
|
238
239
|
|
|
239
240
|
|
|
240
241
|
"""
|
solara/server/app.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import dataclasses
|
|
1
2
|
import importlib.util
|
|
2
3
|
import logging
|
|
3
4
|
import os
|
|
@@ -5,6 +6,7 @@ import pickle
|
|
|
5
6
|
import sys
|
|
6
7
|
import threading
|
|
7
8
|
import traceback
|
|
9
|
+
import warnings
|
|
8
10
|
from enum import Enum
|
|
9
11
|
from pathlib import Path
|
|
10
12
|
from typing import Any, Dict, List, Optional, cast
|
|
@@ -14,6 +16,7 @@ import reacton
|
|
|
14
16
|
from reacton.core import Element, render
|
|
15
17
|
|
|
16
18
|
import solara
|
|
19
|
+
from solara.util import nested_get
|
|
17
20
|
|
|
18
21
|
from . import kernel_context, patch, reload, settings
|
|
19
22
|
from .kernel import Kernel
|
|
@@ -149,9 +152,24 @@ class AppScript:
|
|
|
149
152
|
routes = solara.generate_routes(mod)
|
|
150
153
|
|
|
151
154
|
app = solara.autorouting.RenderPage(self.app_name)
|
|
152
|
-
|
|
155
|
+
|
|
156
|
+
# when the root moduled defined routes, skip the enclosing route object
|
|
157
|
+
if len(routes) == 1 and routes[0].module and hasattr(routes[0].module, "routes"):
|
|
158
|
+
if routes[0].component:
|
|
159
|
+
warnings.warn(
|
|
160
|
+
f"{self.name} has a component defined, but you are also defining routes."
|
|
161
|
+
" To avoid confusing, consider renaming the {self.app_name} component."
|
|
162
|
+
)
|
|
153
163
|
routes = routes[0].children
|
|
154
164
|
|
|
165
|
+
if self.app_name != "Page":
|
|
166
|
+
# if we specified the app name, we replace the component
|
|
167
|
+
if len(routes) > 1:
|
|
168
|
+
raise ValueError(f"App {self.name} has multiple routes, but a default app name was given: {self.app_name}")
|
|
169
|
+
assert len(routes) == 1
|
|
170
|
+
component = nested_get(routes[0].module.__dict__, self.app_name, None)
|
|
171
|
+
routes = [dataclasses.replace(routes[0], component=component)]
|
|
172
|
+
|
|
155
173
|
if settings.ssg.build_path is None:
|
|
156
174
|
settings.ssg.build_path = self.directory.parent.resolve() / "build"
|
|
157
175
|
|
|
@@ -231,6 +249,31 @@ class AppScript:
|
|
|
231
249
|
|
|
232
250
|
solara.lab.toestand.ConnectionStore._type_counter.clear()
|
|
233
251
|
|
|
252
|
+
# we need to remove callbacks that are added in the app code
|
|
253
|
+
# which will be re-executed after the reload and we do not
|
|
254
|
+
# want to keep executing the old ones.
|
|
255
|
+
for kc in kernel_context._on_kernel_start_callbacks.copy():
|
|
256
|
+
callback, path, module, cleanup = kc
|
|
257
|
+
will_reload = False
|
|
258
|
+
if module is not None:
|
|
259
|
+
module_name = module.__name__
|
|
260
|
+
if module_name in reload.reloader.get_reload_module_names():
|
|
261
|
+
will_reload = True
|
|
262
|
+
elif path is not None:
|
|
263
|
+
if str(path.resolve()).startswith(str(self.directory)):
|
|
264
|
+
will_reload = True
|
|
265
|
+
else:
|
|
266
|
+
logger.warning(
|
|
267
|
+
"script %s is not in the same directory as the app %s but is using on_kernel_start, "
|
|
268
|
+
"this might lead to multiple entries, and might indicate a bug.",
|
|
269
|
+
path,
|
|
270
|
+
self.directory,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
if will_reload:
|
|
274
|
+
logger.info("reload: Removing on_kernel_start callback: %s (since it will be added when reloaded)", callback)
|
|
275
|
+
cleanup()
|
|
276
|
+
|
|
234
277
|
context_values = list(kernel_context.contexts.values())
|
|
235
278
|
# save states into the context so the hot reload will
|
|
236
279
|
# keep the same state
|
solara/server/assets/style.css
CHANGED
|
@@ -23,6 +23,12 @@ code::before {
|
|
|
23
23
|
box-shadow: unset !important;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
.v-application--wrap .v-application--wrap {
|
|
27
|
+
/* disable min-height: 100vh set by vuetify for nested apps */
|
|
28
|
+
min-height: unset;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
26
32
|
div.highlight pre code {
|
|
27
33
|
/* box-shadow: 0 2px 1px -1px rgb(0 0 0 / 20%), 0 1px 1px 0 rgb(0 0 0 / 14%), 0 1px 3px 0 rgb(0 0 0 / 12%); */
|
|
28
34
|
/* border: 1px #333 solid; */
|
solara/server/esm.py
CHANGED
|
@@ -2,15 +2,22 @@
|
|
|
2
2
|
# in the future, we may want to move the esm features of ipyreact
|
|
3
3
|
# into a separate package, and then we can import it unconditionally
|
|
4
4
|
import logging
|
|
5
|
+
import threading
|
|
6
|
+
from collections import defaultdict
|
|
5
7
|
from pathlib import Path
|
|
6
8
|
from typing import Dict, List, Tuple, Union
|
|
7
9
|
|
|
8
10
|
import ipyreact.importmap
|
|
9
11
|
import ipyreact.module
|
|
10
12
|
|
|
13
|
+
from solara.server import kernel_context
|
|
14
|
+
|
|
11
15
|
logger = logging.getLogger("solara.server.esm")
|
|
16
|
+
lock = threading.Lock()
|
|
12
17
|
|
|
13
18
|
_modules: Dict[str, Tuple[Union[str, Path], List[str]]] = {}
|
|
19
|
+
_modules_added_per_kernel: Dict[str, Dict[str, ipyreact.module.Module]] = defaultdict(dict)
|
|
20
|
+
_import_map_per_kernel: Dict[str, ipyreact.importmap.ImportMap] = {}
|
|
14
21
|
|
|
15
22
|
|
|
16
23
|
# in solara server, we'll monkey patch ipyreact.module with this
|
|
@@ -29,10 +36,20 @@ def get_module_names():
|
|
|
29
36
|
|
|
30
37
|
|
|
31
38
|
def create_modules():
|
|
39
|
+
kernel_id = kernel_context.get_current_context().id
|
|
40
|
+
_modules_added = _modules_added_per_kernel[kernel_id]
|
|
32
41
|
logger.info("define modules %s", _modules)
|
|
33
42
|
widgets = {}
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
with lock:
|
|
44
|
+
for name, (module, dependencies) in _modules.items():
|
|
45
|
+
if name not in _modules_added:
|
|
46
|
+
_modules_added[name] = create_module(name, module, dependencies=dependencies)
|
|
47
|
+
logger.info("create module %s %s %s", name, module, dependencies)
|
|
48
|
+
else:
|
|
49
|
+
_modules_added[name].code = module if not isinstance(module, Path) else module.read_text(encoding="utf8")
|
|
50
|
+
_modules_added[name].dependencies = dependencies
|
|
51
|
+
logger.info("update module %s %s %s", name, module, dependencies)
|
|
52
|
+
widgets[name] = _modules_added[name]
|
|
36
53
|
return widgets
|
|
37
54
|
|
|
38
55
|
|
|
@@ -41,5 +58,12 @@ def create_module(name, module: Union[str, Path], dependencies: List[str]):
|
|
|
41
58
|
|
|
42
59
|
|
|
43
60
|
def create_import_map():
|
|
44
|
-
|
|
45
|
-
|
|
61
|
+
kernel_id = kernel_context.get_current_context().id
|
|
62
|
+
with lock:
|
|
63
|
+
if kernel_id not in _import_map_per_kernel:
|
|
64
|
+
_import_map_per_kernel[kernel_id] = ipyreact.importmap.ImportMap(import_map=ipyreact.importmap._effective_import_map)
|
|
65
|
+
logger.info("create import map %s", ipyreact.importmap._effective_import_map)
|
|
66
|
+
else:
|
|
67
|
+
_import_map_per_kernel[kernel_id].import_map = ipyreact.importmap._effective_import_map
|
|
68
|
+
logger.info("update import map %s", ipyreact.importmap._effective_import_map)
|
|
69
|
+
return
|
solara/server/kernel_context.py
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
import contextvars
|
|
5
|
+
except ModuleNotFoundError:
|
|
6
|
+
contextvars = None # type: ignore
|
|
7
|
+
|
|
2
8
|
import dataclasses
|
|
3
9
|
import enum
|
|
10
|
+
import inspect
|
|
4
11
|
import logging
|
|
5
12
|
import os
|
|
6
13
|
import pickle
|
|
7
14
|
import threading
|
|
8
15
|
import time
|
|
16
|
+
import typing
|
|
9
17
|
from pathlib import Path
|
|
10
|
-
from
|
|
18
|
+
from types import FrameType, ModuleType
|
|
19
|
+
from typing import Any, Callable, Dict, List, NamedTuple, Optional, cast
|
|
11
20
|
|
|
12
21
|
import ipywidgets as widgets
|
|
13
22
|
import reacton
|
|
@@ -36,11 +45,46 @@ class PageStatus(enum.Enum):
|
|
|
36
45
|
CLOSED = "closed"
|
|
37
46
|
|
|
38
47
|
|
|
39
|
-
|
|
48
|
+
class _on_kernel_callback_entry(NamedTuple):
|
|
49
|
+
callback: Callable[[], Optional[Callable[[], None]]]
|
|
50
|
+
callpoint: Optional[Path]
|
|
51
|
+
module: Optional[ModuleType]
|
|
52
|
+
cleanup: Callable[[], None]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
_on_kernel_start_callbacks: List[_on_kernel_callback_entry] = []
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _find_root_module_frame() -> Optional[FrameType]:
|
|
59
|
+
# basically the module where the call stack origined from
|
|
60
|
+
current_frame = inspect.currentframe()
|
|
61
|
+
root_module_frame = None
|
|
62
|
+
|
|
63
|
+
while current_frame is not None:
|
|
64
|
+
if current_frame.f_code.co_name == "<module>":
|
|
65
|
+
root_module_frame = current_frame
|
|
66
|
+
break
|
|
67
|
+
current_frame = current_frame.f_back
|
|
68
|
+
|
|
69
|
+
return root_module_frame
|
|
40
70
|
|
|
41
71
|
|
|
42
|
-
def on_kernel_start(f: Callable[[], Optional[Callable[[], None]]]):
|
|
43
|
-
|
|
72
|
+
def on_kernel_start(f: Callable[[], Optional[Callable[[], None]]]) -> Callable[[], None]:
|
|
73
|
+
root = _find_root_module_frame()
|
|
74
|
+
path: Optional[Path] = None
|
|
75
|
+
module: Optional[ModuleType] = None
|
|
76
|
+
if root is not None:
|
|
77
|
+
path_str = inspect.getsourcefile(root)
|
|
78
|
+
module = inspect.getmodule(root)
|
|
79
|
+
if path_str is not None:
|
|
80
|
+
path = Path(path_str)
|
|
81
|
+
|
|
82
|
+
def cleanup():
|
|
83
|
+
return _on_kernel_start_callbacks.remove(kce)
|
|
84
|
+
|
|
85
|
+
kce = _on_kernel_callback_entry(f, path, module, cleanup)
|
|
86
|
+
_on_kernel_start_callbacks.append(kce)
|
|
87
|
+
return cleanup
|
|
44
88
|
|
|
45
89
|
|
|
46
90
|
@dataclasses.dataclass
|
|
@@ -74,7 +118,7 @@ class VirtualKernelContext:
|
|
|
74
118
|
|
|
75
119
|
def __post_init__(self):
|
|
76
120
|
with self:
|
|
77
|
-
for f in _on_kernel_start_callbacks:
|
|
121
|
+
for (f, *_) in _on_kernel_start_callbacks:
|
|
78
122
|
cleanup = f()
|
|
79
123
|
if cleanup:
|
|
80
124
|
self.on_close(cleanup)
|
|
@@ -255,12 +299,35 @@ def create_dummy_context():
|
|
|
255
299
|
return kernel_context
|
|
256
300
|
|
|
257
301
|
|
|
302
|
+
if contextvars is not None:
|
|
303
|
+
if typing.TYPE_CHECKING:
|
|
304
|
+
async_context_id = contextvars.ContextVar[str]("async_context_id")
|
|
305
|
+
else:
|
|
306
|
+
async_context_id = contextvars.ContextVar("async_context_id")
|
|
307
|
+
async_context_id.set("default")
|
|
308
|
+
else:
|
|
309
|
+
async_context_id = None
|
|
310
|
+
|
|
311
|
+
|
|
258
312
|
def get_current_thread_key() -> str:
|
|
259
|
-
|
|
260
|
-
|
|
313
|
+
if not solara.server.settings.kernel.threaded:
|
|
314
|
+
if async_context_id is not None:
|
|
315
|
+
try:
|
|
316
|
+
key = async_context_id.get()
|
|
317
|
+
except LookupError:
|
|
318
|
+
raise RuntimeError("no kernel context set")
|
|
319
|
+
else:
|
|
320
|
+
raise RuntimeError("No threading support, and no contextvars support (Python 3.6 is not supported for this)")
|
|
321
|
+
else:
|
|
322
|
+
thread = threading.current_thread()
|
|
323
|
+
key = get_thread_key(thread)
|
|
324
|
+
return key
|
|
261
325
|
|
|
262
326
|
|
|
263
327
|
def get_thread_key(thread: threading.Thread) -> str:
|
|
328
|
+
if not solara.server.settings.kernel.threaded:
|
|
329
|
+
if async_context_id is not None:
|
|
330
|
+
return async_context_id.get()
|
|
264
331
|
thread_key = thread._name + str(thread._ident) # type: ignore
|
|
265
332
|
return thread_key
|
|
266
333
|
|
|
@@ -318,6 +385,7 @@ def initialize_virtual_kernel(session_id: str, kernel_id: str, websocket: websoc
|
|
|
318
385
|
widgets.register_comm_target(kernel)
|
|
319
386
|
appmodule.register_solara_comm_target(kernel)
|
|
320
387
|
with context:
|
|
388
|
+
assert has_current_context()
|
|
321
389
|
assert kernel is Kernel.instance()
|
|
322
390
|
kernel.shell_stream = WebsocketStreamWrapper(websocket, "shell")
|
|
323
391
|
kernel.control_stream = WebsocketStreamWrapper(websocket, "control")
|
solara/server/patch.py
CHANGED
|
@@ -295,7 +295,10 @@ def _WidgetContextAwareThread__bootstrap(self):
|
|
|
295
295
|
# we need to call this manually, because set_context_for_thread
|
|
296
296
|
# uses this, and the original _bootstrap calls it too late for us
|
|
297
297
|
self._set_ident()
|
|
298
|
+
if kernel_context.async_context_id is not None:
|
|
299
|
+
kernel_context.async_context_id.set(self.current_context.id)
|
|
298
300
|
kernel_context.set_context_for_thread(self.current_context, self)
|
|
301
|
+
|
|
299
302
|
shell = self.current_context.kernel.shell
|
|
300
303
|
shell.display_pub.register_hook(shell.display_in_reacton_hook)
|
|
301
304
|
try:
|
solara/server/reload.py
CHANGED
|
@@ -152,13 +152,13 @@ class Reloader:
|
|
|
152
152
|
self._first = False
|
|
153
153
|
|
|
154
154
|
def _on_change(self, name):
|
|
155
|
-
# used for testing
|
|
156
|
-
self.reload_event_next.set()
|
|
157
155
|
# flag that we need to reload all modules next time
|
|
158
156
|
self.requires_reload = True
|
|
159
157
|
# and forward callback
|
|
160
158
|
if self.on_change:
|
|
161
159
|
self.on_change(name)
|
|
160
|
+
# used for testing
|
|
161
|
+
self.reload_event_next.set()
|
|
162
162
|
|
|
163
163
|
def close(self):
|
|
164
164
|
self.watcher.close()
|
solara/server/server.py
CHANGED
|
@@ -13,7 +13,6 @@ import ipyvue
|
|
|
13
13
|
import ipywidgets
|
|
14
14
|
import jinja2
|
|
15
15
|
import requests
|
|
16
|
-
|
|
17
16
|
import solara
|
|
18
17
|
import solara.routing
|
|
19
18
|
import solara.settings
|
|
@@ -35,6 +34,7 @@ ipykernel_major = int(ipykernel.__version__.split(".")[0])
|
|
|
35
34
|
ipywidgets_major = int(ipywidgets.__version__.split(".")[0])
|
|
36
35
|
cache_memory = solara.cache.Memory(max_items=128)
|
|
37
36
|
vue3 = ipyvue.__version__.startswith("3")
|
|
37
|
+
_redirects: Dict[str, str] = {}
|
|
38
38
|
|
|
39
39
|
# first look at the project directory, then the builtin solara directory
|
|
40
40
|
|
|
@@ -302,9 +302,9 @@ def read_root(path: str, root_path: str = "", render_kwargs={}, use_nbextensions
|
|
|
302
302
|
code = content_utf8
|
|
303
303
|
elif embed:
|
|
304
304
|
content_utf8 = content.decode("utf-8")
|
|
305
|
-
code = f
|
|
305
|
+
code = f'<style class="solara-template-css">/*\npath={path}\n*/\n{content_utf8}</style>'
|
|
306
306
|
else:
|
|
307
|
-
code = f'<link rel="stylesheet" type="text/css" href="{url}">'
|
|
307
|
+
code = f'<link rel="stylesheet" type="text/css" href="{url}" class="solara-template-css">'
|
|
308
308
|
return Markup(code)
|
|
309
309
|
|
|
310
310
|
def include_js(path: str, module=False) -> Markup:
|