classification
Title: Automatic upcast does not occur for custom classes
Type: behavior Stage:
Components: ctypes Versions: Python 3.6, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: amaury.forgeotdarc, belopolsky, meador.inge, rharke
Priority: normal Keywords:

Created on 2017-06-17 19:29 by rharke, last changed 2017-06-17 19:39 by ned.deily.

Messages (1)
msg296245 - (view) Author: Ranger Harke (rharke) Date: 2017-06-17 19:29
I have noticed that the automatic upcast mechanism which works for
standard ctypes types does not appear to work for custom types with
_as_parameter_ defined.

Consider the following example (written for Mac OS X but just change
the LoadLibrary call to test on a different OS):

# -- begin --
from ctypes import *

libc = cdll.LoadLibrary('/usr/lib/libc.dylib')

class Base(Structure): pass
class Derived(Base): pass
class Wrapper(object): pass

printf = libc.printf
printf.argtypes = [ c_char_p, POINTER(Base) ]
printf.restype = c_int

derived = Derived()
derived_ptr = pointer(derived)
derived_ptr_wrapper = Wrapper()
derived_ptr_wrapper._as_parameter_ = pointer(derived)

# Case 1 WORKS: Derived* is automatically upcast to Base* and passed to
#               printf
printf(b'%p\n', derived_ptr)

# Case 2 WORKS: The Wrapper can be explicitly upcast to a Base* and
#               passed to printf
printf(b'%p\n', cast(derived_ptr_wrapper, POINTER(Base)))

# Case 3 FAILS: Automatic upcast does NOT happen with the value in the
#               Wrapper: 'expected LP_Base instance instead of
#               LP_Derived'
printf(b'%p\n', derived_ptr_wrapper)
# -- end --

Here we have two types, Base which is a ctypes Structure, and Derived
which derives from Base. A pointer to Derived can be passed to a
function that expects a pointer to Base, and it will automatically be
upcast.

However, once the Wrapper type is introduced, which simply holds a
pointer to Derived in its _as_parameter_ attribute, the automatic
upcast no longer occurs. A manual upcast works, though.

I believe this may be related to #27803, but that is talking about
byref() whereas this is related to cast(). Perhaps the internal
mechanism is the same; certainly it sounds like a similar problem.

I've tested this in Python 2.7 and 3.6, and the result is the same.

Not sure if this is a bug or an enhancement request- filing it as
"behavior" for now since to me, this is something that I expected to
implicitly work.

Thanks!  - Ranger Harke.
History
Date User Action Args
2017-06-17 19:39:41ned.deilysetnosy: + amaury.forgeotdarc, belopolsky, meador.inge
2017-06-17 19:29:40rharkecreate