This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Interrupting class creation in __init_subclass__ may lead to incorrect isinstance() and issubclass() results
Type: behavior Stage: patch review
Components: Interpreter Core Versions: Python 3.9, Python 3.8, Python 3.7, Python 3.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Carl.Friedrich.Bolz, hongweipeng, lukasz.langa, rhettinger, xitop
Priority: normal Keywords: patch

Created on 2019-09-10 09:09 by xitop, last changed 2022-04-11 14:59 by admin.

Files
File name Uploaded Description Edit
x.py Carl.Friedrich.Bolz, 2021-12-23 20:42
Pull Requests
URL Status Linked Edit
PR 30112 open hongweipeng, 2021-12-15 08:21
Messages (9)
msg351599 - (view) Author: (xitop) Date: 2019-09-10 09:09
An exception in __init__subclass__ leads under certain circumstances to wrong isinstance() and issubclass() results. The exception probably leaves Python internal data in inconsistent state.

Here is a demonstration program from Stack Overflow:

--- begin --
from abc import ABCMeta

class Animal(metaclass=ABCMeta):
    pass

class Plant(metaclass=ABCMeta):
    def __init_subclass__(cls):
        assert not issubclass(cls, Animal), "Plants cannot be Animals"

class Dog(Animal):
    pass

try:
    class Triffid(Animal, Plant):
        pass
except Exception:
    pass

print("Dog is Animal?", issubclass(Dog, Animal))
print("Dog is Plant?", issubclass(Dog, Plant))
--- end --

Result is:

Dog is Animal? True
Dog is Plant? True

Changing the order of the print statements will result in:

Dog is Plant? False
Dog is Animal? False

Another ill-behaving program and a partial analysis can be found at SO: https://stackoverflow.com/q/57848663/5378816
msg355037 - (view) Author: (xitop) Date: 2019-10-21 10:05
Same problem also in Python 3.6.8 and the new 3.8.0.
msg358966 - (view) Author: (xitop) Date: 2019-12-28 20:42
Please, could some experienced Python developer take a look at this issue reported more than 3 month ago?
msg381210 - (view) Author: hongweipeng (hongweipeng) * Date: 2020-11-17 06:40
Class `Triffid` failed to create in `__init_subclass__`, so `Triffid._abc_cache` uses its base class `_abc_cache`.
msg383796 - (view) Author: (xitop) Date: 2020-12-26 07:40
Python 3.9.1 is affected too.
msg401784 - (view) Author: (xitop) Date: 2021-09-14 16:03
2nd anniversary. Any reaction from developers would be appreciated. Thank you.
msg401998 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2021-09-17 02:24
Confirmed that this weird behavior is still present.  Am not sure when someone will have the time and inclination to drill into the cause.
msg409106 - (view) Author: Carl Friedrich Bolz-Tereick (Carl.Friedrich.Bolz) * Date: 2021-12-23 20:42
hm, I think I figured it out. The root cause is that even though the creation of the class Triffid fails, it can still be found via Animal.__subclasses__(), which the special subclass logic for ABCs is looking at. Triffid fills its _abc_impl data with some content, but Triffid._abc_impl was never successfully initialized, therefore it mutates the _abc_impl of its first base class Animal.

My conclusion would be that if a class is not successfully created, it shouldn't appear in the .__subclasses__() list of its bases.

See attached script for some illuminating prints.
msg409107 - (view) Author: Carl Friedrich Bolz-Tereick (Carl.Friedrich.Bolz) * Date: 2021-12-23 20:42
Or, in other words, in my opinion this is the root cause of the bug:

class Base:
    def __init_subclass__(cls):
        global broken_class
        broken_class = cls
        assert 0
try:
    class Broken(Base): pass
except: pass
assert broken_class not in Base.__subclasses__()

The assert fails, which imo it shouldn't.
History
Date User Action Args
2022-04-11 14:59:20adminsetgithub: 82266
2021-12-23 20:42:44Carl.Friedrich.Bolzsetmessages: + msg409107
2021-12-23 20:42:14Carl.Friedrich.Bolzsetfiles: + x.py
nosy: + Carl.Friedrich.Bolz
messages: + msg409106

2021-12-15 08:21:42hongweipengsetkeywords: + patch
stage: patch review
pull_requests: + pull_request28335
2021-09-17 02:24:12rhettingersetnosy: + rhettinger
messages: + msg401998
2021-09-17 02:22:48rhettingersetnosy: + lukasz.langa
2021-09-14 16:03:49xitopsetmessages: + msg401784
2020-12-26 07:40:18xitopsetmessages: + msg383796
versions: + Python 3.9
2020-11-17 06:40:22hongweipengsetnosy: + hongweipeng
messages: + msg381210
2019-12-28 20:42:03xitopsetmessages: + msg358966
2019-10-21 10:05:54xitopsetmessages: + msg355037
versions: + Python 3.6, Python 3.8
2019-09-10 09:09:41xitopcreate