diff --git a/Orange/widgets/settings.py b/Orange/widgets/settings.py index 4202c0d55c0..19b608e8ec7 100644 --- a/Orange/widgets/settings.py +++ b/Orange/widgets/settings.py @@ -659,10 +659,11 @@ def find_or_create_context(self, widget, *args): of the context list.""" # First search the contexts that were already used in this widget instance - best_context, best_score = self.find_context(widget.context_settings, args) + best_context, best_score = self.find_context(widget.context_settings, args, move_up=True) # If the exact data was used, reuse the context if best_score == self.PERFECT_MATCH: return best_context, False + # Otherwise check if a better match is available in global_contexts best_context, best_score = self.find_context(self.global_contexts, args, best_score, best_context) @@ -675,7 +676,7 @@ def find_or_create_context(self, widget, *args): self.add_context(widget.context_settings, context) return context, best_context is None - def find_context(self, known_contexts, args, best_score=0, best_context=None): + def find_context(self, known_contexts, args, best_score=0, best_context=None, move_up=False): """Search the given list of contexts and return the context which best matches the given args. @@ -685,7 +686,8 @@ def find_context(self, known_contexts, args, best_score=0, best_context=None): for i, context in enumerate(known_contexts): score = self.match(context, *args) if score == self.PERFECT_MATCH: - self.move_context_up(known_contexts, i) + if move_up: + self.move_context_up(known_contexts, i) return context, score if score > best_score: # NO_MATCH is not OK! best_context, best_score = context, score @@ -1035,6 +1037,8 @@ def is_valid_item(self, setting, item, attrs, metas): Subclasses can override this method to checks data in alternative representations. """ + if not isinstance(item, tuple): + return True return self._var_exists(setting, item, attrs, metas) diff --git a/Orange/widgets/tests/test_context_handler.py b/Orange/widgets/tests/test_context_handler.py index 3eb3ebe4075..1e9b415b803 100644 --- a/Orange/widgets/tests/test_context_handler.py +++ b/Orange/widgets/tests/test_context_handler.py @@ -1,6 +1,7 @@ +from copy import copy from unittest import TestCase from unittest.mock import Mock -from Orange.widgets.settings import ContextHandler, Setting, ContextSetting +from Orange.widgets.settings import ContextHandler, Setting, ContextSetting, Context __author__ = 'anze' @@ -11,7 +12,7 @@ class SimpleWidget: context_setting = ContextSetting(42) -class ContextHandlerTestCase(TestCase): +class TestContextHandler(TestCase): def test_initialize(self): handler = ContextHandler() handler.provider = Mock() @@ -40,3 +41,30 @@ def test_fast_save(self): self.assertEqual(context.values['context_setting'], 55) self.assertEqual(handler.known_settings['context_setting'].default, SimpleWidget.context_setting.default) + + def test_find_or_create_context(self): + widget = SimpleWidget() + handler = ContextHandler() + handler.match = lambda context, i: (context.i == i) * 2 + handler.clone_context = lambda context, i: copy(context) + + c1, c2, c3, c4, c5, c6, c7, c8, c9 = (Context(i=i) + for i in range(1, 10)) + + # finding a perfect match in global_contexts should copy it to + # the front of context_settings (and leave globals as-is) + widget.context_settings = [c2, c5] + handler.global_contexts = [c3, c7] + context, new = handler.find_or_create_context(widget, 7) + self.assertEqual(context.i, 7) + self.assertEqual([c.i for c in widget.context_settings], [7, 2, 5]) + self.assertEqual([c.i for c in handler.global_contexts], [3, 7]) + + # finding a perfect match in context_settings should move it to + # the front of the list + widget.context_settings = [c2, c5] + handler.global_contexts = [c3, c7] + context, new = handler.find_or_create_context(widget, 5) + self.assertEqual(context.i, 5) + self.assertEqual([c.i for c in widget.context_settings], [5, 2]) + self.assertEqual([c.i for c in handler.global_contexts], [3, 7]) diff --git a/Orange/widgets/tests/test_domain_context_handler.py b/Orange/widgets/tests/test_domain_context_handler.py index 2eca64026f0..f3e088d4cba 100644 --- a/Orange/widgets/tests/test_domain_context_handler.py +++ b/Orange/widgets/tests/test_domain_context_handler.py @@ -9,7 +9,7 @@ Discrete = vartype(DiscreteVariable()) -class DomainContextHandlerTestCase(TestCase): +class TestDomainContextHandler(TestCase): def setUp(self): self.domain = Domain( attributes=[ContinuousVariable('c1'), @@ -235,6 +235,26 @@ def test_open_context_with_no_match(self): ('c2', Continuous), ('d4', Discrete))) self.assertEqual(context.values['text'], ('u', -2)) + def test_filter_value(self): + setting = ContextSetting([]) + setting.name = "value" + + def test_filter(before_value, after_value): + data = dict(value=before_value) + self.handler.filter_value(setting, data, *self.args) + self.assertIn("value", data) + self.assertEqual(data["value"], after_value) + + # filter list values + test_filter([], []) + # When list contains attributes asa tuple of (name, type), + # Attributes not present in domain should be filtered out + test_filter([("d1", Discrete), ("d1", Continuous), + ("c1", Continuous), ("c1", Discrete)], + [("d1", Discrete), ("c1", Continuous)]) + # All other values in list should remain + test_filter([0, [1, 2, 3], "abcd", 5.4], [0, [1, 2, 3], "abcd", 5.4]) + def create_context(self, domain, values): if not domain: domain = Domain([])