Why can't I call an exception after I except it? - python

Why can't I raise an Exception instance after I catch that Exception class? Oddly enough, I run into this error when I run the script from a function but not when ran directly in the python shell.
In [2]: def do():
...: try:
...: raise ValueError('yofoo')
...: except TypeError, ValueError:
...: raise ValueError('yo')
...:
In [3]: do()
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-3-30c46b84d9a4> in <module>()
----> 1 do()
<ipython-input-2-b62158d6343b> in do()
1 def do():
2 try:
----> 3 raise ValueError('yofoo')
4 except TypeError, ValueError:
5 raise ValueError('yo')
UnboundLocalError: local variable 'ValueError' referenced before assignment
Expected error here:
In [3]: try:
...: raise ValueError("foo")
...: except ValueError:
...: raise ValueError("bar")
...:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-3-d5c83419a5ea> in <module>()
2 raise ValueError("foo")
3 except ValueError:
----> 4 raise ValueError("bar")
5
ValueError: bar

except TypeError, ValueError:
should be
except (TypeError, ValueError):
When you use except TypeError, ValueError:, you are assigning the Exception instance to the variable name ValueError.
With your current code, when Python parses the do function, it notes ValueError is a local variable because except TypeError, ValueError: assigns a value to ValueError. But when you reference it in the first try-suite, with raise ValueError('yofoo'), the local variable ValueError has no value. So you get UnboundLocalError.
From the docs:
... except (RuntimeError, TypeError, NameError):
... pass
Note that the parentheses around this tuple are required, because
except ValueError, e: was the syntax used for what is normally written
as except ValueError as e: in modern Python (described below). The old
syntax is still supported for backwards compatibility. This means
except RuntimeError, TypeError is not equivalent to except
(RuntimeError, TypeError): but to except RuntimeError as TypeError:
which is not what you want.

Related

Determine cause of exception

In this example
try:
raise ValueError
except ValueError as e:
raise IndexError from e
The traceback notes
...
ValueError:
The above exception was the direct cause of the following exception:
IndexError
...
If I were to catch the IndexError, how would I inspect the traceback to discover that it was caused by a ValueError?
According to PEP 3134, raise ... from sets the __cause__ attribute in the exception. You can access that:
try:
try:
raise ValueError
except ValueError as e:
raise IndexError from e
except IndexError as ee:
print(type(ee.__cause__))
Result:
<class 'ValueError'>

Do try-except many times but raise error if one of them fails (at the end)

So I have say 3 functions I would like to run. Each of them can fail. I would like to built try-except around them to:
let as many as possible out of 3 run AND
raise an error at the end if any of them failed. Is this possible?
The below code fails as operation D (in the middle fails) so E is never reached:
try:
c = 3 + 6
print(c)
except TypeError:
raise TypeError("Wrong type provided in operation C")
try:
d = 3 + '6'
print(d)
except TypeError:
raise TypeError("Wrong type provided in operation D")
try:
e = 7 + 5
print(e)
except TypeError:
raise TypeError("Wrong type provided in operation E")
def f1():
print("f1")
def f2():
raise TypeError
print("f2")
def f3():
print("f3")
err = None
for f in [f1, f2, f3]:
try:
f()
except TypeError as e:
# store first error
if not err:
err = e
if err:
raise err
Output:
f1
f3
[...]
TypeError
If your functions take arguments, you can loop over
[(f1, f1_args, f1_kwargs), (f2, f2_args, f2_kwargs), (f3, f3_args, f3_kwargs)]
Inspired by a comment I tried to come up with a nifty context manager that sort of raises all the exceptions in a nested fashion upon exiting. Comments are welcome.
class ErrorCollector:
def __init__(self):
self.errors = []
def exec(self, f, suppress_types=None, *args, **kwargs):
suppress_types = tuple(suppress_types) if suppress_types else ()
try:
return f(*args, **kwargs)
except suppress_types as e:
self.errors.append(e)
def _raise_all(self, errors):
if len(errors) == 1:
raise errors[0]
for e in errors:
try:
raise e
except type(e):
self._raise_all(errors[1:])
def __enter__(self):
return self
def __exit__(self, exctype, excinst, exctb):
if excinst is not None:
self.errors.append(excinst)
self._raise_all(self.errors)
def f1():
print('f1')
def f2():
raise TypeError('TypeError in f2')
print('f2')
def f3():
raise ValueError('ValueError in f3')
print('f3')
def f4():
raise TypeError('TypeError in f4')
print('f4')
def f5():
print('f5')
def f6():
raise ZeroDivisionError('ZeroDivisionError in f6')
print('f6')
def f7():
print('f7')
Now you can use:
suppress = [TypeError, ValueError]
with ErrorCollector() as ec:
for f in (f1, f2, f3, f4, f5, f6, f7):
ec.exec(f, suppress)
with the output:
f1
f5
[...]
TypeError: TypeError in f2
During handling of the above exception, another exception occurred:
[...]
ValueError: ValueError in f3
During handling of the above exception, another exception occurred:
[...]
TypeError: TypeError in f4
During handling of the above exception, another exception occurred:
[...]
ZeroDivisionError: ZeroDivisionError in f6
Note that f7 is not executed because the ZeroDivisionError was not suppressed.
Other responses have provided ad-hoc handlers of various manners. An interesting alternative if this is a recurring need is to build a context manager similar to contextlib.suppress, something along the lines of (untested):
class Suppressor:
def __init__(self, *to_suppress):
self.to_suppress = to_suppress # empty = suppress all
self.exceptions = []
def __enter__(self):
pass
def __exit__(self, etype, val, _):
if etype is not None:
# if the exception is selected
if not self.to_suppress or isinstance(val, self.to_suppress):
# store and suppress it
self.exceptions.append(val)
return True
# otherwise ignore
Usage:
suppressor = Suppressor(TypeError)
with suppressor:
c = 3 + 6
print(c) # 9
with suppressor:
d = 3 + '6' # error (suppressed)
print(d)
with suppressor:
e = 7 + 5
print(e) # 12
if suppressor.exceptions:
# TypeError("unsupported operand type(s) for +: 'int' and 'str'")
print(repr(suppressor.exceptions[0]))
with suppressor:
# ValueError: invalid literal for int() with base 10: 'a'
e = 7 + int('a')
The most simple would be:
errors = 0
try:
c = 3 + 6
print(c)
except TypeError:
errors +=1
try:
d = 3 + '6'
print(d)
except TypeError:
errors +=1
try:
e = 7 + 5
print(e)
except TypeError:
errors +=1
if errors!=0:
raise TypeError("Wrong type provided in some operation")
If you want to get which operation threw TypeError you can assign it in 3 different variable.
Hope this helps!
You could collect the exceptions. Then, you can actually reraise them rather than building a new exception, by having access to each.
exceptions = []
try:
raise ValueError("thing 1 error") # simulate error in first function
except ValueError as exc:
exceptions.append(exc)
try:
raise ValueError("thing 2 error") # simulate error in second function
except ValueError as exc:
exceptions.append(exc)
try:
raise ValueError("thing 3 error") # simulate error in third function
except ValueError as exc:
exceptions.append(exc)
You can then optionally raise a new exception. Something like:
if exceptions:
raise Exception("something went wrong")
Which gives:
Traceback (most recent call last):
File "./main.py", line 21, in <module>
raise Exception("something went wrong")
Exception: something went wrong
Or, you can directly access any or all of these exceptions. Something like:
raise exceptions[1]
Which gives:
Traceback (most recent call last):
File "./main.py", line 25, in <module>
raise exceptions[1]
File "./main.py", line 11, in <module>
raise ValueError("thing 2 error") # simulate error in second function
ValueError: thing 2 error

Python handling multiple exceptions

I want to handle a specific exception in a certain way, and generically log all the others.
This is what I have:
class MyCustomException(Exception): pass
try:
something()
except MyCustomException:
something_custom()
except Exception as e:
#all others
logging.error("{}".format(e))
The problem is that even MyCustomException will be logged because it inherits from Exception. What can I do to avoid that?
What else is going on in your code?
MyCustomException should be checked and handled before flow ever gets to the second except clause
In [1]: def test():
...: try:
...: raise ValueError()
...: except ValueError:
...: print('valueerror')
...: except Exception:
...: print('exception')
...:
In [2]: test()
valueerror
In [3]: issubclass(ValueError,Exception)
Out[3]: True
Only the first matching except block will be executed:
class X(Exception): pass
try:
raise X
except X:
print 1
except Exception:
print 2
only prints 1.
Even if you raise an exception in an except block, it won't be caught by the other except blocks:
class X(Exception): pass
try:
raise X
except X:
print 1
0/0
except Exception:
print 2
prints 1 and raises ZeroDivisionError: integer division or modulo by zero

How to re-raise an exception in nested try/except blocks?

I know that if I want to re-raise an exception, I simple use raise without arguments in the respective except block. But given a nested expression like
try:
something()
except SomeError as e:
try:
plan_B()
except AlsoFailsError:
raise e # I'd like to raise the SomeError as if plan_B()
# didn't raise the AlsoFailsError
how can I re-raise the SomeError without breaking the stack trace? raise alone would in this case re-raise the more recent AlsoFailsError. Or how could I refactor my code to avoid this issue?
As of Python 3, the traceback is stored in the exception, so a simple raise e will do the (mostly) right thing:
try:
something()
except SomeError as e:
try:
plan_B()
except AlsoFailsError:
raise e # or raise e from None - see below
The traceback produced will include an additional notice that SomeError occurred while handling AlsoFailsError (because of raise e being inside except AlsoFailsError). This is misleading because what actually happened is the other way around - we encountered AlsoFailsError, and handled it, while trying to recover from SomeError. To obtain a traceback that doesn't include AlsoFailsError, replace raise e with raise e from None.
In Python 2 you'd store the exception type, value, and traceback in local variables and use the three-argument form of raise:
try:
something()
except SomeError:
t, v, tb = sys.exc_info()
try:
plan_B()
except AlsoFailsError:
raise t, v, tb
Even if the accepted solution is right, it's good to point to the Six library which has a Python 2+3 solution, using six.reraise.
six.reraise(exc_type, exc_value, exc_traceback=None)
Reraise an exception, possibly with a different traceback.
[...]
So, you can write:
import six
try:
something()
except SomeError:
t, v, tb = sys.exc_info()
try:
plan_B()
except AlsoFailsError:
six.reraise(t, v, tb)
As per Drew McGowen's suggestion, but taking care of a general case (where a return value s is present), here's an alternative to user4815162342's answer:
try:
s = something()
except SomeError as e:
def wrapped_plan_B():
try:
return False, plan_B()
except:
return True, None
failed, s = wrapped_plan_B()
if failed:
raise
Python 3.5+ attaches the traceback information to the error anyway, so it's no longer necessary to save it separately.
>>> def f():
... try:
... raise SyntaxError
... except Exception as e:
... err = e
... try:
... raise AttributeError
... except Exception as e1:
... raise err from None
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in f
File "<stdin>", line 3, in f
SyntaxError: None
>>>

python try except 0

I am reading some python code written a while ago, and found this:
try:
# do some stuff
except 0:
# exception handling stuff
And I'm just not sure what except 0 means? I do have my guesses: Supposed to catch nothing i.e. let the exception propagate or it could be some sort of switch to turn debugging mode on and off by removing the 0 which will then catch everything.
Can anyone lend some insight? a google search yielded nothing...
Thanks!
Some sample code (by request):
try:
if logErrors:
dbStuffer.setStatusToError(prop_id, obj)
db.commit()
except 0:
traceback.print_exc()
From what I understand, This is very useful for debugging purposes (Catching the type of exception)
In your example 0 acts as a placeholder to determine the type of exception.
>>> try:
... x = 5/1 + 4*a/3
... except 0:
... print 'error'
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
NameError: name 'a' is not defined
>>> try:
... x = 5/0 + 4*a/3
... except 0:
... print 'error'
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero
In the first case, the exception is NameError and ZeroDivisionError in the second.
the 0 acts as a placeholder for any type of exception being caught.
>>> try:
... print 'error'
... except:
...
KeyboardInterrupt
>>> try:
... x = 5/0 + 4*a/3
... except:
... print 'error'
...
error
From the Python docs:
"[...] the [except] clause matches the exception if the resulting object is “compatible” with the exception. An object is compatible with an exception if it is the class or a base class of the exception object, or a tuple containing an item compatible with the exception."
In effect the type of the expression is used to determine whether the except clauses matches the exception. As 0 is of an integer type, and exception of that type would match.
Since integers cannot be raised as exception, this is a disabled exceptclass that will not catch anything.

Categories