66in the Jupyter notebook front-end.
77"""
88import os
9+ import typing
910from contextlib import contextmanager
1011from collections .abc import Iterable
12+ import warnings
1113from IPython import get_ipython
1214from ipykernel .comm import Comm
1315from traitlets import (
@@ -34,6 +36,9 @@ def envset(name, default):
3436PROTOCOL_VERSION_MAJOR = __protocol_version__ .split ('.' )[0 ]
3537CONTROL_PROTOCOL_VERSION_MAJOR = __control_protocol_version__ .split ('.' )[0 ]
3638JUPYTER_WIDGETS_ECHO = envset ('JUPYTER_WIDGETS_ECHO' , default = True )
39+ # we keep a strong reference for every widget created, for a discussion on using weak references see:
40+ # https://github.com/jupyter-widgets/ipywidgets/issues/1345
41+ instances : typing .MutableMapping [str , "Widget" ] = {}
3742
3843def _widget_to_json (x , obj ):
3944 if isinstance (x , dict ):
@@ -50,8 +55,8 @@ def _json_to_widget(x, obj):
5055 return {k : _json_to_widget (v , obj ) for k , v in x .items ()}
5156 elif isinstance (x , (list , tuple )):
5257 return [_json_to_widget (v , obj ) for v in x ]
53- elif isinstance (x , str ) and x .startswith ('IPY_MODEL_' ) and x [10 :] in Widget . _active_widgets :
54- return Widget . _active_widgets [x [10 :]]
58+ elif isinstance (x , str ) and x .startswith ('IPY_MODEL_' ) and x [10 :] in instances :
59+ return instances [x [10 :]]
5560 else :
5661 return x
5762
@@ -259,10 +264,16 @@ def items(self):
259264 for view_name , widget in sorted (vn .items ()):
260265 yield (model_module , model_version , model_name , view_module , view_version , view_name ), widget
261266
267+
268+
269+ # a registry of widgets by module, version, and name so we can create a Python model from widgets
270+ # that are constructed from the frontend.
271+ registry = WidgetRegistry ()
272+
262273def register (widget ):
263274 """A decorator registering a widget class in the widget registry."""
264275 w = widget .class_traits ()
265- Widget . _widget_types .register (w ['_model_module' ].default_value ,
276+ registry .register (w ['_model_module' ].default_value ,
266277 w ['_model_module_version' ].default_value ,
267278 w ['_model_name' ].default_value ,
268279 w ['_view_module' ].default_value ,
@@ -272,22 +283,46 @@ def register(widget):
272283 return widget
273284
274285
286+ class _staticproperty (object ):
287+ def __init__ (self , fget ):
288+ self .fget = fget
289+
290+ def __get__ (self , owner_self , owner_cls ):
291+ assert owner_self is None
292+ return self .fget ()
293+
294+
295+
275296class Widget (LoggingHasTraits ):
276297 #-------------------------------------------------------------------------
277298 # Class attributes
278299 #-------------------------------------------------------------------------
279300 _widget_construction_callback = None
280301 _control_comm = None
281302
282- # _active_widgets is a dictionary of all active widget objects
283- _active_widgets = {}
303+ @_staticproperty
304+ def widgets ():
305+ warnings .warn ("Widget.widgets is deprecated, use ipywidgets.widgets.widget.instances" , DeprecationWarning )
306+ return instances
307+
308+ @_staticproperty
309+ def _active_widgets ():
310+ warnings .warn ("Widget._active_widgets is deprecated, use ipywidgets.widgets.widget.instances" , DeprecationWarning )
311+ return instances
312+
313+ @_staticproperty
314+ def _widget_types ():
315+ warnings .warn ("Widget._widget_types is deprecated, use ipywidgets.widgets.widget.register" , DeprecationWarning )
316+ return registry
284317
285- # _widget_types is a registry of widgets by module, version, and name:
286- _widget_types = WidgetRegistry ()
318+ @_staticproperty
319+ def widget_types ():
320+ warnings .warn ("Widget.widget_types is deprecated, use ipywidgets.widgets.widget.register" , DeprecationWarning )
321+ return registry
287322
288323 @classmethod
289324 def close_all (cls ):
290- for widget in list (cls . _active_widgets .values ()):
325+ for widget in list (instances .values ()):
291326 widget .close ()
292327
293328 @staticmethod
@@ -329,7 +364,7 @@ def _handle_control_comm_msg(cls, msg):
329364 if method == 'request_states' :
330365 # Send back the full widgets state
331366 cls .get_manager_state ()
332- widgets = cls . _active_widgets .values ()
367+ widgets = instances .values ()
333368 full_state = {}
334369 drop_defaults = False
335370 for widget in widgets :
@@ -359,7 +394,7 @@ def handle_comm_opened(comm, msg):
359394 state = data ['state' ]
360395
361396 # Find the widget class to instantiate in the registered widgets
362- widget_class = Widget . _widget_types .get (state ['_model_module' ],
397+ widget_class = register .get (state ['_model_module' ],
363398 state ['_model_module_version' ],
364399 state ['_model_name' ],
365400 state ['_view_module' ],
@@ -380,7 +415,7 @@ def get_manager_state(drop_defaults=False, widgets=None):
380415 """
381416 state = {}
382417 if widgets is None :
383- widgets = Widget . _active_widgets .values ()
418+ widgets = instances .values ()
384419 for widget in widgets :
385420 state [widget .model_id ] = widget ._get_embed_state (drop_defaults = drop_defaults )
386421 return {'version_major' : 2 , 'version_minor' : 0 , 'state' : state }
@@ -476,7 +511,7 @@ def _comm_changed(self, change):
476511 self ._model_id = self .model_id
477512
478513 self .comm .on_msg (self ._handle_msg )
479- Widget . _active_widgets [self .model_id ] = self
514+ instances [self .model_id ] = self
480515
481516 @property
482517 def model_id (self ):
@@ -496,7 +531,7 @@ def close(self):
496531 When the comm is closed, all of the widget views are automatically
497532 removed from the front-end."""
498533 if self .comm is not None :
499- Widget . _active_widgets .pop (self .model_id , None )
534+ instances .pop (self .model_id , None )
500535 self .comm .close ()
501536 self .comm = None
502537 self ._repr_mimebundle_ = None
0 commit comments