Commits

Lynn Rees  committed 500d2cc Merge

automerge

  • Participants
  • Parent commits 8df85ee, 9a6b92e
  • Branches maint

Comments (0)

Files changed (78)

 a1f0bca5aa50f91d78e061dc864cfab46a6345ef 0.2.1
 9d6762d69bb4260036e05bcaeb47888a12513ab2 0.2.2
 972418cde844b5af3bf3a089e5cee955497f3490 0.2.3
+9e9c2a646a261ee0023af215410251c11e478862 0.2.4
-Copyright (c) 2012 L. C. Rees.  All rights reserved.
+Copyright (c) 2007-2012 L. C. Rees.  All rights reserved.
 
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
 
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+1.  Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+2.  Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+3.  Neither the name of the Portable Site Information Project nor the names
+of its contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
 
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
 include LICENSE.txt
 include MANIFEST.in
 include README.rst
+include packages
+include requirements.txt
 recursive-include callchain *.py

File callchain/__init__.py

 '''callchain: callables and components joined in one big happy callchain'''
 
 from callchain.call import inside, einside
-from callchain.core import ResetLocalMixin, ResetTypeMixin
-from callchain.patterns import Pathways, Branchways, Nameways
-from callchain.settings import DefaultSettings, RequiredSettings
 from callchain.active_auto.chain import (
     chainq as aachainq, callchain as aacallchain)
 from callchain.active_auto.event import (
     chainq as lmchainq, callchain as lamcallchain)
 from callchain.lazy_man.event import (
     eventq as lmeventq, eventchain as lmeventchain)
+from callchain.core import ResetLocalMixin, ResetTypeMixin
+from callchain.patterns import Pathways, Branchways, Nameways
+from callchain.settings import DefaultSettings, RequiredSettings
 
-__version__ = (0, 2, 4)
+__version__ = (0, 2, 5)

File callchain/active_auto/chain.py

 # -*- coding: utf-8 -*-
 '''active auto-balancing chains appconf'''
 
-from appspace.keys import appifies
-from twoq.active.mixins import AutoResultMixin
+from appspace import appifies
+from twoq.active import AutoResultMixin
 
-from callchain.root import RootMixin
-from callchain.keys.root import KRoot
-from callchain.keys.call import KCall
-from callchain.keys.core import KChain
+from callchain.chain import RootMixin
+from callchain.config import Defaults
+from callchain.keys import KRoot, KCall, KChain
 from callchain.patterns import Pathways, Nameways
 from callchain.services.queue import KThings, KResult
 from callchain.call import ChainMixin, PriorityMixin, inside
 
 
 class thingchain(Pathways):
-    link = 'callchain.active_auto.chainlet.chainlink'
+    class logger(Nameways):
+        key = 'callchain.contrib.keys.KLogger'
+        logger = 'callchain.contrib.logger.loglet'
 
 
 @appifies(KThings, KRoot, KChain, KCall)
-@inside(thingchain)
+@inside(thingchain, defaults=Defaults)
 class callchain(RootMixin, ChainMixin, AutoResultMixin):
 
     '''active queued auto-balancing lite call chain'''
 
 
 @appifies(KThings, KRoot, KChain, KCall)
-@inside(thingchain)
+@inside(thingchain, defaults=Defaults)
 class prioritychain(RootMixin, PriorityMixin, AutoResultMixin):
 
     '''active priority queued auto-balancing lite call chain'''
 
 
 class chain(Pathways):
-    link = 'callchain.active_auto.chainlet.chainlink'
+    class logger(Nameways):
+        key = 'callchain.contrib.keys.KLogger'
+        logger = 'callchain.contrib.logger.loglet'
 
     class filter(Nameways):
         key = 'callchain.services.filter.KFilter'
         key = 'callchain.services.filter.KSlice'
         slice = 'callchain.active_auto.chainlet.slicechain'
 
+    class combine(Nameways):
+        key = 'callchain.services.order.KCombine'
+        combine = 'callchain.active_auto.chainlet.combinechain'
+
     class map(Nameways):
         key = 'callchain.services.map.KMap'
         map = 'callchain.active_auto.chainlet.mapchain'
 
 
 @appifies(KResult, KRoot, KChain, KCall)
-@inside(chain)
+@inside(chain, defaults=Defaults)
 class chainq(RootMixin, ChainMixin, AutoResultMixin):
 
     '''active queued auto-balancing call chain'''
 
 
 @appifies(KThings, KRoot, KChain, KCall)
-@inside(chain)
+@inside(chain, defaults=Defaults)
 class priorityq(RootMixin, PriorityMixin, AutoResultMixin):
 
     '''active priority queued auto-balancing lite call chain'''

File callchain/active_auto/chainlet.py

 # -*- coding: utf-8 -*-
 '''active auto-balancing chainlets'''
 
-from appspace.keys import appifies
-from twoq.mixins.filtering import (
-    FilterMixin, CollectMixin, SetMixin, SliceMixin)
-from twoq.mixins.ordering import RandomMixin, OrderMixin
-from twoq.active.mixins import AutoQMixin, AutoResultMixin
-from twoq.mixins.mapping import DelayMixin, RepeatMixin, MapMixin
-from twoq.mixins.reducing import MathMixin, TruthMixin, ReduceMixin
+from appspace import appifies
+from twoq.active import AutoQMixin, AutoResultMixin
+from twoq.mapping import DelayMixin, RepeatMixin, MapMixin
+from twoq.reducing import MathMixin, TruthMixin, ReduceMixin
+from twoq.ordering import RandomMixin, OrderMixin, CombineMixin
+from twoq.filtering import FilterMixin, CollectMixin, SetMixin, SliceMixin
 
-from callchain.keys.call import KCall
-from callchain.keys.core import KChain
-from callchain.keys.root import KConfig
-from callchain.keys.branch import KLinked
-from callchain.services.queue import KResult
+from callchain.services import KResult
 from callchain.call import ChainMixin, PriorityMixin
-from callchain.services.order import KRandom, KOrder
 from callchain.services.map import KDelay, KRepeat, KMap
+from callchain.keys import KCall, KChain, KConfig, KLinked
 from callchain.services.reduce import KMath, KReduce, KTruth
-from callchain.branch import (
+from callchain.chain import (
     BranchMixin, BranchletMixin, ChainletMixin, LinkedMixin)
+from callchain.services.order import KRandom, KOrder, KCombine
 from callchain.services.filter import KCollect, KSet, KSlice, KFilter
 
 __all__ = (
     'mathchain', 'truthchain', 'reducechain', 'collectchain', 'setchain',
     'slicechain', 'filterchain', 'delaychain', 'repeatchain', 'mapchain',
-    'randomchain', 'orderchain',
+    'randomchain', 'orderchain', 'chainlet', 'combinechain',
 )
 
 
     DelayMixin,
 ):
 
-    '''auto-balancing delayed mapping chainlet'''
+    '''delayed mapping chainlet'''
 
 
 @appifies(KRepeat)
     RepeatMixin,
 ):
 
-    '''auto-balancing repeat chainlet'''
+    '''repeat chainlet'''
 
 
 @appifies(KMap)
     MapMixin,
 ):
 
-    '''auto-balancing mapping chainlet'''
+    '''mapping chainlet'''
 
 
 @appifies(KCollect)
     CollectMixin,
 ):
 
-    '''auto-balancing collecting chainlet'''
+    '''collecting chainlet'''
 
 
 @appifies(KSet)
     SetMixin,
 ):
 
-    '''auto-balancing seting chainlet'''
+    '''seting chainlet'''
 
 
 @appifies(KSlice)
     SliceMixin,
 ):
 
-    '''auto-balancing slicing chainlet'''
+    '''slicing chainlet'''
 
 
 @appifies(KFilter)
     FilterMixin,
 ):
 
-    '''auto-balancing filtering chainlet'''
+    '''filtering chainlet'''
 
 
 @appifies(KRandom)
     RandomMixin,
 ):
 
-    '''auto-balancing randomizing chainlet'''
+    '''randomizing chainlet'''
 
 
 @appifies(KOrder)
     OrderMixin,
 ):
 
-    '''auto-balancing ordering chainlet'''
+    '''ordering chainlet'''
 
 
 @appifies(KMath)
     MathMixin,
 ):
 
-    '''auto-balancing mathing chainlet'''
+    '''mathing chainlet'''
 
 
 @appifies(KReduce)
     ReduceMixin,
 ):
 
-    '''auto-balancing reducing chainlet'''
+    '''reducing chainlet'''
+
+
+@appifies(KCombine)
+class combinechain(
+    ChainletMixin,
+    BranchMixin,
+    BranchletMixin,
+    AutoQMixin,
+    CombineMixin,
+):
+
+    '''combining chainlet'''
 
 
 @appifies(KTruth)
     TruthMixin,
 ):
 
-    '''auto-balancing truthing chainlet'''
+    '''truthing chainlet'''
 
 
 @appifies(KLinked, KConfig, KCall, KChain, KResult)
 class chainlink(BranchMixin, LinkedMixin, ChainMixin, AutoResultMixin):
 
-    '''auto-balancing linked chain'''
+    '''linked chain'''
 
 
 @appifies(KLinked, KConfig, KCall, KChain, KResult)
-class prilink(BranchMixin, LinkedMixin, PriorityMixin, AutoResultMixin):
+class prioritylink(BranchMixin, LinkedMixin, PriorityMixin, AutoResultMixin):
 
-    '''auto-balancing priority linked chain'''
+    '''priority linked chain'''

File callchain/active_auto/event.py

 # -*- coding: utf-8 -*-
 '''active auto-balancing event chains appconf'''
 
-from appspace.keys import appifies
-from twoq.active.mixins import AutoResultMixin
+from appspace import appifies
+from twoq.active import AutoResultMixin
 
-from callchain.keys.core import KEvent
-from callchain.root import EventRootMixin
-from callchain.keys.root import KEventRoot
-from callchain.keys.call import KEventCall
+from callchain.config import Defaults
+from callchain.chain import EventRootMixin
 from callchain.services.apps import events
 from callchain.call import EventMixin, einside
+from callchain.services import KThings, KResult
 from callchain.patterns import Pathways, Nameways
-from callchain.services.queue import KThings, KResult
+from callchain.keys import KEvent, KEventRoot, KEventCall
 
 ###############################################################################
 ## thing event chain ##########################################################
 class thingevent(Pathways):
     chain = 'callchain.active_auto.chainlet.chainlink'
 
+    class logger(Nameways):
+        key = 'callchain.contrib.keys.KLogger'
+        logger = 'callchain.contrib.logger.loglet'
+
 
 @appifies(KThings, KEventRoot, KEvent, KEventCall)
-@einside(thingevent, events)
+@einside(thingevent, events, defaults=Defaults)
 class eventchain(EventRootMixin, EventMixin, AutoResultMixin):
 
     '''active queued auto-balancing lite event chain'''
 class event(Pathways):
     chain = 'callchain.active_auto.chainlet.chainlink'
 
+    class logger(Nameways):
+        key = 'callchain.contrib.keys.KLogger'
+        logger = 'callchain.contrib.logger.loglet'
+
     class filter(Nameways):
         key = 'callchain.services.filter.KFilter'
         filter = 'callchain.active_auto.eventlet.filterevent'
         key = 'callchain.services.filter.KCollect'
         collect = 'callchain.active_auto.eventlet.collectevent'
 
+    class combine(Nameways):
+        key = 'callchain.services.order.KCombine'
+        combine = 'callchain.active_auto.eventlet.combineevent'
+
     class set(Nameways):
         key = 'callchain.services.filter.KSet'
         set = 'callchain.active_auto.eventlet.setevent'
 
 
 @appifies(KResult, KEventRoot, KEvent, KEventCall)
-@einside(event, events)
+@einside(event, events, defaults=Defaults)
 class eventq(EventRootMixin, EventMixin, AutoResultMixin):
 
     '''active queued auto-balancing event chain'''

File callchain/active_auto/eventlet.py

 # -*- coding: utf-8 -*-
 '''active auto-balancing eventlets'''
 
-from appspace.keys import appifies
-from twoq.mixins.filtering import (
-    FilterMixin, CollectMixin, SetMixin, SliceMixin)
-from twoq.mixins.ordering import RandomMixin, OrderMixin
-from twoq.active.mixins import AutoQMixin, AutoResultMixin
-from twoq.mixins.mapping import DelayMixin, RepeatMixin, MapMixin
-from twoq.mixins.reducing import MathMixin, TruthMixin, ReduceMixin
+from appspace import appifies
+from twoq.active import AutoQMixin, AutoResultMixin
+from twoq.mapping import DelayMixin, RepeatMixin, MapMixin
+from twoq.reducing import MathMixin, TruthMixin, ReduceMixin
+from twoq.ordering import RandomMixin, OrderMixin, CombineMixin
+from twoq.filtering import FilterMixin, CollectMixin, SetMixin, SliceMixin
 
 from callchain.call import EventMixin
-from callchain.keys.core import KEvent
-from callchain.keys.root import KConfig
-from callchain.keys.branch import KLinked
-from callchain.keys.call import KEventCall
 from callchain.services.queue import KResult
-from callchain.services.order import KRandom, KOrder
 from callchain.services.map import KDelay, KRepeat, KMap
 from callchain.services.reduce import KMath, KReduce, KTruth
-from callchain.branch import (
+from callchain.keys import KEventCall, KEvent, KLinked, KConfig
+from callchain.services.order import KRandom, KOrder, KCombine
+from callchain.chain import (
     ChainletMixin, EventBranchMixin, BranchletMixin, LinkedMixin)
 from callchain.services.filter import KCollect, KSet, KSlice, KFilter
 
 __all__ = (
     'mathevent', 'truthevent', 'reduceevent', 'collectevent', 'setevent',
     'sliceevent', 'filterevent', 'delayevent', 'repeatevent', 'mapevent',
-    'randomevent', 'orderevent', 'eventlet'
+    'randomevent', 'orderevent', 'eventlet', 'combineevent'
 )
 
 
     '''reducing eventlet'''
 
 
+@appifies(KCombine)
+class combineevent(eventlet, CombineMixin):
+
+    '''combining eventlet'''
+
+
 @appifies(KTruth)
 class truthevent(eventlet, TruthMixin):
 

File callchain/active_man/chain.py

 # -*- coding: utf-8 -*-
 '''active manually balanced chains appconf'''
 
-from appspace.keys import appifies
-from twoq.active.mixins import ManResultMixin
+from appspace import appifies
+from twoq.active import ManResultMixin
 
-from callchain.root import RootMixin
-from callchain.keys.root import KRoot
-from callchain.keys.call import KCall
-from callchain.keys.core import KChain
+from callchain.chain import RootMixin
+from callchain.config import Defaults
+from callchain.services import KThings, KResult
+from callchain.keys import KRoot, KCall, KChain
 from callchain.patterns import Pathways, Nameways
-from callchain.services.queue import KThings, KResult
 from callchain.call import ChainMixin, PriorityMixin, inside
 
 ###############################################################################
 
 
 class thingchain(Pathways):
-    link = 'callchain.active_man.chainlet.chainlink'
+    class logger(Nameways):
+        key = 'callchain.contrib.keys.KLogger'
+        logger = 'callchain.contrib.logger.loglet'
 
 
 @appifies(KThings, KRoot, KChain, KCall)
-@inside(thingchain)
+@inside(thingchain, defaults=Defaults)
 class callchain(RootMixin, ChainMixin, ManResultMixin):
 
     ''''active queued manually balanced lite call chain'''
 
 
 @appifies(KThings, KRoot, KChain, KCall)
-@inside(thingchain)
+@inside(thingchain, defaults=Defaults)
 class prioritychain(RootMixin, PriorityMixin, ManResultMixin):
 
     '''active priority queued manually balanced lite call chain'''
 
 
 class chain(Pathways):
-    link = 'callchain.active_man.chainlet.chainlink'
+    class logger(Nameways):
+        key = 'callchain.contrib.keys.KLogger'
+        logger = 'callchain.contrib.logger.loglet'
 
     class filter(Nameways):
         key = 'callchain.services.filter.KFilter'
         key = 'callchain.services.filter.KSlice'
         slice = 'callchain.active_man.chainlet.slicechain'
 
+    class combine(Nameways):
+        key = 'callchain.services.order.KCombine'
+        combine = 'callchain.active_man.chainlet.combinechain'
+
     class map(Nameways):
         key = 'callchain.services.map.KMap'
         map = 'callchain.active_man.chainlet.mapchain'
 
 
 @appifies(KResult, KRoot, KChain, KCall)
-@inside(chain)
+@inside(chain, defaults=Defaults)
 class chainq(RootMixin, ChainMixin, ManResultMixin):
 
     '''active queued manually balanced call chain'''
 
 
 @appifies(KThings, KRoot, KChain, KCall)
-@inside(chain)
+@inside(chain, defaults=Defaults)
 class priorityq(RootMixin, PriorityMixin, ManResultMixin):
 
     '''active priority queued manually balanced lite call chain'''

File callchain/active_man/chainlet.py

 # -*- coding: utf-8 -*-
 '''active manually balanced chainlets'''
 
-from appspace.keys import appifies
-from twoq.mixins.filtering import (
-    FilterMixin, CollectMixin, SetMixin, SliceMixin)
-from twoq.active.mixins import ManQMixin, ManResultMixin
-from twoq.mixins.ordering import RandomMixin, OrderMixin
-from twoq.mixins.mapping import DelayMixin, RepeatMixin, MapMixin
-from twoq.mixins.reducing import MathMixin, TruthMixin, ReduceMixin
+from appspace import appifies
+from twoq.active import ManQMixin, ManResultMixin
+from twoq.mapping import DelayMixin, RepeatMixin, MapMixin
+from twoq.reducing import MathMixin, TruthMixin, ReduceMixin
+from twoq.ordering import RandomMixin, OrderMixin, CombineMixin
+from twoq.filtering import FilterMixin, CollectMixin, SetMixin, SliceMixin
 
-from callchain.keys.call import KCall
-from callchain.keys.core import KChain
-from callchain.keys.root import KConfig
-from callchain.keys.branch import KLinked
-from callchain.services.queue import KResult
+from callchain.services import KResult
 from callchain.call import ChainMixin, PriorityMixin
-from callchain.services.order import KRandom, KOrder
 from callchain.services.map import KDelay, KRepeat, KMap
+from callchain.keys import KCall, KChain, KConfig, KLinked
 from callchain.services.reduce import KMath, KReduce, KTruth
-from callchain.branch import (
+from callchain.chain import (
     BranchMixin, BranchletMixin, ChainletMixin, LinkedMixin)
+from callchain.services.order import KRandom, KOrder, KCombine
 from callchain.services.filter import KCollect, KSet, KSlice, KFilter
 
 __all__ = (
     DelayMixin,
 ):
 
-    '''manually balanced delayed mapping chainlet'''
+    '''delayed mapping chainlet'''
 
 
 @appifies(KRepeat)
     RepeatMixin,
 ):
 
-    '''manually balanced repeat chainlet'''
+    '''repeat chainlet'''
 
 
 @appifies(KMap)
     MapMixin,
 ):
 
-    '''manually balanced mapping chainlet'''
+    '''mapping chainlet'''
 
 
 @appifies(KCollect)
     CollectMixin,
 ):
 
-    '''manually balanced collecting chainlet'''
+    '''collecting chainlet'''
 
 
 @appifies(KSet)
     SetMixin,
 ):
 
-    '''manually balanced setting chainlet'''
+    '''seting chainlet'''
 
 
 @appifies(KSlice)
     SliceMixin,
 ):
 
-    '''manually balanced slicing chainlet'''
+    '''slicing chainlet'''
 
 
 @appifies(KFilter)
     FilterMixin,
 ):
 
-    '''manually balanced filtering chainlet'''
+    '''filtering chainlet'''
 
 
 @appifies(KRandom)
     RandomMixin,
 ):
 
-    '''manually balanced randomizing chainlet'''
+    '''randomizing chainlet'''
 
 
 @appifies(KOrder)
     OrderMixin,
 ):
 
-    '''manually balanced ordering chainlet'''
+    '''ordering chainlet'''
 
 
 @appifies(KMath)
     MathMixin,
 ):
 
-    '''manually balanced mathing chainlet'''
+    '''mathing chainlet'''
 
 
 @appifies(KReduce)
     ReduceMixin,
 ):
 
-    '''manually balanced reducing chainlet'''
+    '''reducing chainlet'''
+
+
+@appifies(KCombine)
+class combinechain(
+    ChainletMixin,
+    BranchMixin,
+    BranchletMixin,
+    ManQMixin,
+    CombineMixin,
+):
+
+    '''combining chainlet'''
 
 
 @appifies(KTruth)
     TruthMixin,
 ):
 
-    '''manually balanced truthing chainlet'''
+    '''truthing chainlet'''
 
 
 @appifies(KLinked, KConfig, KCall, KChain, KResult)
 class chainlink(BranchMixin, LinkedMixin, ChainMixin, ManResultMixin):
 
-    '''manually balanced linked chain'''
+    '''linked chain'''
 
 
 @appifies(KLinked, KConfig, KCall, KChain, KResult)
-class prilink(BranchMixin, LinkedMixin, PriorityMixin, ManResultMixin):
+class prioritylink(BranchMixin, LinkedMixin, PriorityMixin, ManResultMixin):
 
-    '''manually balanced priority linked chain'''
+    '''priority linked chain'''

File callchain/active_man/event.py

 # -*- coding: utf-8 -*-
 '''active manually balanced event chains appconf'''
 
-from appspace.keys import appifies
-from twoq.active.mixins import ManResultMixin
+from appspace import appifies
+from twoq.active import ManResultMixin
 
-from callchain.keys.core import KEvent
-from callchain.root import EventRootMixin
-from callchain.keys.root import KEventRoot
-from callchain.keys.call import KEventCall
+from callchain.config import Defaults
+from callchain.chain import EventRootMixin
 from callchain.services.apps import events
 from callchain.call import EventMixin, einside
+from callchain.services import KThings, KResult
 from callchain.patterns import Pathways, Nameways
-from callchain.services.queue import KThings, KResult
+from callchain.keys import KEvent, KEventRoot, KEventCall
 
 ###############################################################################
 ## thing event chain ##########################################################
 class thingevent(Pathways):
     chain = 'callchain.active_man.chainlet.chainlink'
 
+    class logger(Nameways):
+        key = 'callchain.contrib.keys.KLogger'
+        logger = 'callchain.contrib.logger.loglet'
+
 
 @appifies(KThings, KEventRoot, KEvent, KEventCall)
-@einside(thingevent, events)
+@einside(thingevent, events, defaults=Defaults)
 class eventchain(EventRootMixin, EventMixin, ManResultMixin):
 
     '''active queued manually balanced lite event chain'''
 class event(Pathways):
     chain = 'callchain.active_man.chainlet.chainlink'
 
+    class logger(Nameways):
+        key = 'callchain.contrib.keys.KLogger'
+        logger = 'callchain.contrib.logger.loglet'
+
     class filter(Nameways):
         key = 'callchain.services.filter.KFilter'
         filter = 'callchain.active_man.eventlet.filterevent'
         key = 'callchain.services.filter.KCollect'
         collect = 'callchain.active_man.eventlet.collectevent'
 
+    class combine(Nameways):
+        key = 'callchain.services.order.KCombine'
+        combine = 'callchain.active_man.eventlet.combineevent'
+
     class set(Nameways):
         key = 'callchain.services.filter.KSet'
         set = 'callchain.active_man.eventlet.setevent'
 
 
 @appifies(KResult, KEventRoot, KEvent, KEventCall)
-@einside(event, events)
+@einside(event, events, defaults=Defaults)
 class eventq(EventRootMixin, EventMixin, ManResultMixin):
 
     '''active queued manually balanced event chain'''

File callchain/active_man/eventlet.py

 # -*- coding: utf-8 -*-
 '''active manually balanced eventlets'''
 
-from appspace.keys import appifies
-from twoq.mixins.filtering import (
-    FilterMixin, CollectMixin, SetMixin, SliceMixin)
-from twoq.mixins.ordering import RandomMixin, OrderMixin
-from twoq.active.mixins import ManQMixin, ManResultMixin
-from twoq.mixins.mapping import DelayMixin, RepeatMixin, MapMixin
-from twoq.mixins.reducing import MathMixin, TruthMixin, ReduceMixin
+from appspace import appifies
+from twoq.active import ManQMixin, ManResultMixin
+from twoq.mapping import DelayMixin, RepeatMixin, MapMixin
+from twoq.reducing import MathMixin, TruthMixin, ReduceMixin
+from twoq.ordering import RandomMixin, OrderMixin, CombineMixin
+from twoq.filtering import FilterMixin, CollectMixin, SetMixin, SliceMixin
 
 from callchain.call import EventMixin
-from callchain.keys.core import KEvent
-from callchain.keys.root import KConfig
-from callchain.keys.branch import KLinked
-from callchain.keys.call import KEventCall
 from callchain.services.queue import KResult
-from callchain.services.order import KRandom, KOrder
 from callchain.services.map import KDelay, KRepeat, KMap
 from callchain.services.reduce import KMath, KReduce, KTruth
+from callchain.keys import KEventCall, KEvent, KLinked, KConfig
+from callchain.services.order import KRandom, KOrder, KCombine
+from callchain.chain import (
+    ChainletMixin, EventBranchMixin, BranchletMixin, LinkedMixin)
 from callchain.services.filter import KCollect, KSet, KSlice, KFilter
-from callchain.branch import (
-    ChainletMixin, EventBranchMixin, BranchletMixin, LinkedMixin)
 
 __all__ = (
     'mathevent', 'truthevent', 'reduceevent', 'collectevent', 'setevent',
     '''reducing eventlet'''
 
 
+@appifies(KCombine)
+class combineevent(eventlet, CombineMixin):
+
+    '''combining eventlet'''
+
+
 @appifies(KTruth)
 class truthevent(eventlet, TruthMixin):
 

File callchain/branch.py

-# -*- coding: utf-8 -*-
-'''callchain branch mixins'''
-
-from itertools import chain
-
-from stuf.utils import lazy
-from appspace.keys import NoAppError
-
-from callchain.managers import Events
-from callchain.core import ResetLocalMixin, CoreMixin
-
-
-class BranchMixin(ResetLocalMixin):
-
-    ''''branch mixin'''
-
-    def _setup(self, root):
-        '''
-        configure branch
-
-        @param root: root chain
-        '''
-        super(BranchMixin, self)._setup(root)
-        # root object
-        self.root = root
-        # root internal appspace manager
-        self._M = root._M
-        # root internal global settings
-        self._G = root._G
-        # root external appspace manager
-        self.M = root.M
-
-
-class EventBranchMixin(BranchMixin):
-
-    '''event branch mixin'''
-
-    @lazy
-    def E(self):
-        '''local event registry'''
-        return Events('events')
-
-    def _eventq(self, event):
-        '''
-        linked chain bound to `event`
-
-        @param event: event label
-        '''
-        # fetch linked chain bound to event
-        key = self.root.event(event)
-        queue = self.E.get(key)
-        if queue is None:
-            # create linked chain if nonexistent
-            queue = self._linkedchain
-            self.E.on(key, queue)
-        return queue
-
-    def _event(self, event):
-        '''
-        calls bound to `event`
-
-        @param event: event label
-        '''
-        key = self.root.event(event)
-        return chain(self.E.events(key), self.root.E.events(key))
-
-
-class BranchletMixin(CoreMixin):
-
-    '''chainlet mixin'''
-
-    def _setup(self, root):
-        '''
-        configure chainlet
-
-        @param root: root chain
-        '''
-        super(BranchletMixin, self)._setup(root)
-        # sync with root postitional arguments
-        self._args = root._args
-        # sync with root keyword arguments
-        self._kw = root._kw
-        # sync with root callable
-        self._call = root._call
-        # sync with root incoming things and outgoing things
-        self.inclear().extend(root.incoming).outextend(root.outgoing)
-
-
-class LinkedMixin(ResetLocalMixin):
-
-    '''linked chain mixin'''
-
-    def close(self):
-        '''close out linked chain and switch to root chain'''
-        return self.root.back(self)
-
-
-class ChainletMixin(ResetLocalMixin):
-
-    '''chainlet mixin'''
-
-    def _load(self, label):
-        '''
-        silent internal switch back...
-
-        @param label: appspaced thing label
-        '''
-        # fetch appspaced thing...
-        try:
-            return super(ChainletMixin, self)._load(label)
-        # ...or revert to root chain
-        except NoAppError:
-            return getattr(self.back(), label)
-
-    def _syncback(self, key, value):
-        '''
-        sync chainlet with root chain
-
-        @param key: key of value
-        @param value: value of value
-        '''
-        self.__dict__[key] = self.root.__dict__[key] = value
-
-    def back(self):
-        '''switch back to root chain'''
-        return self.root.back(self)

File callchain/call.py

 # -*- coding: utf-8 -*-
 '''callchain call mixins'''
 
-from itertools import chain
+from functools import partial
+from itertools import chain, count
 
 from twoq.support import isstring
 from appspace.builders import Appspace
 from callchain.managers import Events
 from callchain.core import ConfigMixin
 from callchain.patterns import Pathways
-from callchain.keys.base import NoServiceError
-from callchain.support import Queue, PriorityQueue, Empty, cury
+from callchain.keys import NoServiceError
+from callchain.support import Empty, Queue, PriorityQueue, total_ordering
+
+
+@total_ordering
+class Partial(object):
+
+    '''partial wrapper'''
+
+    __slots__ = ('_call', 'count', '__call__')
+
+    counter = count()
+
+    def __init__(self, call, *args, **settings):
+        self.__call__ = partial(call, *args, **settings)
+        priority = settings.pop('priority', None)
+        self.count = next(self.counter) if priority is None else priority
+
+    def __lt__(self, other):
+        return self.count < other
+
 
 ###############################################################################
 ## chain components ###########################################################
             key = _M.service(label)
             return getattr(_M.get(key, key)(self), label)
         except NoServiceError:
-            # ...or lookup other appspaced thing
+            # ...or lookup some other appspaced thing
             return super(CallMixin, self)._load(label)
 
     @lazy
 
     def _setup(self, root):
         '''call chain setup'''
-        # chain label
-        self._CALLQ = '_chain'
         # call chain queue
         self._chain = self._queue()
 
         @param key: appspace key (default: False)
         '''
         self._chain.put(
-            cury(call, *(key,) + args, **kw) if not isstring(call)
-            else cury(self.M.get(call, key), *args, **kw)
+            Partial(call, *(key,) + args, **kw) if not isstring(call)
+            else Partial(self.M.get(call, key), *args, **kw)
         )
         return self
 
         @param key: appspace key (default: False)
         '''
         self._chain.put(
-            cury(call, *(key,) + args, **kw)
+            Partial(call, *(key,) + args, **kw)
             if not isstring(call)
-            else cury(self.M.get(call, key), *args, **kw)
+            else Partial(self.M.get(call, key), *args, **kw)
         )
         return self
 
 
         @param *events: event labels
         '''
-        with self.ctx1(workq='_work'):
+        with self.ctx1(workq=self._WORKVAR):
             return self.exhaustcall(
                 lambda x: x(), self._xtend(self._events(*events))._iterable,
             )

File callchain/chain.py

+# -*- coding: utf-8 -*-
+'''root chain mixins'''
+
+from itertools import chain
+
+from appspace.keys import NoAppError
+from stuf.utils import lazy, lazy_class
+
+from callchain.core import CoreMixin
+from callchain.managers import Events
+from callchain.patterns import Pathways
+from callchain.core import ResetLocalMixin
+
+
+class RootMixin(ResetLocalMixin):
+
+    '''root chain mixin'''
+
+    def __init__(self, pattern=None, required=None, defaults=None, **kw):
+        '''
+        init
+
+        @param pattern: pattern configuration or appspace label (default: None)
+        @param required: required settings (default: None)
+        @param defaults: default settings (default: None)
+        '''
+        super(RootMixin, self).__init__(pattern)
+        if pattern is not None:
+            # external appspace
+            self.M = Pathways.appspace(pattern, required, defaults)
+            # freeze external appspace global settings
+            self.M.freeze(kw)
+        else:
+            self.M = None
+
+    def __call__(self, *args):
+        '''new chain session'''
+        # clear call chain and queues and extend incoming things
+        return self.clear().extend(args)
+
+    @lazy_class
+    def port(self):
+        '''python 2.x <-> python 3.x porting helper'''
+        from twoq.support import port
+        return port
+
+    def back(self, branch):
+        '''
+        handle switch from branch chain
+
+        @param branch: branch chain
+        '''
+        self.clear()
+        # sync with branch callable
+        self._call = branch._call
+        # sync with branch postitional arguments
+        self._args = branch._args
+        # sync with branch keyword arguments
+        self._kw = branch._kw
+        # sync with branch incoming and outgoing things
+        return self.extend(branch.incoming).outextend(branch.outgoing)
+
+
+class EventRootMixin(RootMixin):
+
+    '''root event mixin'''
+
+    def __init__(
+        self,
+        patterns=None,
+        events=None,
+        required=None,
+        defaults=None,
+        *args,
+        **kw
+    ):
+        '''
+        init
+
+        @param patterns: pattern config or eventspace label (default: None)
+        @param events: events configuration (default: None)
+        @param required: required settings (default: None)
+        @param defaults: default settings (default: None)
+        '''
+        super(EventRootMixin, self).__init__(
+            patterns, required, defaults, *args, **kw
+        )
+        # update event registry with any other events
+        if events is not None:
+            self.E.update(events)
+
+    def _eventq(self, event):
+        '''
+        linked chain bound to `event`
+
+        @param event: event label
+        '''
+        key = self.E.event(event)
+        # fetch linked chain bound to event
+        queue = self.E.get(key)
+        if queue is None:
+            # create linked chain if nonexistent
+            queue = self._linkedchain
+            self.E.on(key, queue)
+        return queue
+
+    def _event(self, event):
+        '''
+        calls bound to `event`
+
+        @param event: event label
+        '''
+        return self.E.events(self.E.event(event))
+
+    def event(self, event):
+        '''
+        create or fetch `event`
+
+        @param event: event label
+        '''
+        self.E.event(event)
+        return self
+
+    def unevent(self, event):
+        '''
+        drop `event`
+
+        @param event: event label
+        '''
+        self.E.unevent(event)
+        return self
+
+
+class BranchMixin(ResetLocalMixin):
+
+    ''''branch mixin'''
+
+    def _setup(self, root):
+        '''
+        configure branch
+
+        @param root: root chain
+        '''
+        super(BranchMixin, self)._setup(root)
+        # root object
+        self.root = root
+        # root internal appspace manager
+        self._M = root._M
+        # root internal global settings
+        self._G = root._G
+        # root external appspace manager
+        self.M = root.M
+
+
+class EventBranchMixin(BranchMixin):
+
+    '''event branch mixin'''
+
+    @lazy
+    def E(self):
+        '''local event registry'''
+        return Events('events')
+
+    def _eventq(self, event):
+        '''
+        linked chain bound to `event`
+
+        @param event: event label
+        '''
+        # fetch linked chain bound to event
+        key = self.root.event(event)
+        queue = self.E.get(key)
+        if queue is None:
+            # create linked chain if nonexistent
+            queue = self._linkedchain
+            self.E.on(key, queue)
+        return queue
+
+    def _event(self, event):
+        '''
+        calls bound to `event`
+
+        @param event: event label
+        '''
+        key = self.root.event(event)
+        return chain(self.E.events(key), self.root.E.events(key))
+
+
+class BranchletMixin(CoreMixin):
+
+    '''chainlet mixin'''
+
+    def _setup(self, root):
+        '''
+        configure chainlet
+
+        @param root: root chain
+        '''
+        super(BranchletMixin, self)._setup(root)
+        # sync with root postitional arguments
+        self._args = root._args
+        # sync with root keyword arguments
+        self._kw = root._kw
+        # sync with root callable
+        self._call = root._call
+        # sync with root incoming things and outgoing things
+        self.inclear().extend(root.incoming).outextend(root.outgoing)
+
+
+class LinkedMixin(ResetLocalMixin):
+
+    '''linked chain mixin'''
+
+    def close(self):
+        '''close out linked chain and switch to root chain'''
+        return self.root.back(self)
+
+
+class ChainletMixin(ResetLocalMixin):
+
+    '''chainlet mixin'''
+
+    def _load(self, label):
+        '''
+        silent internal switch back...
+
+        @param label: appspaced thing label
+        '''
+        # fetch appspaced thing...
+        try:
+            return super(ChainletMixin, self)._load(label)
+        # ...or revert to root chain
+        except NoAppError:
+            return getattr(self.back(), label)
+
+    def _syncback(self, key, value):
+        '''
+        sync chainlet with root chain
+
+        @param key: key of value
+        @param value: value of value
+        '''
+        self.__dict__[key] = self.root.__dict__[key] = value
+
+    def back(self):
+        '''switch back to root chain'''
+        return self.root.back(self)

File callchain/config.py

+# -*- coding: utf-8 -*-
+'''callchain configuration'''
+
+import logging
+
+from callchain.settings import DefaultSettings
+
+
+class Defaults(DefaultSettings):
+    class log:
+        # log level
+        LEVEL = logging.DEBUG
+        # default log formats
+        DATE = '%a, %d %b %Y %H:%M:%S'
+        # log entry format
+        ENTRY = '%(name)s: %(asctime)s %(levelname)-4s %(message)s'
+
+        class stream:
+            ENABLED = True
+            # redirect STDOUT to the logger?
+            STDOUT = True
+            # sets log level STDOUT is displayed under
+            LEVEL = logging.DEBUG
+
+        class email:
+            ENABLED = False
+            # mail server
+            SERVER = ''
+            # from email address
+            FROM = ''
+            # to email address
+            TO = ''
+            # email subject
+            SUBJECT = ''
+
+        class rotating:
+            ENABLED = False
+            # log file path
+            FILE = ''
+            # number of backups to keep
+            BACKUPS = 1
+            # interval to backup log file
+            INTERVAL = 'h'
+
+        class http:
+            ENABLED = False
+            # web server host
+            HTTP = ''
+            # web url
+            URL = ''
+            # http method
+            METHOD = 'GET'
+
+        class syslog:
+            ENABLED = False
+            # syslog host
+            HOST = 'localhost'
+            # syslog port
+            PORT = 514
+            # syslog facility
+            FACILITY = 'LOG_USER'

File callchain/contrib/__init__.py

+# -*- coding: utf-8 -*-
+'''callchain extensions'''

File callchain/contrib/keys.py

+# -*- coding: utf-8 -*-
+'''callchain contrib keys'''
+
+#from appspace.keys import Attribute
+
+from callchain.services.queue import KService
+
+
+class KLogger(KService):
+
+    '''logging key'''
+
+#    logger = Attribute('the logger')
+
+    def debug(msg):  # @NoSelf
+        '''log debug message'''
+
+    def warning(msg):  # @NoSelf
+        '''log warning message'''
+
+    def info(msg):  # @NoSelf
+        '''log info message'''
+
+    def error(msg):  # @NoSelf
+        '''log error message'''
+
+    def critical(msg):  # @NoSelf
+        '''log critical message'''
+
+    def exception(msg):  # @NoSelf
+        '''log exception message'''

File callchain/contrib/logger.py

+# -*- coding: utf-8 -*-
+'''callchain logging'''
+
+import sys
+import logging
+from logging.handlers import HTTPHandler, SysLogHandler
+from logging.handlers import TimedRotatingFileHandler, SMTPHandler
+
+from appspace import appifies
+from stuf.utils import clsname
+
+from callchain.core import CoreMixin
+from callchain.chain import BranchMixin, ChainletMixin
+
+from callchain.contrib.keys import KLogger
+
+
+class _LogStdout(object):
+
+    '''file-like object for sending stdout output to a logger.'''
+
+    def __init__(self, logger, level=logging.DEBUG):
+        '''
+        build stdout logger
+
+        @param logger: logger
+        @param level: logging level (default: `logging.DEBUG`
+        '''
+        # Set logger level
+        if level == logging.DEBUG:
+            self.logger = logger.debug
+        elif level == logging.CRITICAL:
+            self.logger = logger.critical
+        elif level == logging.ERROR:
+            self.logger = logger.error
+        elif level == logging.WARNING:
+            self.logger = logger.warning
+        elif level == logging.INFO:
+            self.logger = logger.info
+
+    def write(self, msg):
+        '''Writes non-empty strings to logger.'''
+        if msg.lstrip().rstrip() != '':
+            self.logger(msg)
+
+
+@appifies(KLogger)
+class loglet(ChainletMixin, BranchMixin, CoreMixin):
+
+    '''logging chainlet'''
+
+    def _setup(self, root):
+        '''
+        build logger
+
+        @param root: root chain
+        '''
+        super(loglet, self)._setup(root)
+        settings = self._G.log
+        self.logger = logging.getLogger(settings.get('NAME', clsname(root)))
+        # set logging level
+        self.logger.setLevel(settings.LEVEL)
+        # log formatter
+        format = logging.Formatter(
+            # log entry format
+            settings.ENTRY,
+            # date format
+            settings.DATE,
+        )
+        # coroutine to setup log handlers
+        def setlog(thislog): #@IgnorePep8
+            thislog.setFormatter(format)
+            self.logger.addHandler(thislog)
+        # log to STDOUT
+        if settings.stream:
+            setlog(logging.StreamHandler())
+            # redirect STDOUT to the logger
+            if settings.stream.STDOUT:
+                # sets log level STDOUT is displayed under
+                sys.stdout = _LogStdout(self.logger, settings.stream.LEVEL)
+        # log to a rotating file that with periodic backup deletions
+        if settings.rotating.ENABLED:
+            setlog(TimedRotatingFileHandler(
+                # log file path
+                settings.rotating.PATH,
+                # interval to backup log file
+                settings.rotating.INTERVAL,
+                # number of backups to keep
+                settings.rotating.BACKUPS,
+            ))
+        # email log entries to an email address
+        if settings.email.ENABLED:
+            setlog(SMTPHandler(
+                # mail server
+                settings.email.HOST,
+                # from email address
+                settings.email.FROM,
+                # to email address
+                settings.email.TO,
+                # email subject
+                settings.email.SUBJECT,
+            ))
+        # send log entries to a web server
+        if settings.http.ENABLED:
+            setlog(HTTPHandler(
+                # web server host
+                settings.http.HOST,
+                # web url
+                settings.http.URL,
+                # http method
+                settings.http.METHOD,
+            ))
+        # send log entries to syslog
+        if settings.syslog.ENABLED:
+            setlog(SysLogHandler(
+                # syslog host
+                (settings.syslog.HOST, settings.syslog.PORT),
+                # syslog user
+                settings.syslog.FACILITY,
+            ))
+        assert self.logger.handlers, 'configure at least one logging handler'
+
+    def debug(self, msg):
+        '''log debug message'''
+        self.logger.debug(msg)
+        return self
+
+    def warning(self, msg):
+        '''log warning message'''
+        self.logger.warning(msg)
+        return self
+
+    def info(self, msg):
+        '''log info message'''
+        self.logger.info(msg)
+        return self
+
+    def error(self, msg):
+        '''log error message'''
+        self.logger.error(msg)
+        return self
+
+    def critical(self, msg):
+        '''log critical message'''
+        self.logger.critical(msg)
+        return self
+
+    def exception(self, msg):
+        '''log exception message'''
+        self.logger.exception(msg)
+        return self

File callchain/core.py

 # -*- coding: utf-8 -*-
-'''callchain core'''
+'''callchain core mixins'''
 
 from threading import local
 from operator import setitem

File callchain/generic.py

+# -*- coding: utf-8 -*-
+'''callchain generic mixins'''
+
+from callchain.core import CoreMixin
+from callchain.chain import (
+    RootMixin, EventRootMixin, BranchMixin, ChainletMixin, EventBranchMixin,
+    LinkedMixin)
+from callchain.call import ChainMixin, PriorityMixin, EventMixin
+
+###############################################################################
+## chainlet ###################################################################
+###############################################################################
+
+
+class chainlet(ChainletMixin, BranchMixin, CoreMixin):
+
+    '''generic chainlet mixin'''
+
+
+class eventlet(ChainletMixin, EventBranchMixin, CoreMixin):
+
+    '''generic eventlet mixin'''
+
+
+###############################################################################
+## linked chain ###############################################################
+###############################################################################
+
+
+class chainlink(BranchMixin, LinkedMixin, ChainMixin):
+
+    '''generic linked chain'''
+
+
+class prioritylink(BranchMixin, LinkedMixin, PriorityMixin):
+
+    '''generic priority linked chain'''
+
+
+class eventlink(EventBranchMixin, LinkedMixin, EventMixin):
+
+    '''generic linked event chain'''
+
+
+###############################################################################
+## chain ######################################################################
+###############################################################################
+
+
+class callchain(RootMixin, ChainMixin):
+
+    '''generic call chain'''
+
+
+class prioritychain(RootMixin, PriorityMixin):
+
+    '''generic priority chain generic'''
+
+
+class eventchain(EventRootMixin, EventMixin):
+
+    '''generic event chain'''

File callchain/keys.py

+# -*- coding: utf-8 -*-
+'''callchain reset keys'''
+
+from appspace.keys import AppspaceKey, Attribute
+
+
+class KDefaults(AppspaceKey):
+
+    '''default settings key'''
+
+
+class KRequired(AppspaceKey):
+
+    '''required settings key'''
+
+
+class NoServiceError(Exception):
+
+    '''no service error'''
+
+
+class KResetType(AppspaceKey):
+
+    '''reset type key'''
+
+    def reset():  # @NoSelf
+        '''reset previously accessed `lazybase` attributes'''
+
+
+class KResetLocal(KResetType):
+
+    '''reset thread local key'''
+
+
+class KSettings(KResetLocal):
+
+    '''settings key'''
+
+
+class KCore(KResetLocal):
+
+    '''core key'''
+
+    G = Attribute('external application global settings')
+
+    def __init__(root):  # @NoSelf
+        '''
+        init
+
+        @param root: root chain
+        '''
+
+
+class KConfig(KCore):
+
+    '''configuration access key'''
+
+    defaults = Attribute('default settings by their lonesome')
+    required = Attribute('required settings by their lonesome')
+
+
+class KRoot(KConfig):
+
+    '''root chain key'''
+
+    def __init__(pattern=None, required=None, defaults=None, **kw):  # @NoSelf
+        '''
+        init
+
+        @param pattern: pattern configuration or appspace label (default: None)
+        @param required: required settings (default: None)
+        @param defaults: default settings (default: None)
+        '''
+
+    def __call__(*args):  # @NoSelf
+        '''new chain session'''
+
+    def back(branch):  # @NoSelf
+        '''
+        handle return from branch chain
+
+        @param branch: branch chain
+        '''
+
+
+class KEventRoot(KRoot):
+
+    '''root event key'''
+
+    def __init__(  # @NoSelf
+        patterns=None,
+        events=None,
+        required=None,
+        defaults=None,
+        *args,
+        **kw
+    ):
+        '''
+        init
+
+        @param patterns: pattern config or eventspace label (default: None)
+        @param events: events configuration (default: None)
+        @param required: required settings (default: None)
+        @param defaults: default settings (default: None)
+        '''
+
+    def event(event):  # @NoSelf
+        '''
+        create or fetch `event`
+
+        @param event: event label
+        '''
+
+    def unevent(event):  # @NoSelf
+        '''
+        drop `event`
+
+        @param event: event label
+        '''
+
+
+class KChain(KResetLocal):
+
+    '''chain key'''
+
+    def chain(call, key=False, *args, **kw):  # @NoSelf
+        '''
+        add `call` or appspaced `call` to call chain, partializing it with any
+        passed arguments
+
+        @param call: call or appspaced call label
+        @param key: appspace key (default: False)
+        '''
+
+    def clear():  # @NoSelf
+        '''clear things'''
+
+    def tap(call, key=False):  # @NoSelf
+        '''
+        add call
+
+        @param call: callable or appspace label
+        @param key: link call chain key (default: False)
+        '''
+
+    def wrap(call, key=False):  # @NoSelf
+        '''build current callable from factory'''
+
+
+class KEvent(KChain):
+
+    '''event chain key'''
+
+    def on(event, call, key=False, *args, **kw):  # @NoSelf
+        '''
+        bind call to `event`
+
+        @param event: event label
+        @param call: label for call or eventspaced thing
+        @param key: key label (default: False)
+        '''
+
+    def off(event):  # @NoSelf
+        '''
+        clear calls bound to `event`
+
+        @param event: event label
+        '''
+
+    def trigger(*events):  # @NoSelf
+        '''
+        extend primary call chain with partials bound to `events`
+
+        @param *events: event labels
+        '''
+
+
+class KCall(KResetLocal):
+
+    '''call key'''
+
+    L = Attribute('local settings extracted')
+    Meta = Attribute('local settings')
+    port = Attribute('python 2.x <-> python 3.x porting helper')
+    space = Attribute('external appspace interface')
+
+    def __enter__():  # @NoSelf
+        '''enter execution context'''
+
+    def __exit__(e, t, b):  # @NoSelf
+        '''exit execution context'''
+
+    def switch(label, key=False):  # @NoSelf
+        '''
+        overt switch to linked chain configured in external appspace
+
+        @param label: linked chain label
+        @param key: linked chain chain key (default: False)
+        '''
+
+    def commit():  # @NoSelf
+        '''consume call chain'''
+
+
+class KEventCall(KCall):
+
+    '''event call key'''
+
+    def fire(*events):  # @NoSelf
+        '''
+        run calls bound to `events` **NOW**
+
+        @param *events: event labels
+        '''
+
+    def queues(*events):  # @NoSelf
+        '''
+        ordered mapping of processing queues for `events`
+
+        @param *events: event labels
+        '''
+
+
+class KBranch(KCore):
+
+    ''''branch key'''
+
+    G = Attribute('root external global settings')
+    M = Attribute('root external appspace manager')
+    _G = Attribute('root internal global settings')
+    _M = Attribute('root internal appspace manager')
+    root = Attribute('root object')
+
+
+class KEventBranch(KBranch):
+
+    '''event branch key'''
+
+    E = Attribute('local event registry')
+
+
+class KLinked(KBranch):
+
+    '''linked chain mixin'''
+
+    def close():  # @NoSelf
+        '''close out linked chain and switch to root chain'''
+
+
+class KChainletKey(KBranch):
+
+    def back():  # @NoSelf
+        '''switch back to root chain'''

File callchain/keys/__init__.py

-# -*- coding: utf-8 -*-
-'''callchain keys'''

File callchain/keys/base.py

-# -*- coding: utf-8 -*-
-'''callchain base keys'''
-
-from appspace.keys import AppspaceKey
-
-
-class KSettings(AppspaceKey):
-
-    '''settings key'''
-
-
-class KDefaults(AppspaceKey):
-
-    '''default settings key'''
-
-
-class KRequired(AppspaceKey):
-
-    '''required settings key'''
-
-
-class NoServiceError(Exception):
-
-    '''no service error'''

File callchain/keys/branch.py

-# -*- coding: utf-8 -*-
-'''branch keys'''
-
-from appspace.keys import Attribute
-
-from callchain.keys.reset import KCore
-
-
-class KBranch(KCore):
-
-    ''''branch key'''
-
-    G = Attribute('root external global settings')
-    M = Attribute('root external appspace manager')
-    _G = Attribute('root internal global settings')
-    _M = Attribute('root internal appspace manager')
-    root = Attribute('root object')
-
-
-class KEventBranch(KBranch):
-
-    '''event branch key'''
-
-    E = Attribute('local event registry')
-
-
-class KLinked(KBranch):
-
-    '''linked chain mixin'''
-
-    def close():  # @NoSelf
-        '''close out linked chain and switch to root chain'''
-
-
-class KChainletKey(KBranch):
-
-    def back():  # @NoSelf
-        '''switch back to root chain'''

File callchain/keys/call.py

-# -*- coding: utf-8 -*-'''call keys'''
-
-from appspace.keys import Attribute
-
-from callchain.keys.reset import KResetLocal
-
-
-class KCall(KResetLocal):
-
-    '''call key'''
-
-    L = Attribute('local settings extracted')
-    Meta = Attribute('local settings')
-    port = Attribute('python 2.x <-> python 3.x porting helper')
-    space = Attribute('external appspace interface')
-
-    def __enter__():  # @NoSelf
-        '''enter execution context'''
-
-    def __exit__(e, t, b):  # @NoSelf
-        '''exit execution context'''
-
-    def switch(label, key=False):  # @NoSelf
-        '''
-        overt switch to linked chain configured in external appspace
-
-        @param label: linked chain label
-        @param key: linked chain chain key (default: False)
-        '''
-
-    def commit():  # @NoSelf
-        '''consume call chain'''
-
-
-class KEventCall(KCall):
-
-    '''event call key'''
-
-    def fire(*events):  # @NoSelf
-        '''