Skip to content

Commit dfe34fc

Browse files
authored
Merge pull request #2723 from jasongrout/memoryview
Change media widgets to use memory views.
2 parents 4aed9c7 + c7aa0dd commit dfe34fc

File tree

5 files changed

+67
-39
lines changed

5 files changed

+67
-39
lines changed

ipywidgets/widgets/tests/test_widget_image.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,9 @@ def test_value_repr_length():
149149
with get_logo_png() as LOGO_PNG:
150150
with open(LOGO_PNG, 'rb') as f:
151151
img = Image.from_file(f)
152-
assert len(img.__repr__()) < 120
153-
assert img.__repr__().endswith("...')")
152+
assert len(img.__repr__()) < 140
153+
assert img.__repr__().endswith(")")
154+
assert img.__repr__()[-5:-2] == '...'
154155

155156

156157
def test_value_repr_url():

ipywidgets/widgets/trait_types.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,34 @@ def date_from_json(js, manager):
146146
'to_json': date_to_json
147147
}
148148

149+
class ByteMemoryView(traitlets.TraitType):
150+
"""A trait for memory views of bytes."""
151+
152+
default_value = memoryview(b'')
153+
info_text = 'a memory view object'
154+
155+
def validate(self, obj, value):
156+
if isinstance(value, memoryview) and value.format == 'B':
157+
return value
158+
self.error(obj, value)
159+
160+
def default_value_repr(self):
161+
return repr(self.default_value.tobytes())
162+
163+
class CByteMemoryView(ByteMemoryView):
164+
"""A casting version of the byte memory view trait."""
165+
166+
def validate(self, obj, value):
167+
if isinstance(value, memoryview) and value.format == 'B':
168+
return value
169+
170+
try:
171+
mv = memoryview(value)
172+
if mv.format != 'B':
173+
mv = mv.cast('B')
174+
return mv
175+
except Exception:
176+
self.error(obj, value)
149177

150178
class InstanceDict(traitlets.Instance):
151179
"""An instance trait which coerces a dict to an instance.

ipywidgets/widgets/widget_media.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from .domwidget import DOMWidget
88
from .valuewidget import ValueWidget
99
from .widget import register
10-
from traitlets import Unicode, CUnicode, Bytes, Bool
11-
from .trait_types import bytes_serialization
10+
from traitlets import Unicode, CUnicode, Bool
11+
from .trait_types import CByteMemoryView
1212

1313

1414
@register
@@ -23,7 +23,7 @@ class _Media(DOMWidget, ValueWidget, CoreWidget):
2323
"""
2424

2525
# Define the custom state properties to sync with the front-end
26-
value = Bytes(help="The media data as a byte string.").tag(sync=True, **bytes_serialization)
26+
value = CByteMemoryView(help="The media data as a memory view of bytes.").tag(sync=True)
2727

2828
@classmethod
2929
def _from_file(cls, tag, filename, **kwargs):
@@ -116,12 +116,11 @@ def _get_repr(self, cls):
116116

117117
# Return value first like a ValueWidget
118118
signature = []
119-
sig_value = repr(self.value)
120-
prefix, rest = sig_value.split("'", 1)
121-
content = rest[:-1]
122-
if len(content) > 100:
123-
sig_value = "{}'{}...'".format(prefix, content[0:100])
124-
signature.append('{}={}'.format('value', sig_value))
119+
120+
sig_value = 'value={!r}'.format(self.value[:40].tobytes())
121+
if self.value.nbytes > 40:
122+
sig_value = sig_value[:-1]+"..."+sig_value[-1]
123+
signature.append(sig_value)
125124

126125
for key in super(cls, self)._repr_keys():
127126
if key == 'value':

packages/schema/generate-spec.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import ipywidgets as widgets
1212
from ipywidgets import Color
13-
from ipywidgets.widgets.trait_types import TypedTuple
13+
from ipywidgets.widgets.trait_types import TypedTuple, ByteMemoryView
1414
from ipywidgets.widgets.widget_link import Link
1515

1616
HEADER = '''# Model State
@@ -40,46 +40,46 @@
4040
}
4141

4242

43-
def widget_type(widget, widget_list):
43+
def trait_type(trait, widget_list):
4444
attributes = {}
45-
if isinstance(widget, CaselessStrEnum):
45+
if isinstance(trait, CaselessStrEnum):
4646
w_type = 'string'
47-
attributes['enum'] = widget.values
48-
elif isinstance(widget, Unicode):
47+
attributes['enum'] = trait.values
48+
elif isinstance(trait, Unicode):
4949
w_type = 'string'
50-
elif isinstance(widget, (Tuple, List)):
50+
elif isinstance(trait, (Tuple, List)):
5151
w_type = 'array'
52-
elif isinstance(widget, TypedTuple):
52+
elif isinstance(trait, TypedTuple):
5353
w_type = 'array'
54-
attributes['items'] = widget_type(widget._trait, widget_list)
55-
elif isinstance(widget, Bool):
54+
attributes['items'] = trait_type(trait._trait, widget_list)
55+
elif isinstance(trait, Bool):
5656
w_type = 'bool'
57-
elif isinstance(widget, (CFloat, Float)):
57+
elif isinstance(trait, (CFloat, Float)):
5858
w_type = 'float'
59-
elif isinstance(widget, (CInt, Int)):
59+
elif isinstance(trait, (CInt, Int)):
6060
w_type = 'int'
61-
elif isinstance(widget, Color):
61+
elif isinstance(trait, Color):
6262
w_type = 'color'
63-
elif isinstance(widget, Dict):
63+
elif isinstance(trait, Dict):
6464
w_type = 'object'
65-
elif isinstance(widget, Bytes):
65+
elif isinstance(trait, Bytes) or isinstance(trait, ByteMemoryView):
6666
w_type = 'bytes'
67-
elif isinstance(widget, Instance) and issubclass(widget.klass,
67+
elif isinstance(trait, Instance) and issubclass(trait.klass,
6868
widgets.Widget):
6969
w_type = 'reference'
70-
attributes['widget'] = widget.klass.__name__
70+
attributes['widget'] = trait.klass.__name__
7171
# ADD the widget to this documenting list
72-
if (widget.klass not in [i[1] for i in widget_list]
73-
and widget.klass is not widgets.Widget):
74-
widget_list.append((widget.klass.__name__, widget.klass))
75-
elif isinstance(widget, Any):
72+
if (trait.klass not in [i[1] for i in widget_list]
73+
and trait.klass is not widgets.Widget):
74+
widget_list.append((trait.klass.__name__, trait.klass))
75+
elif isinstance(trait, Any):
7676
# In our case, these all happen to be values that are converted to
7777
# strings
7878
w_type = 'label'
7979
else:
80-
w_type = widget.__class__.__name__
80+
w_type = trait.__class__.__name__
8181
attributes['type'] = w_type
82-
if widget.allow_none:
82+
if trait.allow_none:
8383
attributes['allow_none'] = True
8484
return attributes
8585

@@ -91,7 +91,7 @@ def jsdefault(trait):
9191
return 'reference to new instance'
9292
else:
9393
default = trait.default_value
94-
if isinstance(default, bytes):
94+
if isinstance(default, bytes) or isinstance(default, memoryview):
9595
default = trait.default_value_repr()
9696
return default
9797

@@ -163,7 +163,7 @@ def jsonify(identifier, widget, widget_list):
163163
help=trait.help or '',
164164
default=jsdefault(trait)
165165
)
166-
attribute.update(widget_type(trait, widget_list))
166+
attribute.update(trait_type(trait, widget_list))
167167
attributes.append(attribute)
168168

169169
return dict(model=model, view=view, attributes=attributes)

packages/schema/jupyterwidgetmodels.latest.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ Attribute | Type | Default | Help
100100
`loop` | boolean | `true` | When true, the audio will start from the beginning after finishing
101101
`tabbable` | `null` or boolean | `null` | Is widget tabbable?
102102
`tooltip` | `null` or string | `null` | A tooltip caption.
103-
`value` | Bytes | `b''` | The media data as a byte string.
103+
`value` | Bytes | `b''` | The media data as a memory view of bytes.
104104

105105
### BoundedFloatTextModel (@jupyter-widgets/controls, 1.5.0); FloatTextView (@jupyter-widgets/controls, 1.5.0)
106106

@@ -330,7 +330,7 @@ Attribute | Type | Default | Help
330330
`layout` | reference to Layout widget | reference to new instance |
331331
`tabbable` | `null` or boolean | `null` | Is widget tabbable?
332332
`tooltip` | `null` or string | `null` | A tooltip caption.
333-
`value` | Bytes | `b''` | The media data as a byte string.
333+
`value` | Bytes | `b''` | The media data as a memory view of bytes.
334334

335335
### DatePickerModel (@jupyter-widgets/controls, 1.5.0); DatePickerView (@jupyter-widgets/controls, 1.5.0)
336336

@@ -630,7 +630,7 @@ Attribute | Type | Default | Help
630630
`layout` | reference to Layout widget | reference to new instance |
631631
`tabbable` | `null` or boolean | `null` | Is widget tabbable?
632632
`tooltip` | `null` or string | `null` | A tooltip caption.
633-
`value` | Bytes | `b''` | The media data as a byte string.
633+
`value` | Bytes | `b''` | The media data as a memory view of bytes.
634634
`width` | string | `''` | Width of the image in pixels. Use layout.width for styling the widget.
635635

636636
### IntProgressModel (@jupyter-widgets/controls, 1.5.0); ProgressView (@jupyter-widgets/controls, 1.5.0)
@@ -1136,7 +1136,7 @@ Attribute | Type | Default | Help
11361136
`loop` | boolean | `true` | When true, the video will start from the beginning after finishing
11371137
`tabbable` | `null` or boolean | `null` | Is widget tabbable?
11381138
`tooltip` | `null` or string | `null` | A tooltip caption.
1139-
`value` | Bytes | `b''` | The media data as a byte string.
1139+
`value` | Bytes | `b''` | The media data as a memory view of bytes.
11401140
`width` | string | `''` | Width of the video in pixels.
11411141

11421142
### OutputModel (@jupyter-widgets/output, 1.0.0); OutputView (@jupyter-widgets/output, 1.0.0)

0 commit comments

Comments
 (0)