Commits

Lynn Rees  committed 0ab3a06

- filter for object members deep in class bases
- adjust member filters

  • Participants
  • Parent commits 544a30d
  • Branches pu
  • Tags 0.1.5

Comments (0)

Files changed (10)

File twoq/active/mixins.py

     def __contains__(self, value):
         return value in self.incoming
 
+    _oicontains = __contains__
+
     def __len__(self):
         return len(self.incoming)
 
-    count = __len__
+    count = _oicount = __len__
 
     def outcount(self):
         '''count of outgoing items'''
         return len(self.outgoing)
 
+    _ooutcount = outcount
+
     @property
     def balanced(self):
         '''if queues are balanced'''
         return len(self.outgoing) == len(self.incoming)
 
+    _obalanced = balanced
+
     def index(self, thing, _bisect_right=bisect_right):
         '''
         insert thing into incoming things
         '''
         return _bisect_right(self.incoming, thing) - 1
 
+    _oindex = index
+
     def final(self, _l=list, _ln=len):
         '''return outgoing things and clear'''
         results = self.pop() if _ln(self.outgoing) == 1 else _l(self.outgoing)
         self.clear()
         return results
 
+    _ofinal = final
+
     def results(self, _iterexcept=iterexcept):
         '''iterate over reversed outgoing things, clearing as it goes'''
         for thing in _iterexcept(self.outgoing.popleft, IndexError):
             yield thing
 
+    _oresults = results
+
     def value(self, _l=list, _ln=len):
         '''return outgoing things and clear'''
         results = self.pop() if _ln(self.outgoing) == 1 else _l(self.outgoing)
         self._outclear()
         return results
 
+    _ovalue = value
+
     def first(self):
         '''first thing among incoming things'''
         with self._sync as sync:
             sync.append(sync.iterable.popleft())
         return self
 
+    _ofirst = first
+
     def last(self):
         '''last thing among incoming things'''
         with self._sync as sync:
             sync.append(sync.iterable.pop())
         return self
 
+    _olast = last
+
     ###########################################################################
     ## clear queues ###########################################################
     ###########################################################################
         incoming.popleft()
         incoming.rotate(index)
 
+    _oidelitem = __delitem__
+
     def remove(self, thing, _bisect_right=bisect_right):
         '''
         remove thing from incoming things
         incoming.rotate(position)
         return self
 
+    _oiremove = remove
+
     def clear(self):
         '''clear all queues'''
         self._call = None
         self._inclear()
         return self
 
+    _oclear = clear
+
     def inclear(self):
         '''incoming things clear'''
         self._inclear()
         return self
 
+    _oiclear = inclear
+
     def outclear(self):
         '''incoming things clear'''
         self._inclear()
         return self
 
+    _ooutclear = outclear
+
     ###########################################################################
     ## manipulate queues ######################################################
     ###########################################################################
         self._inappend(thing)
         return self
 
+    _oappend = append
+
     def appendleft(self, thing):
         '''incoming things left append'''
         self._inappendleft(thing)
         return self
 
+    _oappendleft = appendleft
+
     def insert(self, index, value):
         '''
         insert thing into incoming things
         incoming.rotate(index)
         return self
 
+    _oinsert = insert
+
     def extend(self, things):
         '''incoming things right extend'''
         self._inextend(things)
         return self
 
+    _oextend = extend
+
     def extendleft(self, things):
         '''incoming things left extend'''
         self._inextendleft(things)
         return self
 
+    _oextendleft = extendleft
+
     def reverse(self, _reversed=None):
         '''iterate over reversed incoming things, clearing as it goes'''
         self.outgoing.extendleft(self.incoming)
         self._inextend(self.outgoing)
         return self
 
+    _screverse = reverse
+
     ###########################################################################
     ## balance queues #########################################################
     ###########################################################################
             _sync.append(_list(self.incoming))
         return self
 
+    _oreup = reup
+
     def shift(self):
         '''shift incoming things to outgoing things'''
         self._inextend(self.outgoing)
         return self
 
+    _oshift = shift
+
     def sync(self):
         '''
         shift incoming things to outgoing things, clearing incoming things
         self._inextend(self.outgoing)
         return self
 
+    _osync = sync
+
     def outshift(self):
         '''shift outgoing things to incoming things'''
         # extend incoming items with outgoing items
         self._outextend(self.incoming)
         return self
 
+    _outshift = outshift
+
     def outsync(self):
         '''
         shift outgoing things to incoming things, clearing outgoing things
         self._outextend(self.incoming)
         return self
 
+    _outsync = outsync
+
 
 class _dq(baseq):
 

File twoq/mixins/filtering.py

 import itertools as it
 import functools as ft
 from threading import local
-from functools import partial
+
+from stuf.utils import getcls
 
 from twoq import support as ct
 
 __all__ = (
     'FilteringMixin', 'FilterMixin', 'CollectMixin', 'SetMixin', 'SliceMixin'
 )
+chain_iter = it.chain.from_iterable
 
 ###############################################################################
 ## filtering subroutines ######################################################
 ###############################################################################
 
 
-def find(call, incoming, _filter=ct.filter):
-    for thing in _filter(call, incoming):
+def find(call, iterable, _filter=ct.filter):
+    '''
+    find the first `True` thing in iterator
+
+    @param call: "Truth" filter
+    @param iterable: an iterable
+    '''
+    for thing in _filter(call, iterable):
         yield thing
         break
 
 
-def members(this, call=None, _filter=ct.filter, _get=getattr):
-    '''collect members of incoming things'''
-    for key in _filter(call, dir(this)):
+def members(iterable, _get=getattr):
+    '''
+    collect members of things
+
+    @param thing: an iterable
+    '''
+    for key in dir(iterable):
         try:
-            thing = _get(this, key)
+            thing = _get(iterable, key)
         except AttributeError:
             pass
         else:
             yield key, thing
 
 
+def memberfilter(call, iterable, _members=members, _filter=ct.filter):
+    '''
+    filter members of things
+
+    @param call: "Truth" filter
+    @param iterable: an iterable
+    '''
+    for i in _filter(call, _members(iterable)):
+        yield i
+
+
 def pick(names, iterable, _attrgetter=op.attrgetter):
+    '''
+    collect attributes of things in iterable
+
+    @param names: sequence of names
+    @param iterable: an iterable
+    '''
     attrfind = _attrgetter(*names)
     for thing in iterable:
         try:
 
 
 def pluck(keys, iterable, _itemgetter=op.itemgetter):
+    '''
+    collect values of things in iterable
+
+    @param keys: sequence of keys
+    @param iterable: an iterable
+    '''
     itemfind = _itemgetter(*keys)
     for thing in iterable:
         try:
 
 
 def unique(iterable, key=None, _ff=ct.filterfalse, _set=set):
+    '''
+    unique things in in iterable
+
+    @param iterable: an iterable
+    @param key: determine uniqueness filter
+    '''
     seen = _set()
     seen_add = seen.add
     if key is None:
             sync.iter(_filter(_truth, sync.iterable))
         return self
 
+    _ocompact = compact
+
     def filter(self, _filter=ct.filter):
         '''incoming things for which call is `True`'''
         with self._sync as sync:
             sync(_filter(self._call, sync.iterable))
         return self
 
+    _ofilter = filter
+
     def find(self, _find=find):
         '''first incoming thing for which call is `True`'''
         with self._sync as sync:
             sync(_find(self._call, self.incoming))
         return self
 
+    _ofind = find
+
     def partition(self, _t=it.tee, _ff=ct.filterfalse, _filter=ct.filter):
         '''
         split incoming things into `True` and `False` things based on results
             sync.append(list(_filter(call, truey)))
         return self
 
+    _opartition = partition
+
     def reject(self, _filterfalse=ct.filterfalse):
         '''incoming things for which call is `False`'''
         with self._sync as sync:
             sync(_filterfalse(self._call, sync.iterable))
         return self
 
+    _oreject = reject
+
     def without(self, *things):
         '''strip things from incoming things'''
         with self._sync as sync:
             sync(ct.filterfalse(lambda x: x in things, sync.iterable))
         return self
+    
+    _owithout = without
 
 
 class CollectMixin(local):
 
     '''gathering mixin'''
+    
+    def deepmembers(self, mz=memberfilter, ci=chain_iter, gc=getcls):
+        '''collect members of incoming things and their bases'''
+        _mz = ft.partial(mz, self._call)
+        with self._sync as sync:
+            def _memfilters(thing, mz=_mz, gc=gc):
+                return ci(ct.map(mz, ci([type.mro(gc(thing)), [thing]])))
+            sync(ci(ct.map(_memfilters, sync.iterable)))
+        return self
 
-    def members(self, _mz=members, _map=ct.map, _ci=it.chain.from_iterable):
+    _odeepmembers = deepmembers
+
+    def members(self, _mz=memberfilter, _ci=it.chain.from_iterable):
         '''collect members of incoming things'''
-        _members = partial(_mz, call=self._call)
+        _mz = ft.partial(_mz, self._call)
         with self._sync as sync:
-            sync(_ci(_map(_members, sync.iterable)))
+            sync(_ci(ct.map(_mz, sync.iterable)))
         return self
 
+    _omembers = members
+
     def pick(self, *names):
         '''attributes of incoming things by attribute `*names`'''
         with self._sync as sync:
             sync(pick(names, sync.iterable))
         return self
+    
+    _opick = pick
 
     def pluck(self, *keys):
         '''items of incoming things by item `*keys`'''
             sync(pluck(keys, sync.iterable))
         return self
 
+    _opluck = pluck
+
 
 class SetMixin(local):
 
                 lambda x, y: _set(x).difference(_set(y)), sync.iterable,
             ))
         return self
+    
+    _odifference = difference
 
     def intersection(self, _reduce=ft.reduce, _set=set):
         '''intersection between incoming things'''
                 lambda x, y: _set(x).intersection(_set(y)), sync.iterable,
             ))
         return self
+    
+    _ointersection = intersection
 
     def union(self, _reduce=ft.reduce, _set=set):
         '''union between incoming things'''
                 lambda x, y: _set(x).union(_set(y)), sync.iterable,
             ))
         return self
+    
+    _ounion = union
 
     def unique(self, _unique=unique):
         '''
         with self._sync as sync:
             sync.iter(_unique(sync.iterable, self._call))
         return self
+    
+    _ounique = unique
 
 
 class SliceMixin(local):
         with self._sync as sync:
             sync.append(_next(_islice(sync.iterable, n, None), default))
         return self
+    
+    _onth = nth
 
     def initial(self, _islice=it.islice, _len=len):
         '''all incoming things except the last thing'''
             sync(_islice(iterable, _len(iterable) - 1))
         return self
 
+    _oinitial = initial
+
     def rest(self, _islice=it.islice):
         '''all incoming things except the first thing'''
         with self._sync as sync:
             sync(_islice(sync.iterable, 1, None))
         return self
+    
+    _orest = rest
 
     def snatch(self, n, _islice=it.islice, _len=len):
         '''
             sync(_islice(iterable, _len(iterable) - n, None))
         return self
 
+    _osnatch = snatch
+
     def take(self, n, _islice=it.islice):
         '''
         first `n` things of incoming things
             sync(_islice(sync.iterable, n))
         return self
 
+    _otake = take
+
 
 class FilterMixin(FilteringMixin, CollectMixin, SetMixin, SliceMixin):
 

File twoq/mixins/mapping.py

 
 
 def invoke(thing, caller=None): #@IgnorePep8
+    '''
+    invoke method on object but return object instead of call result if the
+    call returns None
+
+    @param thing: some thing
+    @param caller: a callable (default: None)
+    '''
     results = caller(thing)
     return thing if results is None else results
 
 
 def delay_each(x, y, wait=0, caller=None, _sleep=time.sleep):
+    '''
+    invoke `caller` with passed arguments, keywords after a delay
+
+    @param x: positional arguments
+    @param y: keywork arguments
+    @param wait: time in seconds to delay (default: 0)
+    @param caller: a callable (default: None)
+    '''
     _sleep(wait)
     return caller(*x, **y)
 
 
 def delay_invoke(x, wait=0, caller=None, _sleep=time.sleep): #@IgnorePep8
+    '''
+    invoke method on object after a delay but return object instead of call
+    result if the call returns None
+
+    @param x: some thing
+    @param wait: time in seconds to delay (default: 0)
+    @param caller: a callable (default: None)
+    '''
     _sleep(wait)
     results = caller(x)
     return x if results is None else results
 
 
 def delay_map(x, wait=None, caller=None, _sleep=time.sleep):
+    '''
+    invoke call on thing after a delay
+
+    @param wait: time in seconds to delay (default: 0)
+    @param caller: a callable (default: None)
+    '''
     _sleep(wait)
     return caller(x)
 
             sync(_map(_delay, sync.iterable))
         return self
 
+    _odelay_each = delay_each
+
     def delay_invoke(self, name, wait, _mc=mc, _di=delay_invoke, _map=ct.map):
         '''
         invoke call on each incoming thing with passed arguments, keywords
             sync(_map(_call, sync.iterable))
         return self
 
+    _odelay_invoke = delay_invoke
+
     def delay_map(self, wait, _delay_map=delay_map, _map=ct.map):
         '''
         invoke call on each incoming thing after a delay
             sync(_map(_call, sync.iterable))
         return self
 
+    _odelay_map = delay_map
+
 
 class CopyMixin(local):
 
             sync(_map(_copy, sync.iterable))
         return self
 
+    _ocopy = copy
+
     def deepcopy(self, _map=ct.map, _deepcopy=cp.deepcopy):
         '''copy each incoming thing deeply'''
         with self._sync as sync:
             sync(_map(_deepcopy, sync.iterable))
         return self
 
+    _odeepcopy = deepcopy
+
 
 class MappingMixin(local):
 
         with self._sync as sync:
             sync(_map(lambda x, y: self._call(*x, **y), sync.iterable))
         return self
+    
+    _oeach = each
 
     def invoke(self, name, _mc=mc, _invoke=invoke, _map=ct.map):
         '''
         with self._sync as sync:
             sync(_map(_call, sync.iterable))
         return self
+    
+    _oinvoke = invoke
 
     def map(self, _map=ct.map):
         '''invoke call on each incoming thing'''
         with self._sync as sync:
             sync(_map(self._call, sync.iterable))
         return self
+    
+    _omap = map
 
 
 class RepeatMixin(local):
         with self._sync as sync:
             sync.iter(_chain(sync.iterable, _repeat(None)))
         return self
+    
+    _opadnone = padnone
 
     def range(self, start, stop=0, step=1, _range=ct.xrange):
         '''
             else:
                 sync(_range(start))
         return self
+    
+    _orange = range
 
     def repeat(self, n, _repeat=it.repeat, _tuple=tuple):
         '''
         with self._sync as sync:
             sync(_repeat(_tuple(sync.iterable), n))
         return self
+    
+    _orepeat = repeat
 
     def times(self, n=None, _starmap=it.starmap, _repeat=it.repeat):
         '''
             else:
                 sync(_starmap(self._call, _repeat(sync.iterable, n)))
         return self
+    
+    _otimes = times
 
 
 class MapMixin(DelayMixin, CopyMixin, MappingMixin, RepeatMixin):

File twoq/mixins/ordering.py

                     _groupby(sync.iterable, self._call),
                 ))
         return self
+    
+    _ogroup = group
 
     def grouper(self, n, fill=None, _zipl=ct.zip_longest, _iter=iter):
         '''
         with self._sync as sync:
             sync(_zipl(fillvalue=fill, *[_iter(sync.iterable)] * n))
         return self
+    
+    _ogrouper = grouper
 
     def reverse(self, _reversed=reversed):
         '''reverse incoming things'''
         with self._sync as sync:
             sync(_reversed(sync.iterable))
         return self
+    
+    _oreverse = reverse
 
     def sort(self, _sorted=sorted):
         '''sort incoming things using call for key function'''
             else:
                 sync(_sorted(sync.iterable, key=self._call))
         return self
+    
+    _osort = sort
 
 
 class RandomMixin(local):
         with self._sync as sync:
             sync.append(_choice(sync.iterable))
         return self
+    
+    _ochoice = choice
 
     def sample(self, n, _sample=rm.sample, _list=list):
         '''
         with self._sync as sync:
             sync(_sample(_list(sync.iterable), n))
         return self
+    
+    _osample = sample
 
     def shuffle(self, _shuffle=rm.shuffle):
         '''shuffle incoming things'''
             _shuffle(iterable)
             sync(iterable)
         return self
+    
+    _oshuffle = shuffle
 
 
 class OrderMixin(OrderingMixin, RandomMixin):

File twoq/mixins/queuing.py

         self._kw = kw
         return self
 
+    _oargs = args
+
     def tap(self, call):
         '''
         add call
         self._call = call
         return self
 
+    _otap = tap
+
     def detap(self):
         '''clear call'''
         # reset postitional arguments
         self._call = None
         return self
 
+    _odetap = detap
+
     def wrap(self, call):
         '''build factory callable and make call'''
         def factory(*args, **kw):
         self._call = factory
         return self
 
+    _owrap = wrap
+
     # aliases
     clear = unwrap = detap

File twoq/mixins/reducing.py

 
 
 def roundrobin(iterable, s=it.islice, c=it.cycle, p=partial, _i=iter, n=next):
+    '''
+    interleave things in iterable into one thing e.g.
+
+    @param iterable: an iterable
+    '''
     pending = len(iterable)
     nexts = c(p(n, _i(i)) for i in iterable)
     while pending:
 
 
 def smash(iterable, _isstring=ct.port.isstring, _Iterable=Iterable):
+    '''
+    flatten deeply nested iterable
+
+    @param iterable: an iterable
+    '''
     for i in iterable:
         if isinstance(i, _Iterable) and not _isstring(i):
             for j in smash(i):
             sync.append(_truediv(_sum(iterable, 0.0), _len(iterable)))
         return self
 
+    _oaverage = average
+
     def fsum(self, _fsum=mt.fsum):
         '''
         add incoming things together
             sync.append(_fsum(sync.iterable))
         return self
 
+    _ofsum = average
+
     def max(self, _max=max):
         '''find maximum value in incoming things using call for key function'''
         with self._sync as sync:
                 sync.append(_max(sync.iterable, key=self._call))
         return self
 
+    _omax = max
+
     def median(self, _div=op.truediv, _len=len, _srt=sorted, _l=list, _i=int):
         '''mean of incoming things'''
         with self._sync as sync:
             sync.append(i[p] if e % 2 == 0 else _div(i[p] + i[p + 1], 2))
         return self
 
+    _omedian = median
+
     def min(self, _min=min):
         '''find minimum value in incoming things using call for key function'''
         with self._sync as sync:
                 sync.append(_min(sync.iterable, key=self._call))
         return self
 
+    _omin = min
+
     def mode(self, _cnt=Counter):
         '''mode of incoming things'''
         with self._sync as sync:
             sync.append(_cnt(sync.iterable).most_common(1)[0][0])
         return self
 
+    _omode = mode
+
     def uncommon(self, _cnt=Counter):
         '''least common incoming thing'''
         with self._sync as sync:
             sync.append(_cnt(sync.iterable).most_common()[:-2:-1][0][0])
         return self
 
+    _ouncommon = uncommon
+
     def frequency(self, _cnt=Counter):
         '''frequency of each incoming thing'''
         with self._sync as sync:
             sync.append(_cnt(sync.iterable).most_common())
         return self
 
+    _ofrequency = frequency
+
     def statrange(self, _srt=sorted, _list=list):
         '''statistical range of incoming things'''
         with self._sync as sync:
             sync.append(iterz[-1] - iterz[0])
         return self
 
+    _ostatrange = statrange
+
     def sum(self, start=0, _sum=sum):
         '''
         add incoming things together
             sync.append(_sum(sync.iterable, start))
         return self
 
+    _osum = sum
+
 
 class ReducingMixin(local):
 
             sync(_chain(sync.iterable))
         return self
 
+    _oflatten = flatten
+
     def merge(self, _merge=hq.merge):
         '''flatten nested and ordered incoming things'''
         with self._sync as sync:
             sync(_merge(*sync.iterable))
         return self
 
+    _omerge = merge
+
     def smash(self, _smash=smash):
         '''flatten deeply nested incoming things'''
         with self._sync as sync:
             sync(_smash(sync.iterable))
         return self
 
+    _osmash = smash
+
     def pairwise(self, _tee=it.tee, _next=next, _zip=ct.zip):
         '''
         every two incoming things as a tuple
             sync(_zip(a, b))
         return self
 
+    _opairwise = pairwise
+
     def reduce(self, initial=None, _reduce=ft.reduce):
         '''
         reduce incoming things to one thing using call
                 sync.append(_reduce(self._call, sync.iterable))
         return self
 
+    _oreduce = reduce
+
     def reduce_right(self, initial=None, _reduce=ft.reduce):
         '''
         reduce incoming things to one thing from right side using call
             else:
                 sync(_reduce(lambda x, y: self._call(y, x), sync.iterable))
         return self
+    
+    _oreduce_right = reduce_right
 
     def roundrobin(self, _roundrobin=roundrobin):
         '''
         with self._sync as sync:
             sync(_roundrobin(sync.iterable))
         return self
+    
+    _oroundrobin = roundrobin
 
     def zip(self, _zip=ct.zip):
         '''
             sync(_zip(*sync.iterable))
         return self
 
+    _ozip = zip
+
 
 class TruthMixin(local):
 
         with self._sync as sync:
             sync.append(_all(_map(self._call, sync.iterable)))
         return self
+    
+    _oall = all
 
     def any(self, _any=any, _map=ct.map):
         '''if `any` incoming things are `True`'''
             sync.append(_any(_map(self._call, sync.iterable)))
         return self
 
+    _oany = any
+
     def contains(self, thing, _contains=op.contains):
         '''
         if `thing` is in incoming things
         with self._sync as sync:
             sync.append(_contains(sync.iterable, thing))
         return self
+    
+    _ocontains = contains
 
     def quantify(self, _sum=isum, _map=ct.map):
         '''how many times call is True for incoming things'''
         with self._sync as sync:
             sync.append(_sum(_map(self._call, sync.iterable)))
         return self
+    
+    _oquantify = quantify
 
 
 class ReduceMixin(MathMixin, ReducingMixin, TruthMixin):

File twoq/tests/active/auto/mixins/filtering.py

         class stoog3: #@IgnorePep8
             name = 'curly'
             age = 60
-        test = lambda x: not x.startswith('__')
+        test = lambda x: not x[0].startswith('__')
         self.assertEqual(
             self.qclass(
                 stooges, stoog2, stoog3
             ('age', 60), ('name', 'curly')],
         )
 
+    def test_deepmembers(self):
+        class stooges:
+            name = 'moe'
+            age = 40
+            def boo(self):
+                return 'boo'
+            def foo(self):
+                return 'foo'
+        class stoog2: #@IgnorePep8
+            name = 'larry'
+            age = 50
+            def boo(self):
+                return 'boo'
+            def foo(self):
+                return 'foo'
+        class stoog3: #@IgnorePep8
+            name = 'curly'
+            age = 60
+            def boo(self):
+                return 'boo'
+            def foo(self):
+                return 'foo'
+        test = lambda x: not x[0].startswith('__')
+        self.assertSequenceEqual(
+            self.qclass(
+                stooges, stoog2, stoog3
+            ).tap(test).deepmembers().sync().value(),
+            [('age', 40), ('boo', stooges.boo), ('foo', stooges.foo),
+            ('name', 'moe'), ('age', 50), ('boo', stoog2.boo),
+            ('foo', stoog2.foo), ('name', 'larry'), ('age', 60), 
+            ('boo', stoog3.boo), ('foo', stoog3.foo), ('name', 'curly')],
+        )
+        import inspect
+        test = lambda x: not x[0].startswith('__') and inspect.ismethod(x[1])
+        self.assertSequenceEqual(
+            self.qclass(
+                stooges, stoog2, stoog3
+            ).tap(test).deepmembers().sync().value(),
+            [('boo', stooges.boo), ('foo', stooges.foo), ('boo', stoog2.boo),
+            ('foo', stoog2.foo), ('boo', stoog3.boo), ('foo', stoog3.foo)],
+        )
+
     def test_pick(self):
         from stuf import stuf
         stooges = [

File twoq/tests/active/auto/test_filtering.py

 class TestAutoFilterQ(unittest.TestCase, AQMixin, AFilterQMixin):
 
     def setUp(self):
+        self.maxDiff = None
         from twoq.active.filtering import afilterq
         self.qclass = afilterq
 
 class TestAutoCollectQ(unittest.TestCase, AQMixin, ACollectQMixin):
 
     def setUp(self):
+        self.maxDiff = None
         from twoq.active.filtering import acollectq
         self.qclass = acollectq
 
 class TestSyncFilterQ(unittest.TestCase, AQMixin, AFilterQMixin):
 
     def setUp(self):
+        self.maxDiff = None
         from twoq.active.filtering import sfilterq
         self.qclass = sfilterq
 
 class TestSyncCollectQ(unittest.TestCase, AQMixin, ACollectQMixin):
 
     def setUp(self):
+        self.maxDiff = None
         from twoq.active.filtering import scollectq
         self.qclass = scollectq
 

File twoq/tests/active/man/mixins/filtering.py

         class stoog3: #@IgnorePep8
             name = 'curly'
             age = 60
-        test = lambda x: not x.startswith('__')
+        test = lambda x: not x[0].startswith('__')
         manq = self.qclass(
             stooges, stoog2, stoog3
         ).tap(test).members().detap().sync()
             ('age', 60), ('name', 'curly')],
         )
         self.assertFalse(manq.balanced)
+        
+    def test_deepmembers(self):
+        class stooges:
+            name = 'moe'
+            age = 40
+            def boo(self):
+                return 'boo'
+            def foo(self):
+                return 'foo'
+        class stoog2: #@IgnorePep8
+            name = 'larry'
+            age = 50
+            def boo(self):
+                return 'boo'
+            def foo(self):
+                return 'foo'
+        class stoog3: #@IgnorePep8
+            name = 'curly'
+            age = 60
+            def boo(self):
+                return 'boo'
+            def foo(self):
+                return 'foo'
+        test = lambda x: not x[0].startswith('__')
+        manq = self.qclass(stooges, stoog2, stoog3).tap(test).deepmembers()
+        self.assertFalse(manq.balanced)
+        manq.sync()
+        self.assertTrue(manq.balanced)
+        self.assertEqual(
+            manq.value(),
+            [('age', 40), ('boo', stooges.boo), ('foo', stooges.foo),
+            ('name', 'moe'), ('age', 50), ('boo', stoog2.boo),
+            ('foo', stoog2.foo), ('name', 'larry'), ('age', 60), 
+            ('boo', stoog3.boo), ('foo', stoog3.foo), ('name', 'curly')],
+        )
+        self.assertFalse(manq.balanced)
+        import inspect
+        test = lambda x: not x[0].startswith('__') and inspect.ismethod(x[1])
+        manq = self.qclass(stooges, stoog2, stoog3).tap(test).deepmembers()
+        self.assertFalse(manq.balanced)
+        manq.sync()
+        self.assertTrue(manq.balanced)
+        self.assertEqual(
+            manq.value(),
+            [('boo', stooges.boo), ('foo', stooges.foo), ('boo', stoog2.boo),
+            ('foo', stoog2.foo), ('boo', stoog3.boo), ('foo', stoog3.foo)],
+        )
+        self.assertFalse(manq.balanced)
 
     def test_pick(self):
         from stuf import stuf

File twoq/tests/active/man/test_filtering.py

 class TestManFilterQ(unittest.TestCase, MFilterQMixin):
 
     def setUp(self):
+        self.maxDiff = None
         from twoq.active.filtering import mfilterq
         self.qclass = mfilterq