Determine cause of exception - python

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'>

Related

Raising multiple exception in python

Is there any way to raise multiple exceptions in python? In the following example, only the first exception is raised.
l = [0]
try:
1 / 0
except ZeroDivisionError as e:
raise Exception('zero division error') from e
try:
l[1]
except IndexError as e:
raise Exception('Index out of range') from e
Is there any other way?
Once an exception is raised and not catched, the execution of your program will stop. Hence, only one exception can be launched in such a simple script.
If you really want your program to raise multiple exceptions, you could however create a custom Exception representing multiple exceptions and launch it inside an except block. Example:
class ZeroDivAndIndexException(Exception):
"""Exception for Zero division and Index Out Of Bounds."""
I = [0]
try:
1 / I[0]
except ZeroDivisionError as e:
try:
I[1]
# Here you may raise Exception('zero division error')
except IndexError as e2:
raise ZeroDivAndIndexException()
Here my solution a bit long but seems to work :
class CustomError(Exception):
pass
l = [0]
exeps = []
try:
1 / 0
except ZeroDivisionError as e:
exeps.append(e)
try:
l[1]
except IndexError as e:
exeps.append(e)
if len(exeps)!=0:
[print(i.args[0]) for i in exeps]
raise CustomError("Multiple errors !!")

How to raise Exception with array?

I tried to throw an exception with array data:
raise Exception([ValidateError.YEAR, row])
When I tried to catch it I get this error:
'Exception' object is not subscriptable
Code is:
except Exception as e:
#invalid
print(e[0])
To access the Exception arguments you passed as a list, you should use .args.
So, I believe you were looking for the following:
except Exception as e:
#valid
print(e.args[0][0])
As a side note, you can pass multiple arguments without them being in a list:
raise Exception(ValidateError.YEAR, row)
And then you need one index less:
except Exception as e:
#also valid
print(e.args[0])
You can subclass Exception and implement the __getitem__ function like so:
class MyException(Exception):
def __init__(self, l):
self.l = l
def __getitem__(self, key):
return self.l[key]
try:
raise MyException([1, 2, 3])
except MyException as e:
print(e[0])
running it:
python3 main.py
1

Python Logging Multiple Errors on Single Line

I'm trying to log some expected errors.
Initially, I wrote my script like:
except (BadZipFile, MemoryError) as e:
logger.error(f'No: {n} - {filename} = {e}')
But I noticed in the logger, only the BadZipFile error message seemed to make it. The MemoryError logs seemed to be blank after the = sign
I thought maybe the e was only storing the BadZipFile error message since it comes first, so I tried making a tuple:
except (BadZipFile, MemoryError) as (eb, em):
logger.error(f'No: {n} - {filename} = {eb, em}')
but of course the syntax is wrong. So what's going wrong with my code initially? Why is the MemoryError log not storing?
Whichever exception occurs first will get caught in except block. So, I think in your case 1st exception raising is BadZipFile. refer to #LMKR examples.
try to raise an exception explicitly to check whether it's working or not...
>>> try:
... if True:
... raise MemoryError("MemoryError occured")
... except (BadZipFile, MemoryError) as e:
... print(e)
Execution of program will be stopped when it occurs an exception and move to the except block. So when an exception occurred it is not possible to get another in the same try block. So will always receives one log of exception.
let me explain it with a bit of code, consider we have two types of errors, IndexError and ZeroDivisionError
Case1:
>>> l = []
>>> a = 1
>>> b = 0
>>> try:
... x = l[0]
... y = a/b
... except (IndexError, ZeroDivisionError) as e:
... print(e)
...
list index out of range
Case2:
>>> try:
... x = a/b
... y = l[0]
... except (IndexError, ZeroDivisionError) as e:
... print(e)
...
division by zero
Case3:
>>> try:
... x = l[0]/b
... except (IndexError, ZeroDivisionError) as e:
... print(e)
...
list index out of range
>>>
Case1, IndexError is raised first, so except bock is called with IndexError
Case2, ZeroDivisionError raised first, so except block is called with ZeroDivisionError
Case3, even though both exceptions are in single line line of execution happens from indexing so IndexError is raised.
EDIT:
In your case BadZipFile exception raised first, so your except block except (BadZipFile, MemoryError) as e: is called with BadZipFile exception. That's why your output is empty for MemoryError.
Even if MemoryError raised first with empty arguments then it will log nothing.
>>> try:
... raise MemoryError("This is memory error")
... except MemoryError as e:
... print(e)
...
This is memory error
>>> try:
... raise MemoryError()
... except MemoryError as e:
... print(e)
...
>>>

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

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.

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
>>>

Categories