@@ 13,7 13,7 @@ def check_ambiguity(candidates, mros):
for d, s, order in zip(dom, sub, orders))
return [cand for cand, _func in candidates
if not any(dominates(dom, cand)
- for dom, _xxx in candidates)]
+ for dom, _func in candidates)]
def clos_mro(types_func_map, callsig):
siglen = len(callsig)
@@ 27,32 27,48 @@ def clos_mro(types_func_map, callsig):
return (x[1] for x in sorted((map(proximity, sig, mros), func)
for sig, func in table))
+
+class NoNextMethod(Exception): pass
+
class GF(object):
def __init__(self, mro=clos_mro):
self.mro = mro
self._reg = {}
self._cache = {}
+ self._funcs = list()
+ self._nm_index = 0
+
+ def __str__(self):
+ return '<funcs: %s, mro: %s, reg: %s, cache: %s, pos: %s>' % (
+ self._funcs, self.mro, self._reg, self._cache, self._nm_index)
def register(self, sig, func):
self._reg[sig] = func
self._cache = {}
def __call__(self, *args, **kwargs):
- self._pos = 0
+ self._nm_index = 0
sig = tuple(x.__class__ for x in args)
func = self._cache.get(sig)
if func is None:
self._funcs = list(self.mro(self._reg, sig))
try:
- self._cache[sig] = func = self._funcs[self._pos]
+ self._cache[sig] = func = self._funcs[self._nm_index]
except IndexError:
raise TypeError('no defined call signature for args (%s)'
% ','.join(str(arg) for arg in args))
return func(*args, **kwargs)
- def next_method(self, *args, **kwargs):
- self._pos += 1
- func = self._funcs[self._pos]
- return func(*args, **kwargs)
+ def next_method(self):
+ self._nm_index += 1
+ try:
+ func = self._funcs[self._nm_index]
+ except IndexError:
+ return None
+ return func
-
+ def call_next_method(self, *args, **kwargs):
+ next = self.next_method()
+ if next:
+ return next(*args, **kwargs)
+ raise NoNextMethod(str(self))
@@ 1,6 1,6 @@
import unittest
from gf import method
-from gf.dispatch import GF
+from gf.dispatch import GF, NoNextMethod
class TestGF(unittest.TestCase):
@@ 27,15 27,15 @@ class TestGF(unittest.TestCase):
l.append('top')
@method(Left, list)
def bip(x, l):
- bip.next_method(x, l)
+ bip.call_next_method(x, l)
l.append('left')
@method(Right, list)
def bip(x, l):
- bip.next_method(x, l)
+ bip.call_next_method(x, l)
l.append('right')
@method(Bottom, list)
def bip(x, l):
- bip.next_method(x, l)
+ bip.call_next_method(x, l)
l.append('bottom')
b = Bottom()
@@ 56,13 56,13 @@ class TestGF(unittest.TestCase):
return ('AX',)
@method(A,Y)
def foo(x,y):
- return foo.next_method(x, y) + ('AY',)
+ return foo.call_next_method(x, y) + ('AY',)
@method(B,X)
def foo(x,y):
- return foo.next_method(x, y) + ('BX',)
+ return foo.call_next_method(x, y) + ('BX',)
@method(B,Y)
def foo(x,y):
- return foo.next_method(x, y) + ('BY',)
+ return foo.call_next_method(x, y) + ('BY',)
b, y = B(), Y()
self.assertEquals(foo(b, y), ('AX', 'AY', 'BX', 'BY'))
@@ 78,8 78,37 @@ class TestGF(unittest.TestCase):
@method(B,X)
def foo(b,x):
return b,x
- b, y = B(), Y()
+ a, b, x, y = A(), B(), X(), Y()
self.assertRaises(TypeError, foo, b, y)
+ self.assertEquals(foo(a, y), (a, y))
+ self.assertEquals(foo(b, x), (b, x))
+ self.assertRaises(TypeError, foo, a, x)
+
+ def test_call_next_method(self):
+ class A(object): pass
+ class B(object): pass
+ @method((A,), (B,))
+ def check(obj):
+ check.call_next_method(obj)
+ self.assertRaises(NoNextMethod, check, B())
+
+ def test_c3_mro(self):
+ class pane(object): pass
+ class scrolling_mixin(object): pass
+ class editing_mixin(object): pass
+ class scrollable_pane(pane, scrolling_mixin): pass
+ class editable_pane(pane, editing_mixin): pass
+ class editable_scrollable_pane(scrollable_pane,
+ editable_pane): pass
+ order = (editable_scrollable_pane,
+ scrollable_pane,
+ editable_pane,
+ pane,
+ scrolling_mixin,
+ editing_mixin,
+ object)
+ self.assertEquals(tuple(editable_scrollable_pane.mro()),
+ order)
if __name__ == '__main__':
unittest.main()