[Python-checkins] bpo-44606: Fix __instancecheck__ and __subclasscheck__ for the union type. (GH-27120)
serhiy-storchaka
webhook-mailer at python.org
Wed Jul 14 00:35:48 EDT 2021
https://github.com/python/cpython/commit/81989058de381108dfd0a4255b93d4fb34417002
commit: 81989058de381108dfd0a4255b93d4fb34417002
branch: main
author: Serhiy Storchaka <storchaka at gmail.com>
committer: serhiy-storchaka <storchaka at gmail.com>
date: 2021-07-14T07:35:39+03:00
summary:
bpo-44606: Fix __instancecheck__ and __subclasscheck__ for the union type. (GH-27120)
* Fix issubclass() for None.
E.g. issubclass(type(None), int | None) returns now True.
* Fix issubclass() for virtual subclasses.
E.g. issubclass(dict, int | collections.abc.Mapping) returns now True.
* Fix crash in isinstance() if the check for one of items raises exception.
files:
A Misc/NEWS.d/next/Core and Builtins/2021-07-13-20-22-12.bpo-44606.S3Bv2w.rst
M Lib/test/test_types.py
M Objects/unionobject.c
diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py
index 7f7ce86ff08ef3..f2c64649e1b670 100644
--- a/Lib/test/test_types.py
+++ b/Lib/test/test_types.py
@@ -661,6 +661,39 @@ def test_or_types_operator(self):
x.__args__ = [str, int]
(int | str ) == x
+ def test_instancecheck(self):
+ x = int | str
+ self.assertIsInstance(1, x)
+ self.assertIsInstance(True, x)
+ self.assertIsInstance('a', x)
+ self.assertNotIsInstance(None, x)
+ self.assertTrue(issubclass(int, x))
+ self.assertTrue(issubclass(bool, x))
+ self.assertTrue(issubclass(str, x))
+ self.assertFalse(issubclass(type(None), x))
+ x = int | None
+ self.assertIsInstance(None, x)
+ self.assertTrue(issubclass(type(None), x))
+ x = int | collections.abc.Mapping
+ self.assertIsInstance({}, x)
+ self.assertTrue(issubclass(dict, x))
+
+ def test_bad_instancecheck(self):
+ class BadMeta(type):
+ def __instancecheck__(cls, inst):
+ 1/0
+ x = int | BadMeta('A', (), {})
+ self.assertTrue(isinstance(1, x))
+ self.assertRaises(ZeroDivisionError, isinstance, [], x)
+
+ def test_bad_subclasscheck(self):
+ class BadMeta(type):
+ def __subclasscheck__(cls, sub):
+ 1/0
+ x = int | BadMeta('A', (), {})
+ self.assertTrue(issubclass(int, x))
+ self.assertRaises(ZeroDivisionError, issubclass, list, x)
+
def test_or_type_operator_with_TypeVar(self):
TV = typing.TypeVar('T')
assert TV | str == typing.Union[TV, str]
@@ -754,9 +787,9 @@ def __eq__(self, other):
for type_ in union_ga:
with self.subTest(f"check isinstance/issubclass is invalid for {type_}"):
with self.assertRaises(TypeError):
- isinstance(list, type_)
+ isinstance(1, type_)
with self.assertRaises(TypeError):
- issubclass(list, type_)
+ issubclass(int, type_)
def test_or_type_operator_with_bad_module(self):
class TypeVar:
diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-07-13-20-22-12.bpo-44606.S3Bv2w.rst b/Misc/NEWS.d/next/Core and Builtins/2021-07-13-20-22-12.bpo-44606.S3Bv2w.rst
new file mode 100644
index 00000000000000..3c7ef3b21fe092
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2021-07-13-20-22-12.bpo-44606.S3Bv2w.rst
@@ -0,0 +1 @@
+Fix ``__instancecheck__`` and ``__subclasscheck__`` for the union type.
diff --git a/Objects/unionobject.c b/Objects/unionobject.c
index d2a10dfec858ea..cf04de1bcc8095 100644
--- a/Objects/unionobject.c
+++ b/Objects/unionobject.c
@@ -70,8 +70,14 @@ union_instancecheck(PyObject *self, PyObject *instance)
if (arg == Py_None) {
arg = (PyObject *)&_PyNone_Type;
}
- if (PyType_Check(arg) && PyObject_IsInstance(instance, arg) != 0) {
- Py_RETURN_TRUE;
+ if (PyType_Check(arg)) {
+ int res = PyObject_IsInstance(instance, arg);
+ if (res < 0) {
+ return NULL;
+ }
+ if (res) {
+ Py_RETURN_TRUE;
+ }
}
}
Py_RETURN_FALSE;
@@ -93,8 +99,17 @@ union_subclasscheck(PyObject *self, PyObject *instance)
Py_ssize_t nargs = PyTuple_GET_SIZE(alias->args);
for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
PyObject *arg = PyTuple_GET_ITEM(alias->args, iarg);
- if (PyType_Check(arg) && (PyType_IsSubtype((PyTypeObject *)instance, (PyTypeObject *)arg) != 0)) {
- Py_RETURN_TRUE;
+ if (arg == Py_None) {
+ arg = (PyObject *)&_PyNone_Type;
+ }
+ if (PyType_Check(arg)) {
+ int res = PyObject_IsSubclass(instance, arg);
+ if (res < 0) {
+ return NULL;
+ }
+ if (res) {
+ Py_RETURN_TRUE;
+ }
}
}
Py_RETURN_FALSE;
More information about the Python-checkins
mailing list