Re: [Python-Dev] Chained Exceptions
Guido van Rossum wrote: [Guido] What if that method catches that exception? [Ka-Ping Yee] Did you mean something like this? def handle(): try: open('spamspamspam') except: catchit() # point A ... def catchit(): try: 1/0 except: pass Then there's no exception to propagate, so it doesn't matter. Once we're get to point A, the division by zero is long forgotten. But at what point does the attaching happen? If I catch the ZeroDivisionException inside catchit() and inspects its context attribute, does it reference the IOError instance raised by open('spamspamspam')? Yes, at least in the way I am imagining this being implemented. I was thinking that when an exception happens, the global exception variable is checked to see if it has a value. If it does that gets assigned to the new exception's 'context' attribute and the new exception gets assigned to the global exception variable. This could potentially cause a lot of extra work: when an inner loop that raises and catches lots of exceptions is invoked in the context of having caught an exception at some outer level, the inner loop keeps attaching the outer exception to each exception raised. [this also contains a partial answer to Philip's email also in this thread] Maybe, but as long as caught exceptions get cleared that should be an issue. Would this be solved if, when an 'except' branch is exited, exceptions are cleared? So, in the above example, once the 'pass' is hit in catchit() no exception is considered active any longer. This could be done with a CLEAR_EXC opcode very easily inserted at the end of an 'except' branch by the compiler. This would require explicit re-raising of exceptions to keep them alive after an 'except' ends, but I think that is actually a good idea and since this might all wait until Python 3000 anyway we don't need to worry about the semantic change. -Brett ___ 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
[Python-Dev] Chained Exceptions
Suppose exceptions have an optional context attribute, which is set when the exception is raised in the context of handling another exception. Thus: def a(): try: raise AError except: raise BError yields an exception which is an instance of BError. This instance would have as its context attribute an instance of AError. Or, in a more complex case: def compute(): try: 1/0 except Exception, exc: log(exc) def log(exc): try: file = open('error.log') # oops, forgot 'w' print file, exc file.close() except: display(exc) def display(exc): print 'Aaaack!', ex# oops, misspelled 'exc' Today, this just gives you a NameError about 'ex'. With the suggested change, you would still get a NameError about 'ex'; its 'context' attribute would show that it occurred while handling an IOError on error.log; and this IOError would have a 'context' attribute containing the original ZeroDivisionError that started it all. What do you think? -- ?!ng ___ 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
Re: [Python-Dev] Chained Exceptions
Guido van Rossum wrote: [Ka-Ping Yee] Suppose exceptions have an optional context attribute, which is set when the exception is raised in the context of handling another exception. Thus: def a(): try: raise AError except: raise BError yields an exception which is an instance of BError. This instance would have as its context attribute an instance of AError. [...] I like the idea, but I'm not sure about the consequences, and I'm not sure how it can be defined rigorously. Would it only happen when something *in* an except clause raises an exception? Which piece of code would be responsible for doing this? Try to come up with a precise specification and we'll talk. If a new exception is raised (e.g., not a bare 'raise') while a current exception is active (e.g., sys.exc_info() would return something other than a tuple of None), then the new exception is made the active exception and the now old exception is assigned to the new exception's context attribute to be the old exception. -Brett ___ 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
Re: [Python-Dev] Chained Exceptions
[Brett C.] If a new exception is raised (e.g., not a bare 'raise') while a current exception is active (e.g., sys.exc_info() would return something other than a tuple of None), then the new exception is made the active exception and the now old exception is assigned to the new exception's context attribute to be the old exception. Define raise. Does that involve a raise statement? What about 1/0? What if you call a method that executes 1/0? What if that method catches that exception? What about the StopIteration conceptually raised by next() called by the for-loop implementation? (Often it doesn't get instantiated at all when the next() method belongs to a built-in iterator.) I believe there are (at least) two use cases: (1) I catch some low-level exception (e.g. socket.error) and turn it into a high-level exception (e.g. an HTTPRequestFailed exception). (2) I write some exception handling code and somehow a bug in the handler (or an uncooperative environment, e.g. a full disk) causes the exception handling code to trip over an exception. I'm fairly certain (but not 100%) that Ping meant to include both use cases. -- --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
Re: [Python-Dev] Chained Exceptions
On Thu, 12 May 2005, Guido van Rossum wrote: Define raise. Does that involve a raise statement? Not necessarily; it could be a raise statement or an inadvertently triggered exception, such as in the example code i posted. What about 1/0? That counts. What if you call a method that executes 1/0? That counts too. What if that method catches that exception? Did you mean something like this? def handle(): try: open('spamspamspam') except: catchit() # point A ... def catchit(): try: 1/0 except: pass Then there's no exception to propagate, so it doesn't matter. Once we're get to point A, the division by zero is long forgotten. What about the StopIteration conceptually raised by next() called by the for-loop implementation? It's caught by the for-loop, so to speak, so it never gets out. Conceptually, the for-loop expands to: while 1: try: item = it.next() except StopIteration: break # body of loop goes here The 'break' can't possibly cause an exception, so the StopIteration exception isn't retained. I believe there are (at least) two use cases: (1) I catch some low-level exception (e.g. socket.error) and turn it into a high-level exception (e.g. an HTTPRequestFailed exception). (2) I write some exception handling code and somehow a bug in the handler (or an uncooperative environment, e.g. a full disk) causes the exception handling code to trip over an exception. I'm fairly certain (but not 100%) that Ping meant to include both use cases. Yes, though i did not expect to provide any mechanism for distinguishing the two cases. Do you think such a mechanism would be necessary? -- ?!ng ___ 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
Re: [Python-Dev] Chained Exceptions
[Phillip J. Eby] I think the main problem is going to be that (IIUC), Python doesn't know when you've exited an 'except:' clause and are therefore no longer handling the exception. But the compiler knows and could insert code to maintain this state. sys.exc_info() still gives you the exception you just caught. I think that a lot of the questions Guido brought up are directly related to this. Right. Also, what about code like this: try: doSomething() except SomeError: pass doSomethingElse() Should exceptions raised by doSomethingElse()' be treated as having the SomeError as their context, if it was raised? If I understand correctly, the interpreter cannot currently distinguish between this, and the case where an error is raised inside the 'except' clause. Indeed the interpreter currently doesn't distinguish between these, but I think it ought to for the purposes of this proposal. -- --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
Re: [Python-Dev] Chained Exceptions
[Guido] What if that method catches that exception? [Ka-Ping Yee] Did you mean something like this? def handle(): try: open('spamspamspam') except: catchit() # point A ... def catchit(): try: 1/0 except: pass Then there's no exception to propagate, so it doesn't matter. Once we're get to point A, the division by zero is long forgotten. But at what point does the attaching happen? If I catch the ZeroDivisionException inside catchit() and inspects its context attribute, does it reference the IOError instance raised by open('spamspamspam')? This could potentially cause a lot of extra work: when an inner loop that raises and catches lots of exceptions is invoked in the context of having caught an exception at some outer level, the inner loop keeps attaching the outer exception to each exception raised. Yes, though i did not expect to provide any mechanism for distinguishing the two cases. Do you think such a mechanism would be necessary? No, I was just trying to figure out what you meant when you said raise. It's clear now. -- --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
Re: [Python-Dev] Chained Exceptions
On May 12, 2005, at 6:32 PM, Ka-Ping Yee wrote: Suppose exceptions have an optional context attribute, which is set when the exception is raised in the context of handling another exception. Thus: def a(): try: raise AError except: raise BError yields an exception which is an instance of BError. This instance would have as its context attribute an instance of AError. I think it's a bad idea to have this happen automatically. Many times if an exception is raised in the except clause, that doesn't necessarily imply it's related to the original exception. It just means there's a bug in the exception handler. Take the divide by 0 example: try: doABunchOfStuff() except: 1/0 If you're going to do anything useful with the chained exception information (such as have it printed by the default exception printer), it'd be best to not print a bunch of irrelevant backtraces for all exceptions up the stack. The reason that doABunchOfStuff failed is not important. What is important is only that you had a divide by 0. In my mind it's much better to be explicit about your intentions, via something like: try: raise AError except Raiseable, e: raise BError(cause=e) Of course you can already do similar with current python, it just can't be spelled as nicely, and the default traceback printer won't use the info: try: raise AError except: newException = BError() newException.cause=sys.exc_info() raise newException James ___ 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
Re: [Python-Dev] Chained Exceptions
James Y Knight wrote: Of course you can already do similar with current python, it just can't be spelled as nicely, and the default traceback printer won't use the info: try: raise AError except: newException = BError() newException.cause=sys.exc_info() raise newException Well, one thing you can do is (somewhat evil ;) :: import sys try: raise AError, 'message' except: exc_type, exc_value, exc_traceback - sys.exc_info() raise BError, exc_value, exc_traceback with the result: Traceback (most recent call last): File ... raise AError, 'message' BError: message So you store the original exception as the argument to the new exception (so it's accessible). This has the nice side effect that message is displayed in the traceback - but the type has changed. Whilst in the above example, it's particularly evil, in the case where the original exception came from a function call and you want to translate the type it works very nicely. Tim Delaney ___ 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
Re: [Python-Dev] Chained Exceptions
[James Y Knight ] I think it's a bad idea to have this happen automatically. Many times if an exception is raised in the except clause, that doesn't necessarily imply it's related to the original exception. It just means there's a bug in the exception handler. Yeah, but especially in that case I think it would be nice if the traceback printed by the system (if all this doesn't get caught at an outer level) could show both the traceback from the handler and the traceback that it was trying to handle -- I've had many occasions where a trivial bug in the handler blew away the original traceback which was shy enough to make repeating it a pain. -- --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