Skip to content

Commit b2ac70a

Browse files
pythongh-88834: Unify the instance check for typing.Union and types.UnionType (pythonGH-128363)
Union now uses the instance checks against its parameters instead of the subclass checks.
1 parent 7c72c1f commit b2ac70a

File tree

3 files changed

+81
-3
lines changed

3 files changed

+81
-3
lines changed

Lib/test/test_typing.py

+73-2
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class Sub(Any): pass
122122

123123
def test_errors(self):
124124
with self.assertRaises(TypeError):
125-
issubclass(42, Any)
125+
isinstance(42, Any)
126126
with self.assertRaises(TypeError):
127127
Any[int] # Any is not a generic type.
128128

@@ -137,6 +137,9 @@ class Something: pass
137137

138138
class MockSomething(Something, Mock): pass
139139
self.assertTrue(issubclass(MockSomething, Any))
140+
self.assertTrue(issubclass(MockSomething, MockSomething))
141+
self.assertTrue(issubclass(MockSomething, Something))
142+
self.assertTrue(issubclass(MockSomething, Mock))
140143
ms = MockSomething()
141144
self.assertIsInstance(ms, MockSomething)
142145
self.assertIsInstance(ms, Something)
@@ -2010,13 +2013,81 @@ def test_basics(self):
20102013
u = Union[int, float]
20112014
self.assertNotEqual(u, Union)
20122015

2013-
def test_subclass_error(self):
2016+
def test_union_isinstance(self):
2017+
self.assertTrue(isinstance(42, Union[int, str]))
2018+
self.assertTrue(isinstance('abc', Union[int, str]))
2019+
self.assertFalse(isinstance(3.14, Union[int, str]))
2020+
self.assertTrue(isinstance(42, Union[int, list[int]]))
2021+
self.assertTrue(isinstance(42, Union[int, Any]))
2022+
2023+
def test_union_isinstance_type_error(self):
2024+
with self.assertRaises(TypeError):
2025+
isinstance(42, Union[str, list[int]])
2026+
with self.assertRaises(TypeError):
2027+
isinstance(42, Union[list[int], int])
2028+
with self.assertRaises(TypeError):
2029+
isinstance(42, Union[list[int], str])
2030+
with self.assertRaises(TypeError):
2031+
isinstance(42, Union[str, Any])
2032+
with self.assertRaises(TypeError):
2033+
isinstance(42, Union[Any, int])
2034+
with self.assertRaises(TypeError):
2035+
isinstance(42, Union[Any, str])
2036+
2037+
def test_optional_isinstance(self):
2038+
self.assertTrue(isinstance(42, Optional[int]))
2039+
self.assertTrue(isinstance(None, Optional[int]))
2040+
self.assertFalse(isinstance('abc', Optional[int]))
2041+
2042+
def test_optional_isinstance_type_error(self):
2043+
with self.assertRaises(TypeError):
2044+
isinstance(42, Optional[list[int]])
2045+
with self.assertRaises(TypeError):
2046+
isinstance(None, Optional[list[int]])
2047+
with self.assertRaises(TypeError):
2048+
isinstance(42, Optional[Any])
2049+
with self.assertRaises(TypeError):
2050+
isinstance(None, Optional[Any])
2051+
2052+
def test_union_issubclass(self):
2053+
self.assertTrue(issubclass(int, Union[int, str]))
2054+
self.assertTrue(issubclass(str, Union[int, str]))
2055+
self.assertFalse(issubclass(float, Union[int, str]))
2056+
self.assertTrue(issubclass(int, Union[int, list[int]]))
2057+
self.assertTrue(issubclass(int, Union[int, Any]))
2058+
self.assertFalse(issubclass(int, Union[str, Any]))
2059+
self.assertTrue(issubclass(int, Union[Any, int]))
2060+
self.assertFalse(issubclass(int, Union[Any, str]))
2061+
2062+
def test_union_issubclass_type_error(self):
20142063
with self.assertRaises(TypeError):
20152064
issubclass(int, Union)
20162065
with self.assertRaises(TypeError):
20172066
issubclass(Union, int)
20182067
with self.assertRaises(TypeError):
20192068
issubclass(Union[int, str], int)
2069+
with self.assertRaises(TypeError):
2070+
issubclass(int, Union[str, list[int]])
2071+
with self.assertRaises(TypeError):
2072+
issubclass(int, Union[list[int], int])
2073+
with self.assertRaises(TypeError):
2074+
issubclass(int, Union[list[int], str])
2075+
2076+
def test_optional_issubclass(self):
2077+
self.assertTrue(issubclass(int, Optional[int]))
2078+
self.assertTrue(issubclass(type(None), Optional[int]))
2079+
self.assertFalse(issubclass(str, Optional[int]))
2080+
self.assertTrue(issubclass(Any, Optional[Any]))
2081+
self.assertTrue(issubclass(type(None), Optional[Any]))
2082+
self.assertFalse(issubclass(int, Optional[Any]))
2083+
2084+
def test_optional_issubclass_type_error(self):
2085+
with self.assertRaises(TypeError):
2086+
issubclass(list[int], Optional[list[int]])
2087+
with self.assertRaises(TypeError):
2088+
issubclass(type(None), Optional[list[int]])
2089+
with self.assertRaises(TypeError):
2090+
issubclass(int, Optional[list[int]])
20202091

20212092
def test_union_any(self):
20222093
u = Union[Any]

Lib/typing.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1733,12 +1733,16 @@ def __repr__(self):
17331733
return super().__repr__()
17341734

17351735
def __instancecheck__(self, obj):
1736-
return self.__subclasscheck__(type(obj))
1736+
for arg in self.__args__:
1737+
if isinstance(obj, arg):
1738+
return True
1739+
return False
17371740

17381741
def __subclasscheck__(self, cls):
17391742
for arg in self.__args__:
17401743
if issubclass(cls, arg):
17411744
return True
1745+
return False
17421746

17431747
def __reduce__(self):
17441748
func, (origin, args) = super().__reduce__()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Unify the instance check for :class:`typing.Union` and
2+
:class:`types.UnionType`: :class:`!Union` now uses the instance checks
3+
against its parameters instead of the subclass checks.

0 commit comments

Comments
 (0)