test_traits.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. # Copyright (c) Jupyter Development Team.
  2. # Distributed under the terms of the Modified BSD License.
  3. """Test trait types of the widget packages."""
  4. import array
  5. import datetime as dt
  6. import pytest
  7. from unittest import TestCase
  8. from traitlets import HasTraits, Int, TraitError
  9. from traitlets.tests.test_traitlets import TraitTestBase
  10. from ipywidgets import Color, NumberFormat
  11. from ipywidgets.widgets.widget import _remove_buffers, _put_buffers
  12. from ipywidgets.widgets.trait_types import date_serialization, TypedTuple
  13. class NumberFormatTrait(HasTraits):
  14. value = NumberFormat(".3f")
  15. class TestNumberFormat(TraitTestBase):
  16. obj = NumberFormatTrait()
  17. _good_values = [
  18. '.2f', '.0%', '($.2f', '+20', '.^20', '.2s', '#x', ',.2r',
  19. ' .2f', '.2', ''
  20. ]
  21. _bad_values = [52, False, 'broken', '..2f', '.2a']
  22. class ColorTrait(HasTraits):
  23. value = Color("black")
  24. class TestColor(TraitTestBase):
  25. obj = ColorTrait()
  26. _good_values = [
  27. "blue", # valid color name
  28. "#AA0", # single digit hex
  29. "#FFFFFF", # double digit hex
  30. "transparent", # special color name
  31. '#aaaa', # single digit hex with alpha
  32. '#ffffffff', # double digit hex with alpha
  33. 'rgb(0, 0, 0)', # rgb
  34. 'rgb( 20,70,50 )', # rgb with spaces
  35. 'rgba(10,10,10, 0.5)', # rgba with float alpha
  36. 'rgba(255, 255, 255, 255)', # out of bounds alpha (spec says clamp to 1)
  37. 'hsl(0.0, .0, 0)', # hsl
  38. 'hsl( 0.5,0.3,0 )', # hsl with spaces
  39. 'hsla(10,10,10, 0.5)', # rgba with float alpha
  40. ]
  41. _bad_values = [
  42. "vanilla", "blues", # Invald color names
  43. 1.2, 0.0, # Should fail with float input
  44. 0, 1, 2, # Should fail with int input
  45. 'rgb(0.4, 512, -40)',
  46. 'hsl(0.4, 512, -40)',
  47. 'rgba(0, 0, 0)',
  48. 'hsla(0, 0, 0)',
  49. None,
  50. ]
  51. class ColorTraitWithNone(HasTraits):
  52. value = Color("black", allow_none=True)
  53. class TestColorWithNone(TraitTestBase):
  54. obj = ColorTraitWithNone()
  55. _good_values = TestColor._good_values + [None]
  56. _bad_values = list(filter(lambda v: v is not None, TestColor._bad_values))
  57. class TestDateSerialization(TestCase):
  58. def setUp(self):
  59. self.to_json = date_serialization['to_json']
  60. self.dummy_manager = None
  61. def test_serialize_none(self):
  62. self.assertIs(self.to_json(None, self.dummy_manager), None)
  63. def test_serialize_date(self):
  64. date = dt.date(1900, 2, 18)
  65. expected = {
  66. 'year': 1900,
  67. 'month': 1,
  68. 'date': 18
  69. }
  70. self.assertEqual(self.to_json(date, self.dummy_manager), expected)
  71. class TestDateDeserialization(TestCase):
  72. def setUp(self):
  73. self.from_json = date_serialization['from_json']
  74. self.dummy_manager = None
  75. def test_deserialize_none(self):
  76. self.assertIs(self.from_json(None, self.dummy_manager), None)
  77. def test_deserialize_date(self):
  78. serialized_date = {
  79. 'year': 1900,
  80. 'month': 1,
  81. 'date': 18
  82. }
  83. expected = dt.date(1900, 2, 18)
  84. self.assertEqual(
  85. self.from_json(serialized_date, self.dummy_manager),
  86. expected
  87. )
  88. class TestBuffers(TestCase):
  89. def test_remove_and_put_buffers(self):
  90. mv1 = memoryview(b'test1')
  91. mv2 = memoryview(b'test2')
  92. state = {'plain': [0, 'text'], # should not get removed
  93. 'x': {'ar': mv1}, # should result in an empty dict
  94. 'y': {'shape': (10, 10), 'data': mv1},
  95. 'z': (mv1, mv2), # tests tuple assigment
  96. 'top': mv1, # test a top level removal
  97. 'deep': {'a': 1, 'b':[0,{'deeper':mv2}]}} # deeply nested
  98. plain = state['plain']
  99. x = state['x']
  100. y = state['y']
  101. y_shape = y['shape']
  102. state_before = state
  103. state, buffer_paths, buffers = _remove_buffers(state)
  104. # check if buffers are removed
  105. self.assertIn('plain', state)
  106. self.assertIn('shape', state['y'])
  107. self.assertNotIn('ar', state['x'])
  108. self.assertEqual(state['x'], {})
  109. self.assertNotIn('data', state['y'])
  110. self.assertNotIn(mv1, state['z'])
  111. self.assertNotIn(mv1, state['z'])
  112. self.assertNotIn('top', state)
  113. self.assertIn('deep', state)
  114. self.assertIn('b', state['deep'])
  115. self.assertNotIn('deeper', state['deep']['b'][1])
  116. # check that items that didn't need change aren't touched
  117. self.assertIsNot(state, state_before)
  118. self.assertIs(state['plain'], plain)
  119. self.assertIsNot(state['x'], x)
  120. self.assertIsNot(state['y'], y)
  121. self.assertIs(state['y']['shape'], y_shape)
  122. # check that the buffer paths really point to the right buffer
  123. for path, buffer in [(['x', 'ar'], mv1), (['y', 'data'], mv1), (['z', 0], mv1), (['z', 1], mv2),\
  124. (['top'], mv1), (['deep', 'b', 1, 'deeper'], mv2)]:
  125. self.assertIn(path, buffer_paths, "%r not in path" % path)
  126. index = buffer_paths.index(path)
  127. self.assertEqual(buffer, buffers[index])
  128. # and check that we can put it back together again
  129. _put_buffers(state, buffer_paths, buffers)
  130. # we know that tuples get converted to list, so help the comparison by changing the tuple to a list
  131. state_before['z'] = list(state_before['z'])
  132. self.assertEqual(state_before, state)
  133. def test_typed_tuple_uninitialized_ints():
  134. class TestCase(HasTraits):
  135. value = TypedTuple(trait=Int())
  136. obj = TestCase()
  137. assert obj.value == ()
  138. def test_typed_tuple_init_ints():
  139. class TestCase(HasTraits):
  140. value = TypedTuple(trait=Int())
  141. obj = TestCase(value=(1, 2, 3))
  142. assert obj.value == (1, 2, 3)
  143. def test_typed_tuple_set_ints():
  144. class TestCase(HasTraits):
  145. value = TypedTuple(trait=Int())
  146. obj = TestCase()
  147. obj.value = (1, 2, 3)
  148. assert obj.value == (1, 2, 3)
  149. def test_typed_tuple_default():
  150. class TestCase(HasTraits):
  151. value = TypedTuple(default_value=(1, 2, 3))
  152. obj = TestCase()
  153. assert obj.value == (1, 2, 3)
  154. def test_typed_tuple_mixed_default():
  155. class TestCase(HasTraits):
  156. value = TypedTuple(default_value=(1, 2, 'foobar'))
  157. obj = TestCase()
  158. assert obj.value == (1, 2, 'foobar')
  159. def test_typed_tuple_bad_default():
  160. class TestCase(HasTraits):
  161. value = TypedTuple(trait=Int(), default_value=(1, 2, 'foobar'))
  162. with pytest.raises(TraitError):
  163. obj = TestCase()
  164. a = obj.value # a read might be needed to trigger default validation
  165. def test_typed_tuple_bad_set():
  166. class TestCase(HasTraits):
  167. value = TypedTuple(trait=Int())
  168. obj = TestCase()
  169. with pytest.raises(TraitError):
  170. obj.value = (1, 2, 'foobar')
  171. def test_typed_tuple_positional_trait():
  172. class TestCase(HasTraits):
  173. value = TypedTuple(Int())
  174. obj = TestCase(value=(1, 2, 3))
  175. assert obj.value == (1, 2, 3)
  176. def test_typed_tuple_positional_default():
  177. class TestCase(HasTraits):
  178. value = TypedTuple((1, 2, 3))
  179. obj = TestCase()
  180. assert obj.value == (1, 2, 3)