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/base-manager/package.json b/packages/base-manager/package.json index cbb35dd378..5221228d5c 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 60dcf38af7..39f90dff41 100644 --- a/packages/base/package.json +++ b/packages/base/package.json @@ -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/base/src/widget.ts b/packages/base/src/widget.ts index ecf886799f..bcd7d64f7a 100644 --- a/packages/base/src/widget.ts +++ b/packages/base/src/widget.ts @@ -1101,11 +1101,11 @@ export class DOMWidgetView extends WidgetView { } updateTooltip(): void { - const title = this.model.get('tooltip'); - if (!title) { - this.el.removeAttribute('title'); - } else if (this.model.get('description').length === 0) { + const title = this.model.get('tooltip') ?? this.model.get('description'); + if (title) { this.el.setAttribute('title', title); + } else { + this.el.removeAttribute('title'); } } diff --git a/packages/controls/package.json b/packages/controls/package.json index 2a1aa6f191..8ce20b0438 100644 --- a/packages/controls/package.json +++ b/packages/controls/package.json @@ -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/controls/src/widget_bool.ts b/packages/controls/src/widget_bool.ts index a08d9ff693..8f2a67b521 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(); } /** @@ -180,16 +179,6 @@ export class CheckboxView extends DescriptionView { } } - updateTooltip(): void { - 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); - } - } - 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 2b0ccdcf7f..fba21c7422 100644 --- a/packages/controls/src/widget_description.ts +++ b/packages/controls/src/widget_description.ts @@ -97,11 +97,6 @@ export class DescriptionView extends DOMWidgetView { } } - updateTooltip(): void { - if (!this.label) return; - this.label.title = this.model.get('tooltip'); - } - label: HTMLLabelElement; } 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(); } } diff --git a/packages/controls/src/widget_selection.ts b/packages/controls/src/widget_selection.ts index 7986383d79..050723c9c3 100644 --- a/packages/controls/src/widget_selection.ts +++ b/packages/controls/src/widget_selection.ts @@ -67,16 +67,6 @@ export class SelectionView extends DescriptionView { } } - updateTooltip(): void { - 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); - } - } - listbox: HTMLSelectElement; } diff --git a/packages/controls/src/widget_string.ts b/packages/controls/src/widget_string.ts index 4bdef6c28d..bf00a3f34b 100644 --- a/packages/controls/src/widget_string.ts +++ b/packages/controls/src/widget_string.ts @@ -378,16 +378,6 @@ export class TextareaView extends StringView { } } - updateTooltip(): void { - 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); - } - } - events(): { [e: string]: string } { return { 'keydown input': 'handleKeyDown', @@ -504,16 +494,6 @@ export class TextView extends StringView { } } - updateTooltip(): void { - 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); - } - } - update(options?: any): void { /** * Update the contents of this view 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. | 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/__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_send_state.py b/python/ipywidgets/ipywidgets/widgets/tests/test_send_state.py index 98465b9b7d..ad1335e52d 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_send_state.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_send_state.py @@ -9,12 +9,11 @@ from ..._version import __control_protocol_version__ + # A widget with simple traits class SimpleWidget(Widget): a = Bool().tag(sync=True) - b = Tuple(Bool(), Bool(), Bool(), default_value=(False, False, False)).tag( - sync=True - ) + b = Tuple(Bool(), Bool(), Bool(), default_value=(False, False, False)).tag(sync=True) c = List(Bool()).tag(sync=True) diff --git a/python/ipywidgets/ipywidgets/widgets/tests/test_set_state.py b/python/ipywidgets/ipywidgets/widgets/tests/test_set_state.py index 22ec54d90f..c19ff57cde 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_set_state.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_set_state.py @@ -19,10 +19,12 @@ def echo(request): yield request.param ipywidgets.widgets.widget.JUPYTER_WIDGETS_ECHO = oldvalue + # # First some widgets to test on: # + # A widget with simple traits (list + tuple to ensure both are handled) class SimpleWidget(Widget): a = Bool().tag(sync=True) @@ -38,7 +40,6 @@ class NumberWidget(Widget): ci = CInt().tag(sync=True) - # A widget where the data might be changed on reception: def transform_fromjson(data, widget): # Switch the two last elements when setting from json, if the first element is True @@ -47,31 +48,37 @@ def transform_fromjson(data, widget): return data return [False] + data[1:-2] + [data[-1], data[-2]] + class TransformerWidget(Widget): d = List(Bool()).tag(sync=True, from_json=transform_fromjson) - # A widget that has a buffer: -class DataInstance(): +class DataInstance: def __init__(self, data=None): self.data = data + def mview_serializer(instance, widget): - return { 'data': memoryview(instance.data) if instance.data else None } + return {"data": memoryview(instance.data) if instance.data else None} + def bytes_serializer(instance, widget): - return { 'data': bytearray(memoryview(instance.data).tobytes()) if instance.data else None } + return {"data": bytearray(memoryview(instance.data).tobytes()) if instance.data else None} + def deserializer(json_data, widget): - return DataInstance( memoryview(json_data['data']).tobytes() if json_data else None ) + return DataInstance(memoryview(json_data["data"]).tobytes() if json_data else None) + class DataWidget(SimpleWidget): d = Instance(DataInstance, args=()).tag(sync=True, to_json=mview_serializer, from_json=deserializer) + # A widget that has a buffer that might be changed on reception: def truncate_deserializer(json_data, widget): - return DataInstance( json_data['data'][:20].tobytes() if json_data else None ) + return DataInstance(json_data["data"][:20].tobytes() if json_data else None) + class TruncateDataWidget(SimpleWidget): d = Instance(DataInstance, args=()).tag(sync=True, to_json=bytes_serializer, from_json=truncate_deserializer) @@ -81,72 +88,82 @@ class TruncateDataWidget(SimpleWidget): # Actual tests: # + def test_set_state_simple(echo): w = SimpleWidget() - w.set_state(dict( - a=True, - b=[True, False, True], - c=[False, True, False], - )) + w.set_state( + dict( + a=True, + b=[True, False, True], + c=[False, True, False], + ) + ) assert len(w.comm.messages) == (1 if echo else 0) def test_set_state_transformer(echo): w = TransformerWidget() - w.set_state(dict( - d=[True, False, True] - )) + w.set_state(dict(d=[True, False, True])) # Since the deserialize step changes the state, this should send an update expected = [] if echo: expected.append( - ((), dict( - buffers=[], - data=dict( - buffer_paths=[], - method='echo_update', - state=dict(d=[True, False, True]), - )))) + ( + (), + dict( + buffers=[], + data=dict( + buffer_paths=[], + method="echo_update", + state=dict(d=[True, False, True]), + ), + ), + ) + ) expected.append( - ((), dict( - buffers=[], - data=dict( - buffer_paths=[], - method='update', - state=dict(d=[False, True, False]), - )))) + ( + (), + dict( + buffers=[], + data=dict( + buffer_paths=[], + method="update", + state=dict(d=[False, True, False]), + ), + ), + ) + ) assert w.comm.messages == expected def test_set_state_data(echo): w = DataWidget() - data = memoryview(b'x'*30) - w.set_state(dict( - a=True, - d={'data': data}, - )) + data = memoryview(b"x" * 30) + w.set_state( + dict( + a=True, + d={"data": data}, + ) + ) assert len(w.comm.messages) == (1 if echo else 0) def test_set_state_data_truncate(echo): w = TruncateDataWidget() - data = memoryview(b'x'*30) - w.set_state(dict( - a=True, - d={'data': data}, - )) + data = memoryview(b"x" * 30) + w.set_state( + dict( + a=True, + d={"data": data}, + ) + ) # Get message for checking - assert len(w.comm.messages) == 2 if echo else 1 # ensure we didn't get more than expected + assert len(w.comm.messages) == 2 if echo else 1 # ensure we didn't get more than expected msg = w.comm.messages[-1] # Assert that the data update (truncation) sends an update - buffers = msg[1].pop('buffers') - assert msg == ((), dict( - data=dict( - method='update', - state=dict(d={}), - buffer_paths=[['d', 'data']] - ))) + buffers = msg[1].pop("buffers") + assert msg == ((), dict(data=dict(method="update", state=dict(d={}), buffer_paths=[["d", "data"]]))) # Sanity: assert len(buffers) == 1 @@ -159,12 +176,14 @@ def test_set_state_numbers_int(echo): w = NumberWidget() # Set everything with ints - w.set_state(dict( - f = 1, - cf = 2, - i = 3, - ci = 4, - )) + w.set_state( + dict( + f=1, + cf=2, + i=3, + ci=4, + ) + ) # Ensure one update message gets produced assert len(w.comm.messages) == (1 if echo else 0) @@ -172,11 +191,7 @@ def test_set_state_numbers_int(echo): 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 - )) + w.set_state(dict(f=1.0, cf=2.0, ci=4.0)) # Ensure one update message gets produced assert len(w.comm.messages) == (1 if echo else 0) @@ -184,10 +199,12 @@ def test_set_state_numbers_float(echo): def test_set_state_float_to_float(echo): w = NumberWidget() # Set floats to float - w.set_state(dict( - f = 1.2, - cf = 2.6, - )) + w.set_state( + dict( + f=1.2, + cf=2.6, + ) + ) # Ensure one message gets produced assert len(w.comm.messages) == (1 if echo else 0) @@ -196,15 +213,13 @@ def test_set_state_cint_to_float(echo): w = NumberWidget() # Set CInt to float - w.set_state(dict( - ci = 5.6 - )) + w.set_state(dict(ci=5.6)) # Ensure an update message gets produced assert len(w.comm.messages) == (2 if echo else 1) msg = w.comm.messages[-1] - data = msg[1]['data'] - assert data['method'] == 'update' - assert data['state'] == {'ci': 5} + data = msg[1]["data"] + assert data["method"] == "update" + assert data["state"] == {"ci": 5} # This test is disabled, meaning ipywidgets REQUIRES @@ -216,9 +231,7 @@ def _x_test_set_state_int_to_int_like(): w = NumberWidget() # Set floats to int-like floats - w.set_state(dict( - i = 3.0 - )) + w.set_state(dict(i=3.0)) # Ensure no update message gets produced assert len(w.comm.messages) == 0 @@ -228,9 +241,8 @@ def test_set_state_int_to_float(echo): # Set Int to float with pytest.raises(TraitError): - w.set_state(dict( - i = 3.5 - )) + w.set_state(dict(i=3.5)) + 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) @@ -238,9 +250,9 @@ class AnnoyingWidget(Widget): value = Float().tag(sync=True) stop = Bool(False) - @observe('value') + @observe("value") def _propagate_value(self, change): - print('_propagate_value', change.new) + print("_propagate_value", change.new) if self.stop: return if change.new == 42: @@ -254,7 +266,7 @@ def _propagate_value(self, change): widget._send = mock.MagicMock() # this mimics a value coming from the front end - widget.set_state({'value': 42}) + widget.set_state({"value": 42}) assert widget.value == 42 assert widget.stop is True @@ -262,15 +274,16 @@ def _propagate_value(self, change): calls = [] widget._send.assert_has_calls(calls) + 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) other = Float().tag(sync=True) - @observe('value') + @observe("value") def _propagate_value(self, change): - print('_propagate_value', change.new) + print("_propagate_value", change.new) if change.new == 42: self.value = 2 self.other = 11 @@ -280,24 +293,23 @@ def _propagate_value(self, change): widget._send = mock.MagicMock() # this mimics a value coming from the front end - widget.set_state({'value': 42}) + widget.set_state({"value": 42}) assert widget.value == 2 assert widget.other == 11 - msg = {'method': 'echo_update', 'state': {'value': 42.0}, 'buffer_paths': []} + msg = {"method": "echo_update", "state": {"value": 42.0}, "buffer_paths": []} call42 = mock.call(msg, buffers=[]) - msg = {'method': 'update', 'state': {'value': 2.0}, 'buffer_paths': []} + msg = {"method": "update", "state": {"value": 2.0}, "buffer_paths": []} call2 = mock.call(msg, buffers=[]) - msg = {'method': 'update', 'state': {'other': 11.0}, 'buffer_paths': []} + msg = {"method": "update", "state": {"other": 11.0}, "buffer_paths": []} call11 = mock.call(msg, buffers=[]) calls = [call42, call2, call11] if echo else [call2, call11] widget._send.assert_has_calls(calls) - def test_echo(): # we always echo values back to the frontend class ValueWidget(Widget): @@ -308,11 +320,11 @@ class ValueWidget(Widget): widget._send = mock.MagicMock() # this mimics a state coming from the front end - widget.set_state({'value': 42, 'unexpected_field': 43}) + widget.set_state({"value": 42, "unexpected_field": 43}) assert widget.value == 42 # we expect this to be echoed - msg = {'method': 'echo_update', 'state': {'value': 42.0}, 'buffer_paths': []} + msg = {"method": "echo_update", "state": {"value": 42.0}, "buffer_paths": []} call42 = mock.call(msg, buffers=[]) calls = [call42] @@ -324,7 +336,8 @@ def test_echo_single(): class ValueWidget(Widget): value = Float().tag(sync=True) square = Float().tag(sync=True) - @observe('value') + + @observe("value") def _square(self, change): self.square = self.value**2 @@ -333,33 +346,34 @@ def _square(self, change): widget._send = mock.MagicMock() # this mimics a value coming from the front end - widget._handle_msg({ - 'content': { - 'data': { - 'method': 'update', - 'state': { - 'value': 8, + widget._handle_msg( + { + "content": { + "data": { + "method": "update", + "state": { + "value": 8, + }, } } } - }) + ) assert widget.value == 8 assert widget.square == 64 # we expect this to be echoed # note that only value is echoed, not square - msg = {'method': 'echo_update', 'state': {'value': 8.0}, 'buffer_paths': []} + msg = {"method": "echo_update", "state": {"value": 8.0}, "buffer_paths": []} call = mock.call(msg, buffers=[]) - msg = {'method': 'update', 'state': {'square': 64}, 'buffer_paths': []} + msg = {"method": "update", "state": {"square": 64}, "buffer_paths": []} call2 = mock.call(msg, buffers=[]) - calls = [call, call2] widget._send.assert_has_calls(calls) -def test_no_echo(echo): +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) @@ -369,16 +383,18 @@ class ValueWidget(Widget): widget._send = mock.MagicMock() # this mimics a value coming from the front end - widget._handle_msg({ - 'content': { - 'data': { - 'method': 'update', - 'state': { - 'value': 42, + widget._handle_msg( + { + "content": { + "data": { + "method": "update", + "state": { + "value": 42, + }, } } } - }) + ) assert widget.value == 42 # widget._send.assert_not_called(calls) @@ -386,4 +402,6 @@ class ValueWidget(Widget): # a regular set should sync to the frontend widget.value = 43 - widget._send.assert_has_calls([mock.call({'method': 'update', 'state': {'value': 43.0}, 'buffer_paths': []}, buffers=[])]) + widget._send.assert_has_calls( + [mock.call({"method": "update", "state": {"value": 43.0}, "buffer_paths": []}, buffers=[])] + ) diff --git a/python/ipywidgets/ipywidgets/widgets/tests/test_widget.py b/python/ipywidgets/ipywidgets/widgets/tests/test_widget.py index c5aa36048a..dbd949dd10 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_widget.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_widget.py @@ -3,17 +3,19 @@ """Test Widget.""" -import inspect +import copy +import gc +import weakref import pytest from IPython.core.interactiveshell import InteractiveShell from IPython.display import display from IPython.utils.capture import capture_output -from .. import widget +import ipywidgets as ipw + from ..widget import Widget from ..widget_button import Button -import copy def test_no_widget_view(): @@ -58,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(): @@ -88,4 +73,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..021e3f4c78 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_widget_box.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_widget_box.py @@ -1,33 +1,83 @@ # 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.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 + finally: + widgets.disable_weakreference() diff --git a/python/ipywidgets/ipywidgets/widgets/tests/utils.py b/python/ipywidgets/ipywidgets/widgets/tests/utils.py index 8dbbcb355b..f382742fd4 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/utils.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/utils.py @@ -1,12 +1,14 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. + from ipywidgets import Widget import ipywidgets.widgets.widget # 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 @@ -14,9 +16,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__() @@ -57,6 +60,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 @@ -66,11 +70,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 @@ -87,11 +94,13 @@ 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) diff --git a/python/ipywidgets/ipywidgets/widgets/widget.py b/python/ipywidgets/ipywidgets/widgets/widget.py index cb05ff2bbb..c95850132b 100644 --- a/python/ipywidgets/ipywidgets/widgets/widget.py +++ b/python/ipywidgets/ipywidgets/widgets/widget.py @@ -6,21 +6,19 @@ 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 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 @@ -41,17 +39,34 @@ 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 to store references to Widget instances. + + A strong reference must be kept to widgets. + """ + if not isinstance(Widget._instances, weakref.WeakValueDictionary): + Widget._instances = weakref.WeakValueDictionary(Widget._instances) + +def disable_weakreference(): + """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. + """ + if isinstance(Widget._instances, weakref.WeakValueDictionary): + Widget._instances = dict(Widget._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 @@ -60,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 @@ -215,18 +230,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: @@ -305,49 +308,11 @@ 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 @@ -389,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: @@ -430,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 @@ -440,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} @@ -461,7 +426,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 @@ -485,6 +450,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)] @@ -499,11 +466,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 +487,76 @@ 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 - - self.comm.on_msg(self._handle_msg) - _instances[self.model_id] = self + if change['old']: + change['old'].on_msg(None) + change['old'].close() + self._instances.pop(change['old'].comm_id, None) + if change['new']: + 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): + 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) @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 +684,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 +753,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 +778,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) @@ -812,17 +813,18 @@ 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 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() diff --git a/python/ipywidgets/ipywidgets/widgets/widget_box.py b/python/ipywidgets/ipywidgets/widgets/widget_box.py index 740e54cb1a..50804bb28c 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) @@ -47,20 +77,23 @@ class Box(DOMWidget, CoreWidget): >>> widgets.Box([title_widget, slider]) """ _model_name = Unicode('BoxModel').tag(sync=True) - _view_name = Unicode('BoxView').tag(sync=True) + _view_name = Unicode("BoxView").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 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 diff --git a/python/jupyterlab_widgets/package.json b/python/jupyterlab_widgets/package.json index 44d597afbe..aa595156c6 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/yarn.lock b/yarn.lock index b2e6fde943..f3ea982d90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -706,7 +706,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 @@ -766,7 +765,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 @@ -828,7 +826,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 @@ -1011,10 +1008,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 @@ -3531,7 +3537,7 @@ __metadata: languageName: node linkType: hard -"@types/backbone@npm:1.4.14": +"@types/backbone@npm:1.4.14, @types/backbone@npm:^1.4.1": version: 1.4.14 resolution: "@types/backbone@npm:1.4.14" dependencies: @@ -3541,16 +3547,6 @@ __metadata: languageName: node linkType: hard -"@types/backbone@npm:^1.4.1": - version: 1.4.22 - resolution: "@types/backbone@npm:1.4.22" - dependencies: - "@types/jquery": "*" - "@types/underscore": "*" - checksum: 095959d89abfdbf01071c8389f8638cf941e235c7ba7b03d2500cea34c8c313830605c3c87b2bb8630760bb9ed8ca54114344c1721947ce6992016482b295c6c - languageName: node - linkType: hard - "@types/base64-js@npm:^1.2.5": version: 1.3.2 resolution: "@types/base64-js@npm:1.3.2" @@ -3579,14 +3575,7 @@ __metadata: languageName: node linkType: hard -"@types/chai@npm:*": - version: 5.0.0 - resolution: "@types/chai@npm:5.0.0" - checksum: ae3d63d8e84b4fc7fce5b0e68d0000834e709e19378e569c9ab97503a1b38f582ff69db6299528a849ec336954690a905225cb0dd9648d823c291f53ae93b458 - languageName: node - linkType: hard - -"@types/chai@npm:^4.1.7": +"@types/chai@npm:*, @types/chai@npm:^4.1.7": version: 4.3.20 resolution: "@types/chai@npm:4.3.20" checksum: 7c5b0c9148f1a844a8d16cb1e16c64f2e7749cab2b8284155b9e494a6b34054846e22fb2b38df6b290f9bf57e6beebb2e121940c5896bc086ad7bab7ed429f06 @@ -3709,14 +3698,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 @@ -3737,16 +3719,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=10.0.0": - version: 22.7.8 - resolution: "@types/node@npm:22.7.8" - dependencies: - undici-types: ~6.19.2 - checksum: c1dd36bd0bf82588e61f82edb29a792f21ce902f90cc5485591f9fd60cec3ea9172e044bf7b1c0849e7cf3a5a01da39516db260cb65cb0b94904010e00634a1c - 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 @@ -3819,16 +3792,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: @@ -4772,18 +4736,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" @@ -4819,13 +4771,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" @@ -4854,13 +4799,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" @@ -5055,7 +4993,7 @@ __metadata: languageName: node linkType: hard -"async@npm:3.2.5": +"async@npm:3.2.5, async@npm:^3.2.3": version: 3.2.5 resolution: "async@npm:3.2.5" checksum: 5ec77f1312301dee02d62140a6b1f7ee0edd2a0f983b6fd2b0849b969f245225b990b47b8243e7b9ad16451a53e7f68e753700385b706198ced888beedba3af4 @@ -5071,13 +5009,6 @@ __metadata: languageName: node linkType: hard -"async@npm:^3.2.3": - version: 3.2.6 - resolution: "async@npm:3.2.6" - checksum: ee6eb8cd8a0ab1b58bd2a3ed6c415e93e773573a91d31df9d5ef559baafa9dab37d3b096fa7993e84585cac3697b2af6ddb9086f45d3ac8cae821bb2aab65682 - languageName: node - linkType: hard - "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -5156,43 +5087,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: @@ -5202,57 +5097,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.2.3": version: 1.2.3 resolution: "backbone@npm:1.2.3" @@ -5710,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" @@ -5770,7 +5601,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: @@ -5789,25 +5620,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" @@ -5852,20 +5664,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" @@ -5900,7 +5705,7 @@ __metadata: languageName: node linkType: hard -"clipanion@npm:4.0.0-rc.3": +"clipanion@npm:4.0.0-rc.3, clipanion@npm:^4.0.0-rc.2": version: 4.0.0-rc.3 resolution: "clipanion@npm:4.0.0-rc.3" dependencies: @@ -5911,17 +5716,6 @@ __metadata: languageName: node linkType: hard -"clipanion@npm:^4.0.0-rc.2": - version: 4.0.0-rc.4 - resolution: "clipanion@npm:4.0.0-rc.4" - dependencies: - typanion: ^3.8.0 - peerDependencies: - typanion: "*" - checksum: a92aa03b24eb89292b7bda570973c164fff16a1c5ba4c4abdd1b0dd6110a57651752114ec9f5cfc29e2040213e514b3220142a2316c4fc4e659ba423caa296c7 - languageName: node - linkType: hard - "cliui@npm:^7.0.2": version: 7.0.4 resolution: "cliui@npm:7.0.4" @@ -5980,13 +5774,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.18 resolution: "codemirror@npm:5.65.18" @@ -6369,13 +6156,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" @@ -6427,20 +6207,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" @@ -6561,20 +6334,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" @@ -6684,7 +6450,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: @@ -6902,15 +6668,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" @@ -7291,7 +7048,7 @@ __metadata: languageName: node linkType: hard -"envinfo@npm:7.13.0": +"envinfo@npm:7.13.0, envinfo@npm:^7.7.3, envinfo@npm:^7.7.4": version: 7.13.0 resolution: "envinfo@npm:7.13.0" bin: @@ -7300,15 +7057,6 @@ __metadata: languageName: node linkType: hard -"envinfo@npm:^7.7.3, envinfo@npm:^7.7.4": - version: 7.14.0 - resolution: "envinfo@npm:7.14.0" - bin: - envinfo: dist/cli.js - checksum: 137c1dd9a4d5781c4a6cdc6b695454ba3c4ba1829f73927198aa4122f11b35b59d7b2cb7e1ceea1364925a30278897548511d22f860c14253a33797d0bebd551 - languageName: node - linkType: hard - "err-code@npm:^2.0.2": version: 2.0.3 resolution: "err-code@npm:2.0.3" @@ -7465,7 +7213,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 @@ -7763,27 +7511,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" @@ -8458,7 +8192,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: @@ -8501,20 +8235,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" @@ -8558,13 +8278,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" @@ -8729,15 +8442,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" @@ -9249,15 +8953,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" @@ -9395,13 +9090,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" @@ -9700,27 +9388,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" @@ -9728,21 +9395,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" @@ -9849,13 +9501,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" @@ -9893,15 +9538,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:^3.0.2": version: 3.0.2 resolution: "jsesc@npm:3.0.2" @@ -9952,13 +9588,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" @@ -10001,17 +9630,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" @@ -10021,20 +9639,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.3.1 - resolution: "jsonc-parser@npm:3.3.1" - checksum: 81ef19d98d9c6bd6e4a37a95e2753c51c21705cbeffd895e177f4b542cca9cda5fda12fb942a71a2e824a9132cf119dc2e642e9286386055e1365b5478f49a47 - languageName: node - linkType: hard - "jsonfile@npm:^4.0.0": version: 4.0.0 resolution: "jsonfile@npm:4.0.0" @@ -10428,20 +10039,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" @@ -10532,17 +10136,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" @@ -10773,7 +10366,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: @@ -11034,20 +10627,13 @@ __metadata: languageName: node linkType: hard -"mime-db@npm:1.52.0": +"mime-db@npm:1.52.0, mime-db@npm:>= 1.43.0 < 2": version: 1.52.0 resolution: "mime-db@npm:1.52.0" checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f languageName: node linkType: hard -"mime-db@npm:>= 1.43.0 < 2": - version: 1.53.0 - resolution: "mime-db@npm:1.53.0" - checksum: 3fd9380bdc0b085d0b56b580e4f89ca4fc3b823722310d795c248f0806b9a80afd5d8f4347f015ad943b9ecfa7cc0b71dffa0db96fa776d01a13474821a2c7fb - languageName: node - linkType: hard - "mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" @@ -11133,7 +10719,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: @@ -11142,7 +10728,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: @@ -11187,15 +10773,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" @@ -11517,20 +11094,13 @@ __metadata: languageName: node linkType: hard -"negotiator@npm:0.6.3": +"negotiator@npm:0.6.3, negotiator@npm:^0.6.3": version: 0.6.3 resolution: "negotiator@npm:0.6.3" checksum: b8ffeb1e262eff7968fc90a2b6767b04cfd9842582a9d0ece0af7049537266e7b2506dfb1d107a32f06dd849ab2aea834d5830f7f4d0e5cb7d36e1ae55d021d9 languageName: node linkType: hard -"negotiator@npm:^0.6.3": - version: 0.6.4 - resolution: "negotiator@npm:0.6.4" - checksum: 7ded10aa02a0707d1d12a9973fdb5954f98547ca7beb60e31cb3a403cc6e8f11138db7a3b0128425cf836fc85d145ec4ce983b2bdf83dca436af879c2d683510 - languageName: node - linkType: hard - "neo-async@npm:^2.6.2": version: 2.6.2 resolution: "neo-async@npm:2.6.2" @@ -13617,7 +13187,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: @@ -13886,15 +13456,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" @@ -14176,21 +13737,7 @@ __metadata: languageName: node linkType: hard -"sanitize-html@npm:^2.3": - version: 2.13.1 - resolution: "sanitize-html@npm:2.13.1" - 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: 645381375fcb9c70070644b02538f1d43d35db6c9761eba32ec5f0ea919fdc70aa19e186ae949e6e21de767cbf11ced3e6a31b322a6c705593dc9a902a257d7a - 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: @@ -14220,15 +13767,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" @@ -14270,7 +13808,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: @@ -14638,7 +14176,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: @@ -14656,15 +14194,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" @@ -14775,13 +14304,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" @@ -15059,15 +14581,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" @@ -15168,13 +14681,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" @@ -15397,13 +14903,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" @@ -15476,13 +14975,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" @@ -15775,13 +15267,6 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~6.19.2": - version: 6.19.8 - resolution: "undici-types@npm:6.19.8" - checksum: de51f1b447d22571cf155dfe14ff6d12c5bdaec237c765085b439c38ca8518fc360e88c70f99469162bf2e14188a7b0bcb06e1ed2dc031042b984b0bb9544017 - languageName: node - linkType: hard - "union@npm:~0.5.0": version: 0.5.0 resolution: "union@npm:0.5.0" @@ -16147,7 +15632,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 @@ -16161,13 +15646,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" @@ -16661,22 +16139,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.11.0": - version: 8.18.0 - resolution: "ws@npm:8.18.0" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 91d4d35bc99ff6df483bdf029b9ea4bfd7af1f16fc91231a96777a63d263e1eabf486e13a2353970efc534f9faa43bdbf9ee76525af22f4752cbc5ebda333975 - languageName: node - linkType: hard - -"ws@npm:~8.17.1": +"ws@npm:^8.11.0, ws@npm:~8.17.1": version: 8.17.1 resolution: "ws@npm:8.17.1" peerDependencies: @@ -16737,7 +16200,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 @@ -16751,13 +16214,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"