Issue 27871: ctypes docs must be more explicit about the type a func returns (original) (raw)
The ctypes documentation leaves an important detail very implicit, which can cause non-deterministic hard crashes when overlooked. The issue is that when you explicitly set the .restype of a function to c_void_p, the function returns not a c_void_p (like a newcomer might assume) but an int -- and when he/she passes that value into the next library function (which should have gotten a c_void_p), then they'll get a hard crash if that memory location lies outside the 32-bit range.
I take it that the implicit assumption in ctypes is that the data types that you get back from the library calls are native python types where applicable for convenience, and the restype (just like argtypes) only configures the marshaling layer. However, that's not very explicitly stated anywhere (except maybe between the lines).
See section 2.7 in the ctypes docs:
Fundamental data types, when returned as foreign function call
results, or, for example, by retrieving structure field members
or array items, are transparently converted to native Python
types. In other words, if a foreign function has a restype of
c_char_p, you will always receive a Python bytes object, not a
c_char_p instance.
Subclasses of fundamental data types do not inherit this
behavior. So, if a foreign functions restype is a subclass of
c_void_p, you will receive an instance of this subclass from the
function call. Of course, you can get the value of the pointer
by accessing the value attribute.
For example:
class my_char_p(ctypes.c_char_p):
pass
>>> locale = ctypes.create_string_buffer(b'en_US.UTF-8')
>>> setlocale.restype = ctypes.c_char_p
>>> result = setlocale(0, locale)
>>> result
b'en_US.UTF-8'
>>> setlocale.restype = my_char_p
>>> result = setlocale(0, locale)
>>> result
my_char_p(31391216)
>>> result.value
b'en_US.UTF-8'
when he/she passes that value into the next library function
A function that takes pointer arguments really should have argtypes defined so that there's no chance of accidentally getting the default integer conversion to a C int.