classification
Title: inconsistency behavior in ctypes.c_char_p dereferencing
Type: behavior Stage: needs patch
Components: ctypes Versions: Python 3.2, Python 3.1, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: theller Nosy List: nullnil, theller, vladris
Priority: normal Keywords:

Created on 2010-03-17 09:09 by nullnil, last changed 2011-07-11 00:21 by vladris.

Messages (2)
msg101214 - (view) Author: Jackson Yang (nullnil) Date: 2010-03-17 09:09
# Python 3.1.2rc1 (r312rc1:78742, Mar  7 2010, 07:49:40)
# [MSC v.1500 32 bit (Intel)] on win32
import ctypes

class T(ctypes.Structure):
	_fields_ = (
		('member', ctypes.c_char * 16),
	)

# dereference a c_char_Array variable would return <bytes>
print('%r'%((ctypes.c_char * 16)()[:]))
# dereference from a c_char_Array member would return <str>, which is buggy
print('%r'%(T().member[:]))
msg140095 - (view) Author: Vlad Riscutia (vladris) Date: 2011-07-11 00:21
Looks like this was implemented by design at some point. In cfield.c, we have specific code to treat character array fields as strings:

    /*  Field descriptors for 'c_char * n' are be scpecial cased to
        return a Python string instead of an Array object instance...
    */
    if (PyCArrayTypeObject_Check(proto)) {
        StgDictObject *adict = PyType_stgdict(proto);
        StgDictObject *idict;
        if (adict && adict->proto) {
            idict = PyType_stgdict(adict->proto);
            if (!idict) {
                PyErr_SetString(PyExc_TypeError,
                                "has no _stginfo_");
                Py_DECREF(self);
                return NULL;
            }
            if (idict->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
                struct fielddesc *fd = _ctypes_get_fielddesc("s");
                getfunc = fd->getfunc;
                setfunc = fd->setfunc;
            }
#ifdef CTYPES_UNICODE
            if (idict->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
                struct fielddesc *fd = _ctypes_get_fielddesc("U");
                getfunc = fd->getfunc;
                setfunc = fd->setfunc;
            }
#endif
        }
    }

Simple fix would be to just remove this whole section though this might break code which relied on string assignment to such fields. For example:

class T(ctypes.Structure):
	_fields_ = (
		('member', ctypes.c_char * 16),
	)

x = T()
x.member = bytes('Spam', 'ascii')

Above works now but will fail if change is made. There is a high chance this would break existing code as I imagine people using this due to convenience. An alternative would be to keep string setfunc but don't change getfunc, though that is also pretty inconsistent as you will be able to set a string but not get one back.

If we are willing to take the risk, fix is easy.
History
Date User Action Args
2011-07-11 00:21:38vladrissetnosy: + vladris
messages: + msg140095
2010-08-05 03:41:50BreamoreBoysetstage: needs patch
versions: + Python 3.1, Python 2.7
2010-03-17 09:09:45nullnilcreate