see the reference manual for details.
+Specifying function pointers using type annotations
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. decorator:: wrap_dll_function(dll)
+ :module: ctypes.util
+
+ A :term:`decorator` that generates :attr:`~ctypes._CFuncPtr.argtypes` and
+ :attr:`~ctypes._CFuncPtr.restype` from a function signature, using the
+ :attr:`~function.__name__` of the function and its :term:`type annotations <annotation>`.
+
+ The decorated function should look like this::
+
+ @wrap_dll_function(dll_to_wrap)
+ def function_ptr_name(arg_name: ctypes_type, ...) -> ctypes_type:
+ # There should be no body
+ pass
+
+ The body of the decorated function is ignored, and any parameters that are
+ missing type annotations are skipped. The names of the parameters are ignored
+ and do not have to match the underlying C implementation.
+
+ If the decorated function does not have a return type annotation, a
+ :exc:`ValueError` is raised. If the name of the function does not exist
+ in *dll*, an :exc:`AttributeError` is raised.
+
+ For example::
+
+ import ctypes
+ from ctypes.util import wrap_dll_function
+
+ @wrap_dll_function(ctypes.pythonapi)
+ def PyObject_GetAttrString(op: ctypes.py_object, attr: ctypes.c_char_p) -> ctypes.py_object:
+ pass
+
+ PyObject_GetAttrString(42, b"real")
+
+
+ .. versionadded:: next
+
+
.. _ctypes-passing-pointers:
Passing pointers (or: passing parameters by reference)
from an annotation-based syntax, similar to how the :mod:`dataclasses` module
is used.
(Contributed by Peter Bierma in :gh:`104533`.)
+* Add :func:`ctypes.util.wrap_dll_function` for generating function pointers
+ through a function signature.
+ (Contributed by Peter Bierma in :gh:`153903`.)
encodings
return process_the_struct(class_or_none)
+
+def wrap_dll_function(dll):
+ def decorator(func):
+ name = func.__name__
+ ptr = getattr(dll, name)
+ annotations = annotationlib.get_annotations(func)
+
+ try:
+ restype = annotations.pop("return")
+ except KeyError as error:
+ raise ValueError(f"{name!r} missing return type annotation") from error
+
+ ptr.restype = restype
+ ptr.argtypes = tuple(annotations.values())
+ return ptr
+
+ return decorator
+
################################################################
# test code
import unittest
from ctypes import (CDLL, Structure, CFUNCTYPE, sizeof, _CFuncPtr,
c_void_p, c_char_p, c_char, c_int, c_uint, c_long)
+from ctypes.util import wrap_dll_function
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
from ._support import (_CData, PyCFuncPtrType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
def test_abstract(self):
self.assertRaises(TypeError, _CFuncPtr, 13, "name", 42, "iid")
+ def test_wrap_dll_function(self):
+ @wrap_dll_function(ctypes.pythonapi)
+ def PyObject_GetAttr(op: ctypes.py_object, attr: ctypes.py_object) -> ctypes.py_object:
+ pass
+
+ class Foo:
+ a = "abc"
+
+ self.assertEqual(PyObject_GetAttr(Foo, "a"), "abc")
+
+ with self.assertRaises(AttributeError):
+ @wrap_dll_function(ctypes.pythonapi)
+ def noexist():
+ pass
+
+ with self.assertRaisesRegex(ValueError, "'PyObject_GetAttrString' missing return type annotation"):
+ @wrap_dll_function(ctypes.pythonapi)
+ def PyObject_GetAttrString(op: ctypes.py_object, attr: ctypes.c_char_p):
+ pass
+
if __name__ == '__main__':
unittest.main()
--- /dev/null
+Add :func:`ctypes.util.wrap_dll_function` for creating
+:class:`~ctypes._CFuncPtr` objects from a function signature.