solara-ui 1.53.0__py3-none-any.whl → 1.55.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- solara/__init__.py +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/test/pytest_plugin.py +6 -1
- solara/toestand.py +4 -0
- {solara_ui-1.53.0.dist-info → solara_ui-1.55.0.dist-info}/METADATA +1 -1
- {solara_ui-1.53.0.dist-info → solara_ui-1.55.0.dist-info}/RECORD +14 -14
- {solara_ui-1.53.0.data → solara_ui-1.55.0.data}/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -0
- {solara_ui-1.53.0.data → solara_ui-1.55.0.data}/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -0
- {solara_ui-1.53.0.dist-info → solara_ui-1.55.0.dist-info}/WHEEL +0 -0
- {solara_ui-1.53.0.dist-info → solara_ui-1.55.0.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.0-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/test/pytest_plugin.py
CHANGED
|
@@ -35,7 +35,12 @@ if typing.TYPE_CHECKING:
|
|
|
35
35
|
|
|
36
36
|
logger = logging.getLogger("solara.pytest_plugin")
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
|
|
39
|
+
# support for pytest-xdist
|
|
40
|
+
worker = os.environ.get("PYTEST_XDIST_WORKER", "gw0")
|
|
41
|
+
# +100 so we do not interfere with the solara integration tests, +1 worker id to avoid port conflicts with other workers
|
|
42
|
+
TEST_PORT_START = int(os.environ.get("PORT", "18765")) + int(worker[2:]) + 100
|
|
43
|
+
|
|
39
44
|
TEST_HOST = solara.server.settings.main.host
|
|
40
45
|
TIMEOUT = float(os.environ.get("SOLARA_PW_TIMEOUT", "18"))
|
|
41
46
|
PYTEST_IPYWIDGETS_SOLARA_APP_WAIT_TIMEOUT = int(os.environ.get("PYTEST_IPYWIDGETS_SOLARA_APP_WAIT_TIMEOUT", "10"))
|
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
|
|
@@ -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=EOkfX79MWzQHzuZpwOLafmVnS4mN76-eJGAk_ztvw18,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,13 +15,13 @@ 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
26
|
solara/validate_hooks.py,sha256=F0CYDOVF_23O1apJBIk9lZMq11JmkoE3BrVVT8QvZWI,9999
|
|
27
27
|
solara/components/__init__.py,sha256=j5Tv0tyzs80Bsl5hvTIF_x-RQyAvr3ooqIXnABamW44,3214
|
|
@@ -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=RKkrTWa1-WGsLTkfcLZIZqI1mXHvxRBjlKiF-s4VjXM,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
|
|
@@ -179,7 +179,7 @@ solara/template/portal/solara_portal/pages/article/__init__.py,sha256=6PgHyyeK1_
|
|
|
179
179
|
solara/template/portal/solara_portal/pages/viz/__init__.py,sha256=l65uqBpFJ6Uh5XytXhLMnR-8G4FBnjqJg86OJX7_LO0,2858
|
|
180
180
|
solara/template/portal/solara_portal/pages/viz/overview.py,sha256=GPlzFxUR0LRQhR96a_CVWquGTjHhDehL1PR03Tcm3gs,365
|
|
181
181
|
solara/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
182
|
-
solara/test/pytest_plugin.py,sha256=
|
|
182
|
+
solara/test/pytest_plugin.py,sha256=M7_LQg4vf7ZHi-0HX8dRwbIEOehK-tx9Yz5v7qZLdkQ,30294
|
|
183
183
|
solara/website/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
184
184
|
solara/website/utils.py,sha256=TfoExocZ8ko2hTcA7XDKI5FfKZ4gi3JTc_f9Oi5L7Fs,855
|
|
185
185
|
solara/website/assets/custom.css,sha256=Qw_FZpAmPrus38TgYKmLmSgEYf6djWzO5pm2fF8kutk,9330
|
|
@@ -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.0.data/data/etc/jupyter/jupyter_notebook_config.d/solara.json,sha256=3UhTBQi6z7F7pPjmqXxfddv79c8VGR9H7zStDLp6AwY,115
|
|
460
|
+
solara_ui-1.55.0.data/data/etc/jupyter/jupyter_server_config.d/solara.json,sha256=D9J-rYxAzyD5GOqWvuPjacGUVFHsYtTfZ4FUbRzRvIA,113
|
|
461
|
+
solara_ui-1.55.0.dist-info/METADATA,sha256=TI1NAM29BE5gmF1oBfwm_775SYrULb4JRuif0B7huWU,7459
|
|
462
|
+
solara_ui-1.55.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
463
|
+
solara_ui-1.55.0.dist-info/licenses/LICENSE,sha256=fFJUz-CWzZ9nEc4QZKu44jMEoDr5fEW-SiqljKpD82E,1086
|
|
464
|
+
solara_ui-1.55.0.dist-info/RECORD,,
|
|
File without changes
|
{solara_ui-1.53.0.data → solara_ui-1.55.0.data}/data/etc/jupyter/jupyter_server_config.d/solara.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|