solara-ui 1.54.0__py3-none-any.whl → 1.55.1__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/minisettings.py +22 -26
- solara/server/kernel_context.py +31 -1
- solara/server/static/solara_bootstrap.py +1 -1
- solara/settings.py +1 -0
- solara/tasks.py +12 -3
- solara/toestand.py +4 -0
- solara/validate_hooks.py +10 -4
- {solara_ui-1.54.0.dist-info → solara_ui-1.55.1.dist-info}/METADATA +1 -1
- {solara_ui-1.54.0.dist-info → solara_ui-1.55.1.dist-info}/RECORD +14 -14
- {solara_ui-1.54.0.data → solara_ui-1.55.1.data}/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -0
- {solara_ui-1.54.0.data → solara_ui-1.55.1.data}/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -0
- {solara_ui-1.54.0.dist-info → solara_ui-1.55.1.dist-info}/WHEEL +0 -0
- {solara_ui-1.54.0.dist-info → solara_ui-1.55.1.dist-info}/licenses/LICENSE +0 -0
solara/__init__.py
CHANGED
solara/minisettings.py
CHANGED
|
@@ -101,37 +101,33 @@ class BaseSettings:
|
|
|
101
101
|
cls = type(self)
|
|
102
102
|
self._values = {**kwargs}
|
|
103
103
|
keys = {k.upper() for k in os.environ.keys()}
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
|
|
105
|
+
for key, field in list(cls.__dict__.items()):
|
|
106
|
+
if key in kwargs or not isinstance(field, _Field):
|
|
106
107
|
continue
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if env_key.upper() == env_var_cased.upper():
|
|
118
|
-
value = convert(field.annotation, os.environ[env_var_cased])
|
|
119
|
-
self._values[key] = value
|
|
108
|
+
value = field.default
|
|
109
|
+
if field.default_factory:
|
|
110
|
+
value = field.default_factory()
|
|
111
|
+
if field.env:
|
|
112
|
+
env_key = field.env.upper()
|
|
113
|
+
if env_key in keys:
|
|
114
|
+
for env_var_cased in os.environ.keys():
|
|
115
|
+
if env_key.upper() == env_var_cased.upper():
|
|
116
|
+
value = convert(field.annotation, os.environ[env_var_cased])
|
|
117
|
+
self._values[key] = value
|
|
120
118
|
|
|
121
119
|
def __init_subclass__(cls) -> None:
|
|
122
120
|
cls.__fields__ = {}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if key == "Config":
|
|
121
|
+
items = list(cls.__dict__.items())
|
|
122
|
+
|
|
123
|
+
for key, value in items:
|
|
124
|
+
if key.startswith("_") or key == "Config" or inspect.isfunction(value):
|
|
127
125
|
continue
|
|
128
|
-
if not isinstance(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
field.__set_name__(cls, key)
|
|
134
|
-
cls.__fields__[key] = field
|
|
126
|
+
if not isinstance(value, _Field):
|
|
127
|
+
value = Field(value)
|
|
128
|
+
setattr(cls, key, value)
|
|
129
|
+
value.__set_name__(cls, key)
|
|
130
|
+
cls.__fields__[key] = value
|
|
135
131
|
|
|
136
132
|
def dict(self, by_alias=True):
|
|
137
133
|
values = self._values.copy()
|
solara/server/kernel_context.py
CHANGED
|
@@ -17,7 +17,7 @@ import threading
|
|
|
17
17
|
import time
|
|
18
18
|
import typing
|
|
19
19
|
from pathlib import Path
|
|
20
|
-
from typing import Any, Callable, Dict, List, Optional, cast
|
|
20
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, cast
|
|
21
21
|
|
|
22
22
|
import ipywidgets as widgets
|
|
23
23
|
import reacton
|
|
@@ -39,6 +39,11 @@ class Local(threading.local):
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
local = Local()
|
|
42
|
+
# same idea, but for `async with ...`
|
|
43
|
+
if typing.TYPE_CHECKING:
|
|
44
|
+
async_stack = contextvars.ContextVar[Union[Tuple[Union[None, "VirtualKernelContext"], ...], None]](name="async_stack", default=None)
|
|
45
|
+
else:
|
|
46
|
+
async_stack = contextvars.ContextVar("async_stack", default=None)
|
|
42
47
|
|
|
43
48
|
|
|
44
49
|
class PageStatus(enum.Enum):
|
|
@@ -100,6 +105,23 @@ class VirtualKernelContext:
|
|
|
100
105
|
def on_close(self, f: Callable[[], None]):
|
|
101
106
|
self._on_close_callbacks.append(f)
|
|
102
107
|
|
|
108
|
+
async def __aenter__(self):
|
|
109
|
+
stack = async_stack.get()
|
|
110
|
+
if stack is None:
|
|
111
|
+
stack = ()
|
|
112
|
+
key = get_current_thread_key()
|
|
113
|
+
async_stack.set(stack + (current_context.get(key, None),))
|
|
114
|
+
new_key = get_current_thread_key()
|
|
115
|
+
current_context[new_key] = self
|
|
116
|
+
|
|
117
|
+
async def __aexit__(self, *args):
|
|
118
|
+
key = get_current_thread_key()
|
|
119
|
+
assert local.kernel_context_stack is not None
|
|
120
|
+
stack = async_stack.get()
|
|
121
|
+
assert stack is not None
|
|
122
|
+
current_context[key] = stack[-1]
|
|
123
|
+
async_stack.set(stack[:-1])
|
|
124
|
+
|
|
103
125
|
def __enter__(self):
|
|
104
126
|
if local.kernel_context_stack is None:
|
|
105
127
|
local.kernel_context_stack = []
|
|
@@ -364,6 +386,7 @@ else:
|
|
|
364
386
|
|
|
365
387
|
|
|
366
388
|
def get_current_thread_key() -> str:
|
|
389
|
+
# consider renaming this to get_current_context_key
|
|
367
390
|
if not solara.server.settings.kernel.threaded:
|
|
368
391
|
if async_context_id is not None:
|
|
369
392
|
try:
|
|
@@ -375,6 +398,13 @@ def get_current_thread_key() -> str:
|
|
|
375
398
|
else:
|
|
376
399
|
thread = threading.current_thread()
|
|
377
400
|
key = get_thread_key(thread)
|
|
401
|
+
# this signals we are using `async with context`, which means we are interested in task-local context
|
|
402
|
+
stack = async_stack.get()
|
|
403
|
+
if stack is not None and len(stack) > 0:
|
|
404
|
+
current_task = asyncio.current_task()
|
|
405
|
+
if current_task is not None:
|
|
406
|
+
task_key = current_task.get_name()
|
|
407
|
+
key = f"{key}-task:{task_key}"
|
|
378
408
|
return key
|
|
379
409
|
|
|
380
410
|
|
|
@@ -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.
|
|
122
|
+
await micropip.install("/wheels/solara-1.55.1-py2.py3-none-any.whl", keep_going=True)
|
|
123
123
|
import solara
|
|
124
124
|
|
|
125
125
|
el = solara.Warning("lala")
|
solara/settings.py
CHANGED
|
@@ -57,6 +57,7 @@ class MainSettings(BaseSettings):
|
|
|
57
57
|
allow_reactive_boolean: bool = True
|
|
58
58
|
# TODO: also change default_container in solara/components/__init__.py
|
|
59
59
|
default_container: Optional[str] = "Column"
|
|
60
|
+
allow_global_context: bool = True
|
|
60
61
|
|
|
61
62
|
class Config:
|
|
62
63
|
env_prefix = "solara_"
|
solara/tasks.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import contextvars
|
|
1
2
|
import sys
|
|
2
3
|
import abc
|
|
3
4
|
import asyncio
|
|
@@ -267,7 +268,16 @@ class TaskAsyncio(Task[P, R]):
|
|
|
267
268
|
|
|
268
269
|
if self.run_in_thread:
|
|
269
270
|
thread_event_loop = asyncio.new_event_loop()
|
|
270
|
-
|
|
271
|
+
|
|
272
|
+
def create_task():
|
|
273
|
+
# remove the stack, since this thread starts with a fresh stack
|
|
274
|
+
import solara.server.kernel_context
|
|
275
|
+
|
|
276
|
+
solara.server.kernel_context.async_stack.set(None)
|
|
277
|
+
return thread_event_loop.create_task(self._async_run(call_event_loop, future, args, kwargs))
|
|
278
|
+
|
|
279
|
+
new_context = contextvars.copy_context()
|
|
280
|
+
self.current_task = current_task = new_context.run(create_task)
|
|
271
281
|
|
|
272
282
|
def runs_in_thread():
|
|
273
283
|
try:
|
|
@@ -298,7 +308,7 @@ class TaskAsyncio(Task[P, R]):
|
|
|
298
308
|
raise
|
|
299
309
|
|
|
300
310
|
self._result.value = TaskResult[R](latest=self._last_value, _state=TaskState.STARTING)
|
|
301
|
-
thread = threading.Thread(target=runs_in_thread, daemon=True)
|
|
311
|
+
thread = threading.Thread(target=runs_in_thread, daemon=True, name=f"TaskAsyncio-{self.function.__name__}")
|
|
302
312
|
thread.start()
|
|
303
313
|
else:
|
|
304
314
|
self.current_task = current_task = asyncio.create_task(self._async_run(call_event_loop, future, args, kwargs))
|
|
@@ -322,7 +332,6 @@ class TaskAsyncio(Task[P, R]):
|
|
|
322
332
|
|
|
323
333
|
task_for_this_call = _get_current_task()
|
|
324
334
|
assert task_for_this_call is not None
|
|
325
|
-
|
|
326
335
|
if self.is_current():
|
|
327
336
|
self._result.value = TaskResult[R](latest=self._last_value, _state=TaskState.STARTING)
|
|
328
337
|
|
solara/toestand.py
CHANGED
|
@@ -313,6 +313,10 @@ class KernelStore(ValueBase[S], ABC):
|
|
|
313
313
|
|
|
314
314
|
def set(self, value: S):
|
|
315
315
|
scope_dict, scope_id = self._get_dict()
|
|
316
|
+
if not solara.settings.main.allow_global_context and scope_id == "global":
|
|
317
|
+
raise RuntimeError(
|
|
318
|
+
f"No kernel context found, and global context is not allowed for task, context key was {solara.server.kernel_context.get_current_thread_key()}"
|
|
319
|
+
)
|
|
316
320
|
old = self.get()
|
|
317
321
|
if self.equals(old, value):
|
|
318
322
|
return
|
solara/validate_hooks.py
CHANGED
|
@@ -31,11 +31,11 @@ noqa_pattern = re.compile(r".*# noqa(?::\s*([A-Z]{2,3}\d{2,3})(?:,\s*([A-Z]{2,3}
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
if sys.version_info < (3, 11):
|
|
34
|
-
ScopeNodeType = t.Union[ast.For, ast.While, ast.If, ast.Try, ast.FunctionDef]
|
|
34
|
+
ScopeNodeType = t.Union[ast.For, ast.While, ast.If, ast.Try, ast.FunctionDef, ast.AsyncFunctionDef]
|
|
35
35
|
TryNodes = (ast.Try,)
|
|
36
36
|
else:
|
|
37
37
|
# except* nodes are only standardized in 3.11+
|
|
38
|
-
ScopeNodeType = t.Union[ast.For, ast.While, ast.If, ast.Try, ast.TryStar, ast.FunctionDef]
|
|
38
|
+
ScopeNodeType = t.Union[ast.For, ast.While, ast.If, ast.Try, ast.TryStar, ast.FunctionDef, ast.AsyncFunctionDef]
|
|
39
39
|
TryNodes = (ast.Try, ast.TryStar)
|
|
40
40
|
|
|
41
41
|
|
|
@@ -100,7 +100,7 @@ class HookValidator(ast.NodeVisitor):
|
|
|
100
100
|
parsed = ast.parse(parsed_source)
|
|
101
101
|
# Get nodes from inside the function body
|
|
102
102
|
func_definition = t.cast(ast.FunctionDef, parsed.body[0])
|
|
103
|
-
self.function_scope: ast.FunctionDef = func_definition
|
|
103
|
+
self.function_scope: t.Union[ast.FunctionDef, ast.AsyncFunctionDef] = func_definition
|
|
104
104
|
self._root_function_scope = self.function_scope
|
|
105
105
|
# None means, *DO* qa
|
|
106
106
|
self.no_qa: t.Optional[t.Set[InvalidReactivityCause]] = None
|
|
@@ -129,7 +129,7 @@ class HookValidator(ast.NodeVisitor):
|
|
|
129
129
|
return InvalidReactivityCause.CONDITIONAL_USE
|
|
130
130
|
elif isinstance(node, (ast.For, ast.While)):
|
|
131
131
|
return InvalidReactivityCause.LOOP_USE
|
|
132
|
-
elif isinstance(node, ast.FunctionDef):
|
|
132
|
+
elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
133
133
|
return InvalidReactivityCause.NESTED_FUNCTION_USE
|
|
134
134
|
elif isinstance(node, TryNodes):
|
|
135
135
|
return InvalidReactivityCause.EXCEPTION_USE
|
|
@@ -175,6 +175,12 @@ class HookValidator(ast.NodeVisitor):
|
|
|
175
175
|
self._visit_children_using_scope(node)
|
|
176
176
|
self.function_scope = old_function_scope
|
|
177
177
|
|
|
178
|
+
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef):
|
|
179
|
+
old_function_scope = self.function_scope
|
|
180
|
+
self.function_scope = node
|
|
181
|
+
self._visit_children_using_scope(node)
|
|
182
|
+
self.function_scope = old_function_scope
|
|
183
|
+
|
|
178
184
|
def _visit_children_using_scope(self, node: ScopeNodeType):
|
|
179
185
|
outer_scope = self.outer_scope
|
|
180
186
|
self.outer_scope = node
|
|
@@ -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
|
|
3
|
+
solara/__init__.py,sha256=-kOoajHZOJA7rILhQiR19_A8BVLw1CANxybDiFOD7sE,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
|
|
@@ -15,15 +15,15 @@ solara/express.py,sha256=R0E2ewwL0m09KdoDNhF_ZF5TnC7ytty5rzM8hDykpGk,6919
|
|
|
15
15
|
solara/kitchensink.py,sha256=RUx3kW6CQAz9PMxB1sPI03IH5xJfsaaXq3w9bBuC6rg,249
|
|
16
16
|
solara/layout.py,sha256=YSsORvn-76LrVSmElS39CBnetyUL9f4hLG_a_SdH_QM,1782
|
|
17
17
|
solara/lifecycle.py,sha256=acUtft_KHj0ZOv2l-X3VcQdma1Tme70jkUp6li8mbH0,1404
|
|
18
|
-
solara/minisettings.py,sha256=
|
|
18
|
+
solara/minisettings.py,sha256=dxhFZxYg-jBhyXzro8tUVjZNpb5Z0Kq_RpYBrn8eCEQ,4699
|
|
19
19
|
solara/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
20
|
solara/reactive.py,sha256=KN0PJl-ivxjgQj008zyPGnORo5bTNaY77uASsSW0mFQ,3430
|
|
21
21
|
solara/routing.py,sha256=G_iZKozdVoUuD-qSMyuPV6jeN4qBqujAUvekw036f88,9143
|
|
22
|
-
solara/settings.py,sha256=
|
|
23
|
-
solara/tasks.py,sha256=
|
|
24
|
-
solara/toestand.py,sha256=
|
|
22
|
+
solara/settings.py,sha256=VeYkEdWeJePXldFvfdJteiiydWTqsNtpgsYscKm7lrA,2726
|
|
23
|
+
solara/tasks.py,sha256=Wjb-0ExfW2lMbT40sYG3yEzUMLOHKKzHshCahBOELk8,37599
|
|
24
|
+
solara/toestand.py,sha256=2fNxOFAg120yud4EmU2s8-aUx4N1D4T8VvdO6Oqi_4s,34357
|
|
25
25
|
solara/util.py,sha256=UUO3BfhXb3tGP-uj8UuTYMx6kuph6PiDp4XXm-f6uyg,9697
|
|
26
|
-
solara/validate_hooks.py,sha256=
|
|
26
|
+
solara/validate_hooks.py,sha256=JTXOnfSPhNQaZFuMYm5EuvB_TlnZz1kw8hqDMNu_B5M,10345
|
|
27
27
|
solara/components/__init__.py,sha256=j5Tv0tyzs80Bsl5hvTIF_x-RQyAvr3ooqIXnABamW44,3214
|
|
28
28
|
solara/components/alert.py,sha256=sNjlrCu2niR6LD9gZFXwvSBMszCKt6nRH58kE7RgsDw,5144
|
|
29
29
|
solara/components/applayout.py,sha256=Q1n8foPT1szjIKqjUum6QxFnUvNj_MheMmyc8r7FIsI,16753
|
|
@@ -113,7 +113,7 @@ solara/server/fastapi.py,sha256=qVIHn0_Kxr6zWqcBWySu5nnJ6pNTSDqb4EHIh-cqH_8,93
|
|
|
113
113
|
solara/server/flask.py,sha256=sjhtTMiAUJ7oacZeL0m3Qem6E6B2knJ0ly845Ayyjio,9645
|
|
114
114
|
solara/server/jupytertools.py,sha256=cYFIUjLX7n0uuEXqWVWvmV6sV7R_MNg8ZZlabQgw8vk,1320
|
|
115
115
|
solara/server/kernel.py,sha256=ZIcVI-wYhjDo1o05PBjkd-Am-5wZ8lAlf7luhz5-_L8,13449
|
|
116
|
-
solara/server/kernel_context.py,sha256=
|
|
116
|
+
solara/server/kernel_context.py,sha256=nOgBgchZZmZpHpHtI6_JRpxgJApZ50eKVIOk_EnyXJo,20477
|
|
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
|
|
@@ -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=
|
|
149
|
+
solara/server/static/solara_bootstrap.py,sha256=38HW-1RWpzOwrCYtVEwprLqVFFEa5-kyQQriR3990LE,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
|
|
@@ -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.
|
|
460
|
-
solara_ui-1.
|
|
461
|
-
solara_ui-1.
|
|
462
|
-
solara_ui-1.
|
|
463
|
-
solara_ui-1.
|
|
464
|
-
solara_ui-1.
|
|
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,,
|
|
File without changes
|
{solara_ui-1.54.0.data → solara_ui-1.55.1.data}/data/etc/jupyter/jupyter_server_config.d/solara.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|