On 27 October 2011 03:09, Greg Ewing
Still feeling in a peppish mood after the last round of PEP 335, I decided to revisit my cofunctions proposal. Last time round, I allowed myself to be excessively swayed by popular opinion, and ended up with something that lacked elegance and failed to address the original issues.
So, I've gone right back to my initial idea. The current version of the draft PEP can be found here:
http://www.cosc.canterbury.ac.nz/greg.ewing/python/generators/cd_current/cof...
together with updated examples here:
http://www.cosc.canterbury.ac.nz/greg.ewing/python/generators/cd_current/Exa...
and prototype implementation available from:
http://www.cosc.canterbury.ac.nz/greg.ewing/python/generators/cofunctions.ht...
In a nutshell, the 'cocall' syntax is gone. Inside a cofunction, if an object being called implements __cocall__, then a cocall (implicit yield-from) is performed automatically, otherwise a normal call is made.
Hi, I've taken the liberty to translate your examples using a small utility that I wrote some time ago and modified to mimic your proposal (imperfectly, of course, since this runs on unpatched python). FWIW, you can see and download the resulting files here (.html files for viewing, .py files for downloading): http://www.marooned.org.uk/~arno/cofunctions/ These don't need ``yield from`` and will work both in Python 2 and 3. The functionality is implemented with three functions defined in cofunctions.py: cofunction, costart, coreturn. The only changes that made to your examples are the following: 1. Add the line ``from cofunctions import *`` at the start of the file 2. Change codef f(x, y): ... to @cofunction def f(cocall, x, y): ... 3. Change 'cocalls' to cofunctions from within cofunctions as follows: f(x, y) becomes: yield cocall(f, x, y) 4. Change return statements within cofunctions to as follows: return 42 becomes coreturn(42) 5. The 'value' attribute of StopIteration exceptions raised by returning cofunctions becomes a 'covalue' attribute Note that #1, #2 and #4 a trivial changes. To perform #3 is actually simple because trying to call a cofunction raises and exception pointing out the incorrect call, so running the examples show which calls to change to cocalls. I could have avoided #5 but somehow I decided to stick with 'covalue' :) Here is, for example, the 'parser.py' example translated: ---------------------------- parser.py -------------------------- from cofunctions import * import re, sys pat = re.compile(r"(\S+)|(<[^>]*>)") text = "<foo> This is a <b> foo file </b> you know. </foo>" def run(): parser = costart(parse_items) next(parser) try: for m in pat.finditer(text): token = m.group(0) print("Feeding:", repr(token)) parser.send(token) parser.send(None) # to signal EOF except StopIteration as e: tree = e.covalue print(tree) @cofunction def parse_elem(cocall, opening_tag): name = opening_tag[1:-1] closing_tag = "%s>" % name items = yield cocall(parse_items, closing_tag) coreturn((name, items)) @cofunction def parse_items(cocall, closing_tag = None): elems = [] while 1: token = yield if not token: break # EOF if is_opening_tag(token): elems.append((yield cocall(parse_elem, token))) elif token == closing_tag: break else: elems.append(token) coreturn(elems) def is_opening_tag(token): return token.startswith("<") and not token.startswith("") run() --------------------------- /parser.py -------------------------- -- Arnaud