Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: issubclass without registration only works for "one-trick pony" collections ABCs. #68052

Closed
mjpieters mannequin opened this issue Apr 4, 2015 · 13 comments
Closed
Assignees
Labels
3.9 only security fixes 3.10 only security fixes 3.11 only security fixes docs Documentation in the Doc dir easy stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@mjpieters
Copy link
Mannequin

mjpieters mannequin commented Apr 4, 2015

BPO 23864
Nosy @gvanrossum, @rhettinger, @mjpieters, @ceronman, @eryksun, @ztane, @saulshanabrook, @csabella, @pablogsal, @maggyero, @miss-islington, @iritkatriel
PRs
  • bpo-45024 and bpo-23864: Document how interface testing works with the collections ABCs #28218
  • [3.10] bpo-45024 and bpo-23864: Document how interface testing works with the collections ABCs (GH-28218) #28266
  • Files
  • patch.diff: failing sequence test
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/rhettinger'
    closed_at = <Date 2021-09-10.03:59:35.028>
    created_at = <Date 2015-04-04.10:57:47.433>
    labels = ['easy', 'type-bug', '3.9', '3.10', '3.11', 'library', 'docs']
    title = 'doc: issubclass without registration only works for "one-trick pony" collections ABCs.'
    updated_at = <Date 2021-10-04.19:18:44.019>
    user = 'https://github.com/mjpieters'

    bugs.python.org fields:

    activity = <Date 2021-10-04.19:18:44.019>
    actor = 'pablogsal'
    assignee = 'rhettinger'
    closed = True
    closed_date = <Date 2021-09-10.03:59:35.028>
    closer = 'rhettinger'
    components = ['Documentation', 'Library (Lib)']
    creation = <Date 2015-04-04.10:57:47.433>
    creator = 'mjpieters'
    dependencies = []
    files = ['38938']
    hgrepos = []
    issue_num = 23864
    keywords = ['patch', 'easy']
    message_count = 13.0
    messages = ['240060', '240061', '240066', '240095', '240666', '331456', '331460', '332496', '400908', '400959', '401548', '401551', '403174']
    nosy_count = 14.0
    nosy_names = ['gvanrossum', 'rhettinger', 'mjpieters', 'joncle', 'docs@python', 'ceronman', 'eryksun', 'ztane', 'saulshanabrook', 'cheryl.sabella', 'pablogsal', 'maggyero', 'miss-islington', 'iritkatriel']
    pr_nums = ['28218', '28266']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue23864'
    versions = ['Python 3.9', 'Python 3.10', 'Python 3.11']

    @mjpieters
    Copy link
    Mannequin Author

    mjpieters mannequin commented Apr 4, 2015

    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.

    @mjpieters mjpieters mannequin assigned docspython Apr 4, 2015
    @mjpieters mjpieters mannequin added docs Documentation in the Doc dir stdlib Python modules in the Lib dir labels Apr 4, 2015
    @mjpieters
    Copy link
    Mannequin Author

    mjpieters mannequin commented Apr 4, 2015

    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

    @ztane
    Copy link
    Mannequin

    ztane mannequin commented Apr 4, 2015

    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).

    @eryksun
    Copy link
    Contributor

    eryksun commented Apr 4, 2015

    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.

    @saulshanabrook
    Copy link
    Mannequin

    saulshanabrook mannequin commented Apr 13, 2015

    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.

    @maggyero
    Copy link
    Mannequin

    maggyero mannequin commented Dec 10, 2018

    Guido, could we add those missing __subclasshook__ for consistency?

    @gvanrossum
    Copy link
    Member

    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.

    @csabella
    Copy link
    Contributor

    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 PEP-3119 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.

    @iritkatriel
    Copy link
    Member

    See also bpo-35190.

    @iritkatriel iritkatriel added easy 3.9 only security fixes 3.10 only security fixes 3.11 only security fixes labels Sep 2, 2021
    @iritkatriel iritkatriel changed the title issubclass without registration only works for "one-trick pony" collections ABCs. doc: issubclass without registration only works for "one-trick pony" collections ABCs. Sep 2, 2021
    @iritkatriel iritkatriel added the type-bug An unexpected behavior, bug, or error label Sep 2, 2021
    @gvanrossum
    Copy link
    Member

    Maybe bpo-45024 is also connected? (It also seems to relate to hasattr() logic.)

    @rhettinger
    Copy link
    Contributor

    New changeset 62fa613 by Raymond Hettinger in branch 'main':
    bpo-45024 and bpo-23864: Document how interface testing works with the collections ABCs (GH-28218)
    62fa613

    @rhettinger
    Copy link
    Contributor

    New changeset 89edd18 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)
    89edd18

    @pablogsal
    Copy link
    Member

    New changeset 9d8a64a 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)
    9d8a64a

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.9 only security fixes 3.10 only security fixes 3.11 only security fixes docs Documentation in the Doc dir easy stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    6 participants