try..except with empty exceptions

classic Classic list List threaded Threaded
35 messages Options
12
Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Pavel Schon
Hi,

I noticed interesting behaviour. Since I don't have python3 installation here, I tested that on Python 2.7.

Well known feature is that try..except block can catch multiple exceptions listed in a tuple:


exceptions = ( TypeError, ValueError )

try:
    a, b = None
except exceptions, e:
    print 'Catched error:', e


However when exceptions=(), then try..except block behaves as no try..except block.


exceptions = ()

try:
    a, b = None   # <--- the error will not be catched
except exceptions, e:
    print 'Catched error:', e


I found use case for it, e.g. when I want to have a method with 'exceptions' argument:


def catch_exceptions(exceptions=()):
  try:
     do_something()
  except exceptions:
     do_something_else()


catch_exceptions()               # catches nothing
catch_exceptions((TypeError,))   # catches TypeError


I believe that behaviour is not documented. What you think?


Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Chris Angelico
On Fri, Apr 10, 2015 at 6:48 PM, Pavel S <pavel at schon.cz> wrote:

> Hi,
>
> I noticed interesting behaviour. Since I don't have python3 installation here, I tested that on Python 2.7.
>
> Well known feature is that try..except block can catch multiple exceptions listed in a tuple:
>
> However when exceptions=(), then try..except block behaves as no try..except block.
>
> exceptions = ()
>
> try:
>     a, b = None   # <--- the error will not be catched
> except exceptions, e:
>     print 'Catched error:', e
>
> I believe that behaviour is not documented. What you think?

It's no different from any other except clause that doesn't match. An
empty tuple of exception types can never match any actual exception
thrown, so it'll be like skipping that except block. It doesn't need
to be specifically documented, as it's a natural consequence of the
use of the empty tuple there.

However, even though it's perfectly plausible as regards language
definition, I would hesitate to use it in production code. I'm not
sure this makes your code any better.

Side point: Even though you're not using Python 3 (where it's
mandatory), I would recommend using the "as" syntax with except
clauses:

exceptions = ()
try:
    a, b = None
except exceptions as e:
    print("Can't happen")

Writing your code to be compatible with both 2.7 and 3.x will make the
eventual port easier.

ChrisA


Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

sohcahtoa82@gmail.com
In reply to this post by Pavel Schon
On Friday, April 10, 2015 at 1:48:22 AM UTC-7, Pavel S wrote:

> Hi,
>
> I noticed interesting behaviour. Since I don't have python3 installation here, I tested that on Python 2.7.
>
> Well known feature is that try..except block can catch multiple exceptions listed in a tuple:
>
>
> exceptions = ( TypeError, ValueError )
>
> try:
>     a, b = None
> except exceptions, e:
>     print 'Catched error:', e
>
>
> However when exceptions=(), then try..except block behaves as no try..except block.
>
>
> exceptions = ()
>
> try:
>     a, b = None   # <--- the error will not be catched
> except exceptions, e:
>     print 'Catched error:', e
>
>
> I found use case for it, e.g. when I want to have a method with 'exceptions' argument:
>
>
> def catch_exceptions(exceptions=()):
>   try:
>      do_something()
>   except exceptions:
>      do_something_else()
>
>
> catch_exceptions()               # catches nothing
> catch_exceptions((TypeError,))   # catches TypeError
>
>
> I believe that behaviour is not documented. What you think?

It isn't document because it is expected.  Why would the exception get caught if you're not writing code to catch it?  If you write a function and pass it a tuple of exceptions to catch, I'm not sure why you would expect it to catch an exception not in the tuple.  Just because the tuple is empty doesn't mean that it should catch *everything* instead.  That would be counter-intuitive.


Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Dave Angel-4
In reply to this post by Pavel Schon
On 04/10/2015 04:48 AM, Pavel S wrote:

> Hi,
>
> I noticed interesting behaviour. Since I don't have python3 installation here, I tested that on Python 2.7.
>
> Well known feature is that try..except block can catch multiple exceptions listed in a tuple:
>
>
> exceptions = ( TypeError, ValueError )
>
> try:
>      a, b = None
> except exceptions, e:
>      print 'Catched error:', e
>
>
> However when exceptions=(), then try..except block behaves as no try..except block.
>
>
> exceptions = ()
>
> try:
>      a, b = None   # <--- the error will not be catched
> except exceptions, e:
>      print 'Catched error:', e
>
>
> I found use case for it, e.g. when I want to have a method with 'exceptions' argument:
>
>
> def catch_exceptions(exceptions=()):
>    try:
>       do_something()
>    except exceptions:
>       do_something_else()
>
>
> catch_exceptions()               # catches nothing
> catch_exceptions((TypeError,))   # catches TypeError
>
>
> I believe that behaviour is not documented. What you think?
>

It's no more surprising than a for loop over an empty tuple or empty
list.  There's nothing to do, so you do nothing.



--
DaveA


Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Steven D'Aprano-11
In reply to this post by sohcahtoa82@gmail.com
On Sat, 11 Apr 2015 05:31 am, sohcahtoa82 at gmail.com wrote:

> It isn't document because it is expected.  Why would the exception get
> caught if you're not writing code to catch it?  If you write a function
> and pass it a tuple of exceptions to catch, I'm not sure why you would
> expect it to catch an exception not in the tuple.  Just because the tuple
> is empty doesn't mean that it should catch *everything* instead.  That
> would be counter-intuitive.

Really? I have to say, I expected it.


try:
    spam()
except This, That:
    # Implicitly a tuple of two exceptions.
    pass


Compare:

try:
    spam()
except:
    # Implicitly an empty tuple.
    pass


I'm not surprised that it fails, especially in Python 2 before
the "except ... as err" syntax was available, but the OP is not alone in
thinking that an empty tuple should catch everything.


I'm fairly dubious about catching everything, that sounds like a good way to
hide bugs, but if you need to catch everything, using Exception is the
usual way to do it.




--
Steven



Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Rustom Mody
In reply to this post by Pavel Schon
On Friday, April 10, 2015 at 2:18:22 PM UTC+5:30, Pavel S wrote:

> Hi,
>
> I noticed interesting behaviour. Since I don't have python3 installation here, I tested that on Python 2.7.
>
> Well known feature is that try..except block can catch multiple exceptions listed in a tuple:
>
>
> exceptions = ( TypeError, ValueError )
>
> try:
>     a, b = None
> except exceptions, e:
>     print 'Catched error:', e
>
>
> However when exceptions=(), then try..except block behaves as no try..except block.
>
>
> exceptions = ()
>
> try:
>     a, b = None   # <--- the error will not be catched
> except exceptions, e:
>     print 'Catched error:', e
>
>
> I found use case for it, e.g. when I want to have a method with 'exceptions' argument:
>
>
> def catch_exceptions(exceptions=()):
>   try:
>      do_something()
>   except exceptions:
>      do_something_else()
>
>
> catch_exceptions()               # catches nothing
> catch_exceptions((TypeError,))   # catches TypeError
>
>
> I believe that behaviour is not documented. What you think?

As others have pointed out: "You asked for it; you got it; what's the issue?"

Nevertheless a tentative +1 from me on the suggestions ?

except :

catches everything

except ():

catches nothing --- which is brittle to say the least.

Also given this sort of lines in the docs (2.7 tutorial):

-----------------------------
... except (RuntimeError, TypeError, NameError):
...     pass

Note that the parentheses around this tuple are required, because except
ValueError, e: was the syntax used for what is normally written as except
ValueError as e: in modern Python (described below). The old syntax is still
supported for backwards compatibility. This means except RuntimeError,
TypeError is not equivalent to except (RuntimeError, TypeError): but to except
RuntimeError as TypeError: which is not what you want.
------------------------------------

there's already some versioning related brittleness around except.


Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Dave Angel-4
In reply to this post by Steven D'Aprano-11
On 04/10/2015 09:42 PM, Steven D'Aprano wrote:

> On Sat, 11 Apr 2015 05:31 am, sohcahtoa82 at gmail.com wrote:
>
>> It isn't document because it is expected.  Why would the exception get
>> caught if you're not writing code to catch it?  If you write a function
>> and pass it a tuple of exceptions to catch, I'm not sure why you would
>> expect it to catch an exception not in the tuple.  Just because the tuple
>> is empty doesn't mean that it should catch *everything* instead.  That
>> would be counter-intuitive.
>
> Really? I have to say, I expected it.
>
>

I'm astounded at your expectation.  That's like saying a for loop on an
empty list ought to loop on all possible objects in the universe.

The tuple lists those exceptions you're interested in, and they are
tried, presumably in order, from that collection.  If none of those
match, then the logic will advance to the next except clause.  If the
tuple is empty, then clearly none will match.

> try:
>      spam()
> except This, That:
>      # Implicitly a tuple of two exceptions.
>      pass
>
>
> Compare:
>
> try:
>      spam()
> except:
>      # Implicitly an empty tuple.

No, an omitted item is not the same as an empty tuple.  If it were, then
we wouldn't have the problem of bare excepts, which are so tempting to
novices.  There's plenty of precedent in many languages for a missing
item being distinct from anything one could actually supply.

When there's no tuple specified, it's a different syntax, and the
semantics are specified separately.



--
DaveA


Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Rustom Mody
In reply to this post by Steven D'Aprano-11
On Saturday, April 11, 2015 at 7:53:31 AM UTC+5:30, Dave Angel wrote:

> On 04/10/2015 09:42 PM, Steven D'Aprano wrote:
> > On Sat, 11 Apr 2015 05:31 am, sohcahtoa82 wrote:
> >
> >> It isn't document because it is expected.  Why would the exception get
> >> caught if you're not writing code to catch it?  If you write a function
> >> and pass it a tuple of exceptions to catch, I'm not sure why you would
> >> expect it to catch an exception not in the tuple.  Just because the tuple
> >> is empty doesn't mean that it should catch *everything* instead.  That
> >> would be counter-intuitive.
> >
> > Really? I have to say, I expected it.
> >
> >
>
> I'm astounded at your expectation.  That's like saying a for loop on an
> empty list ought to loop on all possible objects in the universe.

To work, this analogy should also have two python syntaxes like this:

"Normal" for-loop:
for var in iterable:
  suite

"Empty" for-loop:
for:
  suite

[Sorry Steven? didn't notice you were agreeing with me?
a rare privilege ?
]


Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Dave Angel-4
On 04/10/2015 10:38 PM, Rustom Mody wrote:

> On Saturday, April 11, 2015 at 7:53:31 AM UTC+5:30, Dave Angel wrote:
>> On 04/10/2015 09:42 PM, Steven D'Aprano wrote:
>>> On Sat, 11 Apr 2015 05:31 am, sohcahtoa82 wrote:
>>>
>>>> It isn't document because it is expected.  Why would the exception get
>>>> caught if you're not writing code to catch it?  If you write a function
>>>> and pass it a tuple of exceptions to catch, I'm not sure why you would
>>>> expect it to catch an exception not in the tuple.  Just because the tuple
>>>> is empty doesn't mean that it should catch *everything* instead.  That
>>>> would be counter-intuitive.
>>>
>>> Really? I have to say, I expected it.
>>>
>>>
>>
>> I'm astounded at your expectation.  That's like saying a for loop on an
>> empty list ought to loop on all possible objects in the universe.
>
> To work, this analogy should also have two python syntaxes like this:
>
> "Normal" for-loop:
> for var in iterable:
>    suite
>
> "Empty" for-loop:
> for:
>    suite
>

That tells me nothing about your opinions.  What did you mean by the
phrase "to work"?  My analogy already works.  The for loop on an empty
list loops zero times.  Just like try/except on an empty tuple catches
zero exception types.

As for the separate syntax, that might be an acceptable extension to
Python.  But it already has a convention for an infinite loop, which is
      while True:
I'm pretty sure do{} works as an infinite loop in C, but perhaps I'm
remembering some other language where you could omit the conditional.





--
DaveA


Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Rustom Mody
In reply to this post by Rustom Mody
On Saturday, April 11, 2015 at 9:17:16 AM UTC+5:30, Dave Angel wrote:

> On 04/10/2015 10:38 PM, Rustom Mody wrote:
> > On Saturday, April 11, 2015 at 7:53:31 AM UTC+5:30, Dave Angel wrote:
> >> On 04/10/2015 09:42 PM, Steven D'Aprano wrote:
> >>> On Sat, 11 Apr 2015 05:31 am, sohcahtoa82 wrote:
> >>>
> >>>> It isn't document because it is expected.  Why would the exception get
> >>>> caught if you're not writing code to catch it?  If you write a function
> >>>> and pass it a tuple of exceptions to catch, I'm not sure why you would
> >>>> expect it to catch an exception not in the tuple.  Just because the tuple
> >>>> is empty doesn't mean that it should catch *everything* instead.  That
> >>>> would be counter-intuitive.
> >>>
> >>> Really? I have to say, I expected it.
> >>>
> >>>
> >>
> >> I'm astounded at your expectation.  That's like saying a for loop on an
> >> empty list ought to loop on all possible objects in the universe.
> >
> > To work, this analogy should also have two python syntaxes like this:
> >
> > "Normal" for-loop:
> > for var in iterable:
> >    suite
> >
> > "Empty" for-loop:
> > for:
> >    suite
> >
>
> That tells me nothing about your opinions.  What did you mean by the
> phrase "to work"?

Your analogy is "for loop on an empty list ought to loop on all possible
objects in the universe"
This seemingly works as a demo of a ridiculous expectation
because there is only one pattern of for-loop
for var in iterable:

In the case of exceptions we have two patterns
except e-tuple:
and
except:
with the second having a wildly different semantics from the first


Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Rustom Mody
On Saturday, April 11, 2015 at 9:47:36 AM UTC+5:30, Rustom Mody wrote:

> On Saturday, April 11, 2015 at 9:17:16 AM UTC+5:30, Dave Angel wrote:
> > On 04/10/2015 10:38 PM, Rustom Mody wrote:
> > > On Saturday, April 11, 2015 at 7:53:31 AM UTC+5:30, Dave Angel wrote:
> > >> On 04/10/2015 09:42 PM, Steven D'Aprano wrote:
> > >>> On Sat, 11 Apr 2015 05:31 am, sohcahtoa82 wrote:
> > >>>
> > >>>> It isn't document because it is expected.  Why would the exception get
> > >>>> caught if you're not writing code to catch it?  If you write a function
> > >>>> and pass it a tuple of exceptions to catch, I'm not sure why you would
> > >>>> expect it to catch an exception not in the tuple.  Just because the tuple
> > >>>> is empty doesn't mean that it should catch *everything* instead.  That
> > >>>> would be counter-intuitive.
> > >>>
> > >>> Really? I have to say, I expected it.
> > >>>
> > >>>
> > >>
> > >> I'm astounded at your expectation.  That's like saying a for loop on an
> > >> empty list ought to loop on all possible objects in the universe.
> > >
> > > To work, this analogy should also have two python syntaxes like this:
> > >
> > > "Normal" for-loop:
> > > for var in iterable:
> > >    suite
> > >
> > > "Empty" for-loop:
> > > for:
> > >    suite
> > >
> >
> > That tells me nothing about your opinions.  What did you mean by the
> > phrase "to work"?
>
> Your analogy is "for loop on an empty list ought to loop on all possible
> objects in the universe"
> This seemingly works as a demo of a ridiculous expectation
> because there is only one pattern of for-loop
> for var in iterable:
>
> In the case of exceptions we have two patterns
> except e-tuple:
> and
> except:
> with the second having a wildly different semantics from the first

IOW:
OP is surprised that except (): is a no-op
The real surprise is that except: is valid syntax with questionable semantics


Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Steven D'Aprano-11
In reply to this post by Steven D'Aprano-11
On Sat, 11 Apr 2015 12:23 pm, Dave Angel wrote:

> On 04/10/2015 09:42 PM, Steven D'Aprano wrote:
>> On Sat, 11 Apr 2015 05:31 am, sohcahtoa82 at gmail.com wrote:
>>
>>> It isn't document because it is expected.  Why would the exception get
>>> caught if you're not writing code to catch it?  If you write a function
>>> and pass it a tuple of exceptions to catch, I'm not sure why you would
>>> expect it to catch an exception not in the tuple.  Just because the
>>> tuple
>>> is empty doesn't mean that it should catch *everything* instead.  That
>>> would be counter-intuitive.
>>
>> Really? I have to say, I expected it.
>>
>>
>
> I'm astounded at your expectation.  That's like saying a for loop on an
> empty list ought to loop on all possible objects in the universe.

Not really.

If we wrote:

    for x in:
        # Missing sequence leads to an infinite loop

*then* your analogy would be excellent, but it isn't. With for loops, we
iterate over each item in the sequence, hence an empty sequence means we
don't iterate at all.

But with try...except, an empty exception list means to catch *everything*,
not nothing:

try: ...
except a,b,c: # catches a, b, c

try: ...
except a,b: # catches a, b

try: ...
except a: # catches a

try: ...
except: # catches EVERYTHING, not nothing


Putting (a, b, c) into a tuple shouldn't make a difference, and it doesn't,
unless the tuple is empty. That surprised me.

t = a, b, c
try:
except t:  # same as except a,b,c

t = a, b
try:
except t:  # same as except a,b

t = a,
try:
except t:  # same as except a

t = ()
try:
except t:  # NOT THE SAME as bare except.


I can see the logic behind the current behaviour. If you implement except
clauses like this pseudo-code:


for exc in exceptions:
    if raised_exception matches exc: catch it


then an empty tuple will naturally lead to nothing being caught. That
doesn't mean it isn't surprising from the perspective that an empty
exception list (i.e. a bare except) should be analogous to an empty tuple.


> The tuple lists those exceptions you're interested in, and they are
> tried, presumably in order, from that collection.  If none of those
> match, then the logic will advance to the next except clause.  If the
> tuple is empty, then clearly none will match.

Yes, that makes sense, and I agree that it is reasonable behaviour from one
perspective. But its also reasonable to treat "except ():" as analogous to
a bare except.

[...]
>> try:
>>      spam()
>> except:
>>      # Implicitly an empty tuple.
>
> No, an omitted item is not the same as an empty tuple.

You are correct about Python as it actually is, but it could have been
designed so that except (): was equivalent to a bare except.


> If it were, then
> we wouldn't have the problem of bare excepts, which are so tempting to
> novices.  There's plenty of precedent in many languages for a missing
> item being distinct from anything one could actually supply.

Let us put aside the fact that some people misuse bare excepts, and allow
that there are some uses for it. Now, in Python 2.6 and later, you can
catch everything by catching BaseException. But in older versions, you
could raise strings as well, and the only way to catch everything is with a
bare except.

If you want to write a function that takes a list of things to catch,
defaulting to "everything", in Python 2.6+ we can write:

def spam(things_to_catch=BaseException):
    try:
        do_stuff()
    except things_to_catch:
        handle_exception()


but in older versions you have to write this:

def spam(things_to_catch=None):
    if things_to_catch is None:
        try:
            do_stuff()
        except:
            handle_exception()
    else:
        try:
            do_stuff()
        except things_to_catch:
            handle_exception()


This violates Don't Repeat Yourself. Any time you have "a missing item being
distinct from anything one could actually supply", you have a poor design.

Anyway, in modern Python (2.6 onwards), now that string exceptions are gone,
you can supply something to catch everything. Or nothing, for that matter:

BaseException  # catch everything
Exception  # catch errors
(A, B, C)  # Just A, B or C or their subclasses
A  # Just A (or its subclasses)
()  # Catch nothing.

so I suppose that having an empty tuple mean "catch nothing" is better than
having it catch everything.



--
Steven



Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Serhiy Storchaka-2
On 11.04.15 10:11, Steven D'Aprano wrote:
> Anyway, in modern Python (2.6 onwards), now that string exceptions are gone,
> you can supply something to catch everything. Or nothing, for that matter:
>
> BaseException  # catch everything

Not everything.

 >>> class A: pass
...
 >>> try: raise A
... except BaseException: pass
...
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
__main__.A: <__main__.A instance at 0xb707982c>




Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Terry Reedy
In reply to this post by Steven D'Aprano-11
On 4/10/2015 9:42 PM, Steven D'Aprano wrote:

> try:
>      spam()
> except:
>      # Implicitly an empty tuple.
>      pass

No, specified as equivalent to 'except BaseException:' (or 'except
(BaseException,):', either of which are different from 'except ():'.
"An expression-less except clause, if present, must be last; it matches
any exception."

--
Terry Jan Reedy



Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Cameron Simpson
In reply to this post by Rustom Mody
On 10Apr2015 19:38, Rustom Mody <rustompmody at gmail.com> wrote:

>On Saturday, April 11, 2015 at 7:53:31 AM UTC+5:30, Dave Angel wrote:
>> On 04/10/2015 09:42 PM, Steven D'Aprano wrote:
>> > On Sat, 11 Apr 2015 05:31 am, sohcahtoa82 wrote:
>> >> It isn't document because it is expected.  Why would the exception get
>> >> caught if you're not writing code to catch it?  If you write a function
>> >> and pass it a tuple of exceptions to catch, I'm not sure why you would
>> >> expect it to catch an exception not in the tuple.  Just because the tuple
>> >> is empty doesn't mean that it should catch *everything* instead.  That
>> >> would be counter-intuitive.
>> >
>> > Really? I have to say, I expected it.
>>
>> I'm astounded at your expectation.  That's like saying a for loop on an
>> empty list ought to loop on all possible objects in the universe.
>
>To work, this analogy should also have two python syntaxes like this:
>
>"Normal" for-loop:
>for var in iterable:
>  suite
>
>"Empty" for-loop:
>for:
>  suite

Well, to throw an anaolgy in the mix, the shell has a bare for syntax.

  list='a b c'
  for x in $list
  do  echo $x
  done

echoes a, b and c as you might expect.

This:

  for x
  do  echo $x
  done

echoes the command line arguments. (Which is extremely useful.)

But as with Python, the missing iteration means a _default_ source of
iteration, not _no_ iteration.

To continue the analogy, this:

  list=
  for x in $list
  do  echo $x
  done

echoes nothing. As I would hope Steven would expect.

Importantly, in both Python and the shell you have a way to specify "nothing".  

If the empty tuple were to mean "catch everything" then there would not be a
way to express "catch nothing". Bad bad bad!

Consider this a proof that Python's current meanings for bare except and
"except ()" are sensible, using a proof by contradiction.

Cheers,
Cameron Simpson <cs at zip.com.au>


Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Dave Angel-4
In reply to this post by Steven D'Aprano-11
On 04/11/2015 03:11 AM, Steven D'Aprano wrote:

> On Sat, 11 Apr 2015 12:23 pm, Dave Angel wrote:
>
>> On 04/10/2015 09:42 PM, Steven D'Aprano wrote:
>>> On Sat, 11 Apr 2015 05:31 am, sohcahtoa82 at gmail.com wrote:
>>>
>>>> It isn't document because it is expected.  Why would the exception get
>>>> caught if you're not writing code to catch it?  If you write a function
>>>> and pass it a tuple of exceptions to catch, I'm not sure why you would
>>>> expect it to catch an exception not in the tuple.  Just because the
>>>> tuple
>>>> is empty doesn't mean that it should catch *everything* instead.  That
>>>> would be counter-intuitive.
>>>
>>> Really? I have to say, I expected it.
>>>
>>>
>>
>> I'm astounded at your expectation.  That's like saying a for loop on an
>> empty list ought to loop on all possible objects in the universe.
>
> Not really.
>
> If we wrote:
>
>      for x in:
>          # Missing sequence leads to an infinite loop
>
> *then* your analogy would be excellent, but it isn't. With for loops, we
> iterate over each item in the sequence, hence an empty sequence means we
> don't iterate at all.
>
> But with try...except, an empty exception list means to catch *everything*,
> not nothing:

No an empty exception list means to catch nothing.  A *missing*
exception list means catch everything, but that's a different syntax
>
> try: ...
> except a,b,c: # catches a, b, c
>
> try: ...
> except a,b: # catches a, b
>
> try: ...
> except a: # catches a

try: ...
except (a,)   #catches a

try: ...
except ()  #catches nothing, as expected

>
> try: ...
> except: # catches EVERYTHING, not nothing
>

Different syntax.  No reason for it to pretend that it's being given an
empty tuple or list.

>
> Putting (a, b, c) into a tuple shouldn't make a difference, and it doesn't,
> unless the tuple is empty. That surprised me.
>
> t = a, b, c
> try:
> except t:  # same as except a,b,c
>
> t = a, b
> try:
> except t:  # same as except a,b
>
> t = a,
> try:
> except t:  # same as except a
>
> t = ()
> try:
> except t:  # NOT THE SAME as bare except.

Of course not.  It's empty, so it catches nothing. Just like 'for'

>
>
> I can see the logic behind the current behaviour. If you implement except
> clauses like this pseudo-code:
>
>
> for exc in exceptions:
>      if raised_exception matches exc: catch it
>
>
> then an empty tuple will naturally lead to nothing being caught. That
> doesn't mean it isn't surprising from the perspective that an empty
> exception list (i.e. a bare except) should be analogous to an empty tuple.

Why should it??  It's a different syntax, with different rules.  Perhaps
it should have been consistent, but then it's this statement that's
surprising, not the behavior with an empty tuple.

>
>
>> The tuple lists those exceptions you're interested in, and they are
>> tried, presumably in order, from that collection.  If none of those
>> match, then the logic will advance to the next except clause.  If the
>> tuple is empty, then clearly none will match.
>
> Yes, that makes sense, and I agree that it is reasonable behaviour from one
> perspective. But its also reasonable to treat "except ():" as analogous to
> a bare except.
>
> [...]
>>> try:
>>>       spam()
>>> except:
>>>       # Implicitly an empty tuple.
>>
>> No, an omitted item is not the same as an empty tuple.
>
> You are correct about Python as it actually is, but it could have been
> designed so that except (): was equivalent to a bare except.

Only by changing the bare except behavior.

>
>
>> If it were, then
>> we wouldn't have the problem of bare excepts, which are so tempting to
>> novices.  There's plenty of precedent in many languages for a missing
>> item being distinct from anything one could actually supply.
>
> Let us put aside the fact that some people misuse bare excepts, and allow
> that there are some uses for it. Now, in Python 2.6 and later, you can
> catch everything by catching BaseException. But in older versions, you
> could raise strings as well, and the only way to catch everything is with a
> bare except.
>
> If you want to write a function that takes a list of things to catch,
> defaulting to "everything", in Python 2.6+ we can write:
>
> def spam(things_to_catch=BaseException):
>      try:
>          do_stuff()
>      except things_to_catch:
>          handle_exception()
>
>
> but in older versions you have to write this:
>
> def spam(things_to_catch=None):
>      if things_to_catch is None:
>          try:
>              do_stuff()
>          except:
>              handle_exception()
>      else:
>          try:
>              do_stuff()
>          except things_to_catch:
>              handle_exception()
>
>
> This violates Don't Repeat Yourself. Any time you have "a missing item being
> distinct from anything one could actually supply", you have a poor design.

Yep, and it happens all the time.  For example, mylist[a,b,-1]    What
value can I use for b to mean the whole list?

There are others more grotesque, but I can't think of any at this moment.

>
> Anyway, in modern Python (2.6 onwards), now that string exceptions are gone,
> you can supply something to catch everything. Or nothing, for that matter:
>
> BaseException  # catch everything
> Exception  # catch errors
> (A, B, C)  # Just A, B or C or their subclasses
> A  # Just A (or its subclasses)
> ()  # Catch nothing.
>
> so I suppose that having an empty tuple mean "catch nothing" is better than
> having it catch everything.
>

Just like with all(()) and any(()), there's a logical way and an
illogical way.  An empty list means no items, not all possible items.



--
DaveA


Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Dave Angel-4
On 04/11/2015 06:14 AM, Dave Angel wrote:

> On 04/11/2015 03:11 AM, Steven D'Aprano wrote:
>> On Sat, 11 Apr 2015 12:23 pm, Dave Angel wrote:
>>
>>> On 04/10/2015 09:42 PM, Steven D'Aprano wrote:
>>>> On Sat, 11 Apr 2015 05:31 am, sohcahtoa82 at gmail.com wrote:
>>>>
>>>>> It isn't document because it is expected.  Why would the exception get
>>>>> caught if you're not writing code to catch it?  If you write a
>>>>> function
>>>>> and pass it a tuple of exceptions to catch, I'm not sure why you would
>>>>> expect it to catch an exception not in the tuple.  Just because the
>>>>> tuple
>>>>> is empty doesn't mean that it should catch *everything* instead.  That
>>>>> would be counter-intuitive.
>>>>
>>>> Really? I have to say, I expected it.
>>>>
>>>>
>>>
>>> I'm astounded at your expectation.  That's like saying a for loop on an
>>> empty list ought to loop on all possible objects in the universe.
>>
>> Not really.
>>
>> If we wrote:
>>
>>      for x in:
>>          # Missing sequence leads to an infinite loop
>>
>> *then* your analogy would be excellent, but it isn't. With for loops, we
>> iterate over each item in the sequence, hence an empty sequence means we
>> don't iterate at all.
>>
>> But with try...except, an empty exception list means to catch
>> *everything*,
>> not nothing:
>
> No an empty exception list means to catch nothing.  A *missing*
> exception list means catch everything, but that's a different syntax
>>
>> try: ...
>> except a,b,c: # catches a, b, c
>>
>> try: ...
>> except a,b: # catches a, b
>>
>> try: ...
>> except a: # catches a
>
> try: ...
> except (a,)   #catches a
>
> try: ...
> except ()  #catches nothing, as expected
>
>>
>> try: ...
>> except: # catches EVERYTHING, not nothing
>>
>
> Different syntax.  No reason for it to pretend that it's being given an
> empty tuple or list.
>
>>
>> Putting (a, b, c) into a tuple shouldn't make a difference, and it
>> doesn't,
>> unless the tuple is empty. That surprised me.
>>
>> t = a, b, c
>> try:
>> except t:  # same as except a,b,c
>>
>> t = a, b
>> try:
>> except t:  # same as except a,b
>>
>> t = a,
>> try:
>> except t:  # same as except a
>>
>> t = ()
>> try:
>> except t:  # NOT THE SAME as bare except.
>
> Of course not.  It's empty, so it catches nothing. Just like 'for'
>
>>
>>
>> I can see the logic behind the current behaviour. If you implement except
>> clauses like this pseudo-code:
>>
>>
>> for exc in exceptions:
>>      if raised_exception matches exc: catch it
>>
>>
>> then an empty tuple will naturally lead to nothing being caught. That
>> doesn't mean it isn't surprising from the perspective that an empty
>> exception list (i.e. a bare except) should be analogous to an empty
>> tuple.
>
> Why should it??  It's a different syntax, with different rules.  Perhaps
> it should have been consistent, but then it's this statement that's
> surprising, not the behavior with an empty tuple.
>
>>
>>
>>> The tuple lists those exceptions you're interested in, and they are
>>> tried, presumably in order, from that collection.  If none of those
>>> match, then the logic will advance to the next except clause.  If the
>>> tuple is empty, then clearly none will match.
>>
>> Yes, that makes sense, and I agree that it is reasonable behaviour
>> from one
>> perspective. But its also reasonable to treat "except ():" as
>> analogous to
>> a bare except.
>>
>> [...]
>>>> try:
>>>>       spam()
>>>> except:
>>>>       # Implicitly an empty tuple.
>>>
>>> No, an omitted item is not the same as an empty tuple.
>>
>> You are correct about Python as it actually is, but it could have been
>> designed so that except (): was equivalent to a bare except.
>
> Only by changing the bare except behavior.
>
>>
>>
>>> If it were, then
>>> we wouldn't have the problem of bare excepts, which are so tempting to
>>> novices.  There's plenty of precedent in many languages for a missing
>>> item being distinct from anything one could actually supply.
>>
>> Let us put aside the fact that some people misuse bare excepts, and allow
>> that there are some uses for it. Now, in Python 2.6 and later, you can
>> catch everything by catching BaseException. But in older versions, you
>> could raise strings as well, and the only way to catch everything is
>> with a
>> bare except.
>>
>> If you want to write a function that takes a list of things to catch,
>> defaulting to "everything", in Python 2.6+ we can write:
>>
>> def spam(things_to_catch=BaseException):
>>      try:
>>          do_stuff()
>>      except things_to_catch:
>>          handle_exception()
>>
>>
>> but in older versions you have to write this:
>>
>> def spam(things_to_catch=None):
>>      if things_to_catch is None:
>>          try:
>>              do_stuff()
>>          except:
>>              handle_exception()
>>      else:
>>          try:
>>              do_stuff()
>>          except things_to_catch:
>>              handle_exception()
>>
>>
>> This violates Don't Repeat Yourself. Any time you have "a missing item
>> being
>> distinct from anything one could actually supply", you have a poor
>> design.
>
> Yep, and it happens all the time.  For example, mylist[a,b,-1]    What
> value can I use for b to mean the whole list?
>
> There are others more grotesque, but I can't think of any at this moment.
>
>>
>> Anyway, in modern Python (2.6 onwards), now that string exceptions are
>> gone,
>> you can supply something to catch everything. Or nothing, for that
>> matter:
>>
>> BaseException  # catch everything
>> Exception  # catch errors
>> (A, B, C)  # Just A, B or C or their subclasses
>> A  # Just A (or its subclasses)
>> ()  # Catch nothing.
>>
>> so I suppose that having an empty tuple mean "catch nothing" is better
>> than
>> having it catch everything.
>>
>
> Just like with all(()) and any(()), there's a logical way and an
> illogical way.  An empty list means no items, not all possible items.
>

A better analogy:

       if  item  in mytuple:
              process...

If mytuple is (), should this somehow turn into the equivalent of
       if   True:

of course not.  If mytuple is an empty tuple, then the if never succeeds.

In this case, just like with the real try/except (before 2.6), if you
need to effectively make an infinite tuple, so that all possible items
would match, you'd need different syntax:

      #No syntax needed for this case, we don't care what item is
      process...



--
DaveA


Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Steven D'Aprano-11
In reply to this post by Steven D'Aprano-11
On Sat, 11 Apr 2015 06:22 pm, Serhiy Storchaka wrote:

> On 11.04.15 10:11, Steven D'Aprano wrote:
>> Anyway, in modern Python (2.6 onwards), now that string exceptions are
>> gone, you can supply something to catch everything. Or nothing, for that
>> matter:
>>
>> BaseException  # catch everything
>
> Not everything.
>
>  >>> class A: pass
> ...
>  >>> try: raise A
> ... except BaseException: pass
> ...
> Traceback (most recent call last):
>    File "<stdin>", line 1, in <module>
> __main__.A: <__main__.A instance at 0xb707982c>

Hmmm, I thought that starting from 2.6 exceptions had to inherit from
BaseException. Thanks for the correction.


[steve at ando ~]$ python2.7 -c "class A: pass
raise A()"
Traceback (most recent call last):
  File "<string>", line 2, in <module>
__main__.A: <__main__.A instance at 0xb7ebb1ac>


[steve at ando ~]$ python3.3 -c "class A: pass
raise A()"
Traceback (most recent call last):
  File "<string>", line 2, in <module>
TypeError: exceptions must derive from BaseException


Ahah! So it's 3.x only that catching BaseException should catch everything.

In 2.6, Python stopped supporting string exceptions:

[steve at ando ~]$ python2.6 -c "raise 'spam'"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
TypeError: exceptions must be old-style classes or derived from
BaseException, not str

(glad that at least I remembered that part correctly!)




--
Steven



Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Steven D'Aprano-11
In reply to this post by Rustom Mody
On Sat, 11 Apr 2015 07:27 pm, Cameron Simpson wrote:

> If the empty tuple were to mean "catch everything" then there would not be
> a way to express "catch nothing". Bad bad bad!

# Catch everything:
try:
    spam()
except:
    pass


# Catch nothing:
spam()


:-)


> Consider this a proof that Python's current meanings for bare except and
> "except ()" are sensible, using a proof by contradiction.


Given that Python 3 does not allow you to raise things which don't inherit
from BaseException, I wish that bare except clauses were dropped
altogether. In Python 3, the equivalent to "catch everything" is
spelled "except BaseException", which I think would be a lot less
attractive to beginners than bare "except:".

But I digress.

Yes, I agree that Python's behaviour here is better than the alternative.
Having "except ()" catch nothing is consistent with the behaviour with
other tuples, so I'm okay with that. But it still surprised me :-)



--
Steven



Reply | Threaded
Open this post in threaded view
|

try..except with empty exceptions

Chris Angelico
On Sat, Apr 11, 2015 at 9:00 PM, Steven D'Aprano
<steve+comp.lang.python at pearwood.info> wrote:
> Yes, I agree that Python's behaviour here is better than the alternative.
> Having "except ()" catch nothing is consistent with the behaviour with
> other tuples, so I'm okay with that. But it still surprised me :-)

It's worth noting that there's another false parallel here. Grouping
nothing creates something.

x = 1   # x is an integer
x = (1)  # ditto
x = (((((((((1)))))))))  # LITHP

Okay, so adding parentheses does nothing, right? Right.

x =  # syntax error
x = ()  # empty tuple

"Parentheses around nothing" is NOT the same as "nothing". So if you
compare against this, then it makes perfect sense for "except :" and
"except ():" to be distinctly different.

But I agree, it would be very nice if Python 3 could have abolished
the truly confusing part of this, where "except:" catches everything.
Forcing people to spell it "except BaseException:" would fix all of
this. How hard is it to deprecate and then remove that, same as string
exceptions were removed?

You know what, I'm moving that to -ideas.

ChrisA


12