Skip to content

Commit 08ae06f

Browse files
[3.13] gh-88834: Unify the instance check for typing.Union and types.UnionType (GH-128363) (GH-128370)
Union now uses the instance checks against its parameters instead of the subclass checks. (cherry picked from commit b2ac70a) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 62729d7 commit 08ae06f

File tree

3 files changed

+81
-3
lines changed

3 files changed

+81
-3
lines changed

Lib/test/test_typing.py

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class Sub(Any): pass
120120

121121
def test_errors(self):
122122
with self.assertRaises(TypeError):
123-
issubclass(42, Any)
123+
isinstance(42, Any)
124124
with self.assertRaises(TypeError):
125125
Any[int] # Any is not a generic type.
126126

@@ -135,6 +135,9 @@ class Something: pass
135135

136136
class MockSomething(Something, Mock): pass
137137
self.assertTrue(issubclass(MockSomething, Any))
138+
self.assertTrue(issubclass(MockSomething, MockSomething))
139+
self.assertTrue(issubclass(MockSomething, Something))
140+
self.assertTrue(issubclass(MockSomething, Mock))
138141
ms = MockSomething()
139142
self.assertIsInstance(ms, MockSomething)
140143
self.assertIsInstance(ms, Something)
@@ -2008,13 +2011,81 @@ def test_basics(self):
20082011
u = Union[int, float]
20092012
self.assertNotEqual(u, Union)
20102013

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

20192090
def test_union_any(self):
20202091
u = Union[Any]

Lib/typing.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1783,12 +1783,16 @@ def __repr__(self):
17831783
return super().__repr__()
17841784

17851785
def __instancecheck__(self, obj):
1786-
return self.__subclasscheck__(type(obj))
1786+
for arg in self.__args__:
1787+
if isinstance(obj, arg):
1788+
return True
1789+
return False
17871790

17881791
def __subclasscheck__(self, cls):
17891792
for arg in self.__args__:
17901793
if issubclass(cls, arg):
17911794
return True
1795+
return False
17921796

17931797
def __reduce__(self):
17941798
func, (origin, args) = super().__reduce__()
Lines changed: 3 additions & 0 deletions
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)