Python: catch any exception and put it in a variable - python

To figure out what it would take to avoid some recursion, I need to catch any exception (edit: Not just ones derived from Exception, but all exceptions, including KeyboardInterrupt and user exceptions), put it in a variable, and later re-raise it outside the catch block. Essentially, I'm trying to roll my own finally block. Is this possible?
The actual problem is to call a number of cleanup functions, and if any of them fail, all the others should also be called, then the exception for the one that failed should still propagate. Here's my current solution, it takes a list of Popen objects:
def cleanupProcs(procs):
if not procs:
return
proc = procs.pop(0)
try:
proc.terminate()
proc.wait()
finally:
cleanupProcs(procs)
Is there an iterative way to do this? A more elegant way? A more Pythonic way?

If you want the stacktrace included:
try:
# something
except:
the_type, the_value, the_traceback = sys.exc_info()
later
raise the_type, the_value, the_traceback
(Related to this answer)
See also here for Python 2.7.

In this case, I think I would argue that you may not be doing it right.
To me, the point of an exception is to signal a bum bum baaaaa exceptional circumstance. And when you are writing code that may fire an exception you have two responsible options - catch it and do something with it (like recover), or completely ignore it.
From what you say in your post, you don't really care that an exception happened. It shouldn't halt your program, and program flow should continue as normal. You do however want to know that an exception happened. And this is where the logging module comes in:
import logging
log = logging
def get_some_cheese():
raise ValueError("Sorry, we're right out.")
try:
get_some_cheese()
except:
log.exception("What a waste of life")
When you log an exception it automagically adds stack trace information for you. After you config your logging a bit, you can get it setup to do all sorts of whatever things you want to do - send an email, write to a file or stdout/err, whatever. But then you'll get informed that an exception occured, but you can also simply recover from the error and continue on your merry way cleaning up whatever it is you need to clean up.

I'd probably use BaseException to catch anything that gets thrown, and iterate through all your cleanup functions (instead of using recursion). Then append any exceptions to a list, to deal with (re-raise, log, etc) as appropriate when you finish the cleanup.
def runCleanup(procs):
exceptions = []
for proc in procs:
try:
proc.terminate()
proc.wait()
except BaseException as e:
exceptions.append(e) # Use sys.exc_info() for more detail
return exceptions # To be handled or re-raised as needed

you can use:
procexceptions = []
except Exception, e:
procexceptions.append(e)
and then later (after the loop for terminating processes) you can
raise procexceptions[0]
etc.

Openstack does something very similar for a different reason. Have a look at https://github.com/openstack/nova/blob/master/nova/openstack/common/excutils.py#L30 (function save_and_reraise_exception). In their case it works like a resource manager.

Like just about everything else in Python, exceptions are objects and therefore can be bound to names and operated upon. Here's a short example showing how to grab an exception and use it later:
>>> def to_int(x):
... try:
... return int(x)
... except Exception, e:
... print 'in exception block:', e
... print 'after exception block:', e
>>> to_int('12')
12
>>> to_int('abc')
in exception block: invalid literal for int() with base 10: 'abc'
after exception block: invalid literal for int() with base 10: 'abc'

Thats easy:
>>> try:
... #something
... except BaseException, e: # OK. Using BaseException instead of Exception
... pass
...
>>>
>>> raise e
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>>

Related

Difference between bare except and specifying a specific exception

In this piece of code, I could write a simple except clause without writing Exception in front of it. I mean the last line could be like this :
except:
print('Hit an exception other than KeyError or NameError!')
What is the point of writing Exception in front of an except clause ?
try:
discounted_price(instrument, discount)
except KeyError:
print("There is a keyerror in your code")
except NameError:
print('There is a TypeError in your code')
except Exception:
print('an exception occured')
I tried writing an except clause without Exception keyword and it worked the same.
Thank you guys for your answers . I know the point of catching specific errors. If I want to ask more clearly , what is the difference between two clauses :
except Exception:
print('an exception occured')
except :
print('an exception occured')
The point of specifying the Exception is that ONLY that Exception will be caught, if you do not specify any Exception, then ALL Errors and Exceptions will be caught potentially masking bugs.
For example let's say that I want to read a file if it exists or otherwise print a message to the user, I can write:
try:
with ope("example.txt", "r") as f:
print(f.read())
except:
print("File not found")
But while this code runs fine without raising any exceptions, this code will never read the file, even if it exists!!!
This is because I wrote ope instead of open and the NameError was caught by my bare except, If I write the except in the correct way:
try:
with ope("example.txt", "r") as f:
print(f.read())
except FileNotFoundError:
print("File not found")
Now I can properly debug my code:
Traceback (most recent call last):
File "/home/riccardo/types.py", line 4, in <module>
with ope("example.txt", "r") as f:
NameError: name 'ope' is not defined
A bare expect
try:
...
except:
pass
or catching any exception whatsoever
try:
...
except Exception:
pass
are bad practice, because you can be hiding bug or be interfering with the normal procedure of the program.
You should only catch exception that you know how to handle, everything else you should let it propagate.
For some example:
Hide bug: it can hide some typo in your code as Caridorc example show making you think that you had a problem different than the real problem
Interfering with the normal procedure: you can make it an unexpectedly unkillable program or get in the way of the normal procedure of the program by discarding an exception that another part of the code was expecting.
like for example
while True:
try:
print("running")
except:
print("I'm immortal muahahaha")
this piece of code now you can't stop with you usual control-z (control-z throw and KeyboardInterrupt exception into your program) so you now need to close the whole interpreter/kill it with the task admin just to stop it, and if this was unintended you just introduced a new bug and depending on what you're doing it can be catastrophic.
To illustrate how catastrophic it can be, consider the following hypothetical case: imagine you make so benign function for a medical device and you put something like this
try:
...
except:
print("some error happens")
now it just so happens that while you piece of code was running a HeartAttack exception was raised and your catch it all and ignore piece of code will do, well, just that, and here is the twist this device was a pacemaker... well, congratulation you just killed the poor guy.
And that is why you should only catch the exception you know how to deal with, everything else you let it pass and hope that somebody along the line know how to deal with it, like in the example above, you and your piece of code don't know how to deal with a HeartAttack, but the pacemaker do and the pacemaker was the one that call your piece of code let it deal with it...
for a no so extreme example consider this simple code
def get_number_from_user():
while True:
try:
return int(input("write a number: "))
except:
print("not a valid number try again")
if your user was done with your program and this happens to be the thing running he/she might want to kill it with a control-z as you usually do with any program, but it will find that it doesn't work, the correct way here is to catch the error we know how to deal with in this case, namely ValueError, everything else isn't this function business
def get_number_from_user():
while True:
try:
return int(input("write a number: "))
except ValueError:
print("not a valid number try again")
You also ask about the difference between
try:
...
except:
pass
and this
try:
...
except Exception:
pass
the difference is that a bare except can catch any and all kind of exception, that in python is anything that is or inherit from BaseException that sit at the top of the exception hierarchy, while except Exception will catch only Exception itself or anything that inherit from it (the same apply for any particular exception you put there), this small distinction allow to make some exceptions more special than other, like the aforementioned KeyboardInterrupt that inherit from BaseException instead of Exception, and that is used to signal that the user wants to terminate this program, so you should do so and this distinction is made basically so new programmers don't shoot themselves in the foot when using except Exception
Just to add to the answer provided by #Caridorc, by specifying each error separately, you can run specific error handling code pertaining to that error when the exception arises. If for example the file does not exist, you can print message to that effect. If however, it fails to print because you mistyped g instead of f, you can print a message to say that the variable is not recognised ( separate code for separate error captures). For exmple:
g = 100 # Unrelated variable declared previously
try:
with open("example.txt", "r") as f:
x=print(f.read())
except FileNotFoundError:
print("File not found")
except AttributeError:
print("Reading wrong variable")
except Exception as e:
print("Unknown Error", e)
Note also the last exception except Exception as e:. This is the same as just except: but allows you to handle all other errors that do not fit onto previous captures and retrieve e - the error message that is generated by compiler. There is effectively no difference between except: and except Exception: in terms of execution
Consider the code:
a = 5
b = 0
x = a / b
Executing this will alert you to the fact that you have attempted to divide a float by zero but it will crash your code.
Now consider:
a = 5
b = 0
try:
x = a / b
except: # or 'except Exception:'
print("An exception was raised")
This will raise an exception that is handled by printing a message that an error occurred. You code will not crash but you do not know how to properly handle the code because you do not know what the exception was, just that one occurred.
Now consider:
a = 5
b = 0
try:
x = a / b
except Exception as e:
print("An exception was raised, generating error", e)
Now your code does not crash. Is handled and you know what the error was.
The purpose of writing "Exception" in front of an except clause is to catch all possible exceptions that can occur in the code. By specifying "Exception", you are telling the interpreter to handle any type of exception that might be raised. The more specific the exception specified in the except clause, the more targeted the handling of the exception can be. For example, if you only want to handle "KeyError" exceptions, you can specify that explicitly in the except clause, as in the first example.

How to manage exceptions correctly in PyQt

I am wondering how to manage multiple exceptions in pyqt
I have a function 'encodeVideo()' that may trigger multiple exceptions.
def updateFilename(self):
try:
self.model.updateFilename(self.fileName)
except type_exc.PathIsEmpty as e:
self.errorDialog.errorTypeChanged(e)
self.errorDialog.show()
def updateOutput(self):
try:
self.model.updateOutput(self.encodeDialog.output)
except (type_exc.FileAlreadyExists, type_exc.PathNotExists) as e:
self.errorDialog.errorTypeChanged(e)
self.errorDialog.show()
def encodeVideo(self):
self.updateFilename()
self.updateOutput()
In my case, it is likely to trigger errors both in updateFilname() and updateOutput. When this happens, a dialog will show up and report both errors. However, I seem to manage the exceptions in a wrong way. For example, when error in self.updateFilename() occurs, that doesn't stop my code from continuing the next code self.updateOutput().
You want to handle exceptions as a high as possible in your method call stack; this usually means that exceptions are handled in the UI where the first call was made, if inside any of your methods you need to do something if an exception occurs, you should catch and re-throw the exception, here are some examples:
In your code, the first method that is called from the UI is encodeVideo, therefore, you want to catch and handle your exceptions there:
def updateFilename(self):
self.model.updateFilename(self.fileName)
def updateOutput(self):
self.model.updateOutput(self.encodeDialog.output)
def encodeVideo(self):
try:
self.updateFilename()
self.updateOutput()
except (type_exc.PathIsEmpty, type_exc.FileAlreadyExists, type_exc.PathNotExists) as e:
self.errorDialog.errorTypeChanged(e)
self.errorDialog.show()
Rethrow the exception
Let's imagine that if the call to updatedOutput fails, you want to do something specific, in this case, you can handle the exception in the inner method, but you should rethrow it again so it is handled by the calling method:
def updateOutput(self):
try:
self.model.updateOutput(self.encodeDialog.output)
except type_exc.FileAlreadyExists, e:
print("Do something")
raise type_exc.FileAlreadyExists(e)
def encodeVideo(self):
try:
self.updateFilename()
self.updateOutput()
except (type_exc.PathIsEmpty, type_exc.FileAlreadyExists, type_exc.PathNotExists) as e:
self.errorDialog.errorTypeChanged(e)
self.errorDialog.show()
This is basically a exception and error handling problem. And hence if there is error in any block then system takes it as an error handles or exception handles. Thus if first block of code gave error , then next block contain another handler exception so it is very simple that system treating it as a blocks of errors and exception.

Previous error being masked by current exception context

The following is an example I found at the website for Doug Hellman in a file named "masking_exceptions_catch.py". I can't locate the link at the moment. The exception raised in throws() is discarded while that raised by cleanup() is reported.
In his article, Doug remarks that the handling is non-intuitive. Halfway expecting it to be a bug or limitation in the Python version at the time it was written (circa 2009), I ran it in the current production release of Python for the Mac (2.7.6). It still reports the exception from cleanup(). I find this somewhat amazing and would like to see a description of how it is actually correct or desirable behavior.
#!/usr/bin/env python
import sys
import traceback
def throws():
raise RuntimeError('error from throws')
def nested():
try:
throws()
except:
try:
cleanup()
except:
pass # ignore errors in cleanup
raise # we want to re-raise the original error
def cleanup():
raise RuntimeError('error from cleanup')
def main():
try:
nested()
return 0
except Exception, err:
traceback.print_exc()
return 1
if __name__ == '__main__':
sys.exit(main())
Program output:
$ python masking_exceptions_catch.py
Traceback (most recent call last):
File "masking_exceptions_catch.py", line 24, in main
nested()
File "masking_exceptions_catch.py", line 14, in nested
cleanup()
File "masking_exceptions_catch.py", line 20, in cleanup
raise RuntimeError('error from cleanup')
RuntimeError: error from cleanup
Circling back around to answer. I'll start by not answering your question. :-)
Does this really work?
def f():
try:
raise Exception('bananas!')
except:
pass
raise
So, what does the above do? Cue Jeopardy music.
Alright then, pencils down.
# python 3.3
4 except:
5 pass
----> 6 raise
7
RuntimeError: No active exception to reraise
# python 2.7
1 def f():
2 try:
----> 3 raise Exception('bananas!')
4 except:
5 pass
Exception: bananas!
Well, that was fruitful. For fun, let's try naming the exception.
def f():
try:
raise Exception('bananas!')
except Exception as e:
pass
raise e
What now?
# python 3.3
4 except Exception as e:
5 pass
----> 6 raise e
7
UnboundLocalError: local variable 'e' referenced before assignment
# python 2.7
4 except Exception as e:
5 pass
----> 6 raise e
7
Exception: bananas!
Exception semantics changed pretty drastically between python 2 and 3. But if python 2's behavior is at all surprising to you here, consider: it's basically in line with what python does everywhere else.
try:
1/0
except Exception as e:
x=4
#can I access `x` here after the exception block? How about `e`?
try and except are not scopes. Few things are, actually, in python; we have the "LEGB Rule" to remember the four namespaces - Local, Enclosing, Global, Builtin. Other blocks simply aren't scopes; I can happily declare x within a for loop and expect to still be able to reference it after that loop.
So, awkward. Should exceptions be special-cased to be confined to their enclosing lexical block? Python 2 says no, python 3 says yes. But I'm oversimplifying things here; bare raise is what you initially asked about, and the issues are closely related but not actually the same. Python 3 could have mandated that named exceptions are scoped to their block without addressing the bare raise thing.
What does bare raise do‽
Common usage is to use bare raise as a means to preserve the stack trace. Catch, do logging/cleanup, reraise. Cool, my cleanup code doesn't appear in the traceback, works 99.9% of the time. But things can go south when we try to handle nested exceptions within an exception handler. Sometimes. (see examples at the bottom for when it is/isn't a problem)
Intuitively, no-argument raise would properly handle nested exception handlers, and figure out the correct "current" exception to reraise. That's not exactly reality, though. Turns out that - getting into implementation details here - exception info is saved as a member of the current frame object. And in python 2, there's simply no plumbing to handle pushing/popping exception handlers on a stack within a single frame; there's just simply a field that contains the last exception, irrespective of any handling we may have done to it. That's what bare raise grabs.
6.9. The raise statement
raise_stmt ::= "raise" [expression ["," expression ["," expression]]]
If no expressions are present, raise re-raises the last exception that
was active in the current scope.
So, yes, this is a problem deep within python 2 related to how traceback information is stored - in Highlander tradition, there can be only one (traceback object saved to a given stack frame). As a consequence, bare raise reraises what the current frame believes is the "last" exception, which isn't necessarily the one that our human brains believe is the one specific to the lexically-nested exception block we're in at the time. Bah, scopes!
So, fixed in python 3?
Yes. How? New bytecode instruction (two, actually, there's another implicit one at the start of except handlers) but really who cares - it all "just works" intuitively. Instead of getting RuntimeError: error from cleanup, your example code raises RuntimeError: error from throws as expected.
I can't give you an official reason why this was not included in python 2. The issue has been known since PEP 344, which mentions Raymond Hettinger raising the issue in 2003. If I had to guess, fixing this is a breaking change (among other things, it affects the semantics of sys.exc_info), and that's often a good enough reason not to do it in a minor release.
Options if you're on python 2:
1) Name the exception you intend to reraise, and just deal with a line or two being added to the bottom of your stack trace. Your example nested function becomes:
def nested():
try:
throws()
except BaseException as e:
try:
cleanup()
except:
pass
raise e
And associated traceback:
Traceback (most recent call last):
File "example", line 24, in main
nested()
File "example", line 17, in nested
raise e
RuntimeError: error from throws
So, the traceback is altered, but it works.
1.5) Use the 3-argument version of raise. A lot of people don't know about this one, and it is a legitimate (if clunky) way to preserve your stack trace.
def nested():
try:
throws()
except:
e = sys.exc_info()
try:
cleanup()
except:
pass
raise e[0],e[1],e[2]
sys.exc_info gives us a 3-tuple containing (type, value, traceback), which is exactly what the 3-argument version of raise takes. Note that this 3-arg syntax only works in python 2.
2) Refactor your cleanup code such that it cannot possibly throw an unhandled exception. Remember, it's all about scopes - move that try/except out of nested and into its own function.
def nested():
try:
throws()
except:
cleanup()
raise
def cleanup():
try:
cleanup_code_that_totally_could_raise_an_exception()
except:
pass
def cleanup_code_that_totally_could_raise_an_exception():
raise RuntimeError('error from cleanup')
Now you don't have to worry; since the exception never made it to nested's scope, it won't interfere with the exception you intended to reraise.
3) Use bare raise like you were doing before you read all this and live with it; cleanup code doesn't usually raise exceptions, right? :-)

Python reraise/recatch exception

I would like to know if it is possible in python to raise an exception in one except block and catch it in a later except block. I believe some other languages do this by default.
Here is what it would look like"
try:
something
except SpecificError as ex:
if str(ex) = "some error I am expecting"
print "close softly"
else:
raise
except Exception as ex:
print "did not close softly"
raise
I want the raise in the else clause to trigger the final except statement.
In actuality I am not printing anything but logging it and I want to log more in the case that it is the error message that I am not expecting. However this additional logging will be included in the final except.
I believe one solution would be to make a function if it does not close softly which is called in the final except and in the else clause. But that seems unnecessary.
What about writing 2 try...except blocks like this:
try:
try:
something
except SpecificError as ex:
if str(ex) == "some error I am expecting"
print "close softly"
else:
raise ex
except Exception as ex:
print "did not close softly"
raise ex
Only a single except clause in a try block is invoked. If you want the exception to be caught higher up then you will need to use nested try blocks.
As per python tutorial there is one and only one catched exception per one try statement.
You can find pretty simple example in tutorial that will also show you how to correctly use error formatting.
Anyway why do you really need second one? Could you provide more details on this?
You can do this using the six package.
Six provides simple utilities for wrapping over differences between Python 2 and Python 3.
Specifically, see six.reraise:
Reraise an exception, possibly with a different traceback. In the simple case, reraise(*sys.exc_info()) with an active exception (in an except block) reraises the current exception with the last traceback. A different traceback can be specified with the exc_traceback parameter. Note that since the exception reraising is done within the reraise() function, Python will attach the call frame of reraise() to whatever traceback is raised.

How to see the error and still keep the program on in the Python shell?

I know try/except can handle errors in my program.
But, is there a way of making the error be displayed in the program execution, be ignored and let the execution go on?
In VBScript and other VB-derived languages, you can get this sort of behavior with "ON ERROR GOTO NEXT".
No such behavior exists in Python. Even if you wrap each top-level statement like:
try:
do_something()
except Exception as e:
print e
try:
do_something_else()
except Exception as e:
print e
you'd still have the result that statements within do_something are skipped at the point the exception is thrown.
Though perhaps if you have a particular use-case in mind, there may be other acceptable answers. For instance, in a top-level loop:
while True:
cmd = get_command()
if cmd == 'quit': break
try:
run_command(cmd)
except Exception as e:
print "Error running " + cmd + ":"
print e
import traceback
try:
# do whatever you want
except Exception:
traceback.print_exc()
Of course you should be more specific in a real scenario, i.e. you shouldn't catch and ignore all Exception instances, only the ones you are interested in and you know that they are safe to ignore.
Also note that if an exception happens somewhere within the try..except block, the execution will continue after the try..except block, not at the next statement. This is probably the closest to what you want to achieve.

Categories