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
-~----------~----~----~----~------~----~------~--~---