@@ -99,7 +99,9 @@ class ffi_type(ctypes.Structure):
99
99
100
100
# The GETFUNC and SETFUNC typedefs from "Modules/_ctypes/ctypes.h".
101
101
GETFUNC = ctypes .PYFUNCTYPE (ctypes .py_object , ctypes .c_void_p , ctypes .c_ssize_t )
102
- SETFUNC = ctypes .PYFUNCTYPE (ctypes .py_object , ctypes .c_void_p , ctypes .py_object , ctypes .c_ssize_t )
102
+ # The return type of SETFUNC is declared here as a c_void_p instead of py_object to work around ctypes bug
103
+ # bpo-36880 (https://bugs.python.org/issue36880). See the comment in make_callback_returnable's setfunc for details.
104
+ SETFUNC = ctypes .PYFUNCTYPE (ctypes .c_void_p , ctypes .c_void_p , ctypes .py_object , ctypes .c_ssize_t )
103
105
104
106
105
107
# The StgDictObject structure from "Modules/_ctypes/ctypes.h".
@@ -119,6 +121,10 @@ class StgDictObject(ctypes.Structure):
119
121
]
120
122
121
123
124
+ ctypes .pythonapi .Py_IncRef .restype = None
125
+ ctypes .pythonapi .Py_IncRef .argtypes = [ctypes .POINTER (PyObject )]
126
+
127
+
122
128
def unwrap_mappingproxy (proxy ):
123
129
"""Return the mapping contained in a mapping proxy object."""
124
130
@@ -208,7 +214,18 @@ def setfunc(ptr, value, size):
208
214
)
209
215
210
216
ctypes .memmove (ptr , ctypes .addressof (value ), actual_size )
211
- return None
217
+ # Because of ctypes bug bpo-36880 (https://bugs.python.org/issue36880), returning None from a callback with
218
+ # restype py_object causes a reference counting error that can crash Python.
219
+ # To work around this bug, the restype of SETFUNC is declared as c_void_p instead.
8000
220
+ # This way ctypes performs no automatic reference counting for the returned object, which avoids the bug.
221
+ # However, this way we have to manually convert the Python object to a pointer and adjust its reference count.
222
+ none_ptr = ctypes .cast (id (None ), ctypes .POINTER (PyObject ))
223
+ # The return value of a SETFUNC is expected to have an extra reference
224
+ # (which will be owned by the caller of the SETFUNC).
225
+ ctypes .pythonapi .Py_IncRef (none_ptr )
226
+ # The returned pointer must be returned as a plain int, not as a c_void_p,
227
+ # otherwise ctypes won't recognize it and will raise a TypeError.
228
+ return ctypes .cast (none_ptr , ctypes .c_void_p ).value
212
229
213
230
# Store the getfunc and setfunc as attributes on the ctype, so they don't get garbage-collected.
214
231
ctype ._rubicon_objc_ctypes_patch_getfunc = getfunc
0 commit comments