python / python/cpython

ABCs with bogus __subclasses__ break instance/subclass checks for other ABCs

未關閉
#117,255 1 則留言 2 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

type-bug
主要語言
Python
星號
77.2k
分支
36k
PR 合併指標
PR 指標待擷取

描述

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

貢獻指南

開啟貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

研究方向

從 Lib/_py_abc.py 中遞迴呼叫 subclasses 的位置開始,重現 issue 中的兩個範例。然後將這條路徑與報告中提到的 C 實作進行比較。當有問題的 subclasses 定義不再導致不相關的 ABC 實例或子類別檢查失敗,並且為回報的案例提供回歸涵蓋時,即可視為完成。

由索引模型根據 Issue 內容生成。

評估

技術堆疊
python
領域
backend
Issue 類型
缺陷
難度
4/5
預估耗時
3-5 天
活躍度
停滯
描述清晰度
基本清楚
新手友好度
38/100

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。