mirror of https://github.com/python/cpython
gh-99266: ctypes: Preserve more detailed exception in `ArgumentError`
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
This commit is contained in:
parent
13566a37c2
commit
b4e11a7985
|
@ -466,6 +466,14 @@ integer, string, bytes, a :mod:`ctypes` instance, or an object with an
|
|||
Return types
|
||||
^^^^^^^^^^^^
|
||||
|
||||
.. testsetup::
|
||||
|
||||
from ctypes import CDLL, c_char, c_char_p
|
||||
from ctypes.util import find_library
|
||||
libc = CDLL(find_library('c'))
|
||||
strchr = libc.strchr
|
||||
|
||||
|
||||
By default functions are assumed to return the C :c:expr:`int` type. Other
|
||||
return types can be specified by setting the :attr:`restype` attribute of the
|
||||
function object.
|
||||
|
@ -502,18 +510,19 @@ If you want to avoid the ``ord("x")`` calls above, you can set the
|
|||
:attr:`argtypes` attribute, and the second argument will be converted from a
|
||||
single character Python bytes object into a C char::
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> strchr.restype = c_char_p
|
||||
>>> strchr.argtypes = [c_char_p, c_char]
|
||||
>>> strchr(b"abcdef", b"d")
|
||||
'def'
|
||||
b'def'
|
||||
>>> strchr(b"abcdef", b"def")
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
ArgumentError: argument 2: TypeError: one character string expected
|
||||
ctypes.ArgumentError: argument 2: TypeError: one character bytes, bytearray or integer expected
|
||||
>>> print(strchr(b"abcdef", b"x"))
|
||||
None
|
||||
>>> strchr(b"abcdef", b"d")
|
||||
'def'
|
||||
b'def'
|
||||
>>>
|
||||
|
||||
You can also use a callable Python object (a function or a class for example) as
|
||||
|
|
|
@ -54,6 +54,23 @@ class FunctionTestCase(unittest.TestCase):
|
|||
class X(object, Structure):
|
||||
_fields_ = []
|
||||
|
||||
def test_c_char_parm(self):
|
||||
proto = CFUNCTYPE(c_int, c_char)
|
||||
def callback(*args):
|
||||
return 0
|
||||
|
||||
callback = proto(callback)
|
||||
|
||||
self.assertEqual(callback(b"a"), 0)
|
||||
|
||||
with self.assertRaises(ArgumentError) as cm:
|
||||
callback(b"abc")
|
||||
|
||||
self.assertEqual(str(cm.exception),
|
||||
"argument 1: TypeError: one character bytes, "
|
||||
"bytearray or integer expected")
|
||||
|
||||
|
||||
@need_symbol('c_wchar')
|
||||
def test_wchar_parm(self):
|
||||
f = dll._testfunc_i_bhilfd
|
||||
|
@ -62,6 +79,18 @@ class FunctionTestCase(unittest.TestCase):
|
|||
self.assertEqual(result, 139)
|
||||
self.assertEqual(type(result), int)
|
||||
|
||||
with self.assertRaises(ArgumentError) as cm:
|
||||
f(1, 2, 3, 4, 5.0, 6.0)
|
||||
self.assertEqual(str(cm.exception),
|
||||
"argument 2: TypeError: unicode string expected "
|
||||
"instead of int instance")
|
||||
|
||||
with self.assertRaises(ArgumentError) as cm:
|
||||
f(1, "abc", 3, 4, 5.0, 6.0)
|
||||
self.assertEqual(str(cm.exception),
|
||||
"argument 2: TypeError: one character unicode string "
|
||||
"expected")
|
||||
|
||||
@need_symbol('c_wchar')
|
||||
def test_wchar_result(self):
|
||||
f = dll._testfunc_i_bhilfd
|
||||
|
|
|
@ -78,6 +78,29 @@ class SimpleTypesTestCase(unittest.TestCase):
|
|||
pa = c_wchar_p.from_param(c_wchar_p("123"))
|
||||
self.assertEqual(type(pa), c_wchar_p)
|
||||
|
||||
def test_c_char(self):
|
||||
from ctypes import c_char
|
||||
|
||||
with self.assertRaises(TypeError) as cm:
|
||||
c_char.from_param(b"abc")
|
||||
self.assertEqual(str(cm.exception),
|
||||
"one character bytes, bytearray or integer expected")
|
||||
|
||||
@need_symbol('c_wchar')
|
||||
def test_c_wchar(self):
|
||||
from ctypes import c_wchar
|
||||
|
||||
with self.assertRaises(TypeError) as cm:
|
||||
c_wchar.from_param("abc")
|
||||
self.assertEqual(str(cm.exception),
|
||||
"one character unicode string expected")
|
||||
|
||||
|
||||
with self.assertRaises(TypeError) as cm:
|
||||
c_wchar.from_param(123)
|
||||
self.assertEqual(str(cm.exception),
|
||||
"unicode string expected instead of int instance")
|
||||
|
||||
def test_int_pointers(self):
|
||||
from ctypes import c_short, c_uint, c_int, c_long, POINTER, pointer
|
||||
LPINT = POINTER(c_int)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Preserve more detailed error messages in :mod:`ctypes`.
|
|
@ -2197,6 +2197,7 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value)
|
|||
struct fielddesc *fd;
|
||||
PyObject *as_parameter;
|
||||
int res;
|
||||
PyObject *exc, *val, *tb;
|
||||
|
||||
/* If the value is already an instance of the requested type,
|
||||
we can use it as is */
|
||||
|
@ -2230,24 +2231,37 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value)
|
|||
parg->obj = fd->setfunc(&parg->value, value, 0);
|
||||
if (parg->obj)
|
||||
return (PyObject *)parg;
|
||||
PyErr_Clear();
|
||||
PyErr_Fetch(&exc, &val, &tb);
|
||||
Py_DECREF(parg);
|
||||
|
||||
if (_PyObject_LookupAttr(value, &_Py_ID(_as_parameter_), &as_parameter) < 0) {
|
||||
Py_XDECREF(exc);
|
||||
Py_XDECREF(val);
|
||||
Py_XDECREF(tb);
|
||||
return NULL;
|
||||
}
|
||||
if (as_parameter) {
|
||||
if (_Py_EnterRecursiveCall("while processing _as_parameter_")) {
|
||||
Py_DECREF(as_parameter);
|
||||
Py_XDECREF(exc);
|
||||
Py_XDECREF(val);
|
||||
Py_XDECREF(tb);
|
||||
return NULL;
|
||||
}
|
||||
value = PyCSimpleType_from_param(type, as_parameter);
|
||||
_Py_LeaveRecursiveCall();
|
||||
Py_DECREF(as_parameter);
|
||||
Py_XDECREF(exc);
|
||||
Py_XDECREF(val);
|
||||
Py_XDECREF(tb);
|
||||
return value;
|
||||
}
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"wrong type");
|
||||
if (exc) {
|
||||
PyErr_Restore(exc, val, tb);
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(PyExc_TypeError, "wrong type");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue