try/except/finally

Discussion in 'Python' started by Frank B, Jun 6, 2014.

  1. Frank B

    Frank B Guest

    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.
     
    Frank B, Jun 6, 2014
    #1
    1. Advertisements

  2. Frank B

    Roy Smith Guest

    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.
     
    Roy Smith, Jun 6, 2014
    #2
    1. Advertisements

  3. Frank B

    Frank B Guest

    Ok; thanks for the underscore and clarification. Just need to adjust my thinking a bit.
     
    Frank B, Jun 6, 2014
    #3
  4. 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.
     
    Ned Batchelder, Jun 6, 2014
    #4
  5. Frank B

    Ethan Furman Guest

    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.
     
    Ethan Furman, Jun 7, 2014
    #5
  6. I'd go a little simpler: A return statement inside a finally block is
    code smell.

    ChrisA
     
    Chris Angelico, Jun 8, 2014
    #6
  7. Frank B

    Roy Smith Guest

    Not to my nose. It seems like a perfectly reasonable thing to do.
     
    Roy Smith, Jun 8, 2014
    #7
  8. I agree, the code smell is the return in the except block.
     
    Mark Lawrence, Jun 8, 2014
    #8
  9. Frank B

    Roy Smith Guest

    That's not setting my nose on end either.
     
    Roy Smith, Jun 8, 2014
    #9
  10. Frank B

    Dave Angel Guest

    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.
     
    Dave Angel, Jun 8, 2014
    #10
  11. Frank B

    Rustom Mody Guest

    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
     
    Rustom Mody, Jun 8, 2014
    #11
  12. 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
     
    Marko Rauhamaa, Jun 8, 2014
    #12
  13. 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.
     
    Joshua Landau, Jun 8, 2014
    #13
  14. Frank B

    Ian Kelly Guest

    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()
     
    Ian Kelly, Jun 8, 2014
    #14
  15. 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
     
    Joshua Landau, Jun 8, 2014
    #15
  16. Frank B

    Ian Kelly Guest

    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.
     
    Ian Kelly, Jun 8, 2014
    #16
  17. Frank B

    Philip Shaw Guest

    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.
     
    Philip Shaw, Jun 9, 2014
    #17
  18. 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
     
    Marko Rauhamaa, Jun 9, 2014
    #18
  19. 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.
     
    Skip Montanaro, Jun 9, 2014
    #19
  20. Am 08.06.2014 05:58 schrieb Rustom Mody:
    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
     
    Thomas Rachel, Jun 10, 2014
    #20
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.