Open hroncok opened 7 months ago
According to the documentation, the class.__subclasses__
method is supposed to work on any class. Since type
itself is a class, calling type.__subclasses__()
without an argument should be expected to work instead of producing TypeError: unbound method type.__subclasses__() needs an argument
.
This can be fixed by making the first parameter of type.__subclasses__
optional, defaulting its value to type
.
Demo of a quick fix:
import sys
import builtins
class NewType(type):
def __new__(cls, *args, **kwargs):
if len(args) == 1:
return orig_type(*args)
return super().__new__(cls, *args, **kwargs)
def __subclasses__(cls=type):
return orig_type.__subclasses__(cls)
orig_type = type
builtins.type = NewType
del sys.modules['_abc'], sys.modules['abc'], sys.modules['os']
import _abc
del _abc._abc_init
import abc
import os
class Crossbreed(abc.ABCMeta, os.PathLike):
...
print(os.PathLike.__subclasses__()) # outputs [<class '__main__.Crossbreed'>]
Bug report
Bug description:
Consider this evil class:
Now all instance/subclass checks for other ABCs fail:
This happens because the recursive call to
__subclasses__
in https://github.com/python/cpython/blob/v3.13.0a5/Lib/_py_abc.py#L141 trusts that__subclasses__
will always be callable and return an iterable (or a list in the C implementation).You might argue that the EvilABC class is evil and whoever does that is doomed to be punished. However, it can easily be done by a mistake. This class has a broken
__subclasses__
without explicitly defining it:It is very surprising that by defining a class we can break checks for other classes and hence third party code, even from the standard library:
In my opinion, the
__subclasses__
call should be guarded by a try-except and either:See also https://discuss.python.org/t/abcmeta-change-isinstancecheck-of-additional-parent-class/19908
CPython versions tested on:
3.8, 3.9, 3.10, 3.11, 3.12, 3.13, CPython main branch
Operating systems tested on:
No response