August'24: Kamaelia is in maintenance mode and will recieve periodic updates, about twice a year,
primarily targeted around Python 3 and ecosystem compatibility.
PRs are always welcome. Latest Release: 1.14.32 (2024/3/24)
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:
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 gets
activated at some point later.
When myComponent is
activated the following logic happens for the runtime of myComponent :
defrunComponent(someComponent):
result =
someComponent.initialiseComponent()
ifresult:yieldresult
result = 1
whileresult:
someComponent.closeDownComponent
yieldresult
# 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:
classmyComponent(component):
count = 0
def__init__(self,somearg):
definitialiseComponent(self):
defcloseDownComponent(self):
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:
- component
- Axon.Microprocess.microprocess
- Axon.Axon.AxonObject
- __builtin__.object
Data and other attributes defined here:
- Inboxes = ['inbox', 'control']
- Outboxes = ['outbox', 'signal']
- Usescomponents = []
Methods defined here:
__init__(self)
- You want to overide this method locally. You MUST call this
superconstructor for things to work however.
__str__(self)
- Provides a useful string representation of the component. You
probably want to override this, and append this description using
something like: component.__str__(self)
addChildren(self, *children)
- C.addChildren(list,of,components)
- Register the components as children/subcomponents This takes a list of
children components and adds them to the children list of this
component. This is done by looping of the list and adding each one
individually by calling addChild. addChild has a number of effects
internally described above.
childComponents(self)
- C.childComponents() - Simple
accessor method, returns list of child components
closeDownComponent(self)
- Stub method. This
method is designed to be overridden.
dataReady(self,
boxname='inbox')
- C.dataReady("boxname") -
test, returns true if data is available in the requested inbox. Used by
a component to check an inbox for ready data. You will want to call this
method to periodically check whether you've been sent any messages to
deal with!
You are unlikely to want to override this method.
initialiseComponent(self)
- Stub method. This
method is designed to be overridden.
link(self, source, sink, passthrough=0,
pipewidth=0, synchronous=None)
- C.link(source,sink) - create
linkage between a source and sink.
source is a tuple: (source_component, source_box)
sink is a tuple: (sink_component, sink_box)
passthrough, pipewidth and synchronous are defined as in the linkage
class
main(self)
- C.main() - You may want to override this
method instead of using callbacks
This is the function that gets called by microprocess. If you
override this do so with care. If you don't do it properly, your initialiseComponent, mainBody & closeDownComponent parts
will not be called. Ideally you should not NEED to override this method.
You also should not call this method directly since activate does this
for you in order to create a microthread of control.
mainBody(self)
- Stub method. This
method is designed to be overridden.
recv(self, boxname='inbox')
- C.recv("boxname")
- returns the first piece of data in the requested inbox.
Used by a component to recieve a message from the outside world. All
comms goes via a named box/input queue
You will want to call this method to actually recieve messages you've
been sent. You will want to check for new messages using dataReady first
though.
You are unlikely to want to override this method.
removeChild(self, child)
- C.removeChild(component) -
Deregister component as a child.
Removes the child component, and deregisters it as capable of recieving
messages. You probably want to do this when you enter a closedown state
of some kind for either your component, or the child component.
You will want to call this function when shutting down child components
of your component.
send(self, message, boxname='outbox',
force=False)
- C.send(message,
"boxname") - appends message to the requested outbox.
Used by a component to send a message to the outside world.A ll comms
goes via a named box/output queue.
You will want to call this method to send messages. They are NOT sent
immediately. They are placed in your outbox called 'boxname', and are
periodically collected & delivered by the postman. This is not
guaranteed to stay the same. (ie immediate delivery may happen)
If the outbox is synchronised then noSpaceInBox will be raised if the
box is full unless force is True which should only be used with great
care.
You are unlikely to want to override this method.
synchronisedSend(self, thingsToSend,
outbox='outbox')
C.synchronisedSend(list,
of, things,to, send) -> generator for sending the objects when
space is available. Expected to be used as:
foriinself.synchronisedSend(thingsToSend):
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__
- Class constructor is expected to be called without arguments.
__str__
- Returns a string representation of the component- consisting of
Component,representation of inboxes, representation of outboxes.
- Returns a string that contains the fact that it is a component
object and the name of it.
addChildren
- All arguments are added as child components of the component.
childComponents
- Returns the list of children components of this component.
closeDownComponent
- stub method, returns 1, expected to be overridden by clients.
dataReady
- Returns true if the supplied inbox has data ready for
processing.
initialiseComponent
- Stub method, returns 1, expected to be overridden by clients.
link
- Creates a link, handled by the component's postman, that links a
source component to it's sink, honouring passthrough, pipewidth and
synchronous attributes.
main
- Returns a generator that implements the documented behaviour of a
highly simplistic approach component statemachine.
- This ensures that the closeDownComponent method is called at the end
of the loop. It also repeats the above test.
mainBody
- stub method, returns None, expected to be overridden by clients as
the main loop.
recv
- Takes the first item available off the specified inbox, and returns
it.
removeChild
- Removes the specified component from the set of child components and
deregisters it from the postoffice.
send
- Takes the message and places it into the specified outbox, throws an
exception if there is no space in a synchronous outbox.
- Takes the message and places it into the specified outbox, throws an
exception if there is no space in a synchronous outbox.
synchronisedBox
- Called with no arguments sets the outbox 'outbox' to being a
synchronised box, with a maximum depth of 1.
synchronisedSend
- Takes a list of things to send, and returns a generator that when
repeatedly called tries to send data over a synchronised outbox.
_activityCreator
- Always returns true. Components are microprocesses instantiated by
users typically - thus they are creators of activity, not slaves to it.
Internal function.
_closeDownMicroprocess
- Checks the shutdownMicroprocess message for the scheduler contains a
reference to the postoffice associated with the component.
- Returns a shutdownMicroprocess. Internal Function.
_collect
- Takes the first piece of data in an outbox and returns it. Raises
IndexError if empty. Internal function.
_collectInbox
- Tests with default args. All these deliveries should suceed.
Internal Function.
- Tests with default args. Should raise IndexError as the box should
be empty in this test. Internal Function.
- Tests with inbox arg. Should raise IndexError as the box should be
empty in this test. Internal Function.
- Tests with inbox arg. Tests collection. Internal Function.
_deliver
- Appends the given message to the given inbox. Internal
Function.
- Checks delivery to a synchronised inbox fails when it is full using
the force method.
- Checks delivery to a synchronised inbox fails when it is full.
<br>
_passThroughDeliverIn
- Appends the given message to the given inbox. Internal
Function.
- Should throw noSpaceInBox if a synchronised box is full.
- When force is passed as true the box can be overfilled.
_passThroughDeliverOut
- Appends the given message to the given outbox. Internal
Function.
- Checks delivery is limited to the pipewidth.
- Checks delivery is limited to the pipewidth.
_passThroughDeliverOut_Sync
- Appends messages to given outbox. Should throw noSpaceInBox when
full.
_safeCollect
- Wrapper around _collect - returns None where an IndexError would
normally be thrown. Internal Function.
__addChild
- Registers the component as a child of the component. Internal
function. ttbw
Michael, December 2004