Skip to content

Commit

Permalink
fix: custom styles work in solara server (spacetelescope#2658)
Browse files Browse the repository at this point in the history
In spacetelescope#2580 we fixed support for custom style in combination with
ipypopout. However, this uses a global widget, which does not work
with solara, since multiple virtual kernels would then share the same
widget.

This is using a singleton decorator that make sure each virtual kernel
will create only 1 StyleRegistry widget.
  • Loading branch information
maartenbreddels authored Feb 15, 2024
1 parent bf46c9b commit 59aeab0
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 9 deletions.
4 changes: 2 additions & 2 deletions jdaviz/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def to_unit(self, data, cid, values, original_units, target_units):
ipyvue.register_component_from_file('g-viewer-tab', "container.vue", __file__)


style_registry.instance.add((__file__, 'main_styles.vue'))
style_registry.add((__file__, 'main_styles.vue'))


class ApplicationState(State):
Expand Down Expand Up @@ -262,7 +262,7 @@ def __init__(self, configuration=None, *args, **kwargs):
self._verbosity = 'warning'
self._history_verbosity = 'info'
self.popout_button = PopoutButton(self)
self.style_registry_instance = style_registry.instance
self.style_registry_instance = style_registry.get_style_registry()

# Generate a state object for this application to maintain the state of
# the user interface.
Expand Down
60 changes: 53 additions & 7 deletions jdaviz/style_registry.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,47 @@
from pathlib import Path
import typing as t
import threading

from traitlets import Any, Dict, Instance, default
from ipywidgets import widget_serialization
from ipyvuetify import VuetifyTemplate

from jdaviz.core.style_widget import StyleWidget


T = t.TypeVar("T")
_style_paths: t.Dict[int, Path] = {}


# this is a temporary decorator until we can depend on solara, and solara has
# an equivalent implementation.
def _singleton(factory: [t.Callable[[], T]]) -> t.Callable[[], T]:
def _get_unique_key():
try:
import solara.server.kernel_context as kernel_context

try:
kc = kernel_context.get_current_context()
return kc.id
except RuntimeError:
pass # not running in a solara virtual kernel
except ModuleNotFoundError:
return "not-in-solara"

instances: t.Dict[str, T] = {}
lock = threading.Lock()

def wrapper(*args, **kwargs) -> T:
key = _get_unique_key()
if key not in instances:
with lock:
if key not in instances:
instances[key] = factory(*args, **kwargs)
return instances[key]

return wrapper


class StyleRegistry(VuetifyTemplate):
style_widgets = Dict(default_value={}).tag(sync=True, **widget_serialization)

Expand All @@ -19,20 +56,17 @@ def _template(self):
</template>
"""

def add(self, path):
if hash(path) in self.style_widgets:
return
self.style_widgets = {**self.style_widgets, hash(path): StyleWidget(path)}


instance = StyleRegistry()
def add(path):
if hash(path) in _style_paths:
return
_style_paths[hash(path)] = path


class PopoutStyleWrapper(VuetifyTemplate):
content = Any().tag(sync=True, **widget_serialization)
style_registry_instance = Instance(
StyleRegistry,
default_value=instance
).tag(sync=True, **widget_serialization)

@default("template")
Expand All @@ -45,3 +79,15 @@ def _template(self):
</div>
</template>
"""

@default("style_registry_instance")
@_singleton
def _default_style_registry_instance(self):
return get_style_registry()


@_singleton
def get_style_registry():
return StyleRegistry(
style_widgets={key: StyleWidget(path) for key, path in _style_paths.items()}
)

0 comments on commit 59aeab0

Please sign in to comment.