Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #64967 > unrolled thread

Try-except-finally paradox

Started byJessica Ross <deathweasel@gmail.com>
First post2014-01-29 21:56 -0800
Last post2014-02-01 03:58 +0200
Articles 12 — 11 participants

Back to article view | Back to comp.lang.python


Contents

  Try-except-finally paradox Jessica Ross <deathweasel@gmail.com> - 2014-01-29 21:56 -0800
    Re: Try-except-finally paradox Ian Kelly <ian.g.kelly@gmail.com> - 2014-01-29 23:23 -0700
    Re: Try-except-finally paradox Andrew Berg <robotsondrugs@gmail.com> - 2014-01-30 00:33 -0600
      Re: Try-except-finally paradox Rotwang <sg552@hotmail.co.uk> - 2014-01-30 18:12 +0000
        Re: Try-except-finally paradox Ethan Furman <ethan@stoneleaf.us> - 2014-01-30 10:30 -0800
    Re: Try-except-finally paradox wxjmfauth@gmail.com - 2014-01-29 22:59 -0800
    Re:Try-except-finally paradox Dave Angel <davea@davea.name> - 2014-01-30 07:05 -0500
    Re: Try-except-finally paradox Chris Angelico <rosuav@gmail.com> - 2014-01-31 00:02 +1100
    Re: Try-except-finally paradox MRAB <python@mrabarnett.plus.com> - 2014-01-30 13:11 +0000
    Re: Try-except-finally paradox Chris Angelico <rosuav@gmail.com> - 2014-01-31 00:19 +1100
    Re: Try-except-finally paradox Terry Reedy <tjreedy@udel.edu> - 2014-01-31 00:26 -0500
    Re: Try-except-finally paradox Göktuğ Kayaalp <self@gkayaalp.com> - 2014-02-01 03:58 +0200

#64967 — Try-except-finally paradox

FromJessica Ross <deathweasel@gmail.com>
Date2014-01-29 21:56 -0800
SubjectTry-except-finally paradox
Message-ID<9314ac52-a2be-4382-94ef-2c291f32be1a@googlegroups.com>
I found something like this in a StackOverflow discussion.
>>> def paradox():
...     try:
...             raise Exception("Exception raised during try")
...     except:
...             print "Except after try"
...             return True
...     finally:
...             print "Finally"
...             return False
...     return None
... 
>>> return_val = paradox()
Except after try
Finally
>>> return_val
False

I understand most of this.
What I don't understand is why this returns False rather than True. Does the finally short-circuit the return in the except block?

[toc] | [next] | [standalone]


#64968

FromIan Kelly <ian.g.kelly@gmail.com>
Date2014-01-29 23:23 -0700
Message-ID<mailman.6110.1391063030.18130.python-list@python.org>
In reply to#64967

[Multipart message — attachments visible in raw view] — view raw

On Jan 29, 2014 11:01 PM, "Jessica Ross" <deathweasel@gmail.com> wrote:
>
> I found something like this in a StackOverflow discussion.
> >>> def paradox():
> ...     try:
> ...             raise Exception("Exception raised during try")
> ...     except:
> ...             print "Except after try"
> ...             return True
> ...     finally:
> ...             print "Finally"
> ...             return False
> ...     return None
> ...
> >>> return_val = paradox()
> Except after try
> Finally
> >>> return_val
> False
>
> I understand most of this.
> What I don't understand is why this returns False rather than True. Does
the finally short-circuit the return in the except block?

The docs don't seem to specify what happens in this case, but this behavior
is intuitive to me. If the except suite had raised an exception instead of
returning, the return in the finally would suppress that. The generalized
rule appears to be that the control flow specification executed later
overrides the earlier.

[toc] | [prev] | [next] | [standalone]


#64969

FromAndrew Berg <robotsondrugs@gmail.com>
Date2014-01-30 00:33 -0600
Message-ID<mailman.6111.1391063632.18130.python-list@python.org>
In reply to#64967
On 2014.01.29 23:56, Jessica Ross wrote:
> I found something like this in a StackOverflow discussion.
>>>> def paradox():
> ...     try:
> ...             raise Exception("Exception raised during try")
> ...     except:
> ...             print "Except after try"
> ...             return True
> ...     finally:
> ...             print "Finally"
> ...             return False
> ...     return None
> ... 
>>>> return_val = paradox()
> Except after try
> Finally
>>>> return_val
> False
> 
> I understand most of this.
> What I don't understand is why this returns False rather than True. Does the finally short-circuit the return in the except block?
> 
My guess would be that the interpreter doesn't let the finally block get skipped under any circumstances, so the return value gets set to
True, but then it forces the finally block to be run before returning, which changes the return value to False.

-- 
CPython 3.3.2 | Windows NT 6.2.9200 / FreeBSD 10.0

[toc] | [prev] | [next] | [standalone]


#65032

FromRotwang <sg552@hotmail.co.uk>
Date2014-01-30 18:12 +0000
Message-ID<lce4mr$p4b$1@dont-email.me>
In reply to#64969
On 30/01/2014 06:33, Andrew Berg wrote:
> On 2014.01.29 23:56, Jessica Ross wrote:
>> I found something like this in a StackOverflow discussion.
>>>>> def paradox():
>> ...     try:
>> ...             raise Exception("Exception raised during try")
>> ...     except:
>> ...             print "Except after try"
>> ...             return True
>> ...     finally:
>> ...             print "Finally"
>> ...             return False
>> ...     return None
>> ...
>>>>> return_val = paradox()
>> Except after try
>> Finally
>>>>> return_val
>> False
>>
>> I understand most of this.
>> What I don't understand is why this returns False rather than True.
>> Does the finally short-circuit the return in the except block?
>>
> My guess would be that the interpreter doesn't let the finally block
> get skipped under any circumstances, so the return value gets set to
> True, but then it forces the finally block to be run before returning,
> which changes the return value to False.

Mine too. We can check that the interpreter gets as far as evaluating 
the return value in the except block:

 >>> def paradox2():
     try:
         raise Exception("Raise")
     except:
         print("Except")
         return [print("Return"), True][1]
     finally:
         print("Finally")
         return False
     return None

 >>> ret = paradox2()
Except
Return
Finally
 >>> ret
False

[toc] | [prev] | [next] | [standalone]


#65036

FromEthan Furman <ethan@stoneleaf.us>
Date2014-01-30 10:30 -0800
Message-ID<mailman.6157.1391107943.18130.python-list@python.org>
In reply to#65032
On 01/30/2014 10:12 AM, Rotwang wrote:
> On 30/01/2014 06:33, Andrew Berg wrote:
>> On 2014.01.29 23:56, Jessica Ross wrote:
>>>
>>> I found something like this in a StackOverflow discussion.
>>> --> def paradox():
>>> ...     try:
>>> ...             raise Exception("Exception raised during try")
>>> ...     except:
>>> ...             print "Except after try"
>>> ...             return True
>>> ...     finally:
>>> ...             print "Finally"
>>> ...             return False
>>> ...     return None
>>> ...
>>> --> return_val = paradox()
>>> Except after try
>>> Finally
>>> --> return_val
>>> False
>>>
>>> I understand most of this.
>>> What I don't understand is why this returns False rather than True.
>>> Does the finally short-circuit the return in the except block?
>>>
>> My guess would be that the interpreter doesn't let the finally block
>> get skipped under any circumstances, so the return value gets set to
>> True, but then it forces the finally block to be run before returning,
>> which changes the return value to False.
>
> Mine too. We can check that the interpreter gets as far as evaluating the return value in the except block:
>
> --> def paradox2():
>        try:
>            raise Exception("Raise")
>        except:
>            print("Except")
>            return [print("Return"), True][1]
>        finally:
>            print("Finally")
>            return False
>        return None
>
> --> ret = paradox2()
> Except
> Return
> Finally
> --> ret
> False

And just to be thorough, if the finally block doesn't have a return:

--> def paradox3():
         try:
             raise Exception("Raise")
         except:
             print("Except")
             return [print("Return"), True][1]
         finally:
             print("Finally")
         return None

--> print(paradox3())
Except
Return
Finally
True

--
~Ethan~

[toc] | [prev] | [next] | [standalone]


#64971

Fromwxjmfauth@gmail.com
Date2014-01-29 22:59 -0800
Message-ID<17d88e10-c8ae-4bec-a370-8c797073caab@googlegroups.com>
In reply to#64967
Le jeudi 30 janvier 2014 06:56:16 UTC+1, Jessica Ross a écrit :
> I found something like this in a StackOverflow discussion.
> 
> >>> def paradox():
> 
> ...     try:
> 
> ...             raise Exception("Exception raised during try")
> 
> ...     except:
> 
> ...             print "Except after try"
> 
> ...             return True
> 
> ...     finally:
> 
> ...             print "Finally"
> 
> ...             return False
> 
> ...     return None
> 
> ... 
> 
> >>> return_val = paradox()
> 
> Except after try
> 
> Finally
> 
> >>> return_val
> 
> False
> 
> 
> 
> I understand most of this.
> 
> What I don't understand is why this returns False rather than True. Does the finally short-circuit the return in the except block?

========

The paradox is, in my mind, that the fct paradox() is
programmed to be paradoxal.

Compare with:

>>> def noparadox(i):
...     try:
...         a = 1 / i
...         print('Process')
...     except ZeroDivisionError:
...         print("ZeroDivisionError")
...         a = '?'
...     except Exception:
...         print("Exception")
...         a = '?'
...     finally:
...         print("Finally")
...         return a
...         
>>> noparadox(2)
Process
Finally
0.5
>>> noparadox(0)
ZeroDivisionError
Finally
'?'
>>> noparadox('asdf')
Exception
Finally
'?'
>>>

jmf

[toc] | [prev] | [next] | [standalone]


#64986

FromDave Angel <davea@davea.name>
Date2014-01-30 07:05 -0500
Message-ID<mailman.6126.1391083371.18130.python-list@python.org>
In reply to#64967
 Jessica Ross <deathweasel@gmail.com> Wrote in message:
> I found something like this in a StackOverflow discussion.
>>>> def paradox():
> ...     try:
> ...             raise Exception("Exception raised during try")
> ...     except:
> ...             print "Except after try"
> ...             return True
> ...     finally:
> ...             print "Finally"
> ...             return False
> ...     return None
> ... 
>>>> return_val = paradox()
> Except after try
> Finally
>>>> return_val
> False
> 
> I understand most of this.
> What I don't understand is why this returns False rather than True. Does the finally short-circuit the return in the except block?
> 

The finally has to happen before any return inside the try or the
 except.  And once you're in the finally clause you'll finish it
 before resuming the except clause.  Since it has a return,  that
 will happen before the other returns. The one in the except block
 will never get reached. 

It's the only reasonable behavior., to my mind. 

-- 
DaveA

[toc] | [prev] | [next] | [standalone]


#64992

FromChris Angelico <rosuav@gmail.com>
Date2014-01-31 00:02 +1100
Message-ID<mailman.6130.1391086942.18130.python-list@python.org>
In reply to#64967
On Thu, Jan 30, 2014 at 11:05 PM, Dave Angel <davea@davea.name> wrote:
> The finally has to happen before any return inside the try or the
>  except.  And once you're in the finally clause you'll finish it
>  before resuming the except clause.  Since it has a return,  that
>  will happen before the other returns. The one in the except block
>  will never get reached.
>
> It's the only reasonable behavior., to my mind.

It's arguable that putting a return inside a finally is unreasonable
behaviour, but that's up to the programmer. A finally clause can be
used to do what might be done in C++ with a destructor: "no matter how
this function/block exits, do this as you unwind the stack". In C++, I
might open a file like this:

void func()
{
    ofstream output("output.txt");
    // do a whole lot of stuff ...
    // at the close brace, output.~output() will be called, which will
close the file
}

In Python, the equivalent would be:

def func():
    try:
        output = open("output.txt", "w")
        # do a whole lot of stuff ...
    finally:
        output.close()

(Actually, the Python equivalent would be to use a 'with' clause for
brevity, but 'with' uses try/finally under the covers, so it comes to
the same thing.) The concept of the finally clause is: "Whether
execution runs off the end, hits a return statement, or throws an
exception, I need you do this before anything else happens". Having a
return statement inside 'finally' as well as in 'try' is a bit of a
corner case, since you're now saying "Before you finish this function
and return something, I need you to return something else", which
doesn't usually make sense. If you think Python's behaviour is
confusing, first figure out what you would expect to happen in this
situation :)

ChrisA

[toc] | [prev] | [next] | [standalone]


#64993

FromMRAB <python@mrabarnett.plus.com>
Date2014-01-30 13:11 +0000
Message-ID<mailman.6131.1391087514.18130.python-list@python.org>
In reply to#64967
On 2014-01-30 13:02, Chris Angelico wrote:
> On Thu, Jan 30, 2014 at 11:05 PM, Dave Angel <davea@davea.name> wrote:
>> The finally has to happen before any return inside the try or the
>>  except.  And once you're in the finally clause you'll finish it
>>  before resuming the except clause.  Since it has a return,  that
>>  will happen before the other returns. The one in the except block
>>  will never get reached.
>>
>> It's the only reasonable behavior., to my mind.
>
> It's arguable that putting a return inside a finally is unreasonable
> behaviour, but that's up to the programmer. A finally clause can be
> used to do what might be done in C++ with a destructor: "no matter how
> this function/block exits, do this as you unwind the stack". In C++, I
> might open a file like this:
>
> void func()
> {
>      ofstream output("output.txt");
>      // do a whole lot of stuff ...
>      // at the close brace, output.~output() will be called, which will
> close the file
> }
>
> In Python, the equivalent would be:
>
> def func():
>      try:
>          output = open("output.txt", "w")
>          # do a whole lot of stuff ...
>      finally:
>          output.close()
>
> (Actually, the Python equivalent would be to use a 'with' clause for
> brevity, but 'with' uses try/finally under the covers, so it comes to
> the same thing.) The concept of the finally clause is: "Whether
> execution runs off the end, hits a return statement, or throws an
> exception, I need you do this before anything else happens". Having a
> return statement inside 'finally' as well as in 'try' is a bit of a
> corner case, since you're now saying "Before you finish this function
> and return something, I need you to return something else", which
> doesn't usually make sense. If you think Python's behaviour is
> confusing, first figure out what you would expect to happen in this
> situation :)
>
One of the reasons that the 'with' statement was added was to prevent
the mistake that you've just done. ;-)

What if the file can't be opened?

[toc] | [prev] | [next] | [standalone]


#64994

FromChris Angelico <rosuav@gmail.com>
Date2014-01-31 00:19 +1100
Message-ID<mailman.6132.1391087975.18130.python-list@python.org>
In reply to#64967
On Fri, Jan 31, 2014 at 12:11 AM, MRAB <python@mrabarnett.plus.com> wrote:
> One of the reasons that the 'with' statement was added was to prevent
> the mistake that you've just done. ;-)
>
> What if the file can't be opened?

Yeah, whoops. The open shouldn't be inside try/finally.

def func():
    output = open("output.txt", "w")
    try:
         # do a whole lot of stuff ...
    finally:
         output.close()

But my point still stands, I believe :)

ChrisA

[toc] | [prev] | [next] | [standalone]


#65094

FromTerry Reedy <tjreedy@udel.edu>
Date2014-01-31 00:26 -0500
Message-ID<mailman.6194.1391145984.18130.python-list@python.org>
In reply to#64967
On 1/30/2014 7:05 AM, Dave Angel wrote:
>   Jessica Ross <deathweasel@gmail.com> Wrote in message:
>> I found something like this in a StackOverflow discussion.
>>>>> def paradox():
>> ...     try:
>> ...             raise Exception("Exception raised during try")
>> ...     except:
>> ...             print "Except after try"
>> ...             return True
>> ...     finally:
>> ...             print "Finally"
>> ...             return False
>> ...     return None
>> ...
>>>>> return_val = paradox()
>> Except after try
>> Finally
>>>>> return_val
>> False
>>
>> I understand most of this.
>> What I don't understand is why this returns False rather than True. Does the finally short-circuit the return in the except block?
>>
>
> The finally has to happen before any return inside the try or the
>   except.  And once you're in the finally clause you'll finish it
>   before resuming the except clause.  Since it has a return,  that
>   will happen before the other returns. The one in the except block
>   will never get reached.
>
> It's the only reasonable behavior., to my mind.

Checking with the disassembled code, it appears that the except return 
happens first and is then caught and the value over-written

   2           0 SETUP_FINALLY           45 (to 48)
               3 SETUP_EXCEPT            16 (to 22)

   3           6 LOAD_GLOBAL              0 (Exception)
               9 LOAD_CONST               1 ('Exception raised during try')
              12 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
              15 RAISE_VARARGS            1
              18 POP_BLOCK
              19 JUMP_FORWARD            22 (to 44)

   4     >>   22 POP_TOP
              23 POP_TOP
              24 POP_TOP

   5          25 LOAD_GLOBAL              1 (print)
              28 LOAD_CONST               2 ('Except after try')
              31 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
              34 POP_TOP

   6          35 LOAD_CONST               3 (True)
              38 RETURN_VALUE
              39 POP_EXCEPT
              40 JUMP_FORWARD             1 (to 44)
              43 END_FINALLY
         >>   44 POP_BLOCK
              45 LOAD_CONST               0 (None)

   8     >>   48 LOAD_GLOBAL              1 (print)
              51 LOAD_CONST               4 ('Finally')
              54 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
              57 POP_TOP

   9          58 LOAD_CONST               5 (False)
              61 RETURN_VALUE
              62 END_FINALLY

  10          63 LOAD_CONST               0 (None)
              66 RETURN_VALUE




-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#65169

FromGöktuğ Kayaalp <self@gkayaalp.com>
Date2014-02-01 03:58 +0200
Message-ID<mailman.6247.1391223064.18130.python-list@python.org>
In reply to#64967
Terry Reedy <tjreedy@udel.edu> writes:

I do not have any information on the topic, but I *imagine* that the
when RETURN_VALUE opcode is evaluated within the context of an except
block, it triggers a check for whether a corresponding finally block
exists and should it exist, it is triggered, much like a callback.
So, my *imaginary* algorithm works like this:

return:
  if within an except block EB:
    if EB has a correspoinding final block FB
      run FB
    else
      do return
  else
    do return

In Jessica's example, as the finally block executes a RETURN_VALUE
opcode, and as this is *probably* a jump to the caller, the rest of the
code does not get a chance to be executed.

As I have said earlier, I by no means assert the truth of this idea I've
explained here, but it seems quite reasonable to me.

          gk

> On 1/30/2014 7:05 AM, Dave Angel wrote:
>>   Jessica Ross <deathweasel@gmail.com> Wrote in message:
>>> I found something like this in a StackOverflow discussion.
>>>>>> def paradox():
>>> ...     try:
>>> ...             raise Exception("Exception raised during try")
>>> ...     except:
>>> ...             print "Except after try"
>>> ...             return True
>>> ...     finally:
>>> ...             print "Finally"
>>> ...             return False
>>> ...     return None
>>> ...
>>>>>> return_val = paradox()
>>> Except after try
>>> Finally
>>>>>> return_val
>>> False
>>>
>>> I understand most of this.
>>> What I don't understand is why this returns False rather than True. Does the finally short-circuit the return in the except block?
>>>
>>
>> The finally has to happen before any return inside the try or the
>>   except.  And once you're in the finally clause you'll finish it
>>   before resuming the except clause.  Since it has a return,  that
>>   will happen before the other returns. The one in the except block
>>   will never get reached.
>>
>> It's the only reasonable behavior., to my mind.
>
> Checking with the disassembled code, it appears that the except return
> happens first and is then caught and the value over-written
>
>   2           0 SETUP_FINALLY           45 (to 48)
>               3 SETUP_EXCEPT            16 (to 22)
>
>   3           6 LOAD_GLOBAL              0 (Exception)
>               9 LOAD_CONST               1 ('Exception raised during try')
>              12 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
>              15 RAISE_VARARGS            1
>              18 POP_BLOCK
>              19 JUMP_FORWARD            22 (to 44)
>
>   4     >>   22 POP_TOP
>              23 POP_TOP
>              24 POP_TOP
>
>   5          25 LOAD_GLOBAL              1 (print)
>              28 LOAD_CONST               2 ('Except after try')
>              31 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
>              34 POP_TOP
>
>   6          35 LOAD_CONST               3 (True)
>              38 RETURN_VALUE
>              39 POP_EXCEPT
>              40 JUMP_FORWARD             1 (to 44)
>              43 END_FINALLY
>         >>   44 POP_BLOCK
>              45 LOAD_CONST               0 (None)
>
>   8     >>   48 LOAD_GLOBAL              1 (print)
>              51 LOAD_CONST               4 ('Finally')
>              54 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
>              57 POP_TOP
>
>   9          58 LOAD_CONST               5 (False)
>              61 RETURN_VALUE
>              62 END_FINALLY
>
>  10          63 LOAD_CONST               0 (None)
>              66 RETURN_VALUE
>
>
>
>
> -- 
> Terry Jan Reedy

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web