When I put in a finally clause, the raise statement in except doesn't work.
So the except block doesn't produce an Exception.
What am I missing? What do I need to do if I want to re-raise the Exception after the finally clause returns the value?
def test():
res = 1
try:
raise Exception
res = 2
except:
print('ha fallado')
raise
finally:
return res
test()
Solution:
def test():
res = 1
try:
raise Exception
res = 2
except:
print('ha fallado')
raise
finally:
# ... finally code that need to exec
pass
return res
print(test())
In this way if a Exception happened, the except block handle the exception and then raise it.
If not exception happened return the value.
Thanks for all the answers! So quick :)
Here is an answer that quotes the relevant part of the documentation:
The try clause is executed, including any except and else clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The finally clause is executed. If there is a saved exception it is re-raised at the end of the finally clause. If the finally clause raises another exception, the saved exception is set as the context of the new exception. If the finally clause executes a return or break statement, the saved exception is discarded:
>>> def f():
... try:
... 1/0
... finally:
... return 42
...
>>> f()
42
P.S. I don't quite understand what you actually want to achieve; as noted in the top answer linked by zmbq, you can't really have both.
That's because you put a return statement in the finally block. It means you really want to return a value, even if an exception is thrown.
See here for more information.
Related
What's the best way to catch exceptions which occur in the *loop header instead of the whole loop or body.
Take the following example
for value in complex_generator(): # throws exceptions I might want to catch
... # do work here - but don't catch any exception
What I don't consider helpful is wrapping the whole loop in a try and except block like so:
try:
for value in complex_generator(): # throws exceptions I might want to catch
... # exceptions raised here will also be caught :(
except Exception:
... # handle exception here
Inspired from golang one might encapsulate the try-except-block and always return two elements:
def wrapper(iterable):
try:
for value in iterable:
yield value, None
except Exception as e:
yield None, e
for value, err in wrapper(complex_generator()):
if err != None:
... # handle error
else:
... # do work but don't catch any exception here
This however doesn't feel pythonic and a type checker would also require a additional check. Any ideas?
There are two levels of errors: Those that the generator throws, and those that your worker code throws. I would use a nested try:
try:
for value in complex_generator():
try:
# do work here
except ValueError:
# catch ValueError and keep going
except OtherError:
# catch OtherError and keep going
# any other error breaks the loop
except ExpectedGeneratorError:
# handle generator exception here
except:
# handle more errors
You could extract the inner part into a worker function to keep things tidy.
Get the generator first?
try:
cg = complex_generator()
except Exception as e:
cg = [] # could alternatively have a success boolean here, and wrap the `for` loop below in an `if`
for value in cg:
...
I still think #Tomalak's answer is the most pythonic way to solve this problem. However I want to share the solution I ended up using, because both the complex_generator as well as the loop body might raise the same exception.
from typing import Iterable, Iterator, TypeVar
T = TypeVar("T")
def wrapper(iterable: Iterable[T]) -> Iterator[T | Exception]:
try:
for value in iterable:
yield value
except Exception as e:
yield e
for value in wrapper(complex_generator()):
if isinstance(value, Exception):
handle_generator_error(value)
else:
try:
process(value)
except Exception as e:
handle_loop_error(e)
Note: For more complex scenarios one might use match (structural pattern matching) and move the body error handling inside the process function.
I have a function within a module, that does something like this:
def some_func():
try:
# do some error-prone thing
raise ValueError
return 'calculated foo'
except AttributeError as err:
# handle it
pass
except:
print('Some other error happened, let\'s reraise it....')
raise
else:
pass
finally:
return 'default foo'
Then within my main program,
try:
val = some_func()
print('val=', val)
except:
print('In main except')
raise
else:
print('In main else')
pass
finally:
print('And we\'re done')
My output is:
Some other error happened, let's reraise it....
val= default foo
In main else
And we're done
No exception is raised.
At the risk of missing something obvious, why isn't the ValueError being reraised within my main? It almost seems like the return within my finally in some_func() is causing the exception not to be reraised, but this seems odd to me and I can't find any documentation of it. Here's what I think should be happpening, would like to understand where I'm off.
I call some_func() within my main program
some_func() raises ValueError
The ValueError is caught within the some_func() except, prints "Some other error happened" and reraises it.
Back in main, I thought the reraised ValueError should get caught by the except, should print 'In main except', reraise, and then the exception itself should be uncaught causing the program to halt. But I get no exception raised and wind up in the else clause instead.
This is intended behavior and described in the documentation:
If finally is present, it specifies a ‘cleanup’ handler. (...) If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. (...) If the finally clause executes a return, break or continue statement, the saved exception is discarded
try:
...
except (SomeError) as err:
...
else:
...
finally:
if err:
...
This gives an error: 'err not defined'. Because the exception argument - err - is not defined as far as the finally block is concerned. It appears then that the exception argument is local to the exception block.
You can get round it by copying err to another variable defined outside the block:
teleport = ""
try:
...
except (SomeError) as err:
teleport = err
else:
...
finally:
if teleport:
...
But why can't you simply reference the exception argument in the finally block? (Assuming I've not overlooked something else.)
try blocks will execute code that could possibly raise an exception. except block will execute the moment an exception is raised. The else block executes if no except is raised, and the finally block is executed no matter what.
There is no point in checking for an exception in the finally block when you can just do that in the else block.
Aside from that, the variable was likely garbage collected at the end of execution of the except block. It's similar to what happens with with blocks. This is why you can't do if err:
You cannot access just because exception is not raised and so variable is not defined, hence the undefined variable error. Besides there is no point in dealing with exception in your final block, you should do that stuff in except block itself.
I am experimenting with Python Decorators in order to understand what it happening and I have hit a head-scratcher.
My code is this (python 2.7.6):
import traceback
def dec(func):
def wrapped(*args, **kwargs):
try:
if flag:
print 'flagged'
else:
print 'unflagged'
except NameError as e:
print 'error?'
raise
finally:
return func(*args, **kwargs)
return wrapped
#dec
def foo(x):
print x
foo(3)
when run, the output is:
error?
3
I expected that calling foo(3) would raise:
NameError: global name 'flag' is not defined
Why is the "raise" not raising? Clearly, the error is caught - the print from the Except block is executed...
The return in the finally overrides and cancels any exception or function return that might have triggered the finally block. This is documented in the Python Language Reference:
If finally is present, it specifies a ‘cleanup’ handler. The try
clause is executed, including any except and else clauses. If an
exception occurs in any of the clauses and is not handled, the
exception is temporarily saved. The finally clause is executed. If
there is a saved exception, it is re-raised at the end of the
finally clause. If the finally clause raises another exception or
executes a return or break statement, the saved exception is
discarded:
For example:
def f():
try:
1/0
finally:
return
f() # No exception
def g():
try:
return 1
finally:
return 0
g() # 0
def h():
try:
raise NameError
finally:
raise TypeError
h() # TypeError
I find myself doing this when I want to catch an exception, always run some specific code, then re-raise the original exception:
try:
error = False
# do something that *might* raise an exception
except Exception:
error = True
finally:
# something I *always* want to run
if error:
raise
I am using the flag because calling raise without a previous exception raises a TypeError. Is there a more Pythonic way to do with without the flag?
Raise the exception in the except handler:
try:
# do something that *might* raise an exception
except Exception:
raise
finally:
# something I *always* want to run
The finally suite is always going to be executed wether or not you re-raised the exception.
From the documentation:
If finally is present, it specifies a ‘cleanup’ handler. The try clause is executed, including any except and else clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The finally clause is executed. If there is a saved exception it is re-raised at the end of the finally clause.
Note that if the finally suite uses a break or return statement, the saved exception is discarded:
If the finally clause executes a return or break statement, the saved exception is discarded:
def f():
try:
1/0
finally:
return 42
>>> f()
42
but if you issue a break, continue or return in the try suite, the finally suite is executed still:
When a return, break or continue statement is executed in the try suite of a try...finally statement, the finally clause is also executed ‘on the way out.’
Note that before Python 2.5, you could not even combine except and finally suites in the same try statement; see PEP 341: Unified try/except/finally. Instead, you were expected to nest try statements:
try:
try:
# some code that could raise an exception
except SomeException:
# exception handler
finally:
# cleanup code, always executed
finally will always execute, no matter what happens in the try or except block, or whether the except block is even present.
Both of these will work:
try:
# do something that *might* raise an exception
finally:
# something I *always* want to run
try:
# do something that *might* raise an exception
except Exception:
raise
finally:
# something I *always* want to run