Repository navigation
Problems caused by isinstance(list[int], type) returning True #89828
Description
Activity
This is a meta-issue for problems caused by isinstance(list[int]) returning True.
See also discussion in bpo-45438.
- added3.9 (EOL)end of lifeend of life3.10 (EOL)end of lifeend of life3.11only security fixesonly security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Oct 29, 2021 isinstance(x, type) returns True for instances of types.GenericAlias (like list[int]). While it may help in some cases related to typing, in many unrelated cases it causes problems if the value which is not a type passes checks for types.
Also, isinstance(x, type) been not equal to issubclass(type(x), type) can cause other problems. No matter what the result should be, it should be consistent.
There are many occurrences of isinstance(..., type) in the code.
$ find Lib -name '*.py' \! -path '*/test*' -exec egrep 'isinstance.*, type\)' '{}' + | wc -l 55
And all of them can potentially be broken if pass a types.GenericAlias instance. Many of them are safe, but every case should be analyzed.
- changed the title
[-]Problems caused by isinstance(list[int]) returning True[/-][+]Problems caused by isinstance(list[int], type) returning True[/+]on Oct 29, 2021 - changed the title
[-]Problems caused by isinstance(list[int]) returning True[/-][+]Problems caused by isinstance(list[int], type) returning True[/+]on Oct 29, 2021 One thing that probably should be considered in this context:
isinstance(arg, type) == issubclass(type(arg), type)
Holds True for arg in (Optional[X], Union[X, Y]). Both sides evaluate to False. (v3.10.0)
While I still think both sides evaluating to True would be more intuitive, this supports the proposed change.
Small test snippet:
from typing import Dict, List, Set, Tuple, Optional, Union import pytest @pytest.mark.parametrize('arg', [ list, List[int], list[int], dict, Dict[str, int], dict[str, int], set, Set[int], set[int], tuple, Tuple[str, int], tuple[str, int], Optional[int], Union[int, str] ]) def test_invariant(arg): same = isinstance(arg, type) == issubclass(type(arg), type) result = "Check" if same else "Failed" print(f"\n{result}: Testing: {arg=} with {type(arg)=}: {isinstance(arg, type)=} <> {issubclass(type(arg), type)=}") assert sameAny other commonly used annotations that could be added to the checklist?
12 remaining items
Gentle ping, as the next alpha will be release soon
We will still allow instantiating e.g. list[int], right?
I certainly hope so! That would be a much more breaking change if we were to change that, and I can't personally see any benefit to doing so.
The cause what that
list[int].__class__returnedtypeinstead oftypes.GenericAlias.I suppose also that there may be problems caused by relaying other dunder methods. There is a list of exceptions, but it is not complete.
typing._GenericAliasin general does not relay dunder methods (except__name__and__qualname__).- added a commit that references this issue
on Jun 26, 2022 At this point I'd like to report the following inconsistency between C
PyType_Check(t)and Python'sisinstance(t, type)that I've noticed. I don't know if it's fixed at the moment, just so that it doesn't get lost:from abc import ABCMeta # the C implementation is required for this snippet class MyABC(metaclass=ABCMeta): pass t = list[int] isinstance(t, type) and issubclass(t, MyABC)
causing the following error:
Traceback (most recent call last): File "...", line 9, in <module> isinstance(t, type) and issubclass(t, MyABC) File "/usr/lib/python3.10/abc.py", line 123, in __subclasscheck__ return _abc_subclasscheck(cls, subclass) TypeError: issubclass() arg 1 must be a classBut if we change
abcimport to_py_abc,from _py_abc import ABCMeta # Python implementation class MyABC(metaclass=ABCMeta): pass t = list[int] isinstance(t, type) and issubclass(t, MyABC)
the above absolutely does not produce any error.
This is of course clearly aGenericAlias-related problem.At this point I'd like to report the following inconsistency between C
PyType_Check(t)and Python'sisinstance(t, type)that I've noticed. I don't know if it's fixed at the moment, just so that it doesn't get lost:
[...]What Python version? The most recent version where I can repro that error is 3.10. The fix appears to be in 3.11 and on main (3.12 alpha).
Oh, right. Yes, it was 3.10, like in the traceback.
Sorry for not specifying the version and not checking if the bug is not already fixed in versions >3.10.
Also sorry for causing unnecessary chaos.
Thanks and good luck!There is not much that we can do with this in 3.10. The workaround in the user code is:
isinstance(t, type) and not isinstance(t, types.GenericAlias) and issubclass(t, MyABC)
The middle check is not needed in 3.11+.
I think this issue can now be closed. I have not found other sites in the stdlib with user visible differences.
But I have found an interesting difference in the behavior of
issubclass(), which should rarely affect user code: #101162.
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: