New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Interrupting class creation in __init_subclass__ may lead to incorrect isinstance() and issubclass() results #82266
Comments
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: 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)) Result is: Dog is Animal? True Changing the order of the print statements will result in: Dog is Plant? False Another ill-behaving program and a partial analysis can be found at SO: https://stackoverflow.com/q/57848663/5378816 |
Same problem also in Python 3.6.8 and the new 3.8.0. |
Please, could some experienced Python developer take a look at this issue reported more than 3 month ago? |
Class |
Python 3.9.1 is affected too. |
2nd anniversary. Any reaction from developers would be appreciated. Thank you. |
Confirmed that this weird behavior is still present. Am not sure when someone will have the time and inclination to drill into the cause. |
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. |
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. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: