On Sat, Apr 01, 2017 at 09:27:34PM +0200, Ram Rachum wrote:
> Today I got burned because I had code that did this:
> 
>     except TimeoutError:
> 
> When it should have done this:
> 
>     except socket.timeout:

On the face of it, this isn't a serious problem. It seems to me rather 
like mistakenly writing:

    except TypeError

when you actually intended to write:

    except ZeroDivisionError

You failed to catch the exception that you needed, an got an exception. 
That's a bug, and the fix for it is to catch the correct exception. It 
isn't to introduce a new, more complex exception TypeOrZeroDivisionError 
that potentially catches too much.


> There's also another timeout error class in asyncio.
> 
> Initially I thought, why not make them all use the same exception class?

That's the wrong way to think about adding classes:

Wrong: "Why not add this new class?"

Right: "Why add this new class?"

We don't start from a position of "Default allow", that is, we add any 
arbitrary change suggested unless people find a reason to reject it. We 
start from a conservative position of "Default deny". All changes are 
rejected, unless they can show enough positive reasons to accept the 
change.

If you start off thinking "Why not make this change?", you will spend a 
lot of time suggesting things which are rejected. You should spend more 
time thinking critically about what actual positive reasons: "Why make 
this change?"

What is the benefit of this class? Does it unify two or more *related* 
exceptions? Guido doesn't think so, and I don't think so either. A 
socket timeout and an asyncio timeout are, to my mind, *unrelated*.

Just because they both relate to the passage of time doesn't make them 
the same thing.


> But Guido objects to that:
> 
> I considered this, and decided against unifying the two TimeoutErrors.
> 
> First the builtin TimeoutError is specifically a subclass of OSError
> representing the case where errno is ETIMEDOUT.  But
> asyncio.TimeoutError means nothing of the sort.
> 
> Second, the precedent is concurrent.futures.TimeoutError. The asyncio
> one is used under the same conditions as that one.

I agree with Guido's analysis. I don't think that the connection between 
the two timeout exceptions are related. 



> So here's my idea: Define a new base class `BaseTimeoutError` and have it
> be a base class of all other timeout errors. This way people could do:
> 
>     except BaseTimeoutError:
> 
> And it'll catch all of them.

Why would you do that?

This is exactly what I mean when I say that we need to have positive 
reasons for a change. It isn't enough to say that we can catch "all of 
them". We can already do that:

    # Did I miss any?
    except (TimeoutError, 
            asyncio.TimeoutError, 
            concurrent.futures.TimeoutError, 
            multiprocessing.TimeoutError,
            socket.timeout,
            subprocess.TimeoutExpired):

but *why* would I write that code? Under what circumstances will I have 
something that could raise *any* of those exceptions, and I will want to 
treat them *all* the same way?

It seems far more likely that I would *not* want to treat them the same 
way. If I have some networking code that raises socket.timeout, I 
probably want to catch that exception and try again (maybe with 
backoff). But if that same piece of networking code were to raise 
concurrent.futures.TimeoutError or subprocess.TimeoutExpired, I'd want 
to know about it! I'd want to see the exception, which means *not* 
catching it.

Or the other way around... if I am working with Futures, and have to 
deal with futures.TimeoutError, that doesn't mean that a networking 
timeout or a system call timeout should be treated the same way.

I'm having difficulty in seeing any positive benefit to this proposed 
base class.

If you really want this, you can name a tuple of all the unrelated 
exceptions you want to catch:

TimeoutErrors = (TimeoutError, asyncio.TimeoutError, 
        concurrent.futures.TimeoutError, socket.timeout,
        multiprocessing.TimeoutError, subprocess.TimeoutExpired)

try:
    ...
except TimeoutErrors:
    ...

but I cannot possibly recommend that. To me, that looks like it will 
catch too much under nearly all reasonable circumstances.




-- 
Steve
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to