New submission from Andrew Barnert:

Serhiy Storchaka raised an issue (http://bugs.python.org/msg256910) with static 
type hints on #25864 (http://bugs.python.org/issue25864), which I believe also 
applies to runtime ABCs.

Consider this class, which uses `[]` in a way similar to generic types:

    class Thing:
        def __getitem__(self, specialization):
            return type(self)._specialize(specialization)(self)
        def __len__(self):
            return len(self._specializations)

Because this type looks like it supports the old-style sequence protocol, 
calling either `iter` or `reversed` on an instance will successfully return a 
useless iterator. (You may get an error when you start trying to iterate it, 
but you may not even then.) You don't want that, so you add either this:

        __iter__ = None
        __reversed__ = None

... or this:

        def __iter__(self): raise TypeError('not iterable')
        def __reversed__(self): raise TypeError('not iterable')

Unfortunately, doing either means that `issubclass(Thing, 
collections.abc.Iterable)` now returns true. Which is the exact opposite of the 
intention of that check. (The same is true for `typing.Iterable` and 
`typing.Reversible`.) So, fixing the problem for duck typing creates the 
equivalent problem for explicit typing.

There are a few possible solutions here:

1. Maybe document it, otherwise do nothing.

2. Change the ABCs to check that the dunder method exists and is not None (or 
is callable, or is a non-data descriptor). Then, the one way to opt out is to 
assign `__iter__ = __reversed__ = None`.

3. Add an `ABC.unregister` method that can be used to explicitly state that 
this type does not support that ABC, regardless of what its `__subclasshook__` 
says.

Possible argument for #1: `Iterable` rarely has a problem. (Types that use 
`__getitem__` for something completely un-sequence-like, like `typing`'s 
generic types, usually don't have `__len__`. Types that have both `__getitem__` 
and `__len__`, like mappings, usually have a reasonable alternative `__iter__` 
to offer.) `Reversible` would have a problem if there was such an ABC, but 
there isn't. Off the top of my head, I can't think of any of the other implicit 
ABCs that are susceptible to this problem.

The counter-argument is that static typehinting definitely does have this 
problem (https://github.com/ambv/typehinting/issues/170), and, depending on how 
that's solved, it may well make sense to use the same solution here.

If we do need a solution, #2 seems better than #3 (or anything else I could 
think up). The only problem there is that `def __iter__(self): raise 
TypeError('not iterable')` gives you a nicer error than `__iter__ = None`.

----------
components: Library (Lib)
messages: 257052
nosy: abarnert
priority: normal
severity: normal
status: open
title: Implicit ABCs have no means of "anti-registration"
versions: Python 3.6

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue25958>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to