solara-ui 1.37.1__py2.py3-none-any.whl → 1.38.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  """Build webapps using IPywidgets"""
2
2
 
3
- __version__ = "1.37.1"
3
+ __version__ = "1.38.0"
4
4
  github_url = "https://github.com/widgetti/solara"
5
5
  git_branch = "master"
6
6
 
@@ -275,14 +275,14 @@ def CrossFilterSlider(
275
275
  with solara.HBox(align_items="center"):
276
276
  label = f"Select {column} {mode} " if not invert else f"Drop {column} {mode} "
277
277
 
278
- if py_types[column] == int:
278
+ if py_types[column] is int:
279
279
  if isinstance(filter_value, int):
280
280
  solara.SliderInt(label=label, value=filter_value, min=vmin, max=vmax, on_value=set_filter_value, disabled=not enable, thumb_label=False)
281
281
  else:
282
282
  solara.Error(f"Filter value is not an integer type, but {type(filter_value)} (value = {filter_value})")
283
283
  if filter_value is not None:
284
284
  solara.Text(f"{filter_value:,}")
285
- elif py_types[column] == float:
285
+ elif py_types[column] is float:
286
286
  if isinstance(filter_value, float):
287
287
  solara.SliderFloat(label=label, value=filter_value, min=vmin, max=vmax, on_value=set_filter_value, disabled=not enable, thumb_label=False)
288
288
  else:
solara/core.py CHANGED
@@ -29,7 +29,7 @@ def component(
29
29
  validate_hooks.HookValidator(obj).run()
30
30
  except Exception as e:
31
31
  if not isinstance(e, validate_hooks.HookValidationError):
32
- # we probably failed because of a unknown reason, but we do not want to break the user's code
32
+ # we probably failed because of an unknown reason, but we do not want to break the user's code
33
33
  warnings.warn(f"Failed to validate hooks for component {obj.__qualname__}: {e}")
34
34
  else:
35
35
  raise
solara/minisettings.py CHANGED
@@ -14,7 +14,7 @@ def _get_type(annotation):
14
14
  if annotation == Optional[check_type]:
15
15
  return check_type
16
16
  if hasattr(annotation, "__origin__"):
17
- if annotation.__origin__ == dict:
17
+ if annotation.__origin__ is dict:
18
18
  return dict
19
19
  return annotation
20
20
 
@@ -72,13 +72,13 @@ def convert(annotation, value: str) -> Any:
72
72
  annotation = sub_type
73
73
  values = value.split(",")
74
74
  return [convert(sub_type, k) for k in values]
75
- if annotation == str:
75
+ if annotation is str:
76
76
  return value
77
- elif annotation == int:
77
+ elif annotation is int:
78
78
  return int(value)
79
- elif annotation == float:
79
+ elif annotation is float:
80
80
  return float(value)
81
- elif annotation == bool:
81
+ elif annotation is bool:
82
82
  if value in ("True", "true", "1"):
83
83
  return True
84
84
  elif value in ("False", "false", "0"):
solara/server/flask.py CHANGED
@@ -199,7 +199,7 @@ def nbext(dir, filename):
199
199
  for directory in server.nbextensions_directories:
200
200
  file = directory / dir / filename
201
201
  if file.exists():
202
- return send_from_directory(directory, dir + os.path.sep + filename)
202
+ return send_from_directory(directory, dir + "/" + filename)
203
203
  return flask.Response("not found", status=404)
204
204
 
205
205
 
solara/server/patch.py CHANGED
@@ -466,6 +466,12 @@ def patch():
466
466
  os.environ["MPLBACKEND"] = "module://ipykernel.pylab.backend_inline"
467
467
  else:
468
468
  os.environ["MPLBACKEND"] = "module://matplotlib_inline.backend_inline"
469
+ # if matplotlib is already imported, we need to set the backend
470
+ # similar to how matplotlib does it in it's __init__.py
471
+ if "matplotlib" in sys.modules:
472
+ import matplotlib
473
+
474
+ matplotlib.rcParams["backend"] = os.environ.get("MPLBACKEND")
469
475
 
470
476
  # the ipyvue.Template module cannot be accessed like ipyvue.Template
471
477
  # because the import in ipvue overrides it
@@ -9,6 +9,7 @@ import threading
9
9
  import typing
10
10
  from typing import Any, Dict, List, Optional, Set, Union, cast
11
11
  from uuid import uuid4
12
+ import warnings
12
13
 
13
14
  import anyio
14
15
  import starlette.websockets
@@ -85,7 +86,11 @@ prefix = ""
85
86
  # Since starlette seems to accept really large values for http, lets do the same for websockets
86
87
  # An arbitrarily large value we settled on for now is 32kb
87
88
  # If we don't do this, users with many cookies will fail to get a websocket connection.
88
- websockets.legacy.http.MAX_LINE = 1024 * 32
89
+ ws_version = tuple(map(int, websockets.__version__.split(".")))
90
+ if ws_version[0] >= 13:
91
+ websockets.legacy.http.MAX_LINE_LENGTH = int(os.environ.get("WEBSOCKETS_MAX_LINE_LENGTH", str(1024 * 32))) # type: ignore
92
+ else:
93
+ websockets.legacy.http.MAX_LINE = 1024 * 32 # type: ignore
89
94
 
90
95
 
91
96
  class WebsocketDebugInfo:
@@ -322,17 +327,45 @@ def close(request: Request):
322
327
 
323
328
 
324
329
  async def root(request: Request, fullpath: str = ""):
330
+ forwarded_host = request.headers.get("x-forwarded-host")
331
+ forwarded_proto = request.headers.get("x-forwarded-proto")
332
+ host = request.headers.get("host")
333
+ if forwarded_proto and forwarded_proto != request.scope["scheme"]:
334
+ warnings.warn(f"""Header x-forwarded-proto={forwarded_proto!r} does not match scheme={request.scope['scheme']!r} as given by the asgi framework (probably uvicorn)
335
+
336
+ This might be a configuration mismatch behind a reverse proxy and can cause issues with redirect urls, and auth.
337
+
338
+ Most likely, you need to trust your reverse proxy server, see:
339
+ https://solara.dev/documentation/getting_started/deploying/self-hosted
340
+
341
+ If you use uvicorn (the default when you use `solara run`), make sure you
342
+ configure the following environment variables for uvicorn correctly:
343
+ UVICORN_PROXY_HEADERS=1 # only needed for uvicorn < 0.10, since it is the default after 0.10
344
+ FORWARDED_ALLOW_IPS="127.0.0.1" # 127.0.0.1 is the default, replace this by the ip of the proxy server
345
+
346
+ Make sure you replace the IP with the correct IP of the reverse proxy server (instead of 127.0.0.1).
347
+
348
+ If you are sure that only the reverse proxy can reach the solara server, you can consider setting:
349
+ FORWARDED_ALLOW_IPS="*" # This can be a security risk, only use when you know what you are doing
350
+ """)
325
351
  if settings.oauth.private and not has_auth_support:
326
352
  raise RuntimeError("SOLARA_OAUTH_PRIVATE requires solara-enterprise")
327
353
  root_path = settings.main.root_path or ""
328
354
  if not settings.main.base_url:
355
+ # Note:
356
+ # starlette does not respect x-forwarded-host, and therefore
357
+ # base_url and expected_origin below could be different
358
+ # x-forwarded-host should only be considered if the same criteria in
359
+ # uvicorn's ProxyHeadersMiddleware accepts x-forwarded-proto
329
360
  settings.main.base_url = str(request.base_url)
330
361
  # if not explicltly set,
362
+ configured_root_path = settings.main.root_path
363
+ scope = request.scope
364
+ root_path_asgi = scope.get("route_root_path", scope.get("root_path", ""))
331
365
  if settings.main.root_path is None:
332
366
  # use the default root path from the app, which seems to also include the path
333
367
  # if we are mounted under a path
334
- scope = request.scope
335
- root_path = scope.get("route_root_path", scope.get("root_path", ""))
368
+ root_path = root_path_asgi
336
369
  logger.debug("root_path: %s", root_path)
337
370
  # or use the script-name header, for instance when running under a reverse proxy
338
371
  script_name = request.headers.get("script-name")
@@ -345,6 +378,51 @@ async def root(request: Request, fullpath: str = ""):
345
378
  root_path = script_name
346
379
  settings.main.root_path = root_path
347
380
 
381
+ # lets be flexible about the trailing slash
382
+ # TODO: maybe we should be more strict about the trailing slash
383
+ naked_root_path = settings.main.root_path.rstrip("/")
384
+ naked_base_url = settings.main.base_url.rstrip("/")
385
+ if not naked_base_url.endswith(naked_root_path):
386
+ msg = f"""base url {naked_base_url!r} does not end with root path {naked_root_path!r}
387
+
388
+ This could be a configuration mismatch behind a reverse proxy and can cause issues with redirect urls, and auth.
389
+
390
+ See also https://solara.dev/documentation/getting_started/deploying/self-hosted
391
+ """
392
+ if "script-name" in request.headers:
393
+ msg += f"""It looks like the reverse proxy sets the script-name header to {request.headers['script-name']!r}
394
+ """
395
+ if "x-script-name" in request.headers:
396
+ msg += f"""It looks like the reverse proxy sets the x-script-name header to {request.headers['x-script-name']!r}
397
+ """
398
+ if configured_root_path:
399
+ msg += f"""It looks like the root path was configured to {configured_root_path!r} in the settings
400
+ """
401
+ if root_path_asgi:
402
+ msg += f"""It looks like the root path set by the asgi framework was configured to {root_path_asgi!r}
403
+ """
404
+ warnings.warn(msg)
405
+ if host and forwarded_host and forwarded_proto:
406
+ port = request.base_url.port
407
+ ports = {"http": 80, "https": 443}
408
+ expected_origin = f"{forwarded_proto}://{forwarded_host}"
409
+ if port and port != ports[forwarded_proto]:
410
+ expected_origin += f":{port}"
411
+ starlette_origin = settings.main.base_url
412
+ # strip off trailing / because we compare to the naked root path
413
+ starlette_origin = starlette_origin.rstrip("/")
414
+ if naked_root_path:
415
+ # take off the root path
416
+ starlette_origin = starlette_origin[: -len(naked_root_path)]
417
+ if starlette_origin != expected_origin:
418
+ warnings.warn(f"""Origin as determined by starlette ({starlette_origin!r}) does not match expected origin ({expected_origin!r}) based on x-forwarded-proto ({forwarded_proto!r}) and x-forwarded-host ({forwarded_host!r}) headers.
419
+
420
+ This might be a configuration mismatch behind a reverse proxy and can cause issues with redirect urls, and auth.
421
+ Most likely your proxy server sets the host header incorrectly (value for this request was {host!r})
422
+
423
+ See also https://solara.dev/documentation/getting_started/deploying/self-hosted
424
+ """)
425
+
348
426
  request_path = request.url.path
349
427
  if request_path.startswith(root_path):
350
428
  request_path = request_path[len(root_path) :]
@@ -370,7 +448,7 @@ async def root(request: Request, fullpath: str = ""):
370
448
  # however, samesite=none requires Secure https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
371
449
  # when hosted on the localhost domain we can always set the Secure flag
372
450
  # to allow samesite https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies
373
- if request.headers.get("x-forwarded-proto", "http") == "https" or request.base_url.hostname == "localhost":
451
+ if request.scope["scheme"] == "https" or request.headers.get("x-forwarded-proto", "http") == "https" or request.base_url.hostname == "localhost":
374
452
  samesite = "none"
375
453
  secure = True
376
454
  response.set_cookie(
@@ -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.37.1-py2.py3-none-any.whl", keep_going=True)
122
+ await micropip.install("/wheels/solara-1.38.0-py2.py3-none-any.whl", keep_going=True)
123
123
  import solara
124
124
 
125
125
  el = solara.Warning("lala")
solara/server/utils.py CHANGED
@@ -3,6 +3,7 @@ import logging
3
3
  import os
4
4
  from pathlib import Path
5
5
  import pdb
6
+ import sys
6
7
  import traceback
7
8
 
8
9
  from rich import print
@@ -20,7 +21,13 @@ def start_error(title, msg, exception: Exception = None):
20
21
  def path_is_child_of(path: Path, parent: Path) -> bool:
21
22
  # We use os.path.normpath() because we do not want to follow symlinks
22
23
  # in editable installs, since some packages are symlinked
23
- return os.path.normpath(path).startswith(os.path.normpath(parent))
24
+ path_string = os.path.normpath(path)
25
+ parent_string = os.path.normpath(parent)
26
+ if sys.platform == "win32":
27
+ # on windows, we sometimes get different casing (only seen on CI)
28
+ path_string = path_string.lower()
29
+ parent_string = parent_string.lower()
30
+ return path_string.startswith(parent_string)
24
31
 
25
32
 
26
33
  @contextlib.contextmanager
@@ -38,6 +38,7 @@ TEST_PORT_START = int(os.environ.get("PORT", "18765")) + 100 # do not interfere
38
38
  TEST_HOST = solara.server.settings.main.host
39
39
  TIMEOUT = float(os.environ.get("SOLARA_PW_TIMEOUT", "18"))
40
40
  PYTEST_IPYWIDGETS_SOLARA_APP_WAIT_TIMEOUT = int(os.environ.get("PYTEST_IPYWIDGETS_SOLARA_APP_WAIT_TIMEOUT", "10"))
41
+ runners = os.environ.get("SOLARA_TEST_RUNNERS", "solara,voila,jupyter_lab,jupyter_notebook").split(",")
41
42
 
42
43
 
43
44
  @pytest.fixture(scope="session")
@@ -264,10 +265,13 @@ class ServerVoila(ServerBase):
264
265
  raise RuntimeError("Jupyter server already running, use lsof -i :{self.port} to find the process and kill it")
265
266
  cmd = (
266
267
  "voila --no-browser --VoilaTest.log_level=DEBUG --Voila.port_retries=0 --VoilaExecutor.timeout=240"
267
- f" --Voila.port={self.port} --show_tracebacks=True {self.notebook_path} --enable_nbextensions=True"
268
+ f' --Voila.port={self.port} --show_tracebacks=True "{self.notebook_path}" --enable_nbextensions=True'
268
269
  )
269
270
  logger.info(f"Starting Voila server at {self.base_url} with command {cmd}")
270
- args = shlex.split(cmd)
271
+ if sys.platform == "win32":
272
+ args = cmd
273
+ else:
274
+ args = shlex.split(cmd)
271
275
  self.popen = subprocess.Popen(args, shell=False, stdout=sys.stdout, stderr=sys.stderr, stdin=None)
272
276
  self.started.set()
273
277
 
@@ -294,15 +298,21 @@ class ServerJupyter(ServerBase):
294
298
  def serve(self):
295
299
  if self.has_started():
296
300
  raise RuntimeError(f"Jupyter server already running, use lsof -i :{self.port} to find the process and kill it")
297
- cmd = f'jupyter lab --port={self.port} --no-browser --ServerApp.token="" --port-retries=0 {self.notebook_path}'
301
+ cmd = f'jupyter lab --port={self.port} --no-browser --ServerApp.token="" --port-retries=0 "{self.notebook_path}"'
298
302
  logger.info(f"Starting Jupyter (lab) server at {self.base_url} with command {cmd}")
299
- args = shlex.split(cmd)
303
+ if sys.platform == "win32":
304
+ args = cmd
305
+ else:
306
+ args = shlex.split(cmd)
300
307
  self.popen = subprocess.Popen(args, shell=False, stdout=sys.stdout, stderr=sys.stderr, stdin=None)
301
308
  self.started.set()
302
309
 
303
310
 
304
311
  @pytest.fixture(scope="session")
305
312
  def voila_server(pytestconfig: Any, notebook_path):
313
+ if "voila" not in runners:
314
+ yield None
315
+ return
306
316
  port = pytestconfig.getoption("--voila-port")
307
317
  host = pytestconfig.getoption("--solara-host")
308
318
  write_notebook(["print('hello')"], notebook_path)
@@ -317,6 +327,9 @@ def voila_server(pytestconfig: Any, notebook_path):
317
327
 
318
328
  @pytest.fixture(scope="session")
319
329
  def jupyter_server(pytestconfig: Any, notebook_path):
330
+ if "jupyter_lab" not in runners and "jupyter_notebook" not in runners:
331
+ yield None
332
+ return
320
333
  port = pytestconfig.getoption("--jupyter-port")
321
334
  host = pytestconfig.getoption("--solara-host")
322
335
  write_notebook(["print('hello')"], notebook_path)
@@ -520,9 +533,6 @@ def create_runner_solara(solara_server, solara_app, page_session: "playwright.sy
520
533
  yield run
521
534
 
522
535
 
523
- runners = os.environ.get("SOLARA_TEST_RUNNERS", "solara,voila,jupyter_lab,jupyter_notebook").split(",")
524
-
525
-
526
536
  @pytest.fixture(params=runners)
527
537
  def ipywidgets_runner(
528
538
  solara_server,
solara/toestand.py CHANGED
@@ -144,7 +144,6 @@ class ValueBase(Generic[T]):
144
144
  for listener2, scope in self.listeners2[scope_id].copy():
145
145
  if scope is not None:
146
146
  scopes.add(scope)
147
- stack = contextlib.ExitStack()
148
147
  with contextlib.ExitStack() as stack:
149
148
  for scope in scopes:
150
149
  stack.enter_context(scope)
@@ -1,5 +1,9 @@
1
1
  # Solara Changelog
2
2
 
3
+ ## Version 1.37.1
4
+
5
+ * Bug Fix: The newly introduced hooks validator could emit a warning on certain call syntax. This can fail CI pipelines that do now allow warnings [c7c3b6a](https://github.com/widgetti/solara/commit/c7c3b6ab2929c4da1ffc3ab8e2e423cf0cfa7567).
6
+
3
7
  ## Version 1.37.0
4
8
 
5
9
  * Feature: We now check if a component follows the rules of hooks https://solara.dev/documentation/advanced/understanding/rules-of-hooks [#706](https://github.com/widgetti/solara/pull/706)
@@ -8,7 +12,7 @@
8
12
  * Fix: solara-server skips directories when reading extensions which might cause a startup failure otherwise [#715](https://github.com/widgetti/solara/pull/715)
9
13
 
10
14
  ## Version 1.36.0
11
- * Feature: Provide a jupyter-widgets-popout-container css class on body for doing specific styling in combination with ipypoout [85c1899](85c189919a42b2590558d9713fc3a2440f46b7c0)
15
+ * Feature: Provide a jupyter-widgets-popout-container css class on body for doing specific styling in combination with ipypoout [85c1899](https://github.com/widgetti/solara/commit/85c189919a42b2590558d9713fc3a2440f46b7c0)
12
16
 
13
17
  ## Version 1.35.1
14
18
 
@@ -20,7 +20,7 @@ def DownloadFile(file_path=file_path, url=url, expected_size=expected_size, on_d
20
20
  status = "Done 🎉"
21
21
  else:
22
22
  MEGABYTES = 2.0**20.0
23
- status = "Downloading {}... ({:6.2f}/{:6.2f} MB)".format(file_path, downloaded_size / MEGABYTES, expected_size / MEGABYTES)
23
+ status = f"Downloading {file_path}... ({downloaded_size / MEGABYTES:6.2f}/{expected_size / MEGABYTES:6.2f} MB)"
24
24
  # status = "hi"
25
25
  # return MarkdownIt(f'{status}')
26
26
  assert download.progress is not None
@@ -170,13 +170,13 @@ Please note that Python 3.6 is not supported for Solara OAuth.
170
170
 
171
171
  ### Wrong redirection
172
172
 
173
- If the redirection back to solara return to the wrong address, it might be due to solara not choosing the right default for `SOLARA_BASE_URL`. For instance this variable could be set to `SOLARA_BASE_URL=https://solara.dev` for the solara.dev server. If you application runs behind a subpath, e.g. `/myapp`, you might have to set `SOLARA_ROOT_PATH=/myapp`.
173
+ If the redirection back to solara return to the wrong address, it might be due to solara not choosing the right default for `SOLARA_BASE_URL`. This can happen in a situation where you have multiple reverse proxies that communicate via https and therefore need to set the Host header. For instance this variable could be set to `SOLARA_BASE_URL=https://solara.dev` for the solara.dev server. If you application runs behind a subpath, e.g. `/myapp`, you might have to set `SOLARA_ROOT_PATH=/myapp`.
174
174
 
175
175
 
176
176
  ### Wrong schema detected for redirect URL
177
177
 
178
- Solara needs to give the OAuth providers a redirect URL to get back to your Solara application after navigating to the OAuth provider website. For our documentation server, we ask the OAuth provider to redirect to `https://solara.dev/_solara/auth/authorize`. The protocol part (`https`) and the domain name part (`solara.dev`) or this URL is constructed from the request URL (what the browser sends to the server).
178
+ Solara needs to give the OAuth providers a redirect URL to get back to your Solara application after navigating to the OAuth provider website. For our documentation server, we ask the OAuth provider to redirect to `https://solara.dev/_solara/auth/authorize`. The protocol part (`https`) and the domain name part (`solara.dev`) of this URL is constructed from the request (what the browser sends to the server).
179
179
 
180
- If you are running Aolara behind a reverse proxy server (like nginx), make sure that the `X-Forwarded-Proto` and `Host` headers are forwarded correctly so Solara can construct the correct redirect URL to send to the OAuth provider.
180
+ If you are running Solara behind a reverse proxy server (like nginx), make sure that the `X-Forwarded-Proto` and `Host` headers are forwarded correctly, and the proxy server is trusted by uvicorn (by configuring the `FORWARDED_ALLOW_IPS` environment variable) so Solara can construct the correct redirect URL to send to the OAuth provider.
181
181
 
182
182
  See our [self hosted deployment](https://solara.dev/documentation/getting_started/deploying/self-hosted) for more information on how to configure your reverse proxy server.
@@ -69,7 +69,7 @@
69
69
  "\n",
70
70
  "\n",
71
71
  "df = px.data.iris()\n",
72
- "df\n"
72
+ "df"
73
73
  ]
74
74
  },
75
75
  {
@@ -163,6 +163,7 @@
163
163
  "columns = list(df.columns)\n",
164
164
  "x_axis = solara.reactive(\"sepal_length\")\n",
165
165
  "\n",
166
+ "\n",
166
167
  "@solara.component\n",
167
168
  "def Page():\n",
168
169
  " # Create a scatter plot by passing \"x_axis.value\" to px.scatter\n",
@@ -170,10 +171,10 @@
170
171
  " # and re-execute this function when x_axis value changes\n",
171
172
  " fig = px.scatter(df, x_axis.value, \"sepal_width\")\n",
172
173
  " solara.FigurePlotly(fig)\n",
173
- " \n",
174
+ "\n",
174
175
  " # Pass x_axis to Select component\n",
175
176
  " # The select will control the x_axis reactive variable\n",
176
- " solara.Select(label=\"X-axis\", value=x_axis, values=columns)\n"
177
+ " solara.Select(label=\"X-axis\", value=x_axis, values=columns)"
177
178
  ]
178
179
  },
179
180
  {
@@ -219,12 +220,13 @@
219
220
  "source": [
220
221
  "y_axis = solara.reactive(\"sepal_width\")\n",
221
222
  "\n",
223
+ "\n",
222
224
  "@solara.component\n",
223
225
  "def Page():\n",
224
226
  " fig = px.scatter(df, x_axis.value, y_axis.value)\n",
225
227
  " solara.FigurePlotly(fig)\n",
226
228
  " solara.Select(label=\"X-axis\", value=x_axis, values=columns)\n",
227
- " solara.Select(label=\"Y-axis\", value=y_axis, values=columns) "
229
+ " solara.Select(label=\"Y-axis\", value=y_axis, values=columns)"
228
230
  ]
229
231
  },
230
232
  {
@@ -267,8 +269,7 @@
267
269
  " solara.Select(label=\"X-axis\", value=x_axis, values=columns)\n",
268
270
  " solara.Select(label=\"Y-axis\", value=y_axis, values=columns)\n",
269
271
  " # display it pre-formatted using the backticks `` using Markdown\n",
270
- " solara.Markdown(f\"`{click_data}`\")\n",
271
- " "
272
+ " solara.Markdown(f\"`{click_data}`\")"
272
273
  ]
273
274
  },
274
275
  {
@@ -319,7 +320,7 @@
319
320
  " row_index = click_data.value[\"points\"][\"point_indexes\"][0]\n",
320
321
  " x = click_data.value[\"points\"][\"xs\"][0]\n",
321
322
  " y = click_data.value[\"points\"][\"ys\"][0]\n",
322
- " solara.Markdown(f\"`Click on index={row_index} x={x} y={y}`\")\n"
323
+ " solara.Markdown(f\"`Click on index={row_index} x={x} y={y}`\")"
323
324
  ]
324
325
  },
325
326
  {
@@ -373,8 +374,8 @@
373
374
  "\n",
374
375
  "def find_nearest_neighbours(df, xcol, ycol, x, y, n=10):\n",
375
376
  " df = df.copy()\n",
376
- " df[\"distance\"] = ((df[xcol] - x)**2 + (df[ycol] - y)**2)**0.5\n",
377
- " return df.sort_values('distance')[1:n+1]\n",
377
+ " df[\"distance\"] = ((df[xcol] - x) ** 2 + (df[ycol] - y) ** 2) ** 0.5\n",
378
+ " return df.sort_values(\"distance\")[1 : n + 1]\n",
378
379
  "\n",
379
380
  "\n",
380
381
  "@solara.component\n",
@@ -385,13 +386,12 @@
385
386
  " x = click_data.value[\"points\"][\"xs\"][0]\n",
386
387
  " y = click_data.value[\"points\"][\"ys\"][0]\n",
387
388
  "\n",
388
- " # add an indicator \n",
389
+ " # add an indicator\n",
389
390
  " fig.add_trace(px.scatter(x=[x], y=[y], text=[\"⭐️\"]).data[0])\n",
390
391
  " df_nearest = find_nearest_neighbours(df, x_axis.value, y_axis.value, x, y, n=3)\n",
391
392
  " else:\n",
392
393
  " df_nearest = None\n",
393
394
  "\n",
394
- "\n",
395
395
  " solara.FigurePlotly(fig, on_click=click_data.set)\n",
396
396
  " solara.Select(label=\"X-axis\", value=x_axis, values=columns)\n",
397
397
  " solara.Select(label=\"Y-axis\", value=y_axis, values=columns)\n",
@@ -348,8 +348,8 @@
348
348
  "from pathlib import Path\n",
349
349
  "import solara\n",
350
350
  "\n",
351
- "ROOT = Path(solara.__file__).parent / 'website' / 'pages' / 'docs' / 'content' / '04-tutorial'\n",
352
- "path = ROOT / Path('SF_crime_sample.csv.gz')\n",
351
+ "ROOT = Path(solara.__file__).parent / \"website\" / \"pages\" / \"docs\" / \"content\" / \"04-tutorial\"\n",
352
+ "path = ROOT / Path(\"SF_crime_sample.csv.gz\")\n",
353
353
  "url = \"https://raw.githubusercontent.com/widgetti/solara/master/solara/website/pages/docs/content/04-tutorial/SF_crime_sample.csv\"\n",
354
354
  "\n",
355
355
  "if path.exists():\n",
@@ -357,7 +357,7 @@
357
357
  "else:\n",
358
358
  " df_crime = pd.read_csv(url)\n",
359
359
  "\n",
360
- "df_crime\n"
360
+ "df_crime"
361
361
  ]
362
362
  },
363
363
  {
@@ -666,9 +666,9 @@
666
666
  }
667
667
  ],
668
668
  "source": [
669
- "df_crime['Category'] = df_crime['Category'].str.title()\n",
670
- "df_crime['PdDistrict'] = df_crime['PdDistrict'].str.title()\n",
671
- "df_crime\n"
669
+ "df_crime[\"Category\"] = df_crime[\"Category\"].str.title()\n",
670
+ "df_crime[\"PdDistrict\"] = df_crime[\"PdDistrict\"].str.title()\n",
671
+ "df_crime"
672
672
  ]
673
673
  },
674
674
  {
@@ -687,12 +687,12 @@
687
687
  "outputs": [],
688
688
  "source": [
689
689
  "def crime_filter(df, district_values, category_values):\n",
690
- " df_dist = df.loc[df['PdDistrict'].isin(district_values)]\n",
691
- " df_category = df_dist.loc[df_dist['Category'].isin(category_values)]\n",
690
+ " df_dist = df.loc[df[\"PdDistrict\"].isin(district_values)]\n",
691
+ " df_category = df_dist.loc[df_dist[\"Category\"].isin(category_values)]\n",
692
692
  " return df_category\n",
693
693
  "\n",
694
694
  "\n",
695
- "dff_crime = crime_filter(df_crime, ['Bayview', 'Northern'], ['Vandalism', 'Assault', 'Robbery'])\n"
695
+ "dff_crime = crime_filter(df_crime, [\"Bayview\", \"Northern\"], [\"Vandalism\", \"Assault\", \"Robbery\"])"
696
696
  ]
697
697
  },
698
698
  {
@@ -723,24 +723,26 @@
723
723
  "source": [
724
724
  "import matplotlib.pyplot as plt\n",
725
725
  "\n",
726
+ "\n",
726
727
  "def crime_charts(df):\n",
727
- " cat_unique = df['Category'].value_counts()\n",
728
+ " cat_unique = df[\"Category\"].value_counts()\n",
728
729
  " cat_unique = cat_unique.reset_index()\n",
729
- " \n",
730
- " dist_unique = df['PdDistrict'].value_counts()\n",
730
+ "\n",
731
+ " dist_unique = df[\"PdDistrict\"].value_counts()\n",
731
732
  " dist_unique = dist_unique.reset_index()\n",
732
- " \n",
733
- " fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20,10))\n",
734
733
  "\n",
735
- " ax1.bar(cat_unique['Category'], cat_unique['count'])\n",
736
- " ax1.set_title('Amount of Criminal Case Based on Category')\n",
737
- " ax2.bar(dist_unique['PdDistrict'], dist_unique['count'])\n",
738
- " ax2.set_title('Amount of Criminal Case in Selected District')\n",
734
+ " fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))\n",
735
+ "\n",
736
+ " ax1.bar(cat_unique[\"Category\"], cat_unique[\"count\"])\n",
737
+ " ax1.set_title(\"Amount of Criminal Case Based on Category\")\n",
738
+ " ax2.bar(dist_unique[\"PdDistrict\"], dist_unique[\"count\"])\n",
739
+ " ax2.set_title(\"Amount of Criminal Case in Selected District\")\n",
739
740
  "\n",
740
741
  " display(fig)\n",
741
742
  " plt.close(fig)\n",
742
743
  "\n",
743
- "crime_charts(dff_crime)\n"
744
+ "\n",
745
+ "crime_charts(dff_crime)"
744
746
  ]
745
747
  },
746
748
  {
@@ -767,22 +769,28 @@
767
769
  "def crime_map(df):\n",
768
770
  " latitude = 37.77\n",
769
771
  " longitude = -122.42\n",
770
- " \n",
772
+ "\n",
771
773
  " sanfran_map = folium.Map(location=[latitude, longitude], zoom_start=12)\n",
772
774
  "\n",
773
775
  " incidents = folium.plugins.MarkerCluster().add_to(sanfran_map)\n",
774
776
  "\n",
775
777
  " # loop through the dataframe and add each data point to the mark cluster\n",
776
- " for lat, lng, label, in zip(df.Y, df.X, df.Category):\n",
778
+ " for (\n",
779
+ " lat,\n",
780
+ " lng,\n",
781
+ " label,\n",
782
+ " ) in zip(df.Y, df.X, df.Category):\n",
777
783
  " folium.Marker(\n",
778
- " location=[lat, lng],\n",
779
- " icon=None,\n",
780
- " popup=label,\n",
784
+ " location=[lat, lng],\n",
785
+ " icon=None,\n",
786
+ " popup=label,\n",
781
787
  " ).add_to(incidents)\n",
782
- " \n",
788
+ "\n",
783
789
  " # show map\n",
784
790
  " display(sanfran_map)\n",
785
- "crime_map(dff_crime.iloc[0:50, :])\n"
791
+ "\n",
792
+ "\n",
793
+ "crime_map(dff_crime.iloc[0:50, :])"
786
794
  ]
787
795
  },
788
796
  {
@@ -805,9 +813,12 @@
805
813
  "outputs": [],
806
814
  "source": [
807
815
  "import solara\n",
808
- "districts = solara.reactive(['Bayview', 'Northern'],)\n",
809
- "categories = solara.reactive(['Vandalism', 'Assault', 'Robbery'])\n",
810
- "limit = solara.reactive(100)\n"
816
+ "\n",
817
+ "districts = solara.reactive(\n",
818
+ " [\"Bayview\", \"Northern\"],\n",
819
+ ")\n",
820
+ "categories = solara.reactive([\"Vandalism\", \"Assault\", \"Robbery\"])\n",
821
+ "limit = solara.reactive(100)"
811
822
  ]
812
823
  },
813
824
  {
@@ -833,12 +844,14 @@
833
844
  " row_count = len(dff)\n",
834
845
  " if row_count > limit.value:\n",
835
846
  " solara.Warning(f\"Only showing the first {limit.value} of {row_count:,} crimes on map\")\n",
836
- " crime_map(dff.iloc[:limit.value])\n",
847
+ " crime_map(dff.iloc[: limit.value])\n",
837
848
  " if row_count > 0:\n",
838
849
  " crime_charts(dff)\n",
839
850
  " else:\n",
840
851
  " solara.Warning(\"You filtered out all the data, no charts shown\")\n",
841
- "View()\n"
852
+ "\n",
853
+ "\n",
854
+ "View()"
842
855
  ]
843
856
  },
844
857
  {
@@ -857,7 +870,7 @@
857
870
  "outputs": [],
858
871
  "source": [
859
872
  "limit.value = 70\n",
860
- "districts.value = ['Soutern', 'Northern']\n"
873
+ "districts.value = [\"Soutern\", \"Northern\"]"
861
874
  ]
862
875
  },
863
876
  {
@@ -885,7 +898,7 @@
885
898
  "metadata": {},
886
899
  "outputs": [],
887
900
  "source": [
888
- "solara.SelectMultiple('District', all_values=[str(k) for k in df_crime['PdDistrict'].unique().tolist()], values=districts)\n"
901
+ "solara.SelectMultiple(\"District\", all_values=[str(k) for k in df_crime[\"PdDistrict\"].unique().tolist()], values=districts)"
889
902
  ]
890
903
  },
891
904
  {
@@ -907,11 +920,13 @@
907
920
  "source": [
908
921
  "@solara.component\n",
909
922
  "def Controls():\n",
910
- " solara.SelectMultiple('District', all_values=[str(k) for k in df_crime['PdDistrict'].unique().tolist()], values=districts)\n",
911
- " solara.SelectMultiple('Category', all_values=[str(k) for k in df_crime['Category'].unique().tolist()], values=categories)\n",
923
+ " solara.SelectMultiple(\"District\", all_values=[str(k) for k in df_crime[\"PdDistrict\"].unique().tolist()], values=districts)\n",
924
+ " solara.SelectMultiple(\"Category\", all_values=[str(k) for k in df_crime[\"Category\"].unique().tolist()], values=categories)\n",
912
925
  " solara.Text(\"Maximum number of rows to show on map\")\n",
913
- " solara.SliderInt('', value=limit, min=1, max=1000)\n",
914
- "Controls()\n"
926
+ " solara.SliderInt(\"\", value=limit, min=1, max=1000)\n",
927
+ "\n",
928
+ "\n",
929
+ "Controls()"
915
930
  ]
916
931
  },
917
932
  {
@@ -930,7 +945,7 @@
930
945
  "outputs": [],
931
946
  "source": [
932
947
  "# Note that we can read AND write reactive variables\n",
933
- "categories.value = [*categories.value, 'Warrants']\n"
948
+ "categories.value = [*categories.value, \"Warrants\"]"
934
949
  ]
935
950
  },
936
951
  {
@@ -957,7 +972,9 @@
957
972
  " with solara.Sidebar():\n",
958
973
  " Controls()\n",
959
974
  " View()\n",
960
- "Page()\n"
975
+ "\n",
976
+ "\n",
977
+ "Page()"
961
978
  ]
962
979
  },
963
980
  {
@@ -257,11 +257,11 @@ $ SOLARA_APP=sol.py uvicorn --workers 1 --root-path /solara -b 0.0.0.0:8765 sola
257
257
  ```
258
258
 
259
259
  In the case of an [OAuth setup](https://solara.dev/documentation/advanced/enterprise/oauth) it is important to make sure that the `X-Forwarded-Proto` and `Host` headers are forwarded correctly.
260
- If you are running uvicorn (the default if you use `solara run ...`) you will need to configure uvicorn to accept these headers using e.g.:
260
+ If you are running uvicorn (the default if you use `solara run ...`) you will need to configure uvicorn to trust these headers using e.g.:
261
261
 
262
262
  ```bash
263
- export UVICORN_PROXY_HEADERS=1
264
- export FORWARDED_ALLOW_IPS = "127.0.0.1" # If your solara-server can *only* be reached by the proxy, you can set it to "*", otherwise put in the IP of the reverse proxy
263
+ export UVICORN_PROXY_HEADERS=1 # only needed for uvicorn < 0.10, since it is the default after 0.10
264
+ export FORWARDED_ALLOW_IPS = "127.0.0.1" # 127.0.0.1 is the default, replace this by the ip of the proxy server
265
265
  ```
266
266
 
267
267
  Make sure you replace the IP with the correct IP of the reverse proxy server (instead of `127.0.0.1`). If you are sure that only the reverse proxy can reach the solara server, you can consider
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: solara-ui
3
- Version: 1.37.1
3
+ Version: 1.38.0
4
4
  Dynamic: Summary
5
5
  Project-URL: Home, https://www.github.com/widgetti/solara
6
6
  Project-URL: Documentation, https://solara.dev
@@ -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=Fwhz5Tzd-qZC9LKusjxwWZdeshUFRb4o5f8WI9RRBPg,3597
3
+ solara/__init__.py,sha256=j-Z-TmP3BAoJhQdzGoCQ9HfcvS9NqwfDGQfJV_Sdn5I,3597
4
4
  solara/__main__.py,sha256=hxMYlUg-t-KTOJSVfEHwvcTV51WHX6INpN0F_qaesUg,23672
5
5
  solara/alias.py,sha256=9vfLzud77NP8in3OID9b5mmIO8NyrnFjN2_aE0lSb1k,216
6
6
  solara/autorouting.py,sha256=iQ-jP5H-kdu1uZyLEFeiHG1IsOLZLzwKVtQPyXSgGSM,23093
@@ -8,19 +8,19 @@ solara/cache.py,sha256=rZEW_xVIj3vvajntsQDnaglniTQ90izkX8vOqe1mMvE,10500
8
8
  solara/checks.html,sha256=NZoefOKYpE6NHQJchi4WE5HkDG3xpJW0kY6TOAFHQtE,3304
9
9
  solara/checks.py,sha256=WtMzUM1HN127juk5fFV2jdsJ1qT5Ghg21wEZfiVfIFc,7563
10
10
  solara/comm.py,sha256=BhHFO1fBfFP0cOKxx_oKUr6-8UqpfGZbVaofYxIXrS8,765
11
- solara/core.py,sha256=bB9OJAmFNsWFBGepRJaGaS12xtoY2-xahmBVD7CuvRU,1429
11
+ solara/core.py,sha256=7CeEG6GDLxwAbNauKJNNLUlZhXG4VO4Gu3F9C6sA6ZM,1430
12
12
  solara/datatypes.py,sha256=xHHjObTEf4Ar3e4Euvi0sA7UWDSbNTXuyLIf_0wcDs8,4186
13
13
  solara/express.py,sha256=R0E2ewwL0m09KdoDNhF_ZF5TnC7ytty5rzM8hDykpGk,6919
14
14
  solara/kitchensink.py,sha256=RUx3kW6CQAz9PMxB1sPI03IH5xJfsaaXq3w9bBuC6rg,249
15
15
  solara/layout.py,sha256=YSsORvn-76LrVSmElS39CBnetyUL9f4hLG_a_SdH_QM,1782
16
16
  solara/lifecycle.py,sha256=acUtft_KHj0ZOv2l-X3VcQdma1Tme70jkUp6li8mbH0,1404
17
- solara/minisettings.py,sha256=YLLOnvoZsaICKfnQ8zxNHMhAEXq4HMVR_e-LAHLE2XQ,4852
17
+ solara/minisettings.py,sha256=Ys0GdWo8i44HU8ILjesZ4PdTXlzkHQdlKxTO42ng8EA,4852
18
18
  solara/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  solara/reactive.py,sha256=GKp7agR3KtUAyLaxwcrMG9yYs_zO8KbpdslFk8Ata20,2940
20
20
  solara/routing.py,sha256=G_iZKozdVoUuD-qSMyuPV6jeN4qBqujAUvekw036f88,9143
21
21
  solara/settings.py,sha256=fCi8q8QSrpL9vX8UnqNYJwyeTZ1OOyni9-uC_eaaTgU,2138
22
22
  solara/tasks.py,sha256=4qV7HRAKCQ_POHUWkg8Rzx8hiFRO82P6ypgXKb6RUSo,30187
23
- solara/toestand.py,sha256=cGVw1TAX_Kn9u2IN_GHhCPEOP8W3N0PwA-WHuvk8EgU,23570
23
+ solara/toestand.py,sha256=I7HXtr2bJt8-f5Hl0p-WPVEYqX9vIN2Em6BT46__0gY,23531
24
24
  solara/util.py,sha256=ex3Hggy-kTQa4DTEMDlhFTihzqnTfjxWCbHk0ihaZSM,8934
25
25
  solara/validate_hooks.py,sha256=F0CYDOVF_23O1apJBIk9lZMq11JmkoE3BrVVT8QvZWI,9999
26
26
  solara/components/__init__.py,sha256=kVFPHiqv0-G_jQoL5NJnYskB9l9_2Y9jxVP3fFY8cVg,2636
@@ -33,7 +33,7 @@ solara/components/code_highlight_css.py,sha256=J0fZHuEu8jeEKAq_HKbzgiMR1-VwMVnKA
33
33
  solara/components/code_highlight_css.vue,sha256=UX4jtEetV1W25Uvu8xQ-TbEaBzbp_7DlXtXDO9SdZfY,5776
34
34
  solara/components/columns.py,sha256=bGCUU9MLxkt2OiS31oLHWrEOlskxT1Xo65dBxXhmhbQ,5564
35
35
  solara/components/component_vue.py,sha256=_WNF_MMM0pENBeokFJziro3iBm67uvNlIR9D2gx53gs,4335
36
- solara/components/cross_filter.py,sha256=vb9UfvxUy-NEW2rgbB-KouCec6OKCp0gps8TujySh6w,13663
36
+ solara/components/cross_filter.py,sha256=Q5vOkerWXNCtRbSboHjXcnjoys8bHv5b7G_fH1A1lio,13663
37
37
  solara/components/dataframe.py,sha256=9Zr-usCz9UkO2xpNlDpooRGu-Bwtn49Cy4cdAaO_KGg,21033
38
38
  solara/components/datatable.py,sha256=A64-BRM3d8ZKUURbYSfHCsqqJg7MKQhFpnJ9uqCBGMg,7836
39
39
  solara/components/datatable.vue,sha256=xoIT7tS2QSKgJHt0kHRODsDx81S9SEwk1EoVM9sgFWg,7061
@@ -107,19 +107,19 @@ solara/server/app.py,sha256=JWWr2UZLxJVRCahFZUBeMwSpUn9dyswzywDuvCUfWQU,20352
107
107
  solara/server/cdn_helper.py,sha256=mjdDWL1jVed40rEagP9L5CfZwFB77yUKqcFg--u2eds,3250
108
108
  solara/server/esm.py,sha256=dX9pzTJQ6kd6qNHQgzC938O5LTowLhuATXO0Q1paz44,2951
109
109
  solara/server/fastapi.py,sha256=qVIHn0_Kxr6zWqcBWySu5nnJ6pNTSDqb4EHIh-cqH_8,93
110
- solara/server/flask.py,sha256=v3TUqZMKzijnoB9fv3xipM-kPtLziyNAV6Wiw0DLh7w,9149
110
+ solara/server/flask.py,sha256=WR7qIlJVmnYhOt5zvokiBTXWdBQhf4RUCibfx2yim-4,9141
111
111
  solara/server/jupytertools.py,sha256=cYFIUjLX7n0uuEXqWVWvmV6sV7R_MNg8ZZlabQgw8vk,1320
112
112
  solara/server/kernel.py,sha256=3mwRRBw6BOcKLACL4fCUGgtI_RZ5KTSM1MlAtRlDbmA,11092
113
113
  solara/server/kernel_context.py,sha256=RrNVAkoev6u6LZBvDfG86zyVs7eDVUsrp_4Au_FLlgY,16718
114
- solara/server/patch.py,sha256=bwIlgXJSMUEk2eMTqIXaWG3es3WiAq3e2AilFMvrZKQ,18788
114
+ solara/server/patch.py,sha256=4bZ7yq5qSvgijkQmfY90v2R9A9ktSCTY65hktGY2wes,19069
115
115
  solara/server/reload.py,sha256=BBH7QhrV1-e9RVyNE3uz1oPj1DagC3t_XSqGPNz0nJE,9747
116
116
  solara/server/server.py,sha256=aje6QmZV1ikpqSAuI_ksOWcdOkz6Za4-xCogE1g2qRw,16380
117
117
  solara/server/settings.py,sha256=8QpVW_hYe4QvSVvDMeobpUEFa_jjCAGrSKgCGzjZ3As,7340
118
118
  solara/server/shell.py,sha256=xKox0fvDxdcWleE8p2ffCkihvjLJsWn2FujMbgUjYn0,8677
119
- solara/server/starlette.py,sha256=zOyE5cdsE-JVvXuoKWv2nnkWHeUBVV3b6i9-3sK0utI,23866
119
+ solara/server/starlette.py,sha256=IZbjMQhMST24WThFtAnaWdTbq4wVE-3RwoFyqSsBz5M,28331
120
120
  solara/server/telemetry.py,sha256=GPKGA5kCIqJb76wgxQ2_U2uV_s0r-1tKqv-GVxo5hs8,6038
121
121
  solara/server/threaded.py,sha256=k9k461md4MxEFX-RLit5RpVRPFlQNwr-qp5pprT8JB0,2347
122
- solara/server/utils.py,sha256=qYoSi5adwNVdcUuGj6N7jQU9fpXYDuBpsPg7bAKOMUw,938
122
+ solara/server/utils.py,sha256=1Pa6HF8O1jsxDBcCWlVfP_9jFiUIQgqiIhT4oJ-iUmo,1207
123
123
  solara/server/websocket.py,sha256=hUYw9TeVf4vHKL8TGG4RAZGLL7rmkt1INVq5qSYRWOo,1076
124
124
  solara/server/assets/custom.css,sha256=4p04-uxHTobfr6Xkvf1iOrYiks8NciWLT_EwHZSy6jw,15
125
125
  solara/server/assets/custom.js,sha256=YT-UUYzA5uI5-enmbIsrRhofY_IJAO-HanaMwiNUEos,16
@@ -137,7 +137,7 @@ solara/server/static/highlight-dark.css,sha256=gmC3pr3x2BqJgTswNoN6AkqfEhBk24hPF
137
137
  solara/server/static/highlight.css,sha256=k8ZdT5iwrGQ5tXTQHAXuxvZrSUq3kwCdEpy3mlFoZjs,2637
138
138
  solara/server/static/main-vuetify.js,sha256=-FLlUqclZdVhCXsqawzpxtQ9vpaDA6KQdwoDKJCr_AI,8926
139
139
  solara/server/static/main.js,sha256=mcx4JNQ4Lg4pNdUIqMoZos1mZyYFS48yd_JNFFJUqIE,5679
140
- solara/server/static/solara_bootstrap.py,sha256=DVlOxL7cHNbKU7-v7V2hmQgh9kZDA_V8a9I1Z4V_-X8,3195
140
+ solara/server/static/solara_bootstrap.py,sha256=x2DaqAbQuZ9aEugW1efIYP8FghP6UoblytAujsjNzu4,3195
141
141
  solara/server/static/sun.svg,sha256=jEKBAGCr7b9zNYv0VUb7lMWKjnU2dX69_Ye_DZWGXJI,6855
142
142
  solara/server/static/webworker.js,sha256=cjAFz7-SygStHJnYlJUlJs-gE_7YQeQ-WBDcmKYyjvo,1372
143
143
  solara/server/templates/index.html.j2,sha256=JXQo1M-STFHLBOFetgG7509cAq8xUP0VAEtYDzz35fY,31
@@ -170,7 +170,7 @@ solara/template/portal/solara_portal/pages/article/__init__.py,sha256=6PgHyyeK1_
170
170
  solara/template/portal/solara_portal/pages/viz/__init__.py,sha256=l65uqBpFJ6Uh5XytXhLMnR-8G4FBnjqJg86OJX7_LO0,2858
171
171
  solara/template/portal/solara_portal/pages/viz/overview.py,sha256=GPlzFxUR0LRQhR96a_CVWquGTjHhDehL1PR03Tcm3gs,365
172
172
  solara/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
173
- solara/test/pytest_plugin.py,sha256=n0iXaY1hPXu4qeEbXvc68I4pBZrVn9dspWXw0hv5L1s,26438
173
+ solara/test/pytest_plugin.py,sha256=ubuRwz0H0KmNHGRB7GkCaM8M8FRkeVQeYq0h7Ypz_8I,26768
174
174
  solara/website/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
175
175
  solara/website/utils.py,sha256=TfoExocZ8ko2hTcA7XDKI5FfKZ4gi3JTc_f9Oi5L7Fs,855
176
176
  solara/website/assets/custom.css,sha256=yBAhh-21ycWwDC2HLDKuMbNAR9ROj3gmF1wOdoq5olM,9888
@@ -191,7 +191,7 @@ solara/website/components/markdown.py,sha256=-ueVz2IlQQlSHJlLAp4nDdQaKZI6ycZKAzR
191
191
  solara/website/components/notebook.py,sha256=MM73_c5l49SSpK63O4TYMsQ-mA43CwfhfU7VKXjfNC0,6867
192
192
  solara/website/components/sidebar.py,sha256=oPKov45G-6i1SJXtLshc4sDCTrnr47PNzvtL6tPmT5A,5668
193
193
  solara/website/pages/__init__.py,sha256=T7lJcubDDA18Ct-5lwXMcVdptj1QqyKuY6ASRe1gdGA,31602
194
- solara/website/pages/doc_use_download.py,sha256=lWx9so-xPgV19FGTFqvgnL5_IeJragj8bYmClVPfuLw,3139
194
+ solara/website/pages/doc_use_download.py,sha256=wTzDT21FRcHwfDAAxZPmHFqF5lyLvkHZ2mieZ4kfz9Y,3127
195
195
  solara/website/pages/docutils.py,sha256=2ne991oUtK4dMj-qN18NtsVYxSfCT1c6o5yYThQnxUE,736
196
196
  solara/website/pages/apps/__init__.py,sha256=7ZmplbUsDo3uYHA9WI07hA8ZgRcDYbjw7aeohlu7mHk,284
197
197
  solara/website/pages/apps/jupyter-dashboard-1.py,sha256=mDRpnz6xEO0uoc_QEe71gfnl6T--o7rUzOo6q5C4x54,3442
@@ -206,7 +206,7 @@ solara/website/pages/apps/multipage/__init__.py,sha256=zljTAXbsV8hqb67dwmx_PhzN-
206
206
  solara/website/pages/apps/multipage/page1.py,sha256=5hK0RZ8UBBOaZcPKaplbLeb0VvaerhB6m3Jn5C0afRM,872
207
207
  solara/website/pages/apps/multipage/page2.py,sha256=uRJ8YPFyKy7GR_Ii8DJSx3akb3H15rQAJZETMt9jVEk,1422
208
208
  solara/website/pages/changelog/__init__.py,sha256=iBCpD5LVrFxQtEHX116u_6vqNBktZD3AXR65eTZblFo,227
209
- solara/website/pages/changelog/changelog.md,sha256=Hc0LQmWH5oZZXEaQtv54vvcnFTdbVPCc0gbyVyWyuuA,18423
209
+ solara/website/pages/changelog/changelog.md,sha256=u9GqLFx48DXxw4BQieG_e2oG7QGRIWj0fzZ8mD2NFbk,18730
210
210
  solara/website/pages/contact/__init__.py,sha256=L6o45fHAMClqyWdHWxI6JuiwUTP-U-yXCgd3lxMUcxA,223
211
211
  solara/website/pages/contact/contact.md,sha256=zp66RGhOE_tdkRaWKZqGbuk-PnOqBWhDVvX5zSLXoHw,798
212
212
  solara/website/pages/documentation/__init__.py,sha256=zJh3kmr6OhFPbax8Igp-LN-m9cZNrs9sq9ifFuC8Kic,6003
@@ -240,7 +240,7 @@ solara/website/pages/documentation/advanced/content/20-understanding/40-routing.
240
240
  solara/website/pages/documentation/advanced/content/20-understanding/50-solara-server.md,sha256=0DaBpVnR2smTAKjgY73zlEATMsN5CK8XRr2gfg0H7GY,5933
241
241
  solara/website/pages/documentation/advanced/content/20-understanding/60-voila.md,sha256=jlL0kyzzDdHytNizBBLPx7buFFforlIDdMF724C2RLE,1132
242
242
  solara/website/pages/documentation/advanced/content/30-enterprise/00-overview.md,sha256=FsLYifZZdSZrRuL0ix0T2AFDkOkDeSBkONSOxcFp91w,380
243
- solara/website/pages/documentation/advanced/content/30-enterprise/10-oauth.md,sha256=juypsyEycVgptRPtlbsByg5kfdkF911zYZrxePMvCkM,9432
243
+ solara/website/pages/documentation/advanced/content/30-enterprise/10-oauth.md,sha256=yhY6tV2T9JDsFeP7IMIpg3n9HCwGcNR99_uO3ldDj8A,9677
244
244
  solara/website/pages/documentation/advanced/content/40-development/00-overview.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
245
245
  solara/website/pages/documentation/advanced/content/40-development/01-contribute.md,sha256=zzcRg3Jk7JSdPoybMeFzICyS1c1MqCnN7sbd1WF0-II,2880
246
246
  solara/website/pages/documentation/advanced/content/40-development/10-setup.md,sha256=fFq8dJGFYq3pZ9cpCzUmjS4vuVYUkTJ3ekqaahPafkY,2293
@@ -406,13 +406,13 @@ solara/website/pages/documentation/getting_started/content/04-tutorials/40-strea
406
406
  solara/website/pages/documentation/getting_started/content/04-tutorials/50-dash.md,sha256=jxsM17kPIgmaHqPWxtV5xC-RWMzlJfb5QEmUT1IJrtI,5399
407
407
  solara/website/pages/documentation/getting_started/content/04-tutorials/60-jupyter-dashboard-part1.py,sha256=RvhBzAs_-mpfi9MqLceC_cF0aTh8Ki4EalnQM63TkRM,3377
408
408
  solara/website/pages/documentation/getting_started/content/04-tutorials/SF_crime_sample.csv.gz,sha256=6jfiB65wgPXQwRHi4lSuGUHNULo_Me6kmIU74Kd7kxg,575276
409
- solara/website/pages/documentation/getting_started/content/04-tutorials/_data_science.ipynb,sha256=a9EdGu2zXWccH2bmY25qhu5eg2usACJle1AFdwgjDTs,14031
410
- solara/website/pages/documentation/getting_started/content/04-tutorials/_jupyter_dashboard_1.ipynb,sha256=SGM-iUPjIi03nJqAx2aIOxw_hdKnmGP-npqJBWeum9Y,57417
409
+ solara/website/pages/documentation/getting_started/content/04-tutorials/_data_science.ipynb,sha256=cdW4TwZUc7pNvDHusU27Ye88RXyoiO1nKI2Eh-lu4_M,14017
410
+ solara/website/pages/documentation/getting_started/content/04-tutorials/_jupyter_dashboard_1.ipynb,sha256=YzQwT9Wcpe4cqznymTE7n_B-_b7PtGiZVtpaUHeR9-Q,57673
411
411
  solara/website/pages/documentation/getting_started/content/05-fundamentals/00-overview.md,sha256=b_emhNMTTMB5wgUYymDQCv7-YkNA5_beRfVnZY_K9-Y,461
412
412
  solara/website/pages/documentation/getting_started/content/05-fundamentals/10-components.md,sha256=dEUb2jpkKE0J8VV10HbbpSBTmU766X74MX9rMs2s8vE,13931
413
413
  solara/website/pages/documentation/getting_started/content/05-fundamentals/50-state-management.md,sha256=2erhyogDTBzgX4kOOBbNCm2UiONcg29HmLxVvSd6LHA,4599
414
414
  solara/website/pages/documentation/getting_started/content/07-deploying/00-overview.md,sha256=frkz54EDE8o7L0LohVi1liftJDH2_FthLaNRkHRLIPU,368
415
- solara/website/pages/documentation/getting_started/content/07-deploying/10-self-hosted.md,sha256=maCleEOHZFRHyYOG09TdumdzPNNsHc2fKAtEWH3S9Bw,10363
415
+ solara/website/pages/documentation/getting_started/content/07-deploying/10-self-hosted.md,sha256=w5zBNJxeqKOJz98zpLq9pqbsLH_NdjVU9FM1mnxMwTI,10375
416
416
  solara/website/pages/documentation/getting_started/content/07-deploying/20-cloud-hosted.md,sha256=kdqdM5q0jcv86OwW7WI-MwsQjKYaj9jxENAsVxdL05U,4076
417
417
  solara/website/pages/showcase/__init__.py,sha256=_6VP_Lxomr-hQz-hceEyLKo503gmcuuxqwJQW7Ps8Vo,6326
418
418
  solara/website/pages/showcase/domino_code_assist.py,sha256=dxEbAYeZwiSx1_JHVd1dsnEqpPwiv3TcmYSonwjc-PE,2297
@@ -436,9 +436,9 @@ solara/widgets/vue/gridlayout.vue,sha256=nFZJotdznqI9tUYZ1Elv9OLA0adazxvVZAggMHH
436
436
  solara/widgets/vue/html.vue,sha256=48K5rjp0AdJDeRV6F3nOHW3J0WXPeHn55r5pGClK2fU,112
437
437
  solara/widgets/vue/navigator.vue,sha256=SLrzBI0Eiys-7maXA4e8yyD13-O5b4AnCGE9wKuJDHE,3646
438
438
  solara/widgets/vue/vegalite.vue,sha256=E3dlfhR-Ol7nqQZN6wCZC_3Tz98CJW0i_EU39mj0XHw,3986
439
- solara_ui-1.37.1.data/data/etc/jupyter/jupyter_notebook_config.d/solara.json,sha256=3UhTBQi6z7F7pPjmqXxfddv79c8VGR9H7zStDLp6AwY,115
440
- solara_ui-1.37.1.data/data/etc/jupyter/jupyter_server_config.d/solara.json,sha256=D9J-rYxAzyD5GOqWvuPjacGUVFHsYtTfZ4FUbRzRvIA,113
441
- solara_ui-1.37.1.dist-info/METADATA,sha256=jq54RNJUy26Oo3M7l2pebv77nAGo7Qv5oI8Jzd6o-jA,7284
442
- solara_ui-1.37.1.dist-info/WHEEL,sha256=L5_n4Kc1NmrSdVgbp6hdnwwVwBnoYOCnbHBRMD-qNJ4,105
443
- solara_ui-1.37.1.dist-info/licenses/LICENSE,sha256=fFJUz-CWzZ9nEc4QZKu44jMEoDr5fEW-SiqljKpD82E,1086
444
- solara_ui-1.37.1.dist-info/RECORD,,
439
+ solara_ui-1.38.0.data/data/etc/jupyter/jupyter_notebook_config.d/solara.json,sha256=3UhTBQi6z7F7pPjmqXxfddv79c8VGR9H7zStDLp6AwY,115
440
+ solara_ui-1.38.0.data/data/etc/jupyter/jupyter_server_config.d/solara.json,sha256=D9J-rYxAzyD5GOqWvuPjacGUVFHsYtTfZ4FUbRzRvIA,113
441
+ solara_ui-1.38.0.dist-info/METADATA,sha256=tfBvwmDVV6UFhPMLFYz4-736RP2kpcnXpoT34B8Ee4s,7284
442
+ solara_ui-1.38.0.dist-info/WHEEL,sha256=L5_n4Kc1NmrSdVgbp6hdnwwVwBnoYOCnbHBRMD-qNJ4,105
443
+ solara_ui-1.38.0.dist-info/licenses/LICENSE,sha256=fFJUz-CWzZ9nEc4QZKu44jMEoDr5fEW-SiqljKpD82E,1086
444
+ solara_ui-1.38.0.dist-info/RECORD,,