Skip to content

Commit d89dce9

Browse files
Skip CPython specific descriptor tests on PyPy.
PyPy does not ship the ``_pickle`` C module used to obtain a C extension backed slot wrapper, so import of the descriptor tests fails at collection time on PyPy. Beyond the missing module, the native CPython descriptor types exercised here (wrapper_descriptor, method_descriptor, classmethod_descriptor, getset_descriptor, member_descriptor) are CPython implementation details which PyPy does not expose as the same distinct types. The regression these tests guard against is also specific to the wrapt C extension which is not built on PyPy. Detect PyPy via ``platform.python_implementation()`` and skip the test classes covering native CPython descriptors and the two subclass attribute access tests which wrap a native slot wrapper. The Python level descriptor tests (property, classmethod, staticmethod, custom user defined descriptor, plain Python method) continue to run on PyPy. Guard the ``_pickle`` import behind the same flag so the module loads on PyPy in the first place.
1 parent 86f4e0e commit d89dce9

1 file changed

Lines changed: 28 additions & 2 deletions

File tree

tests/core/test_descriptor_get_class_access.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,33 @@
1818
"""
1919

2020
import io
21+
import platform
2122
import socket
2223
import types
2324
import unittest
2425

25-
import _pickle
26-
2726
import wrapt
2827

28+
# The native CPython descriptor types tested below (slot wrappers, method
29+
# descriptors, classmethod descriptors, getset descriptors, member
30+
# descriptors) are CPython implementation details. PyPy implements its
31+
# descriptor protocol differently and does not always expose the same
32+
# distinct types, and modules such as ``_pickle`` which are used here to
33+
# obtain a C-extension backed slot wrapper are also absent. The tests
34+
# guarding behaviour of these CPython specific descriptors are skipped
35+
# on PyPy. The bug they guard against was specific to the wrapt C
36+
# extension which is not built on PyPy in any case.
37+
38+
IS_PYPY = platform.python_implementation() == "PyPy"
39+
40+
skip_on_pypy = unittest.skipIf(
41+
IS_PYPY,
42+
"exercises CPython specific native descriptor types not present on PyPy",
43+
)
44+
45+
if not IS_PYPY:
46+
import _pickle # type: ignore[import-not-found]
47+
2948

3049
def _wrapper(wrapped, instance, args, kwargs):
3150
return wrapped(*args, **kwargs)
@@ -44,6 +63,7 @@ def _find_member_descriptor():
4463
return None
4564

4665

66+
@skip_on_pypy
4767
class TestSlotWrapperDescriptor(unittest.TestCase):
4868
# ``wrapper_descriptor`` is the descriptor type for C-level type slots
4969
# such as ``_pickle.Unpickler.load`` (displayed as
@@ -71,6 +91,7 @@ def test_instance_level_get(self):
7191
self.assertIsNotNone(bound)
7292

7393

94+
@skip_on_pypy
7495
class TestMethodDescriptor(unittest.TestCase):
7596
# ``method_descriptor`` is the descriptor type for built-in methods
7697
# defined in C such as ``str.upper``.
@@ -94,6 +115,7 @@ def test_instance_level_call(self):
94115
self.assertEqual(bound(), "HELLO")
95116

96117

118+
@skip_on_pypy
97119
class TestClassMethodDescriptor(unittest.TestCase):
98120
# ``classmethod_descriptor`` is the descriptor type for C-level
99121
# classmethods such as ``dict.fromkeys``. A classmethod descriptor
@@ -106,6 +128,7 @@ def test_class_level_get(self):
106128
self.assertEqual(bound(["a", "b"], 1), {"a": 1, "b": 1})
107129

108130

131+
@skip_on_pypy
109132
class TestGetSetDescriptor(unittest.TestCase):
110133
# ``getset_descriptor`` is the descriptor type generated for attributes
111134
# defined via ``PyGetSetDef`` in C, such as ``type.__name__``.
@@ -131,6 +154,7 @@ def test_instance_level_get(self):
131154
self.assertEqual(wrapped.__get__(str, type), "str")
132155

133156

157+
@skip_on_pypy
134158
class TestMemberDescriptor(unittest.TestCase):
135159
# ``member_descriptor`` is the descriptor type generated for slots
136160
# defined via ``PyMemberDef`` in C and for entries in ``__slots__``.
@@ -230,6 +254,7 @@ class TestSubclassAttributeAccess(unittest.TestCase):
230254
# existing descriptor off a parent class, wrap it, and install the
231255
# wrapper as an attribute of a subclass.
232256

257+
@skip_on_pypy
233258
def test_wrapped_slot_wrapper_via_subclass(self):
234259
class Sub(_pickle.Unpickler):
235260
pass
@@ -238,6 +263,7 @@ class Sub(_pickle.Unpickler):
238263
Sub.load = wrapped
239264
self.assertIsNotNone(Sub.load.__doc__)
240265

266+
@skip_on_pypy
241267
def test_wrapped_dict_slot_via_subclass(self):
242268
class SubDict(dict):
243269
pass

0 commit comments

Comments
 (0)