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:

  • 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.
  • NOTE: The way you do this is self.__super.__init__()

__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:
    • 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__

  • 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

 

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

This web site is powered by the same code created for the bicker manor project. For more details, contact Michael Sparks at BBC Research directly (cf contact)