solara-ui 1.31.0__py2.py3-none-any.whl → 1.32.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.
Files changed (38) hide show
  1. solara/__init__.py +1 -1
  2. solara/components/applayout.py +6 -2
  3. solara/components/datatable.py +5 -12
  4. solara/lab/hooks/dataframe.py +1 -12
  5. solara/lab/utils/dataframe.py +40 -0
  6. solara/minisettings.py +13 -5
  7. solara/server/flask.py +3 -5
  8. solara/server/kernel_context.py +110 -60
  9. solara/server/server.py +8 -4
  10. solara/server/settings.py +23 -1
  11. solara/server/starlette.py +4 -5
  12. solara/server/static/main-vuetify.js +3 -1
  13. solara/server/static/solara_bootstrap.py +1 -1
  14. solara/tasks.py +19 -10
  15. solara/toestand.py +22 -13
  16. solara/website/assets/custom.css +13 -0
  17. solara/website/components/algolia.py +6 -0
  18. solara/website/components/algolia_api.vue +2 -1
  19. solara/website/components/header.py +9 -17
  20. solara/website/components/sidebar.py +91 -0
  21. solara/website/pages/__init__.py +25 -67
  22. solara/website/pages/changelog/__init__.py +2 -0
  23. solara/website/pages/changelog/changelog.md +12 -0
  24. solara/website/pages/contact/__init__.py +2 -0
  25. solara/website/pages/documentation/__init__.py +2 -88
  26. solara/website/pages/documentation/advanced/content/10-howto/30-testing.md +267 -16
  27. solara/website/pages/documentation/advanced/content/15-reference/41-asset-files.md +36 -0
  28. solara/website/pages/documentation/advanced/content/30-enterprise/10-oauth.md +11 -2
  29. solara/website/pages/documentation/advanced/content/40-development/10-setup.md +1 -1
  30. solara/website/pages/documentation/faq/content/99-faq.md +27 -0
  31. solara/website/pages/documentation/getting_started/content/02-installing.md +2 -2
  32. solara/website/pages/documentation/getting_started/content/07-deploying/10-self-hosted.md +20 -4
  33. {solara_ui-1.31.0.dist-info → solara_ui-1.32.0.dist-info}/METADATA +2 -2
  34. {solara_ui-1.31.0.dist-info → solara_ui-1.32.0.dist-info}/RECORD +38 -36
  35. {solara_ui-1.31.0.data → solara_ui-1.32.0.data}/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -0
  36. {solara_ui-1.31.0.data → solara_ui-1.32.0.data}/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -0
  37. {solara_ui-1.31.0.dist-info → solara_ui-1.32.0.dist-info}/WHEEL +0 -0
  38. {solara_ui-1.31.0.dist-info → solara_ui-1.32.0.dist-info}/licenses/LICENSE +0 -0
solara/toestand.py CHANGED
@@ -77,7 +77,12 @@ def merge_state(d1: S, **kwargs) -> S:
77
77
  if dataclasses.is_dataclass(d1):
78
78
  return dataclasses.replace(d1, **kwargs) # type: ignore
79
79
  if "pydantic" in sys.modules and isinstance(d1, sys.modules["pydantic"].BaseModel):
80
- return type(d1)(**{**d1.dict(), **kwargs}) # type: ignore
80
+ module = sys.modules["pydantic"]
81
+ version_major = int(module.__version__.split(".")[0])
82
+ if version_major >= 2:
83
+ return d1.model_copy(update=kwargs)
84
+ else:
85
+ return d1.copy(update=kwargs)
81
86
  return cast(S, {**cast(dict, d1), **kwargs})
82
87
 
83
88
 
@@ -282,21 +287,25 @@ class KernelStoreValue(KernelStore[S]):
282
287
  return self.default_value
283
288
 
284
289
 
290
+ def _create_key_callable(f: Callable[[], S]):
291
+ try:
292
+ prefix = f.__qualname__
293
+ except Exception:
294
+ prefix = repr(f)
295
+ with KernelStore.scope_lock:
296
+ index = KernelStore._type_counter[prefix]
297
+ KernelStore._type_counter[prefix] += 1
298
+ try:
299
+ key = f.__module__ + ":" + prefix + ":" + str(index)
300
+ except Exception:
301
+ key = prefix + ":" + str(index)
302
+ return key
303
+
304
+
285
305
  class KernelStoreFactory(KernelStore[S]):
286
306
  def __init__(self, factory: Callable[[], S], key=None):
287
307
  self.factory = factory
288
- try:
289
- prefix = factory.__qualname__
290
- except Exception:
291
- prefix = repr(factory)
292
- if key is None:
293
- with KernelStore.scope_lock:
294
- index = self._type_counter[prefix]
295
- self._type_counter[prefix] += 1
296
- try:
297
- key = factory.__module__ + ":" + prefix + ":" + str(index)
298
- except Exception:
299
- key = prefix + ":" + str(index)
308
+ key = key or _create_key_callable(factory)
300
309
  super().__init__(key=key)
301
310
 
302
311
  def initial_value(self) -> S:
@@ -239,6 +239,16 @@ blockquote p:last-child {
239
239
  border-radius: 8px;
240
240
  }
241
241
 
242
+ .v-navigation-drawer__content .algolia {
243
+ padding: 0.35rem;
244
+ }
245
+
246
+ @media screen and (max-width: 960px) {
247
+ .header-logo-container{
248
+ justify-content: center !important;
249
+ }
250
+ }
251
+
242
252
  /* vuetify component overrides */
243
253
 
244
254
  .v-btn.solara-docs-button {
@@ -262,6 +272,9 @@ blockquote p:last-child {
262
272
  }
263
273
 
264
274
  /* header */
275
+ .header-logo {
276
+ display: block;
277
+ }
265
278
  .news {
266
279
  background-color: var(--color-primary);
267
280
  padding: 10px;
@@ -0,0 +1,6 @@
1
+ import solara
2
+
3
+
4
+ @solara.component_vue("algolia_api.vue")
5
+ def Algolia():
6
+ pass
@@ -12,9 +12,10 @@
12
12
  rounded
13
13
  clearable
14
14
  ref="search"
15
- style="max-width: 50%; flex-grow: 1;"
15
+ style="flex-grow: 1; max-width: 650px;"
16
16
  @click="show($event, on);"
17
17
  @keyup.enter="item = 0"
18
+ class="algolia"
18
19
  ></v-text-field>
19
20
  </template>
20
21
  <v-list v-if="results != null && results.length == 0">
@@ -4,11 +4,7 @@ import solara
4
4
  import solara.lab
5
5
  from solara.alias import rv
6
6
  from solara.server import settings
7
-
8
-
9
- @solara.component_vue("algolia_api.vue")
10
- def Algolia():
11
- pass
7
+ from .algolia import Algolia
12
8
 
13
9
 
14
10
  @solara.component
@@ -31,21 +27,17 @@ def Header(
31
27
  )
32
28
  with solara.v.AppBar(tag="header", flat=True, class_="bg-primary-fade padding-40", height="auto", clipped_left=True):
33
29
  with rv.ToolbarTitle(class_="d-flex", style_="align-items:center"):
34
- if route_current and len(route_current.children) > 0:
30
+ if route_current is not None and route_current.module is not None and hasattr(route_current.module, "Sidebar"):
35
31
  with solara.Button(icon=True, class_="hidden-md-and-up", on_click=lambda: on_toggle_left_menu and on_toggle_left_menu()):
36
32
  rv.Icon(children=["mdi-menu"])
37
- with solara.Link(path_or_route="/"):
38
- solara.Image(router.root_path + f"/static/assets/images/logo{'_white' if dark_effective else ''}.svg")
39
- rv.Spacer()
40
33
 
41
- if (
42
- route_current_with_children is not None
43
- and route_current is not None
44
- and route_current.path == "documentation"
45
- and route_current_with_children.path == "/"
34
+ with solara.Row(
35
+ justify="start", classes=["header-logo-container"], style={"flex-grow": "1", "background-color": "transparent", "align-items": "center"}
46
36
  ):
47
- solara.v.Spacer()
48
- else:
37
+ with solara.Link(path_or_route="/"):
38
+ solara.Image(router.root_path + f"/static/assets/images/logo{'_white' if dark_effective else ''}.svg", classes=["header-logo"])
39
+
40
+ with rv.Html(tag="ul", class_="main-menu menu d-none d-md-flex", style_="flex-grow: 1;"):
49
41
  if settings.search.enabled:
50
42
  from solara_enterprise.search.search import Search
51
43
 
@@ -53,7 +45,7 @@ def Header(
53
45
  else:
54
46
  with solara.Row(justify="end", style={"align-items": "center", "flex-grow": "1", "background-color": "transparent"}):
55
47
  Algolia()
56
- with rv.Html(tag="ul", class_="main-menu menu d-none d-md-flex"):
48
+
57
49
  for route in all_routes:
58
50
  if route.path in ["apps", "contact", "changelog"]:
59
51
  continue
@@ -0,0 +1,91 @@
1
+ import solara
2
+
3
+
4
+ @solara.component
5
+ def Sidebar():
6
+ route_current, all_routes = solara.use_route(-1)
7
+ router = solara.use_router()
8
+ if route_current is None:
9
+ return solara.Error("Page not found")
10
+
11
+ # Pick out documentation route, so we can use this on any page
12
+ for route in all_routes:
13
+ if route.path == "documentation":
14
+ all_routes = route.children
15
+ break
16
+
17
+ with solara.v.List(expand=True, nav=True, style_="height: 100%; display: flex; flex-direction: column;") as main:
18
+ with solara.v.ListItemGroup(v_model=router.path):
19
+ for route in all_routes:
20
+ if len(route.children) == 1 or route.path == "/":
21
+ with solara.Link("/documentation/" + route.path if route.path != "/" else "/documentation"):
22
+ with solara.v.ListItem(value="/documentation/" + route.path if route.path != "/" else "/documentation"):
23
+ if route.path == "/":
24
+ solara.v.ListItemIcon(children=[solara.v.Icon(children=["mdi-home"])])
25
+ solara.v.ListItemTitle(style_="padding: 0 20px;", children=[route.label])
26
+ else:
27
+ with solara.v.ListGroup(
28
+ v_slots=[
29
+ {
30
+ "name": "activator",
31
+ "children": solara.v.ListItemTitle(
32
+ children=[route.label],
33
+ style_="padding: 0 20px;",
34
+ ),
35
+ }
36
+ ],
37
+ value=router.path.startswith("/documentation/" + route.path),
38
+ ):
39
+ for item in route.children:
40
+ if item.path == "/":
41
+ continue
42
+ if item.children != [] and any([c.label is not None and c.path != "/" for c in item.children]):
43
+ with solara.v.ListGroup(
44
+ v_slots=[
45
+ {
46
+ "name": "activator",
47
+ "children": solara.v.ListItemTitle(
48
+ children=[item.label],
49
+ ),
50
+ }
51
+ ],
52
+ sub_group=True,
53
+ no_action=True,
54
+ value=router.path.startswith("/documentation/" + route.path + "/" + item.path),
55
+ ):
56
+ for subitem in item.children:
57
+ # skip pages that are only used to demonstrate Link or Router usage
58
+ if subitem.path == "/" or subitem.label is None:
59
+ continue
60
+ path = (
61
+ "/documentation/" + route.path + "/" + item.path + "/" + subitem.path
62
+ if item.path != "fullscreen"
63
+ else "/apps/" + subitem.path
64
+ )
65
+ with solara.Link(
66
+ path,
67
+ ):
68
+ with solara.v.ListItem(dense=True, style_="padding: 0 20px;", value=path):
69
+ solara.v.ListItemContent(
70
+ children=[subitem.label],
71
+ )
72
+ else:
73
+ with solara.v.ListItemGroup(value="/documentation/" + route.path + "/" + item.path):
74
+ with solara.Link(
75
+ "/documentation/" + route.path + "/" + item.path,
76
+ ):
77
+ with solara.v.ListItem(dense=True, style_="padding: 0 20px;"):
78
+ solara.v.ListItemContent(
79
+ children=[item.label],
80
+ )
81
+ with solara.v.ListItemGroup():
82
+ with solara.Link("/contact"):
83
+ with solara.v.ListItem():
84
+ solara.v.ListItemIcon(children=[solara.v.Icon(children=["mdi-email"])])
85
+ solara.v.ListItemTitle(style_="padding: 0 20px;", children=["Contact"])
86
+ with solara.Link("/changelog"):
87
+ with solara.v.ListItem():
88
+ solara.v.ListItemIcon(children=[solara.v.Icon(children=["mdi-history"])])
89
+ solara.v.ListItemTitle(style_="padding: 0 20px;", children=["Changelog"])
90
+
91
+ return main
@@ -1,10 +1,9 @@
1
- import contextlib
2
-
3
1
  import solara
4
2
  from solara import autorouting
5
3
  from solara.alias import rv
6
4
  from solara.components.title import Title
7
5
  from solara.server import server
6
+ from solara.website.components.algolia import Algolia
8
7
 
9
8
  from ..components import Header, Hero
10
9
  from ..components.mailchimp import MailChimp
@@ -237,39 +236,6 @@ def List(children=[], class_: str = None):
237
236
  return rv.Html(tag="ul", children=children, attributes={"class": class_})
238
237
 
239
238
 
240
- @solara.component
241
- def Sidebar():
242
- route_current, all_routes = solara.use_route()
243
- router = solara.use_router()
244
- selected = router.path
245
- with rv.Col(tag="aside", md=4, lg=3, class_="sidebar bg-grey d-none d-md-block") as main:
246
- with List():
247
- for route in all_routes:
248
- if route.children and route.data is None:
249
- path = solara.resolve_path(route.children[0])
250
- path = getattr(route.module, "redirect", path)
251
- path = getattr(route.children[0].module, "redirect", path)
252
- with solara.Link(path) if path is not None else contextlib.nullcontext():
253
- with SimpleListItem(route.label, class_="active" if path == selected else None):
254
- with List():
255
- for child in route.children[1:]:
256
- path = solara.resolve_path(child)
257
- path = getattr(child.module, "redirect", path)
258
- with solara.Link(path) if path is not None else contextlib.nullcontext():
259
- title = child.label or "no label"
260
- if callable(title):
261
- title = "Error: dynamic title"
262
- SimpleListItem(title, class_="active" if path == selected else None)
263
- else:
264
- path = solara.resolve_path(route)
265
- path = getattr(route.module, "redirect", path)
266
- if route.children:
267
- path = getattr(route.children[0].module, "redirect", path)
268
- with solara.Link(path) if path is not None else contextlib.nullcontext():
269
- SimpleListItem(route.label, class_="active" if path == selected else None)
270
- return main
271
-
272
-
273
239
  @solara.component
274
240
  def Layout(children=[]):
275
241
  router = solara.use_router()
@@ -476,17 +442,22 @@ def Layout(children=[]):
476
442
  style_="flex-wrap: nowrap; margin: 0; min-height: calc(100vh - 215.5px);",
477
443
  justify="center" if route_current is not None and route_current.path in ["documentation", "showcase"] else "start",
478
444
  ):
479
- if route_current is not None and hasattr(route_current.module, "Sidebar"):
480
- route_current.module.Sidebar() # type: ignore
481
- else:
482
- if route_current is not None and route_current.path not in ["documentation", "showcase", "contact", "changelog"]:
483
- Sidebar()
445
+ if route_current is not None and route_current.module is not None and hasattr(route_current.module, "Sidebar"):
446
+ with solara.v.NavigationDrawer(
447
+ clipped=True,
448
+ class_="d-none d-md-block",
449
+ height="unset",
450
+ style_="min-height: calc(100vh - 215.5px);",
451
+ width="20rem",
452
+ v_model=True, # Forces menu to display even if it had somehow been closed
453
+ ):
454
+ route_current.module.Sidebar()
484
455
  with rv.Col(
485
456
  tag="main",
486
457
  md=True,
487
458
  class_="pa-0",
488
459
  style_=f"""max-width: {'1024px' if route_current.path not in ['documentation', 'contact', 'changelog']
489
- else 'unset'}; overflow: hidden auto;""",
460
+ else 'unset'}; overflow-x: hidden;""",
490
461
  ):
491
462
  if route_current is not None and route_current.path == "/":
492
463
  with rv.Row(align="center"):
@@ -512,6 +483,7 @@ def Layout(children=[]):
512
483
  overlay_opacity=0.5,
513
484
  style_="height: 100vh",
514
485
  ):
486
+ Algolia()
515
487
  with rv.List(nav=True):
516
488
  with rv.ListItemGroup(active_class="text--primary"):
517
489
  for route in all_routes:
@@ -520,32 +492,18 @@ def Layout(children=[]):
520
492
  with solara.Link(route):
521
493
  solara.ListItem(route.label)
522
494
 
523
- # Drawer navigation for sidebar
524
- with rv.NavigationDrawer(
525
- v_model=show_left_menu,
526
- on_v_model=set_show_left_menu,
527
- fixed=True,
528
- absolute=True,
529
- hide_overlay=False,
530
- overlay_color="#000000",
531
- overlay_opacity=0.5,
532
- style_="height: 100vh",
533
- ):
534
- with rv.List(nav=True):
535
- current_path = router.path
536
- with rv.ListItemGroup(active_class="text--primary", v_model=current_path):
537
- for route in all_routes_sidebar:
538
- # this gets rid of the api/link child routes
539
- if len([k for k in route.children if k.label]) == 0:
540
- with solara.Link(route):
541
- solara.ListItem(route.label, value=solara.resolve_path(route))
542
- else:
543
- with solara.ListItem(route.label or "no-title", value=solara.resolve_path(route) + "_no_select"):
544
- for subroute in route.children:
545
- if subroute.label:
546
- with solara.Link(subroute):
547
- with solara.ListItem(subroute.label, value=solara.resolve_path(subroute)):
548
- pass
495
+ if route_current is not None and route_current.module is not None and hasattr(route_current.module, "Sidebar"):
496
+ with solara.v.NavigationDrawer(
497
+ absolute=True,
498
+ clipped=True,
499
+ class_="d-md-none d-block",
500
+ height="unset",
501
+ style_="min-height: 100vh;",
502
+ v_model=show_left_menu,
503
+ on_v_model=set_show_left_menu,
504
+ width="20rem",
505
+ ):
506
+ route_current.module.Sidebar()
549
507
 
550
508
  return main
551
509
 
@@ -1,8 +1,10 @@
1
1
  from pathlib import Path
2
2
 
3
3
  import solara
4
+ from solara.website.components.sidebar import Sidebar
4
5
 
5
6
  title = "Changelog"
6
7
  HERE = Path(__file__)
7
8
 
8
9
  Page = solara.Markdown(Path(HERE.parent / "changelog.md").read_text())
10
+ Sidebar = Sidebar
@@ -1,5 +1,17 @@
1
1
  # Solara Changelog
2
2
 
3
+
4
+ ## Version 1.31
5
+
6
+ We changed solara from a single package into multiple packages.
7
+
8
+ The `solara` package is a meta package that installs all the necessary dependencies to get started with Solara. By default, we install:
9
+
10
+ * [`pip install "solara-ui[all]"`](https://pypi.org/project/solara-ui)
11
+ * [`pip install "solara-server[starlette,dev]"`](https://pypi.org/project/solara-ui)
12
+
13
+ Notebook users can simply install `solara-ui` if they do not need the solara-server. Read our [installation guide](https://solara.dev/documentation/getting_started/installing) for more information.
14
+
3
15
  ## Version 1.30.1
4
16
 
5
17
  ### Details
@@ -1,8 +1,10 @@
1
1
  from pathlib import Path
2
2
 
3
3
  import solara
4
+ from solara.website.components.sidebar import Sidebar
4
5
 
5
6
  title = "Contact"
6
7
  HERE = Path(__file__)
7
8
 
8
9
  Page = solara.Markdown(Path(HERE.parent / "contact.md").read_text())
10
+ Sidebar = Sidebar
@@ -1,10 +1,12 @@
1
1
  from pathlib import Path
2
2
 
3
3
  import solara
4
+ from solara.website.components.sidebar import Sidebar
4
5
 
5
6
  _title = "Documentation"
6
7
 
7
8
  HERE = Path(__file__).parent
9
+ Sidebar = Sidebar
8
10
 
9
11
  route_order = ["/", "getting_started", "examples", "components", "api", "advanced", "faq"]
10
12
 
@@ -79,94 +81,6 @@ def Page(children=[]):
79
81
  pass
80
82
 
81
83
 
82
- @solara.component
83
- def Sidebar():
84
- route_current, all_routes = solara.use_route()
85
- router = solara.use_router()
86
- if route_current is None:
87
- return solara.Error("Page not found")
88
-
89
- with solara.v.NavigationDrawer(
90
- clipped=True, width="20rem", height="unset", style_="min-height: calc(100vh - 215.5px);", class_="d-none d-md-block"
91
- ) as main:
92
- with solara.v.List(expand=True, nav=True, style_="height: calc(100vh - 215.5px); display: flex; flex-direction: column;"):
93
- with solara.v.ListItemGroup(v_model=router.path):
94
- for route in all_routes:
95
- if len(route.children) == 1 or route.path == "/":
96
- with solara.Link("/documentation/" + route.path if route.path != "/" else "/documentation"):
97
- with solara.v.ListItem(value="/documentation/" + route.path if route.path != "/" else "/documentation"):
98
- if route.path == "/":
99
- solara.v.ListItemIcon(children=[solara.v.Icon(children=["mdi-home"])])
100
- solara.v.ListItemTitle(style_="padding: 0 20px;", children=[route.label])
101
- else:
102
- with solara.v.ListGroup(
103
- v_slots=[
104
- {
105
- "name": "activator",
106
- "children": solara.v.ListItemTitle(
107
- children=[route.label],
108
- style_="padding: 0 20px;",
109
- ),
110
- }
111
- ],
112
- value=router.path.startswith("/documentation/" + route.path),
113
- ):
114
- for item in route.children:
115
- if item.path == "/":
116
- continue
117
- if item.children != [] and any([c.label is not None and c.path != "/" for c in item.children]):
118
- with solara.v.ListGroup(
119
- v_slots=[
120
- {
121
- "name": "activator",
122
- "children": solara.v.ListItemTitle(
123
- children=[item.label],
124
- ),
125
- }
126
- ],
127
- sub_group=True,
128
- no_action=True,
129
- value=router.path.startswith("/documentation/" + route.path + "/" + item.path),
130
- ):
131
- for subitem in item.children:
132
- # skip pages that are only used to demonstrate Link or Router usage
133
- if subitem.path == "/" or subitem.label is None:
134
- continue
135
- path = (
136
- "/documentation/" + route.path + "/" + item.path + "/" + subitem.path
137
- if item.path != "fullscreen"
138
- else "/apps/" + subitem.path
139
- )
140
- with solara.Link(
141
- path,
142
- ):
143
- with solara.v.ListItem(dense=True, style_="padding: 0 20px;", value=path):
144
- solara.v.ListItemContent(
145
- children=[subitem.label],
146
- )
147
- else:
148
- with solara.v.ListItemGroup(value="/documentation/" + route.path + "/" + item.path):
149
- with solara.Link(
150
- "/documentation/" + route.path + "/" + item.path,
151
- ):
152
- with solara.v.ListItem(dense=True, style_="padding: 0 20px;"):
153
- solara.v.ListItemContent(
154
- children=[item.label],
155
- )
156
- solara.v.Spacer(style_="flex-grow: 1;")
157
- with solara.v.ListItemGroup():
158
- with solara.Link("/contact"):
159
- with solara.v.ListItem():
160
- solara.v.ListItemIcon(children=[solara.v.Icon(children=["mdi-email"])])
161
- solara.v.ListItemTitle(style_="padding: 0 20px;", children=["Contact"])
162
- with solara.Link("/changelog"):
163
- with solara.v.ListItem():
164
- solara.v.ListItemIcon(children=[solara.v.Icon(children=["mdi-history"])])
165
- solara.v.ListItemTitle(style_="padding: 0 20px;", children=["Changelog"])
166
-
167
- return main
168
-
169
-
170
84
  @solara.component
171
85
  def Layout(children=[]):
172
86
  route_current, all_routes = solara.use_route()