Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-ipywidgets for 
openSUSE:Factory checked in at 2022-12-26 23:27:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-ipywidgets (Old)
 and      /work/SRC/openSUSE:Factory/.python-ipywidgets.new.1563 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-ipywidgets"

Mon Dec 26 23:27:57 2022 rev:12 rq:1045321 version:8.0.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-ipywidgets/python-ipywidgets.changes      
2022-09-10 20:17:50.324913421 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-ipywidgets.new.1563/python-ipywidgets.changes
    2022-12-26 23:28:10.768974086 +0100
@@ -1,0 +2,14 @@
+Sun Dec 25 19:30:47 UTC 2022 - Ben Greiner <[email protected]>
+
+- Update to 8.0.4
+  * Fix: slider change event issue with tapping: #3597, #3617
+  * Fix: unintentional deprecation warnings: #3648, #3650
+  * Fix: registry state lookup failed, making is impossible to
+    create widgets from the frontend: #3653
+- Release 8.0.3
+  * Fix: be backwards compatibel with 7.x, where we re-instroduced
+    .widget and .widget_types #3567
+  * Fix: be backwards compatibel with 7.x, revert hold_sync during
+    set_state #3642
+
+-------------------------------------------------------------------

Old:
----
  ipywidgets-8.0.2.tar.gz

New:
----
  ipywidgets-8.0.4.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-ipywidgets.spec ++++++
--- /var/tmp/diff_new_pack.foefzb/_old  2022-12-26 23:28:11.244976866 +0100
+++ /var/tmp/diff_new_pack.foefzb/_new  2022-12-26 23:28:11.248976889 +0100
@@ -16,9 +16,8 @@
 #
 
 
-%{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-ipywidgets
-Version:        8.0.2
+Version:        8.0.4
 Release:        0
 Summary:        IPython HTML widgets for Jupyter
 License:        BSD-3-Clause
@@ -26,16 +25,18 @@
 URL:            https://github.com/jupyter-widgets/ipywidgets
 Source0:        
https://files.pythonhosted.org/packages/source/i/ipywidgets/ipywidgets-%{version}.tar.gz
 BuildRequires:  %{python_module base >= 3.7}
+BuildRequires:  %{python_module pip}
 BuildRequires:  %{python_module setuptools}
+BuildRequires:  %{python_module wheel}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 BuildRequires:  unzip
 Requires:       python-ipykernel >= 4.5.1
 Requires:       python-ipython >= 6.1.0
 Requires:       python-ipython_genutils >= 0.2
-Requires:       python-jupyterlab_widgets >= 3.0
 Requires:       python-traitlets >= 4.3.1
-Requires:       python-widgetsnbextension >= 4.0
+Requires:       (python-jupyterlab_widgets >= 3.0 with 
python-jupyterlab_widgets < 4)
+Requires:       (python-widgetsnbextension >= 4.0 with 
python-widgetsnbextension < 5)
 Provides:       python-jupyter_ipywidgets = %{version}
 Obsoletes:      python-jupyter_ipywidgets < %{version}
 BuildArch:      noarch
@@ -44,11 +45,11 @@
 BuildRequires:  %{python_module ipython >= 6.1.0}
 BuildRequires:  %{python_module ipython_genutils >= 0.2}
 BuildRequires:  %{python_module jsonschema}
-BuildRequires:  %{python_module jupyterlab_widgets >= 3}
+BuildRequires:  %{python_module jupyterlab_widgets >= 3 with 
%python-jupyterlab_widgets < 4}
 BuildRequires:  %{python_module pytest >= 3.6.0}
 BuildRequires:  %{python_module pytz}
 BuildRequires:  %{python_module traitlets >= 4.3.1}
-BuildRequires:  %{python_module widgetsnbextension >= 4.0}
+BuildRequires:  %{python_module widgetsnbextension >= 4.0 with 
%python-widgetsnbextension < 5}
 # /SECTION
 %if "%{python_flavor}" == "python3" || "%{?python_provides}"  == "python3"
 Provides:       jupyter-ipywidgets = %{version}
@@ -60,12 +61,14 @@
 
 %prep
 %autosetup -p1 -n ipywidgets-%{version}
+# remove shebangs from test modules. Those are not standalone scripts.
+sed -i '1{/env python/d}' ipywidgets/widgets/tests/*.py
 
 %build
-%python_build
+%pyproject_wheel
 
 %install
-%python_install
+%pyproject_install
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
 
 %check

++++++ ipywidgets-8.0.2.tar.gz -> ipywidgets-8.0.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ipywidgets-8.0.2/PKG-INFO 
new/ipywidgets-8.0.4/PKG-INFO
--- old/ipywidgets-8.0.2/PKG-INFO       2022-09-02 20:40:07.634295700 +0200
+++ new/ipywidgets-8.0.4/PKG-INFO       2022-12-22 10:14:32.496301000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: ipywidgets
-Version: 8.0.2
+Version: 8.0.4
 Summary: Jupyter interactive widgets
 Home-page: http://jupyter.org
 Author: Jupyter Development Team
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ipywidgets-8.0.2/ipywidgets/_version.py 
new/ipywidgets-8.0.4/ipywidgets/_version.py
--- old/ipywidgets-8.0.2/ipywidgets/_version.py 2022-09-02 20:39:55.000000000 
+0200
+++ new/ipywidgets-8.0.4/ipywidgets/_version.py 2022-12-22 10:14:04.000000000 
+0100
@@ -1,7 +1,7 @@
 # Copyright (c) Jupyter Development Team.
 # Distributed under the terms of the Modified BSD License.
 
-__version__ = '8.0.2'
+__version__ = '8.0.4'
 
 __protocol_version__ = '2.1.0'
 __control_protocol_version__ = '1.0.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ipywidgets-8.0.2/ipywidgets/embed.py 
new/ipywidgets-8.0.4/ipywidgets/embed.py
--- old/ipywidgets-8.0.2/ipywidgets/embed.py    2022-09-02 20:27:45.000000000 
+0200
+++ new/ipywidgets-8.0.4/ipywidgets/embed.py    2022-12-22 09:55:44.000000000 
+0100
@@ -12,7 +12,7 @@
 
 import json
 import re
-from .widgets import Widget, DOMWidget
+from .widgets import Widget, DOMWidget, widget as widget_module
 from .widgets.widget_link import Link
 from .widgets.docutils import doc_subst
 from ._version import __html_manager_version__
@@ -129,7 +129,7 @@
 
 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._active_widgets.items(): # go over all 
widgets
+    for widget_id, widget in widget_module._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 @@
         view_specs: a list of widget view specs
     """
     if views is None:
-        views = [w for w in Widget._active_widgets.values() if isinstance(w, 
DOMWidget)]
+        views = [w for w in widget_module._instances.values() if isinstance(w, 
DOMWidget)]
     else:
         try:
             views[0]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ipywidgets-8.0.2/ipywidgets/tests/test_embed.py 
new/ipywidgets-8.0.4/ipywidgets/tests/test_embed.py
--- old/ipywidgets-8.0.2/ipywidgets/tests/test_embed.py 2022-09-02 
20:27:45.000000000 +0200
+++ new/ipywidgets-8.0.4/ipywidgets/tests/test_embed.py 2022-12-22 
09:55:44.000000000 +0100
@@ -9,7 +9,7 @@
 
 import traitlets
 
-from ..widgets import IntSlider, IntText, Text, Widget, jslink, HBox, 
widget_serialization
+from ..widgets import IntSlider, IntText, Text, Widget, jslink, HBox, 
widget_serialization, widget as widget_module
 from ..embed import embed_data, embed_snippet, embed_minimal_html, 
dependency_state
 
 
@@ -29,7 +29,7 @@
 class TestEmbed:
 
     def teardown(self):
-        for w in tuple(Widget._active_widgets.values()):
+        for w in tuple(widget_module._instances.values()):
             w.close()
 
     def test_embed_data_simple(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ipywidgets-8.0.2/ipywidgets/widgets/tests/test_send_state.py 
new/ipywidgets-8.0.4/ipywidgets/widgets/tests/test_send_state.py
--- old/ipywidgets-8.0.2/ipywidgets/widgets/tests/test_send_state.py    
2022-09-02 20:27:45.000000000 +0200
+++ new/ipywidgets-8.0.4/ipywidgets/widgets/tests/test_send_state.py    
2022-12-22 09:55:44.000000000 +0100
@@ -12,29 +12,20 @@
 # 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)
 
+
 def test_empty_send_state():
     w = SimpleWidget()
     w.send_state([])
     assert w.comm.messages == []
 
+
 def test_empty_hold_sync():
     w = SimpleWidget()
     with w.hold_sync():
         pass
     assert w.comm.messages == []
-
-
-def test_control():
-    comm = DummyComm()
-    Widget.close_all()
-    w = SimpleWidget()
-    Widget.handle_control_comm_opened(
-        comm, dict(metadata={'version': __control_protocol_version__})
-    )
-    Widget._handle_control_comm_msg(dict(content=dict(
-        data={'method': 'request_states'}
-    )))
-    assert comm.messages
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ipywidgets-8.0.2/ipywidgets/widgets/tests/test_set_state.py 
new/ipywidgets-8.0.4/ipywidgets/widgets/tests/test_set_state.py
--- old/ipywidgets-8.0.2/ipywidgets/widgets/tests/test_set_state.py     
2022-09-02 20:27:45.000000000 +0200
+++ new/ipywidgets-8.0.4/ipywidgets/widgets/tests/test_set_state.py     
2022-12-22 09:55:44.000000000 +0100
@@ -67,14 +67,14 @@
     return DataInstance( memoryview(json_data['data']).tobytes() if json_data 
else None )
 
 class DataWidget(SimpleWidget):
-    d = Instance(DataInstance).tag(sync=True, to_json=mview_serializer, 
from_json=deserializer)
+    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 )
 
 class TruncateDataWidget(SimpleWidget):
-    d = Instance(DataInstance).tag(sync=True, to_json=bytes_serializer, 
from_json=truncate_deserializer)
+    d = Instance(DataInstance, args=()).tag(sync=True, 
to_json=bytes_serializer, from_json=truncate_deserializer)
 
 
 #
@@ -287,10 +287,13 @@
     msg = {'method': 'echo_update', 'state': {'value': 42.0}, 'buffer_paths': 
[]}
     call42 = mock.call(msg, buffers=[])
 
-    msg = {'method': 'update', 'state': {'value': 2.0, 'other': 11.0}, 
'buffer_paths': []}
+    msg = {'method': 'update', 'state': {'value': 2.0}, 'buffer_paths': []}
     call2 = mock.call(msg, buffers=[])
 
-    calls = [call42, call2] if echo else [call2]
+    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)
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ipywidgets-8.0.2/ipywidgets/widgets/tests/test_utils.py 
new/ipywidgets-8.0.4/ipywidgets/widgets/tests/test_utils.py
--- old/ipywidgets-8.0.2/ipywidgets/widgets/tests/test_utils.py 1970-01-01 
01:00:00.000000000 +0100
+++ new/ipywidgets-8.0.4/ipywidgets/widgets/tests/test_utils.py 2022-12-22 
09:55:44.000000000 +0100
@@ -0,0 +1,72 @@
+# Copyright (c) Jupyter Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+import inspect
+import pytest
+
+from ..utils import deprecation
+from .utils import call_method
+
+CALL_PATH = inspect.getfile(call_method)
+
+def test_deprecation():
+    caller_path = inspect.stack(context=0)[1].filename
+    with pytest.deprecated_call() as record:
+        deprecation('Deprecated call')
+    # Make sure the deprecation pointed to the external function calling this 
test function
+    assert len(record) == 1
+    assert record[0].filename == caller_path
+
+    with pytest.deprecated_call() as record:
+        deprecation('Deprecated call', ['ipywidgets/widgets/tests'])
+    # Make sure the deprecation pointed to the external function calling this 
test function
+    assert len(record) == 1
+    assert record[0].filename == caller_path
+
+    with pytest.deprecated_call() as record:
+        deprecation('Deprecated call', 'ipywidgets/widgets/tests')
+    # Make sure the deprecation pointed to the external function calling this 
test function
+    assert len(record) == 1
+    assert record[0].filename == caller_path
+
+    with pytest.deprecated_call() as record:
+        deprecation('Deprecated call', [])
+    # Make sure the deprecation pointed to *this* file
+    assert len(record) == 1
+    assert record[0].filename == __file__
+
+def test_deprecation_indirect():
+    # If the line that calls "deprecation" is not internal, it is considered 
the source:
+    with pytest.warns(DeprecationWarning) as record:
+        call_method(deprecation, "test message", [])
+    assert len(record) == 1
+    assert record[0].filename == CALL_PATH
+
+def test_deprecation_indirect_internal():
+    # If the line that calls "deprecation" is internal, it is not considered 
the source:
+    with pytest.warns(DeprecationWarning) as record:
+        call_method(deprecation, "test message", [CALL_PATH])
+    assert len(record) == 1
+    assert record[0].filename == __file__
+
+def test_deprecation_nested1():
+    def level1():
+        deprecation("test message", [])
+
+    with pytest.warns(DeprecationWarning) as record:
+        call_method(level1)
+
+    assert len(record) == 1
+    assert record[0].filename == __file__
+
+def test_deprecation_nested2():
+    def level2():
+        deprecation("test message", [])
+    def level1():
+        level2()
+
+    with pytest.warns(DeprecationWarning) as record:
+        call_method(level1)
+
+    assert len(record) == 1
+    assert record[0].filename == __file__
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ipywidgets-8.0.2/ipywidgets/widgets/tests/test_widget.py 
new/ipywidgets-8.0.4/ipywidgets/widgets/tests/test_widget.py
--- old/ipywidgets-8.0.2/ipywidgets/widgets/tests/test_widget.py        
2022-09-02 20:27:45.000000000 +0200
+++ new/ipywidgets-8.0.4/ipywidgets/widgets/tests/test_widget.py        
2022-12-22 09:55:44.000000000 +0100
@@ -3,10 +3,14 @@
 
 """Test Widget."""
 
+import inspect
+
+import pytest
 from IPython.core.interactiveshell import InteractiveShell
 from IPython.display import display
 from IPython.utils.capture import capture_output
 
+from .. import widget
 from ..widget import Widget
 from ..widget_button import Button
 
@@ -22,10 +26,12 @@
 
     assert len(cap.outputs) == 1, "expect 1 output"
     mime_bundle = cap.outputs[0].data
-    assert mime_bundle['text/plain'] == repr(w), "expected plain text output"
-    assert 'application/vnd.jupyter.widget-view+json' not in mime_bundle, 
"widget has no view"
-    assert cap.stdout == '', repr(cap.stdout)
-    assert cap.stderr == '', repr(cap.stderr)
+    assert mime_bundle["text/plain"] == repr(w), "expected plain text output"
+    assert (
+        "application/vnd.jupyter.widget-view+json" not in mime_bundle
+    ), "widget has no view"
+    assert cap.stdout == "", repr(cap.stdout)
+    assert cap.stderr == "", repr(cap.stderr)
 
 
 def test_widget_view():
@@ -39,19 +45,38 @@
 
     assert len(cap.outputs) == 1, "expect 1 output"
     mime_bundle = cap.outputs[0].data
-    assert mime_bundle['text/plain'] == repr(w), "expected plain text output"
-    assert 'application/vnd.jupyter.widget-view+json' in mime_bundle, "widget 
should have have a view"
-    assert cap.stdout == '', repr(cap.stdout)
-    assert cap.stderr == '', repr(cap.stderr)
+    assert mime_bundle["text/plain"] == repr(w), "expected plain text output"
+    assert (
+        "application/vnd.jupyter.widget-view+json" in mime_bundle
+    ), "widget should have have a view"
+    assert cap.stdout == "", repr(cap.stdout)
+    assert cap.stderr == "", repr(cap.stderr)
 
 
 def test_close_all():
     # create a couple of widgets
     widgets = [Button() for i in range(10)]
 
-    assert len(Widget._active_widgets) > 0, "expect active widgets"
-
+    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._active_widgets) == 0, "active widgets should be cleared"
+    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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ipywidgets-8.0.2/ipywidgets/widgets/tests/test_widget_button.py 
new/ipywidgets-8.0.4/ipywidgets/widgets/tests/test_widget_button.py
--- old/ipywidgets-8.0.2/ipywidgets/widgets/tests/test_widget_button.py 
1970-01-01 01:00:00.000000000 +0100
+++ new/ipywidgets-8.0.4/ipywidgets/widgets/tests/test_widget_button.py 
2022-12-22 09:55:44.000000000 +0100
@@ -0,0 +1,12 @@
+# Copyright (c) Jupyter Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+import inspect
+import pytest
+from ipywidgets import Button
+
+def test_deprecation_fa_icons():
+    with pytest.deprecated_call() as record:
+        Button(icon='fa-home')
+    assert len(record) == 1
+    assert record[0].filename == inspect.stack(context=0)[1].filename
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ipywidgets-8.0.2/ipywidgets/widgets/tests/test_widget_string.py 
new/ipywidgets-8.0.4/ipywidgets/widgets/tests/test_widget_string.py
--- old/ipywidgets-8.0.2/ipywidgets/widgets/tests/test_widget_string.py 
2022-09-02 20:27:45.000000000 +0200
+++ new/ipywidgets-8.0.4/ipywidgets/widgets/tests/test_widget_string.py 
2022-12-22 09:55:44.000000000 +0100
@@ -1,8 +1,10 @@
 # Copyright (c) Jupyter Development Team.
 # Distributed under the terms of the Modified BSD License.
 
-from ..widget_string import Combobox
+import inspect
+import pytest
 
+from ..widget_string import Combobox, Text
 
 def test_combobox_creation_blank():
     w = Combobox()
@@ -32,3 +34,32 @@
             "Vanilla",
         )
     assert w.ensure_option == True
+
+def test_tooltip_deprecation():
+    caller_path = inspect.stack(context=0)[1].filename
+    with pytest.deprecated_call() as record:
+        w = Text(description_tooltip="testing")
+    assert len(record) == 1
+    assert record[0].filename == caller_path
+
+    with pytest.deprecated_call() as record:
+        w.description_tooltip
+    assert len(record) == 1
+    assert record[0].filename == caller_path
+
+    with pytest.deprecated_call() as record:
+        w.description_tooltip == "testing"
+    assert len(record) == 1
+    assert record[0].filename == caller_path
+
+    with pytest.deprecated_call() as record:
+        w.description_tooltip = "second value"
+    assert len(record) == 1
+    assert record[0].filename == caller_path
+    assert w.tooltip == "second value"
+
+def test_on_submit_deprecation():
+    with pytest.deprecated_call() as record:
+        Text().on_submit(lambda *args: ...)
+    assert len(record) == 1
+    assert record[0].filename == inspect.stack(context=0)[1].filename
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ipywidgets-8.0.2/ipywidgets/widgets/tests/utils.py 
new/ipywidgets-8.0.4/ipywidgets/widgets/tests/utils.py
--- old/ipywidgets-8.0.2/ipywidgets/widgets/tests/utils.py      2022-09-02 
20:27:45.000000000 +0200
+++ new/ipywidgets-8.0.4/ipywidgets/widgets/tests/utils.py      2022-12-22 
09:55:45.000000000 +0100
@@ -3,6 +3,7 @@
 
 from ipykernel.comm import Comm
 from ipywidgets import Widget
+import ipywidgets.widgets.widget
 
 class DummyComm(Comm):
     comm_id = 'a-b-c-d'
@@ -25,14 +26,16 @@
 undefined = object()
 
 def setup_test_comm():
-    _widget_attrs['_comm_default'] = getattr(Widget, '_comm_default', 
undefined)
-    Widget._comm_default = lambda self: DummyComm()
+    Widget.comm.klass = DummyComm
+    ipywidgets.widgets.widget.Comm = DummyComm
     _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():
+    Widget.comm.klass = Comm
+    ipywidgets.widgets.widget.Comm = Comm
     for attr, value in _widget_attrs.items():
         if value is undefined:
             delattr(Widget, attr)
@@ -45,3 +48,6 @@
 
 def teardown():
     teardown_test_comm()
+
+def call_method(method, *args, **kwargs):
+    method(*args, **kwargs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ipywidgets-8.0.2/ipywidgets/widgets/utils.py 
new/ipywidgets-8.0.4/ipywidgets/widgets/utils.py
--- old/ipywidgets-8.0.2/ipywidgets/widgets/utils.py    1970-01-01 
01:00:00.000000000 +0100
+++ new/ipywidgets-8.0.4/ipywidgets/widgets/utils.py    2022-12-22 
09:55:45.000000000 +0100
@@ -0,0 +1,64 @@
+# Copyright (c) Jupyter Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+from pathlib import Path
+import sys
+import inspect
+import warnings
+
+def _get_frame(level):
+    """Get the frame at the given stack level."""
+    # sys._getframe is much faster than inspect.stack, but isn't guaranteed to
+    # exist in all python implementations, so we fall back to inspect.stack()
+
+    # We need to add one to level to account for this get_frame call.
+    if hasattr(sys, '_getframe'):
+        frame = sys._getframe(level+1)
+    else:
+        frame = inspect.stack(context=0)[level+1].frame
+    return frame
+
+
+# This function is from https://github.com/python/cpython/issues/67998
+# (https://bugs.python.org/file39550/deprecated_module_stacklevel.diff) and
+# calculates the appropriate stacklevel for deprecations to target the
+# deprecation for the caller, no matter how many internal stack frames we have
+# added in the process. For example, with the deprecation warning in the
+# __init__ below, the appropriate stacklevel will change depending on how deep
+# the inheritance hierarchy is.
+def _external_stacklevel(internal):
+    """Find the stacklevel of the first frame that doesn't contain any of the 
given internal strings
+
+    The depth will be 1 at minimum in order to start checking at the caller of
+    the function that called this utility method.
+    """
+    # Get the level of my caller's caller
+    level = 2
+    frame = _get_frame(level)
+
+    # Normalize the path separators:
+    normalized_internal = [str(Path(s)) for s in internal]
+
+    # climb the stack frames while we see internal frames
+    while frame and any(s in str(Path(frame.f_code.co_filename)) for s in 
normalized_internal):
+        level +=1
+        frame = frame.f_back
+
+    # Return the stack level from the perspective of whoever called us (i.e., 
one level up)
+    return level-1
+
+def deprecation(message, internal='ipywidgets/widgets/'):
+    """Generate a deprecation warning targeting the first frame that is not 
'internal'
+    
+    internal is a string or list of strings, which if they appear in filenames 
in the
+    frames, the frames will be considered internal. Changing this can be 
useful if, for examnple,
+    we know that ipywidgets is calling out to traitlets internally.
+    """
+    if isinstance(internal, str):
+        internal = [internal]
+
+    # stack level of the first external frame from here
+    stacklevel = _external_stacklevel(internal)
+
+    # The call to .warn adds one frame, so bump the stacklevel up by one
+    warnings.warn(message, DeprecationWarning, stacklevel=stacklevel+1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ipywidgets-8.0.2/ipywidgets/widgets/widget.py 
new/ipywidgets-8.0.4/ipywidgets/widgets/widget.py
--- old/ipywidgets-8.0.2/ipywidgets/widgets/widget.py   2022-09-02 
20:27:45.000000000 +0200
+++ new/ipywidgets-8.0.4/ipywidgets/widgets/widget.py   2022-12-22 
09:55:45.000000000 +0100
@@ -6,6 +6,7 @@
 in the Jupyter notebook front-end.
 """
 import os
+import typing
 from contextlib import contextmanager
 from collections.abc import Iterable
 from IPython import get_ipython
@@ -17,8 +18,13 @@
 
 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
+TRAITLETS_FILE = inspect.getfile(HasTraits)
+
 # Based on jupyter_core.paths.envset
 def envset(name, default):
     """Return True if the given environment variable is turned on, otherwise 
False
@@ -34,6 +40,9 @@
 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:
+#  https://github.com/jupyter-widgets/ipywidgets/issues/1345
+_instances : typing.MutableMapping[str, "Widget"] = {}
 
 def _widget_to_json(x, obj):
     if isinstance(x, dict):
@@ -50,8 +59,8 @@
         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 
Widget._active_widgets:
-        return Widget._active_widgets[x[10:]]
+    elif isinstance(x, str) and x.startswith('IPY_MODEL_') and x[10:] in 
_instances:
+        return _instances[x[10:]]
     else:
         return x
 
@@ -259,10 +268,16 @@
                             for view_name, widget in sorted(vn.items()):
                                     yield (model_module, model_version, 
model_name, view_module, view_version, view_name), widget
 
+
+
+# a registry of widgets by module, version, and name so we can create a Python 
model from widgets
+# that are constructed from the frontend.
+_registry = WidgetRegistry()
+
 def register(widget):
     """A decorator registering a widget class in the widget registry."""
     w = widget.class_traits()
-    Widget._widget_types.register(w['_model_module'].default_value,
+    _registry.register(w['_model_module'].default_value,
                                  w['_model_module_version'].default_value,
                                  w['_model_name'].default_value,
                                  w['_view_module'].default_value,
@@ -272,6 +287,16 @@
     return widget
 
 
+class _staticproperty(object):
+    def __init__(self, fget):
+        self.fget = fget
+
+    def __get__(self, owner_self, owner_cls):
+        assert owner_self is None
+        return self.fget()
+
+
+
 class Widget(LoggingHasTraits):
     #-------------------------------------------------------------------------
     # Class attributes
@@ -279,15 +304,49 @@
     _widget_construction_callback = None
     _control_comm = None
 
-    # _active_widgets is a dictionary of all active widget objects
-    _active_widgets = {}
-
-    # _widget_types is a registry of widgets by module, version, and name:
-    _widget_types = WidgetRegistry()
+    @_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
 
     @classmethod
     def close_all(cls):
-        for widget in list(cls._active_widgets.values()):
+        for widget in list(_instances.values()):
             widget.close()
 
     @staticmethod
@@ -329,7 +388,7 @@
         if method == 'request_states':
             # Send back the full widgets state
             cls.get_manager_state()
-            widgets = cls._active_widgets.values()
+            widgets = _instances.values()
             full_state = {}
             drop_defaults = False
             for widget in widgets:
@@ -359,7 +418,7 @@
         state = data['state']
 
         # Find the widget class to instantiate in the registered widgets
-        widget_class = Widget._widget_types.get(state['_model_module'],
+        widget_class = _registry.get(state['_model_module'],
                                                state['_model_module_version'],
                                                state['_model_name'],
                                                state['_view_module'],
@@ -380,7 +439,7 @@
         """
         state = {}
         if widgets is None:
-            widgets = Widget._active_widgets.values()
+            widgets = _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}
@@ -476,7 +535,7 @@
         self._model_id = self.model_id
 
         self.comm.on_msg(self._handle_msg)
-        Widget._active_widgets[self.model_id] = self
+        _instances[self.model_id] = self
 
     @property
     def model_id(self):
@@ -496,7 +555,7 @@
         When the comm is closed, all of the widget views are automatically
         removed from the front-end."""
         if self.comm is not None:
-            Widget._active_widgets.pop(self.model_id, None)
+            _instances.pop(self.model_id, None)
             self.comm.close()
             self.comm = None
             self._repr_mimebundle_ = None
@@ -581,7 +640,7 @@
         # The order of these context managers is important. Properties must
         # be locked when the hold_trait_notification context manager is
         # released and notifications are fired.
-        with self.hold_sync(), self._lock_property(**sync_data), 
self.hold_trait_notifications():
+        with self._lock_property(**sync_data), self.hold_trait_notifications():
             for name in sync_data:
                 if name in self.keys:
                     from_json = self.trait_metadata(name, 'from_json',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ipywidgets-8.0.2/ipywidgets/widgets/widget_button.py 
new/ipywidgets-8.0.4/ipywidgets/widgets/widget_button.py
--- old/ipywidgets-8.0.2/ipywidgets/widgets/widget_button.py    2022-09-02 
20:27:45.000000000 +0200
+++ new/ipywidgets-8.0.4/ipywidgets/widgets/widget_button.py    2022-12-22 
09:55:45.000000000 +0100
@@ -7,6 +7,7 @@
 click events on the button and trigger backend code when the clicks are fired.
 """
 
+from .utils import deprecation
 from .domwidget import DOMWidget
 from .widget import CallbackDispatcher, register, widget_serialization
 from .widget_core import CoreWidget
@@ -14,7 +15,6 @@
 from .trait_types import Color, InstanceDict
 
 from traitlets import Unicode, Bool, CaselessStrEnum, Instance, validate, 
default
-import warnings
 
 
 @register
@@ -70,8 +70,9 @@
         """Strip 'fa-' if necessary'"""
         value = proposal['value']
         if 'fa-' in value:
-            warnings.warn("icons names no longer need 'fa-', "
-            "just use the class names themselves (for example, 'gear spin' 
instead of 'fa-gear fa-spin')", DeprecationWarning)
+            deprecation("icons names no longer need 'fa-', "
+            "just use the class names themselves (for example, 'gear spin' 
instead of 'fa-gear fa-spin')",
+            internal=['ipywidgets/widgets/', 'traitlets/traitlets.py', 
'/contextlib.py'])
             value = value.replace('fa-', '')
         return value
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ipywidgets-8.0.2/ipywidgets/widgets/widget_description.py 
new/ipywidgets-8.0.4/ipywidgets/widgets/widget_description.py
--- old/ipywidgets-8.0.2/ipywidgets/widgets/widget_description.py       
2022-09-02 20:27:45.000000000 +0200
+++ new/ipywidgets-8.0.4/ipywidgets/widgets/widget_description.py       
2022-12-22 09:55:45.000000000 +0100
@@ -9,6 +9,8 @@
 from .widget_style import Style
 from .widget_core import CoreWidget
 from .domwidget import DOMWidget
+from .utils import deprecation
+
 import warnings
 
 @register
@@ -27,7 +29,7 @@
 
     def __init__(self, *args, **kwargs):
         if 'description_tooltip' in kwargs:
-            warnings.warn("the description_tooltip argument is deprecated, use 
tooltip instead", DeprecationWarning)
+            deprecation("the description_tooltip argument is deprecated, use 
tooltip instead")
             kwargs.setdefault('tooltip', kwargs['description_tooltip'])
             del kwargs['description_tooltip']
         super().__init__(*args, **kwargs)
@@ -47,10 +49,10 @@
         .. deprecated :: 8.0.0
            Use tooltip attribute instead.
         """
-        warnings.warn(".description_tooltip is deprecated, use .tooltip 
instead", DeprecationWarning)
+        deprecation(".description_tooltip is deprecated, use .tooltip instead")
         return self.tooltip
 
     @description_tooltip.setter
     def description_tooltip(self, tooltip):
-        warnings.warn(".description_tooltip is deprecated, use .tooltip 
instead", DeprecationWarning)
+        deprecation(".description_tooltip is deprecated, use .tooltip instead")
         self.tooltip = tooltip
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ipywidgets-8.0.2/ipywidgets/widgets/widget_string.py 
new/ipywidgets-8.0.4/ipywidgets/widgets/widget_string.py
--- old/ipywidgets-8.0.2/ipywidgets/widgets/widget_string.py    2022-09-02 
20:27:45.000000000 +0200
+++ new/ipywidgets-8.0.4/ipywidgets/widgets/widget_string.py    2022-12-22 
09:55:45.000000000 +0100
@@ -11,8 +11,8 @@
 from .widget import CallbackDispatcher, register, widget_serialization
 from .widget_core import CoreWidget
 from .trait_types import Color, InstanceDict, TypedTuple
+from .utils import deprecation
 from traitlets import Unicode, Bool, Int
-from warnings import warn
 
 
 class _StringStyle(DescriptionStyle, CoreWidget):
@@ -142,8 +142,7 @@
         remove: bool (optional)
             Whether to unregister the callback
         """
-        import warnings
-        warnings.warn("on_submit is deprecated. Instead, set the 
.continuous_update attribute to False and observe the value changing with: 
mywidget.observe(callback, 'value').", DeprecationWarning)
+        deprecation("on_submit is deprecated. Instead, set the 
.continuous_update attribute to False and observe the value changing with: 
mywidget.observe(callback, 'value').")
         self._submission_callbacks.register_callback(callback, remove=remove)
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ipywidgets-8.0.2/ipywidgets.egg-info/PKG-INFO 
new/ipywidgets-8.0.4/ipywidgets.egg-info/PKG-INFO
--- old/ipywidgets-8.0.2/ipywidgets.egg-info/PKG-INFO   2022-09-02 
20:40:07.000000000 +0200
+++ new/ipywidgets-8.0.4/ipywidgets.egg-info/PKG-INFO   2022-12-22 
10:14:32.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: ipywidgets
-Version: 8.0.2
+Version: 8.0.4
 Summary: Jupyter interactive widgets
 Home-page: http://jupyter.org
 Author: Jupyter Development Team
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ipywidgets-8.0.2/ipywidgets.egg-info/SOURCES.txt 
new/ipywidgets-8.0.4/ipywidgets.egg-info/SOURCES.txt
--- old/ipywidgets-8.0.2/ipywidgets.egg-info/SOURCES.txt        2022-09-02 
20:40:07.000000000 +0200
+++ new/ipywidgets-8.0.4/ipywidgets.egg-info/SOURCES.txt        2022-12-22 
10:14:32.000000000 +0100
@@ -22,6 +22,7 @@
 ipywidgets/widgets/domwidget.py
 ipywidgets/widgets/interaction.py
 ipywidgets/widgets/trait_types.py
+ipywidgets/widgets/utils.py
 ipywidgets/widgets/valuewidget.py
 ipywidgets/widgets/widget.py
 ipywidgets/widgets/widget_bool.py
@@ -56,8 +57,10 @@
 ipywidgets/widgets/tests/test_send_state.py
 ipywidgets/widgets/tests/test_set_state.py
 ipywidgets/widgets/tests/test_traits.py
+ipywidgets/widgets/tests/test_utils.py
 ipywidgets/widgets/tests/test_widget.py
 ipywidgets/widgets/tests/test_widget_box.py
+ipywidgets/widgets/tests/test_widget_button.py
 ipywidgets/widgets/tests/test_widget_datetime.py
 ipywidgets/widgets/tests/test_widget_float.py
 ipywidgets/widgets/tests/test_widget_image.py

Reply via email to