From ee3a24188474cb950733529697e6e4130a35b7de Mon Sep 17 00:00:00 2001 From: Hyunmin Shin Date: Sat, 30 Jul 2022 03:36:21 +0900 Subject: [PATCH 1/2] Fix instancecheck, subclasscheck of UnionType --- vm/src/builtins/union.rs | 30 ++++++++++++++++++++++++------ vm/src/protocol/object.rs | 2 +- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/vm/src/builtins/union.rs b/vm/src/builtins/union.rs index d43fbc10b6..461c1af37e 100644 --- a/vm/src/builtins/union.rs +++ b/vm/src/builtins/union.rs @@ -90,15 +90,33 @@ impl PyUnion { } #[pymethod(magic)] - fn instancecheck(_zelf: PyRef, _obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { - Err(vm - .new_type_error("isinstance() argument 2 cannot be a parameterized generic".to_owned())) + fn instancecheck(zelf: PyRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if zelf + .args + .iter() + .any(|x| x.class().is(vm.ctx.types.generic_alias_type)) + { + Err(vm.new_type_error( + "isinstance() argument 2 cannot be a parameterized generic".to_owned(), + )) + } else { + obj.is_instance(zelf.args().as_object(), vm) + } } #[pymethod(magic)] - fn subclasscheck(_zelf: PyRef, _obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { - Err(vm - .new_type_error("issubclass() argument 2 cannot be a parameterized generic".to_owned())) + fn subclasscheck(zelf: PyRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if zelf + .args + .iter() + .any(|x| x.class().is(vm.ctx.types.generic_alias_type)) + { + Err(vm.new_type_error( + "issubclass() argument 2 cannot be a parameterized generic".to_owned(), + )) + } else { + obj.is_subclass(zelf.args().as_object(), vm) + } } #[pymethod(name = "__ror__")] diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index 79e252c4e2..ca7aa1455e 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -398,7 +398,7 @@ impl PyObject { }) .and(self.check_cls(cls, vm, || { format!( - "issubclass() arg 2 must be a class or tuple of classes, not {}", + "issubclass() arg 2 must be a class, a tuple of classes, or a union, not {}", cls.class() ) })) From ed001b4300b4af6711f563cc2d5f684c6fcc746b Mon Sep 17 00:00:00 2001 From: Hyunmin Shin Date: Sun, 31 Jul 2022 00:06:32 +0900 Subject: [PATCH 2/2] Update test_types.py from CPython 3.10 --- Lib/test/test_types.py | 78 ++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index a611142aed..8487a0b1d9 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -709,27 +709,63 @@ def test_hash(self): self.assertEqual(hash(int | str), hash(str | int)) self.assertEqual(hash(int | str), hash(typing.Union[int, str])) - # TODO: RUSTPYTHON - @unittest.expectedFailure - 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_instancecheck_and_subclasscheck(self): + for x in (int | str, typing.Union[int, str]): + with self.subTest(x=x): + 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)) + + for x in (int | None, typing.Union[int, None]): + with self.subTest(x=x): + self.assertIsInstance(None, x) + self.assertTrue(issubclass(type(None), x)) + + for x in ( + int | collections.abc.Mapping, + typing.Union[int, collections.abc.Mapping], + ): + with self.subTest(x=x): + self.assertIsInstance({}, x) + self.assertNotIsInstance((), x) + self.assertTrue(issubclass(dict, x)) + self.assertFalse(issubclass(list, x)) + + def test_instancecheck_and_subclasscheck_order(self): + T = typing.TypeVar('T') + + will_resolve = ( + int | T, + typing.Union[int, T], + ) + for x in will_resolve: + with self.subTest(x=x): + self.assertIsInstance(1, x) + self.assertTrue(issubclass(int, x)) + + wont_resolve = ( + T | int, + typing.Union[T, int], + ) + for x in wont_resolve: + with self.subTest(x=x): + with self.assertRaises(TypeError): + issubclass(int, x) + with self.assertRaises(TypeError): + isinstance(1, x) + + for x in (*will_resolve, *wont_resolve): + with self.subTest(x=x): + with self.assertRaises(TypeError): + issubclass(object, x) + with self.assertRaises(TypeError): + isinstance(object(), x) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_bad_instancecheck(self): class BadMeta(type): def __instancecheck__(cls, inst): @@ -738,8 +774,6 @@ def __instancecheck__(cls, inst): self.assertTrue(isinstance(1, x)) self.assertRaises(ZeroDivisionError, isinstance, [], x) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_bad_subclasscheck(self): class BadMeta(type): def __subclasscheck__(cls, sub):