This page is dedicated to the public discussion of PEP 343: Anonymous Block Redux and Generator Enhancements.
I think this is a good one; I hope people agree. Its acceptance will obsolete about 4 other PEPs! (A sign that it fulfills a need and that the proposed solution is powerful.)
Please read the PEP; then add your comments here. Please sign your comments with your name. GvR (Guido van Rossum)
Can someone please speak to the implementation schedule for WithStatement? I am dying to use it, but I don't appear able to import it even from the future yet! -- Barnard Masonry
I feel like PEP 343 and PEP 342 work together in some important way, but it's not clear what that is. Does one depend on the other? What kind of things are possible if both are accepted (vs. just one of them)? I think this is important to understanding where PEP 343 is leading; some discussion of this would be appreciated. -- IanBicking
No, they are quite independent. PEP 342 is for making generators into real coroutines. Maybe throw() can help a tiny bit there but I think it's not essential. PEP 342 adds argument-less yield to PEP 343 which is a very minor improvement (no "yield None"). GvR
To assist in avoiding the mistake pointed out in the optional extension, the documentation of the standard library should include a description on how to use (or: how not to use) objects that support the __enter__/__exit__ protocol. E.g.
Do not use objects of this class as a parameter to with.
or
Objects of this class may be used as a parameter to with. However, they will become invalid after the with block.
-- Eric Nieuwland
I'd be very surprised if an object supporting __enter__ and __exit__ would say "do not use this in a with-statement."
What makes sense, however, is to distinguish between objects that can be used at most once in a with-statement (like files) and objects that can be used over and over (like locks). GvR
The solution to this problem recommended in PEP 346 is to throw an explicit exception in __enter__ when a non-reusable template is reused. PEP 343 uses this approach for the generator-based templates, and I would expect an __enter__ method on file objects to do the same thing. -- Alyssa Coghlan
next_throw() is easier to grep with next(). Niki Spahiev
Given that 99% of uses of both are implicit, I don't see how that matters. GvR
I don't want to teach beginners why python uses 'raise' sometimes and 'throw' other times. I predict: they're going to be coming from other languages, they're going to accidentally use 'throw' as the statement, and they're going to get mad at "all of python's weird quirks, like how it has both raise and throw". Drew Perttula
I'm not so worried. Your tolerance for language quirks seems rather low.
GvR Raymond Hettinger added on python-dev (copied here by GvR):
- "This was intended. There was much discussion about this for PEP 288 and this was more or less a concensus choice. The term is already associated with exceptions in other languages and it captures the concept of the raise occurring somewhere else (what is thrown at or into). It is simple, clear, and tends to suggest all the right things. I have no doubt that someone can point out differing semantics in other languages but I don't care. The verbal cue is what we are after."
I've tuned out the recent conversation on these PEPs quite religiously, because I never thought they would result in anything even vaguely useful. Fortunately, I was dead wrong. PEP-343 is simple, elegant, and feels very Pythonic to me. I need to catch up now and think through the implications and corner-cases, but I can give a provisional +1 to the concept. Thanks to all those that didn't tune out and worked to get us to this point. -- Kevin Jacobs
A few immediate comments:
- Is g.throw(...) supposed to let you raise exceptions in other threads (by having g catch the exception you throw it, then raise its own exception for its caller)? The PEP should be clear about this. It would be great if the answer is yes and if that's the case, objects like queues and sockets should be turned into generators to permit cross-thread signalling using generator exceptions. But I had the impression that this would be difficult in CPython.
- This is not the intention at all. The PEP specifically speaks of "where the generator g is currently suspended". By definition this implies that it is not running in another thread. You must have had threads on your mind too much
recently to even think of this.
GvR - I will read it again, but I don't remember seeing anything that made me think the generator couldn't be suspended in another thread. phr
- Generators aren't tied to a thread, but they can only be executing in one thread at a time. When a generator yields in one thread, another thread can resume it with next() or throw() -- but then the resumed generator executes in the thread that called next() or throw(). There's nothing new to this -- it's been like this
since generators were invented. GvR
- Well, throw() didn't exist when generators were invented, so throw hasn't always been like anything ;-). If throw is supposed to resume execution in the throwing thread rather than the thread where the generator was last suspended, the PEP should clearly specify that. --phr
It already says that throw() is just like next(). GvR
- Well, throw() didn't exist when generators were invented, so throw hasn't always been like anything ;-). If throw is supposed to resume execution in the throwing thread rather than the thread where the generator was last suspended, the PEP should clearly specify that. --phr
- Generators aren't tied to a thread, but they can only be executing in one thread at a time. When a generator yields in one thread, another thread can resume it with next() or throw() -- but then the resumed generator executes in the thread that called next() or throw(). There's nothing new to this -- it's been like this
- I will read it again, but I don't remember seeing anything that made me think the generator couldn't be suspended in another thread. phr
- This is not the intention at all. The PEP specifically speaks of "where the generator g is currently suspended". By definition this implies that it is not running in another thread. You must have had threads on your mind too much
- I thought some of the original motivation of PEP 310 was to lessen the pain of giving up the idiom of expecting files to be closed when their last reference goes away. So if 343 no longer guarantees that the exit method will be run when the "with" statement finishes, it's lost some of its main motivating functionality. I wonder if having a more serious treatment of block scope, so that destructors are guaranteed to be called when a scope exits (by falling through or via an exception), would also take care of this issue.
Where did you read that the __exit__ method isn't guaranteed to be called upon exit from the with statement? The translation explicitly calls it! Read again. You may be confused with g.close(). That's only relevant when you use a for-loop without an explicit with-statement. GvR
There was some good discussion on clpy a while ago that I'll try to locate, and add some more comments during the weekend. --Paul Rubin (phr)
Overall I think this is a great proposal. It will allow a lot of things to be made a great deal more natural - probably many things that have not yet been considered.
I can see a lot of areas in the standard libs where improvements could be made using this construct. It will seem slightly inconsistent in areas where this is not done.
The only negative to the proposal is the extra complexity. The idea is a little magical, and may not be easy for new users to pick up. New users will not be forced to use it of course. However ironically the very usefulness of the idiom will probably lead to it being widely used in libraries, which will of course require new users to learn it sooner or later!
--
