Python: try except else Strange behavior - python

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

Related

Python2 vs Python3 : Exception variable not defined [duplicate]

This question already has an answer here:
Why do I get a `NameError` (or `UnboundLocalError`) from using a named exception after the `except` block?
(1 answer)
Closed last month.
I wrote this small snippet in python2 (2.7.18) to catch the exception in a variable and it works
>>> ex = None
>>> try:
... raise Exception("test")
... except Exception as ex:
... print(ex)
...
test
>>> ex
Exception('test',)
>>>
>>> ex2 = None
>>> try:
... raise Exception("test")
... except Exception as ex2:
... print(ex2)
... finally:
... print(ex2)
...
test
test
When I run the same in python3 (3.10.8), I get NameError
>>> ex = None
>>> try:
... raise Exception("test")
... except Exception as ex:
... print(ex)
...
test
>>> ex
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'ex' is not defined. Did you mean: 'hex'?
>>>
>>> ex2 = None
>>> try:
... raise Exception("test")
... except Exception as ex2:
... print(ex2)
... finally:
... print(ex2)
...
test
Traceback (most recent call last):
File "<stdin>", line 6, in <module>
NameError: name 'ex2' is not defined
What is the reason for this? Is the new python3 compiler doing optimisation where None assignment doesn't mean anything or is the try/except clause doing some magic?
What is the workaround for this which works in both python2 and python3?
It's not a magic really. It's documented here:
When an exception has been assigned using as target, it is cleared at the end of the except clause. This is as if:
except E as N:
foo
was translated to
except E as N:
try:
foo
finally:
del N
And the reason:
Exceptions are cleared
because with the traceback attached to them, they form a reference
cycle with the stack frame, keeping all locals in that frame alive
until the next garbage collection occurs.
You can get the traceback object with ex.__traceback__.
What is the workaround for this which works in both python2 and
python3?
You have to explicitly assign it to a different name(something other than target in as target as it's gonna get deleted):
exc_object = None
try:
raise Exception("test")
except Exception as ex:
print(ex)
exc_object = ex
print(exc_object)
Note: This is only the case with except block and has nothing to do with as assignment statement as you can see in context managers: with...as... block for example.

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

How to catch an exception that was already caught by a nested function call in Python

In the following example I would like to be able to call the function b() from a(), and for a() to recognize that an IndexError has occurred in b().
def a():
try:
b()
except IndexError:
print('Caught the error in a!')
def b():
array = ["First", "Second"]
try:
print(array[2])
except IndexError:
print('Caught the error in b!')
a()
The output of this script looks like this:
Caught the error in b!
What I would like to see is a way for this script to output the following:
Caught the error in b!
Caught the error in a!
I would greatly prefer an answer with the constraint that modifications only be made to the function a() for the specific real-world scenario I'm actually dealing with, but will accept another answer if this isn't possible.
My (incorrect) intuition would assume that the script is simply terminating after the exception is being caught in b(), but the following example proves that this is not the case:
def a():
try:
b()
except IndexError:
print('Caught the error in a!')
print('Both chances are over now.')
def b():
array = ["First", "Second"]
try:
print(array[2])
except IndexError:
print('Caught the error in b!')
a()
This script will output the following:
Caught the error in b!
Both chances are over now.
Which proves to me that the function a() will continue executing after the IndexError exception occurs in b().
def a():
try:
b()
except IndexError:
print('Caught the error in a!')
print('Both chances are over now.')
def b():
array = ["First", "Second"]
try:
print(array[2])
except IndexError:
print('Caught the error in b!')
raise
a()
Use <exception> as e and raise <exception> from e:
def a():
try:
b()
except IndexError:
print('Caught the error in a!')
print('Both chances are over now.')
def b():
array = ["First", "Second"]
try:
print(array[2])
except IndexError as e:
raise IndexError('Caught the error in b!') from e

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