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)
...
>>>
Related
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 !!")
Say I have a block of exception statements:
try:
expression
except err1:
#process error
...
...
except err10:
#process error
and I want to call sys.exit(1) if ANY of the exceptions are raised. Do I have to call it manually in every single sub-block or is there a built in way to have a statement akin to:
...
except err10:
#process error
"if any of these exception were raised":
sys.exit(1)
One thing you could do is:
flag = False
try:
expression
flag = True
except err1:
#process error
...
...
except err10:
#process error
if not flag:
sys.exit(1) #exit program
If the flag is False, that means that you didn’t pass through the try loop, and so an error was raised.
In Python, there is an optional else block which is executed in case no exception is raised. You may use this to set a flag for you code and exit the code out of the try/except block as:
is_exception = True
try:
expression
except err1:
# ... something
except err10:
# ... something else
else:
# This will be executed if there is no exception
is_exception = False
if is_exception:
sys.exit(1)
raised = True
try:
expression
except err1:
# process error
raise
...
except err10:
# process error
raise
else:
# if no error was raised
raised = False
finally:
if raised:
raise SystemExit
Here's what I was talking about in my comment:
isok = False
try:
#try to do something
isok = True
except err1:
#do something besides raising an exception
except err5:
#do something besides raising an exception
if not isok:
raise SystemExit
When I execute the following code, I am getting an exception instead of normal behavior.
try:
fun1(1)
fun1(1)
except Exception as e:
print "exception"
else:
print "hi"
def fun1(i):
if i == 0:
raise Exception()
else:
print "no exception"
The above code doesn't have any exceptions, so instead of printing "no exception" it shows "exception" as output.
You do have an exception; you are catching the NameError exception (which is a subclass of Exception). You get that exception because fun1 doesn't exist yet.
Move the fun1() definition to before the try..except or move that code into a function you call after fun1() has been created.
You'd also be better off with using a less-generic exception, it'd would have made the problem apparent much earlier; you could use ValueError if you want to test things, it is far less likely to be thrown for unrelated issues:
>>> try:
... fun1(1)
... fun1(1)
... except ValueError:
... print "exception"
... else:
... print "hi"
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
NameError: name 'fun1' is not defined
>>> def fun1(i):
... if i == 0:
... raise ValueError()
... else:
... print "no exception"
...
>>> try:
... fun1(1)
... fun1(1)
... except ValueError:
... print "exception"
... else:
... print "hi"
...
no exception
no exception
hi
>>> try:
... fun1(0)
... except ValueError:
... print "exception"
... else:
... print "hi"
...
exception
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
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
>>>