From d48292749a6ae7f56320047018a3762f6bbfa9a0 Mon Sep 17 00:00:00 2001 From: liamb27 <157428166+liamb27@users.noreply.github.com> Date: Sun, 31 Mar 2024 23:57:39 -0700 Subject: [PATCH 01/15] Update Widget List.ipynb Example for FloatLogSlider labeled min and max in reverse order; changed it so it is correct. --- docs/source/examples/Widget List.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/examples/Widget List.ipynb b/docs/source/examples/Widget List.ipynb index 57164f6015..9df098100d 100644 --- a/docs/source/examples/Widget List.ipynb +++ b/docs/source/examples/Widget List.ipynb @@ -169,8 +169,8 @@ "widgets.FloatLogSlider(\n", " value=10,\n", " base=10,\n", - " min=-10, # max exponent of base\n", - " max=10, # min exponent of base\n", + " min=-10, # min exponent of base\n", + " max=10, # max exponent of base\n", " step=0.2, # exponent step\n", " description='Log Slider'\n", ")" From 3fec38a0e44e95664c6822322a803f925249f9eb Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Sun, 26 May 2024 10:07:46 +1000 Subject: [PATCH 02/15] Update package imports 1. @lumino/messaging ^2.1 should be ^2.0.1 2. @lumino/messaging ^2.1 should be ^2.0.1 3. istanbul-instrumenter-loader is not maintaned for 6 years --- packages/base-manager/package.json | 1 - packages/base/package.json | 3 +-- packages/controls/package.json | 7 +++---- packages/html-manager/package.json | 2 +- python/jupyterlab_widgets/package.json | 11 +++++++++++ python/widgetsnbextension/package.json | 2 +- 6 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/base-manager/package.json b/packages/base-manager/package.json index 41dcbd1f21..3921c38873 100644 --- a/packages/base-manager/package.json +++ b/packages/base-manager/package.json @@ -50,7 +50,6 @@ "chai": "^4.0.0", "chai-as-promised": "^7.0.0", "expect.js": "^0.3.1", - "istanbul-instrumenter-loader": "^3.0.1", "karma": "^6.3.3", "karma-chrome-launcher": "^3.1.0", "karma-coverage": "^2.0.3", diff --git a/packages/base/package.json b/packages/base/package.json index bc576c14f7..54e434f145 100644 --- a/packages/base/package.json +++ b/packages/base/package.json @@ -35,7 +35,7 @@ "dependencies": { "@jupyterlab/services": "^6.0.0 || ^7.0.0", "@lumino/coreutils": "^1.11.1 || ^2.1", - "@lumino/messaging": "^1.10.1 || ^2.1", + "@lumino/messaging": "^1.10.1 || ^2.0.1", "@lumino/widgets": "^1.30.0 || ^2.1", "@types/backbone": "1.4.14", "@types/lodash": "^4.14.134", @@ -55,7 +55,6 @@ "chai": "^4.0.0", "chai-as-promised": "^7.0.0", "expect.js": "^0.3.1", - "istanbul-instrumenter-loader": "^3.0.1", "karma": "^6.3.3", "karma-chrome-launcher": "^3.1.0", "karma-coverage": "^2.0.3", diff --git a/packages/controls/package.json b/packages/controls/package.json index 8c1d90f663..3ad03c66a2 100644 --- a/packages/controls/package.json +++ b/packages/controls/package.json @@ -34,10 +34,10 @@ "test:unit:ie": "npm run test:unit:default -- --browsers=IE" }, "dependencies": { - "@jupyter-widgets/base": "^6.0.8", - "@lumino/algorithm": "^1.9.1 || ^2.1", + "@jupyter-widgets/base": "^6.0.7", + "@lumino/algorithm": "^1.9.2 || ^2.0.1", "@lumino/domutils": "^1.8.1 || ^2.1", - "@lumino/messaging": "^1.10.1 || ^2.1", + "@lumino/messaging": "^1.10.1 || ^2.0.1", "@lumino/signaling": "^1.10.1 || ^2.1", "@lumino/widgets": "^1.30.0 || ^2.1", "d3-color": "^3.0.1", @@ -57,7 +57,6 @@ "chai": "^4.0.0", "css-loader": "^6.5.1", "expect.js": "^0.3.1", - "istanbul-instrumenter-loader": "^3.0.1", "karma": "^6.3.3", "karma-chrome-launcher": "^3.1.0", "karma-coverage": "^2.0.3", diff --git a/packages/html-manager/package.json b/packages/html-manager/package.json index aed9619ec9..8fbc380345 100644 --- a/packages/html-manager/package.json +++ b/packages/html-manager/package.json @@ -44,7 +44,7 @@ "@jupyterlab/outputarea": "^3.0.0 || ^4.0.0", "@jupyterlab/rendermime": "^3.0.0 || ^4.0.0", "@jupyterlab/rendermime-interfaces": "^3.0.0 || ^4.0.0", - "@lumino/messaging": "^1.10.1 || ^2.1", + "@lumino/messaging": "^1.10.1 || ^2.0.1", "@lumino/widgets": "^1.30.0 || ^2.1", "ajv": "^8.6.0", "jquery": "^3.1.1" diff --git a/python/jupyterlab_widgets/package.json b/python/jupyterlab_widgets/package.json index 71fafd5410..412c8b463a 100644 --- a/python/jupyterlab_widgets/package.json +++ b/python/jupyterlab_widgets/package.json @@ -93,5 +93,16 @@ "extension": true, "outputDir": "labextension", "schemaDir": "./schema" + }, + "optionalDependencies": { + "react": "^18.3.1" + }, + "peerDependencies": { + "react": "*" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } } } diff --git a/python/widgetsnbextension/package.json b/python/widgetsnbextension/package.json index 502030aa7f..f5610f081d 100644 --- a/python/widgetsnbextension/package.json +++ b/python/widgetsnbextension/package.json @@ -28,7 +28,7 @@ "@jupyter-widgets/html-manager": "^1.0.11", "@jupyter-widgets/output": "^6.0.8", "@jupyterlab/services": "^6.0.0 || ^7.0.0", - "@lumino/messaging": "^1.10.1 || ^2.1", + "@lumino/messaging": "^1.10.1 || ^2.0.1", "@lumino/widgets": "^1.30.0 || ^2.1", "backbone": "1.4.0" }, From 2baf3d663ee8c9c33b805fe96ceea6534702960b Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Sun, 26 May 2024 10:25:17 +1000 Subject: [PATCH 03/15] Fix/unify tooltips. --- packages/base/src/widget.ts | 10 ++++++++-- packages/controls/src/widget_bool.ts | 13 +++++-------- packages/controls/src/widget_description.ts | 3 ++- packages/controls/src/widget_selection.ts | 8 ++------ packages/controls/src/widget_string.ts | 16 ++++------------ .../ipywidgets/widgets/widget_output.py | 1 + 6 files changed, 22 insertions(+), 29 deletions(-) diff --git a/packages/base/src/widget.ts b/packages/base/src/widget.ts index aea060fc8d..f4e450b341 100644 --- a/packages/base/src/widget.ts +++ b/packages/base/src/widget.ts @@ -1096,14 +1096,20 @@ export class DOMWidgetView extends WidgetView { } updateTooltip(): void { - const title = this.model.get('tooltip'); + const title = this.tooltip; if (!title) { this.el.removeAttribute('title'); - } else if (this.model.get('description').length === 0) { + } else if (!this.model.get('description')) { this.el.setAttribute('title', title); } } + get tooltip() { + return ( + this.model.get('tooltip') ?? (this.model.get('description')) + ); + } + /** * Update the DOM classes applied to an element, default to this.el. */ diff --git a/packages/controls/src/widget_bool.ts b/packages/controls/src/widget_bool.ts index a08d9ff693..bb7e0ee72a 100644 --- a/packages/controls/src/widget_bool.ts +++ b/packages/controls/src/widget_bool.ts @@ -153,8 +153,7 @@ export class CheckboxView extends DescriptionView { this.descriptionSpan.textContent = description; } this.typeset(this.descriptionSpan); - this.descriptionSpan.title = description; - this.checkbox.title = description; + this.updateTooltip(); } /** @@ -181,13 +180,11 @@ export class CheckboxView extends DescriptionView { } updateTooltip(): void { + super.updateTooltip(); if (!this.checkbox) return; // we might be constructing the parent - const title = this.model.get('tooltip'); - if (!title) { - this.checkbox.removeAttribute('title'); - } else if (this.model.get('description').length === 0) { - this.checkbox.setAttribute('title', title); - } + const title = this.tooltip; + this.checkbox.setAttribute('title', title); + this.descriptionSpan.setAttribute('title', title); } events(): { [e: string]: string } { diff --git a/packages/controls/src/widget_description.ts b/packages/controls/src/widget_description.ts index 2b0ccdcf7f..67aec82c32 100644 --- a/packages/controls/src/widget_description.ts +++ b/packages/controls/src/widget_description.ts @@ -98,8 +98,9 @@ export class DescriptionView extends DOMWidgetView { } updateTooltip(): void { + super.updateTooltip(); if (!this.label) return; - this.label.title = this.model.get('tooltip'); + this.label.title = this.tooltip; } label: HTMLLabelElement; diff --git a/packages/controls/src/widget_selection.ts b/packages/controls/src/widget_selection.ts index 11174799cd..5eb37e2752 100644 --- a/packages/controls/src/widget_selection.ts +++ b/packages/controls/src/widget_selection.ts @@ -68,13 +68,9 @@ export class SelectionView extends DescriptionView { } updateTooltip(): void { + super.updateTooltip(); if (!this.listbox) return; // we might be constructing the parent - const title = this.model.get('tooltip'); - if (!title) { - this.listbox.removeAttribute('title'); - } else if (this.model.get('description').length === 0) { - this.listbox.setAttribute('title', title); - } + this.listbox.setAttribute('title', this.tooltip); } listbox: HTMLSelectElement; diff --git a/packages/controls/src/widget_string.ts b/packages/controls/src/widget_string.ts index 4bdef6c28d..3c708398d8 100644 --- a/packages/controls/src/widget_string.ts +++ b/packages/controls/src/widget_string.ts @@ -379,13 +379,9 @@ export class TextareaView extends StringView { } updateTooltip(): void { + super.updateTooltip(); if (!this.textbox) return; // we might be constructing the parent - const title = this.model.get('tooltip'); - if (!title) { - this.textbox.removeAttribute('title'); - } else if (this.model.get('description').length === 0) { - this.textbox.setAttribute('title', title); - } + this.textbox.setAttribute('title', this.tooltip); } events(): { [e: string]: string } { @@ -505,13 +501,9 @@ export class TextView extends StringView { } updateTooltip(): void { + super.updateTooltip(); if (!this.textbox) return; // we might be constructing the parent - const title = this.model.get('tooltip'); - if (!title) { - this.textbox.removeAttribute('title'); - } else if (this.model.get('description').length === 0) { - this.textbox.setAttribute('title', title); - } + this.textbox.setAttribute('title', this.tooltip); } update(options?: any): void { diff --git a/python/ipywidgets/ipywidgets/widgets/widget_output.py b/python/ipywidgets/ipywidgets/widgets/widget_output.py index 150ac93471..c8729d9fed 100644 --- a/python/ipywidgets/ipywidgets/widgets/widget_output.py +++ b/python/ipywidgets/ipywidgets/widgets/widget_output.py @@ -59,6 +59,7 @@ def func(): msg_id = Unicode('', help="Parent message id of messages to capture").tag(sync=True) outputs = TypedTuple(trait=Dict(), help="The output messages synced from the frontend.").tag(sync=True) + tooltip = Unicode('', allow_none=True, help="A tooltip caption.").tag(sync=True) __counter = 0 From 53adf7572c494f9d74a1c064aae505a38ab2fc75 Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Sun, 26 May 2024 10:28:06 +1000 Subject: [PATCH 04/15] DirectionalLinkModel - remove link to source and target on cleanup. --- packages/controls/src/widget_link.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/controls/src/widget_link.ts b/packages/controls/src/widget_link.ts index 2b90330e41..7de1904898 100644 --- a/packages/controls/src/widget_link.ts +++ b/packages/controls/src/widget_link.ts @@ -85,9 +85,12 @@ export class DirectionalLinkModel extends CoreWidgetModel { undefined ); this.stopListening(this.sourceModel, 'destroy', undefined); + this.set('source', [null, '']); } if (this.targetModel) { this.stopListening(this.targetModel, 'destroy', undefined); + this.set('target', [null, '']); + this.save_changes(); } } From b9d043e1bb4b2ff48d35843e44eeeb79485c94c7 Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Sun, 26 May 2024 10:39:20 +1000 Subject: [PATCH 05/15] Add weakref with opt in automatic widget deletion using 'enable_weakreference'. --- .../ipywidgets/ipywidgets/widgets/__init__.py | 2 +- .../ipywidgets/widgets/tests/test_widget.py | 173 +++++++++++++++++- .../widgets/tests/test_widget_box.py | 92 ++++++++-- .../ipywidgets/ipywidgets/widgets/widget.py | 159 ++++++++++------ 4 files changed, 350 insertions(+), 76 deletions(-) diff --git a/python/ipywidgets/ipywidgets/widgets/__init__.py b/python/ipywidgets/ipywidgets/widgets/__init__.py index b90d3ee111..0951bad905 100644 --- a/python/ipywidgets/ipywidgets/widgets/__init__.py +++ b/python/ipywidgets/ipywidgets/widgets/__init__.py @@ -1,7 +1,7 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. -from .widget import Widget, CallbackDispatcher, register, widget_serialization +from .widget import Widget, CallbackDispatcher, register, widget_serialization, enable_weakreference, disable_weakreference from .domwidget import DOMWidget from .valuewidget import ValueWidget diff --git a/python/ipywidgets/ipywidgets/widgets/tests/test_widget.py b/python/ipywidgets/ipywidgets/widgets/tests/test_widget.py index c5aa36048a..34fd9402a2 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_widget.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_widget.py @@ -3,17 +3,21 @@ """Test Widget.""" +import copy +import gc import inspect +import weakref import pytest from IPython.core.interactiveshell import InteractiveShell from IPython.display import display from IPython.utils.capture import capture_output +import ipywidgets as ipw + from .. import widget from ..widget import Widget from ..widget_button import Button -import copy def test_no_widget_view(): @@ -88,4 +92,169 @@ def test_widget_copy(): with pytest.raises(NotImplementedError): copy.copy(button) with pytest.raises(NotImplementedError): - copy.deepcopy(button) \ No newline at end of file + copy.deepcopy(button) + + +def test_widget_open(): + button = Button() + model_id = button.model_id + assert model_id in widget._instances + spec = button.get_view_spec() + assert list(spec) == ["version_major", "version_minor", "model_id"] + assert spec["model_id"] + button.close() + assert model_id not in widget._instances + with pytest.raises(RuntimeError, match="Widget is closed"): + button.open() + with pytest.raises(RuntimeError, match="Widget is closed"): + button.get_view_spec() + + +@pytest.mark.parametrize( + "class_name", + [ + "Accordion", + "AppLayout", + "Audio", + "BoundedFloatText", + "BoundedIntText", + "Box", + "Button", + "ButtonStyle", + "Checkbox", + "ColorPicker", + "ColorsInput", + "Combobox", + "Controller", + "CoreWidget", + "DOMWidget", + "DatePicker", + "DatetimePicker", + "Dropdown", + "FileUpload", + "FloatLogSlider", + "FloatProgress", + "FloatRangeSlider", + "FloatSlider", + "FloatText", + "FloatsInput", + "GridBox", + "HBox", + "HTML", + "HTMLMath", + "Image", + "IntProgress", + "IntRangeSlider", + "IntSlider", + "IntText", + "IntsInput", + "Label", + "Layout", + "NaiveDatetimePicker", + "Output", + "Password", + "Play", + "RadioButtons", + "Select", + "SelectMultiple", + "SelectionRangeSlider", + "SelectionSlider", + "SliderStyle", + "Stack", + "Style", + "Tab", + "TagsInput", + "Text", + "Textarea", + "TimePicker", + "ToggleButton", + "ToggleButtons", + "ToggleButtonsStyle", + "TwoByTwoLayout", + "VBox", + "Valid", + "ValueWidget", + "Video", + "Widget", + ], +) +@pytest.mark.parametrize("enable_weakref", [True, False]) +def test_weakreference(class_name, enable_weakref): + # Ensure the base instance of all widgets can be deleted / garbage collected. + if enable_weakref: + ipw.enable_weakreference() + cls = getattr(ipw, class_name) + if class_name in ['SelectionRangeSlider', 'SelectionSlider']: + kwgs = {"options": [1, 2, 4]} + else: + kwgs = {} + try: + w = cls(**kwgs) + deleted = False + def on_delete(): + nonlocal deleted + deleted = True + weakref.finalize(w, on_delete) + # w should be the only strong ref to the widget. + # calling `del` should invoke its immediate deletion calling the `__del__` method. + if not enable_weakref: + w.close() + del w + gc.collect() + assert deleted + finally: + if enable_weakref: + ipw.disable_weakreference() + + +@pytest.mark.parametrize("weakref_enabled", [True, False]) +def test_button_weakreference(weakref_enabled: bool): + try: + click_count = 0 + deleted = False + + def on_delete(): + nonlocal deleted + deleted = True + + class TestButton(Button): + def my_click(self, b): + nonlocal click_count + click_count += 1 + + b = TestButton(description="button") + weakref.finalize(b, on_delete) + b_ref = weakref.ref(b) + assert b in widget._instances.values() + + b.on_click(b.my_click) + b.on_click(lambda x: setattr(x, "clicked", True)) + + b.click() + assert click_count == 1 + + if weakref_enabled: + ipw.enable_weakreference() + assert b in widget._instances.values(), "Instances not transferred" + ipw.disable_weakreference() + assert b in widget._instances.values(), "Instances not transferred" + ipw.enable_weakreference() + assert b in widget._instances.values(), "Instances not transferred" + + b.click() + assert click_count == 2 + assert getattr(b, "clicked") + + del b + gc.collect() + if weakref_enabled: + assert deleted + else: + assert not deleted + assert b_ref() in widget._instances.values() + b_ref().close() + gc.collect() + assert deleted, "Closing should remove the last strong reference." + + finally: + ipw.disable_weakreference() diff --git a/python/ipywidgets/ipywidgets/widgets/tests/test_widget_box.py b/python/ipywidgets/ipywidgets/widgets/tests/test_widget_box.py index 551f68dcc4..5d50324d08 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_widget_box.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_widget_box.py @@ -1,33 +1,85 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. -from unittest import TestCase +import gc +import weakref +import pytest from traitlets import TraitError import ipywidgets as widgets -class TestBox(TestCase): +def test_box_construction(): + box = widgets.Box() + assert box.get_state()["children"] == [] - def test_construction(self): - box = widgets.Box() - assert box.get_state()['children'] == [] - def test_construction_with_children(self): - html = widgets.HTML('some html') - slider = widgets.IntSlider() - box = widgets.Box([html, slider]) - children_state = box.get_state()['children'] - assert children_state == [ - widgets.widget._widget_to_json(html, None), - widgets.widget._widget_to_json(slider, None), - ] +def test_box_construction_with_children(): + html = widgets.HTML("some html") + slider = widgets.IntSlider() + box = widgets.Box([html, slider]) + children_state = box.get_state()["children"] + assert children_state == [ + widgets.widget._widget_to_json(html, None), + widgets.widget._widget_to_json(slider, None), + ] - def test_construction_style(self): - box = widgets.Box(box_style='warning') - assert box.get_state()['box_style'] == 'warning' - def test_construction_invalid_style(self): - with self.assertRaises(TraitError): - widgets.Box(box_style='invalid') +def test_box_construction_style(): + box = widgets.Box(box_style="warning") + assert box.get_state()["box_style"] == "warning" + + +def test_construction_invalid_style(): + with pytest.raises(TraitError): + widgets.Box(box_style="invalid") + + +def test_box_validate_mode(): + slider = widgets.IntSlider() + closed_button = widgets.Button() + closed_button.close() + with pytest.raises(TraitError, match="Invalid or closed items found.*"): + widgets.Box( + children=[closed_button, slider, "Not a widget"] + ) + box = widgets.Box( + children=[closed_button, slider, "Not a widget"], + validate_mode="log_error", + ) + assert len (box.children) == 1, "Invalid items should be dropped." + assert slider in box.children + + box.validate_mode = "raise" + with pytest.raises(TraitError): + box.children += ("Not a widget", closed_button) + + +def test_box_gc(): + widgets.VBox._active_widgets + widgets.enable_weakreference() + # Test Box gc collected and children lifecycle managed. + try: + deleted = False + + class TestButton(widgets.Button): + def my_click(self, b): + pass + + button = TestButton(description="button") + button.on_click(button.my_click) + + b = widgets.VBox(children=[button]) + + def on_delete(): + nonlocal deleted + deleted = True + + weakref.finalize(b, on_delete) + del b + gc.collect() + assert deleted + widgets.VBox._active_widgets + finally: + widgets.disable_weakreference() diff --git a/python/ipywidgets/ipywidgets/widgets/widget.py b/python/ipywidgets/ipywidgets/widgets/widget.py index 2dc674097d..e90cdfea5d 100644 --- a/python/ipywidgets/ipywidgets/widgets/widget.py +++ b/python/ipywidgets/ipywidgets/widgets/widget.py @@ -6,13 +6,13 @@ in the Jupyter notebook front-end. """ import os -import sys import typing +import weakref from contextlib import contextmanager from collections.abc import Iterable from IPython import get_ipython from traitlets import ( - Any, HasTraits, Unicode, Dict, Instance, List, Int, Set, Bytes, observe, default, Container, + Any, HasTraits, Unicode, Dict, Instance, List, Int, Set, observe, default, Container, Undefined) from json import loads as jsonloads, dumps as jsondumps from .. import comm @@ -41,17 +41,39 @@ def envset(name, default): PROTOCOL_VERSION_MAJOR = __protocol_version__.split('.')[0] CONTROL_PROTOCOL_VERSION_MAJOR = __control_protocol_version__.split('.')[0] JUPYTER_WIDGETS_ECHO = envset('JUPYTER_WIDGETS_ECHO', default=True) -# we keep a strong reference for every widget created, for a discussion on using weak references see: +# for a discussion on using weak references see: # https://github.com/jupyter-widgets/ipywidgets/issues/1345 _instances : typing.MutableMapping[str, "Widget"] = {} +def enable_weakreference(): + """Use a WeakValueDictionary instead of a standard dictionary to map + `comm_id` to `widget` for every widget instance. + + By default widgets are mapped using a standard dictionary. Use this feature + to permit widget garbage collection. + """ + global _instances + if not isinstance(_instances, weakref.WeakValueDictionary): + _instances = weakref.WeakValueDictionary(_instances) + +def disable_weakreference(): + """Use a Dictionary to map `comm_id` to `widget` for every widget instance. + + Note: this is the default setting and maintains a strong reference to the + the widget preventing automatic garbage collection. If the close method + is called, the widget will remove itself enabling garbage collection. + """ + global _instances + if isinstance(_instances, weakref.WeakValueDictionary): + _instances = dict(_instances) + def _widget_to_json(x, obj): - if isinstance(x, dict): - return {k: _widget_to_json(v, obj) for k, v in x.items()} + if isinstance(x, Widget): + return f"IPY_MODEL_{x.model_id}" elif isinstance(x, (list, tuple)): return [_widget_to_json(v, obj) for v in x] - elif isinstance(x, Widget): - return "IPY_MODEL_" + x.model_id + elif isinstance(x, dict): + return {k: _widget_to_json(v, obj) for k, v in x.items()} else: return x @@ -215,18 +237,6 @@ def register_callback(self, callback, remove=False): elif not remove and callback not in self.callbacks: self.callbacks.append(callback) -def _show_traceback(method): - """decorator for showing tracebacks""" - def m(self, *args, **kwargs): - try: - return(method(self, *args, **kwargs)) - except Exception as e: - ip = get_ipython() - if ip is None: - self.log.warning("Exception in widget method %s: %s", method, e, exc_info=True) - else: - ip.showtraceback() - return m class WidgetRegistry: @@ -304,7 +314,7 @@ class Widget(LoggingHasTraits): #------------------------------------------------------------------------- _widget_construction_callback = None _control_comm = None - + @_staticproperty def widgets(): # Because this is a static attribute, it will be accessed when initializing this class. In that case, since a user @@ -461,7 +471,7 @@ def _get_embed_state(self, drop_defaults=False): return state def get_view_spec(self): - return dict(version_major=2, version_minor=0, model_id=self._model_id) + return {"version_major":2, "version_minor":0, "model_id": self._model_id} #------------------------------------------------------------------------- # Traits @@ -499,11 +509,12 @@ def _default_keys(self): #------------------------------------------------------------------------- def __init__(self, **kwargs): """Public constructor""" - self._model_id = kwargs.pop('model_id', None) + if 'model_id' in kwargs: + self.comm = self._create_comm(kwargs.pop('model_id')) super().__init__(**kwargs) + self.open() Widget._call_widget_constructed(self) - self.open() def __copy__(self): raise NotImplementedError("Widgets cannot be copied; custom implementation required") @@ -519,53 +530,84 @@ def __del__(self): # Properties #------------------------------------------------------------------------- - def open(self): - """Open a comm to the frontend if one isn't already open.""" - if self.comm is None: - state, buffer_paths, buffers = _remove_buffers(self.get_state()) - args = dict(target_name='jupyter.widget', - data={'state': state, 'buffer_paths': buffer_paths}, - buffers=buffers, - metadata={'version': __protocol_version__} - ) - if self._model_id is not None: - args['comm_id'] = self._model_id + @default('comm') + def _default_comm(self): + return self._create_comm() - self.comm = comm.create_comm(**args) + def open(self): + """Open a comm to the frontend if one isn't already open.""" + assert self._model_id + + + def _create_comm(self, comm_id=None): + """Open a new comm to the frontend.""" + state, buffer_paths, buffers = _remove_buffers(self.get_state()) + self.comm = comm_ = comm.create_comm( + target_name="jupyter.widget", + data={"state": state, "buffer_paths": buffer_paths}, + buffers=buffers, + metadata={"version": __protocol_version__}, + comm_id=comm_id, + ) + return comm_ + @observe('comm') def _comm_changed(self, change): """Called when the comm is changed.""" - if change['new'] is None: - return - self._model_id = self.model_id + if change['old']: + change['old'].on_msg(None) + change['old'].close() + # On python shutdown _instances can be None + if isinstance(_instances, dict): + _instances.pop(change['old'].comm_id, None) + if change['new']: + if isinstance(_instances, dict): + _instances[change['new'].comm_id] = self + + # prevent memory leaks by using a weak reference to self. + ref = weakref.ref(self) + def _handle_msg(msg): + self_ = ref() + if self_ is not None: + try: + self_._handle_msg(msg) + except Exception as e: + self_._show_traceback(self_._handle_msg, e) + + change['new'].on_msg(_handle_msg) + - self.comm.on_msg(self._handle_msg) - _instances[self.model_id] = self @property def model_id(self): + return self._model_id + + @property + def _model_id(self): """Gets the model id of this widget. If a Comm doesn't exist yet, a Comm will be created automagically.""" - return self.comm.comm_id + if not self._repr_mimebundle_: + # a closed widget will not be found at the frontend so raise an error here. + msg = f"Widget is closed: {self!r}" + raise RuntimeError(msg) + return getattr(self.comm, "comm_id", None) + #------------------------------------------------------------------------- # Methods #------------------------------------------------------------------------- def close(self): - """Close method. + """Permanently close the widget. Closes the underlying comm. When the comm is closed, all of the widget views are automatically removed from the front-end.""" - if self.comm is not None: - _instances.pop(self.model_id, None) - self.comm.close() - self.comm = None - self._repr_mimebundle_ = None + self._repr_mimebundle_ = None + self.comm = None def send_state(self, key=None): """Sends the widget state, or a piece of it, to the front-end, if it exists. @@ -693,15 +735,18 @@ def notify_change(self, change): # Send the state to the frontend before the user-registered callbacks # are called. name = change['name'] - if self.comm is not None and getattr(self.comm, 'kernel', True) is not None: + comm = self._trait_values.get('comm') + if comm and getattr(comm, 'kernel', None): # Make sure this isn't information that the front-end just sent us. - if name in self.keys and self._should_send_property(name, getattr(self, name)): + if name in self.keys and self._should_send_property(name, change['new']): # Send new state to front-end self.send_state(key=name) super().notify_change(change) def __repr__(self): - return self._gen_repr_from_keys(self._repr_keys()) + if not self._repr_mimebundle_: + return f'' + return self._gen_repr_from_keys(self._repr_keys()) #------------------------------------------------------------------------- # Support methods @@ -759,7 +804,6 @@ def _should_send_property(self, key, value): return True # Event handlers - @_show_traceback def _handle_msg(self, msg): """Called when a msg is received from the front-end""" data = msg['content']['data'] @@ -785,6 +829,14 @@ def _handle_msg(self, msg): else: self.log.error('Unknown front-end to back-end widget msg with method "%s"' % method) + def _show_traceback(self, method, e:Exception): + ip = get_ipython() + if ip is None: + self.log.warning("Exception in widget method %s: %s", method, e, exc_info=True) + else: + ip.showtraceback() + + def _handle_custom_msg(self, content, buffers): """Called when a custom msg is received.""" self._msg_callbacks(self, content, buffers) @@ -821,8 +873,9 @@ def _repr_mimebundle_(self, **kwargs): def _send(self, msg, buffers=None): """Sends a message to the model in the front-end.""" - if self.comm is not None and (self.comm.kernel is not None if hasattr(self.comm, "kernel") else True): - self.comm.send(data=msg, buffers=buffers) + comm = self.comm + if comm is not None and getattr(comm, "kernel", True): + comm.send(data=msg, buffers=buffers) def _repr_keys(self): traits = self.traits() From cf8ef63c360504f0f5f0ad4664a20ff884ef4fbe Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Sun, 26 May 2024 10:55:27 +1000 Subject: [PATCH 06/15] Add Children for Box (and subclasses) to support validation of any type of iterable plus a configurable option to omit invalid items (including closed widgets) and log a warning or error. --- .../ipywidgets/widgets/widget_box.py | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/python/ipywidgets/ipywidgets/widgets/widget_box.py b/python/ipywidgets/ipywidgets/widgets/widget_box.py index 740e54cb1a..598bb40ef2 100644 --- a/python/ipywidgets/ipywidgets/widgets/widget_box.py +++ b/python/ipywidgets/ipywidgets/widgets/widget_box.py @@ -1,3 +1,4 @@ + # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. @@ -7,14 +8,16 @@ group other widgets together and control their relative layouts. """ +from __future__ import annotations + +import typing + +from traitlets import CaselessStrEnum, TraitError, TraitType, Unicode -from .widget import register, widget_serialization, Widget +from .docutils import doc_subst from .domwidget import DOMWidget +from .widget import Widget, register, widget_serialization from .widget_core import CoreWidget -from .docutils import doc_subst -from .trait_types import TypedTuple -from traitlets import Unicode, CaselessStrEnum, Instance - _doc_snippets = {} _doc_snippets['box_params'] = """ @@ -25,8 +28,35 @@ one of 'success', 'info', 'warning' or 'danger', or ''. Applies a predefined style to the box. Defaults to '', which applies no pre-defined style. + + validate_mode: str + one of 'raise', 'warning', error'. + How invalid children will be treated. + 'raise' will raise a trait error. + 'warning' and 'error' will log an error using box.log dropping + the invalid items from children. """ +class Children(TraitType['tuple[Widget,...]', typing.Iterable[Widget]]): + default_value = () + + def validate(self, obj: Box, value: typing.Iterable[Widget]): + valid, invalid = [], [] + for v in value: + if isinstance(v, Widget) and v._repr_mimebundle_: + valid.append(v) + else: + invalid.append(v) + if invalid: + msg = f'Invalid or closed items found: {invalid}' + if obj.validate_mode == 'log_warning': + obj.log.warning(msg) + elif obj.validate_mode == 'log_error': + obj.log.error(msg) + else: + raise TraitError(msg) + return tuple(valid) + @register @doc_subst(_doc_snippets) @@ -48,19 +78,23 @@ class Box(DOMWidget, CoreWidget): """ _model_name = Unicode('BoxModel').tag(sync=True) _view_name = Unicode('BoxView').tag(sync=True) + tooltip = Unicode('', allow_none=True, help='A tooltip caption.').tag(sync=True) + validate_mode = CaselessStrEnum(['raise', 'log_warning', 'log_error'], 'raise') # Child widgets in the container. # Using a tuple here to force reassignment to update the list. # When a proper notifying-list trait exists, use that instead. - children = TypedTuple(trait=Instance(Widget), help="List of widget children").tag( - sync=True, **widget_serialization) + children = Children(help='List of widget children').tag( + sync=True, **widget_serialization + ) box_style = CaselessStrEnum( values=['success', 'info', 'warning', 'danger', ''], default_value='', help="""Use a predefined styling for the box.""").tag(sync=True) - + def __init__(self, children=(), **kwargs): - kwargs['children'] = children + if children: + kwargs['children'] = children super().__init__(**kwargs) @register From 54bd2c67b49326d1db202fb22b239436d42632c7 Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Sun, 26 May 2024 11:00:32 +1000 Subject: [PATCH 07/15] Update yarn.lock (ran jlpm dlx yarn-berry-deduplicate). --- yarn.lock | 630 ++++-------------------------------------------------- 1 file changed, 45 insertions(+), 585 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3cf819b206..96205a8920 100644 --- a/yarn.lock +++ b/yarn.lock @@ -751,7 +751,6 @@ __metadata: chai: ^4.0.0 chai-as-promised: ^7.0.0 expect.js: ^0.3.1 - istanbul-instrumenter-loader: ^3.0.1 karma: ^6.3.3 karma-chrome-launcher: ^3.1.0 karma-coverage: ^2.0.3 @@ -777,7 +776,7 @@ __metadata: dependencies: "@jupyterlab/services": ^6.0.0 || ^7.0.0 "@lumino/coreutils": ^1.11.1 || ^2.1 - "@lumino/messaging": ^1.10.1 || ^2.1 + "@lumino/messaging": ^1.10.1 || ^2.0.1 "@lumino/widgets": ^1.30.0 || ^2.1 "@types/backbone": 1.4.14 "@types/base64-js": ^1.2.5 @@ -793,7 +792,6 @@ __metadata: chai: ^4.0.0 chai-as-promised: ^7.0.0 expect.js: ^0.3.1 - istanbul-instrumenter-loader: ^3.0.1 jquery: ^3.1.1 karma: ^6.3.3 karma-chrome-launcher: ^3.1.0 @@ -820,9 +818,9 @@ __metadata: dependencies: "@jupyter-widgets/base": ^6.0.7 "@jupyterlab/services": ^6.0.0 || ^7.0.0 - "@lumino/algorithm": ^1.9.1 || ^2.1 + "@lumino/algorithm": ^1.9.2 || ^2.0.1 "@lumino/domutils": ^1.8.1 || ^2.1 - "@lumino/messaging": ^1.10.1 || ^2.1 + "@lumino/messaging": ^1.10.1 || ^2.0.1 "@lumino/signaling": ^1.10.1 || ^2.1 "@lumino/widgets": ^1.30.0 || ^2.1 "@types/d3-color": ^3.0.2 @@ -837,7 +835,6 @@ __metadata: d3-color: ^3.0.1 d3-format: ^3.0.1 expect.js: ^0.3.1 - istanbul-instrumenter-loader: ^3.0.1 jquery: ^3.1.1 karma: ^6.3.3 karma-chrome-launcher: ^3.1.0 @@ -948,7 +945,7 @@ __metadata: "@jupyterlab/outputarea": ^3.0.0 || ^4.0.0 "@jupyterlab/rendermime": ^3.0.0 || ^4.0.0 "@jupyterlab/rendermime-interfaces": ^3.0.0 || ^4.0.0 - "@lumino/messaging": ^1.10.1 || ^2.1 + "@lumino/messaging": ^1.10.1 || ^2.0.1 "@lumino/widgets": ^1.30.0 || ^2.1 "@types/jquery": ^3.5.16 "@types/mocha": ^9.0.0 @@ -1017,10 +1014,19 @@ __metadata: jquery: ^3.1.1 npm-run-all: ^4.1.5 prettier: ^2.3.2 + react: ^18.3.1 rimraf: ^3.0.2 semver: ^7.3.5 source-map-loader: ^4.0.1 typescript: ~4.9.4 + peerDependencies: + react: "*" + dependenciesMeta: + react: + optional: true + peerDependenciesMeta: + react: + optional: true languageName: unknown linkType: soft @@ -1034,7 +1040,7 @@ __metadata: "@jupyter-widgets/html-manager": ^1.0.10 "@jupyter-widgets/output": ^6.0.7 "@jupyterlab/services": ^6.0.0 || ^7.0.0 - "@lumino/messaging": ^1.10.1 || ^2.1 + "@lumino/messaging": ^1.10.1 || ^2.0.1 "@lumino/widgets": ^1.30.0 || ^2.1 backbone: 1.4.0 css-loader: ^6.5.1 @@ -2755,20 +2761,13 @@ __metadata: languageName: node linkType: hard -"@lumino/algorithm@npm:^1.11.1 || ^2.0.0, @lumino/algorithm@npm:^2.0.1": +"@lumino/algorithm@npm:^1.11.1 || ^2.0.0, @lumino/algorithm@npm:^1.9.2 || ^2.0.1, @lumino/algorithm@npm:^2.0.1": version: 2.0.1 resolution: "@lumino/algorithm@npm:2.0.1" checksum: cbf7fcf6ee6b785ea502cdfddc53d61f9d353dcb9659343511d5cd4b4030be2ff2ca4c08daec42f84417ab0318a3d9972a17319fa5231693e109ab112dcf8000 languageName: node linkType: hard -"@lumino/algorithm@npm:^1.9.1 || ^2.1, @lumino/algorithm@npm:^1.9.2": - version: 1.9.2 - resolution: "@lumino/algorithm@npm:1.9.2" - checksum: a89e7c63504236119634858e271db1cc649684d30ced5a6ebe2788af7c0837f1e05a6fd3047d8525eb756c42ce137f76b3688f75fd3ef915b71cd4f213dfbb96 - languageName: node - linkType: hard - "@lumino/application@npm:^2.3.1": version: 2.3.1 resolution: "@lumino/application@npm:2.3.1" @@ -2780,15 +2779,6 @@ __metadata: languageName: node linkType: hard -"@lumino/collections@npm:^1.9.3": - version: 1.9.3 - resolution: "@lumino/collections@npm:1.9.3" - dependencies: - "@lumino/algorithm": ^1.9.2 - checksum: 1c87a12743eddd6f6b593e47945a5645e2f99ad61c5192499b0745e48ee9aff263c7145541e77dfeea4c9f50bdd017fddfa47bfc60e718de4f28533ce45bf8c3 - languageName: node - linkType: hard - "@lumino/collections@npm:^2.0.1": version: 2.0.1 resolution: "@lumino/collections@npm:2.0.1" @@ -2860,17 +2850,7 @@ __metadata: languageName: node linkType: hard -"@lumino/messaging@npm:^1.10.1 || ^2.1": - version: 1.10.3 - resolution: "@lumino/messaging@npm:1.10.3" - dependencies: - "@lumino/algorithm": ^1.9.2 - "@lumino/collections": ^1.9.3 - checksum: 1131e80379fa9b8a9b5d3418c90e25d4be48e2c92ec711518190772f9e8845a695bef45daddd06a129168cf6f158c8ad80ae86cb245f566e9195bbd9a0843b7a - languageName: node - linkType: hard - -"@lumino/messaging@npm:^2.0.1": +"@lumino/messaging@npm:^1.10.1 || ^2.0.1, @lumino/messaging@npm:^2.0.1": version: 2.0.1 resolution: "@lumino/messaging@npm:2.0.1" dependencies: @@ -3758,14 +3738,7 @@ __metadata: languageName: node linkType: hard -"@types/minimatch@npm:*": - version: 5.1.2 - resolution: "@types/minimatch@npm:5.1.2" - checksum: 0391a282860c7cb6fe262c12b99564732401bdaa5e395bee9ca323c312c1a0f45efbf34dce974682036e857db59a5c9b1da522f3d6055aeead7097264c8705a8 - languageName: node - linkType: hard - -"@types/minimatch@npm:^3.0.3": +"@types/minimatch@npm:*, @types/minimatch@npm:^3.0.3": version: 3.0.5 resolution: "@types/minimatch@npm:3.0.5" checksum: c41d136f67231c3131cf1d4ca0b06687f4a322918a3a5adddc87ce90ed9dbd175a3610adee36b106ae68c0b92c637c35e02b58c8a56c424f71d30993ea220b92 @@ -3786,16 +3759,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=10.0.0": - version: 20.12.12 - resolution: "@types/node@npm:20.12.12" - dependencies: - undici-types: ~5.26.4 - checksum: 5373983874b9af7c216e7ca5d26b32a8d9829c703a69f1e66f2113598b5be8582c0e009ca97369f1ec9a6282b3f92812208d06eb1e9fc3bd9b939b022303d042 - languageName: node - linkType: hard - -"@types/node@npm:^17.0.2": +"@types/node@npm:*, @types/node@npm:>=10.0.0, @types/node@npm:^17.0.2": version: 17.0.45 resolution: "@types/node@npm:17.0.45" checksum: aa04366b9103b7d6cfd6b2ef64182e0eaa7d4462c3f817618486ea0422984c51fc69fd0d436eae6c9e696ddfdbec9ccaa27a917f7c2e8c75c5d57827fe3d95e8 @@ -3868,16 +3832,7 @@ __metadata: languageName: node linkType: hard -"@types/sinon@npm:*": - version: 17.0.3 - resolution: "@types/sinon@npm:17.0.3" - dependencies: - "@types/sinonjs__fake-timers": "*" - checksum: c8e9956d9c90fe1ec1cc43085ae48897f93f9ea86e909ab47f255ea71f5229651faa070393950fb6923aef426c84e92b375503f9f8886ef44668b82a8ee49e9a - languageName: node - linkType: hard - -"@types/sinon@npm:^10.0.2": +"@types/sinon@npm:*, @types/sinon@npm:^10.0.2": version: 10.0.20 resolution: "@types/sinon@npm:10.0.20" dependencies: @@ -4808,7 +4763,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:8.12.0": +"ajv@npm:8.12.0, ajv@npm:^8.0.0, ajv@npm:^8.12.0, ajv@npm:^8.6.0, ajv@npm:^8.9.0": version: 8.12.0 resolution: "ajv@npm:8.12.0" dependencies: @@ -4820,18 +4775,6 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^5.0.0": - version: 5.5.2 - resolution: "ajv@npm:5.5.2" - dependencies: - co: ^4.6.0 - fast-deep-equal: ^1.0.0 - fast-json-stable-stringify: ^2.0.0 - json-schema-traverse: ^0.3.0 - checksum: a69645c843e1676b0ae1c5192786e546427f808f386d26127c6585479378066c64341ceec0b127b6789d79628e71d2a732d402f575b98f9262db230d7b715a94 - languageName: node - linkType: hard - "ajv@npm:^6.12.4, ajv@npm:^6.12.5": version: 6.12.6 resolution: "ajv@npm:6.12.6" @@ -4844,32 +4787,13 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.0, ajv@npm:^8.12.0, ajv@npm:^8.6.0, ajv@npm:^8.9.0": - version: 8.13.0 - resolution: "ajv@npm:8.13.0" - dependencies: - fast-deep-equal: ^3.1.3 - json-schema-traverse: ^1.0.0 - require-from-string: ^2.0.2 - uri-js: ^4.4.1 - checksum: 6de82d0b2073e645ca3300561356ddda0234f39b35d2125a8700b650509b296f41c00ab69f53178bbe25ad688bd6ac3747ab44101f2f4bd245952e8fd6ccc3c1 - languageName: node - linkType: hard - -"ansi-colors@npm:4.1.1": +"ansi-colors@npm:4.1.1, ansi-colors@npm:^4.1.1": version: 4.1.1 resolution: "ansi-colors@npm:4.1.1" checksum: 138d04a51076cb085da0a7e2d000c5c0bb09f6e772ed5c65c53cb118d37f6c5f1637506d7155fb5f330f0abcf6f12fa2e489ac3f8cdab9da393bf1bb4f9a32b0 languageName: node linkType: hard -"ansi-colors@npm:^4.1.1": - version: 4.1.3 - resolution: "ansi-colors@npm:4.1.3" - checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e - languageName: node - linkType: hard - "ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0, ansi-escapes@npm:^4.3.2": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" @@ -4879,13 +4803,6 @@ __metadata: languageName: node linkType: hard -"ansi-regex@npm:^2.0.0": - version: 2.1.1 - resolution: "ansi-regex@npm:2.1.1" - checksum: 190abd03e4ff86794f338a31795d262c1dfe8c91f7e01d04f13f646f1dcb16c5800818f886047876f1272f065570ab86b24b99089f8b68a0e11ff19aed4ca8f1 - languageName: node - linkType: hard - "ansi-regex@npm:^3.0.0": version: 3.0.1 resolution: "ansi-regex@npm:3.0.1" @@ -4914,13 +4831,6 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^2.2.1": - version: 2.2.1 - resolution: "ansi-styles@npm:2.2.1" - checksum: ebc0e00381f2a29000d1dac8466a640ce11943cef3bda3cd0020dc042e31e1058ab59bf6169cd794a54c3a7338a61ebc404b7c91e004092dd20e028c432c9c2c - languageName: node - linkType: hard - "ansi-styles@npm:^3.2.1": version: 3.2.1 resolution: "ansi-styles@npm:3.2.1" @@ -5202,43 +5112,7 @@ __metadata: languageName: node linkType: hard -"babel-code-frame@npm:^6.26.0": - version: 6.26.0 - resolution: "babel-code-frame@npm:6.26.0" - dependencies: - chalk: ^1.1.3 - esutils: ^2.0.2 - js-tokens: ^3.0.2 - checksum: 9410c3d5a921eb02fa409675d1a758e493323a49e7b9dddb7a2a24d47e61d39ab1129dd29f9175836eac9ce8b1d4c0a0718fcdc57ce0b865b529fd250dbab313 - languageName: node - linkType: hard - -"babel-generator@npm:^6.18.0": - version: 6.26.1 - resolution: "babel-generator@npm:6.26.1" - dependencies: - babel-messages: ^6.23.0 - babel-runtime: ^6.26.0 - babel-types: ^6.26.0 - detect-indent: ^4.0.0 - jsesc: ^1.3.0 - lodash: ^4.17.4 - source-map: ^0.5.7 - trim-right: ^1.0.1 - checksum: 5397f4d4d1243e7157e3336be96c10fcb1f29f73bf2d9842229c71764d9a6431397d249483a38c4d8b1581459e67be4df6f32d26b1666f02d0f5bfc2c2f25193 - languageName: node - linkType: hard - -"babel-messages@npm:^6.23.0": - version: 6.23.0 - resolution: "babel-messages@npm:6.23.0" - dependencies: - babel-runtime: ^6.22.0 - checksum: c8075c17587a33869e1a5bd0a5b73bbe395b68188362dacd5418debbc7c8fd784bcd3295e81ee7e410dc2c2655755add6af03698c522209f6a68334c15e6d6ca - languageName: node - linkType: hard - -"babel-runtime@npm:^6.22.0, babel-runtime@npm:^6.23.0, babel-runtime@npm:^6.26.0": +"babel-runtime@npm:^6.23.0": version: 6.26.0 resolution: "babel-runtime@npm:6.26.0" dependencies: @@ -5248,57 +5122,6 @@ __metadata: languageName: node linkType: hard -"babel-template@npm:^6.16.0": - version: 6.26.0 - resolution: "babel-template@npm:6.26.0" - dependencies: - babel-runtime: ^6.26.0 - babel-traverse: ^6.26.0 - babel-types: ^6.26.0 - babylon: ^6.18.0 - lodash: ^4.17.4 - checksum: 028dd57380f09b5641b74874a19073c53c4fb3f1696e849575aae18f8c80eaf21db75209057db862f3b893ce2cd9b795d539efa591b58f4a0fb011df0a56fbed - languageName: node - linkType: hard - -"babel-traverse@npm:^6.18.0, babel-traverse@npm:^6.26.0": - version: 6.26.0 - resolution: "babel-traverse@npm:6.26.0" - dependencies: - babel-code-frame: ^6.26.0 - babel-messages: ^6.23.0 - babel-runtime: ^6.26.0 - babel-types: ^6.26.0 - babylon: ^6.18.0 - debug: ^2.6.8 - globals: ^9.18.0 - invariant: ^2.2.2 - lodash: ^4.17.4 - checksum: fca037588d2791ae0409f1b7aa56075b798699cccc53ea04d82dd1c0f97b9e7ab17065f7dd3ecd69101d7874c9c8fd5e0f88fa53abbae1fe94e37e6b81ebcb8d - languageName: node - linkType: hard - -"babel-types@npm:^6.18.0, babel-types@npm:^6.26.0": - version: 6.26.0 - resolution: "babel-types@npm:6.26.0" - dependencies: - babel-runtime: ^6.26.0 - esutils: ^2.0.2 - lodash: ^4.17.4 - to-fast-properties: ^1.0.3 - checksum: d16b0fa86e9b0e4c2623be81d0a35679faff24dd2e43cde4ca58baf49f3e39415a011a889e6c2259ff09e1228e4c3a3db6449a62de59e80152fe1ce7398fde76 - languageName: node - linkType: hard - -"babylon@npm:^6.18.0": - version: 6.18.0 - resolution: "babylon@npm:6.18.0" - bin: - babylon: ./bin/babylon.js - checksum: 0777ae0c735ce1cbfc856d627589ed9aae212b84fb0c03c368b55e6c5d3507841780052808d0ad46e18a2ba516e93d55eeed8cd967f3b2938822dfeccfb2a16d - languageName: node - linkType: hard - "backbone@npm:1.4.0": version: 1.4.0 resolution: "backbone@npm:1.4.0" @@ -5731,19 +5554,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^1.1.3": - version: 1.1.3 - resolution: "chalk@npm:1.1.3" - dependencies: - ansi-styles: ^2.2.1 - escape-string-regexp: ^1.0.2 - has-ansi: ^2.0.0 - strip-ansi: ^3.0.0 - supports-color: ^2.0.0 - checksum: 9d2ea6b98fc2b7878829eec223abcf404622db6c48396a9b9257f6d0ead2acf18231ae368d6a664a83f272b0679158da12e97b5229f794939e555cc574478acd - languageName: node - linkType: hard - "chalk@npm:^2.0.1, chalk@npm:^2.1.0, chalk@npm:^2.3.0, chalk@npm:^2.4.1, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -5798,7 +5608,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:3.5.3": +"chokidar@npm:3.5.3, chokidar@npm:^3.3.0, chokidar@npm:^3.5.1": version: 3.5.3 resolution: "chokidar@npm:3.5.3" dependencies: @@ -5817,25 +5627,6 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^3.3.0, chokidar@npm:^3.5.1": - version: 3.6.0 - resolution: "chokidar@npm:3.6.0" - dependencies: - anymatch: ~3.1.2 - braces: ~3.0.2 - fsevents: ~2.3.2 - glob-parent: ~5.1.2 - is-binary-path: ~2.1.0 - is-glob: ~4.0.1 - normalize-path: ~3.0.0 - readdirp: ~3.6.0 - dependenciesMeta: - fsevents: - optional: true - checksum: d2f29f499705dcd4f6f3bbed79a9ce2388cf530460122eed3b9c48efeab7a4e28739c6551fd15bec9245c6b9eeca7a32baa64694d64d9b6faeb74ddb8c4a413d - languageName: node - linkType: hard - "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -5880,20 +5671,13 @@ __metadata: languageName: node linkType: hard -"cli-spinners@npm:2.6.1": +"cli-spinners@npm:2.6.1, cli-spinners@npm:^2.5.0": version: 2.6.1 resolution: "cli-spinners@npm:2.6.1" checksum: 423409baaa7a58e5104b46ca1745fbfc5888bbd0b0c5a626e052ae1387060839c8efd512fb127e25769b3dc9562db1dc1b5add6e0b93b7ef64f477feb6416a45 languageName: node linkType: hard -"cli-spinners@npm:^2.5.0": - version: 2.9.2 - resolution: "cli-spinners@npm:2.9.2" - checksum: 1bd588289b28432e4676cb5d40505cfe3e53f2e4e10fbe05c8a710a154d6fe0ce7836844b00d6858f740f2ffe67cdc36e0fce9c7b6a8430e80e6388d5aa4956c - languageName: node - linkType: hard - "cli-truncate@npm:^2.1.0": version: 2.1.0 resolution: "cli-truncate@npm:2.1.0" @@ -6008,13 +5792,6 @@ __metadata: languageName: node linkType: hard -"co@npm:^4.6.0": - version: 4.6.0 - resolution: "co@npm:4.6.0" - checksum: 5210d9223010eb95b29df06a91116f2cf7c8e0748a9013ed853b53f362ea0e822f1e5bb054fb3cefc645239a4cf966af1f6133a3b43f40d591f3b68ed6cf0510 - languageName: node - linkType: hard - "codemirror@npm:^5.48.0": version: 5.65.16 resolution: "codemirror@npm:5.65.16" @@ -6397,13 +6174,6 @@ __metadata: languageName: node linkType: hard -"convert-source-map@npm:^1.5.0": - version: 1.9.0 - resolution: "convert-source-map@npm:1.9.0" - checksum: dc55a1f28ddd0e9485ef13565f8f756b342f9a46c4ae18b843fe3c30c675d058d6a4823eff86d472f187b176f0adf51ea7b69ea38be34be4a63cbbf91b0593c8 - languageName: node - linkType: hard - "convert-source-map@npm:^2.0.0": version: 2.0.0 resolution: "convert-source-map@npm:2.0.0" @@ -6462,20 +6232,13 @@ __metadata: languageName: node linkType: hard -"core-util-is@npm:1.0.2": +"core-util-is@npm:1.0.2, core-util-is@npm:~1.0.0": version: 1.0.2 resolution: "core-util-is@npm:1.0.2" checksum: 7a4c925b497a2c91421e25bf76d6d8190f0b2359a9200dbeed136e63b2931d6294d3b1893eda378883ed363cd950f44a12a401384c609839ea616befb7927dab languageName: node linkType: hard -"core-util-is@npm:~1.0.0": - version: 1.0.3 - resolution: "core-util-is@npm:1.0.3" - checksum: 9de8597363a8e9b9952491ebe18167e3b36e7707569eed0ebf14f8bba773611376466ae34575bca8cfe3c767890c859c74056084738f09d4e4a6f902b2ad7d99 - languageName: node - linkType: hard - "cors@npm:2.8.5, cors@npm:~2.8.5": version: 2.8.5 resolution: "cors@npm:2.8.5" @@ -6596,20 +6359,13 @@ __metadata: languageName: node linkType: hard -"csstype@npm:3.0.10": +"csstype@npm:3.0.10, csstype@npm:^3.0.2": version: 3.0.10 resolution: "csstype@npm:3.0.10" checksum: 20a8fa324f2b33ddf94aa7507d1b6ab3daa6f3cc308888dc50126585d7952f2471de69b2dbe0635d1fdc31223fef8e070842691877e725caf456e2378685a631 languageName: node linkType: hard -"csstype@npm:^3.0.2": - version: 3.1.3 - resolution: "csstype@npm:3.1.3" - checksum: 8db785cc92d259102725b3c694ec0c823f5619a84741b5c7991b8ad135dfaa66093038a1cc63e03361a6cd28d122be48f2106ae72334e067dd619a51f49eddf7 - languageName: node - linkType: hard - "custom-event@npm:~1.0.0": version: 1.0.1 resolution: "custom-event@npm:1.0.1" @@ -6712,7 +6468,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:2.6.9, debug@npm:^2.6.8": +"debug@npm:2.6.9": version: 2.6.9 resolution: "debug@npm:2.6.9" dependencies: @@ -6918,15 +6674,6 @@ __metadata: languageName: node linkType: hard -"detect-indent@npm:^4.0.0": - version: 4.0.0 - resolution: "detect-indent@npm:4.0.0" - dependencies: - repeating: ^2.0.0 - checksum: 328f273915c1610899bc7d4784ce874413d0a698346364cd3ee5d79afba1c5cf4dbc97b85a801e20f4d903c0598bd5096af32b800dfb8696b81464ccb3dfda2c - languageName: node - linkType: hard - "detect-indent@npm:^5.0.0": version: 5.0.0 resolution: "detect-indent@npm:5.0.0" @@ -7451,7 +7198,7 @@ __metadata: languageName: node linkType: hard -"escape-string-regexp@npm:^1.0.2, escape-string-regexp@npm:^1.0.5": +"escape-string-regexp@npm:^1.0.5": version: 1.0.5 resolution: "escape-string-regexp@npm:1.0.5" checksum: 6092fda75c63b110c706b6a9bfde8a612ad595b628f0bd2147eea1d3406723020810e591effc7db1da91d80a71a737a313567c5abb3813e8d9c71f4aa595b410 @@ -7788,27 +7535,13 @@ __metadata: languageName: node linkType: hard -"extsprintf@npm:1.3.0": +"extsprintf@npm:1.3.0, extsprintf@npm:^1.2.0": version: 1.3.0 resolution: "extsprintf@npm:1.3.0" checksum: cee7a4a1e34cffeeec18559109de92c27517e5641991ec6bab849aa64e3081022903dd53084f2080d0d2530803aa5ee84f1e9de642c365452f9e67be8f958ce2 languageName: node linkType: hard -"extsprintf@npm:^1.2.0": - version: 1.4.1 - resolution: "extsprintf@npm:1.4.1" - checksum: a2f29b241914a8d2bad64363de684821b6b1609d06ae68d5b539e4de6b28659715b5bea94a7265201603713b7027d35399d10b0548f09071c5513e65e8323d33 - languageName: node - linkType: hard - -"fast-deep-equal@npm:^1.0.0": - version: 1.1.0 - resolution: "fast-deep-equal@npm:1.1.0" - checksum: 69b4c9534d9805f13a341aa72f69641d0b9ae3cc8beb25c64e68a257241c7bb34370266db27ae4fc3c4da0518448c01a5f587a096a211471c86a38facd9a1486 - languageName: node - linkType: hard - "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -8469,7 +8202,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:7.2.0": +"glob@npm:7.2.0, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.7": version: 7.2.0 resolution: "glob@npm:7.2.0" dependencies: @@ -8511,20 +8244,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.7": - version: 7.2.3 - resolution: "glob@npm:7.2.3" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^3.1.1 - once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 - languageName: node - linkType: hard - "glob@npm:^8.0.1": version: 8.1.0 resolution: "glob@npm:8.1.0" @@ -8568,13 +8287,6 @@ __metadata: languageName: node linkType: hard -"globals@npm:^9.18.0": - version: 9.18.0 - resolution: "globals@npm:9.18.0" - checksum: e9c066aecfdc5ea6f727344a4246ecc243aaf66ede3bffee10ddc0c73351794c25e727dd046090dcecd821199a63b9de6af299a6e3ba292c8b22f0a80ea32073 - languageName: node - linkType: hard - "globalthis@npm:^1.0.3": version: 1.0.4 resolution: "globalthis@npm:1.0.4" @@ -8723,15 +8435,6 @@ __metadata: languageName: node linkType: hard -"has-ansi@npm:^2.0.0": - version: 2.0.0 - resolution: "has-ansi@npm:2.0.0" - dependencies: - ansi-regex: ^2.0.0 - checksum: 1b51daa0214440db171ff359d0a2d17bc20061164c57e76234f614c91dbd2a79ddd68dfc8ee73629366f7be45a6df5f2ea9de83f52e1ca24433f2cc78c35d8ec - languageName: node - linkType: hard - "has-bigints@npm:^1.0.1, has-bigints@npm:^1.0.2": version: 1.0.2 resolution: "has-bigints@npm:1.0.2" @@ -9246,15 +8949,6 @@ __metadata: languageName: node linkType: hard -"invariant@npm:^2.2.2": - version: 2.2.4 - resolution: "invariant@npm:2.2.4" - dependencies: - loose-envify: ^1.0.0 - checksum: cc3182d793aad82a8d1f0af697b462939cb46066ec48bbf1707c150ad5fad6406137e91a262022c269702e01621f35ef60269f6c0d7fd178487959809acdfb14 - languageName: node - linkType: hard - "ip-address@npm:^9.0.5": version: 9.0.5 resolution: "ip-address@npm:9.0.5" @@ -9385,13 +9079,6 @@ __metadata: languageName: node linkType: hard -"is-finite@npm:^1.0.0": - version: 1.1.0 - resolution: "is-finite@npm:1.1.0" - checksum: 532b97ed3d03e04c6bd203984d9e4ba3c0c390efee492bad5d1d1cd1802a68ab27adbd3ef6382f6312bed6c8bb1bd3e325ea79a8dc8fe080ed7a06f5f97b93e7 - languageName: node - linkType: hard - "is-fullwidth-code-point@npm:^3.0.0": version: 3.0.0 resolution: "is-fullwidth-code-point@npm:3.0.0" @@ -9683,27 +9370,6 @@ __metadata: languageName: node linkType: hard -"istanbul-instrumenter-loader@npm:^3.0.1": - version: 3.0.1 - resolution: "istanbul-instrumenter-loader@npm:3.0.1" - dependencies: - convert-source-map: ^1.5.0 - istanbul-lib-instrument: ^1.7.3 - loader-utils: ^1.1.0 - schema-utils: ^0.3.0 - peerDependencies: - webpack: ^2.0.0 || ^3.0.0 || ^4.0.0 - checksum: 6b2eb9987f79dd451c43e0fcc6fa77bf0f7ac91f3237b7833a07ad6f35e15a6bff579e943edfc2dee203408b6c3a2b4b11f3028b8628cb7304df3decc7552831 - languageName: node - linkType: hard - -"istanbul-lib-coverage@npm:^1.2.1": - version: 1.2.1 - resolution: "istanbul-lib-coverage@npm:1.2.1" - checksum: 72bfeaa9212f5a6abb243cbce4933712599ba9a6fbdee819f4f5a4cf87ed15cb92772fcab219e93c3712c578774d6d8e54084440423356b3da5d9f8ecaba9888 - languageName: node - linkType: hard - "istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": version: 3.2.2 resolution: "istanbul-lib-coverage@npm:3.2.2" @@ -9711,21 +9377,6 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-instrument@npm:^1.7.3": - version: 1.10.2 - resolution: "istanbul-lib-instrument@npm:1.10.2" - dependencies: - babel-generator: ^6.18.0 - babel-template: ^6.16.0 - babel-traverse: ^6.18.0 - babel-types: ^6.18.0 - babylon: ^6.18.0 - istanbul-lib-coverage: ^1.2.1 - semver: ^5.3.0 - checksum: c299d73820b0ac93d1c53f436181da09579083dc4a0febadbda93f598f9a5591fe4888c3071a913eede36148d6481fdf163fa0b6ec7156fffe2a95cff965fc51 - languageName: node - linkType: hard - "istanbul-lib-instrument@npm:^5.1.0": version: 5.2.1 resolution: "istanbul-lib-instrument@npm:5.2.1" @@ -9823,13 +9474,6 @@ __metadata: languageName: node linkType: hard -"js-tokens@npm:^3.0.2": - version: 3.0.2 - resolution: "js-tokens@npm:3.0.2" - checksum: ff24cf90e6e4ac446eba56e604781c1aaf3bdaf9b13a00596a0ebd972fa3b25dc83c0f0f67289c33252abb4111e0d14e952a5d9ffb61f5c22532d555ebd8d8a9 - languageName: node - linkType: hard - "js-yaml@npm:4.1.0, js-yaml@npm:^4.1.0": version: 4.1.0 resolution: "js-yaml@npm:4.1.0" @@ -9867,15 +9511,6 @@ __metadata: languageName: node linkType: hard -"jsesc@npm:^1.3.0": - version: 1.3.0 - resolution: "jsesc@npm:1.3.0" - bin: - jsesc: bin/jsesc - checksum: 9384cc72bf8ef7f2eb75fea64176b8b0c1c5e77604854c72cb4670b7072e112e3baaa69ef134be98cb078834a7812b0bfe676ad441ccd749a59427f5ed2127f1 - languageName: node - linkType: hard - "jsesc@npm:^2.5.1": version: 2.5.2 resolution: "jsesc@npm:2.5.2" @@ -9926,13 +9561,6 @@ __metadata: languageName: node linkType: hard -"json-schema-traverse@npm:^0.3.0": - version: 0.3.1 - resolution: "json-schema-traverse@npm:0.3.1" - checksum: a685c36222023471c25c86cddcff506306ecb8f8941922fd356008419889c41c38e1c16d661d5499d0a561b34f417693e9bb9212ba2b2b2f8f8a345a49e4ec1a - languageName: node - linkType: hard - "json-schema-traverse@npm:^0.4.1": version: 0.4.1 resolution: "json-schema-traverse@npm:0.4.1" @@ -9975,17 +9603,6 @@ __metadata: languageName: node linkType: hard -"json5@npm:^1.0.1": - version: 1.0.2 - resolution: "json5@npm:1.0.2" - dependencies: - minimist: ^1.2.0 - bin: - json5: lib/cli.js - checksum: 866458a8c58a95a49bef3adba929c625e82532bcff1fe93f01d29cb02cac7c3fe1f4b79951b7792c2da9de0b32871a8401a6e3c5b36778ad852bf5b8a61165d7 - languageName: node - linkType: hard - "json5@npm:^2.1.2, json5@npm:^2.2.2, json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" @@ -9995,20 +9612,13 @@ __metadata: languageName: node linkType: hard -"jsonc-parser@npm:3.2.0": +"jsonc-parser@npm:3.2.0, jsonc-parser@npm:^3.2.0": version: 3.2.0 resolution: "jsonc-parser@npm:3.2.0" checksum: 946dd9a5f326b745aa326d48a7257e3f4a4b62c5e98ec8e49fa2bdd8d96cef7e6febf1399f5c7016114fd1f68a1c62c6138826d5d90bc650448e3cf0951c53c7 languageName: node linkType: hard -"jsonc-parser@npm:^3.2.0": - version: 3.2.1 - resolution: "jsonc-parser@npm:3.2.1" - checksum: 656d9027b91de98d8ab91b3aa0d0a4cab7dc798a6830845ca664f3e76c82d46b973675bbe9b500fae1de37fd3e81aceacbaa2a57884bf2f8f29192150d2d1ef7 - languageName: node - linkType: hard - "jsonfile@npm:^4.0.0": version: 4.0.0 resolution: "jsonfile@npm:4.0.0" @@ -10402,20 +10012,13 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:2.0.5": +"lilconfig@npm:2.0.5, lilconfig@npm:^2.0.5": version: 2.0.5 resolution: "lilconfig@npm:2.0.5" checksum: f7bb9e42656f06930ad04e583026f087508ae408d3526b8b54895e934eb2a966b7aafae569656f2c79a29fe6d779b3ec44ba577e80814734c8655d6f71cdf2d1 languageName: node linkType: hard -"lilconfig@npm:^2.0.5": - version: 2.1.0 - resolution: "lilconfig@npm:2.1.0" - checksum: 8549bb352b8192375fed4a74694cd61ad293904eee33f9d4866c2192865c44c4eb35d10782966242634e0cbc1e91fe62b1247f148dc5514918e3a966da7ea117 - languageName: node - linkType: hard - "lines-and-columns@npm:^1.1.6": version: 1.2.4 resolution: "lines-and-columns@npm:1.2.4" @@ -10506,17 +10109,6 @@ __metadata: languageName: node linkType: hard -"loader-utils@npm:^1.1.0": - version: 1.4.2 - resolution: "loader-utils@npm:1.4.2" - dependencies: - big.js: ^5.2.2 - emojis-list: ^3.0.0 - json5: ^1.0.1 - checksum: eb6fb622efc0ffd1abdf68a2022f9eac62bef8ec599cf8adb75e94d1d338381780be6278534170e99edc03380a6d29bc7eb1563c89ce17c5fed3a0b17f1ad804 - languageName: node - linkType: hard - "loader-utils@npm:^2.0.0": version: 2.0.4 resolution: "loader-utils@npm:2.0.4" @@ -10747,7 +10339,7 @@ __metadata: languageName: node linkType: hard -"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": +"loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" dependencies: @@ -11100,7 +10692,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:2 || 3, minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": +"minimatch@npm:2 || 3, minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -11109,7 +10701,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:3.0.5": +"minimatch@npm:3.0.5, minimatch@npm:~3.0.4": version: 3.0.5 resolution: "minimatch@npm:3.0.5" dependencies: @@ -11154,15 +10746,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:~3.0.4": - version: 3.0.8 - resolution: "minimatch@npm:3.0.8" - dependencies: - brace-expansion: ^1.1.7 - checksum: 850cca179cad715133132693e6963b0db64ab0988c4d211415b087fc23a3e46321e2c5376a01bf5623d8782aba8bdf43c571e2e902e51fdce7175c7215c29f8b - languageName: node - linkType: hard - "minimist-options@npm:4.1.0": version: 4.1.0 resolution: "minimist-options@npm:4.1.0" @@ -13426,7 +13009,7 @@ __metadata: languageName: node linkType: hard -"qs@npm:6.11.0": +"qs@npm:6.11.0, qs@npm:^6.4.0": version: 6.11.0 resolution: "qs@npm:6.11.0" dependencies: @@ -13435,15 +13018,6 @@ __metadata: languageName: node linkType: hard -"qs@npm:^6.4.0": - version: 6.12.1 - resolution: "qs@npm:6.12.1" - dependencies: - side-channel: ^1.0.6 - checksum: aa761d99e65b6936ba2dd2187f2d9976afbcda38deb3ff1b3fe331d09b0c578ed79ca2abdde1271164b5be619c521ec7db9b34c23f49a074e5921372d16242d5 - languageName: node - linkType: hard - "querystringify@npm:^2.1.1": version: 2.2.0 resolution: "querystringify@npm:2.2.0" @@ -13547,7 +13121,7 @@ __metadata: languageName: node linkType: hard -"react@npm:>=17.0.0 <19.0.0, react@npm:^18.2.0": +"react@npm:>=17.0.0 <19.0.0, react@npm:^18.2.0, react@npm:^18.3.1": version: 18.3.1 resolution: "react@npm:18.3.1" dependencies: @@ -13823,15 +13397,6 @@ __metadata: languageName: node linkType: hard -"repeating@npm:^2.0.0": - version: 2.0.1 - resolution: "repeating@npm:2.0.1" - dependencies: - is-finite: ^1.0.0 - checksum: d2db0b69c5cb0c14dd750036e0abcd6b3c3f7b2da3ee179786b755cf737ca15fa0fff417ca72de33d6966056f4695440e680a352401fc02c95ade59899afbdd0 - languageName: node - linkType: hard - "require-directory@npm:^2.1.1": version: 2.1.1 resolution: "require-directory@npm:2.1.1" @@ -14113,21 +13678,7 @@ __metadata: languageName: node linkType: hard -"sanitize-html@npm:^2.3": - version: 2.13.0 - resolution: "sanitize-html@npm:2.13.0" - dependencies: - deepmerge: ^4.2.2 - escape-string-regexp: ^4.0.0 - htmlparser2: ^8.0.0 - is-plain-object: ^5.0.0 - parse-srcset: ^1.0.2 - postcss: ^8.3.11 - checksum: d88602328306dbbddb9c5e2a5798783a3b38977a7ef40bf81dae31220d7fb583149c1046a33ec6817e9d96d172b1aaa9ea159776eb1ee08f6a0571150114c9bf - languageName: node - linkType: hard - -"sanitize-html@npm:~2.12.1": +"sanitize-html@npm:^2.3, sanitize-html@npm:~2.12.1": version: 2.12.1 resolution: "sanitize-html@npm:2.12.1" dependencies: @@ -14157,15 +13708,6 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^0.3.0": - version: 0.3.0 - resolution: "schema-utils@npm:0.3.0" - dependencies: - ajv: ^5.0.0 - checksum: 441fa4bd4900afb19eb9da1d8d6271056b71ce3d8b1b73bbece791de1d4c90ac7e97ffc9787607aa53611aaf2996711af7c18ba8669f06b084b218cab1e701e3 - languageName: node - linkType: hard - "schema-utils@npm:^2.7.0": version: 2.7.1 resolution: "schema-utils@npm:2.7.1" @@ -14207,7 +13749,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.3.0, semver@npm:^5.4.1, semver@npm:^5.5.0, semver@npm:^5.6.0": +"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.4.1, semver@npm:^5.5.0, semver@npm:^5.6.0": version: 5.7.2 resolution: "semver@npm:5.7.2" bin: @@ -14407,7 +13949,7 @@ __metadata: languageName: node linkType: hard -"side-channel@npm:^1.0.4, side-channel@npm:^1.0.6": +"side-channel@npm:^1.0.4": version: 1.0.6 resolution: "side-channel@npm:1.0.6" dependencies: @@ -14586,7 +14128,7 @@ __metadata: languageName: node linkType: hard -"sonic-boom@npm:3.8.0": +"sonic-boom@npm:3.8.0, sonic-boom@npm:^3.7.0": version: 3.8.0 resolution: "sonic-boom@npm:3.8.0" dependencies: @@ -14604,15 +14146,6 @@ __metadata: languageName: node linkType: hard -"sonic-boom@npm:^3.7.0": - version: 3.8.1 - resolution: "sonic-boom@npm:3.8.1" - dependencies: - atomic-sleep: ^1.0.0 - checksum: 79c90d7a2f928489fd3d4b68d8f8d747a426ca6ccf83c3b102b36f899d4524463dd310982ab7ab6d6bcfd34b7c7c281ad25e495ad71fbff8fd6fa86d6273fc6b - languageName: node - linkType: hard - "sort-keys@npm:^2.0.0": version: 2.0.0 resolution: "sort-keys@npm:2.0.0" @@ -14723,13 +14256,6 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.5.7": - version: 0.5.7 - resolution: "source-map@npm:0.5.7" - checksum: 5dc2043b93d2f194142c7f38f74a24670cd7a0063acdaf4bf01d2964b402257ae843c2a8fa822ad5b71013b5fcafa55af7421383da919752f22ff488bc553f4d - languageName: node - linkType: hard - "source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1": version: 0.6.1 resolution: "source-map@npm:0.6.1" @@ -14992,15 +14518,6 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^3.0.0": - version: 3.0.1 - resolution: "strip-ansi@npm:3.0.1" - dependencies: - ansi-regex: ^2.0.0 - checksum: 9b974de611ce5075c70629c00fa98c46144043db92ae17748fb780f706f7a789e9989fd10597b7c2053ae8d1513fd707816a91f1879b2f71e6ac0b6a863db465 - languageName: node - linkType: hard - "strip-ansi@npm:^4.0.0": version: 4.0.0 resolution: "strip-ansi@npm:4.0.0" @@ -15101,13 +14618,6 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^2.0.0": - version: 2.0.0 - resolution: "supports-color@npm:2.0.0" - checksum: 602538c5812b9006404370b5a4b885d3e2a1f6567d314f8b4a41974ffe7d08e525bf92ae0f9c7030e3b4c78e4e34ace55d6a67a74f1571bc205959f5972f88f0 - languageName: node - linkType: hard - "supports-color@npm:^5.3.0, supports-color@npm:^5.4.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -15312,13 +14822,6 @@ __metadata: languageName: node linkType: hard -"to-fast-properties@npm:^1.0.3": - version: 1.0.3 - resolution: "to-fast-properties@npm:1.0.3" - checksum: bd0abb58c4722851df63419de3f6d901d5118f0440d3f71293ed776dd363f2657edaaf2dc470e3f6b7b48eb84aa411193b60db8a4a552adac30de9516c5cc580 - languageName: node - linkType: hard - "to-fast-properties@npm:^2.0.0": version: 2.0.0 resolution: "to-fast-properties@npm:2.0.0" @@ -15398,13 +14901,6 @@ __metadata: languageName: node linkType: hard -"trim-right@npm:^1.0.1": - version: 1.0.1 - resolution: "trim-right@npm:1.0.1" - checksum: 9120af534e006a7424a4f9358710e6e707887b6ccf7ea69e50d6ac6464db1fe22268400def01752f09769025d480395159778153fb98d4a2f6f40d4cf5d4f3b6 - languageName: node - linkType: hard - "tsconfig-paths@npm:^4.1.2": version: 4.2.0 resolution: "tsconfig-paths@npm:4.2.0" @@ -15688,13 +15184,6 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~5.26.4": - version: 5.26.5 - resolution: "undici-types@npm:5.26.5" - checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487 - languageName: node - linkType: hard - "union@npm:~0.5.0": version: 0.5.0 resolution: "union@npm:0.5.0" @@ -15820,7 +15309,7 @@ __metadata: languageName: node linkType: hard -"uri-js@npm:^4.2.2, uri-js@npm:^4.4.1": +"uri-js@npm:^4.2.2": version: 4.4.1 resolution: "uri-js@npm:4.4.1" dependencies: @@ -16070,7 +15559,7 @@ __metadata: languageName: node linkType: hard -"vscode-jsonrpc@npm:8.2.0": +"vscode-jsonrpc@npm:8.2.0, vscode-jsonrpc@npm:^8.0.2": version: 8.2.0 resolution: "vscode-jsonrpc@npm:8.2.0" checksum: f302a01e59272adc1ae6494581fa31c15499f9278df76366e3b97b2236c7c53ebfc71efbace9041cfd2caa7f91675b9e56f2407871a1b3c7f760a2e2ee61484a @@ -16084,13 +15573,6 @@ __metadata: languageName: node linkType: hard -"vscode-jsonrpc@npm:^8.0.2": - version: 8.2.1 - resolution: "vscode-jsonrpc@npm:8.2.1" - checksum: 2af2c333d73f6587896a7077978b8d4b430e55c674d5dbb90597a84a6647057c1655a3bff398a9b08f1f8ba57dbd2deabf05164315829c297b0debba3b8bc19e - languageName: node - linkType: hard - "vscode-languageserver-protocol@npm:^3.17.0": version: 3.17.5 resolution: "vscode-languageserver-protocol@npm:3.17.5" @@ -16585,22 +16067,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.11.0": - version: 8.17.0 - resolution: "ws@npm:8.17.0" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 147ef9eab0251364e1d2c55338ad0efb15e6913923ccbfdf20f7a8a6cb8f88432bcd7f4d8f66977135bfad35575644f9983201c1a361019594a4e53977bf6d4e - languageName: node - linkType: hard - -"ws@npm:~8.11.0": +"ws@npm:^8.11.0, ws@npm:~8.11.0": version: 8.11.0 resolution: "ws@npm:8.11.0" peerDependencies: @@ -16661,7 +16128,7 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:20.2.4": +"yargs-parser@npm:20.2.4, yargs-parser@npm:^20.2.2, yargs-parser@npm:^20.2.3": version: 20.2.4 resolution: "yargs-parser@npm:20.2.4" checksum: d251998a374b2743a20271c2fd752b9fbef24eb881d53a3b99a7caa5e8227fcafd9abf1f345ac5de46435821be25ec12189a11030c12ee6481fef6863ed8b924 @@ -16675,13 +16142,6 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^20.2.2, yargs-parser@npm:^20.2.3": - version: 20.2.9 - resolution: "yargs-parser@npm:20.2.9" - checksum: 8bb69015f2b0ff9e17b2c8e6bfe224ab463dd00ca211eece72a4cd8a906224d2703fb8a326d36fdd0e68701e201b2a60ed7cf81ce0fd9b3799f9fe7745977ae3 - languageName: node - linkType: hard - "yargs-unparser@npm:2.0.0": version: 2.0.0 resolution: "yargs-unparser@npm:2.0.0" From 75da2f96764c282db49d6a6617b1c96fa3c2e8d3 Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Tue, 28 May 2024 18:40:16 +1000 Subject: [PATCH 08/15] Run lint. --- packages/base/src/widget.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/base/src/widget.ts b/packages/base/src/widget.ts index f4e450b341..44b341f662 100644 --- a/packages/base/src/widget.ts +++ b/packages/base/src/widget.ts @@ -1105,9 +1105,7 @@ export class DOMWidgetView extends WidgetView { } get tooltip() { - return ( - this.model.get('tooltip') ?? (this.model.get('description')) - ); + return this.model.get('tooltip') ?? this.model.get('description'); } /** From 0c1dafedeaad9e587690de4386dbed286d0a1009 Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Sun, 9 Jun 2024 15:34:23 +1000 Subject: [PATCH 09/15] Fix tests that need dummy comm & update yarn.lock. --- packages/controls/package.json | 2 +- .../widgets/tests/test_send_state.py | 8 ++- .../widgets/tests/test_set_state.py | 30 ++++----- .../ipywidgets/widgets/tests/utils.py | 11 ++++ yarn.lock | 66 +++++++++---------- 5 files changed, 65 insertions(+), 52 deletions(-) diff --git a/packages/controls/package.json b/packages/controls/package.json index 3ad03c66a2..e1acc3a3c7 100644 --- a/packages/controls/package.json +++ b/packages/controls/package.json @@ -34,7 +34,7 @@ "test:unit:ie": "npm run test:unit:default -- --browsers=IE" }, "dependencies": { - "@jupyter-widgets/base": "^6.0.7", + "@jupyter-widgets/base": "^6.0.8", "@lumino/algorithm": "^1.9.2 || ^2.0.1", "@lumino/domutils": "^1.8.1 || ^2.1", "@lumino/messaging": "^1.10.1 || ^2.0.1", diff --git a/python/ipywidgets/ipywidgets/widgets/tests/test_send_state.py b/python/ipywidgets/ipywidgets/widgets/tests/test_send_state.py index ec18ae4af1..7a7018f0e2 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_send_state.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_send_state.py @@ -3,12 +3,14 @@ from traitlets import Bool, Tuple, List -from .utils import setup, teardown, DummyComm + +from .utils import dummy_comm_fixture from ..widget import Widget from ..._version import __control_protocol_version__ + # A widget with simple traits class SimpleWidget(Widget): a = Bool().tag(sync=True) @@ -18,13 +20,13 @@ class SimpleWidget(Widget): c = List(Bool()).tag(sync=True) -def test_empty_send_state(): +def test_empty_send_state(dummy_comm_fixture): w = SimpleWidget() w.send_state([]) assert w.comm.messages == [] -def test_empty_hold_sync(): +def test_empty_hold_sync(dummy_comm_fixture): w = SimpleWidget() with w.hold_sync(): pass diff --git a/python/ipywidgets/ipywidgets/widgets/tests/test_set_state.py b/python/ipywidgets/ipywidgets/widgets/tests/test_set_state.py index 82ecbd9311..133b39d3d6 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_set_state.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_set_state.py @@ -6,7 +6,7 @@ from traitlets import Bool, Tuple, List, Instance, CFloat, CInt, Float, Int, TraitError, observe -from .utils import setup, teardown +from .utils import dummy_comm_fixture # noqa: F401 import ipywidgets from ipywidgets import Widget @@ -81,7 +81,7 @@ class TruncateDataWidget(SimpleWidget): # Actual tests: # -def test_set_state_simple(echo): +def test_set_state_simple(echo, dummy_comm_fixture): w = SimpleWidget() w.set_state(dict( a=True, @@ -92,7 +92,7 @@ def test_set_state_simple(echo): assert len(w.comm.messages) == (1 if echo else 0) -def test_set_state_transformer(echo): +def test_set_state_transformer(echo, dummy_comm_fixture): w = TransformerWidget() w.set_state(dict( d=[True, False, True] @@ -119,7 +119,7 @@ def test_set_state_transformer(echo): assert w.comm.messages == expected -def test_set_state_data(echo): +def test_set_state_data(echo, dummy_comm_fixture): w = DataWidget() data = memoryview(b'x'*30) w.set_state(dict( @@ -129,7 +129,7 @@ def test_set_state_data(echo): assert len(w.comm.messages) == (1 if echo else 0) -def test_set_state_data_truncate(echo): +def test_set_state_data_truncate(echo, dummy_comm_fixture): w = TruncateDataWidget() data = memoryview(b'x'*30) w.set_state(dict( @@ -153,7 +153,7 @@ def test_set_state_data_truncate(echo): assert buffers[0] == data[:20].tobytes() -def test_set_state_numbers_int(echo): +def test_set_state_numbers_int(echo, dummy_comm_fixture): # JS does not differentiate between float/int. # Instead, it formats exact floats as ints in JSON (1.0 -> '1'). @@ -169,7 +169,7 @@ def test_set_state_numbers_int(echo): assert len(w.comm.messages) == (1 if echo else 0) -def test_set_state_numbers_float(echo): +def test_set_state_numbers_float(echo, dummy_comm_fixture): w = NumberWidget() # Set floats to int-like floats w.set_state(dict( @@ -181,7 +181,7 @@ def test_set_state_numbers_float(echo): assert len(w.comm.messages) == (1 if echo else 0) -def test_set_state_float_to_float(echo): +def test_set_state_float_to_float(echo, dummy_comm_fixture): w = NumberWidget() # Set floats to float w.set_state(dict( @@ -192,7 +192,7 @@ def test_set_state_float_to_float(echo): assert len(w.comm.messages) == (1 if echo else 0) -def test_set_state_cint_to_float(echo): +def test_set_state_cint_to_float(echo, dummy_comm_fixture): w = NumberWidget() # Set CInt to float @@ -223,7 +223,7 @@ def _x_test_set_state_int_to_int_like(): assert len(w.comm.messages) == 0 -def test_set_state_int_to_float(echo): +def test_set_state_int_to_float(echo, dummy_comm_fixture): w = NumberWidget() # Set Int to float @@ -232,7 +232,7 @@ def test_set_state_int_to_float(echo): i = 3.5 )) -def test_property_lock(echo): +def test_property_lock(echo, dummy_comm_fixture): # when this widget's value is set to 42, it sets itself to 2, and then back to 42 again (and then stops) class AnnoyingWidget(Widget): value = Float().tag(sync=True) @@ -262,7 +262,7 @@ def _propagate_value(self, change): calls = [] widget._send.assert_has_calls(calls) -def test_hold_sync(echo): +def test_hold_sync(echo, dummy_comm_fixture): # when this widget's value is set to 42, it sets the value to 2, and also sets a different trait value class AnnoyingWidget(Widget): value = Float().tag(sync=True) @@ -298,7 +298,7 @@ def _propagate_value(self, change): -def test_echo(): +def test_echo(dummy_comm_fixture): # we always echo values back to the frontend class ValueWidget(Widget): value = Float().tag(sync=True) @@ -319,7 +319,7 @@ class ValueWidget(Widget): widget._send.assert_has_calls(calls) -def test_echo_single(): +def test_echo_single(dummy_comm_fixture): # we always echo multiple changes back in 1 update class ValueWidget(Widget): value = Float().tag(sync=True) @@ -359,7 +359,7 @@ def _square(self, change): widget._send.assert_has_calls(calls) -def test_no_echo(echo): +def test_no_echo(dummy_comm_fixture): # in cases where values coming from the frontend are 'heavy', we might want to opt out class ValueWidget(Widget): value = Float().tag(sync=True, echo_update=False) diff --git a/python/ipywidgets/ipywidgets/widgets/tests/utils.py b/python/ipywidgets/ipywidgets/widgets/tests/utils.py index 260485e3f8..20592b4a22 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/utils.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/utils.py @@ -1,6 +1,8 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. +import pytest + from ipywidgets import Widget import ipywidgets.widgets.widget @@ -95,3 +97,12 @@ def teardown(): def call_method(method, *args, **kwargs): method(*args, **kwargs) + + +@pytest.fixture +def dummy_comm_fixture(): + setup() + try: + yield + finally: + teardown() \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 96205a8920..b6242c157e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -732,11 +732,11 @@ __metadata: languageName: node linkType: hard -"@jupyter-widgets/base-manager@^1.0.8, @jupyter-widgets/base-manager@workspace:packages/base-manager": +"@jupyter-widgets/base-manager@^1.0.9, @jupyter-widgets/base-manager@workspace:packages/base-manager": version: 0.0.0-use.local resolution: "@jupyter-widgets/base-manager@workspace:packages/base-manager" dependencies: - "@jupyter-widgets/base": ^6.0.7 + "@jupyter-widgets/base": ^6.0.8 "@jupyterlab/services": ^6.0.0 || ^7.0.0 "@lumino/coreutils": ^1.11.1 || ^2 "@types/base64-js": ^1.2.5 @@ -770,7 +770,7 @@ __metadata: languageName: unknown linkType: soft -"@jupyter-widgets/base@^6.0.7, @jupyter-widgets/base@workspace:packages/base": +"@jupyter-widgets/base@^6.0.8, @jupyter-widgets/base@workspace:packages/base": version: 0.0.0-use.local resolution: "@jupyter-widgets/base@workspace:packages/base" dependencies: @@ -812,11 +812,11 @@ __metadata: languageName: unknown linkType: soft -"@jupyter-widgets/controls@^5.0.8, @jupyter-widgets/controls@workspace:packages/controls": +"@jupyter-widgets/controls@^5.0.9, @jupyter-widgets/controls@workspace:packages/controls": version: 0.0.0-use.local resolution: "@jupyter-widgets/controls@workspace:packages/controls" dependencies: - "@jupyter-widgets/base": ^6.0.7 + "@jupyter-widgets/base": ^6.0.8 "@jupyterlab/services": ^6.0.0 || ^7.0.0 "@lumino/algorithm": ^1.9.2 || ^2.0.1 "@lumino/domutils": ^1.8.1 || ^2.1 @@ -866,9 +866,9 @@ __metadata: version: 0.0.0-use.local resolution: "@jupyter-widgets/example-web1@workspace:examples/web1" dependencies: - "@jupyter-widgets/base": ^6.0.7 - "@jupyter-widgets/base-manager": ^1.0.8 - "@jupyter-widgets/controls": ^5.0.8 + "@jupyter-widgets/base": ^6.0.8 + "@jupyter-widgets/base-manager": ^1.0.9 + "@jupyter-widgets/controls": ^5.0.9 chai: ^4.0.0 css-loader: ^6.5.1 karma: ^6.3.3 @@ -887,9 +887,9 @@ __metadata: version: 0.0.0-use.local resolution: "@jupyter-widgets/example-web2@workspace:examples/web2" dependencies: - "@jupyter-widgets/base": ^6.0.7 - "@jupyter-widgets/base-manager": ^1.0.8 - "@jupyter-widgets/controls": ^5.0.8 + "@jupyter-widgets/base": ^6.0.8 + "@jupyter-widgets/base-manager": ^1.0.9 + "@jupyter-widgets/controls": ^5.0.9 codemirror: ^5.48.0 css-loader: ^6.5.1 font-awesome: ^4.7.0 @@ -902,9 +902,9 @@ __metadata: version: 0.0.0-use.local resolution: "@jupyter-widgets/example-web3@workspace:examples/web3" dependencies: - "@jupyter-widgets/base": ^6.0.7 - "@jupyter-widgets/controls": ^5.0.8 - "@jupyter-widgets/html-manager": ^1.0.10 + "@jupyter-widgets/base": ^6.0.8 + "@jupyter-widgets/controls": ^5.0.9 + "@jupyter-widgets/html-manager": ^1.0.11 "@jupyterlab/services": ^6.0.0 || ^7.0.0 "@types/codemirror": ^5.60.0 "@types/node": ^17.0.2 @@ -924,7 +924,7 @@ __metadata: version: 0.0.0-use.local resolution: "@jupyter-widgets/example-web4@workspace:examples/web4" dependencies: - "@jupyter-widgets/html-manager": ^1.0.10 + "@jupyter-widgets/html-manager": ^1.0.11 css-loader: ^6.5.1 font-awesome: ^4.7.0 style-loader: ^3.3.1 @@ -932,16 +932,16 @@ __metadata: languageName: unknown linkType: soft -"@jupyter-widgets/html-manager@^1.0.10, @jupyter-widgets/html-manager@workspace:packages/html-manager": +"@jupyter-widgets/html-manager@^1.0.11, @jupyter-widgets/html-manager@workspace:packages/html-manager": version: 0.0.0-use.local resolution: "@jupyter-widgets/html-manager@workspace:packages/html-manager" dependencies: "@fortawesome/fontawesome-free": ^5.12.0 - "@jupyter-widgets/base": ^6.0.7 - "@jupyter-widgets/base-manager": ^1.0.8 - "@jupyter-widgets/controls": ^5.0.8 - "@jupyter-widgets/output": ^6.0.7 - "@jupyter-widgets/schema": ^0.5.4 + "@jupyter-widgets/base": ^6.0.8 + "@jupyter-widgets/base-manager": ^1.0.9 + "@jupyter-widgets/controls": ^5.0.9 + "@jupyter-widgets/output": ^6.0.8 + "@jupyter-widgets/schema": ^0.5.5 "@jupyterlab/outputarea": ^3.0.0 || ^4.0.0 "@jupyterlab/rendermime": ^3.0.0 || ^4.0.0 "@jupyterlab/rendermime-interfaces": ^3.0.0 || ^4.0.0 @@ -978,10 +978,10 @@ __metadata: version: 0.0.0-use.local resolution: "@jupyter-widgets/jupyterlab-manager@workspace:python/jupyterlab_widgets" dependencies: - "@jupyter-widgets/base": ^6.0.7 - "@jupyter-widgets/base-manager": ^1.0.8 - "@jupyter-widgets/controls": ^5.0.8 - "@jupyter-widgets/output": ^6.0.7 + "@jupyter-widgets/base": ^6.0.8 + "@jupyter-widgets/base-manager": ^1.0.9 + "@jupyter-widgets/controls": ^5.0.9 + "@jupyter-widgets/output": ^6.0.8 "@jupyterlab/application": ^3.0.0 || ^4.0.0 "@jupyterlab/apputils": ^3.0.0 || ^4.0.0 "@jupyterlab/builder": ^3.0.0 || ^4.0.0 @@ -1034,11 +1034,11 @@ __metadata: version: 0.0.0-use.local resolution: "@jupyter-widgets/notebook-manager@workspace:python/widgetsnbextension" dependencies: - "@jupyter-widgets/base": ^6.0.7 - "@jupyter-widgets/base-manager": ^1.0.8 - "@jupyter-widgets/controls": ^5.0.8 - "@jupyter-widgets/html-manager": ^1.0.10 - "@jupyter-widgets/output": ^6.0.7 + "@jupyter-widgets/base": ^6.0.8 + "@jupyter-widgets/base-manager": ^1.0.9 + "@jupyter-widgets/controls": ^5.0.9 + "@jupyter-widgets/html-manager": ^1.0.11 + "@jupyter-widgets/output": ^6.0.8 "@jupyterlab/services": ^6.0.0 || ^7.0.0 "@lumino/messaging": ^1.10.1 || ^2.0.1 "@lumino/widgets": ^1.30.0 || ^2.1 @@ -1052,17 +1052,17 @@ __metadata: languageName: unknown linkType: soft -"@jupyter-widgets/output@^6.0.7, @jupyter-widgets/output@workspace:packages/output": +"@jupyter-widgets/output@^6.0.8, @jupyter-widgets/output@workspace:packages/output": version: 0.0.0-use.local resolution: "@jupyter-widgets/output@workspace:packages/output" dependencies: - "@jupyter-widgets/base": ^6.0.7 + "@jupyter-widgets/base": ^6.0.8 rimraf: ^3.0.2 typescript: ~4.9.4 languageName: unknown linkType: soft -"@jupyter-widgets/schema@^0.5.4, @jupyter-widgets/schema@workspace:packages/schema": +"@jupyter-widgets/schema@^0.5.5, @jupyter-widgets/schema@workspace:packages/schema": version: 0.0.0-use.local resolution: "@jupyter-widgets/schema@workspace:packages/schema" languageName: unknown From 3e49c8e82af35e5dc10e7665a12a3de888461ce3 Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Sat, 31 Aug 2024 10:53:40 +1000 Subject: [PATCH 10/15] Fix tests and lock file. --- .../widgets/tests/test_send_state.py | 4 +- .../widgets/tests/test_set_state.py | 28 ++-- .../ipywidgets/widgets/tests/utils.py | 18 +- yarn.lock | 158 ++++++++++++++---- 4 files changed, 152 insertions(+), 56 deletions(-) diff --git a/python/ipywidgets/ipywidgets/widgets/tests/test_send_state.py b/python/ipywidgets/ipywidgets/widgets/tests/test_send_state.py index 2e8408b44e..ad1335e52d 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_send_state.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_send_state.py @@ -17,13 +17,13 @@ class SimpleWidget(Widget): c = List(Bool()).tag(sync=True) -def test_empty_send_state(dummy_comm_fixture): +def test_empty_send_state(): w = SimpleWidget() w.send_state([]) assert w.comm.messages == [] -def test_empty_hold_sync(dummy_comm_fixture): +def test_empty_hold_sync(): w = SimpleWidget() with w.hold_sync(): pass diff --git a/python/ipywidgets/ipywidgets/widgets/tests/test_set_state.py b/python/ipywidgets/ipywidgets/widgets/tests/test_set_state.py index 157b6b9af2..c19ff57cde 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_set_state.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_set_state.py @@ -89,7 +89,7 @@ class TruncateDataWidget(SimpleWidget): # -def test_set_state_simple(echo, dummy_comm_fixture): +def test_set_state_simple(echo): w = SimpleWidget() w.set_state( dict( @@ -102,7 +102,7 @@ def test_set_state_simple(echo, dummy_comm_fixture): assert len(w.comm.messages) == (1 if echo else 0) -def test_set_state_transformer(echo, dummy_comm_fixture): +def test_set_state_transformer(echo): w = TransformerWidget() w.set_state(dict(d=[True, False, True])) # Since the deserialize step changes the state, this should send an update @@ -137,7 +137,7 @@ def test_set_state_transformer(echo, dummy_comm_fixture): assert w.comm.messages == expected -def test_set_state_data(echo, dummy_comm_fixture): +def test_set_state_data(echo): w = DataWidget() data = memoryview(b"x" * 30) w.set_state( @@ -149,7 +149,7 @@ def test_set_state_data(echo, dummy_comm_fixture): assert len(w.comm.messages) == (1 if echo else 0) -def test_set_state_data_truncate(echo, dummy_comm_fixture): +def test_set_state_data_truncate(echo): w = TruncateDataWidget() data = memoryview(b"x" * 30) w.set_state( @@ -170,7 +170,7 @@ def test_set_state_data_truncate(echo, dummy_comm_fixture): assert buffers[0] == data[:20].tobytes() -def test_set_state_numbers_int(echo, dummy_comm_fixture): +def test_set_state_numbers_int(echo): # JS does not differentiate between float/int. # Instead, it formats exact floats as ints in JSON (1.0 -> '1'). @@ -188,7 +188,7 @@ def test_set_state_numbers_int(echo, dummy_comm_fixture): assert len(w.comm.messages) == (1 if echo else 0) -def test_set_state_numbers_float(echo, dummy_comm_fixture): +def test_set_state_numbers_float(echo): w = NumberWidget() # Set floats to int-like floats w.set_state(dict(f=1.0, cf=2.0, ci=4.0)) @@ -196,7 +196,7 @@ def test_set_state_numbers_float(echo, dummy_comm_fixture): assert len(w.comm.messages) == (1 if echo else 0) -def test_set_state_float_to_float(echo, dummy_comm_fixture): +def test_set_state_float_to_float(echo): w = NumberWidget() # Set floats to float w.set_state( @@ -209,7 +209,7 @@ def test_set_state_float_to_float(echo, dummy_comm_fixture): assert len(w.comm.messages) == (1 if echo else 0) -def test_set_state_cint_to_float(echo, dummy_comm_fixture): +def test_set_state_cint_to_float(echo): w = NumberWidget() # Set CInt to float @@ -236,7 +236,7 @@ def _x_test_set_state_int_to_int_like(): assert len(w.comm.messages) == 0 -def test_set_state_int_to_float(echo, dummy_comm_fixture): +def test_set_state_int_to_float(echo): w = NumberWidget() # Set Int to float @@ -244,7 +244,7 @@ def test_set_state_int_to_float(echo, dummy_comm_fixture): w.set_state(dict(i=3.5)) -def test_property_lock(echo, dummy_comm_fixture): +def test_property_lock(echo): # when this widget's value is set to 42, it sets itself to 2, and then back to 42 again (and then stops) class AnnoyingWidget(Widget): value = Float().tag(sync=True) @@ -275,7 +275,7 @@ def _propagate_value(self, change): widget._send.assert_has_calls(calls) -def test_hold_sync(echo, dummy_comm_fixture): +def test_hold_sync(echo): # when this widget's value is set to 42, it sets the value to 2, and also sets a different trait value class AnnoyingWidget(Widget): value = Float().tag(sync=True) @@ -310,7 +310,7 @@ def _propagate_value(self, change): widget._send.assert_has_calls(calls) -def test_echo(dummy_comm_fixture): +def test_echo(): # we always echo values back to the frontend class ValueWidget(Widget): value = Float().tag(sync=True) @@ -331,7 +331,7 @@ class ValueWidget(Widget): widget._send.assert_has_calls(calls) -def test_echo_single(dummy_comm_fixture): +def test_echo_single(): # we always echo multiple changes back in 1 update class ValueWidget(Widget): value = Float().tag(sync=True) @@ -373,7 +373,7 @@ def _square(self, change): widget._send.assert_has_calls(calls) -def test_no_echo(dummy_comm_fixture): +def test_no_echo(): # in cases where values coming from the frontend are 'heavy', we might want to opt out class ValueWidget(Widget): value = Float().tag(sync=True, echo_update=False) diff --git a/python/ipywidgets/ipywidgets/widgets/tests/utils.py b/python/ipywidgets/ipywidgets/widgets/tests/utils.py index 97b5aa3fe4..d3d7016b6a 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/utils.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/utils.py @@ -9,6 +9,7 @@ # The new comm package is not available in our Python 3.7 CI (older ipykernel version) try: import comm + NEW_COMM_PACKAGE = True except ImportError: NEW_COMM_PACKAGE = False @@ -16,9 +17,10 @@ import ipykernel.comm import pytest -class DummyComm(): - comm_id = 'a-b-c-d' - kernel = 'Truthy' + +class DummyComm: + comm_id = "a-b-c-d" + kernel = "Truthy" def __init__(self, *args, **kwargs): super().__init__() @@ -59,6 +61,7 @@ def dummy_get_comm_manager(**kwargs): orig_create_comm = comm.create_comm orig_get_comm_manager = comm.get_comm_manager + def setup_test_comm(): if NEW_COMM_PACKAGE: comm.create_comm = dummy_create_comm @@ -68,11 +71,14 @@ def setup_test_comm(): ipykernel.comm.Comm = DummyComm Widget.comm.klass = DummyComm ipywidgets.widgets.widget.Comm = DummyComm - _widget_attrs['_repr_mimebundle_'] = Widget._repr_mimebundle_ + _widget_attrs["_repr_mimebundle_"] = Widget._repr_mimebundle_ + def raise_not_implemented(*args, **kwargs): raise NotImplementedError() + Widget._repr_mimebundle_ = raise_not_implemented + def teardown_test_comm(): if NEW_COMM_PACKAGE: comm.create_comm = orig_create_comm @@ -89,12 +95,14 @@ def teardown_test_comm(): setattr(Widget, attr, value) _widget_attrs.clear() + @pytest.fixture(autouse=True) def setup(): setup_test_comm() yield teardown_test_comm() + def call_method(method, *args, **kwargs): method(*args, **kwargs) @@ -105,4 +113,4 @@ def dummy_comm_fixture(): try: yield finally: - teardown() \ No newline at end of file + teardown_test_comm() diff --git a/yarn.lock b/yarn.lock index 0afe9c6969..decb4c3e96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -732,11 +732,11 @@ __metadata: languageName: node linkType: hard -"@jupyter-widgets/base-manager@^1.0.9, @jupyter-widgets/base-manager@workspace:packages/base-manager": +"@jupyter-widgets/base-manager@^1.0.11, @jupyter-widgets/base-manager@workspace:packages/base-manager": version: 0.0.0-use.local resolution: "@jupyter-widgets/base-manager@workspace:packages/base-manager" dependencies: - "@jupyter-widgets/base": ^6.0.8 + "@jupyter-widgets/base": ^6.0.10 "@jupyterlab/services": ^6.0.0 || ^7.0.0 "@lumino/coreutils": ^1.11.1 || ^2 "@types/base64-js": ^1.2.5 @@ -770,7 +770,25 @@ __metadata: languageName: unknown linkType: soft -"@jupyter-widgets/base@^6.0.8, @jupyter-widgets/base@workspace:packages/base": +"@jupyter-widgets/base7@npm:@jupyter-widgets/base@4.1.6, @jupyter-widgets/base@npm:^4.1.6": + version: 4.1.6 + resolution: "@jupyter-widgets/base@npm:4.1.6" + dependencies: + "@jupyterlab/services": ^6.0.0 + "@lumino/coreutils": ^1.2.0 + "@lumino/messaging": ^1.2.1 + "@lumino/widgets": ^1.3.0 + "@types/backbone": ^1.4.1 + "@types/lodash": ^4.14.134 + backbone: 1.2.3 + base64-js: ^1.2.1 + jquery: ^3.1.1 + lodash: ^4.17.4 + checksum: cdd11e8fd480a44b9e9f5a37529361c407456d9e2d2a09f35b8f067b65e915c132aaaaab8d8523f3c8c624e3403215d6eb56a21337f57213b75ebb377a26c208 + languageName: node + linkType: hard + +"@jupyter-widgets/base@^6.0.10, @jupyter-widgets/base@workspace:packages/base": version: 0.0.0-use.local resolution: "@jupyter-widgets/base@workspace:packages/base" dependencies: @@ -812,11 +830,29 @@ __metadata: languageName: unknown linkType: soft -"@jupyter-widgets/controls@^5.0.9, @jupyter-widgets/controls@workspace:packages/controls": +"@jupyter-widgets/controls7@npm:@jupyter-widgets/controls@3.1.6": + version: 3.1.6 + resolution: "@jupyter-widgets/controls@npm:3.1.6" + dependencies: + "@jupyter-widgets/base": ^4.1.6 + "@lumino/algorithm": ^1.1.0 + "@lumino/domutils": ^1.1.0 + "@lumino/messaging": ^1.2.1 + "@lumino/signaling": ^1.2.0 + "@lumino/widgets": ^1.3.0 + d3-format: ^1.3.0 + jquery: ^3.1.1 + jquery-ui: ^1.12.1 + underscore: ^1.8.3 + checksum: 9c69ebe9ce22d7e7cf3cd66dc849505c1433da4424d8daa7411d85f41027d5600c20c2350d703fe582fd44a4b5d11ad8b404fe058b686f17cd47bafd41547646 + languageName: node + linkType: hard + +"@jupyter-widgets/controls@^5.0.11, @jupyter-widgets/controls@workspace:packages/controls": version: 0.0.0-use.local resolution: "@jupyter-widgets/controls@workspace:packages/controls" dependencies: - "@jupyter-widgets/base": ^6.0.8 + "@jupyter-widgets/base": ^6.0.10 "@jupyterlab/services": ^6.0.0 || ^7.0.0 "@lumino/algorithm": ^1.9.2 || ^2.0.1 "@lumino/domutils": ^1.8.1 || ^2.1 @@ -866,9 +902,9 @@ __metadata: version: 0.0.0-use.local resolution: "@jupyter-widgets/example-web1@workspace:examples/web1" dependencies: - "@jupyter-widgets/base": ^6.0.8 - "@jupyter-widgets/base-manager": ^1.0.9 - "@jupyter-widgets/controls": ^5.0.9 + "@jupyter-widgets/base": ^6.0.10 + "@jupyter-widgets/base-manager": ^1.0.11 + "@jupyter-widgets/controls": ^5.0.11 chai: ^4.0.0 css-loader: ^6.5.1 karma: ^6.3.3 @@ -887,9 +923,9 @@ __metadata: version: 0.0.0-use.local resolution: "@jupyter-widgets/example-web2@workspace:examples/web2" dependencies: - "@jupyter-widgets/base": ^6.0.8 - "@jupyter-widgets/base-manager": ^1.0.9 - "@jupyter-widgets/controls": ^5.0.9 + "@jupyter-widgets/base": ^6.0.10 + "@jupyter-widgets/base-manager": ^1.0.11 + "@jupyter-widgets/controls": ^5.0.11 codemirror: ^5.48.0 css-loader: ^6.5.1 font-awesome: ^4.7.0 @@ -902,9 +938,9 @@ __metadata: version: 0.0.0-use.local resolution: "@jupyter-widgets/example-web3@workspace:examples/web3" dependencies: - "@jupyter-widgets/base": ^6.0.8 - "@jupyter-widgets/controls": ^5.0.9 - "@jupyter-widgets/html-manager": ^1.0.11 + "@jupyter-widgets/base": ^6.0.10 + "@jupyter-widgets/controls": ^5.0.11 + "@jupyter-widgets/html-manager": ^1.0.13 "@jupyterlab/services": ^6.0.0 || ^7.0.0 "@types/codemirror": ^5.60.0 "@types/node": ^17.0.2 @@ -924,7 +960,7 @@ __metadata: version: 0.0.0-use.local resolution: "@jupyter-widgets/example-web4@workspace:examples/web4" dependencies: - "@jupyter-widgets/html-manager": ^1.0.11 + "@jupyter-widgets/html-manager": ^1.0.13 css-loader: ^6.5.1 font-awesome: ^4.7.0 style-loader: ^3.3.1 @@ -932,16 +968,18 @@ __metadata: languageName: unknown linkType: soft -"@jupyter-widgets/html-manager@^1.0.11, @jupyter-widgets/html-manager@workspace:packages/html-manager": +"@jupyter-widgets/html-manager@^1.0.13, @jupyter-widgets/html-manager@workspace:packages/html-manager": version: 0.0.0-use.local resolution: "@jupyter-widgets/html-manager@workspace:packages/html-manager" dependencies: "@fortawesome/fontawesome-free": ^5.12.0 - "@jupyter-widgets/base": ^6.0.8 - "@jupyter-widgets/base-manager": ^1.0.9 - "@jupyter-widgets/controls": ^5.0.9 - "@jupyter-widgets/output": ^6.0.8 - "@jupyter-widgets/schema": ^0.5.5 + "@jupyter-widgets/base": ^6.0.10 + "@jupyter-widgets/base-manager": ^1.0.11 + "@jupyter-widgets/base7": "npm:@jupyter-widgets/base@4.1.6" + "@jupyter-widgets/controls": ^5.0.11 + "@jupyter-widgets/controls7": "npm:@jupyter-widgets/controls@3.1.6" + "@jupyter-widgets/output": ^6.0.10 + "@jupyter-widgets/schema": ^0.5.6 "@jupyterlab/outputarea": ^3.0.0 || ^4.0.0 "@jupyterlab/rendermime": ^3.0.0 || ^4.0.0 "@jupyterlab/rendermime-interfaces": ^3.0.0 || ^4.0.0 @@ -979,10 +1017,10 @@ __metadata: version: 0.0.0-use.local resolution: "@jupyter-widgets/jupyterlab-manager@workspace:python/jupyterlab_widgets" dependencies: - "@jupyter-widgets/base": ^6.0.8 - "@jupyter-widgets/base-manager": ^1.0.9 - "@jupyter-widgets/controls": ^5.0.9 - "@jupyter-widgets/output": ^6.0.8 + "@jupyter-widgets/base": ^6.0.10 + "@jupyter-widgets/base-manager": ^1.0.11 + "@jupyter-widgets/controls": ^5.0.11 + "@jupyter-widgets/output": ^6.0.10 "@jupyterlab/application": ^3.0.0 || ^4.0.0 "@jupyterlab/apputils": ^3.0.0 || ^4.0.0 "@jupyterlab/builder": ^3.0.0 || ^4.0.0 @@ -1035,11 +1073,11 @@ __metadata: version: 0.0.0-use.local resolution: "@jupyter-widgets/notebook-manager@workspace:python/widgetsnbextension" dependencies: - "@jupyter-widgets/base": ^6.0.8 - "@jupyter-widgets/base-manager": ^1.0.9 - "@jupyter-widgets/controls": ^5.0.9 - "@jupyter-widgets/html-manager": ^1.0.11 - "@jupyter-widgets/output": ^6.0.8 + "@jupyter-widgets/base": ^6.0.10 + "@jupyter-widgets/base-manager": ^1.0.11 + "@jupyter-widgets/controls": ^5.0.11 + "@jupyter-widgets/html-manager": ^1.0.13 + "@jupyter-widgets/output": ^6.0.10 "@jupyterlab/services": ^6.0.0 || ^7.0.0 "@lumino/messaging": ^1.10.1 || ^2.0.1 "@lumino/widgets": ^1.30.0 || ^2.1 @@ -1053,17 +1091,17 @@ __metadata: languageName: unknown linkType: soft -"@jupyter-widgets/output@^6.0.8, @jupyter-widgets/output@workspace:packages/output": +"@jupyter-widgets/output@^6.0.10, @jupyter-widgets/output@workspace:packages/output": version: 0.0.0-use.local resolution: "@jupyter-widgets/output@workspace:packages/output" dependencies: - "@jupyter-widgets/base": ^6.0.8 + "@jupyter-widgets/base": ^6.0.10 rimraf: ^3.0.2 typescript: ~4.9.4 languageName: unknown linkType: soft -"@jupyter-widgets/schema@^0.5.5, @jupyter-widgets/schema@workspace:packages/schema": +"@jupyter-widgets/schema@^0.5.6, @jupyter-widgets/schema@workspace:packages/schema": version: 0.0.0-use.local resolution: "@jupyter-widgets/schema@workspace:packages/schema" languageName: unknown @@ -2847,6 +2885,13 @@ __metadata: languageName: node linkType: hard +"@lumino/algorithm@npm:^1.1.0, @lumino/algorithm@npm:^1.9.0, @lumino/algorithm@npm:^1.9.2": + version: 1.9.2 + resolution: "@lumino/algorithm@npm:1.9.2" + checksum: a89e7c63504236119634858e271db1cc649684d30ced5a6ebe2788af7c0837f1e05a6fd3047d8525eb756c42ce137f76b3688f75fd3ef915b71cd4f213dfbb96 + languageName: node + linkType: hard + "@lumino/algorithm@npm:^1.11.1 || ^2.0.0, @lumino/algorithm@npm:^1.9.2 || ^2.0.1, @lumino/algorithm@npm:^2.0.1": version: 2.0.1 resolution: "@lumino/algorithm@npm:2.0.1" @@ -2865,6 +2910,15 @@ __metadata: languageName: node linkType: hard +"@lumino/collections@npm:^1.9.3": + version: 1.9.3 + resolution: "@lumino/collections@npm:1.9.3" + dependencies: + "@lumino/algorithm": ^1.9.2 + checksum: 1c87a12743eddd6f6b593e47945a5645e2f99ad61c5192499b0745e48ee9aff263c7145541e77dfeea4c9f50bdd017fddfa47bfc60e718de4f28533ce45bf8c3 + languageName: node + linkType: hard + "@lumino/collections@npm:^2.0.1": version: 2.0.1 resolution: "@lumino/collections@npm:2.0.1" @@ -2987,6 +3041,16 @@ __metadata: languageName: node linkType: hard +"@lumino/messaging@npm:^1.10.0, @lumino/messaging@npm:^1.10.3, @lumino/messaging@npm:^1.2.1": + version: 1.10.3 + resolution: "@lumino/messaging@npm:1.10.3" + dependencies: + "@lumino/algorithm": ^1.9.2 + "@lumino/collections": ^1.9.3 + checksum: 1131e80379fa9b8a9b5d3418c90e25d4be48e2c92ec711518190772f9e8845a695bef45daddd06a129168cf6f158c8ad80ae86cb245f566e9195bbd9a0843b7a + languageName: node + linkType: hard + "@lumino/messaging@npm:^1.10.1 || ^2.0.1, @lumino/messaging@npm:^2.0.1": version: 2.0.1 resolution: "@lumino/messaging@npm:2.0.1" @@ -4978,7 +5042,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.12.4, ajv@npm:^6.12.5": +"ajv@npm:^6.12.3, ajv@npm:^6.12.4, ajv@npm:^6.12.5": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -5325,6 +5389,15 @@ __metadata: languageName: node linkType: hard +"backbone@npm:1.2.3": + version: 1.2.3 + resolution: "backbone@npm:1.2.3" + dependencies: + underscore: ">=1.7.0" + checksum: 7e460e9e951bcba0907334d41ccc4bd84b5d17658e72fc61f4c7e1057c238e82f97d3545582cfa0501cb824ee2d44f78a45cd18f735d869e6b71430a0c183073 + languageName: node + linkType: hard + "backbone@npm:1.4.0": version: 1.4.0 resolution: "backbone@npm:1.4.0" @@ -9822,7 +9895,7 @@ __metadata: languageName: node linkType: hard -"json5@npm:^2.1.2, json5@npm:^2.2.2, json5@npm:^2.2.3": +"json5@npm:^2.1.1, json5@npm:^2.1.2, json5@npm:^2.2.2, json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" bin: @@ -16300,6 +16373,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:^7.4.6": + version: 7.5.10 + resolution: "ws@npm:7.5.10" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: f9bb062abf54cc8f02d94ca86dcd349c3945d63851f5d07a3a61c2fcb755b15a88e943a63cf580cbdb5b74436d67ef6b67f745b8f7c0814e411379138e1863cb + languageName: node + linkType: hard + "ws@npm:^8.11.0, ws@npm:~8.11.0": version: 8.11.0 resolution: "ws@npm:8.11.0" From 7fa9cba62169062eb4ef1e1856c55742a3a45c1c Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Tue, 10 Sep 2024 19:29:16 +1000 Subject: [PATCH 11/15] Simplify tooltips to work everywhere --- packages/base/src/widget.ts | 12 ++++-------- packages/controls/src/widget_bool.ts | 8 -------- packages/controls/src/widget_description.ts | 6 ------ packages/controls/src/widget_selection.ts | 6 ------ packages/controls/src/widget_string.ts | 12 ------------ 5 files changed, 4 insertions(+), 40 deletions(-) diff --git a/packages/base/src/widget.ts b/packages/base/src/widget.ts index 46c6b0d9c7..bcd7d64f7a 100644 --- a/packages/base/src/widget.ts +++ b/packages/base/src/widget.ts @@ -1101,18 +1101,14 @@ export class DOMWidgetView extends WidgetView { } updateTooltip(): void { - const title = this.tooltip; - if (!title) { - this.el.removeAttribute('title'); - } else if (!this.model.get('description')) { + const title = this.model.get('tooltip') ?? this.model.get('description'); + if (title) { this.el.setAttribute('title', title); + } else { + this.el.removeAttribute('title'); } } - get tooltip() { - return this.model.get('tooltip') ?? this.model.get('description'); - } - /** * Update the DOM classes applied to an element, default to this.el. */ diff --git a/packages/controls/src/widget_bool.ts b/packages/controls/src/widget_bool.ts index bb7e0ee72a..8f2a67b521 100644 --- a/packages/controls/src/widget_bool.ts +++ b/packages/controls/src/widget_bool.ts @@ -179,14 +179,6 @@ export class CheckboxView extends DescriptionView { } } - updateTooltip(): void { - super.updateTooltip(); - if (!this.checkbox) return; // we might be constructing the parent - const title = this.tooltip; - this.checkbox.setAttribute('title', title); - this.descriptionSpan.setAttribute('title', title); - } - events(): { [e: string]: string } { return { 'click input[type="checkbox"]': '_handle_click', diff --git a/packages/controls/src/widget_description.ts b/packages/controls/src/widget_description.ts index 67aec82c32..fba21c7422 100644 --- a/packages/controls/src/widget_description.ts +++ b/packages/controls/src/widget_description.ts @@ -97,12 +97,6 @@ export class DescriptionView extends DOMWidgetView { } } - updateTooltip(): void { - super.updateTooltip(); - if (!this.label) return; - this.label.title = this.tooltip; - } - label: HTMLLabelElement; } diff --git a/packages/controls/src/widget_selection.ts b/packages/controls/src/widget_selection.ts index 8269b61fbf..050723c9c3 100644 --- a/packages/controls/src/widget_selection.ts +++ b/packages/controls/src/widget_selection.ts @@ -67,12 +67,6 @@ export class SelectionView extends DescriptionView { } } - updateTooltip(): void { - super.updateTooltip(); - if (!this.listbox) return; // we might be constructing the parent - this.listbox.setAttribute('title', this.tooltip); - } - listbox: HTMLSelectElement; } diff --git a/packages/controls/src/widget_string.ts b/packages/controls/src/widget_string.ts index 3c708398d8..bf00a3f34b 100644 --- a/packages/controls/src/widget_string.ts +++ b/packages/controls/src/widget_string.ts @@ -378,12 +378,6 @@ export class TextareaView extends StringView { } } - updateTooltip(): void { - super.updateTooltip(); - if (!this.textbox) return; // we might be constructing the parent - this.textbox.setAttribute('title', this.tooltip); - } - events(): { [e: string]: string } { return { 'keydown input': 'handleKeyDown', @@ -500,12 +494,6 @@ export class TextView extends StringView { } } - updateTooltip(): void { - super.updateTooltip(); - if (!this.textbox) return; // we might be constructing the parent - this.textbox.setAttribute('title', this.tooltip); - } - update(options?: any): void { /** * Update the contents of this view From 234752c512b3bc1b149740793180a81b0c08719e Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Wed, 11 Sep 2024 18:39:19 +1000 Subject: [PATCH 12/15] Remove unnecessary code. --- python/ipywidgets/ipywidgets/widgets/tests/utils.py | 10 ---------- python/ipywidgets/ipywidgets/widgets/widget_box.py | 3 +-- python/ipywidgets/ipywidgets/widgets/widget_output.py | 1 - 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/python/ipywidgets/ipywidgets/widgets/tests/utils.py b/python/ipywidgets/ipywidgets/widgets/tests/utils.py index d3d7016b6a..f382742fd4 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/utils.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/utils.py @@ -1,7 +1,6 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. -import pytest from ipywidgets import Widget import ipywidgets.widgets.widget @@ -105,12 +104,3 @@ def setup(): def call_method(method, *args, **kwargs): method(*args, **kwargs) - - -@pytest.fixture -def dummy_comm_fixture(): - setup() - try: - yield - finally: - teardown_test_comm() diff --git a/python/ipywidgets/ipywidgets/widgets/widget_box.py b/python/ipywidgets/ipywidgets/widgets/widget_box.py index 598bb40ef2..50804bb28c 100644 --- a/python/ipywidgets/ipywidgets/widgets/widget_box.py +++ b/python/ipywidgets/ipywidgets/widgets/widget_box.py @@ -77,8 +77,7 @@ class Box(DOMWidget, CoreWidget): >>> widgets.Box([title_widget, slider]) """ _model_name = Unicode('BoxModel').tag(sync=True) - _view_name = Unicode('BoxView').tag(sync=True) - tooltip = Unicode('', allow_none=True, help='A tooltip caption.').tag(sync=True) + _view_name = Unicode("BoxView").tag(sync=True) validate_mode = CaselessStrEnum(['raise', 'log_warning', 'log_error'], 'raise') # Child widgets in the container. diff --git a/python/ipywidgets/ipywidgets/widgets/widget_output.py b/python/ipywidgets/ipywidgets/widgets/widget_output.py index c8729d9fed..150ac93471 100644 --- a/python/ipywidgets/ipywidgets/widgets/widget_output.py +++ b/python/ipywidgets/ipywidgets/widgets/widget_output.py @@ -59,7 +59,6 @@ def func(): msg_id = Unicode('', help="Parent message id of messages to capture").tag(sync=True) outputs = TypedTuple(trait=Dict(), help="The output messages synced from the frontend.").tag(sync=True) - tooltip = Unicode('', allow_none=True, help="A tooltip caption.").tag(sync=True) __counter = 0 From 9d5a482a4cbc3bcc19d4ce3ad5be22f23b99cc97 Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Thu, 12 Sep 2024 18:13:28 +1000 Subject: [PATCH 13/15] Update the widget model specification for 'Children'. --- docs/source/dev_install.md | 1 + .../schema/jupyterwidgetmodels.latest.json | 42 ++++--------------- packages/schema/jupyterwidgetmodels.latest.md | 14 +++---- 3 files changed, 15 insertions(+), 42 deletions(-) diff --git a/docs/source/dev_install.md b/docs/source/dev_install.md index ad90794b4b..56d2bd90a8 100644 --- a/docs/source/dev_install.md +++ b/docs/source/dev_install.md @@ -90,6 +90,7 @@ To update the widget model specification with changes, do something like this in ``` python ./packages/schema/generate-spec.py -f json-pretty packages/schema/jupyterwidgetmodels.latest.json python ./packages/schema/generate-spec.py -f markdown packages/schema/jupyterwidgetmodels.latest.md +jlpm prettier ``` ## Releasing new versions diff --git a/packages/schema/jupyterwidgetmodels.latest.json b/packages/schema/jupyterwidgetmodels.latest.json index 0cefeb25bf..6a2b37ccef 100644 --- a/packages/schema/jupyterwidgetmodels.latest.json +++ b/packages/schema/jupyterwidgetmodels.latest.json @@ -441,12 +441,8 @@ { "default": [], "help": "List of widget children", - "items": { - "type": "reference", - "widget": "Widget" - }, "name": "children", - "type": "array" + "type": "Children" }, { "default": "reference to new instance", @@ -935,12 +931,8 @@ { "default": [], "help": "List of widget children", - "items": { - "type": "reference", - "widget": "Widget" - }, "name": "children", - "type": "array" + "type": "Children" }, { "default": "reference to new instance", @@ -3753,12 +3745,8 @@ { "default": [], "help": "List of widget children", - "items": { - "type": "reference", - "widget": "Widget" - }, "name": "children", - "type": "array" + "type": "Children" }, { "default": "reference to new instance", @@ -3850,12 +3838,8 @@ { "default": [], "help": "List of widget children", - "items": { - "type": "reference", - "widget": "Widget" - }, "name": "children", - "type": "array" + "type": "Children" }, { "default": "reference to new instance", @@ -6643,12 +6627,8 @@ { "default": [], "help": "List of widget children", - "items": { - "type": "reference", - "widget": "Widget" - }, "name": "children", - "type": "array" + "type": "Children" }, { "default": "reference to new instance", @@ -6756,12 +6736,8 @@ { "default": [], "help": "List of widget children", - "items": { - "type": "reference", - "widget": "Widget" - }, "name": "children", - "type": "array" + "type": "Children" }, { "default": "reference to new instance", @@ -7914,12 +7890,8 @@ { "default": [], "help": "List of widget children", - "items": { - "type": "reference", - "widget": "Widget" - }, "name": "children", - "type": "array" + "type": "Children" }, { "default": "reference to new instance", diff --git a/packages/schema/jupyterwidgetmodels.latest.md b/packages/schema/jupyterwidgetmodels.latest.md index eb7e7147b8..bc061245f2 100644 --- a/packages/schema/jupyterwidgetmodels.latest.md +++ b/packages/schema/jupyterwidgetmodels.latest.md @@ -76,7 +76,7 @@ that the widget is registered with. | `_view_module_version` | string | `'2.0.0'` | | `_view_name` | string | `'AccordionView'` | | `box_style` | string (one of `'success'`, `'info'`, `'warning'`, `'danger'`, `''`) | `''` | Use a predefined styling for the box. | -| `children` | array of reference to Widget widget | `[]` | List of widget children | +| `children` | Children | `[]` | List of widget children | | `layout` | reference to Layout widget | reference to new instance | | `selected_index` | `null` or number (integer) | `null` | The index of the selected page. This is either an integer selecting a particular sub-widget, or None to have no widgets selected. | | `tabbable` | `null` or boolean | `null` | Is widget tabbable? | @@ -163,7 +163,7 @@ that the widget is registered with. | `_view_module_version` | string | `'2.0.0'` | | `_view_name` | string | `'BoxView'` | | `box_style` | string (one of `'success'`, `'info'`, `'warning'`, `'danger'`, `''`) | `''` | Use a predefined styling for the box. | -| `children` | array of reference to Widget widget | `[]` | List of widget children | +| `children` | Children | `[]` | List of widget children | | `layout` | reference to Layout widget | reference to new instance | | `tabbable` | `null` or boolean | `null` | Is widget tabbable? | | `tooltip` | `null` or string | `null` | A tooltip caption. | @@ -663,7 +663,7 @@ that the widget is registered with. | `_view_module_version` | string | `'2.0.0'` | | `_view_name` | string | `'GridBoxView'` | | `box_style` | string (one of `'success'`, `'info'`, `'warning'`, `'danger'`, `''`) | `''` | Use a predefined styling for the box. | -| `children` | array of reference to Widget widget | `[]` | List of widget children | +| `children` | Children | `[]` | List of widget children | | `layout` | reference to Layout widget | reference to new instance | | `tabbable` | `null` or boolean | `null` | Is widget tabbable? | | `tooltip` | `null` or string | `null` | A tooltip caption. | @@ -680,7 +680,7 @@ that the widget is registered with. | `_view_module_version` | string | `'2.0.0'` | | `_view_name` | string | `'HBoxView'` | | `box_style` | string (one of `'success'`, `'info'`, `'warning'`, `'danger'`, `''`) | `''` | Use a predefined styling for the box. | -| `children` | array of reference to Widget widget | `[]` | List of widget children | +| `children` | Children | `[]` | List of widget children | | `layout` | reference to Layout widget | reference to new instance | | `tabbable` | `null` or boolean | `null` | Is widget tabbable? | | `tooltip` | `null` or string | `null` | A tooltip caption. | @@ -1179,7 +1179,7 @@ that the widget is registered with. | `_view_module_version` | string | `'2.0.0'` | | `_view_name` | string | `'StackView'` | | `box_style` | string (one of `'success'`, `'info'`, `'warning'`, `'danger'`, `''`) | `''` | Use a predefined styling for the box. | -| `children` | array of reference to Widget widget | `[]` | List of widget children | +| `children` | Children | `[]` | List of widget children | | `layout` | reference to Layout widget | reference to new instance | | `selected_index` | `null` or number (integer) | `null` | The index of the selected page. This is either an integer selecting a particular sub-widget, or None to have no widgets selected. | | `tabbable` | `null` or boolean | `null` | Is widget tabbable? | @@ -1198,7 +1198,7 @@ that the widget is registered with. | `_view_module_version` | string | `'2.0.0'` | | `_view_name` | string | `'TabView'` | | `box_style` | string (one of `'success'`, `'info'`, `'warning'`, `'danger'`, `''`) | `''` | Use a predefined styling for the box. | -| `children` | array of reference to Widget widget | `[]` | List of widget children | +| `children` | Children | `[]` | List of widget children | | `layout` | reference to Layout widget | reference to new instance | | `selected_index` | `null` or number (integer) | `null` | The index of the selected page. This is either an integer selecting a particular sub-widget, or None to have no widgets selected. | | `tabbable` | `null` or boolean | `null` | Is widget tabbable? | @@ -1402,7 +1402,7 @@ that the widget is registered with. | `_view_module_version` | string | `'2.0.0'` | | `_view_name` | string | `'VBoxView'` | | `box_style` | string (one of `'success'`, `'info'`, `'warning'`, `'danger'`, `''`) | `''` | Use a predefined styling for the box. | -| `children` | array of reference to Widget widget | `[]` | List of widget children | +| `children` | Children | `[]` | List of widget children | | `layout` | reference to Layout widget | reference to new instance | | `tabbable` | `null` or boolean | `null` | Is widget tabbable? | | `tooltip` | `null` or string | `null` | A tooltip caption. | From 8286c7c402223a81bc8c3d2a707d86f2d4618733 Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Sat, 26 Oct 2024 18:39:49 +1100 Subject: [PATCH 14/15] Make _model_id a property --- .../ipywidgets/ipywidgets/widgets/widget.py | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/python/ipywidgets/ipywidgets/widgets/widget.py b/python/ipywidgets/ipywidgets/widgets/widget.py index 1034f43e69..6eda43e809 100644 --- a/python/ipywidgets/ipywidgets/widgets/widget.py +++ b/python/ipywidgets/ipywidgets/widgets/widget.py @@ -471,7 +471,7 @@ def _get_embed_state(self, drop_defaults=False): return state def get_view_spec(self): - return {"version_major":2, "version_minor":0, "model_id": self._model_id} + return {"version_major": 2, "version_minor": 0, "model_id": self.model_id} #------------------------------------------------------------------------- # Traits @@ -495,6 +495,8 @@ def get_view_spec(self): keys = List(help="The traits which are synced.") + _model_id: None | str = None + @default('keys') def _default_keys(self): return [name for name in self.traits(sync=True)] @@ -537,7 +539,7 @@ def _default_comm(self): def open(self): """Open a comm to the frontend if one isn't already open.""" - assert self._model_id + assert self.model_id def _create_comm(self, comm_id=None): @@ -564,7 +566,8 @@ def _comm_changed(self, change): _instances.pop(change['old'].comm_id, None) if change['new']: if isinstance(_instances, dict): - _instances[change['new'].comm_id] = self + _instances[change["new"].comm_id] = self + self._model_id = change["new"].comm_id # prevent memory leaks by using a weak reference to self. ref = weakref.ref(self) @@ -578,14 +581,8 @@ def _handle_msg(msg): change['new'].on_msg(_handle_msg) - - @property def model_id(self): - return self._model_id - - @property - def _model_id(self): """Gets the model id of this widget. If a Comm doesn't exist yet, a Comm will be created automagically.""" @@ -864,10 +861,10 @@ def _repr_mimebundle_(self, **kwargs): # http://tools.ietf.org/html/rfc6838 # and the currently registered mimetypes at # http://www.iana.org/assignments/media-types/media-types.xhtml. - data['application/vnd.jupyter.widget-view+json'] = { - 'version_major': 2, - 'version_minor': 0, - 'model_id': self._model_id + data["application/vnd.jupyter.widget-view+json"] = { + "version_major": 2, + "version_minor": 0, + "model_id": self.model_id, } return data From ca3d0cb0964e7ad333515399eb1600bcbe03c248 Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Tue, 5 Nov 2024 08:16:29 +1100 Subject: [PATCH 15/15] Change _instances from 'global' variable to class variable. --- python/ipywidgets/ipywidgets/embed.py | 6 +- .../ipywidgets/ipywidgets/tests/test_embed.py | 4 +- .../ipywidgets/widgets/tests/test_widget.py | 39 ++------ .../widgets/tests/test_widget_box.py | 2 - .../ipywidgets/ipywidgets/widgets/widget.py | 96 +++++-------------- python/ipywidgets/setup.cfg | 2 + 6 files changed, 41 insertions(+), 108 deletions(-) diff --git a/python/ipywidgets/ipywidgets/embed.py b/python/ipywidgets/ipywidgets/embed.py index ca58880092..e9bffbfd65 100644 --- a/python/ipywidgets/ipywidgets/embed.py +++ b/python/ipywidgets/ipywidgets/embed.py @@ -12,7 +12,7 @@ import json import re -from .widgets import Widget, DOMWidget, widget as widget_module +from .widgets import Widget, DOMWidget from .widgets.widget_link import Link from .widgets.docutils import doc_subst from ._version import __html_manager_version__ @@ -129,7 +129,7 @@ def _get_recursive_state(widget, store=None, drop_defaults=False): def add_resolved_links(store, drop_defaults): """Adds the state of any link models between two models in store""" - for widget_id, widget in widget_module._instances.items(): # go over all widgets + for widget_id, widget in Widget._instances.items(): # go over all widgets if isinstance(widget, Link) and widget_id not in store: if widget.source[0].model_id in store and widget.target[0].model_id in store: store[widget.model_id] = widget._get_embed_state(drop_defaults=drop_defaults) @@ -207,7 +207,7 @@ def embed_data(views, drop_defaults=True, state=None): view_specs: a list of widget view specs """ if views is None: - views = [w for w in widget_module._instances.values() if isinstance(w, DOMWidget)] + views = [w for w in Widget._instances.values() if isinstance(w, DOMWidget)] else: try: views[0] diff --git a/python/ipywidgets/ipywidgets/tests/test_embed.py b/python/ipywidgets/ipywidgets/tests/test_embed.py index a295442455..c5e6b97aa1 100644 --- a/python/ipywidgets/ipywidgets/tests/test_embed.py +++ b/python/ipywidgets/ipywidgets/tests/test_embed.py @@ -9,7 +9,7 @@ import traitlets -from ..widgets import IntSlider, IntText, Text, Widget, jslink, HBox, widget_serialization, widget as widget_module +from ..widgets import IntSlider, IntText, Text, Widget, jslink, HBox, widget_serialization from ..embed import embed_data, embed_snippet, embed_minimal_html, dependency_state @@ -29,7 +29,7 @@ class CaseWidget(Widget): class TestEmbed: def teardown(self): - for w in tuple(widget_module._instances.values()): + for w in tuple(Widget._instances.values()): w.close() def test_embed_data_simple(self): diff --git a/python/ipywidgets/ipywidgets/widgets/tests/test_widget.py b/python/ipywidgets/ipywidgets/widgets/tests/test_widget.py index 34fd9402a2..dbd949dd10 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_widget.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_widget.py @@ -5,7 +5,6 @@ import copy import gc -import inspect import weakref import pytest @@ -15,7 +14,6 @@ import ipywidgets as ipw -from .. import widget from ..widget import Widget from ..widget_button import Button @@ -62,29 +60,12 @@ def test_close_all(): # create a couple of widgets widgets = [Button() for i in range(10)] - assert len(widget._instances) > 0, "expect active widgets" - assert widget._instances[widgets[0].model_id] is widgets[0] + assert len(Widget._instances) > 0, "expect active widgets" + assert Widget._instances[widgets[0].model_id] is widgets[0] # close all the widgets Widget.close_all() - assert len(widget._instances) == 0, "active widgets should be cleared" - - -def test_compatibility(): - button = Button() - assert widget._instances[button.model_id] is button - with pytest.deprecated_call() as record: - assert widget._instances is widget.Widget.widgets - assert widget._instances is widget.Widget._active_widgets - assert widget._registry is widget.Widget.widget_types - assert widget._registry is widget.Widget._widget_types - - Widget.close_all() - assert not widget.Widget.widgets - assert not widget.Widget._active_widgets - caller_path = inspect.stack(context=0)[1].filename - assert all(x.filename == caller_path for x in record) - assert len(record) == 6 + assert len(Widget._instances) == 0, "active widgets should be cleared" def test_widget_copy(): @@ -98,12 +79,12 @@ def test_widget_copy(): def test_widget_open(): button = Button() model_id = button.model_id - assert model_id in widget._instances + assert model_id in Widget._instances spec = button.get_view_spec() assert list(spec) == ["version_major", "version_minor", "model_id"] assert spec["model_id"] button.close() - assert model_id not in widget._instances + assert model_id not in Widget._instances with pytest.raises(RuntimeError, match="Widget is closed"): button.open() with pytest.raises(RuntimeError, match="Widget is closed"): @@ -225,7 +206,7 @@ def my_click(self, b): b = TestButton(description="button") weakref.finalize(b, on_delete) b_ref = weakref.ref(b) - assert b in widget._instances.values() + assert b in Widget._instances.values() b.on_click(b.my_click) b.on_click(lambda x: setattr(x, "clicked", True)) @@ -235,11 +216,11 @@ def my_click(self, b): if weakref_enabled: ipw.enable_weakreference() - assert b in widget._instances.values(), "Instances not transferred" + assert b in Widget._instances.values(), "Instances not transferred" ipw.disable_weakreference() - assert b in widget._instances.values(), "Instances not transferred" + assert b in Widget._instances.values(), "Instances not transferred" ipw.enable_weakreference() - assert b in widget._instances.values(), "Instances not transferred" + assert b in Widget._instances.values(), "Instances not transferred" b.click() assert click_count == 2 @@ -251,7 +232,7 @@ def my_click(self, b): assert deleted else: assert not deleted - assert b_ref() in widget._instances.values() + assert b_ref() in Widget._instances.values() b_ref().close() gc.collect() assert deleted, "Closing should remove the last strong reference." diff --git a/python/ipywidgets/ipywidgets/widgets/tests/test_widget_box.py b/python/ipywidgets/ipywidgets/widgets/tests/test_widget_box.py index 5d50324d08..021e3f4c78 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_widget_box.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_widget_box.py @@ -57,7 +57,6 @@ def test_box_validate_mode(): def test_box_gc(): - widgets.VBox._active_widgets widgets.enable_weakreference() # Test Box gc collected and children lifecycle managed. try: @@ -80,6 +79,5 @@ def on_delete(): del b gc.collect() assert deleted - widgets.VBox._active_widgets finally: widgets.disable_weakreference() diff --git a/python/ipywidgets/ipywidgets/widgets/widget.py b/python/ipywidgets/ipywidgets/widgets/widget.py index 6eda43e809..c95850132b 100644 --- a/python/ipywidgets/ipywidgets/widgets/widget.py +++ b/python/ipywidgets/ipywidgets/widgets/widget.py @@ -19,8 +19,6 @@ from base64 import standard_b64encode -from .utils import deprecation, _get_frame - from .._version import __protocol_version__, __control_protocol_version__, __jupyter_widgets_base_version__ import inspect @@ -43,29 +41,24 @@ def envset(name, default): JUPYTER_WIDGETS_ECHO = envset('JUPYTER_WIDGETS_ECHO', default=True) # for a discussion on using weak references see: # https://github.com/jupyter-widgets/ipywidgets/issues/1345 -_instances : typing.MutableMapping[str, "Widget"] = {} def enable_weakreference(): - """Use a WeakValueDictionary instead of a standard dictionary to map - `comm_id` to `widget` for every widget instance. + """Use a WeakValueDictionary to store references to Widget instances. - By default widgets are mapped using a standard dictionary. Use this feature - to permit widget garbage collection. + A strong reference must be kept to widgets. """ - global _instances - if not isinstance(_instances, weakref.WeakValueDictionary): - _instances = weakref.WeakValueDictionary(_instances) + if not isinstance(Widget._instances, weakref.WeakValueDictionary): + Widget._instances = weakref.WeakValueDictionary(Widget._instances) def disable_weakreference(): - """Use a Dictionary to map `comm_id` to `widget` for every widget instance. - - Note: this is the default setting and maintains a strong reference to the - the widget preventing automatic garbage collection. If the close method - is called, the widget will remove itself enabling garbage collection. + """Use a standard dictionary to store references to Widget instances (default behavior). + + Note: this is the default setting and maintains a strong reference to the + the widget preventing automatic garbage collection. When the widget is closed + it can be garbage collected. """ - global _instances - if isinstance(_instances, weakref.WeakValueDictionary): - _instances = dict(_instances) + if isinstance(Widget._instances, weakref.WeakValueDictionary): + Widget._instances = dict(Widget._instances) def _widget_to_json(x, obj): if isinstance(x, Widget): @@ -82,8 +75,8 @@ def _json_to_widget(x, obj): return {k: _json_to_widget(v, obj) for k, v in x.items()} elif isinstance(x, (list, tuple)): return [_json_to_widget(v, obj) for v in x] - elif isinstance(x, str) and x.startswith('IPY_MODEL_') and x[10:] in _instances: - return _instances[x[10:]] + elif isinstance(x, str) and x.startswith("IPY_MODEL_") and x[10:] in Widget._instances: + return Widget._instances[x[10:]] else: return x @@ -314,50 +307,12 @@ class Widget(LoggingHasTraits): #------------------------------------------------------------------------- _widget_construction_callback = None _control_comm = None - - @_staticproperty - def widgets(): - # Because this is a static attribute, it will be accessed when initializing this class. In that case, since a user - # did not explicitly try to use this attribute, we do not want to throw a deprecation warning. - # So we check if the thing calling this static property is one of the known initialization functions in traitlets. - frame = _get_frame(2) - if not (frame.f_code.co_filename == TRAITLETS_FILE and (frame.f_code.co_name in ('getmembers', 'setup_instance', 'setup_class'))): - deprecation("Widget.widgets is deprecated.") - return _instances - - @_staticproperty - def _active_widgets(): - # Because this is a static attribute, it will be accessed when initializing this class. In that case, since a user - # did not explicitly try to use this attribute, we do not want to throw a deprecation warning. - # So we check if the thing calling this static property is one of the known initialization functions in traitlets. - frame = _get_frame(2) - if not (frame.f_code.co_filename == TRAITLETS_FILE and (frame.f_code.co_name in ('getmembers', 'setup_instance', 'setup_class'))): - deprecation("Widget._active_widgets is deprecated.") - return _instances - - @_staticproperty - def _widget_types(): - # Because this is a static attribute, it will be accessed when initializing this class. In that case, since a user - # did not explicitly try to use this attribute, we do not want to throw a deprecation warning. - # So we check if the thing calling this static property is one of the known initialization functions in traitlets. - frame = _get_frame(2) - if not (frame.f_code.co_filename == TRAITLETS_FILE and (frame.f_code.co_name in ('getmembers', 'setup_instance', 'setup_class'))): - deprecation("Widget._widget_types is deprecated.") - return _registry - - @_staticproperty - def widget_types(): - # Because this is a static attribute, it will be accessed when initializing this class. In that case, since a user - # did not explicitly try to use this attribute, we do not want to throw a deprecation warning. - # So we check if the thing calling this static property is one of the known initialization functions in traitlets. - frame = _get_frame(2) - if not (frame.f_code.co_filename == TRAITLETS_FILE and (frame.f_code.co_name in ('getmembers', 'setup_instance', 'setup_class'))): - deprecation("Widget.widget_types is deprecated.") - return _registry + + _instances: typing.ClassVar[typing.MutableMapping[str, "Widget"]] = {} @classmethod def close_all(cls): - for widget in list(_instances.values()): + for widget in list(Widget._instances.values()): widget.close() @staticmethod @@ -399,7 +354,7 @@ def _handle_control_comm_msg(cls, msg): if method == 'request_states': # Send back the full widgets state cls.get_manager_state() - widgets = _instances.values() + widgets = cls._instances.values() full_state = {} drop_defaults = False for widget in widgets: @@ -440,8 +395,8 @@ def handle_comm_opened(comm, msg): _put_buffers(state, data['buffer_paths'], msg['buffers']) widget.set_state(state) - @staticmethod - def get_manager_state(drop_defaults=False, widgets=None): + @classmethod + def get_manager_state(cls, drop_defaults=False, widgets=None): """Returns the full state for a widget manager for embedding :param drop_defaults: when True, it will not include default value @@ -450,7 +405,7 @@ def get_manager_state(drop_defaults=False, widgets=None): """ state = {} if widgets is None: - widgets = _instances.values() + widgets = cls._instances.values() for widget in widgets: state[widget.model_id] = widget._get_embed_state(drop_defaults=drop_defaults) return {'version_major': 2, 'version_minor': 0, 'state': state} @@ -561,14 +516,11 @@ def _comm_changed(self, change): if change['old']: change['old'].on_msg(None) change['old'].close() - # On python shutdown _instances can be None - if isinstance(_instances, dict): - _instances.pop(change['old'].comm_id, None) + self._instances.pop(change['old'].comm_id, None) if change['new']: - if isinstance(_instances, dict): - _instances[change["new"].comm_id] = self - self._model_id = change["new"].comm_id - + self._instances[change["new"].comm_id] = self + self._model_id = change["new"].comm_id + # prevent memory leaks by using a weak reference to self. ref = weakref.ref(self) def _handle_msg(msg): diff --git a/python/ipywidgets/setup.cfg b/python/ipywidgets/setup.cfg index 36fbdf847c..c3fb6a9fda 100644 --- a/python/ipywidgets/setup.cfg +++ b/python/ipywidgets/setup.cfg @@ -23,6 +23,8 @@ classifiers = Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 + Programming Language :: Python :: 3.13 Programming Language :: Python :: 3 :: Only Framework :: Jupyter