Skip to content

Start to unify the comm_open messages in the spec. #1228

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Apr 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ipywidgets/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
version_info = (7, 0, 0, 'dev0')
__version__ = '.'.join(map(str, version_info))
__protocol_version__ = '2.0'
__jupyter_widget_version__ = '3.0.0'
__jupyter_widget_version__ = '3'
1 change: 1 addition & 0 deletions ipywidgets/widgets/tests/test_interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,7 @@ def test_get_interact_value():
from ipywidgets.widgets import ValueWidget
from traitlets import Unicode
class TheAnswer(ValueWidget):
_model_module = Unicode('jupyter-js-widgets')
description = Unicode()
def get_interact_value(self):
return 42
Expand Down
89 changes: 65 additions & 24 deletions ipywidgets/widgets/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,27 +172,60 @@ def m(self, *args, **kwargs):
return m


def register(key=None):
"""Returns a decorator registering a widget class in the widget registry.

If no key is provided, the class name is used as a key.
A key is provided for each core Jupyter widget so that the frontend can use
this key regardless of the language of the kernel.
"""
def wrap(widget):
l = key if key is not None else widget.__module__ + widget.__name__
Widget.widget_types[l] = widget
return widget
return wrap

class WidgetRegistry(object):

def __init__(self):
self._registry = {}

def register(self, model_module, model_module_version_range, model_name, view_module, view_module_version_range, view_name, klass):
"""Register a value"""
model_module = self._registry.setdefault(model_module, {})
model_version = model_module.setdefault(model_module_version_range, {})
model_name = model_version.setdefault(model_name, {})
view_module = model_name.setdefault(view_module, {})
view_version = view_module.setdefault(view_module_version_range, {})
view_version[view_name] = klass

def get(self, model_module, model_module_version, model_name, view_module, view_module_version, view_name):
"""Get a value"""
module_versions = self._registry[model_module]
# The python semver module doesn't work well, for example, it can't do match('3', '*')
# so we just take the first model module version.
#model_names = next(v for k, v in module_versions.items()
# if semver.match(model_module_version, k))
model_names = list(module_versions.values())[0]
view_modules = model_names[model_name]
view_versions = view_modules[view_module]
# The python semver module doesn't work well, so we just take the first view module version
#view_names = next(v for k, v in view_versions.items()
# if semver.match(view_module_version, k))
view_names = list(view_versions.values())[0]
widget_class = view_names[view_name]
return widget_class

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,
w['_model_module_version'].default_value,
w['_model_name'].default_value,
w['_view_module'].default_value,
w['_view_module_version'].default_value,
w['_view_name'].default_value,
widget)
return widget

class Widget(LoggingConfigurable):
#-------------------------------------------------------------------------
# Class attributes
#-------------------------------------------------------------------------
_widget_construction_callback = None

# widgets is a dictionary of all active widget objects
widgets = {}
widget_types = {}

# widget_types is a registry of widgets by module, version, and name:
widget_types = WidgetRegistry()

@staticmethod
def on_widget_constructed(callback):
Expand All @@ -211,12 +244,20 @@ def _call_widget_constructed(widget):
@staticmethod
def handle_comm_opened(comm, msg):
"""Static method, called when a widget is constructed."""
class_name = str(msg['content']['data']['widget_class'])
if class_name in Widget.widget_types:
widget_class = Widget.widget_types[class_name]
else:
widget_class = import_item(class_name)
data = msg['content']['data']
state = data['state']

# Find the widget class to instantiate in the registered widgets
widget_class = Widget.widget_types.get(state['_model_module'],
state['_model_module_version'],
state['_model_name'],
state['_view_module'],
state['_view_module_version'],
state['_view_name'])
widget = widget_class(comm=comm)
if 'buffer_paths' in data:
_put_buffers(state, data['buffer_paths'], msg['buffers'])
widget.set_state(state)

@staticmethod
def get_manager_state(drop_defaults=False):
Expand All @@ -235,18 +276,18 @@ def get_view_spec(self):
#-------------------------------------------------------------------------
# Traits
#-------------------------------------------------------------------------
_model_module = Unicode('jupyter-js-widgets',
help="A JavaScript module name in which to find _model_name.").tag(sync=True)
_model_module = Unicode(None,
help="A JavaScript module name in which to find _model_name.", read_only=True).tag(sync=True)
_model_name = Unicode('WidgetModel',
help="Name of the model object in the front-end.").tag(sync=True)
help="Name of the model.", read_only=True).tag(sync=True)
_model_module_version = Unicode('*',
help="A semver requirement for the model module version.").tag(sync=True)
help="A semver requirement for the model module version.", read_only=True).tag(sync=True)
_view_module = Unicode(None, allow_none=True,
help="A JavaScript module in which to find _view_name.").tag(sync=True)
_view_name = Unicode(None, allow_none=True,
help="Name of the view object.").tag(sync=True)
_view_module_version = Unicode('*',
help="A semver requirement for the view module version.").tag(sync=True)
help="A semver requirement for the view module.").tag(sync=True)
comm = Instance('ipykernel.comm.Comm', allow_none=True)

msg_throttle = Int(1, help="""Maximum number of msgs the front-end can send before receiving an idle msg from the back-end.""").tag(sync=True)
Expand Down
6 changes: 3 additions & 3 deletions ipywidgets/widgets/widget_bool.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def __init__(self, value=None, **kwargs):
_model_name = Unicode('BoolModel').tag(sync=True)


@register('Jupyter.Checkbox')
@register
class Checkbox(_Bool):
"""Displays a boolean `value` in the form of a checkbox.

Expand All @@ -43,7 +43,7 @@ class Checkbox(_Bool):
_model_name = Unicode('CheckboxModel').tag(sync=True)


@register('Jupyter.ToggleButton')
@register
class ToggleButton(_Bool):
"""Displays a boolean `value` in the form of a toggle button.

Expand All @@ -69,7 +69,7 @@ class ToggleButton(_Bool):
help="""Use a predefined styling for the button.""").tag(sync=True)


@register('Jupyter.Valid')
@register
class Valid(_Bool):
"""Displays a boolean `value` in the form of a green check (True / valid)
or a red cross (False / invalid).
Expand Down
8 changes: 4 additions & 4 deletions ipywidgets/widgets/widget_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from warnings import warn


@register('Jupyter.Box')
@register
class Box(DOMWidget, CoreWidget):
"""Displays multiple widgets in a group."""
_model_module = Unicode('jupyter-js-widgets').tag(sync=True)
Expand All @@ -25,7 +25,7 @@ class Box(DOMWidget, CoreWidget):
# Child widgets in the container.
# Using a tuple here to force reassignment to update the list.
# When a proper notifying-list trait exists, that is what should be used here.
children = Tuple().tag(sync=True, help="List of widget children", **widget_serialization)
children = Tuple(help="List of widget children").tag(sync=True, **widget_serialization)

box_style = CaselessStrEnum(
values=['success', 'info', 'warning', 'danger', ''], default_value='',
Expand All @@ -41,14 +41,14 @@ def _fire_children_displayed(self):
child._handle_displayed()


@register('Jupyter.VBox')
@register
class VBox(Box):
"""Displays multiple widgets vertically using the flexible box model."""
_model_name = Unicode('VBoxModel').tag(sync=True)
_view_name = Unicode('VBoxView').tag(sync=True)


@register('Jupyter.HBox')
@register
class HBox(Box):
"""Displays multiple widgets horizontally using the flexible box model."""
_model_name = Unicode('HBoxModel').tag(sync=True)
Expand Down
4 changes: 2 additions & 2 deletions ipywidgets/widgets/widget_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
import warnings


@register('Jupyter.ButtonStyle')
@register
class ButtonStyle(Style, CoreWidget):
"""Button style widget."""
_model_name = Unicode('ButtonStyleModel').tag(sync=True)
button_color = Color(None, allow_none=True, help="Color of the button").tag(sync=True)
font_weight = Unicode().tag(sync=True)


@register('Jupyter.Button')
@register
class Button(DOMWidget, CoreWidget):
"""Button widget.

Expand Down
2 changes: 1 addition & 1 deletion ipywidgets/widgets/widget_color.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from traitlets import Unicode, Bool


@register('Jupyter.ColorPicker')
@register
class ColorPicker(LabeledWidget, ValueWidget, CoreWidget):
value = Color('black').tag(sync=True)
concise = Bool().tag(sync=True)
Expand Down
6 changes: 3 additions & 3 deletions ipywidgets/widgets/widget_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from traitlets import Bool, Int, Float, Unicode, List, Instance


@register('Jupyter.ControllerButton')
@register
class Button(ValueWidget, CoreWidget):
"""Represents a gamepad or joystick button."""
value = Float(min=0.0, max=1.0, read_only=True).tag(sync=True)
Expand All @@ -25,7 +25,7 @@ class Button(ValueWidget, CoreWidget):
_model_name = Unicode('ControllerButtonModel').tag(sync=True)


@register('Jupyter.ControllerAxis')
@register
class Axis(ValueWidget, CoreWidget):
"""Represents a gamepad or joystick axis."""
value = Float(min=-1.0, max=1.0, read_only=True).tag(sync=True)
Expand All @@ -36,7 +36,7 @@ class Axis(ValueWidget, CoreWidget):
_model_name = Unicode('ControllerAxisModel').tag(sync=True)


@register('Jupyter.Controller')
@register
class Controller(DOMWidget, CoreWidget):
"""Represents a game controller."""
index = Int().tag(sync=True)
Expand Down
1 change: 0 additions & 1 deletion ipywidgets/widgets/widget_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,3 @@ class CoreWidget(Widget):

_model_module_version = Unicode(__jupyter_widget_version__).tag(sync=True)
_view_module_version = Unicode(__jupyter_widget_version__).tag(sync=True)

2 changes: 1 addition & 1 deletion ipywidgets/widgets/widget_date.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from traitlets import Unicode


@register('Jupyter.DatePicker')
@register
class DatePicker(LabeledWidget, ValueWidget, CoreWidget):
value = Datetime(None, allow_none=True).tag(sync=True, **datetime_serialization)

Expand Down
10 changes: 5 additions & 5 deletions ipywidgets/widgets/widget_float.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def _validate_max(self, proposal):
return max


@register('Jupyter.FloatText')
@register
class FloatText(_Float):
""" Displays a float value within a textbox. For a textbox in
which the value must be within a specific range, use BoundedFloatText.
Expand All @@ -82,7 +82,7 @@ class FloatText(_Float):
_model_name = Unicode('FloatTextModel').tag(sync=True)


@register('Jupyter.BoundedFloatText')
@register
class BoundedFloatText(_BoundedFloat):
""" Displays a float value within a textbox. Value must be within the range specified.

Expand All @@ -105,7 +105,7 @@ class BoundedFloatText(_BoundedFloat):
_model_name = Unicode('FloatTextModel').tag(sync=True)


@register('Jupyter.FloatSlider')
@register
class FloatSlider(_BoundedFloat):
""" Slider/trackbar of floating values with the specified range.

Expand Down Expand Up @@ -145,7 +145,7 @@ class FloatSlider(_BoundedFloat):
continuous_update = Bool(True, help="Update the value of the widget as the user is holding the slider.").tag(sync=True)


@register('Jupyter.FloatProgress')
@register
class FloatProgress(_BoundedFloat):
""" Displays a progress bar.

Expand Down Expand Up @@ -246,7 +246,7 @@ def _validate_value(self, proposal):
return lower, upper


@register('Jupyter.FloatRangeSlider')
@register
class FloatRangeSlider(_BoundedFloatRange):
""" Slider/trackbar that represents a pair of floats bounded by minimum and maximum value.

Expand Down
2 changes: 1 addition & 1 deletion ipywidgets/widgets/widget_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from traitlets import Unicode, CUnicode, Bytes, observe


@register('Jupyter.Image')
@register
class Image(DOMWidget, ValueWidget, CoreWidget):
"""Displays an image as a widget.

Expand Down
16 changes: 8 additions & 8 deletions ipywidgets/widgets/widget_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,15 @@ def _validate_max(self, proposal):
self.value = max
return max

@register('Jupyter.IntText')
@register
@_int_doc
class IntText(_Int):
"""Textbox widget that represents an integer."""
_view_name = Unicode('IntTextView').tag(sync=True)
_model_name = Unicode('IntTextModel').tag(sync=True)


@register('Jupyter.BoundedIntText')
@register
@_bounded_int_doc
class BoundedIntText(_BoundedInt):
"""Textbox widget that represents an integer bounded from above and below.
Expand All @@ -143,14 +143,14 @@ class BoundedIntText(_BoundedInt):
_model_name = Unicode('IntTextModel').tag(sync=True)


@register('Jupyter.SliderStyle')
@register
class SliderStyle(Style, CoreWidget):
"""Button style widget."""
_model_name = Unicode('SliderStyleModel').tag(sync=True)
handle_color = Color(None, allow_none=True).tag(sync=True)


@register('Jupyter.IntSlider')
@register
@_bounded_int_doc
class IntSlider(_BoundedInt):
"""Slider widget that represents an integer bounded from above and below.
Expand All @@ -171,14 +171,14 @@ def _default_style(self):
return SliderStyle()


@register('Jupyter.ProgressStyle')
@register
class ProgressStyle(Style, CoreWidget):
"""Button style widget."""
_model_name = Unicode('ProgressStyleModel').tag(sync=True)
bar_color = Color(None, allow_none=True).tag(sync=True)


@register('Jupyter.IntProgress')
@register
@_bounded_int_doc
class IntProgress(_BoundedInt):
"""Progress bar that represents an integer bounded from above and below.
Expand Down Expand Up @@ -261,7 +261,7 @@ def _validate_value(self, proposal):
return lower, upper


@register('Jupyter.IntRangeSlider')
@register
class IntRangeSlider(_BoundedIntRange):
"""Slider/trackbar that represents a pair of ints bounded by minimum and maximum value.

Expand All @@ -284,7 +284,7 @@ class IntRangeSlider(_BoundedIntRange):
continuous_update = Bool(True, help="Update the value of the widget as the user is sliding the slider.").tag(sync=True)


@register('Jupyter.Play')
@register
class Play(_BoundedInt):
interval = CInt(100).tag(sync=True)

Expand Down
Loading