Hi,
Axon is the core set of modules in Kamaelia[1], and is essentially a set of tools for managing concurrency inside a single thread. Whilst it is a pre-requisite for Kamaelia, it can be used independently as well. Rather than the usual statemachine (or state machine derived) approach it uses communicating generators. The design of the system is inspired very heavily by asynchronous hardware verification systems. (Which leads to similarities with CSP & unix pipelines) [1] Kamaelia is BBC R&D testbed for developing media network protocols. (It takes it's name from an internal BBC R&D project aimed at looking how we scale internet delivery of the BBC's TV & Radio output.) Sourceforge page with Axon release: http://sourceforge.net/projects/kamaelia/ Installation is the usual "python setup.py install" dance, and the API should be fairly stable, and has evolved over a period of time, hence the 1.0 release version. The project became test infected rather late in the day, so some of test suite has been retrofitted if it looks a bit odd! Kamaelia's website: http://kamaelia.sourceforge.net/ (Explains context and there's a presentation that contains a simplified implementation using decorators, which might help show use cases) I've copied much of the README at the end of this mail for the curious. (License is the Mozilla tri-license : MPL1.1, GPL2.0, LGPL2.1) Merry Christmas! (Hopefully someone will find this a nice christmas toy :) Michael. -------- Axon is the core of Kamaelia. The contents of this directory must be installed before the rest of Kamaelia can be used. It can also be used independently of Kamaelia. The install procedure is python's usual dance: * python setup.py install Documentation is held in two places: * The usual 'pydoc name' - probably worth starting with: pydoc Axon.Component * The test suite is designed to allow you to get low level API behaviour information - "should X,Y, Z work? If so, what happens?". It's a partly retrofitted test suite, but some is TDD. (TDD took over late in the project) As a result, for example, passing a -v flag result in the docstring for each test to be dumped in a form that allows collation, and summarisation. (For an example of what we expect to automate from the test suite, see the end of this README file) Sample producer/consumber & wrapper component system: /-- testComponent -----------------------------------------------\ | | | +-- Producer ----+ +-- Consumer ----+ | | | |result|--->|source| |result|--->|_input|| | +----------------+ +----------------+ | | | \----------------------------------------------------------------/ The testComponent creates 2 subcomponents, creates the links in place, and takes the output from the consumer and links it to its own private/internal _input inbox. When it recieves a value from the consumer, it reports this fact and ceases operation. Producer sends values to its result outbox Consumer takes values from its source, does some work and sends results to its outbox (It's probably worth noting that an introspection system would be possible to write/nice to see that would be able to derive the above diagram from the running system) Example code: class Producer(component): Inboxes=[] Outboxes=["result"] def __init__(self): self.__super.__init__() def main(self): i = 100 while(i): i = i -1 self.send("hello", "result") yield 1 class Consumer(component): Inboxes=["source"] Outboxes=["result"] def __init__(self): self.__super.__init__() self.count = 0 self.i = 30 def doSomething(self): print self.name, "Woo",self.i if self.dataReady("source"): self.recv("source") self.count = self.count +1 self.send(self.count, "result") def main(self): yield 1 while(self.i): self.i = self.i -1 self.doSomething() yield 1 class testComponent(component): Inboxes=["_input"] Outboxes=[] def __init__(self): self.__super.__init__() self.producer = Producer() self.consumer = Consumer() self.addChildren(self.producer, self.consumer) self.link((self.producer, "result"), (self.consumer, "source")) linkage(self.consumer,self,"result","_input", self.postoffice) def childComponents(self): return [self.producer, self.consumer] def mainBody(self): while len(self.inboxes["_input"]) < 1: yield 1 result = self.recv("_input") print "Consumer finished with result:", result, "!" r = scheduler() p = testComponent() children = p.childComponents() p.activate() for p in children: p.activate() scheduler.run.runThreads(slowmo=0) (It would probably be nice to have better syntactic sugar here by using dictionaries, operators (eg '|' ) and decorators. The presentation on the website on Kamaelia shows a partial semi-reimplementation of ideas using decorators to eliminate the classes above) For various reasons it makes sense to run all Axon code using the -OO flags - this is due to the currently highly inefficient debug framework. One downside of async systems is that debuggers tend to have a hard time - but this has been thought of upfront :), the downside is that if you run with debugging enabled/possible, then the system runs like a pig. (Due to some rather heavy duty fumbling around in the garbage collector) Michael, December 2004 ----------------------------------------------------------------------------- Example of expected autodocs from test suite: (Ideally these would be merged with (or replace!)the doc strings/output from pydoc.) ./test_Component.py -v 2>&1 | ~/bin/parsetestResults.pl Standard: __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. Public: 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. 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. private: __addChild Registers the component as a child of the component. Internal function. _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. _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. Internall Function. -- http://mail.python.org/mailman/listinfo/python-list