[Python-checkins] gh-99266: ctypes: Preserve more detailed exception in `ArgumentError`
kumaraditya303
webhook-mailer at python.org
Sat Jan 21 08:44:48 EST 2023
https://github.com/python/cpython/commit/b4e11a7985a3bc116596c63d1e5f8bbd653041b9
commit: b4e11a7985a3bc116596c63d1e5f8bbd653041b9
branch: main
author: Kamil Turek <kamil.turek at hotmail.com>
committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com>
date: 2023-01-21T19:14:43+05:30
summary:
gh-99266: ctypes: Preserve more detailed exception in `ArgumentError`
Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com>
files:
A Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst
M Doc/library/ctypes.rst
M Lib/test/test_ctypes/test_functions.py
M Lib/test/test_ctypes/test_parameters.py
M Modules/_ctypes/_ctypes.c
diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst
index 50ab29375623..4de5c820f2c6 100644
--- a/Doc/library/ctypes.rst
+++ b/Doc/library/ctypes.rst
@@ -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
diff --git a/Lib/test/test_ctypes/test_functions.py b/Lib/test/test_ctypes/test_functions.py
index 95633dfa8b38..703bd2c601cc 100644
--- a/Lib/test/test_ctypes/test_functions.py
+++ b/Lib/test/test_ctypes/test_functions.py
@@ -54,6 +54,23 @@ class X(object, _SimpleCData):
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 @@ def test_wchar_parm(self):
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
diff --git a/Lib/test/test_ctypes/test_parameters.py b/Lib/test/test_ctypes/test_parameters.py
index 84839d9c6a96..22d290db1bcb 100644
--- a/Lib/test/test_ctypes/test_parameters.py
+++ b/Lib/test/test_ctypes/test_parameters.py
@@ -78,6 +78,29 @@ def test_cw_strings(self):
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)
diff --git a/Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst b/Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst
new file mode 100644
index 000000000000..97e9569e40a9
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst
@@ -0,0 +1 @@
+Preserve more detailed error messages in :mod:`ctypes`.
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index 4ce6433a2e45..272cafb5a9a3 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -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;
}
More information about the Python-checkins
mailing list