Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does return inside finally gives empty dictionary?

I want to keep a track of exceptions inside a dictionary and return the same. However when I do so, finally block gives me an empty dictionary. The logic pretty much works for scalars. Can someone explain the behavior please.

In scalar context:

def test():
    temp = 1
    try:
        raise ValueError("sdfs")
    except:
        temp = 2
    finally:
        temp = temp + 3
        return temp
test()
5

With dictionary:

def test():
    temp = dict()
    try:
        raise ValueError("something")
    except Exception as error:
        print("error is :{}".format(error))
        temp['except'] = "something" + error
    finally:
        return temp

test()
error is : something
{}
like image 608
saz01 Avatar asked Dec 12 '17 16:12

saz01


2 Answers

You caused another exception to be raised in the exception handler, which is swallowed because there is a finally handler that returns from the function.

You can't just concatenate an exception object and a string, so an additional TypeError is raised, and the assignment to the dictionary is never reached.

Converting the exception to a string first works:

>>> def test():
...     temp = dict()
...     try:
...         raise ValueError("something")
...     except Exception as error:
...         print("error is :{}".format(error))
...         temp['except'] = "something" + str(error)
...     finally:
...         return temp
...
>>> test()
error is :something
{'except': 'somethingsomething'}

From the try statement 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. 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[.]

(Bold emphasis mine).

like image 108
Martijn Pieters Avatar answered Nov 14 '22 22:11

Martijn Pieters


The line "something" + error raises an error which is discarded because you returned from finally. Due to the error the assignment to the dictionary temp is never performed giving the impression that nothing happens.

This is stated in the documentation for try-except:

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

(Emphasis mine)

Without an error (i.e temp['except'] = error you get the expected behavior:

>>> test()
error is :something
{'except': ValueError('something')}
like image 32
Dimitris Fasarakis Hilliard Avatar answered Nov 14 '22 21:11

Dimitris Fasarakis Hilliard