10000 gh-99266: ctypes: Preserve more detailed exception in `ArgumentError` · python/cpython@b4e11a7 · GitHub
[go: up one dir, main page]

Skip to content

Commit b4e11a7

Browse files
gh-99266: ctypes: Preserve more detailed exception in ArgumentError
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
1 parent 13566a3 commit b4e11a7

File tree

5 files changed

+83
-7
lines changed

5 files changed

+83
-7
lines changed

Doc/library/ctypes.rst

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,14 @@ integer, string, bytes, a :mod:`ctypes` instance, or an object with an
466466
Return types
467467
^^^^^^^^^^^^
468468

469+
.. testsetup::
470+
471+
from ctypes import CDLL, c_char, c_char_p
472+
from ctypes.util import find_library
473+
libc = CDLL(find_library('c'))
474+
strchr = libc.strchr
475+
476+
469477
By default functions are assumed to return the C :c:expr:`int` type. Other
470478
return types can be specified by setting the :attr:`restype` attribute of the
471479
function object.
@@ -502,18 +510,19 @@ If you want to avoid the ``ord("x")`` calls above, you can set the
502510
:attr:`argtypes` attribute, and the second argument will be converted from a
503511
single character Python bytes object into a C char::
504512

513+
.. doctest::
514+
505515
>>> strchr.restype = c_char_p
506516
>>> strchr.argtypes = [c_char_p, c_char]
507517
>>> strchr(b"abcdef", b"d")
508-
'def'
518+
b'def'
509519
>>> strchr(b"abcdef", b"def")
510520
Traceback (most recent call last):
511-
File "<stdin>", line 1, in <module>
512-
ArgumentError: argument 2: TypeError: one character string expected
521+
ctypes.ArgumentError: argument 2: TypeError: one character bytes, bytearray or integer expected
513522
>>> print(strchr(b"abcdef", b"x"))
514523
None
515524
>>> strchr(b"abcdef", b"d")
516-
'def'
525+
b'def'
517526
>>>
518527

519528
You can also use a callable Python object (a function or a class for example) as

Lib/test/test_ctypes/test_functions.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,23 @@ class X(object, _SimpleCData):
5454
class X(object, Structure):
5555
_fields_ = []
5656

57+
def test_c_char_parm(self):
58+
proto = CFUNCTYPE(c_int, c_char)
59+
def callback(*args):
60+
return 0
61+
62+
callback = proto(callback)
63+
64+
self.assertEqual(callback(b"a"), 0)
65+
66+
with self.assertRaises(ArgumentError) as cm:
67+
callback(b"abc")
68+
69+
self.assertEqual(str(cm.exception),
70+
"argument 1: TypeError: one character bytes, "
71+
"bytearray or integer expected")
72+
73+
5774
@need_symbol('c_wchar')
5875
def test_wchar_parm(self):
5976
f = dll._testfunc_i_bhilfd
@@ -62,6 +79,18 @@ def test_wchar_parm(self):
6279
self.assertEqual(result, 139)
6380
self.assertEqual(type(result), int)
6481

82+
with self.assertRaises(ArgumentError) as cm:
83+
f(1, 2, 3, 4, 5.0, 6.0)
84+
self.assertEqual(str(cm.exception),
85+
"argument 2: TypeError: unicode string expected "
86+
"instead of int instance")
87+
88+
with self.assertRaises(ArgumentError) as cm:
89+
f(1, "abc", 3, 4, 5.0, 6.0)
90+
self.assertEqual(str(cm.exception),
91+
"argument 2: TypeError: one character unicode string "
92+
"expected")
93+
6594
@need_symbol('c_wchar')
6695
def test_wchar_result(self):
6796
f = dll._testfunc_i_bhilfd

Lib/test/test_ctypes/test_parameters.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,29 @@ def test_cw_strings(self):
7878
pa = c_wchar_p.from_param(c_wchar_p("123"))
7979
self.assertEqual(type(pa), c_wchar_p)
8080

81+
def test_c_char(self):
82+
from ctypes import c_char
83+
84+
with self.assertRaises(TypeError) as cm:
85+
c_char.from_param(b"abc")
86+
self.assertEqual(str(cm.exception),
87+
"one character bytes, bytearray or integer expected")
88+
89+
@need_symbol('c_wchar')
90+
def test_c_wchar(self):
91+
from ctypes import c_wchar
92+
93+
with self.assertRaises(TypeError) as cm:
94+
c_wchar.from_param("abc")
95+
self.assertEqual(str(cm.exception),
96+
"one character unicode string expected")
97+
98+
99+
with self.assertRaises(TypeError) as cm:
100+
c_wchar.from_param(123)
101+
self.assertEqual(str(cm.exception),
102+
"unicode string expected instead of int instance")
103+
81104
def test_int_pointers(self):
82105
from ctypes import c_short, c_uint, c_int, c_long, POINTER, pointer
83106
LPINT = POINTER(c_int)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Preserve more detailed error messages in :mod:`ctypes`.

Modules/_ctypes/_ctypes.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2197,6 +2197,7 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value)
21972197
struct fielddesc *fd;
21982198
PyObject *as_parameter;
21992199
int res;
2200+
PyObject *exc, *val, *tb;
22002201

22012202
/* If the value is already an instance of the requested type,
22022203
we can use it as is */
@@ -2230,24 +2231,37 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value)
22302231
parg->obj = fd->setfunc(&parg->value, value, 0);
22312232
if (parg->obj)
22322233
return (PyObject *)parg;
2233-
PyErr_Clear();
2234+
PyErr_Fetch(&exc, &val, &tb);
22342235
Py_DECREF(parg);
22352236

22362237
if (_PyObject_LookupAttr(value, &_Py_ID(_as_parameter_), &as_parameter) < 0) {
2238+
Py_XDECREF(exc);
2239+
Py_XDECREF(val);
2240+
Py_XDECREF(tb);
22372241
return NULL;
22382242
}
22392243
if (as_parameter) {
22402244
if (_Py_EnterRecursiveCall("while processing _as_parameter_")) {
22412245
Py_DECREF(as_parameter);
2246+
Py_XDECREF(exc);
2247+
Py_XDECREF(val);
2248+
Py_XDECREF(tb);
22422249
return NULL;
22432250
}
22442251
value = PyCSimpleType_from_param(type, as_parameter);
22452252
_Py_LeaveRecursiveCall();
22462253
Py_DECREF(as_parameter);
2254+
Py_XDECREF(exc);
2255+
Py_XDECREF(val);
2256+
Py_XDECREF(tb);
22472257
return value;
22482258
}
2249-
PyErr_SetString(PyExc_TypeError,
2250-
"wrong type");
2259+
if (exc) {
2260+
PyErr_Restore(exc, val, tb);
2261+
}
2262+
else {
2263+
PyErr_SetString(PyExc_TypeError, "wrong type");
2264+
}
22512265
return NULL;
22522266
}
22532267

0 commit comments

Comments
 (0)
0