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


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

Re: The Most Diabolical Python Antipattern

Started byIan Kelly <ian.g.kelly@gmail.com>
First post2015-01-29 15:51 -0700
Last post2015-01-31 01:28 +0000
Articles 14 — 4 participants

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

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: The Most Diabolical Python Antipattern Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-29 15:51 -0700
    Re: The Most Diabolical Python Antipattern Marko Rauhamaa <marko@pacujo.net> - 2015-01-30 08:16 +0200
      Re: The Most Diabolical Python Antipattern Mark Lawrence <breamoreboy@yahoo.co.uk> - 2015-01-30 08:10 +0000
        Re: The Most Diabolical Python Antipattern Marko Rauhamaa <marko@pacujo.net> - 2015-01-30 11:02 +0200
          Re: The Most Diabolical Python Antipattern Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-30 02:17 -0700
          Re: The Most Diabolical Python Antipattern Marko Rauhamaa <marko@pacujo.net> - 2015-01-30 12:00 +0200
            Re: The Most Diabolical Python Antipattern Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-30 08:21 -0700
              Re: The Most Diabolical Python Antipattern Marko Rauhamaa <marko@pacujo.net> - 2015-01-30 17:30 +0200
                Re: The Most Diabolical Python Antipattern Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-30 08:42 -0700
                  Re: The Most Diabolical Python Antipattern Marko Rauhamaa <marko@pacujo.net> - 2015-01-30 17:56 +0200
                    Re: The Most Diabolical Python Antipattern Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-30 09:38 -0700
                Re: The Most Diabolical Python Antipattern Chris Angelico <rosuav@gmail.com> - 2015-01-31 02:55 +1100
      Re: The Most Diabolical Python Antipattern Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-30 02:11 -0700
      Re: The Most Diabolical Python Antipattern Mark Lawrence <breamoreboy@yahoo.co.uk> - 2015-01-31 01:28 +0000

#84852 — Re: The Most Diabolical Python Antipattern

FromIan Kelly <ian.g.kelly@gmail.com>
Date2015-01-29 15:51 -0700
SubjectRe: The Most Diabolical Python Antipattern
Message-ID<mailman.18286.1422571931.18130.python-list@python.org>
On Thu, Jan 29, 2015 at 10:32 AM, Tim Chase
<python.list@tim.thechases.com> wrote:
> On 2015-01-29 17:17, Mark Lawrence wrote:
>> The author is quite clear on his views here
>> https://realpython.com/blog/python/the-most-diabolical-python-antipattern/
>> but what do you guys and gals think?
>
> I just read that earlier today and agree for the most part.  The only
> exception (pun only partially intended) I've found is in functions
> that need to return a defined type.  I have one that I call int0()
> that is my "give me a freakin' int" function which is something like
>
>   def int0(val):
>     try:
>       return int(val)
>     except:
>       return 0
>
> because I deal with a lot of CSV data from client/vendor that has
> blanks, "NULL", "---", and plenty of other rubbish to suggest
> something that, for my purposes is really just a 0.
>
> Yes, I've been stung by it occasionally, but it's not much trouble to
> see that I'm getting a 0 some place that should have a value I need
> to extract.

At least use "except Exception" instead of a bare except. Do you
really want things like SystemExit and KeyboardInterrupt to get turned
into 0?

[toc] | [next] | [standalone]


#84863

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-01-30 08:16 +0200
Message-ID<87r3uchjyg.fsf@elektro.pacujo.net>
In reply to#84852
Ian Kelly <ian.g.kelly@gmail.com>:

> At least use "except Exception" instead of a bare except. Do you
> really want things like SystemExit and KeyboardInterrupt to get turned
> into 0?

How about:

==============================
    try:
        do_interesting_stuff()
    except ValueError:
        try:
            log_it()
        except:
            pass
        raise
==============================

Surprisingly this variant could raise an unexpected exception:

==============================
    try:
        do_interesting_stuff()
    except ValueError:
        try:
            log_it()
        finally:
            raise
==============================

A Python bug?


Marko

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


#84867

FromMark Lawrence <breamoreboy@yahoo.co.uk>
Date2015-01-30 08:10 +0000
Message-ID<mailman.18296.1422605482.18130.python-list@python.org>
In reply to#84863
On 30/01/2015 06:16, Marko Rauhamaa wrote:
> Ian Kelly <ian.g.kelly@gmail.com>:
>
>> At least use "except Exception" instead of a bare except. Do you
>> really want things like SystemExit and KeyboardInterrupt to get turned
>> into 0?
>
> How about:
>
> ==============================
>      try:
>          do_interesting_stuff()
>      except ValueError:
>          try:
>              log_it()
>          except:
>              pass
>          raise
> ==============================
>
> Surprisingly this variant could raise an unexpected exception:
>
> ==============================
>      try:
>          do_interesting_stuff()
>      except ValueError:
>          try:
>              log_it()
>          finally:
>              raise
> ==============================
>
> A Python bug?
>
>
> Marko
>

It depends on the Python version that you're running - I think!!!  See 
https://www.python.org/dev/peps/pep-3134/ 
https://www.python.org/dev/peps/pep-0409/ 
https://www.python.org/dev/peps/pep-0415/ and finally try (groan :) 
https://pypi.python.org/pypi/pep3134/

-- 
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

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


#84869

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-01-30 11:02 +0200
Message-ID<87r3ucljyv.fsf@elektro.pacujo.net>
In reply to#84867
Mark Lawrence <breamoreboy@yahoo.co.uk>:

> On 30/01/2015 06:16, Marko Rauhamaa wrote:
>> How about:
>>
>> ==============================
>>      try:
>>          do_interesting_stuff()
>>      except ValueError:
>>          try:
>>              log_it()
>>          except:
>>              pass
>>          raise
>> ==============================
>>
>> Surprisingly this variant could raise an unexpected exception:
>>
>> ==============================
>>      try:
>>          do_interesting_stuff()
>>      except ValueError:
>>          try:
>>              log_it()
>>          finally:
>>              raise
>> ==============================
>>
>> A Python bug?
>
> It depends on the Python version that you're running - I think!!!  See
> https://www.python.org/dev/peps/pep-3134/

TL;DR

My Python did do exception chaining, but the problem is the surface
exception changes, which could throw off the whole error recovery.

So I'm thinking I might have found a valid use case for the "diabolical
antipattern."


Marko

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


#84872

FromIan Kelly <ian.g.kelly@gmail.com>
Date2015-01-30 02:17 -0700
Message-ID<mailman.18300.1422609481.18130.python-list@python.org>
In reply to#84869
On Fri, Jan 30, 2015 at 2:02 AM, Marko Rauhamaa <marko@pacujo.net> wrote:
> Mark Lawrence <breamoreboy@yahoo.co.uk>:
>
>> On 30/01/2015 06:16, Marko Rauhamaa wrote:
>>> How about:
>>>
>>> ==============================
>>>      try:
>>>          do_interesting_stuff()
>>>      except ValueError:
>>>          try:
>>>              log_it()
>>>          except:
>>>              pass
>>>          raise
>>> ==============================
>>>
>>> Surprisingly this variant could raise an unexpected exception:
>>>
>>> ==============================
>>>      try:
>>>          do_interesting_stuff()
>>>      except ValueError:
>>>          try:
>>>              log_it()
>>>          finally:
>>>              raise
>>> ==============================
>>>
>>> A Python bug?
>>
>> It depends on the Python version that you're running - I think!!!  See
>> https://www.python.org/dev/peps/pep-3134/
>
> TL;DR
>
> My Python did do exception chaining, but the problem is the surface
> exception changes, which could throw off the whole error recovery.
>
> So I'm thinking I might have found a valid use case for the "diabolical
> antipattern."

I suppose, although it seems awfully contrived to me. In any case it
would still be better with "except Exception" rather than the bare
except. Unless re-raising that ValueError is more important to you
than letting the user hit Ctrl-C during the logging call.

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


#84874

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-01-30 12:00 +0200
Message-ID<87fvaslh9h.fsf@elektro.pacujo.net>
In reply to#84869
Marko Rauhamaa <marko@pacujo.net>:

>>> Surprisingly this variant could raise an unexpected exception:
>>>
>>> ==============================
>>>      try:
>>>          do_interesting_stuff()
>>>      except ValueError:
>>>          try:
>>>              log_it()
>>>          finally:
>>>              raise
>>> ==============================
>>>
>>> A Python bug?
> [...]
> My Python did do exception chaining, but the problem is the surface
> exception changes, which could throw off the whole error recovery.

BTW, the code above can be fixed:

==============================
  try:
      do_interesting_stuff()
  except ValueError as e:
      try:
          log_it()
      finally:
          raise e
==============================

Now the surface exception is kept and the subsidiary exception is
chained to it.

I'm a bit baffled why the two pieces of code are not equivalent.


Marko

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


#84891

FromIan Kelly <ian.g.kelly@gmail.com>
Date2015-01-30 08:21 -0700
Message-ID<mailman.18306.1422631319.18130.python-list@python.org>
In reply to#84874
On Fri, Jan 30, 2015 at 3:00 AM, Marko Rauhamaa <marko@pacujo.net> wrote:
> Marko Rauhamaa <marko@pacujo.net>:
>
>>>> Surprisingly this variant could raise an unexpected exception:
>>>>
>>>> ==============================
>>>>      try:
>>>>          do_interesting_stuff()
>>>>      except ValueError:
>>>>          try:
>>>>              log_it()
>>>>          finally:
>>>>              raise
>>>> ==============================
>>>>
>>>> A Python bug?
>> [...]
>> My Python did do exception chaining, but the problem is the surface
>> exception changes, which could throw off the whole error recovery.
>
> BTW, the code above can be fixed:
>
> ==============================
>   try:
>       do_interesting_stuff()
>   except ValueError as e:
>       try:
>           log_it()
>       finally:
>           raise e
> ==============================
>
> Now the surface exception is kept and the subsidiary exception is
> chained to it.
>
> I'm a bit baffled why the two pieces of code are not equivalent.

The bare raise re-raises the most recent exception that is being
handled. The "raise e" raises that exception specifically, which is
not the most recent in the case of a secondary exception.

Note that the exceptions are actually chained *in reverse*; the
message falsely indicates that the secondary exception was raised
first, and the primary exception was raised while handling it, e.g.:

Traceback (most recent call last):
  File "<stdin>", line 5, in <module>
TypeError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 7, in <module>
  File "<stdin>", line 2, in <module>
ValueError

That's because "raise e" causes the exception e to be freshly raised,
whereas the bare "raise" merely makes the existing exception context
active again.

It's interesting to note here that although the exception retains its
original traceback information (note the two separate lines in the
traceback), it is not chained again from the TypeError. One might
expect to actually see the ValueError, followed by the TypeError,
followed by the ValueError in the chain. That doesn't happen because
the two ValueErrors raised are actually the same object, and Python is
apparently wise enough to break the chain to avoid an infinite cycle.

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


#84892

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-01-30 17:30 +0200
Message-ID<87zj90jngf.fsf@elektro.pacujo.net>
In reply to#84891
Ian Kelly <ian.g.kelly@gmail.com>:

> The bare raise re-raises the most recent exception that is being
> handled. The "raise e" raises that exception specifically, which is
> not the most recent in the case of a secondary exception.

Scary. That affects all finally clauses. Must remember that.

The pitfall is avoided by using the "except: pass" antipattern but then
you lose exception chaining.


Marko

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


#84893

FromIan Kelly <ian.g.kelly@gmail.com>
Date2015-01-30 08:42 -0700
Message-ID<mailman.18307.1422632572.18130.python-list@python.org>
In reply to#84892
On Fri, Jan 30, 2015 at 8:30 AM, Marko Rauhamaa <marko@pacujo.net> wrote:
> Ian Kelly <ian.g.kelly@gmail.com>:
>
>> The bare raise re-raises the most recent exception that is being
>> handled. The "raise e" raises that exception specifically, which is
>> not the most recent in the case of a secondary exception.
>
> Scary. That affects all finally clauses. Must remember that.
>
> The pitfall is avoided by using the "except: pass" antipattern but then
> you lose exception chaining.

Like I suggested earlier, just don't catch the inner exception at all.
The result will be both exceptions propagated, chained in the proper
order.

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


#84896

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-01-30 17:56 +0200
Message-ID<87vbjojm86.fsf@elektro.pacujo.net>
In reply to#84893
Ian Kelly <ian.g.kelly@gmail.com>:

> Like I suggested earlier, just don't catch the inner exception at all.
> The result will be both exceptions propagated, chained in the proper
> order.

Depends on the situation.


Marko

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


#84902

FromIan Kelly <ian.g.kelly@gmail.com>
Date2015-01-30 09:38 -0700
Message-ID<mailman.18312.1422635977.18130.python-list@python.org>
In reply to#84896
On Fri, Jan 30, 2015 at 8:56 AM, Marko Rauhamaa <marko@pacujo.net> wrote:
> Ian Kelly <ian.g.kelly@gmail.com>:
>
>> Like I suggested earlier, just don't catch the inner exception at all.
>> The result will be both exceptions propagated, chained in the proper
>> order.
>
> Depends on the situation.

Like what? If you want to specifically propagate the original
exception in order to be caught again elsewhere, then I think there's
a code smell to that. If this inner exception handler doesn't
specifically know how to handle a ValueError, then why should some
outer exception handler be able to handle an exception that could have
come from virtually anywhere?

A better approach to that would be to create a specific exception
class that narrowly identifies what went wrong, and raise *that* with
the other exceptions chained to it. E.g.:

  try:
      do_interesting_stuff()
  except ValueError as e:
      try:
          log_it()
      except Exception:
          # Chain both exceptions as __context__
          raise SpecificException
      else:
          # Chain the original exception as __cause__
          raise SpecificException from e

Or if you don't care about distinguishing __cause__ from __context__:

  try:
      do_interesting_stuff()
  except ValueError:
      try:
          log_it()
      finally:
          raise SpecificException

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


#84895

FromChris Angelico <rosuav@gmail.com>
Date2015-01-31 02:55 +1100
Message-ID<mailman.18309.1422633366.18130.python-list@python.org>
In reply to#84892
On Sat, Jan 31, 2015 at 2:42 AM, Ian Kelly <ian.g.kelly@gmail.com> wrote:
> Like I suggested earlier, just don't catch the inner exception at all.
> The result will be both exceptions propagated, chained in the proper
> order.

So many MANY times, the best thing to do with unrecognized exceptions
is simply to not catch them.

ChrisA

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


#84871

FromIan Kelly <ian.g.kelly@gmail.com>
Date2015-01-30 02:11 -0700
Message-ID<mailman.18299.1422609145.18130.python-list@python.org>
In reply to#84863
On Thu, Jan 29, 2015 at 11:16 PM, Marko Rauhamaa <marko@pacujo.net> wrote:
> Ian Kelly <ian.g.kelly@gmail.com>:
>
>> At least use "except Exception" instead of a bare except. Do you
>> really want things like SystemExit and KeyboardInterrupt to get turned
>> into 0?
>
> How about:
>
> ==============================
>     try:
>         do_interesting_stuff()
>     except ValueError:
>         try:
>             log_it()
>         except:
>             pass
>         raise
> ==============================

Are you asking if I think this is better? It still swallows arbitrary
exceptions. Why would you want to re-raise the anticipated (and
logged) ValueError instead of the exception that could potentially be
unexpected?

> Surprisingly this variant could raise an unexpected exception:
>
> ==============================
>     try:
>         do_interesting_stuff()
>     except ValueError:
>         try:
>             log_it()
>         finally:
>             raise
> ==============================
>
> A Python bug?

This does what it is supposed to. "If no expressions are present,
raise re-raises the last exception that was active in the current
scope." In this case, what that exception is depends on whether the
finally clause was entered as a result of an exception or fall-through
from the try clause. If you only want to re-raise the ValueError, then
use the first form above. If you only want to re-raise the other
exception, then do so from an except block (or don't catch it in the
first place).

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


#84928

FromMark Lawrence <breamoreboy@yahoo.co.uk>
Date2015-01-31 01:28 +0000
Message-ID<mailman.18325.1422667744.18130.python-list@python.org>
In reply to#84863
On 30/01/2015 08:10, Mark Lawrence wrote:
> On 30/01/2015 06:16, Marko Rauhamaa wrote:
>> Ian Kelly <ian.g.kelly@gmail.com>:
>>
>>> At least use "except Exception" instead of a bare except. Do you
>>> really want things like SystemExit and KeyboardInterrupt to get turned
>>> into 0?
>>
>> How about:
>>
>> ==============================
>>      try:
>>          do_interesting_stuff()
>>      except ValueError:
>>          try:
>>              log_it()
>>          except:
>>              pass
>>          raise
>> ==============================
>>
>> Surprisingly this variant could raise an unexpected exception:
>>
>> ==============================
>>      try:
>>          do_interesting_stuff()
>>      except ValueError:
>>          try:
>>              log_it()
>>          finally:
>>              raise
>> ==============================
>>
>> A Python bug?
>>
>>
>> Marko
>>
>
> It depends on the Python version that you're running - I think!!!  See
> https://www.python.org/dev/peps/pep-3134/
> https://www.python.org/dev/peps/pep-0409/
> https://www.python.org/dev/peps/pep-0415/ and finally try (groan :)
> https://pypi.python.org/pypi/pep3134/
>

http://bugs.python.org/issue23353 looks like fun and references PEP3134 
for anybody who's interested.

-- 
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

[toc] | [prev] | [standalone]


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


csiph-web