Thursday 25 April 2024

try...catch...finally - Not (quite) what I expected

Consider this bit of code:-

def foo(): print("Called foo()") try: print("In the 'try' block") 10 / 0 except Exception as e: print("Got an exception: ", str(e)) finally: print("In the 'finally' block") print("About to return from foo() at the bottom") foo()

What do you think it will output? It probably won't come as a surprise:-

$ python3 a.py Called foo() In the 'try' block Got an exception: division by zero In the 'finally' block

Now consider this slightly variation:-

def foo(): print("Called foo()") try: print("In the 'try' block") 10 / 0 except Exception as e: print("Got an exception: ", str(e)) return print("In the 'except' block, after the 'return' statement") finally: print("In the 'finally' block") print("About to return from foo() at the bottom") foo()

Note the addition of the return statement to the except block. I would have bet money that it would behave differently: that the return in the except block would return straight out of foo(). and the finally block would not be reached. I would have lost the money:-

$ python3 a.py Called foo() In the 'try' block Got an exception: division by zero In the 'finally' block

The return statement in the except block does not exit foo(): rather it drops to the finally block. I can't claim credit for noticing this: my youngest son got caught out by this behaviour and told me about it.

One other thing to notice: in either case, if an exception occurs and is caught, foo() returns at the bottom of the finally block: the print statement at the bottom of foo() is never reached. Ofcourse, of no exception occurs, the finally block is still run and the rest of foo() runs as you would expect.

It got me to wondering if other languages behaved the same way. Let's try it in Javascript:-

function foo() { console.log("Called foo()") try { console.log("In the 'try' block") throw new Error("oops") } catch(e) { console.log("Got an exception: ", e) return console.log("In the 'catch' block, after the 'return' statement") } finally { console.log("In the 'finally' block") } console.log("About to return from foo() at the bottom") } foo()

It behaves in exactly the same way:-

$ node a.js Called foo() In the 'try' block Got an exception: Error: oops at foo (/home/eamonn/tmp/a.js:7:15) at Object. (/home/eamonn/tmp/a.js:22:1) at Module._compile (node:internal/modules/cjs/loader:1198:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1252:10) at Module.load (node:internal/modules/cjs/loader:1076:32) at Function.Module._load (node:internal/modules/cjs/loader:911:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) at node:internal/main/run_main_module:22:47 In the 'finally' block

Ofcourse, this is all well documented: it's just not exactly how I thought it worked. RTFM, people 😉

No comments:

Post a Comment