I've not posted this code because it previously hadn't been used in more than one (private) project, but it seems to hold water in 2 different projects (one is some IOT work from a year ago, one is a private work project I can't discuss at the moment), so I'm posting a link to it here for interest.
What is Guild? Well in a very short word, is a Kamaelia style Actor library. Previously in a Kamaelia presentation about concurrency... http://www.slideshare.net/kamaelian/kamaelia-lightning2010opensource ... I noted that the actor model is where you have active objects with an inbox, whereas Kamaelia's outboxes would map to late binding in the actor model. (see slides 10 and 11) What does this mean for Kamaelia? It means that inboxes now look like methods - decorated as actor methods. It means that outboxes also look like methods, decorated as late_bind or late_bind_safe. There is a default implementation for an "output" method - which is late_bind-able This means a simple transformer component now becomes: from guild import * class CapStream(Actor): @actor_method def input(self, data): self.output(str.capitalize(data) ) Something that actively "ticks" would look like this: class Ticker(Actor): @process_method def process(self): time.sleep(0.5) self.output( "hello world" ) As usual, interesting things are made by joining components together. Some initial utility components/actors already exist from guild.components import Printer cap_stream = CapStream() ticker = Ticker() printer = Printer() pipe(ticker, "output", cap_stream, "input") pipe(cap_stream, "output", printer, "input") start(cap_stream, ticker, printer) time.sleep(2) stop(cap_stream, ticker, printer) wait_for(cap_stream, ticker, printer) The start/stop/wait_for calls are necessary because we're dealing with separate threads rather than a scheduler. This may improve, but it's perhaps worth noting that this will shutdown cleanly, even though it launches a bunch of threads. The word "output" is the name of a late_bind (or late_bind_safe) method in the source object and the word "input" is the name of an actor_method in another Actor. The the av test code, it used: pipe(wct, "produce", d, "show") Where wct is a web cam component ,and d is a display component. The webcam calls self.produce() when a frame is ready. The pipe forces that frame to be passed to the display's "show" actor method. Snipping the last parts of these, they look like this: class WebCamTest(Actor): [ .. snip .. ] @late_bind def produce(self, image, timestamp): pass class SomeDisplay(Actor): [ .. snip .. ] @actor_method def show(self,image, timestamp): # pass self.frames.append((image, timestamp)) self.display.blit(image, (0,0) ) pygame.display.flip() This avoids the whole idiom of "if self.dataReady("..."): x = self.recv()" and so on. If the methods used by all your components for output are called "output", and all the inputs are called "input" the actor method becomes pipelineable. For example the above example becomes this: from guild import * from guild.components import Printer class CapStream(Actor): @actor_method def input(self, data): self.output(str.capitalize(data) ) class Ticker(Actor): @process_method def process(self): time.sleep(0.5) self.output( "hello world" ) cap_stream = CapStream() ticker = Ticker() printer = Printer() pipeline(ticker, cap_stream, printer) start(cap_stream, ticker, printer) time.sleep(2) stop(cap_stream, ticker, printer) wait_for(cap_stream, ticker, printer) Likewise, if you have a component where "produce" and "show" make more sense than "output" and "input" then you can add aliases: class WebCamTest(Actor): [ .. snip .. ] @late_bind def produce(self, image, timestamp): pass output = produce class SomeDisplay(Actor): [ .. snip .. ] @actor_method def show(self,image, timestamp): # pass self.frames.append((image, timestamp)) self.display.blit(image, (0,0) ) pygame.display.flip() input = show Which then allows things like: webcam = WebCamTest() display = SomeDisplay() pipeline(webcam, display) start(webcam, display) time.sleep(2) stop(webcam, display) wait_for(webcam, display) Not saying this is perfect, there's certainly some things I want to improve here to make the syntactic sugar nicer, but this strikes me as a good start! As I say, the code is here at the moment: https://github.com/sparkslabs/guild And hopefully should grow sufficient extras to allow interoperation with kamaelia, and later hopefully supplant it. No benchmarking done on it as yet, but it runs sufficiently fast for my needs at present. The 99 bottles code looks like this: - https://github.com/sparkslabs/guild/blob/master/examples/99bottles.py It also supports things like backplanes, and STM by default. Any comments welcome. Mainly forwarding because I'm finding this useful at the moment, and will probably extend in some interesting ways. Michael. -- You received this message because you are subscribed to the Google Groups "kamaelia" group. To unsubscribe from this group and stop receiving emails from it, send an email to kamaelia+unsubscr...@googlegroups.com. To post to this group, send email to kamaelia@googlegroups.com. Visit this group at http://groups.google.com/group/kamaelia. For more options, visit https://groups.google.com/groups/opt_out.