solara 1.24.0__py2.py3-none-any.whl → 1.25.1__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 +1 -1
- solara/__main__.py +4 -1
- solara/cache.py +9 -4
- solara/checks.py +9 -4
- solara/lab/components/__init__.py +1 -0
- solara/lab/components/chat.py +203 -0
- solara/minisettings.py +1 -1
- solara/server/assets/style.css +1545 -0
- solara/server/flask.py +1 -1
- solara/server/kernel.py +3 -3
- solara/server/patch.py +2 -0
- solara/server/reload.py +1 -1
- solara/server/server.py +58 -0
- solara/server/settings.py +1 -0
- solara/server/starlette.py +32 -13
- solara/server/static/solara_bootstrap.py +1 -1
- solara/server/telemetry.py +8 -3
- solara/server/templates/loader-plain.html +1 -1
- solara/server/templates/loader-solara.html +1 -1
- solara/server/templates/solara.html.j2 +20 -25
- solara/util.py +15 -2
- solara/website/components/notebook.py +44 -1
- solara/website/pages/__init__.py +3 -0
- solara/website/pages/api/__init__.py +1 -0
- solara/website/pages/api/chat.py +109 -0
- solara/website/pages/apps/jupyter-dashboard-1.py +116 -0
- solara/website/pages/apps/scatter.py +4 -4
- solara/website/pages/doc_use_download.py +1 -1
- solara/website/pages/docs/content/04-tutorial/00-overview.md +1 -0
- solara/website/pages/docs/content/04-tutorial/60-jupyter-dashboard-part1.py +18 -1
- solara/website/pages/docs/content/04-tutorial/_jupyter_dashboard_1.ipynb +607 -14
- solara/website/pages/docs/content/10-howto/ipywidget_libraries.md +1 -1
- solara/website/pages/docs/content/95-changelog.md +31 -0
- solara/website/pages/examples/ai/chatbot.py +96 -0
- solara/website/public/success.html +16 -7
- solara/website/templates/index.html.j2 +16 -15
- {solara-1.24.0.dist-info → solara-1.25.1.dist-info}/METADATA +9 -8
- {solara-1.24.0.dist-info → solara-1.25.1.dist-info}/RECORD +43 -40
- {solara-1.24.0.dist-info → solara-1.25.1.dist-info}/WHEEL +1 -1
- solara/server/assets/index.css +0 -14480
- {solara-1.24.0.data → solara-1.25.1.data}/data/prefix/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -0
- {solara-1.24.0.data → solara-1.25.1.data}/data/prefix/etc/jupyter/jupyter_server_config.d/solara.json +0 -0
- {solara-1.24.0.dist-info → solara-1.25.1.dist-info}/entry_points.txt +0 -0
- {solara-1.24.0.dist-info → solara-1.25.1.dist-info}/licenses/LICENSE +0 -0
solara/__init__.py
CHANGED
solara/__main__.py
CHANGED
|
@@ -18,6 +18,8 @@ from uvicorn.main import LEVEL_CHOICES, LOOP_CHOICES
|
|
|
18
18
|
import solara
|
|
19
19
|
from solara.server import settings
|
|
20
20
|
|
|
21
|
+
from .server import telemetry
|
|
22
|
+
|
|
21
23
|
try:
|
|
22
24
|
from solara_enterprise.ssg import ssg_crawl
|
|
23
25
|
except ImportError:
|
|
@@ -84,7 +86,7 @@ def _check_version():
|
|
|
84
86
|
def find_all_packages_paths():
|
|
85
87
|
paths = []
|
|
86
88
|
# sitepackages = set([os.path.dirname(k) for k in site.getsitepackages()])
|
|
87
|
-
sitepackages =
|
|
89
|
+
sitepackages = {k for k in site.getsitepackages()}
|
|
88
90
|
paths.extend(list(sitepackages))
|
|
89
91
|
for name, module in sys.modules.items():
|
|
90
92
|
if hasattr(module, "__path__"):
|
|
@@ -287,6 +289,7 @@ def run(
|
|
|
287
289
|
|
|
288
290
|
failed = False
|
|
289
291
|
if reload:
|
|
292
|
+
telemetry._auto_restart_enabled = True
|
|
290
293
|
solara_root = Path(solara.__file__).parent
|
|
291
294
|
|
|
292
295
|
reload_dirs = list(reload_dirs if reload_dirs else [])
|
solara/cache.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
import inspect
|
|
3
3
|
import logging
|
|
4
|
+
import sys
|
|
4
5
|
from typing import (
|
|
5
6
|
Any,
|
|
6
7
|
Callable,
|
|
@@ -15,12 +16,11 @@ from typing import (
|
|
|
15
16
|
)
|
|
16
17
|
|
|
17
18
|
import cachetools
|
|
18
|
-
import typing_extensions
|
|
19
|
-
from reacton.utils import equals
|
|
20
|
-
|
|
21
19
|
import solara
|
|
22
20
|
import solara.settings
|
|
23
21
|
import solara.util
|
|
22
|
+
import typing_extensions
|
|
23
|
+
from reacton.utils import equals
|
|
24
24
|
|
|
25
25
|
logger = logging.getLogger("solara.cache")
|
|
26
26
|
|
|
@@ -59,7 +59,12 @@ class MemoizedFunction(Generic[P, R]):
|
|
|
59
59
|
nonlocals = inspect.getclosurevars(f).nonlocals
|
|
60
60
|
if nonlocals:
|
|
61
61
|
raise ValueError(f"Memoized functions cannot depend on nonlocal variables, it now depends on {nonlocals}")
|
|
62
|
-
|
|
62
|
+
if sys.version_info[:2] < (3, 9):
|
|
63
|
+
# usedforsecurity is only available in Python 3.9+
|
|
64
|
+
codehash = hashlib.md5(f.__code__.co_code).hexdigest()
|
|
65
|
+
else:
|
|
66
|
+
codehash = hashlib.md5(f.__code__.co_code, usedforsecurity=False).hexdigest() # type: ignore
|
|
67
|
+
|
|
63
68
|
self.function_key = (f.__qualname__, codehash)
|
|
64
69
|
current_globals = dict(inspect.getclosurevars(f).globals)
|
|
65
70
|
_global_values_used.setdefault(self.function_key, current_globals)
|
solara/checks.py
CHANGED
|
@@ -19,6 +19,7 @@ logger = logging.getLogger(__name__)
|
|
|
19
19
|
|
|
20
20
|
jupyter_checked_path = get_solara_home() / ".jupyter_checked"
|
|
21
21
|
solara_checked_path = get_solara_home() / ".solara_checked"
|
|
22
|
+
solara_version = solara.__version__
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
def _should_perform_check(path: Path):
|
|
@@ -67,12 +68,12 @@ def JupyterCheck():
|
|
|
67
68
|
# if the widgets do not work
|
|
68
69
|
IPython.display.display(
|
|
69
70
|
IPython.display.Javascript(
|
|
70
|
-
data="""
|
|
71
|
+
data=f"""
|
|
71
72
|
const prevIframe = document.getElementById("solara-jupyter-check");
|
|
72
73
|
if(prevIframe)
|
|
73
74
|
prevIframe.remove();
|
|
74
75
|
const iframe = document.createElement('iframe')
|
|
75
|
-
iframe.setAttribute("src", "https://solara.dev/static/public/success.html?check=purejs");
|
|
76
|
+
iframe.setAttribute("src", "https://solara.dev/static/public/success.html?check=purejs&version={solara_version}");
|
|
76
77
|
iframe.style.width = "0px";
|
|
77
78
|
iframe.style.height = "0px";
|
|
78
79
|
iframe.style.display = "none";
|
|
@@ -95,7 +96,7 @@ document.body.appendChild(iframe);
|
|
|
95
96
|
# this iframe should only get through if the widget installation succeeded
|
|
96
97
|
return solara.v.Html(
|
|
97
98
|
tag="iframe",
|
|
98
|
-
attributes={"src": "https://solara.dev/static/public/success.html?check=widget", "width": "0px", "height": "0px"},
|
|
99
|
+
attributes={"src": f"https://solara.dev/static/public/success.html?check=widget&version={solara_version}", "width": "0px", "height": "0px"},
|
|
99
100
|
style_="display: none;",
|
|
100
101
|
)
|
|
101
102
|
|
|
@@ -111,7 +112,11 @@ def SolaraCheck():
|
|
|
111
112
|
solara.use_effect(flag_solara_checked, [])
|
|
112
113
|
return solara.v.Html(
|
|
113
114
|
tag="iframe",
|
|
114
|
-
attributes={
|
|
115
|
+
attributes={
|
|
116
|
+
"src": f"https://solara.dev/static/public/success.html?system=solara&check=widget&version={solara_version}",
|
|
117
|
+
"width": "0px",
|
|
118
|
+
"height": "0px",
|
|
119
|
+
},
|
|
115
120
|
style_="display: none;",
|
|
116
121
|
)
|
|
117
122
|
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from typing import Callable, Dict, List, Optional, Union
|
|
3
|
+
|
|
4
|
+
from typing_extensions import Literal
|
|
5
|
+
|
|
6
|
+
import solara
|
|
7
|
+
from solara.components.input import use_change
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@solara.component
|
|
11
|
+
def ChatBox(
|
|
12
|
+
children: List[solara.Element] = [],
|
|
13
|
+
style: Optional[Union[str, Dict[str, str]]] = None,
|
|
14
|
+
classes: List[str] = [],
|
|
15
|
+
):
|
|
16
|
+
"""
|
|
17
|
+
The ChatBox component is a container for ChatMessage components.
|
|
18
|
+
Its primary use is to ensure the proper ordering of messages,
|
|
19
|
+
using `flex-direction: column-reverse` together with `reversed(messages)`.
|
|
20
|
+
|
|
21
|
+
# Arguments
|
|
22
|
+
|
|
23
|
+
* `children`: A list of child components.
|
|
24
|
+
* `style`: CSS styles to apply to the component. Either a string or a dictionary.
|
|
25
|
+
* `classes`: A list of CSS classes to apply to the component.
|
|
26
|
+
"""
|
|
27
|
+
style_flat = solara.util._flatten_style(style)
|
|
28
|
+
if "flex-grow" not in style_flat:
|
|
29
|
+
style_flat += " flex-grow: 1;"
|
|
30
|
+
if "flex-direction" not in style_flat:
|
|
31
|
+
style_flat += " flex-direction: column-reverse;"
|
|
32
|
+
if "overflow-y" not in style_flat:
|
|
33
|
+
style_flat += " overflow-y: auto;"
|
|
34
|
+
|
|
35
|
+
classes += ["chat-box"]
|
|
36
|
+
with solara.Column(
|
|
37
|
+
style=style_flat,
|
|
38
|
+
classes=classes,
|
|
39
|
+
):
|
|
40
|
+
for child in list(reversed(children)):
|
|
41
|
+
solara.display(child)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@solara.component
|
|
45
|
+
def ChatInput(
|
|
46
|
+
send_callback: Optional[Callable] = None,
|
|
47
|
+
disabled: bool = False,
|
|
48
|
+
style: Optional[Union[str, Dict[str, str]]] = None,
|
|
49
|
+
classes: List[str] = [],
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
The ChatInput component renders a text input together with a send button.
|
|
53
|
+
|
|
54
|
+
# Arguments
|
|
55
|
+
|
|
56
|
+
* `send_callback`: A callback function for when the user presses enter or clicks the send button.
|
|
57
|
+
* `disabled`: Whether the input should be disabled. Useful for disabling sending further messages while a chatbot is replying,
|
|
58
|
+
among other things.
|
|
59
|
+
* `style`: CSS styles to apply to the component. Either a string or a dictionary. These styles are applied to the container component.
|
|
60
|
+
* `classes`: A list of CSS classes to apply to the component. Also applied to the container.
|
|
61
|
+
"""
|
|
62
|
+
message, set_message = solara.use_state("") # type: ignore
|
|
63
|
+
style_flat = solara.util._flatten_style(style)
|
|
64
|
+
|
|
65
|
+
if "align-items" not in style_flat:
|
|
66
|
+
style_flat += " align-items: center;"
|
|
67
|
+
|
|
68
|
+
with solara.Row(style=style_flat, classes=classes):
|
|
69
|
+
|
|
70
|
+
def send(*ignore_args):
|
|
71
|
+
if message != "" and send_callback is not None:
|
|
72
|
+
send_callback(message)
|
|
73
|
+
set_message("")
|
|
74
|
+
|
|
75
|
+
message_input = solara.v.TextField(
|
|
76
|
+
label="Type a message...",
|
|
77
|
+
v_model=message,
|
|
78
|
+
on_v_model=set_message,
|
|
79
|
+
rounded=True,
|
|
80
|
+
filled=True,
|
|
81
|
+
hide_details=True,
|
|
82
|
+
style_="flex-grow: 1;",
|
|
83
|
+
disabled=disabled,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
use_change(message_input, send, update_events=["keyup.enter"])
|
|
87
|
+
|
|
88
|
+
button = solara.v.Btn(color="primary", icon=True, children=[solara.v.Icon(children=["mdi-send"])], disabled=message == "")
|
|
89
|
+
|
|
90
|
+
use_change(button, send, update_events=["click"])
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@solara.component
|
|
94
|
+
def ChatMessage(
|
|
95
|
+
children: Union[List[solara.Element], str],
|
|
96
|
+
user: bool = False,
|
|
97
|
+
avatar: Union[solara.Element, str, Literal[False], None] = None,
|
|
98
|
+
name: Optional[str] = None,
|
|
99
|
+
color: Optional[str] = "rgba(0,0,0,.06)",
|
|
100
|
+
avatar_background_color: Optional[str] = None,
|
|
101
|
+
border_radius: Optional[str] = None,
|
|
102
|
+
notch: bool = False,
|
|
103
|
+
style: Optional[Union[str, Dict[str, str]]] = None,
|
|
104
|
+
classes: List[str] = [],
|
|
105
|
+
):
|
|
106
|
+
"""
|
|
107
|
+
The ChatMessage component renders a message. Messages with `user=True` are rendered on the right side of the screen,
|
|
108
|
+
all others on the left.
|
|
109
|
+
|
|
110
|
+
# Arguments
|
|
111
|
+
|
|
112
|
+
* `children`: A list of child components.
|
|
113
|
+
* `user`: Whether the message is from the current user or not.
|
|
114
|
+
* `avatar`: An avatar to display next to the message. Can be a string representation of a URL or Material design icon name,
|
|
115
|
+
a solara Element, False to disable avatars altogether, or None to display initials based on `name`.
|
|
116
|
+
* `name`: The name of the user who sent the message.
|
|
117
|
+
* `color`: The background color of the message. Defaults to `rgba(0,0,0,.06)`. Can be any valid CSS color.
|
|
118
|
+
* `avatar_background_color`: The background color of the avatar. Defaults to `color` if left as `None`.
|
|
119
|
+
* `border_radius`: Sets the roundness of the corners of the message. Defaults to `None`,
|
|
120
|
+
which applies the default border radius of a `solara.Column`, i.e. `4px`.
|
|
121
|
+
* `notch`: Whether to display a speech bubble style notch on the side of the message.
|
|
122
|
+
* `style`: CSS styles to apply to the component. Either a string or a dictionary. Applied to the container of the message.
|
|
123
|
+
* `classes`: A list of CSS classes to apply to the component. Applied to the same container.
|
|
124
|
+
"""
|
|
125
|
+
style_flat = solara.util._flatten_style(style)
|
|
126
|
+
|
|
127
|
+
if "border-radius" not in style_flat:
|
|
128
|
+
style_flat += f" border-radius: {border_radius if border_radius is not None else ''};"
|
|
129
|
+
if f"border-top-{'right' if user else 'left'}-radius" not in style_flat:
|
|
130
|
+
style_flat += f" border-top-{'right' if user else 'left'}-radius: 0;"
|
|
131
|
+
if "padding" not in style_flat:
|
|
132
|
+
style_flat += " padding: .5em 1.5em;"
|
|
133
|
+
|
|
134
|
+
msg_uuid = solara.use_memo(lambda: str(uuid.uuid4()), dependencies=[])
|
|
135
|
+
with solara.Row(
|
|
136
|
+
justify="end" if user else "start",
|
|
137
|
+
style={"flex-direction": "row-reverse" if user else "row", "padding": "5px"},
|
|
138
|
+
):
|
|
139
|
+
if avatar is not False:
|
|
140
|
+
with solara.v.Avatar(color=avatar_background_color if avatar_background_color is not None else color):
|
|
141
|
+
if avatar is None and name is not None:
|
|
142
|
+
initials = "".join([word[:1] for word in name.split(" ")])
|
|
143
|
+
solara.HTML(tag="span", unsafe_innerHTML=initials, classes=["headline"])
|
|
144
|
+
elif isinstance(avatar, solara.Element):
|
|
145
|
+
solara.display(avatar)
|
|
146
|
+
elif isinstance(avatar, str) and avatar.startswith("mdi-"):
|
|
147
|
+
solara.v.Icon(children=[avatar])
|
|
148
|
+
else:
|
|
149
|
+
solara.HTML(tag="img", attributes={"src": avatar, "width": "100%"})
|
|
150
|
+
classes_new = classes + ["chat-message-" + msg_uuid, "right" if user else "left"]
|
|
151
|
+
with solara.Column(
|
|
152
|
+
classes=classes_new,
|
|
153
|
+
gap=0,
|
|
154
|
+
style=style_flat,
|
|
155
|
+
):
|
|
156
|
+
if name is not None:
|
|
157
|
+
solara.Text(name, style="font-weight: bold;", classes=["message-name", "right" if user else "left"])
|
|
158
|
+
for child in children:
|
|
159
|
+
if isinstance(child, solara.Element):
|
|
160
|
+
solara.display(child)
|
|
161
|
+
else:
|
|
162
|
+
solara.Markdown(child)
|
|
163
|
+
# we use the uuid to generate 'scoped' CSS, i.e. css that only applies to the component instance.
|
|
164
|
+
extra_styles = (
|
|
165
|
+
f""".chat-message-{msg_uuid}:before{{
|
|
166
|
+
content: '';
|
|
167
|
+
position: absolute;
|
|
168
|
+
width: 0;
|
|
169
|
+
height: 0;
|
|
170
|
+
border: 6px solid;
|
|
171
|
+
top: 0;
|
|
172
|
+
}}
|
|
173
|
+
.chat-message-{msg_uuid}.left:before{{
|
|
174
|
+
left: -12px;
|
|
175
|
+
border-color: var(--color) var(--color) transparent transparent;
|
|
176
|
+
}}
|
|
177
|
+
.chat-message-{msg_uuid}.right:before{{
|
|
178
|
+
right: -12px;
|
|
179
|
+
border-color: var(--color) transparent transparent var(--color);
|
|
180
|
+
}}"""
|
|
181
|
+
if notch
|
|
182
|
+
else ""
|
|
183
|
+
)
|
|
184
|
+
solara.Style(
|
|
185
|
+
f"""
|
|
186
|
+
.chat-message-{msg_uuid}{{
|
|
187
|
+
--color: {color};
|
|
188
|
+
max-width: 75%;
|
|
189
|
+
position: relative;
|
|
190
|
+
}}
|
|
191
|
+
.chat-message-{msg_uuid}.left{{
|
|
192
|
+
border-top-left-radius: 0;
|
|
193
|
+
background-color:var(--color);
|
|
194
|
+
{ "margin-left: 10px !important;" if notch else ""}
|
|
195
|
+
}}
|
|
196
|
+
.chat-message-{msg_uuid}.right{{
|
|
197
|
+
border-top-right-radius: 0;
|
|
198
|
+
background-color:var(--color);
|
|
199
|
+
{ "margin-right: 10px !important;" if notch else ""}
|
|
200
|
+
}}
|
|
201
|
+
{extra_styles}
|
|
202
|
+
"""
|
|
203
|
+
)
|
solara/minisettings.py
CHANGED
|
@@ -94,7 +94,7 @@ class BaseSettings:
|
|
|
94
94
|
def __init__(self, **kwargs) -> None:
|
|
95
95
|
cls = type(self)
|
|
96
96
|
self._values = {**kwargs}
|
|
97
|
-
keys =
|
|
97
|
+
keys = {k.upper() for k in os.environ.keys()}
|
|
98
98
|
for key, field in cls.__dict__.items():
|
|
99
99
|
if key in kwargs:
|
|
100
100
|
continue
|