Skip to content

Commit ca017a1

Browse files
[3.12] gh-88834: Unify the instance check for typing.Union and types.UnionType (GH-128363) (GH-128371)
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 fcc9bc4 commit ca017a1

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
@@ -121,7 +121,7 @@ class Sub(Any): pass
121121

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

@@ -136,6 +136,9 @@ class Something: pass
136136

137137
class MockSomething(Something, Mock): pass
138138
self.assertTrue(issubclass(MockSomething, Any))
139+
self.assertTrue(issubclass(MockSomething, MockSomething))
140+
self.assertTrue(issubclass(MockSomething, Something))
141+
self.assertTrue(issubclass(MockSomething, Mock))
139142
ms = MockSomething()
140143
self.assertIsInstance(ms, MockSomething)
141144
self.assertIsInstance(ms, Something)
@@ -1810,13 +1813,81 @@ def test_basics(self):
18101813
u = Union[int, float]
18111814
self.assertNotEqual(u, Union)
18121815

1813-
def test_subclass_error(self):
1816+
def test_union_isinstance(self):
1817+
self.assertTrue(isinstance(42, Union[int, str]))
1818+
self.assertTrue(isinstance('abc', Union[int, str]))
1819+
self.assertFalse(isinstance(3.14, Union[int, str]))
1820+
self.assertTrue(isinstance(42, Union[int, list[int]]))
1821+
self.assertTrue(isinstance(42, Union[int, Any]))
1822+
1823+
def test_union_isinstance_type_error(self):
1824+
with self.assertRaises(TypeError):
1825+
isinstance(42, Union[str, list[int]])
1826+
with self.assertRaises(TypeError):
1827+
isinstance(42, Union[list[int], int])
1828+
with self.assertRaises(TypeError):
1829+
isinstance(42, Union[list[int], str])
1830+
with self.assertRaises(TypeError):
1831+
isinstance(42, Union[str, Any])
1832+
with self.assertRaises(TypeError):
1833+
isinstance(42, Union[Any, int])
1834+
with self.assertRaises(TypeError):
1835+
isinstance(42, Union[Any, str])
1836+
1837+
def test_optional_isinstance(self):
1838+
self.assertTrue(isinstance(42, Optional[int]))
1839+
self.assertTrue(isinstance(None, Optional[int]))
1840+
self.assertFalse(isinstance('abc', Optional[int]))
1841+
1842+
def test_optional_isinstance_type_error(self):
1843+
with self.assertRaises(TypeError):
1844+
isinstance(42, Optional[list[int]])
1845+
with self.assertRaises(TypeError):
1846+
isinstance(None, Optional[list[int]])
1847+
with self.assertRaises(TypeError):
1848+
isinstance(42, Optional[Any])
1849+
with self.assertRaises(TypeError):
1850+
isinstance(None, Optional[Any])
1851+
1852+
def test_union_issubclass(self):
1853+
self.assertTrue(issubclass(int, Union[int, str]))
1854+
self.assertTrue(issubclass(str, Union[int, str]))
1855+
self.assertFalse(issubclass(float, Union[int, str]))
1856+
self.assertTrue(issubclass(int, Union[int, list[int]]))
1857+
self.assertTrue(issubclass(int, Union[int, Any]))
1858+
self.assertFalse(issubclass(int, Union[str, Any]))
1859+
self.assertTrue(issubclass(int, Union[Any, int]))
1860+
self.assertFalse(issubclass(int, Union[Any, str]))
1861+
1862+
def test_union_issubclass_type_error(self):
18141863
with self.assertRaises(TypeError):
18151864
issubclass(int, Union)
18161865
with self.assertRaises(TypeError):
18171866
issubclass(Union, int)
18181867
with self.assertRaises(TypeError):
18191868
issubclass(Union[int, str], int)
1869+
with self.assertRaises(TypeError):
1870+
issubclass(int, Union[str, list[int]])
1871+
with self.assertRaises(TypeError):
1872+
issubclass(int, Union[list[int], int])
1873+
with self.assertRaises(TypeError):
1874+
issubclass(int, Union[list[int], str])
1875+
1876+
def test_optional_issubclass(self):
1877+
self.assertTrue(issubclass(int, Optional[int]))
1878+
self.assertTrue(issubclass(type(None), Optional[int]))
1879+
self.assertFalse(issubclass(str, Optional[int]))
1880+
self.assertTrue(issubclass(Any, Optional[Any]))
1881+
self.assertTrue(issubclass(type(None), Optional[Any]))
1882+
self.assertFalse(issubclass(int, Optional[Any]))
1883+
1884+
def test_optional_issubclass_type_error(self):
1885+
with self.assertRaises(TypeError):
1886+
issubclass(list[int], Optional[list[int]])
1887+
with self.assertRaises(TypeError):
1888+
issubclass(type(None), Optional[list[int]])
1889+
with self.assertRaises(TypeError):
1890+
issubclass(int, Optional[list[int]])
18201891

18211892
def test_union_any(self):
18221893
u = Union[Any]

Lib/typing.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1610,12 +1610,16 @@ def __repr__(self):
16101610
return super().__repr__()
16111611

16121612
def __instancecheck__(self, obj):
1613-
return self.__subclasscheck__(type(obj))
1613+
for arg in self.__args__:
1614+
if isinstance(obj, arg):
1615+
return True
1616+
return False
16141617

16151618
def __subclasscheck__(self, cls):
16161619
for arg in self.__args__:
16171620
if issubclass(cls, arg):
16181621
return True
1622+
return False
16191623

16201624
def __reduce__(self):
16211625
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)