From ba43eeafaa2de878136f78d019c3c1fb49fd4545 Mon Sep 17 00:00:00 2001 From: "Maarten A. Breddels" Date: Wed, 23 Mar 2022 10:14:53 +0100 Subject: [PATCH] fix: avoid using a global force_load widget --- ipyvue/ForceLoad.py | 3 --- ipyvue/VueTemplateWidget.py | 14 +++++++++----- ipyvue/VueWidget.py | 19 ++++++++++++++----- ipyvue/util.py | 24 ++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 ipyvue/util.py diff --git a/ipyvue/ForceLoad.py b/ipyvue/ForceLoad.py index 1cc797a..9249013 100644 --- a/ipyvue/ForceLoad.py +++ b/ipyvue/ForceLoad.py @@ -7,6 +7,3 @@ class ForceLoad(DOMWidget): _model_name = Unicode("ForceLoadModel").tag(sync=True) _model_module = Unicode("jupyter-vue").tag(sync=True) _model_module_version = Unicode(semver).tag(sync=True) - - -force_load_instance = ForceLoad() diff --git a/ipyvue/VueTemplateWidget.py b/ipyvue/VueTemplateWidget.py index 54b00c2..38d270e 100644 --- a/ipyvue/VueTemplateWidget.py +++ b/ipyvue/VueTemplateWidget.py @@ -1,11 +1,12 @@ import os -from traitlets import Any, Unicode, List, Dict, Union, Instance +from traitlets import Any, Unicode, List, Dict, Union, Instance, default from ipywidgets import DOMWidget from ipywidgets.widgets.widget import widget_serialization from .Template import Template, get_template from ._version import semver -from .ForceLoad import force_load_instance +from .ForceLoad import ForceLoad +from .util import singleton import inspect from importlib import import_module @@ -94,12 +95,15 @@ class VueTemplate(DOMWidget, Events): "to_json": _class_to_json, } - # Force the loading of jupyter-vue before dependent extensions when in a static - # context (embed, voila) - _jupyter_vue = Any(force_load_instance, read_only=True).tag( + # see VueWidget + _jupyter_vue = Instance(ForceLoad, read_only=True).tag( sync=True, **widget_serialization ) + @default("_jupyter_vue") + def _default_jupyter_vue(self): + return singleton(ForceLoad) + _model_name = Unicode("VueTemplateModel").tag(sync=True) _view_name = Unicode("VueView").tag(sync=True) diff --git a/ipyvue/VueWidget.py b/ipyvue/VueWidget.py index b85a737..5421733 100644 --- a/ipyvue/VueWidget.py +++ b/ipyvue/VueWidget.py @@ -1,11 +1,12 @@ -from traitlets import Unicode, Instance, Union, List, Any, Dict from ipywidgets import DOMWidget -from ipywidgets.widgets.widget_layout import Layout -from ipywidgets.widgets.widget import widget_serialization, CallbackDispatcher from ipywidgets.widgets.trait_types import InstanceDict +from ipywidgets.widgets.widget import CallbackDispatcher, widget_serialization +from ipywidgets.widgets.widget_layout import Layout +from traitlets import Any, Dict, Instance, List, Unicode, Union, default from ._version import semver -from .ForceLoad import force_load_instance +from .ForceLoad import ForceLoad +from .util import singleton class ClassList: @@ -138,10 +139,18 @@ class VueWidget(DOMWidget, Events): # Force the loading of jupyter-vue before dependent extensions when in a static # context (embed, voila) - _jupyter_vue = Any(force_load_instance, read_only=True).tag( + # this happens when e.g. jupyter-vuedraggable requires jupyter-vue, but it is not + # loaded yet (via a widget creation). This does not trigger the library loading + # via the widget manager, but straight through requirejs + + _jupyter_vue = Instance(ForceLoad, read_only=True).tag( sync=True, **widget_serialization ) + @default("_jupyter_vue") + def _default_jupyter_vue(self): + return singleton(ForceLoad) + _model_name = Unicode("VueModel").tag(sync=True) _view_name = Unicode("VueView").tag(sync=True) diff --git a/ipyvue/util.py b/ipyvue/util.py new file mode 100644 index 0000000..34655c4 --- /dev/null +++ b/ipyvue/util.py @@ -0,0 +1,24 @@ +from ipywidgets import Widget +import threading + + +lock = threading.Lock() + + +def singleton(WidgetClass, **kwargs): + # find a 'shared' instance (singleton) to avoid creating + # too many widgets. In contexts where self.widgets is not + # a global dict, this allows different users/context to + # have a singleton without sharing the widget + with lock: + if hasattr(Widget, "widgets"): + # pre 8.0 + all_widgets = Widget.widgets + if hasattr(Widget, "widgets"): + all_widgets = Widget._widgets + else: + + for widget in all_widgets.values(): + if isinstance(widget, WidgetClass): + return widget + return WidgetClass(**kwargs)