try/except/finally

F

Frank B

Ok; this is a bit esoteric.

So finally is executed regardless of whether an exception occurs, so states the docs.

But, I thought, if I <return> from my function first, that should take precedence.

au contraire

Turns out that if you do this:

try:
failingthing()
except FailException:
return 0
finally:
return 1

Then finally really is executed regardless... even though you told it to return.

That seems odd to me.
 
R

Roy Smith

Frank B said:
Ok; this is a bit esoteric.

So finally is executed regardless of whether an exception occurs, so states
the docs.

But, I thought, if I <return> from my function first, that should take
precedence.

au contraire

Turns out that if you do this:

try:
failingthing()
except FailException:
return 0
finally:
return 1

Then finally really is executed regardless... even though you told it to
return.

That seems odd to me.

That's exactly what it's supposed to do. The idea of finally is, "No
matter what else happens, including calling sys.exit(), make sure this
code executed". It's typically used to release some critical resource
which was acquired in the body of the try block.

https://docs.python.org/2/reference/compound_stmts.html#the-try-statement
says:

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

The only way I can think of to bypass a finally block would be to call
os._exit(), or send yourself a kill signal.
 
F

Frank B

Ok; thanks for the underscore and clarification. Just need to adjust my thinking a bit.
 
N

Ned Batchelder

Ok; thanks for the underscore and clarification. Just need to adjust my thinking a bit.

Did this come up in real code? I've seen this point about
finally/return semantics a number of times, but haven't seen real code
that needed adjusting based on it.
 
E

Ethan Furman

Did this come up in real code? I've seen this point about finally/return semantics a number of times, but haven't seen
real code that needed adjusting based on it.

I don't remember if I almost had this in real code or if I learned about it first, but it can definitely be a gotcha.
It seems to me that if the try block exits with an explicit return, and then the finally block exits with an explicit
return, some kind of error ought to be raised.
 
C

Chris Angelico

I don't remember if I almost had this in real code or if I learned about it
first, but it can definitely be a gotcha. It seems to me that if the try
block exits with an explicit return, and then the finally block exits with
an explicit return, some kind of error ought to be raised.

I'd go a little simpler: A return statement inside a finally block is
code smell.

ChrisA
 
D

Dave Angel

Frank B said:
Ok; this is a bit esoteric.

So finally is executed regardless of whether an exception occurs, so states the docs.

But, I thought, if I <return> from my function first, that should take precedence.

au contraire

Turns out that if you do this:

try:
failingthing()
except FailException:
return 0
finally:
return 1

Then finally really is executed regardless... even though you told it to return.

That seems odd to me.

The thing that's odd to me is that a return is permissible inside
a finally block. That return
should be at top level, even with the finally line. And of course
something else should be in the body of the finally
block.

If you wanted the finally block to change the return value, it
should do it via a variable.

retval = 0
try:
failingthing()
except FailException:
return retval
finally:
retval =1
return something

I imagine the finally clause was designed to do cleanup, like
closing files. And it certainly predated the with statement.
 
R

Rustom Mody

I'd go a little simpler: A return statement inside a finally block is
code smell.

Some people¹ think that gotos are a code-smell.

And since both return and exceptions are thinly veiled gotos, what we
have here are two smells outsmelling each other.

¹ I am not exactly those people.
A chap called E W Dijkstra made the statement: "Goto statement considered
harmful" and became famous.
The chap who taught me programming said to me: "What the goto does to
control structure, the assignment does to data structure"
He did not become famous.
However in my view he made the more intelligent statement
 
M

Marko Rauhamaa

Mark Lawrence said:
I agree, the code smell is the return in the except block.

Here's a regular pattern that I use for nonblocking I/O:

def poll(self):
try:
message = self.sock.recv(0x10000)
except IOError as e:
if e.errno == errno.EAGAIN:
return
if errcode == errno.EINTR:
self.trigger()
return
self.handle_io_error(e.errno)
return
self.trigger()
self.handle_recv(message)

Does anyone have an example motivating a return from finally? It seems
to me it would always be a bad idea as it silently clears all unexpected
exceptions.


Marko
 
J

Joshua Landau

Does anyone have an example motivating a return from finally? It seems
to me it would always be a bad idea as it silently clears all unexpected
exceptions.

In a general sense:

try:
something_that_can_break()
return foo() # before clean_up
finally:
clean_up()
if default:
return default() # after clean_up()

What's the best replacement? Note: I've never done this.

---

I do sometimes use

try:
return x
finally:
x += 1

over

ret = x
x += 1
return ret

now-a-days.
 
I

Ian Kelly

In a general sense:

try:
something_that_can_break()
return foo() # before clean_up
finally:
clean_up()
if default:
return default() # after clean_up()

What's the best replacement? Note: I've never done this.

Why not just move the default out of the finally block?

try:
something_that_can_break()
return foo() # before clean_up
finally:
clean_up()
if default:
return default() # after clean_up()
 
J

Joshua Landau

The only way I can think of to bypass a finally block would be to call
os._exit(), or send yourself a kill signal.

If you're willing to use implementation details...

---

# BreakN.py

import sys

# Turn tracing on if it is off
if sys.gettrace() is None: sys.settrace(lambda frame, event, arg: None)

def break_n(n):
frame = sys._getframe().f_back

for _ in range(n):
frame.f_trace = skip_function_tracer
frame = frame.f_back

def skip_function_tracer(frame, event, arg):
try:
# Skip this line
while True:
frame.f_lineno += 1

except ValueError as e:
# Finished tracing function; remove trace
pass

---

# Thing_you_run.py

from BreakN import break_n

def foo():
try:
print("I am not skipped")
break_n(1)
print("I am skipped")
...
finally:
print("I am skipped")
...

foo()
#>>> I am not skipped
 
I

Ian Kelly

Why not just move the default out of the finally block?

try:
something_that_can_break()
return foo() # before clean_up
finally:
clean_up()
if default:
return default() # after clean_up()

Never mind, that doesn't work. But you could do this:

try:
something_that_can_break()
return foo() # before clean_up
except ExpectedException:
if default:
return default() # after clean_up()
else:
raise
finally:
clean_up()

And then anything unexpected will be propagated instead of silenced.
 
P

Philip Shaw

The thing that's odd to me is that a return is permissible inside
a finally block. That return
should be at top level, even with the finally line. And of course
something else should be in the body of the finally
block.

It does have some legitimate uses, for example:

try:
failingThing()
finally:
simple_cleanup()
if(that_worked())
return
complicated
cleanup
with
lots
of
blocks

OTOH, it could just be that Guido didn't think of banning it when
exceptions were first added and doesn't want to introduce an
incompatability later.
 
M

Marko Rauhamaa

Philip Shaw said:
OTOH, it could just be that Guido didn't think of banning [return from
finally] when exceptions were first added and doesn't want to
introduce an incompatability later.

You don't have to ban all nonsensical things. Most guns allow you to
shoot yourself in the foot, even those with static type checking.


Marko
 
S

Skip Montanaro

It would be great if someone could discuss it from the viewpoint of bytecode. e.g., how the stack is popped, etc.

BITD, you couldn't have try/except/finally. You could have try/except
or try/finally. You could nest the two, which is what people used to
do (back when we had to walk uphill both ways to school in a
snowstorm). As I recall, it wasn't implemented from the start because
the benefit of having try/except/finally didn't outweigh the
difficulty of implementation. Someone finally buckled down and
implemented it. To understand it, I think you might have to read the
source and PEP 341.
 
T

Thomas Rachel

Am 08.06.2014 05:58 schrieb Rustom Mody:
Some people¹ think that gotos are a code-smell.
¹ I am not exactly those people.
A chap called E W Dijkstra made the statement: "Goto statement considered
harmful" and became famous.

And became widely misunderstood. If anybody would read the whole what he
wrote, people would learn that he doesn't criticise the *use* of goto,
but he wants the *replacement* of goto with something else (like
exceptions).

As C doesn't have exceptions, goto is in many cases the simplest and
easiest way of handling errors.

Essentially, you can write both good and bad code both with and without
goto.


Thomaas
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top