On Fri, Sep 3, 2021 at 5:04 PM Oscar Benjamin <oscar.j.benjamin@gmail.com> wrote:
On Fri, 3 Sept 2021 at 17:32, Guido van Rossum <guido@python.org> wrote:
>
> On Fri, Sep 3, 2021 at 4:24 AM Oscar Benjamin <oscar.j.benjamin@gmail.com> wrote:
>>
>> On Fri, 3 Sept 2021 at 08:10, Serhiy Storchaka <storchaka@gmail.com> wrote:
>> >
>> > There are two different kinds of TypeError: if the end user passes an
>> > instance of wrong type and if the author of the library makes an error
>> > in implementation of some protocols.
>> >
>> > For example, len(obj) raises TypeError in two cases: if obj does not
>> > have __len__ (user error) and if obj.returns non-integer (implementation
>> > error). for x in obj raises TypeError if obj does not have __iter__
>> > (user error) and if iter(obj) does not have __next__ (implementation error).
>> >
>> > User errors can be fixed on user side, implementation errors can only be
>> > fixed by the author of the class. Even if the user and the author is the
>> > same person, these errors point to different places of code.
>> >
>> > Would it be worth to add a special TypeError subclass for implementation
>> > errors to distinguish them from user errors? How to name it
>> > (ImplementationError, ProtocolError, etc)?
>>
>> I think that it would be good to make TypeError more fine-grained.
>> Another example is:
>>
>> >>> sum(1, 2, 3)
>> Traceback (most recent call last):
>>   File "<stdin>", line 1, in <module>
>> TypeError: sum() takes at most 2 arguments (3 given)
>>
>> There can be reasons in library code to catch TypeError that might
>> arise from bad user input but in those situations you would usually
>> not want to catch this TypeError. The error from calling a function
>> with the wrong number of arguments would usually mean a bug in the
>> library code which should not be caught. Conversely if the user input
>> is a callable and you do want to catch the error resulting from it
>> being called with the wrong number of arguments then catching
>> TypeError is too broad again. Something like BadArgumentsError would
>> be better.
>
> The question is, would anyone ever want to make a distinction between the two in *real* code? I find it unlikely that someone would write
>
> try:
>     sum(x, y, z)
> except TypeError:
>     ...
>
> If you bury the sum() call deep inside other code, I'd say your try/except net is too wide.

You've misunderstood. I do *not* want to catch the exception from sum(x, y, z).

I know you don't. My point with that example was to show how unlikely it is that someone would write that in the first place (since the code would always fail rather than in specific cases).
 
Sometimes a situation arises that there is a problem involving an
exception being raised that is best fixed by catching the exception.
Python does not provide much of a programmatic API for inspecting
exceptions beyond simply choosing which class of exceptions to catch.
That makes it important to limit the class of exceptions caught as
carefully as possible.

I find it unlikely that TypeError (in particular) is of much use for repairing a situation, precisely because it is raised for situations where a value has an incorrect type. That sounds to me like either a bug in the code (and you don't fix bugs by catching exceptions, you fix them by changing the source code), or something that should be checked explicitly before making the call (e.g. if you can only sum ints and floats, and your data is from an unknown source, you should check the data before calling sum).
 
If the exception that needs to be caught is literally TypeError then
it is not possible to be more precise than simply "except TypeError".
I have seen code that parses the error messages but that seems flakey
to me. The problem with "except TypeError" is that it also catches
exceptions from situations where the exception should very rarely be
caught e.g. sum(1, 2, 3).

This argument would be more convincing if you could quote real code where this has been a problem.

In the case of sum(), it's no hardship to ensure that you pass a list of values, avoiding the sum(x, y, z) mistake, so if you wish to catch the TypeError from sum(a), that should not need this language change.
 
If the different cases that currently result in TypeError were broken
down into more fine-grained exception classes then I think that would
be good. A similar thing happened for OSError - in that case it was
previously possible to distinguish cases using the errno attribute but
it was awkward. In the case of TypeError there is nothing akin to the
errno attribute so you either catch all TypeError or none.

And that's exactly the difference. For OSError it is *obviously* useful to catch the exception and take different actions based on the errno value, and using separate subclasses makes such code more robust. I have seen many such examples in real code (my own, and others'). But where are the examples showing that TypeError needs to be more granular? I find Serhiy's examples insufficiently convincing.

--
--Guido van Rossum (python.org/~guido)