ctypes: wrong exception message in docs
Documentation
The ctypes docs say that the message of the ArgumentError for the presented example should be as follows:

In fact, it looks like this:
>>> strchr = libc.strchr
>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>>
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 2: TypeError: wrong type
I will prepare a PR.
- PR: gh-99267
Note that the better error message from the c_set() set function gets cleared by PyCSimpleType_from_param() before it looks for an _as_parameter_ attribute on the object. We're left with the less useful message "wrong type". The original exception could be saved and then restored if the object doesn't have an _as_parameter_ attribute.
c_set():
https://github.com/python/cpython/blob/2eee9d9cd7eb1e396fa9a4af7c5fadeeafbdaa38/Modules/_ctypes/cfield.c#L1114-L1137
PyCSimpleType_from_param():
https://github.com/python/cpython/blob/2eee9d9cd7eb1e396fa9a4af7c5fadeeafbdaa38/Modules/_ctypes/_ctypes.c#L2208-L2274
Actually, I don't see why it doesn't first check for an _as_parameter_ attribute on value, and I don't see why it needs to recursively call PyCSimpleType_from_param().
As per the docs, _as_parameter_ can return another object with the _as_parameter_ attribute. I believe that's why the recursion is needed.
The from_param() class method receives the Python object passed to the function call, it should do a typecheck or whatever is needed to make sure this object is acceptable, and then return the object itself, its as_parameter attribute, or whatever you want to pass as the C function argument in this case. Again, the result should be an integer, string, bytes, a ctypes instance, or an object with an as_parameter attribute.
https://docs.python.org/3/library/ctypes.html
@eryksun I followed your suggestion of saving and restoring the exception. I opened a PR.