try..except with empty exceptions

Steven D'Aprano steve+comp.lang.python at pearwood.info
Sat Apr 11 03:11:01 EDT 2015


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




More information about the Python-list mailing list