# HG changeset patch # User Adrien Di Mascio # Date 1234797864 -3600 # Node ID 66ff0b2f7d03a8f3c8e644e4eca8bc9538750c6e # Parent 59b6542f5729c5174d97193b4482d232ae4c2206 simple test implementation for binary operators on selectors diff -r 59b6542f5729 -r 66ff0b2f7d03 test/unittest_selectors.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/unittest_selectors.py Mon Feb 16 16:24:24 2009 +0100 @@ -0,0 +1,80 @@ +"""unit tests for selectors mechanism + +:organization: Logilab +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +""" + +from logilab.common.testlib import TestCase, unittest_main + +from cubicweb.vregistry import Selector, AndSelector, OrSelector + +class _1_(Selector): + def __call__(self, *args, **kwargs): + return 1 + +class _0_(Selector): + def __call__(self, *args, **kwargs): + return 0 + +def _2_(*args, **kwargs): + return 2 + + +class SelectorsTC(TestCase): + def test_basic_and(self): + selector = _1_() & _1_() + self.assertEquals(selector(None), 2) + selector = _1_() & _0_() + self.assertEquals(selector(None), 0) + selector = _0_() & _1_() + self.assertEquals(selector(None), 0) + + def test_basic_or(self): + selector = _1_() | _1_() + self.assertEquals(selector(None), 1) + selector = _1_() | _0_() + self.assertEquals(selector(None), 1) + selector = _0_() | _1_() + self.assertEquals(selector(None), 1) + selector = _0_() | _0_() + self.assertEquals(selector(None), 0) + + def test_selector_and_function(self): + selector = _1_() & _2_ + self.assertEquals(selector(None), 3) + selector = _2_ & _1_() + self.assertEquals(selector(None), 3) + + def test_three_and(self): + selector = _1_() & _1_() & _1_() + self.assertEquals(selector(None), 3) + selector = _1_() & _0_() & _1_() + self.assertEquals(selector(None), 0) + selector = _0_() & _1_() & _1_() + self.assertEquals(selector(None), 0) + + def test_three_or(self): + selector = _1_() | _1_() | _1_() + self.assertEquals(selector(None), 1) + selector = _1_() | _0_() | _1_() + self.assertEquals(selector(None), 1) + selector = _0_() | _1_() | _1_() + self.assertEquals(selector(None), 1) + selector = _0_() | _0_() | _0_() + self.assertEquals(selector(None), 0) + + def test_composition(self): + selector = (_1_() & _1_()) & (_1_() & _1_()) + self.failUnless(isinstance(selector, AndSelector)) + self.assertEquals(len(selector.selectors), 4) + self.assertEquals(selector(None), 4) + selector = (_1_() & _0_()) | (_1_() & _1_()) + self.failUnless(isinstance(selector, OrSelector)) + self.assertEquals(len(selector.selectors), 2) + self.assertEquals(selector(None), 2) + + +if __name__ == '__main__': + unittest_main() + diff -r 59b6542f5729 -r 66ff0b2f7d03 vregistry.py --- a/vregistry.py Mon Feb 16 14:18:13 2009 +0100 +++ b/vregistry.py Mon Feb 16 16:24:24 2009 +0100 @@ -543,3 +543,65 @@ return 0 return selector + +# selector base classes and operations ######################################## +class Selector(object): + """base class for selector classes providing implementation + for operators ``&`` and ``|`` + + This class is only here to give access to binary operators, the + selector logic itself should be implemented in the __call__ method + """ + def __init__(self, *selectors): + self.selectors = self.merge_selectors(selectors) + + @classmethod + def merge_selectors(cls, selectors): + """merge selectors when possible : + + AndSelector(AndSelector(sel1, sel2), AndSelector(sel3, sel4)) + ==> AndSelector(sel1, sel2, sel3, sel4) + """ + merged_selectors = [] + for selector in selectors: + if isinstance(selector, cls): + merged_selectors += selector.selectors + else: + merged_selectors.append(selector) + return merged_selectors + + def __and__(self, other): + return AndSelector(self, other) + + def __or__(self, other): + return OrSelector(self, other) + + __ror__ = __or__ # for cases like (function | selector) + __rand__ = __and__ # for cases like (function & selector) + # XXX (function | function) or (function & function) not managed yet + + def __call__(self, cls, *args, **kwargs): + return NotImplementedError("selector %s must implement its logic " + "in its __call__ method" % self.__class__.__name__) + + +class AndSelector(Selector): + """and-chained selectors (formerly known as chainall)""" + def __call__(self, cls, *args, **kwargs): + score = 0 + for selector in self.selectors: + partscore = selector(cls, *args, **kwargs) + if not partscore: + return 0 + score += partscore + return score + + +class OrSelector(Selector): + """or-chained selectors (formerly known as chainfirst)""" + def __call__(self, cls, *args, **kwargs): + for selector in self.selectors: + partscore = selector(cls, *args, **kwargs) + if partscore: + return partscore + return 0