solara-ui 1.55.1__py3-none-any.whl → 1.57.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.
solara/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """Build webapps using IPywidgets"""
2
2
 
3
- __version__ = "1.55.1"
3
+ __version__ = "1.57.0"
4
4
  github_url = "https://github.com/widgetti/solara"
5
5
  git_branch = "master"
6
6
 
@@ -1,3 +1,4 @@
1
+ import asyncio
1
2
  import os
2
3
  from os.path import isfile, join
3
4
  from pathlib import Path
@@ -8,6 +9,11 @@ import humanize
8
9
  import ipyvuetify as vy
9
10
  import traitlets
10
11
 
12
+ try:
13
+ import watchfiles
14
+ except ModuleNotFoundError:
15
+ watchfiles = None # type: ignore
16
+
11
17
  import solara
12
18
  from solara.components import Div
13
19
 
@@ -84,6 +90,7 @@ def FileBrowser(
84
90
  on_file_name: Optional[Callable[[str], None]] = None,
85
91
  start_directory=None,
86
92
  can_select=False,
93
+ watch: bool = False,
87
94
  ):
88
95
  """File/directory browser at the server side.
89
96
 
@@ -109,6 +116,8 @@ def FileBrowser(
109
116
  * `directory_first`: If `True` directories are shown before files. Default: `False`.
110
117
  * `on_file_name`: (deprecated) Use on_file_open instead.
111
118
  * `start_directory`: (deprecated) Use directory instead.
119
+ * `watch`: If `True`, watch the current directory for file changes and automatically refresh the file list.
120
+ Requires the `watchfiles` package to be installed.
112
121
  """
113
122
  if start_directory is not None:
114
123
  directory = start_directory # pragma: no cover
@@ -122,6 +131,8 @@ def FileBrowser(
122
131
  warning, set_warning = solara.use_state(cast(Optional[str], None))
123
132
  scroll_pos_stack, set_scroll_pos_stack = solara.use_state(cast(List[int], []))
124
133
  scroll_pos, set_scroll_pos = solara.use_state(0)
134
+ # Counter to trigger re-render when files change
135
+ file_change_counter, set_file_change_counter = solara.use_state(0)
125
136
  selected_private, set_selected_private = use_reactive_or_value(
126
137
  selected,
127
138
  on_value=on_path_select if can_select else lambda x: None,
@@ -137,12 +148,48 @@ def FileBrowser(
137
148
  # if we select a file, we need to make sure the directory is correct
138
149
  # NOTE: although we expect a Path, abuse might make it a string
139
150
  if isinstance(selected_private, Path):
140
- # Note that we do not call .resolve() before using .parent, otherwise /some/path/.. will
141
- # set the current directory to /, moving up two levels.
142
- current_dir.value = selected_private.parent.resolve()
151
+ # Only sync directory from selected if the path is absolute.
152
+ # Relative paths would resolve against the process CWD, not the
153
+ # current directory shown in the FileBrowser, leading to incorrect paths.
154
+ if selected_private.is_absolute():
155
+ # Note that we do not call .resolve() before using .parent, otherwise /some/path/.. will
156
+ # set the current directory to /, moving up two levels.
157
+ current_dir.value = selected_private.parent.resolve()
143
158
 
144
159
  solara.use_effect(sync_directory_from_selected, [selected_private])
145
160
 
161
+ def watch_directory():
162
+ if not watch:
163
+ return
164
+ if not watchfiles:
165
+ logger.warning("watchfiles not installed, cannot watch directory")
166
+ return
167
+
168
+ # Check if there's a running event loop before creating the coroutine
169
+ try:
170
+ asyncio.get_running_loop()
171
+ except RuntimeError:
172
+ # No running event loop (e.g., in unit tests or non-server environments)
173
+ logger.warning("No running event loop, cannot watch directory for changes")
174
+ return
175
+
176
+ dir_to_watch = current_dir.value
177
+
178
+ async def watch_task():
179
+ try:
180
+ async for _ in watchfiles.awatch(dir_to_watch):
181
+ logger.debug("Directory %s changed, refreshing file list", dir_to_watch)
182
+ set_file_change_counter(lambda x: x + 1)
183
+ except RuntimeError:
184
+ pass # swallow the RuntimeError: Already borrowed errors from watchfiles
185
+ except Exception:
186
+ logger.exception("Error watching directory")
187
+
188
+ future = asyncio.create_task(watch_task())
189
+ return future.cancel
190
+
191
+ solara.use_effect(watch_directory, [watch, current_dir.value])
192
+
146
193
  def change_dir(new_dir: Path):
147
194
  if os.access(new_dir, os.R_OK):
148
195
  current_dir.value = new_dir
@@ -1,5 +1,6 @@
1
+ import asyncio
1
2
  from pathlib import Path
2
- from typing import BinaryIO, Callable, List, Optional, Union, cast
3
+ from typing import Any, BinaryIO, Callable, Coroutine, List, Optional, Union, cast
3
4
 
4
5
  import ipyvuetify as vy
5
6
  import ipywidgets as widgets
@@ -19,7 +20,13 @@ class FileDownloadWidget(vy.VuetifyTemplate):
19
20
 
20
21
  @solara.component
21
22
  def FileDownload(
22
- data: Union[str, bytes, BinaryIO, Callable[[], Union[str, bytes, BinaryIO]]],
23
+ data: Union[
24
+ str,
25
+ bytes,
26
+ BinaryIO,
27
+ Callable[[], Union[str, bytes, BinaryIO]],
28
+ Callable[[], Coroutine[Any, Any, Union[str, bytes, BinaryIO]]],
29
+ ],
23
30
  filename: Optional[str] = None,
24
31
  label: Optional[str] = None,
25
32
  icon_name: Optional[str] = "mdi-cloud-download-outline",
@@ -130,7 +137,7 @@ def FileDownload(
130
137
 
131
138
  ## Arguments
132
139
 
133
- * `data`: The data to download. Can be a string, bytes, or a file like object, or a function that returns one of these.
140
+ * `data`: The data to download. Can be a string, bytes, or a file like object, or a function (or coroutine function) that returns one of these.
134
141
  * `filename`: The name of the file the user will see as default when downloading (default name is "solara-download.dat").
135
142
  If a file object is provided, the filename will be extracted from the file object if possible.
136
143
  * `label`: The label of the button. If not provided, the label will be "Download: {filename}".
@@ -162,7 +169,11 @@ def FileDownload(
162
169
  def get_data() -> Optional[bytes]:
163
170
  if request_download:
164
171
  if callable(data):
165
- data_non_lazy = data()
172
+ # if it is a coroutine, we start a new event loop and run it
173
+ if asyncio.iscoroutinefunction(data):
174
+ data_non_lazy = asyncio.run(data())
175
+ else:
176
+ data_non_lazy = data()
166
177
  else:
167
178
  data_non_lazy = data
168
179
  if hasattr(data_non_lazy, "read"):
solara/server/server.py CHANGED
@@ -392,6 +392,7 @@ def read_root(
392
392
  "path": path,
393
393
  "root_path": root_path,
394
394
  "jupyter_root_path": jupyter_root_path,
395
+ "force_refresh": settings.theme.force_refresh,
395
396
  "resources": resources,
396
397
  "theme": settings.theme.dict(),
397
398
  "production": settings.main.mode == "production",
solara/server/settings.py CHANGED
@@ -39,6 +39,7 @@ class ThemeSettings(BaseSettings):
39
39
  loader: str = "solara"
40
40
  show_banner: bool = True
41
41
  title: str = "Solara ☀️"
42
+ force_refresh: bool = False
42
43
 
43
44
  class Config:
44
45
  env_prefix = "solara_theme_"
@@ -119,7 +119,7 @@ async def main():
119
119
  ]
120
120
  for dep in requirements:
121
121
  await micropip.install(dep, keep_going=True)
122
- await micropip.install("/wheels/solara-1.55.1-py2.py3-none-any.whl", keep_going=True)
122
+ await micropip.install("/wheels/solara-1.57.0-py2.py3-none-any.whl", keep_going=True)
123
123
  import solara
124
124
 
125
125
  el = solara.Warning("lala")
@@ -245,6 +245,7 @@
245
245
  solara.rootPath = {{ root_path | tojson | safe}};
246
246
  solara.jupyterRootPath = {{ jupyter_root_path | tojson | safe}};
247
247
  solara.cdn = {{ cdn | tojson | safe }};
248
+ solara.forceRefresh = {{ force_refresh | tojson | safe }};
248
249
  // the vue templates expect it to not have a trailing slash
249
250
  solara.cdn = solara.cdn.replace(/\/$/, '');
250
251
  // keep this for backwards compatibility
@@ -364,7 +365,9 @@
364
365
  }
365
366
  },
366
367
  needsRefresh: function (value) {
367
- if (this.needsRefresh && !this.cancelAutoRefresh && !solara.production) {
368
+ if(solara.forceRefresh) {
369
+ this.reload();
370
+ } else if (this.needsRefresh && !this.cancelAutoRefresh && !solara.production) {
368
371
  console.log('value for needRefresh', value);
369
372
  setTimeout(() => {
370
373
  console.log('this.autoRefreshCount', this.autoRefreshCount)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: solara-ui
3
- Version: 1.55.1
3
+ Version: 1.57.0
4
4
  Dynamic: Summary
5
5
  Project-URL: Home, https://www.github.com/widgetti/solara
6
6
  Project-URL: Documentation, https://solara.dev
@@ -27,7 +27,7 @@ License: The MIT License (MIT)
27
27
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
28
  THE SOFTWARE.
29
29
  Classifier: License :: OSI Approved :: MIT License
30
- Requires-Python: >=3.7
30
+ Requires-Python: >=3.8
31
31
  Requires-Dist: humanize
32
32
  Requires-Dist: ipyvue>=1.9.0
33
33
  Requires-Dist: ipyvuetify>=1.6.10
@@ -40,7 +40,6 @@ Requires-Dist: markdown; extra == 'all'
40
40
  Requires-Dist: numpy; extra == 'all'
41
41
  Requires-Dist: pillow; extra == 'all'
42
42
  Requires-Dist: pygments; extra == 'all'
43
- Requires-Dist: pygments==2.10; (python_version < '3.7') and extra == 'all'
44
43
  Requires-Dist: pymdown-extensions; extra == 'all'
45
44
  Provides-Extra: cache
46
45
  Requires-Dist: cachetools; extra == 'cache'
@@ -50,7 +49,6 @@ Requires-Dist: pillow; extra == 'extra'
50
49
  Provides-Extra: markdown
51
50
  Requires-Dist: markdown; extra == 'markdown'
52
51
  Requires-Dist: pygments; extra == 'markdown'
53
- Requires-Dist: pygments==2.10; (python_version < '3.7') and extra == 'markdown'
54
52
  Requires-Dist: pymdown-extensions; extra == 'markdown'
55
53
  Description-Content-Type: text/markdown
56
54
 
@@ -1,6 +1,6 @@
1
1
  prefix/etc/jupyter/jupyter_notebook_config.d/solara.json,sha256=3UhTBQi6z7F7pPjmqXxfddv79c8VGR9H7zStDLp6AwY,115
2
2
  prefix/etc/jupyter/jupyter_server_config.d/solara.json,sha256=D9J-rYxAzyD5GOqWvuPjacGUVFHsYtTfZ4FUbRzRvIA,113
3
- solara/__init__.py,sha256=-kOoajHZOJA7rILhQiR19_A8BVLw1CANxybDiFOD7sE,3647
3
+ solara/__init__.py,sha256=YfpMbibOxKDM1T7D6kwzffLEKo-WZeoGf_jo-bADrqw,3647
4
4
  solara/__main__.py,sha256=J_f3D_mgiNicU_ay_I-qn08GOViSXm04Ym0mopqh2Os,24853
5
5
  solara/_stores.py,sha256=N2Ec-61XNFXwigBx8f5QYPx7gDXenCOBdmLPXiJB45E,12320
6
6
  solara/alias.py,sha256=9vfLzud77NP8in3OID9b5mmIO8NyrnFjN2_aE0lSb1k,216
@@ -43,8 +43,8 @@ solara/components/download.vue,sha256=xdh4olwVvGkQwGNRMU5eVUW1FGvcXrYrCyjddJbvH7
43
43
  solara/components/echarts.py,sha256=aAedLqKuVJnBi3FwhSdQIgAn-w55sNb_hGSmqkosfuA,3069
44
44
  solara/components/echarts.vue,sha256=7TGmxaRUmH-LeH16jHiUzyuZgebGu_JDiWsa2DBSWW4,4056
45
45
  solara/components/figure_altair.py,sha256=t4EEwXrdoisrbV5j2MS-HBlPc5HSFCK5r4mRXvYRH9w,1150
46
- solara/components/file_browser.py,sha256=vd9BRENagoxxL-RtH5HheQm_0zFrnBiipjtNNvJkIdU,9762
47
- solara/components/file_download.py,sha256=Lil0qyiozU_Pxyb_HgnJXOumrxMeDwwavEmZZw6kAs8,7475
46
+ solara/components/file_browser.py,sha256=JKSjMOPulEY0zlgSP0jxyfV9Eg7rUX7bLMYN9v0TBfw,11721
47
+ solara/components/file_download.py,sha256=fnt6KqgGIVk1m_lwxURK82Cz3wMdxuVtTn3FOuv5p6M,7854
48
48
  solara/components/file_drop.py,sha256=BA53EZsCzzPCS8i2ZH_S1NAkmmQlTOvNEoXAt0_1l4A,5239
49
49
  solara/components/file_drop.vue,sha256=7V6YjHhoqOCVDMVPtObNwfHz2MdXLCFlNEqK1brl3zI,2243
50
50
  solara/components/file_list_widget.vue,sha256=atp-FO9tBjvyCQ_32NqeB9Rcehg03vPLD1eIROgBDDU,1860
@@ -117,8 +117,8 @@ solara/server/kernel_context.py,sha256=nOgBgchZZmZpHpHtI6_JRpxgJApZ50eKVIOk_EnyX
117
117
  solara/server/patch.py,sha256=KMoO3IK5sk-BWnwPNXhmGW4MGAgxGosgtUKMFf7oNm0,19902
118
118
  solara/server/qt.py,sha256=QdMxX2T0Ol_j3QHYwInDyT5Gy4sOhYljMPYfru5kwLg,3774
119
119
  solara/server/reload.py,sha256=BBH7QhrV1-e9RVyNE3uz1oPj1DagC3t_XSqGPNz0nJE,9747
120
- solara/server/server.py,sha256=YbOVCeho_tmYC2xcrJTzDe62H_xTpwiMuqsBWvgN_2A,17125
121
- solara/server/settings.py,sha256=TOtXN7R7Zz19APXlXpFCjh3Uk-U8u3HHpE56X09O6i0,7632
120
+ solara/server/server.py,sha256=zz-ht0RjtG6HqWApNqjZ4YHSSIKFiMYwPuOLEHrpMoY,17180
121
+ solara/server/settings.py,sha256=5Lzl5LvQuw1dejqAzYRUfgnuG-BeCi5f0EyVnq51BxU,7664
122
122
  solara/server/shell.py,sha256=eLrpTAw3j_1pU-2S7_Jzd4xkoSs_CA7Nv7w0Q7IVLUM,9333
123
123
  solara/server/starlette.py,sha256=gEBSJf-jZQ6Ep3HKIkcZNadpxgCp6VWdvlq67TjxJQ8,32934
124
124
  solara/server/telemetry.py,sha256=GPKGA5kCIqJb76wgxQ2_U2uV_s0r-1tKqv-GVxo5hs8,6038
@@ -146,7 +146,7 @@ solara/server/static/highlight-dark.css,sha256=xO8-vta9vG4s1OfJNHXWqiLWzx_gM03jo
146
146
  solara/server/static/highlight.css,sha256=k8ZdT5iwrGQ5tXTQHAXuxvZrSUq3kwCdEpy3mlFoZjs,2637
147
147
  solara/server/static/main-vuetify.js,sha256=R3qM4xMlstMpRUdRaul78p34z_Av2ONSTXksg2V9TqQ,9503
148
148
  solara/server/static/main.js,sha256=mcx4JNQ4Lg4pNdUIqMoZos1mZyYFS48yd_JNFFJUqIE,5679
149
- solara/server/static/solara_bootstrap.py,sha256=38HW-1RWpzOwrCYtVEwprLqVFFEa5-kyQQriR3990LE,3195
149
+ solara/server/static/solara_bootstrap.py,sha256=oCCjKpqoeGJ7_P-pxwMZs09xnX1mKSRocfx0nUpYkMw,3195
150
150
  solara/server/static/sun.svg,sha256=jEKBAGCr7b9zNYv0VUb7lMWKjnU2dX69_Ye_DZWGXJI,6855
151
151
  solara/server/static/webworker.js,sha256=cjAFz7-SygStHJnYlJUlJs-gE_7YQeQ-WBDcmKYyjvo,1372
152
152
  solara/server/templates/index.html.j2,sha256=JXQo1M-STFHLBOFetgG7509cAq8xUP0VAEtYDzz35fY,31
@@ -155,7 +155,7 @@ solara/server/templates/loader-plain.html,sha256=VEtMDC8MQj75o2iWJ_gR70Jp05oOoyR
155
155
  solara/server/templates/loader-solara.css,sha256=QerfzwlenkSJMq8uk_qEtoSdcI-DKMRrH9XXDEPsrUA,1670
156
156
  solara/server/templates/loader-solara.html,sha256=wFPnTevFBcRXQj6ZCZdgkOGTMwAAiOmNlgFdzdJLpXA,2744
157
157
  solara/server/templates/plain.html,sha256=yO1r2hidA3bxOclaxtI_vTZtdDTFn2KKeeoldJuinXk,2762
158
- solara/server/templates/solara.html.j2,sha256=qbhRj-KYUr-HMlUpFOblGCNXg4ko6QXArIywvpCCirM,21850
158
+ solara/server/templates/solara.html.j2,sha256=dYVPXPWVs2Y8oBr5bJQ_yUDVrg7SzAJYUBcAMYdj2T8,22009
159
159
  solara/template/button.py,sha256=HM382prl4bNLF8sLBd9Sh43ReMGedG_z_h4XN4mDYRI,311
160
160
  solara/template/markdown.py,sha256=31G3ezFooiveSrUH5afz5_nJ8SStjbx-_x5YLXv_hPk,1137
161
161
  solara/template/portal/.flake8,sha256=wxWLwamdP33Jm1mPa1sVe3uT0AZkG_wHPbY3Ci7j5-g,136
@@ -456,9 +456,9 @@ solara/widgets/vue/gridlayout.vue,sha256=LZk-YlqM7nv_7Y5TTq2xqfH1j2SLP1QOH5eiz7G
456
456
  solara/widgets/vue/html.vue,sha256=48K5rjp0AdJDeRV6F3nOHW3J0WXPeHn55r5pGClK2fU,112
457
457
  solara/widgets/vue/navigator.vue,sha256=3hhh2_sXpnsdM1vrs2nQ0bZHLCB10HhtQeYLS-tO85s,4790
458
458
  solara/widgets/vue/vegalite.vue,sha256=zhocRsUCNIRQCEbD16er5sYnuHU0YThatRHnorA3P18,4596
459
- solara_ui-1.55.1.data/data/etc/jupyter/jupyter_notebook_config.d/solara.json,sha256=3UhTBQi6z7F7pPjmqXxfddv79c8VGR9H7zStDLp6AwY,115
460
- solara_ui-1.55.1.data/data/etc/jupyter/jupyter_server_config.d/solara.json,sha256=D9J-rYxAzyD5GOqWvuPjacGUVFHsYtTfZ4FUbRzRvIA,113
461
- solara_ui-1.55.1.dist-info/METADATA,sha256=08VRBHu8t0-txUDqxhT2vJjIwQOHyhV53cQLvdQrjzY,7459
462
- solara_ui-1.55.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
463
- solara_ui-1.55.1.dist-info/licenses/LICENSE,sha256=fFJUz-CWzZ9nEc4QZKu44jMEoDr5fEW-SiqljKpD82E,1086
464
- solara_ui-1.55.1.dist-info/RECORD,,
459
+ solara_ui-1.57.0.data/data/etc/jupyter/jupyter_notebook_config.d/solara.json,sha256=3UhTBQi6z7F7pPjmqXxfddv79c8VGR9H7zStDLp6AwY,115
460
+ solara_ui-1.57.0.data/data/etc/jupyter/jupyter_server_config.d/solara.json,sha256=D9J-rYxAzyD5GOqWvuPjacGUVFHsYtTfZ4FUbRzRvIA,113
461
+ solara_ui-1.57.0.dist-info/METADATA,sha256=xVxjY3GG79bODxOZRc8unZUpjcO5zmBpvT5zykRME_k,7304
462
+ solara_ui-1.57.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
463
+ solara_ui-1.57.0.dist-info/licenses/LICENSE,sha256=fFJUz-CWzZ9nEc4QZKu44jMEoDr5fEW-SiqljKpD82E,1086
464
+ solara_ui-1.57.0.dist-info/RECORD,,