ABCs with bogus __subclasses__ break instance/subclass checks for other ABCs
Ninguém assumiu esta issue ainda.
- Linguagem predominante
- Python
- Estrelas
- 77.2k
- Forks
- 36k
- Métricas de merge de PRs
- Métricas de PR pendentes
Descrição
Bug report
Bug description:
Consider this evil class:
import abc
class EvilABC(abc.ABC):
__subclasses__ = ...
Now all instance/subclass checks for other ABCs fail:
>>> isinstance(..., abc.ABC)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
isinstance(..., abc.ABC)
~~~~~~~~~~^^^^^^^^^^^^^^
File "<frozen abc>", line 119, in __instancecheck__
File "<frozen abc>", line 123, in __subclasscheck__
File "<frozen abc>", line 123, in __subclasscheck__
TypeError: attribute of type 'ellipsis' is not callable
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:
class Crossbreed(abc.ABCMeta, os.PathLike):
...
>>> isinstance("/", os.PathLike)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
isinstance("/", os.PathLike)
~~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "<frozen abc>", line 119, in __instancecheck__
File "<frozen abc>", line 123, in __subclasscheck__
File "<frozen abc>", line 123, in __subclasscheck__
TypeError: unbound method type.__subclasses__() needs an argument
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:
$ python3 -c 'import abc, os, subprocess
> class A(abc.ABCMeta, os.PathLike): pass
> subprocess.run(("echo",))'
Traceback (most recent call last):
File "<string>", line 3, in <module>
File "/usr/lib64/python3.12/subprocess.py", line 548, in run
with Popen(*popenargs, **kwargs) as process:
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.12/subprocess.py", line 1026, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "/usr/lib64/python3.12/subprocess.py", line 1802, in _execute_child
elif isinstance(args, os.PathLike):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen abc>", line 119, in __instancecheck__
File "<frozen abc>", line 123, in __subclasscheck__
File "<frozen abc>", line 123, in __subclasscheck__
TypeError: unbound method type.__subclasses__() needs an argument
In my opinion, the __subclasses__ call should be guarded by a try-except and either:
- skip broken subclasses, or
- skip broken subclasses and warn about them, or
- raise a better exception message, saying which subclass is broken
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
Guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Direção de pesquisa
Comece por Lib/_py_abc.py, na chamada recursiva de subclasses, e reproduza os dois exemplos da issue. Em seguida, compare esse caminho com a implementação em C mencionada no relatório. O trabalho estará concluído quando definições problemáticas de subclasses não fizerem mais com que verificações não relacionadas de instâncias ou subclasses de ABC falhem, com cobertura de regressão para os casos relatados.
Escrita pelo modelo de indexação a partir do texto da issue.
Avaliação
- Stack de tecnologia
- python
- Domínio
- backend
- Tipo de issue
- Bug
- Dificuldade
- 4/5
- Tempo estimado
- 3-5 dias
- Status de atividade
- Estagnada
- Clareza
- Razoavelmente clara
- Facilidade para iniciantes
- 38/100