https://github.com/python/cpython/commit/d3b1bb228c951f8245f36ee28d9b175786522730
commit: d3b1bb228c951f8245f36ee28d9b175786522730
branch: main
author: Petr Viktorin <[email protected]>
committer: encukou <[email protected]>
date: 2025-01-21T10:59:18+01:00
summary:
gh-128156: Guard use of `ffi_type_complex_double` on macOS system libffi
(GH-128680)
* Determine ffi complex support at runtime
* Also, generate SIMPLE_TYPE_CHARS once at runtime
files:
A Misc/NEWS.d/next/Library/2025-01-09-16-20-34.gh-issue-128156.GfObBq.rst
M Doc/library/ctypes.rst
M Lib/test/test_ctypes/test_c_simple_type_meta.py
M Modules/_ctypes/_ctypes.c
M Modules/_ctypes/cfield.c
M Modules/_ctypes/ctypes.h
M Tools/c-analyzer/cpython/globals-to-fix.tsv
diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst
index 164d706e7738e2..615138302e1379 100644
--- a/Doc/library/ctypes.rst
+++ b/Doc/library/ctypes.rst
@@ -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 |
diff --git a/Lib/test/test_ctypes/test_c_simple_type_meta.py
b/Lib/test/test_ctypes/test_c_simple_type_meta.py
index e8f347a0d0c57b..b446fd5c77dde2 100644
--- a/Lib/test/test_ctypes/test_c_simple_type_meta.py
+++ b/Lib/test/test_ctypes/test_c_simple_type_meta.py
@@ -1,4 +1,5 @@
import unittest
+from test.support import MS_WINDOWS
import ctypes
from ctypes import POINTER, c_void_p
@@ -150,3 +151,20 @@ class Sub(CtBase):
self.assertIsInstance(POINTER(Sub), p_meta)
self.assertIsSubclass(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)
diff --git
a/Misc/NEWS.d/next/Library/2025-01-09-16-20-34.gh-issue-128156.GfObBq.rst
b/Misc/NEWS.d/next/Library/2025-01-09-16-20-34.gh-issue-128156.GfObBq.rst
new file mode 100644
index 00000000000000..ec6a55040ae6cb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-01-09-16-20-34.gh-issue-128156.GfObBq.rst
@@ -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.
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index c4d130a5ec1d52..5d37610cc1c970 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -1776,11 +1776,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
@@ -2252,17 +2247,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;
}
diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c
index dcac9da75360a4..e045d206f05580 100644
--- a/Modules/_ctypes/cfield.c
+++ b/Modules/_ctypes/cfield.c
@@ -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;
@@ -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 */
@@ -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);
@@ -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]
diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h
index cc09639e21f7c2..9e8097feae2c17 100644
--- a/Modules/_ctypes/ctypes.h
+++ b/Modules/_ctypes/ctypes.h
@@ -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
@@ -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;
diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv
b/Tools/c-analyzer/cpython/globals-to-fix.tsv
index a74779803228c2..54954cfb5f83ff 100644
--- a/Tools/c-analyzer/cpython/globals-to-fix.tsv
+++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv
@@ -407,7 +407,8 @@ Modules/_tkinter.c - trbInCmd -
## other
Include/datetime.h - PyDateTimeAPI -
-Modules/_ctypes/cfield.c _ctypes_get_fielddesc initialized -
+Modules/_ctypes/cfield.c _ctypes_init_fielddesc initialized -
+Modules/_ctypes/cfield.c - formattable -
Modules/_ctypes/malloc_closure.c - _pagesize -
Modules/_cursesmodule.c - curses_module_loaded -
Modules/_cursesmodule.c - curses_initscr_called -
@@ -422,7 +423,6 @@ Modules/readline.c - libedit_history_start -
##-----------------------
## state
-Modules/_ctypes/cfield.c - formattable -
Modules/_ctypes/malloc_closure.c - free_list -
Modules/_curses_panel.c - lop -
Modules/_ssl/debughelpers.c _PySSL_keylog_callback lock -
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]