[Ka-Ping Yee] > This PEP is a concrete proposal for exception chaining, to follow > up on its mention here on Python-Dev last week as well as earlier > discussions in the past year or two. > > http://www.python.org/peps/pep-0344.html
Here's a bunch of commentary: You're not giving enough credit to Java, which has the "cause" part nailed IMO. I like the motivation and rationale, but I think the spec is weak and would benefit from a better understanding of how exception handling currently works. In particular, please read and understand the comment in ceval.c starting with this line: /* Implementation notes for set_exc_info() and reset_exc_info(): There's too much text devoted early on to examples. I think these should come at the end; in particular, hiding the proposed semantics at the very end of a section that otherwise contains only illustrative examples is a bad idea (especially since the examples are easy enough to guess, if you've read the rationale). I don't think the PEP adequately predicts what should happen in this example: def foo(): try: 1/0 # raises ZeroDivisionError except: bar() raise sys.does_not_exist # raises AttributeError def bar(): try: 1+"" # raises TypeError except TypeError: pass Intuitively, the AttributeError should have the ZeroDivisionError as its __context__, but I think the clearing of the thread's exception context when the except clause in bar() is left will drop the exception context. If you follow the save/restore semantics described in that ceval.c comment referenced above, you'll get the correct semantics, I believe. Also, in that same example, according to your specs, the TypeError raised by bar() has the ZeroDivisionError raised in foo() as its context. Do we really want this? I still have the feeling that perhaps the context ought to be attached later, e.g. only when an exception "passes through" a frame that happens to be handling an exception already (whether in an except clause or in a finally clause). When chaining exceptions, I think it should be an error if the cause is not an exception instance (or None). Yes, this will just substitute a different exception, but I still think it's the right thing to do -- otherwise code walking the chain of causes must be constantly aware of this possibility, and since during normal use it will never happen, that would be a great way to trip it up (perhaps even to cause a circularity!). Do we really need both __context__ and __cause__? Methinks that you only ever need one: either you explicitly chain a new exception to a cause, and then the context is probably the same or irrelevant, or you don't explicitly chain, and then cause is absent. Since the traceback printing code is to prefer __cause__ over __context__, why can't we unify these? About the only reason I can think of is that with __cause__ you know it was intentional and with __context__ you know it wasn't; but when is it important knowing the difference? Do we really need new syntax to set __cause__? Java does this without syntax by having a standard API initCause() (as well as constructors taking a cause as argument; I understand why you don't want to rely on that -- neither does Java). That seems more general because it can be used outside the context of a raise statement. Why insert a blank line between chained tracebacks? In Java, I often find the way chained tracebacks are printed confusing, because the "deepest" stack frame (where the exception originally occurred) is no longer at the top of the printout. I expect the same confusion to happen for Python, since it prints everything in the exact opposite order as Java does, so again the original exception is somewhere in the middle. I don't think I want to fix this by printing the outermost exception first and the chained exception later (which would keep the stack frames in their proper order but emphasizes the low-level exception rather than the one that matches the except clause that would have caught it at the outermost level), but I might want to add an extra line at the very end (and perhaps at each chaining point) warning the user that the exception has a chained counterpart that was printed earlier. Why should the C level APIs not automatically set __context__? (There may be an obvious reason but it doesn't hurt stating it.) You're unclear on how the C code should be modified to ensure that the proper calls to PyErr_SetContext() are made. I was surprised to learn that yield clears the exception state; I wonder if this isn't a bug in the generator implementation? IMO better semantics would be for the exception state to survive across yield. You should at least mention what should happen to string exceptions, even if (as I presume) the only sane approach is not to support this for string exceptions (a string exception may be the end of the chain, but it cannot have a chained exception itself). I don't like the example (in "Open Issues") of applications wrapping arbitrary exceptions in ApplicationError. I consider this bad style, even if the chaining makes it not quite as bad as it used to be. I don't see the need for "except *, exc" -- assuming all exceptions derive from a single base class, we can just write the name of that base class. I don't like having sys.exception; instead, the only way to access the "current" exception ought to be to use an except clause with a variable. (sys.last_exception is fine.) I like the idea of taking all APIs that currently require a (type, value, traceback) triple to *also* accept a single exception instance. You should probably reference the proposal (pending a PEP; I think Brett is working on it?) that all exceptions should eventually derive from a common base class (a la Throwable in Java). I hope that this can be accepted together with the son-of-PEP-343 (PEP 343 plus generator exception injection and finalization) so __exit__ can take a single exception argument from the start. (But what should it receive if a string exception is being caught? A triple perhaps?) -- --Guido van Rossum (home page: http://www.python.org/~guido/) _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com