Hi All,

Thanks Steve & John, this has been really helpful. It pulls out a few points - 
such as "who uses this" and "why do they use this". Each aspect of which 
affects what the API should look like.

Taking some specific points:

Firstly there's the point that John makes here:
> It seems like it is starting with the class-based component and just
> abstracting out some bits 

Which is clearly true. Steve also says this:

> It's turning a function into a class -  and then a whole lot more.

Which is an interesting point - it is taking a generator function, embedding 
it in a class, and then "stealing" the first argument. Which no matter which 
I accept feels odd. The idea is "use as component", but that example was 
being used as a shorthand for writing the body of a generator component.

This is usefully blunt as well:

> To my brain, that is easier to process one line at a time than trying
> to grok the mind-bending:
> @GeneratorComponent(Inboxes = ["inbox", "control"],
>                     Outboxes = ["outbox", "signal"])

There's no use adding in a piece of functionality intended to simplify things 
if it doesn't actually do so. 

For me the aim of syntactic sugar at this stage is to simplify creating new 
components. Integrating generators written for other scenarios is somewhat 
harder, so I'll come back to that in a moment.

I've also taken on board this comment:
> I've seen lots of very handy decorators that take no arguments (other
> than the decorated function/class).  I've seen a lot fewer handy
> decorators that take exactly one additional argument.

Which I take to mean that the most understandable decorators have the 
following characteristic, for a given function.
   * The function still does what it looks like it does
   * It's signature is unchanged
   * But it may have additional behaviour, which simplifies the use of the
      function in some manner, and simplifies it's internal logic.

That to me suggests that rather than do this sort of this:
@inbox
@inbox('control')
@outbox('signal')
@outbox
class target_class(foo):
    pass

That actually doing something like this makes more sense:
    class printer(component):
        @pauseonboxesempty
        @acceptsShutdownException
        def main(self):
            while True:
                for line in self.Inbox(""):
                    print line.strip()
                yield 1

Of course we could simply declare that simple handling is "any message causes 
shutdown, and always pause if no data", allowing lots of components to have 
this form:

    class printer(component):
        @simple_control_flow
        def main(self):
            while True:
                for line in self.Inbox(""):
                    print line.strip()
                yield 1

Or
    class printer(component):
        @basic_control_flow
        def main(self):
            while True:
                for line in self.Inbox(""):
                    print line.strip()
                yield 1

The 99 bottles example would then look like this:

    class follow(component):
        fname = None
        @basic_control_flow
        def main(self):
            f = file(self.fname)
            f.seek(0,2)
            while True:
                line = f.readline()
                if not line: # no data
                    time.sleep(.1)
                else:
                    self.send(line, "outbox")
                yield 1

    class grep(component):
        pattern = "."
        @basic_control_flow
        def main(self):
            regex = re.compile(pattern)
            for line in self.Inbox("inbox"):
                if regex.match(line):
                    self.send(line, "outbox")
            yield 1

    class printer(component):
        @basic_control_flow
        def main(self):
            while True:
                for line in self.Inbox(""):
                    print line.strip()
                yield 1

   from Kamaelia import Pipeline # Further comments coming on this line

   Pipeline(
      follow(fname="somefile"),
      grep("hello"),
      printer()
   ).run()

This also means that you can play around with stuff, and allow something like 
this:
    class printer(component):
        @basic_control_flow
        @wrap_std_generator
        def main():
            while True:
                lines = yield
                for line in lines:
                    print line.strip()

Or even assume @basic_control_flow, since it doesn't have any other option:
    class printer(component):
        @wrap_std_generator
        def main():
            while True:
                lines = yield
                for line in lines:
                    print line.strip()

That then also allows the creation of a decorator, allowing something like 
this:

    @use_as_component
    def printer():
        while True:
            lines = yield
            for line in lines:
                print line.strip()

Where "inbox" maps to data passed into lines. Data yielded back gets sent 
to "outbox" (if not None). Any control message sent it causes an exception to 
get thrown in - GotShutdown for example - and that control message is passed 
on. If the generator exits it sends on the message.

That then seems relatively sane & painless. If you want to use a traditional 
generator, you wrap it using "use_as_component", and can then follow a 
outwards from there.

This particular example is nice, because it means that you can do this:

    from mygeneratorset import printer
    from Kamaelia import Pipeline, TCPClient, use_as_component

    Pipeline(
       TCPClient("example.com", 19),  # chargen
       use_as_component( printer() ),
    ).run()

I'm not 100% sure on this at the moment, but this approach seems saner, than 
trying to write a "one decorator fits all" approach. In particular, the thing 
I'm unclear about from this is how to make this work nicely with WSGI 
components. Indeed, it would be particularly nice to be able to invert this 
the other way around - to allow Kamaelia components to be used as WSGI 
components. That I'm even less clear on, so maybe come back to that later

For further food for thought I've included a collection of ideas about what 
the sorts of decorators might make sense after my .sig below.

As you can expect I'm still mulling over what these might look like and why 
they might be useful.

Any comments/thoughts welcome :)

Regards,


Michael.
 -- 
http://yeoldeclue.com/blog
http://twitter.com/kamaelian
http://www.kamaelia.org/Home

class printer(component):
    @sleep_if_no_data
    @shutdown_on_any_control_message
    def main(self):
        while True:
            for line in self.Inbox(""):
                print line.strip()
            yield 1

class printer(component):
    @sleep_if_no_data
    @shutdown_on_any_control_message
    @wrap_std_generator
    def main():
        while True:
            lines = yield
            for line in self.Inbox(""):
                print line.strip()



class printer(component):
    @simple_control_flow
    def main(self):
        while True:
            for line in self.Inbox(""):
                print line.strip()
            yield 1

class printer(component):
    @simple_control_flow
    @wrap_std_generator
    def main():
        while True:
            lines = yield
            for line in lines:
                print line.strip()

class follow(component):
    fname = None
    @sleep_if_no_data
    @shutdown_on_any_control_message
    def main(self):
        f = file(self.fname)
        f.seek(0,2)
        while True:
            line = f.readline()
            if not line: # no data
                time.sleep(.1)
            else:
                self.send(line, "outbox")
            yield 1

class grep(component):
    pattern = "."
    @sleep_if_no_data
    @shutdown_on_any_control_message
    def main(self):
        regex = re.compile(pattern)
        for line in self.Inbox("inbox"):
            if regex.match(line):
                self.send(line, "outbox")
        yield 1

class follow(component):
    fname = None
    @simple_control_flow
    def main(self):
        f = file(self.fname)
        f.seek(0,2)
        while True:
            line = f.readline()
            if not line: # no data
                time.sleep(.1)
            else:
                self.send(line, "outbox")
            yield 1

class grep(component):
    pattern = "."
    @simple_control_flow
    def main(self):
        regex = re.compile(pattern)
        for line in self.Inbox("inbox"):
            if regex.match(line):
                self.send(line, "outbox")
        yield 1


class follow(component):
    fname = None
    @standardboxhandling
    @wrap_std_generator
    def main(self):
        f = file(self.fname)
        f.seek(0,2)
        while True:
            line = f.readline()
            if not line: # no data
                time.sleep(.1)
            else:
                yield line

class grep(component):
    pattern = "."
    @standardboxhandling
    def main(self):
        regex = re.compile(pattern)
        for line in self.Inbox("inbox"):
            if regex.match(line):
                self.send(line, "outbox")
        yield 1

class grep(component):
    pattern = "."
    @standardboxhandling
    @wrap_std_generator
    def main():
        regex = re.compile(pattern)
        lines = yield
        while True:
            r = []
            for line in lines:
                if regex.match(line):
                    r.append(line)
            lines = yield r



class follow(component):
    fname = None
    @pauseonboxesempty
    @acceptsShutdownException
    def main(self):
        f = file(self.fname)
        f.seek(0,2)
        while True:
            line = f.readline()
            if not line: # no data
                time.sleep(.1)
            else:
                self.send(line, "outbox")
            yield 1

class follow(component):
    fname = None
    @pauseonboxesempty
    @acceptsShutdownException
    @wrap_generator()
    def main(self):
        f = file(self.fname)
        f.seek(0,2)
        while True:
            line = f.readline()
            if not line: # no data
                time.sleep(.1)
            else:
                yield line

class follow(component):
    fname = None
    @pauseonboxesempty
    @acceptsShutdownException
    @wrap_std_generator(args="fname")
    def main(fname):
        f = file(fname)
        f.seek(0,2)
        while True:
            line = f.readline()
            if not line: # no data
                time.sleep(.1)
            else:
                yield line

class grep(component):
    pattern = "."
    @pauseonboxesempty
    @acceptsShutdownException
    def main(self):
        regex = re.compile(pattern)
        for line in self.Inbox("inbox"):
            if regex.match(line):
                self.send(line, "outbox")
        yield 1

class grep(component):
    pattern = "."
    @pauseonboxesempty
    @acceptsShutdownException
    @wrap_generator
    def main(self):
        regex = re.compile(pattern)
        while True:
            for line in self.Inbox("inbox"):
                if regex.match(line):
                    yield line

class grep(component):
    pattern = "."
    @pauseonboxesempty
    @acceptsShutdownException
    @wrap_generator(inboxmapping={"inbox":"source"})
    def main(source):
        regex = re.compile(pattern)
        while True:
            for line in source:
                if regex.match(line):
                    yield line

class printer(component):
    @pauseonboxesempty
    @acceptsShutdownException
    def main(self):
        while True:
            for line in self.Inbox(""):
                print line.strip()
            yield 1


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"kamaelia" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/kamaelia?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to