def test_trait_list_iter_observables_not_a_list_error(self): # Test when the given object is not a list instance = ClassWithList() observer = ListItemObserver(notify=True, optional=False) with self.assertRaises(ValueError) as exception_context: list(observer.iter_observables(instance.number)) self.assertIn( "Expected a TraitList to be observed", str(exception_context.exception))
def test_trait_list_iter_observables_not_a_trait_list_optional(self): # Test when the given object is a list but not an IObservable instance = ClassWithList() observer = ListItemObserver(notify=True, optional=True) self.assertIsNone(instance.not_a_trait_list) actual = list(observer.iter_observables(instance.not_a_trait_list)) self.assertEqual(actual, []) instance.not_a_trait_list = CustomList() actual = list(observer.iter_observables(instance.not_a_trait_list)) self.assertEqual(actual, [])
def test_trait_list_iter_observables_error(self): # If the user chains a ListItemObserver after an observer that # does not produce a TraitList, raise an error instance = ClassWithList() instance.not_a_trait_list = CustomList() observer = ListItemObserver(notify=True, optional=False) with self.assertRaises(ValueError) as exception_context: next(observer.iter_observables(instance.not_a_trait_list)) self.assertIn("Expected a TraitList to be observed", str(exception_context.exception))
def test_list_items_optional_true(self): expr = expression.list_items(optional=True) expected = [ create_graph(ListItemObserver(notify=True, optional=True), ), ] actual = expr._as_graphs() self.assertEqual(actual, expected)
def list_items(notify=True, optional=False): """ Create a new expression for observing items inside a list. Events emitted (if any) will be instances of :class:`~traits.observation.events.ListChangeEvent`. e.g. ``trait("containers").list_items()`` for observing mutations to a list named ``containers``. e.g. ``trait("containers").list_items().trait("value")`` for observing the trait ``value`` on any items in the list ``containers``. Parameters ---------- notify : bool, optional Whether to notify for changes. Default is to notify. optional : bool, optional Whether to ignore this if the upstream object is not a list. Default is false and an error will be raised if the object is not a list. Returns ------- new_expression : ObserverExpression """ observer = ListItemObserver(notify=notify, optional=optional) return SingleObserverExpression(observer)
def test_maintain_notifier(self): # Test maintaining downstream notifier by # observing list of list instance = ClassWithListOfList() graph = create_graph( ListItemObserver(notify=False, optional=False), ListItemObserver(notify=True, optional=False), ) handler = mock.Mock() call_add_or_remove_notifiers( object=instance.list_of_list, graph=graph, handler=handler, ) # when instance.list_of_list.append([]) # then # the first ListItemObserver has notify=False self.assertEqual(handler.call_count, 0) # but the second ListItemObserver is given to the nested list nested_list = instance.list_of_list[0] # when nested_list.append(1) # then ((event, ), _), = handler.call_args_list self.assertIs(event.object, nested_list) self.assertEqual(event.added, [1]) self.assertEqual(event.removed, []) self.assertEqual(event.index, 0) handler.reset_mock() # when # the list is removed, it is not observed instance.list_of_list.pop() nested_list.append(1) # then self.assertEqual(handler.call_count, 0)
def test_optional_observers(self): # ListItemObserver.optional is true, meaning it will ignore # incompatible incoming object. instance = ClassWithList() graph = create_graph(ListItemObserver(notify=True, optional=True), ) handler = mock.Mock() call_add_or_remove_notifiers( object=instance.not_a_trait_list, graph=graph, handler=handler, ) instance.not_a_trait_list = CustomList() instance.not_a_trait_list.append(1) self.assertEqual(handler.call_count, 0)
def test_notifier_list_change(self): instance = ClassWithList(values=[]) graph = create_graph(ListItemObserver(notify=True, optional=False), ) handler = mock.Mock() call_add_or_remove_notifiers( object=instance.values, graph=graph, handler=handler, ) # when instance.values.append(1) # then ((event, ), _), = handler.call_args_list self.assertIs(event.object, instance.values) self.assertEqual(event.added, [1]) self.assertEqual(event.removed, []) self.assertEqual(event.index, 0)
def test_notifier_custom_trait_list_change(self): # Test compatibility with any extension of TraitList, not just # TraitListObject instance = ClassWithList() instance.custom_trait_list = CustomTraitList() graph = create_graph(ListItemObserver(notify=True, optional=False), ) handler = mock.Mock() call_add_or_remove_notifiers( object=instance.custom_trait_list, graph=graph, handler=handler, ) # when instance.custom_trait_list.append(1) # then ((event, ), _), = handler.call_args_list self.assertIs(event.object, instance.custom_trait_list) self.assertEqual(event.added, [1]) self.assertEqual(event.removed, []) self.assertEqual(event.index, 0)
def test_equal_observers(self): observer1 = ListItemObserver(notify=False, optional=False) observer2 = ListItemObserver(notify=False, optional=False) self.assertEqual(observer1, observer2) self.assertEqual(hash(observer1), hash(observer2))
def test_not_equal_different_type(self): observer1 = ListItemObserver(notify=False, optional=False) imposter = mock.Mock() imposter.notify = False imposter.optional = False self.assertNotEqual(observer1, imposter)
def test_not_equal_optional(self): observer1 = ListItemObserver(notify=True, optional=True) observer2 = ListItemObserver(notify=True, optional=False) self.assertNotEqual(observer1, observer2)
def test_trait_list_iter_objects_ignore_if_optional_and_not_list(self): observer = ListItemObserver(notify=True, optional=True) actual = list(observer.iter_objects(set([1]))) self.assertEqual(actual, [])