From 011c978cb96a1f2c5710411ad81861d59d808af0 Mon Sep 17 00:00:00 2001 From: Jun Komoda <45822440+junkmd@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:05:24 +0000 Subject: [PATCH 1/4] Add `test_creating_pointer_in_dunder_init_...`. --- .../test_ctypes/test_c_simple_type_meta.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/Lib/test/test_ctypes/test_c_simple_type_meta.py b/Lib/test/test_ctypes/test_c_simple_type_meta.py index fa5144a3ca01bb..196423e0d35172 100644 --- a/Lib/test/test_ctypes/test_c_simple_type_meta.py +++ b/Lib/test/test_ctypes/test_c_simple_type_meta.py @@ -85,3 +85,54 @@ class Sub(CtBase): self.assertIsInstance(POINTER(Sub), p_meta) self.assertTrue(issubclass(POINTER(Sub), Sub)) + + def test_creating_pointer_in_dunder_init_1(self): + class ct_meta(type): + def __init__(self, name, bases, namespace): + if bases == (object,): + ptr_bases = (self, PtrBase) + else: + ptr_bases = (self, POINTER(bases[0])) + p = p_meta(f"POINTER({self.__name__})", ptr_bases, {}) + ctypes._pointer_type_cache[self] = p + + class p_meta(PyCSimpleType, ct_meta): + pass + + class PtrBase(c_void_p, metaclass=p_meta): + pass + + class CtBase(object, metaclass=ct_meta): + pass + + class Sub(CtBase): + pass + + class Sub2(Sub): + pass + + self.assertIsInstance(POINTER(Sub2), p_meta) + self.assertTrue(issubclass(POINTER(Sub2), Sub2)) + self.assertTrue(issubclass(POINTER(Sub2), POINTER(Sub))) + self.assertTrue(issubclass(POINTER(Sub), POINTER(CtBase))) + + def test_creating_pointer_in_dunder_init_2(self): + class ct_meta(type): + def __init__(self, name, bases, namespace): + p = p_meta(f"POINTER({self.__name__})", (self, c_void_p), {}) + ctypes._pointer_type_cache[self] = p + + class p_meta(PyCSimpleType, ct_meta): + pass + + class Core(object): + pass + + class CtBase(Core, metaclass=ct_meta): + pass + + class Sub(CtBase): + pass + + self.assertIsInstance(POINTER(Sub), p_meta) + self.assertTrue(issubclass(POINTER(Sub), Sub)) From 7a67476cb4b0199e2dd8e0f23c7a090a6ae71539 Mon Sep 17 00:00:00 2001 From: Jun Komoda <45822440+junkmd@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:07:14 +0000 Subject: [PATCH 2/4] Add comments. --- Lib/test/test_ctypes/test_c_simple_type_meta.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/test/test_ctypes/test_c_simple_type_meta.py b/Lib/test/test_ctypes/test_c_simple_type_meta.py index 196423e0d35172..843a7f5af5a7c5 100644 --- a/Lib/test/test_ctypes/test_c_simple_type_meta.py +++ b/Lib/test/test_ctypes/test_c_simple_type_meta.py @@ -87,6 +87,9 @@ class Sub(CtBase): self.assertTrue(issubclass(POINTER(Sub), Sub)) def test_creating_pointer_in_dunder_init_1(self): + # Test for modern metaclass initialization that does not + # require recursion avoidance. + class ct_meta(type): def __init__(self, name, bases, namespace): if bases == (object,): @@ -117,6 +120,8 @@ class Sub2(Sub): self.assertTrue(issubclass(POINTER(Sub), POINTER(CtBase))) def test_creating_pointer_in_dunder_init_2(self): + # A simpler variant of the above. + class ct_meta(type): def __init__(self, name, bases, namespace): p = p_meta(f"POINTER({self.__name__})", (self, c_void_p), {}) From 59968701ad75526bc69c502cb5ef40079f723f53 Mon Sep 17 00:00:00 2001 From: Jun Komoda <45822440+junkmd@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:11:11 +0000 Subject: [PATCH 3/4] Add `super().__init__` calls and early returns to limit recursion. --- .../test_ctypes/test_c_simple_type_meta.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_ctypes/test_c_simple_type_meta.py b/Lib/test/test_ctypes/test_c_simple_type_meta.py index 843a7f5af5a7c5..6430cc29f6373f 100644 --- a/Lib/test/test_ctypes/test_c_simple_type_meta.py +++ b/Lib/test/test_ctypes/test_c_simple_type_meta.py @@ -87,11 +87,16 @@ class Sub(CtBase): self.assertTrue(issubclass(POINTER(Sub), Sub)) def test_creating_pointer_in_dunder_init_1(self): - # Test for modern metaclass initialization that does not - # require recursion avoidance. - class ct_meta(type): def __init__(self, name, bases, namespace): + super().__init__(name, bases, namespace) + + # Avoid recursion. + # (See test_creating_pointer_in_dunder_new_1) + if bases == (c_void_p,): + return + if issubclass(self, PtrBase): + return if bases == (object,): ptr_bases = (self, PtrBase) else: @@ -120,10 +125,14 @@ class Sub2(Sub): self.assertTrue(issubclass(POINTER(Sub), POINTER(CtBase))) def test_creating_pointer_in_dunder_init_2(self): - # A simpler variant of the above. - class ct_meta(type): def __init__(self, name, bases, namespace): + super().__init__(name, bases, namespace) + + # Avoid recursion. + # (See test_creating_pointer_in_dunder_new_2) + if isinstance(self, p_meta): + return self p = p_meta(f"POINTER({self.__name__})", (self, c_void_p), {}) ctypes._pointer_type_cache[self] = p From e1831ac3ba14a8fcf02cb3f10c218fdd08ce0d22 Mon Sep 17 00:00:00 2001 From: Jun Komoda <45822440+junkmd@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:05:09 +0000 Subject: [PATCH 4/4] Fix an early return in `__init__`. --- Lib/test/test_ctypes/test_c_simple_type_meta.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_ctypes/test_c_simple_type_meta.py b/Lib/test/test_ctypes/test_c_simple_type_meta.py index 6430cc29f6373f..eb77d6d7782478 100644 --- a/Lib/test/test_ctypes/test_c_simple_type_meta.py +++ b/Lib/test/test_ctypes/test_c_simple_type_meta.py @@ -132,7 +132,7 @@ def __init__(self, name, bases, namespace): # Avoid recursion. # (See test_creating_pointer_in_dunder_new_2) if isinstance(self, p_meta): - return self + return p = p_meta(f"POINTER({self.__name__})", (self, c_void_p), {}) ctypes._pointer_type_cache[self] = p