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
Discrepancy between isinstance() and issubclass() for union types #88772
Comments
>>> isinstance(None, int | type(None))
True
>>> issubclass(type(None), int | type(None))
True
>>> isinstance(None, int | None)
True
>>> issubclass(type(None), int | None)
False
>>> import collections.abc
>>> isinstance({}, int | collections.abc.Mapping)
True
>>> issubclass(dict, int | collections.abc.Mapping)
False I do not know what behavior is correct. |
Ken Jin, should we treat type(None) as a subclass of int|None? |
I have found this when refactored the code of Objects/unionobject.c. So I have a patch which fixes this, but I want to make sure what behavior is considered correct. |
This looks like a bug. I think the union form of isinstance/issubclass should have the same behavior as the tuple form. We promise checking of virtual classes in the docs for union types: The fix seems to be changing one line of code in Objects/unionobject.c from PyType_IsSubtype to PyObject_IsSubclass. Since it isn't a large change and it's a bug, I think we can backport it to 3.10 too. @serhiy and Guido,
I think we can avoid this special case altogether by converting all None to type(None) when creating _Py_Union. This is what the typing.Union counterpart does, and it is also what PEP-484 says about None https://www.python.org/dev/peps/pep-0484/#using-none: >>> Union[None, str].__args__
(<class 'NoneType'>, <class 'str'>) Though I know some people are opinionated about None -> type(None). This would require adding one more check at https://github.com/python/cpython/blob/3.10/Objects/unionobject.c#L266 . if(Py_IsNone(i_element))
i_element = &_PyNone_Type |
Converting None to type(None) is fine, as long as the str() /repr() |
@serhiy, can I work on converting None to type(None) please? |
>>> class BadMeta(type):
... def __instancecheck__(cls, inst):
... 1/0
...
>>> x = int | BadMeta('A', (), {})
>>> isinstance([], x)
Fatal Python error: _Py_CheckFunctionResult: a function returned a result with an exception set
Python runtime state: initialized
Traceback (most recent call last):
File "<stdin>", line 3, in __instancecheck__
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception: SystemError: <built-in method __instancecheck__ of types.Union object at 0x7f024bbb8960> returned a result with an exception set Current thread 0x00007f024beb1280 (most recent call first): |
PR 27120 fixes __instancecheck__ and __subclasscheck__. Converting None to type(None) will be done in other PR. |
That's unfortunate :(. Serhiy, thanks for catching all these bugs in union. I recently realized you probably made 50% of all bug reports for union and they're very much appreciated :).
Alright. Do tell me if you're too busy and want me to take it up instead. I'll be glad to help. |
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: