This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: doc: issubclass without registration only works for "one-trick pony" collections ABCs.
Type: behavior Stage: resolved
Components: Documentation, Library (Lib) Versions: Python 3.11, Python 3.10, Python 3.9
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: rhettinger Nosy List: ceronman, cheryl.sabella, docs@python, eryksun, gvanrossum, iritkatriel, joncle, maggyero, miss-islington, mjpieters, pablogsal, rhettinger, saulshanabrook, ztane
Priority: normal Keywords: easy, patch

Created on 2015-04-04 10:57 by mjpieters, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
patch.diff saulshanabrook, 2015-04-13 17:27 failing sequence test
Pull Requests
URL Status Linked Edit
PR 28218 merged rhettinger, 2021-09-07 17:26
PR 28266 merged miss-islington, 2021-09-10 02:52
Messages (13)
msg240060 - (view) Author: Martijn Pieters (mjpieters) * Date: 2015-04-04 10:57
The collections.abc documentation implies that *any* of the container ABCs can be used in an issubclass test against a class that implements all abstract methods:

> These ABCs allow us to ask classes or instances if they provide particular functionality [...]

In reality this only applies to the "One Trick Ponies" (term from PEP 3119, things like Container and Iterable, those classes with one or two methods). It fails for the compound container ABCs:

>>> from collections.abc import Sequence, Container, Sized
>>> class MySequence(object):
...     def __contains__(self, item): pass
...     def __len__(self): pass
...     def __iter__(self): pass
...     def __getitem__(self, index): pass
...     def __len__(self): pass
... 
>>> issubclass(MySequence, Container)
True
>>> issubclass(MySequence, Sized)
True
>>> issubclass(MySequence, Sequence)
False

That's because the One Trick Ponies implement a __subclasshook__ method that is locked to the specific class and returns NotImplemented for subclasses; for instance, the Iterable.__subclasshook__ implementation is:

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterable:
            if any("__iter__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

The compound container classes build on top of the One Trick Ponies, so the class test will fail, NotImplemented is returned and the normal ABC tests for base classes that have been explicitly registered continues, but this won't include unregistered complete implementations.

Either the compound classes need their own __subclasshook__ implementations, or the documentation needs to be updated to make it clear that without explicit registrations the issubclass() (and isinstance()) tests only apply to the One Trick Ponies.
msg240061 - (view) Author: Martijn Pieters (mjpieters) * Date: 2015-04-04 11:04
I should have added the mixin methods for the Sequence implementation; the more complete demonstration is:

>>> from collections.abc import Sequence, Container, Sized
>>> class MySequence(object):
...     def __contains__(self, item): pass
...     def __len__(self): pass
...     def __iter__(self): pass
...     def __getitem__(self, index): pass
...     def __len__(self): pass
...     def __reversed__(self): pass
...     def index(self, item): pass
...     def count(self, item): pass
... 
>>> issubclass(MySequence, Container)
True
>>> issubclass(MySequence, Sized)
True
>>> issubclass(MySequence, Sequence)
False
msg240066 - (view) Author: Antti Haapala (ztane) * Date: 2015-04-04 11:30
This does apply to all versions of Python from 2.6 up. Registering does work of course.

I believe the reason for not having the __subclasshook__ is the following sentence in PEP 3119: "ABCs are intended to solve problems that don't have a good solution at all in Python 2, such as distinguishing between mappings and sequences."

This used to be worse in <3.3 because there if you ever inherit from `Sequence` you will always end up having `__dict__`, even if you just want `__slots__`.

(By the way, if Py2 documentation is fixed, it should also say that these ABCs are new as of 2.6, not since 2.4 like the rest of the collections module).
msg240095 - (view) Author: Eryk Sun (eryksun) * (Python triager) Date: 2015-04-04 22:51
Probably I'm overlooking something, but why isn't this hook defined cooperatively, with a terminating base class method that returns True? If the call chain progresses to the base, then all of the interfaces have been satisfied. Otherwise one of the bases returns NotImplemented. If it's implemented cooperatively, then the `cls is Iterable` check can be removed, because it returns super().__subclasshook__(C) instead of True.
msg240666 - (view) Author: Saul Shanabrook (saulshanabrook) * Date: 2015-04-13 17:18
I have added a failing test to based on the first example, of a class that provides the necessary methods, but fails to be an instance of Sequence.
msg331456 - (view) Author: Géry (maggyero) * Date: 2018-12-10 00:16
Guido, could we add those missing __subclasshook__ for consistency?
msg331460 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2018-12-10 02:01
No, I consider this is a documentation problem. I don't recall why the docs say that (I don't even know if they still say that or whether Martijn misread them), but IMO this should not be changed.
msg332496 - (view) Author: Cheryl Sabella (cheryl.sabella) * (Python committer) Date: 2018-12-24 17:53
This isn't meant as a comment from any previous posts.  It's simply meant to correct a statement (based on new information in the past 2 years) from the original post.

Since this original report, some ABCs that are not "One Trick Ponies" have been added which implement  __subclasshook__.  `Collection` is one of those, so using the original example:

>>> from collections.abc import Sequence, Container, Sized, Collection
>>> class MySequence(object):
...     def __contains__(self, item): pass
...     def __len__(self): pass
...     def __iter__(self): pass
...     def __getitem__(self, index): pass
...     def __len__(self): pass
...     def __reversed__(self): pass
...     def index(self, item): pass
...     def count(self, item): pass
... 
>>> issubclass(MySequence, Container)
True
>>> issubclass(MySequence, Sized)
True
>>> issubclass(MySequence, Sequence)
False
>>> issubclass(MySequence, Collection)
True

Collection is not a "One Trick Pony" because it is used for Sized, Iterable Containers.

Generator, Coroutine, and ASyncGenerator are also not "One Trick Ponies" (although they are defined under that section in _collections_abc.py).

Again, for reference, the definition of One Trick Pony from PEP3119 is:
These abstract classes represent single methods like __iter__ or __len__.

If only One Trick Ponies implemented __subclasshook__, then the original documentation issue:
> These ABCs allow us to ask classes or instances if they provide particular functionality, for example:

maybe could have been changed to:
> These ABCs allow us to ask classes or instances if they provide singular functionality, for example:

But, that's not really correct anymore.
msg400908 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-09-02 10:21
See also issue35190.
msg400959 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2021-09-02 21:10
Maybe issue45024 is also connected? (It also seems to relate to hasattr() logic.)
msg401548 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2021-09-10 02:51
New changeset 62fa613f6a6e872723505ee9d56242c31a654a9d by Raymond Hettinger in branch 'main':
bpo-45024 and bpo-23864: Document how interface testing works with the collections ABCs (GH-28218)
https://github.com/python/cpython/commit/62fa613f6a6e872723505ee9d56242c31a654a9d
msg401551 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2021-09-10 03:58
New changeset 89edd18779e382c5fa7f57722b0b897a907ed2c4 by Miss Islington (bot) in branch '3.10':
bpo-45024 and bpo-23864: Document how interface testing works with the collections ABCs (GH-28218) (GH-28266)
https://github.com/python/cpython/commit/89edd18779e382c5fa7f57722b0b897a907ed2c4
msg403174 - (view) Author: Pablo Galindo Salgado (pablogsal) * (Python committer) Date: 2021-10-04 19:18
New changeset 9d8a64a7ccebb288094d4e9da66f30d2ada9a973 by Pablo Galindo (Miss Islington (bot)) in branch '3.10':
bpo-45024 and bpo-23864: Document how interface testing works with the collections ABCs (GH-28218) (GH-28266)
https://github.com/python/cpython/commit/9d8a64a7ccebb288094d4e9da66f30d2ada9a973
History
Date User Action Args
2022-04-11 14:58:15adminsetgithub: 68052
2021-10-04 19:18:44pablogsalsetnosy: + pablogsal
messages: + msg403174
2021-09-10 03:59:35rhettingersetstatus: open -> closed
assignee: docs@python -> rhettinger
resolution: fixed
stage: patch review -> resolved
2021-09-10 03:58:56rhettingersetmessages: + msg401551
2021-09-10 02:52:01miss-islingtonsetnosy: + miss-islington
pull_requests: + pull_request26687
2021-09-10 02:51:15rhettingersetmessages: + msg401548
2021-09-07 17:26:35rhettingersetkeywords: + patch
nosy: + rhettinger

pull_requests: + pull_request26644
stage: patch review
2021-09-02 21:10:32gvanrossumsetmessages: + msg400959
2021-09-02 10:21:21iritkatrielsettype: behavior
title: issubclass without registration only works for "one-trick pony" collections ABCs. -> doc: issubclass without registration only works for "one-trick pony" collections ABCs.

keywords: + easy, - patch
nosy: + iritkatriel
versions: + Python 3.9, Python 3.10, Python 3.11
messages: + msg400908
2019-02-27 20:32:05ceronmansetnosy: + ceronman
2019-02-14 16:29:33josh.runlinkissue25737 superseder
2018-12-24 17:53:44cheryl.sabellasetnosy: + cheryl.sabella
messages: + msg332496
2018-12-10 02:01:27gvanrossumsetmessages: + msg331460
2018-12-10 00:16:52maggyerosetnosy: + gvanrossum, maggyero
messages: + msg331456
2015-11-26 12:30:58eryksunlinkissue25737 superseder
2015-04-13 17:27:58saulshanabrooksetfiles: + patch.diff
2015-04-13 17:27:51saulshanabrooksetfiles: - patch.diff
2015-04-13 17:26:35saulshanabrooksetfiles: + patch.diff
2015-04-13 17:24:51saulshanabrooksetfiles: - patch.diff
2015-04-13 17:18:03saulshanabrooksetfiles: + patch.diff

nosy: + saulshanabrook
messages: + msg240666

keywords: + patch
2015-04-04 22:51:35eryksunsetnosy: + eryksun
messages: + msg240095
2015-04-04 11:30:16ztanesetnosy: + ztane
messages: + msg240066
2015-04-04 11:04:53mjpieterssetmessages: + msg240061
2015-04-04 10:59:26jonclesetnosy: + joncle
2015-04-04 10:57:47mjpieterscreate