Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
4 changes: 2 additions & 2 deletions Doc/library/ctypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,8 @@ Fundamental data types
(1)
The constructor accepts any object with a truth value.

Additionally, if IEC 60559 compatible complex arithmetic (Annex G) is supported, the following
complex types are available:
Additionally, if IEC 60559 compatible complex arithmetic (Annex G) is supported
in both C and ``libffi``, the following complex types are available:

+----------------------------------+---------------------------------+-----------------+
| ctypes type | C type | Python type |
Expand Down
18 changes: 18 additions & 0 deletions Lib/test/test_ctypes/test_c_simple_type_meta.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import unittest
from test.support import MS_WINDOWS
import ctypes
from ctypes import POINTER, c_void_p

Expand Down Expand Up @@ -150,3 +151,20 @@ class Sub(CtBase):

self.assertIsInstance(POINTER(Sub), p_meta)
self.assertTrue(issubclass(POINTER(Sub), Sub))

def test_bad_type_message(self):
"""Verify the error message that lists all available type codes"""
# (The string is generated at runtime, so this checks the underlying
# set of types as well as correct construction of the string.)
with self.assertRaises(AttributeError) as cm:
class F(metaclass=PyCSimpleType):
_type_ = "\0"
message = str(cm.exception)
expected_type_chars = list('cbBhHiIlLdCEFfuzZqQPXOv?g')
if not hasattr(ctypes, 'c_float_complex'):
expected_type_chars.remove('C')
expected_type_chars.remove('E')
expected_type_chars.remove('F')
if not MS_WINDOWS:
expected_type_chars.remove('X')
self.assertIn("'" + ''.join(expected_type_chars) + "'", message)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
When using macOS system ``libffi``, support for complex types in
:mod:`ctypes` is now checked at runtime (macOS 10.15 or newer). The types
must also be available at build time.
19 changes: 5 additions & 14 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1762,11 +1762,6 @@ class _ctypes.c_void_p "PyObject *" "clinic_state_sub()->PyCSimpleType_Type"
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=dd4d9646c56f43a9]*/

#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdCEFfuzZqQPXOv?g";
#else
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdfuzZqQPXOv?g";
#endif

/*[clinic input]
_ctypes.c_wchar_p.from_param as c_wchar_p_from_param
Expand Down Expand Up @@ -2237,17 +2232,13 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds)
"which must be a string of length 1");
goto error;
}
if (!strchr(SIMPLE_TYPE_CHARS, *proto_str)) {
fmt = _ctypes_get_fielddesc(proto_str);
if (!fmt) {
PyErr_Format(PyExc_AttributeError,
"class must define a '_type_' attribute which must be\n"
"a single character string containing one of '%s'.",
SIMPLE_TYPE_CHARS);
goto error;
}
fmt = _ctypes_get_fielddesc(proto_str);
if (fmt == NULL) {
PyErr_Format(PyExc_ValueError,
"_type_ '%s' not supported", proto_str);
"a single character string containing one of the\n"
"supported types: '%s'.",
_ctypes_get_simple_type_chars());
goto error;
}

Expand Down
76 changes: 68 additions & 8 deletions Modules/_ctypes/cfield.c
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,10 @@ for code in 'sbBcdCEFgfhHiIlLqQPzuUZXvO':

// always contains NULLs:
struct fielddesc fmt_nil;

// Result of _ctypes_get_simple_type_chars. Initialized just after
// the rest of formattable, so we stash it here.
char simple_type_chars[26];
};

static struct formattable formattable;
Expand Down Expand Up @@ -1315,8 +1319,8 @@ _Py_COMP_DIAG_PUSH

/* Delayed initialization. Windows cannot statically reference dynamically
loaded addresses from DLLs. */
void
_ctypes_init_fielddesc(void)
static void
_ctypes_init_fielddesc_locked(void)
{
/* Fixed-width integers */

Expand Down Expand Up @@ -1432,9 +1436,11 @@ for base_code, base_c_type in [

TABLE_ENTRY_SW(d, &ffi_type_double);
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
TABLE_ENTRY(C, &ffi_type_complex_double);
TABLE_ENTRY(E, &ffi_type_complex_float);
TABLE_ENTRY(F, &ffi_type_complex_longdouble);
if (Py_FFI_COMPLEX_AVAILABLE) {
TABLE_ENTRY(C, &ffi_type_complex_double);
TABLE_ENTRY(E, &ffi_type_complex_float);
TABLE_ENTRY(F, &ffi_type_complex_longdouble);
}
#endif
TABLE_ENTRY(g, &ffi_type_longdouble);
TABLE_ENTRY_SW(f, &ffi_type_float);
Expand Down Expand Up @@ -1466,21 +1472,75 @@ for base_code, base_c_type in [
formattable.fmt_bool.code = '?';
formattable.fmt_bool.setfunc = bool_set;
formattable.fmt_bool.getfunc = bool_get;

/*[python input]
all_chars = "cbBhHiIlLdCEFfuzZqQPXOv?g"
print(f' assert(sizeof(formattable.simple_type_chars) == {len(all_chars)+1});')
print(f' int i = 0;')
for char in all_chars:
ident_char = {'?': 'bool'}.get(char, char)
print(f" if (formattable.fmt_{ident_char}.code) "
+ f"formattable.simple_type_chars[i++] = '{char}';")
print(f" formattable.simple_type_chars[i] = 0;")
[python start generated code]*/
assert(sizeof(formattable.simple_type_chars) == 26);
int i = 0;
if (formattable.fmt_c.code) formattable.simple_type_chars[i++] = 'c';
if (formattable.fmt_b.code) formattable.simple_type_chars[i++] = 'b';
if (formattable.fmt_B.code) formattable.simple_type_chars[i++] = 'B';
if (formattable.fmt_h.code) formattable.simple_type_chars[i++] = 'h';
if (formattable.fmt_H.code) formattable.simple_type_chars[i++] = 'H';
if (formattable.fmt_i.code) formattable.simple_type_chars[i++] = 'i';
if (formattable.fmt_I.code) formattable.simple_type_chars[i++] = 'I';
if (formattable.fmt_l.code) formattable.simple_type_chars[i++] = 'l';
if (formattable.fmt_L.code) formattable.simple_type_chars[i++] = 'L';
if (formattable.fmt_d.code) formattable.simple_type_chars[i++] = 'd';
if (formattable.fmt_C.code) formattable.simple_type_chars[i++] = 'C';
if (formattable.fmt_E.code) formattable.simple_type_chars[i++] = 'E';
if (formattable.fmt_F.code) formattable.simple_type_chars[i++] = 'F';
if (formattable.fmt_f.code) formattable.simple_type_chars[i++] = 'f';
if (formattable.fmt_u.code) formattable.simple_type_chars[i++] = 'u';
if (formattable.fmt_z.code) formattable.simple_type_chars[i++] = 'z';
if (formattable.fmt_Z.code) formattable.simple_type_chars[i++] = 'Z';
if (formattable.fmt_q.code) formattable.simple_type_chars[i++] = 'q';
if (formattable.fmt_Q.code) formattable.simple_type_chars[i++] = 'Q';
if (formattable.fmt_P.code) formattable.simple_type_chars[i++] = 'P';
if (formattable.fmt_X.code) formattable.simple_type_chars[i++] = 'X';
if (formattable.fmt_O.code) formattable.simple_type_chars[i++] = 'O';
if (formattable.fmt_v.code) formattable.simple_type_chars[i++] = 'v';
if (formattable.fmt_bool.code) formattable.simple_type_chars[i++] = '?';
if (formattable.fmt_g.code) formattable.simple_type_chars[i++] = 'g';
formattable.simple_type_chars[i] = 0;
/*[python end generated code: output=e6e5098a02f4b606 input=72031a625eac00c1]*/

}
#undef FIXINT_FIELDDESC_FOR
_Py_COMP_DIAG_POP

struct fielddesc *
_ctypes_get_fielddesc(const char *fmt)
static void
_ctypes_init_fielddesc(void)
{
static bool initialized = false;
static PyMutex mutex = {0};
PyMutex_Lock(&mutex);
if (!initialized) {
_ctypes_init_fielddesc();
_ctypes_init_fielddesc_locked();
initialized = true;
}
PyMutex_Unlock(&mutex);
}

char *
_ctypes_get_simple_type_chars(void) {
_ctypes_init_fielddesc();
return formattable.simple_type_chars;
}

struct fielddesc *
_ctypes_get_fielddesc(const char *fmt)
{
_ctypes_init_fielddesc();

struct fielddesc *result = NULL;
switch(fmt[0]) {
/*[python input]
Expand Down
12 changes: 12 additions & 0 deletions Modules/_ctypes/ctypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,17 @@
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_typeobject.h" // _PyType_GetModuleState()

// Do we support C99 complex types in ffi?
// For Apple's libffi, this must be determined at runtime (see gh-128156).
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
# include "../_complex.h" // complex
# if USING_APPLE_OS_LIBFFI && defined(__has_builtin) && __has_builtin(__builtin_available)
# define Py_FFI_COMPLEX_AVAILABLE __builtin_available(macOS 10.15, *)
# else
# define Py_FFI_COMPLEX_AVAILABLE 1
# endif
#else
# define Py_FFI_COMPLEX_AVAILABLE 0
#endif

#ifndef MS_WIN32
Expand Down Expand Up @@ -255,6 +264,9 @@ struct fielddesc {
GETFUNC getfunc_swapped;
};

// Get all single-character type codes (for use in error messages)
extern char *_ctypes_get_simple_type_chars(void);

typedef struct CFieldObject {
PyObject_HEAD
Py_ssize_t offset;
Expand Down
Loading