Skip to content

gh-121249: adjust formatting codes for complex types in struct/ctypes modules #132827

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

Merged
merged 1 commit into from
Apr 23, 2025
Merged
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
6 changes: 3 additions & 3 deletions Doc/library/struct.rst
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,9 @@ C11 standard) is supported, the following format characters are available:
+--------+--------------------------+--------------------+----------------+------------+
| Format | C Type | Python type | Standard size | Notes |
+========+==========================+====================+================+============+
| ``E`` | :c:expr:`float complex` | complex | 8 | \(10) |
| ``F`` | :c:expr:`float complex` | complex | 8 | \(10) |
+--------+--------------------------+--------------------+----------------+------------+
| ``C`` | :c:expr:`double complex` | complex | 16 | \(10) |
| ``D`` | :c:expr:`double complex` | complex | 16 | \(10) |
+--------+--------------------------+--------------------+----------------+------------+

.. versionchanged:: 3.3
Expand All @@ -285,7 +285,7 @@ C11 standard) is supported, the following format characters are available:
Added support for the ``'e'`` format.

.. versionchanged:: 3.14
Added support for the ``'E'`` and ``'C'`` formats.
Added support for the ``'F'`` and ``'D'`` formats.


Notes:
Expand Down
2 changes: 1 addition & 1 deletion Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1130,7 +1130,7 @@ struct
------

* Support the :c:expr:`float complex` and :c:expr:`double complex` C types in
the :mod:`struct` module (formatting characters ``'E'`` and ``'C'``,
the :mod:`struct` module (formatting characters ``'F'`` and ``'D'``,
respectively) if the compiler has C11 complex arithmetic.
(Contributed by Sergey B Kirpichev in :gh:`121249`.)

Expand Down
6 changes: 3 additions & 3 deletions Lib/ctypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,13 @@ class c_longdouble(_SimpleCData):

try:
class c_double_complex(_SimpleCData):
_type_ = "C"
_type_ = "D"
_check_size(c_double_complex)
class c_float_complex(_SimpleCData):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you should define c_float_complex first, just like c_float is defined before c_double above?

_type_ = "E"
_type_ = "F"
_check_size(c_float_complex)
class c_longdouble_complex(_SimpleCData):
_type_ = "F"
_type_ = "G"
except AttributeError:
pass

Expand Down
6 changes: 3 additions & 3 deletions Lib/test/test_ctypes/test_c_simple_type_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,11 @@ def test_bad_type_message(self):
class F(metaclass=PyCSimpleType):
_type_ = "\0"
message = str(cm.exception)
expected_type_chars = list('cbBhHiIlLdCEFfuzZqQPXOv?g')
expected_type_chars = list('cbBhHiIlLdDFGfuzZqQPXOv?g')
if not hasattr(ctypes, 'c_float_complex'):
expected_type_chars.remove('C')
expected_type_chars.remove('E')
expected_type_chars.remove('F')
expected_type_chars.remove('D')
expected_type_chars.remove('G')
if not MS_WINDOWS:
expected_type_chars.remove('X')
self.assertIn("'" + ''.join(expected_type_chars) + "'", message)
16 changes: 8 additions & 8 deletions Lib/test/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
NAN = float('nan')

try:
struct.pack('C', 1j)
struct.pack('D', 1j)
have_c_complex = True
except struct.error:
have_c_complex = False
Expand Down Expand Up @@ -801,23 +801,23 @@ def test_c_complex_round_trip(self):
values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2,
-3, INF, -INF, NAN], 2)]
for z in values:
for f in ['E', 'C', '>E', '>C', '<E', '<C']:
for f in ['F', 'D', '>F', '>D', '<F', '<D']:
with self.subTest(z=z, format=f):
round_trip = struct.unpack(f, struct.pack(f, z))[0]
self.assertComplexesAreIdentical(z, round_trip)

@unittest.skipIf(have_c_complex, "requires no C11 complex type support")
def test_c_complex_error(self):
msg1 = "'E' format not supported on this system"
msg2 = "'C' format not supported on this system"
msg1 = "'F' format not supported on this system"
msg2 = "'D' format not supported on this system"
with self.assertRaisesRegex(struct.error, msg1):
struct.pack('E', 1j)
struct.pack('F', 1j)
with self.assertRaisesRegex(struct.error, msg1):
struct.unpack('E', b'1')
struct.unpack('F', b'1')
with self.assertRaisesRegex(struct.error, msg2):
struct.pack('C', 1j)
struct.pack('D', 1j)
with self.assertRaisesRegex(struct.error, msg2):
struct.unpack('C', b'1')
struct.unpack('D', b'1')


class UnpackIteratorTest(unittest.TestCase):
Expand Down
8 changes: 4 additions & 4 deletions Modules/_ctypes/callproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -648,14 +648,14 @@ union result {
int i;
long l;
long long q;
long double D;
long double g;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vstinner, this is something I worry about here.

IIUIC, this is a private interface and we can rename it's members. It seems this is not used across the codebase, but I appreciate a second look. (No tests seems to be affected, though maybe it's just lack of test coverage.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems this is not used across the codebase,

I confirm, the long double member is simply not used. I would suggest to remove it (but in a separated PR).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to remove it

I'm not sure. It seems, naming of union members aren't used - only size is important.

As long as we keep one-letter codes, I think it's good to have corresponding members in this struct.

Same holds for struct tagPyCArgObject - I forgot to update that. I'll make a patch. BTW do we need different (wrt union result) unnamed union here? I doubt.

double d;
float f;
void *p;
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
double complex C;
float complex E;
long double complex F;
double complex D;
float complex F;
Comment on lines +656 to +657
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change order?

Suggested change
double complex D;
float complex F;
float complex F;
double complex D;

long double complex G;
#endif
};

Expand Down
48 changes: 24 additions & 24 deletions Modules/_ctypes/cfield.c
Original file line number Diff line number Diff line change
Expand Up @@ -764,9 +764,9 @@ d_get(void *ptr, Py_ssize_t size)
}

#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
/* C: double complex */
/* D: double complex */
static PyObject *
C_set(void *ptr, PyObject *value, Py_ssize_t size)
D_set(void *ptr, PyObject *value, Py_ssize_t size)
{
assert(NUM_BITS(size) || (size == sizeof(double complex)));
Py_complex c = PyComplex_AsCComplex(value);
Expand All @@ -780,7 +780,7 @@ C_set(void *ptr, PyObject *value, Py_ssize_t size)
}

static PyObject *
C_get(void *ptr, Py_ssize_t size)
D_get(void *ptr, Py_ssize_t size)
{
assert(NUM_BITS(size) || (size == sizeof(double complex)));
double complex x;
Expand All @@ -789,9 +789,9 @@ C_get(void *ptr, Py_ssize_t size)
return PyComplex_FromDoubles(creal(x), cimag(x));
}

/* E: float complex */
/* F: float complex */
static PyObject *
E_set(void *ptr, PyObject *value, Py_ssize_t size)
F_set(void *ptr, PyObject *value, Py_ssize_t size)
{
assert(NUM_BITS(size) || (size == sizeof(float complex)));
Py_complex c = PyComplex_AsCComplex(value);
Expand All @@ -805,7 +805,7 @@ E_set(void *ptr, PyObject *value, Py_ssize_t size)
}

static PyObject *
E_get(void *ptr, Py_ssize_t size)
F_get(void *ptr, Py_ssize_t size)
{
assert(NUM_BITS(size) || (size == sizeof(float complex)));
float complex x;
Expand All @@ -814,9 +814,9 @@ E_get(void *ptr, Py_ssize_t size)
return PyComplex_FromDoubles(crealf(x), cimagf(x));
}

/* F: long double complex */
/* G: long double complex */
static PyObject *
F_set(void *ptr, PyObject *value, Py_ssize_t size)
G_set(void *ptr, PyObject *value, Py_ssize_t size)
{
assert(NUM_BITS(size) || (size == sizeof(long double complex)));
Py_complex c = PyComplex_AsCComplex(value);
Expand All @@ -830,7 +830,7 @@ F_set(void *ptr, PyObject *value, Py_ssize_t size)
}

static PyObject *
F_get(void *ptr, Py_ssize_t size)
G_get(void *ptr, Py_ssize_t size)
{
assert(NUM_BITS(size) || (size == sizeof(long double complex)));
long double complex x;
Expand Down Expand Up @@ -1372,7 +1372,7 @@ struct formattable {
for nbytes in 8, 16, 32, 64:
for sgn in 'i', 'u':
print(f' struct fielddesc fmt_{sgn}{nbytes};')
for code in 'sbBcdCEFgfhHiIlLqQPzuUZXvO':
for code in 'sbBcdFDGgfhHiIlLqQPzuUZXvO':
print(f' struct fielddesc fmt_{code};')
[python start generated code]*/
struct fielddesc fmt_i8;
Expand All @@ -1388,9 +1388,9 @@ for code in 'sbBcdCEFgfhHiIlLqQPzuUZXvO':
struct fielddesc fmt_B;
struct fielddesc fmt_c;
struct fielddesc fmt_d;
struct fielddesc fmt_C;
struct fielddesc fmt_E;
struct fielddesc fmt_F;
struct fielddesc fmt_D;
struct fielddesc fmt_G;
struct fielddesc fmt_g;
struct fielddesc fmt_f;
struct fielddesc fmt_h;
Expand All @@ -1409,7 +1409,7 @@ for code in 'sbBcdCEFgfhHiIlLqQPzuUZXvO':
struct fielddesc fmt_X;
struct fielddesc fmt_v;
struct fielddesc fmt_O;
/*[python end generated code: output=fa648744ec7f919d input=087d58357d4bf2c5]*/
/*[python end generated code: output=f5a07c066fedaca6 input=ffa5d46c29dfb07a]*/

// bool has code '?':
struct fielddesc fmt_bool;
Expand Down Expand Up @@ -1598,9 +1598,9 @@ 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)
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);
TABLE_ENTRY(D, &ffi_type_complex_double);
TABLE_ENTRY(F, &ffi_type_complex_float);
TABLE_ENTRY(G, &ffi_type_complex_longdouble);
}
#endif
TABLE_ENTRY(g, &ffi_type_longdouble);
Expand Down Expand Up @@ -1635,7 +1635,7 @@ for base_code, base_c_type in [
formattable.fmt_bool.getfunc = bool_get;

/*[python input]
all_chars = "cbBhHiIlLdCEFfuzZqQPXOv?g"
all_chars = "cbBhHiIlLdDFGfuzZqQPXOv?g"
print(f' assert(sizeof(formattable.simple_type_chars) == {len(all_chars)+1});')
print(f' int i = 0;')
for char in all_chars:
Expand All @@ -1656,9 +1656,9 @@ print(f" formattable.simple_type_chars[i] = 0;")
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_D.code) formattable.simple_type_chars[i++] = 'D';
if (formattable.fmt_F.code) formattable.simple_type_chars[i++] = 'F';
if (formattable.fmt_G.code) formattable.simple_type_chars[i++] = 'G';
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';
Expand All @@ -1672,7 +1672,7 @@ print(f" formattable.simple_type_chars[i] = 0;")
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]*/
/*[python end generated code: output=2aa52670d1570f18 input=cff3e7cb95adac61]*/

}
#undef FIXINT_FIELDDESC_FOR
Expand All @@ -1689,17 +1689,17 @@ _ctypes_get_fielddesc(const char *fmt)
struct fielddesc *result = NULL;
switch(fmt[0]) {
/*[python input]
for code in 'sbBcdCEFgfhHiIlLqQPzuUZXvO':
for code in 'sbBcdDFGgfhHiIlLqQPzuUZXvO':
print(f" case '{code}': result = &formattable.fmt_{code}; break;")
[python start generated code]*/
case 's': result = &formattable.fmt_s; break;
case 'b': result = &formattable.fmt_b; break;
case 'B': result = &formattable.fmt_B; break;
case 'c': result = &formattable.fmt_c; break;
case 'd': result = &formattable.fmt_d; break;
case 'C': result = &formattable.fmt_C; break;
case 'E': result = &formattable.fmt_E; break;
case 'D': result = &formattable.fmt_D; break;
case 'F': result = &formattable.fmt_F; break;
case 'G': result = &formattable.fmt_G; break;
case 'g': result = &formattable.fmt_g; break;
case 'f': result = &formattable.fmt_f; break;
case 'h': result = &formattable.fmt_h; break;
Expand All @@ -1718,7 +1718,7 @@ for code in 'sbBcdCEFgfhHiIlLqQPzuUZXvO':
case 'X': result = &formattable.fmt_X; break;
case 'v': result = &formattable.fmt_v; break;
case 'O': result = &formattable.fmt_O; break;
/*[python end generated code: output=81a8223dda9f81f7 input=2f59666d3c024edf]*/
/*[python end generated code: output=6e5c91940732fde9 input=902223feffc2fe38]*/
case '?': result = &formattable.fmt_bool; break;
}
if (!result || !result->code) {
Expand Down
24 changes: 12 additions & 12 deletions Modules/_struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -913,11 +913,11 @@ static const formatdef native_table[] = {
{'f', sizeof(float), FLOAT_ALIGN, nu_float, np_float},
{'d', sizeof(double), DOUBLE_ALIGN, nu_double, np_double},
#ifdef Py_HAVE_C_COMPLEX
{'E', sizeof(float complex), FLOAT_COMPLEX_ALIGN, nu_float_complex, np_float_complex},
{'C', sizeof(double complex), DOUBLE_COMPLEX_ALIGN, nu_double_complex, np_double_complex},
{'F', sizeof(float complex), FLOAT_COMPLEX_ALIGN, nu_float_complex, np_float_complex},
{'D', sizeof(double complex), DOUBLE_COMPLEX_ALIGN, nu_double_complex, np_double_complex},
#else
{'E', 1, 0, nu_complex_stub, np_complex_stub},
{'C', 1, 0, nu_complex_stub, np_complex_stub},
{'F', 1, 0, nu_complex_stub, np_complex_stub},
{'D', 1, 0, nu_complex_stub, np_complex_stub},
#endif
{'P', sizeof(void *), VOID_P_ALIGN, nu_void_p, np_void_p},
{0}
Expand Down Expand Up @@ -1253,11 +1253,11 @@ static formatdef bigendian_table[] = {
{'f', 4, 0, bu_float, bp_float},
{'d', 8, 0, bu_double, bp_double},
#ifdef Py_HAVE_C_COMPLEX
{'E', 8, 0, bu_float_complex, bp_float_complex},
{'C', 16, 0, bu_double_complex, bp_double_complex},
{'F', 8, 0, bu_float_complex, bp_float_complex},
{'D', 16, 0, bu_double_complex, bp_double_complex},
#else
{'E', 1, 0, nu_complex_stub, np_complex_stub},
{'C', 1, 0, nu_complex_stub, np_complex_stub},
{'F', 1, 0, nu_complex_stub, np_complex_stub},
{'D', 1, 0, nu_complex_stub, np_complex_stub},
#endif
{0}
};
Expand Down Expand Up @@ -1577,11 +1577,11 @@ static formatdef lilendian_table[] = {
{'f', 4, 0, lu_float, lp_float},
{'d', 8, 0, lu_double, lp_double},
#ifdef Py_HAVE_C_COMPLEX
{'E', 8, 0, lu_float_complex, lp_float_complex},
{'C', 16, 0, lu_double_complex, lp_double_complex},
{'F', 8, 0, lu_float_complex, lp_float_complex},
{'D', 16, 0, lu_double_complex, lp_double_complex},
#else
{'E', 1, 0, nu_complex_stub, np_complex_stub},
{'C', 1, 0, nu_complex_stub, np_complex_stub},
{'F', 1, 0, nu_complex_stub, np_complex_stub},
{'D', 1, 0, nu_complex_stub, np_complex_stub},
#endif
{0}
};
Expand Down
Loading