Axon.Component.py Version: Axon 1.0 TODO: Document the fact that runBody is actually the main() function, and hence using a standard generator where main() is, is fine. TODO: Make a note that microthread is meant as a term to mean "active generator object". (This was written during python 2.2 days when generators were uncommon, hence convoluted over explanation) TODO: Generally chop down and rewrite better A component is a microprocess with a microthread of control, input/output queues (inboxes/ outboxes) connected by linkages to other components, with postmen taking messages from output queues, passing them along linkages to inboxes. A microprocess is a class that supports parallel execution using microthreads. A microthread takes more explanation is a thread of control resulting from a function of the following form: def foo():
yield 1
while 1: print "this" yield 1 print "that" bla = foo() results in bla containing an intelligent function representing the thread of control inside the function - in this case inside the while loop - that remembers where the program counter was when control was yielded back to the caller. Repeated calls to bla.next() continue where you left off. First call to bla() & you get "this" printed. Next time you get "that" then "this" printed (when you go back round the loop). The time after that, you get "that" then "this" printed, and so on. If you have 10 calls to foo() you end up with 10 function calls that remember where they were in foo(), which run for a bit and then return control back to the caller. If you repeatedly call all of these function calls, you essentially end up with foo() running 10 times in parallel. It's these special "position remembering" functions that get termed microthreads. Clearly in order to have a microthread, you have to have a piece of code capable of being called in this manner - ie it yields control back to it's caller periodically - this is a what a microprocess in this context. An object that has a "special method" that can be treated in this manner. A component puts a layer of wrapper on top of the microprocess, which adds in input & output queues (inboxes/outboxes). To make life easier, a component has some wrappers around the "special function" to make user code less obfuscated. The life cycle for a component runs as follows: myComponent = FooComponent() # Calls Foo.__init__() constructor, # which must call super class constructor myComponent gets activated at some point later. When myComponent is activated the following logic happens for the runtime of myComponent : def runComponent(someComponent):
result = someComponent.initialiseComponent()
if result: yield result result = 1 while result: result = someComponent.mainBody() if result: yield result someComponent.closeDownComponent yield result # Component ceases running Dummy methods for the methods listed here are provided, so missing these out won't result in broken code. The upshot is a user component can look like this: class myComponent(component):
count = 0
def __init__(self,somearg): self.__class__.count = self.__class__.count + 1 self.somearg = somearg self.Component() # !!!! Must happen if this method exists def initialiseComponent(self):
print "We can perform pre-loop initialisation here!" print "If we created a new component here, we'd have" print " return newComponent[theComponent]" print " as the last line of this function" return 1 def mainLoop(self):
print "We're now in the main loop" print "If we wanted to exit the loop, we return a false value" print "at the end of this function" print "If we want to continue round, we return a true, non-None," print "value. This component is set to run & run..." return 1 def closeDownComponent(self):
print "We can't get here since mainLoop above always returns true" print "If it returned false however, we would get sent here for" print "shutdown code." print "After executing this, the component stops execution" This creates a component class which has the default input/output boxes of "inbox" and "outbox". Pydoc Style Documentation class component(Axon.Microprocess.microprocess) Method resolution order:
Data and other attributes defined here:
Methods defined here: __init__(self)
NOTE: The way you do this is self.__super.__init__() __str__(self)
addChildren(self, *children)
childComponents(self)
closeDownComponent(self)
dataReady(self, boxname='inbox')
initialiseComponent(self)
link(self, source, sink, passthrough=0, pipewidth=0, synchronous=None)
main(self)
mainBody(self)
recv(self, boxname='inbox')
removeChild(self, child)
send(self, message, boxname='outbox', force=False)
synchronisedSend(self, thingsToSend, outbox='outbox')
for i in self.synchronisedSend(thingsToSend):
yield 1 Largely has to be done that way due to not being able to wrap yield. See test/SynchronousLinks_SystemTest.py for an example Testdoc Documentation __init__
__str__
addChildren
childComponents
closeDownComponent
dataReady
initialiseComponent
link
main
mainBody
recv
removeChild
send
synchronisedBox
synchronisedSend
_activityCreator
_closeDownMicroprocess
_collect
_collectInbox
_deliver
_passThroughDeliverIn
_passThroughDeliverOut
_passThroughDeliverOut_Sync
_safeCollect
__addChild
Michael, December 2004 |
Kamaelia
is an open source project originated from and guided by BBC
Research. For more information browse the site or get in
contact.
This is an ongoing community based development site. As a result the contents of this page is the opinions of the contributors of the pages involved not the organisations involved. Specificially, this page may contain personal views which are not the views of the BBC. (the site is powered by a wiki engine)
(C) Copyright 2008 Kamaelia Contributors, including the British Broadcasting Corporation, All Rights Reserved