Author abarnert
Recipients abarnert
Date 2015-12-26.22:26:42
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1451168802.83.0.911068284232.issue25958@psf.upfronthosting.co.za>
In-reply-to
Content
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`.
History
Date User Action Args
2015-12-26 22:26:42abarnertsetrecipients: + abarnert
2015-12-26 22:26:42abarnertsetmessageid: <1451168802.83.0.911068284232.issue25958@psf.upfronthosting.co.za>
2015-12-26 22:26:42abarnertlinkissue25958 messages
2015-12-26 22:26:42abarnertcreate