Skip to content

bpo-39632: Fix ctypes variadic function call convention #18560

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion Lib/ctypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import os as _os, sys as _sys
import types as _types

__version__ = "1.1.0"
__version__ = "1.1.1"

from _ctypes import Union, Structure, Array
from _ctypes import _Pointer
Expand Down Expand Up @@ -560,4 +560,18 @@ def DllCanUnloadNow():
elif sizeof(kind) == 8: c_uint64 = kind
del(kind)

# _ctypes needs the following types for variadic function type promotion
import _ctypes
_ctypes.c_int = c_int
_ctypes.c_uint = c_uint
_ctypes.c_int8 = c_int8
_ctypes.c_uint8 = c_uint8
_ctypes.c_int16 = c_int16
_ctypes.c_uint16 = c_uint16
_ctypes.c_int32 = c_int32
_ctypes.c_uint32 = c_uint32
_ctypes.c_int64 = c_int64
_ctypes.c_uint64 = c_uint64
_ctypes.c_double = c_double

_reset_cache()
41 changes: 41 additions & 0 deletions Lib/ctypes/test/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,47 @@ def test_doubleresult(self):
self.assertEqual(result, -21)
self.assertEqual(type(result), float)

def test_doubleresult_var(self):
f = dll._testfunc_d_bhilfd_var
# This function only supports the following argument types:
# c_byte, c_short, c_int, c_long, c_float, c_double
# Even if it's a variadic function, don't try to pass anything else
f.argtypes = (c_int, ...)
f.restype = c_double

# builtin types
result = f(0, 1, 2, 3, 4, 5.0, 6.0)
self.assertEqual(result, 21)
self.assertEqual(type(result), float)

# user defined types with _as_parameter_
class AsParameter:
def __init__(self, number):
self._as_parameter_ = number
result = f(0, *map(AsParameter, (1, 2, 3, 4, 5.0, 6.0)))
self.assertEqual(result, 21)
self.assertEqual(type(result), float)

# ctypes without automatic type promotion
result = f(
0, c_int(1), c_int(2), c_int(3), c_int(4), c_double(5.0),
c_double(6.0)
)
self.assertEqual(result, 21)
self.assertEqual(type(result), float)

# ctypes with automatic type promotion
result = f(
0, c_byte(1), c_int8(2 ** 7 - 1), c_uint32(2 ** 32 - 1),
c_int32(4), c_float(5.0), c_double(6.0)
)
self.assertEqual(result, 1 + 2 ** 7 - 1 - 1 + 4 + 5. + 6.)
self.assertEqual(type(result), float)

result = f(0, -1, -2, -3, -4, -5.0, -6.0)
self.assertEqual(result, -21)
self.assertEqual(type(result), float)

def test_longdoubleresult(self):
f = dll._testfunc_D_bhilfD
f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_longdouble]
Expand Down
2 changes: 1 addition & 1 deletion Lib/ctypes/test/test_python_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def test_PyObj_FromPtr(self):

def test_PyOS_snprintf(self):
PyOS_snprintf = pythonapi.PyOS_snprintf
PyOS_snprintf.argtypes = POINTER(c_char), c_size_t, c_char_p
PyOS_snprintf.argtypes = POINTER(c_char), c_size_t, c_char_p, ...

buf = c_buffer(256)
PyOS_snprintf(buf, sizeof(buf), b"Hello from %s", b"ctypes")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Fix and improve ctypes variadic function support. Functions that take a
variable number of arguments should now be declared using an Ellipsis (...)
as their last argument (just as a prototype for a C function). ctypes variadic
functions declared this way automatically perform the default argument
promotion (small integer types are promoted to c_int and c_float is promoted to
double).
Loading