Skip to content

Strange interaction between typing.Protocol and unrelated isinstance() checks #105144

Description

@AlexWaygood

On main, an isinstance() check against Sized works just the same as it does on Python 3.11:

>>> from typing import Sized
>>> isinstance(1, Sized)
False

However! If you first subclass Sized like this, TypeError is raised on that isinstance() check!

>>> from typing import Sized, Protocol
>>> class Foo(Sized, Protocol): pass
...
>>> isinstance(1, Sized)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\alexw\coding\cpython\Lib\typing.py", line 1153, in __instancecheck__
    return self.__subclasscheck__(type(obj))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\cpython\Lib\typing.py", line 1428, in __subclasscheck__
    return issubclass(cls, self.__origin__)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen abc>", line 123, in __subclasscheck__
  File "C:\Users\alexw\coding\cpython\Lib\typing.py", line 1793, in __subclasscheck__
    return super().__subclasscheck__(other)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen abc>", line 123, in __subclasscheck__
  File "C:\Users\alexw\coding\cpython\Lib\typing.py", line 1876, in _proto_hook
    raise TypeError("Instance and class checks can only be used with"
TypeError: Instance and class checks can only be used with @runtime_checkable protocols

This was originally reported by @vnmabus in python/typing_extensions#207.

Note that (because of the abc-module cache), this doesn't reproduce if you do an isinstance() check before subclassing typing.Sized:

>>> import typing
>>> isinstance(1, typing.Sized)
False
>>> class Foo(typing.Sized, typing.Protocol): pass
...
>>> isinstance(1, typing.Sized)
False

Linked PRs

Activity

  1. added
    type-bugAn unexpected behavior, bug, or error
    stdlibStandard Library Python modules in the Lib/ directory
    3.12only security fixes
    3.13only security fixes
    on May 31, 2023
  2. AlexWaygood commented on May 31, 2023

    @AlexWaygood
    MemberAuthor

    This bisects to b27fe67

  3. changed the title [-]Strange interaction between `typing._BaseGenericAlias` and `typing.Protocol`[/-] [+]Strange interaction between `typing._BaseGenericAlias` and `typing.Protocol` with `isinstance()` checks[/+] on May 31, 2023
  4. changed the title [-]Strange interaction between `typing._BaseGenericAlias` and `typing.Protocol` with `isinstance()` checks[/-] [+]Strange interaction between `typing.Protocol` and unrelated `isinstance()` checks[/+] on May 31, 2023
  5. AlexWaygood commented on May 31, 2023

    @AlexWaygood
    MemberAuthor

    Also reproduces if you use collections.abc.Sized rather than typing.Sized:

    >>> import collections.abc
    >>> import typing
    >>> class Foo(collections.abc.Sized, typing.Protocol): pass
    ...
    >>> isinstance(1, collections.abc.Sized)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "C:\Users\alexw\coding\cpython\Lib\abc.py", line 119, in __instancecheck__
        return _abc_instancecheck(cls, instance)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "C:\Users\alexw\coding\cpython\Lib\abc.py", line 123, in __subclasscheck__
        return _abc_subclasscheck(cls, subclass)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "C:\Users\alexw\coding\cpython\Lib\typing.py", line 1796, in __subclasscheck__
        return super().__subclasscheck__(other)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "C:\Users\alexw\coding\cpython\Lib\abc.py", line 123, in __subclasscheck__
        return _abc_subclasscheck(cls, subclass)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "C:\Users\alexw\coding\cpython\Lib\typing.py", line 1879, in _proto_hook
        raise TypeError("Instance and class checks can only be used with"
    TypeError: Instance and class checks can only be used with @runtime_checkable protocols
  6. self-assigned this
    on May 31, 2023
  7. added a commit that references this issue on May 31, 2023
  8. added a commit that references this issue on May 31, 2023
  9. added a commit that references this issue on May 31, 2023
  10. JelleZijlstra commented on May 31, 2023

    @JelleZijlstra
    Member

    With some debugging we found that this is due to code in _abc.c that iterates over all the __subclasses__ of the ABC:

    subclasses = PyObject_CallMethod(self, "__subclasses__", NULL);

    Alex's PR #105152 works around this problem by re-arranging the code in typing.py. In #105159 I propose a fix on the ABC side instead. We should consider merging that PR too to prevent similar problems with other ABC subclasses.

  11. added a commit that references this issue on May 31, 2023
  12. AlexWaygood commented on Jun 16, 2023

    @AlexWaygood
    MemberAuthor

    This issue is fixed, and Jelle's decided not to pursue #105159, so I'll close this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

3.12only security fixes3.13only security fixesstdlibStandard Library Python modules in the Lib/ directorytopic-typingtype-bugAn unexpected behavior, bug, or error

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions