PEP 288  proposes a more general solution, allowing custom exception passing to generators.
Is there any reason to prefer gen.close() over the more general solution gen.throw(Close) which results in nearly identical code and yet allows other exception types to be handled as well?
After re-skimming PEP 288 I'm still not convinced that a more general problem exists for which .close() isn't sufficient. The one motivating example there (writing a log file) seems forced and can be done in other ways.
Note, the general purpose solution is a natural extension of the existing syntax and is easily implemented without messing with 'try/finally'.
I don't understand this remark. AFAICT PEP 288 doesn't propose new syntax, only a new method and its semantics. And I don't see Samuele's solution as "messing with try/finally".
Pretty much all that was holding up the general solution was that I had not convinced Guido that the clean-up problem exists in practice. It looks like you've surmounted that obstacle for me.
But you still haven't convinced me of the need for the more generalized PEP 288 mechanism.
I do think that the possibility of implementing PEP 288 in the future suggests that Samuele's .close() should be implemented in terms of a special exception, not in terms of a 'return'.
The spec needs to define clearly what should happen if the generator catches and ignores the exception, e.g.:
def forever(): while True: try: yield None except: pass
f = forever() f.next() f.close()
Clearly at this point the generator reaches the yield again. What should happen then? Should it suspend so that a subsequent call to f.next() can receive another value? Or should reaching yield after the generator is closed raise another exception? I'm leaning towards the latter, despite the fact that it will cause an infinite loop in this case -- that's no different when you have a print statement instead of a yield statement.
(Mentally substituting a print for a yield is often a useful way to think about a generator. The reverse can also be useful to consider converting a non-generator to a generator: if it prints a sequence of values, it can also yield the same sequence.)
Another comment on Samuele's PEP: It is sort of sad that the *user* of a generator has to know that the generator's close() must be called. Normally, the beauty of using a try/finally for cleanup is that your callers don't need to know about it. But I see no way around this. And this is still an argument that pleads against the whole thing, either PEP 288 or Samuele's smaller variant: the usual near-guarantee that code in a finally clause will be executed no matter what (barring fatal errors, os._exit() or os.execv()) does not apply. And this was the original argument against allowing yield inside try/finally. But the need for cleanup is also clear, so I like Samuele's KISS compromise.
--Guido van Rossum (home page: http://www.python.org/%7Eguido/)