Skip to content

Commit 7487db4

Browse files
skirpichevZeroIntensitypicnixzvstinner
authored
gh-121249: Support _Complex types in the struct module (#121613)
Co-authored-by: Peter Bierma <[email protected]> Co-authored-by: Bénédikt Tran <[email protected]> Co-authored-by: Victor Stinner <[email protected]>
1 parent f55273b commit 7487db4

File tree

5 files changed

+321
-29
lines changed

5 files changed

+321
-29
lines changed

Doc/library/struct.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,12 +267,26 @@ platform-dependent.
267267
| ``P`` | :c:expr:`void \*` | integer | | \(5) |
268268
+--------+--------------------------+--------------------+----------------+------------+
269269

270+
Additionally, if IEC 60559 compatible complex arithmetic (Annex G of the
271+
C11 standard) is supported, the following format characters are available:
272+
273+
+--------+--------------------------+--------------------+----------------+------------+
274+
| Format | C Type | Python type | Standard size | Notes |
275+
+========+==========================+====================+================+============+
276+
| ``E`` | :c:expr:`float complex` | complex | 8 | \(10) |
277+
+--------+--------------------------+--------------------+----------------+------------+
278+
| ``C`` | :c:expr:`double complex` | complex | 16 | \(10) |
279+
+--------+--------------------------+--------------------+----------------+------------+
280+
270281
.. versionchanged:: 3.3
271282
Added support for the ``'n'`` and ``'N'`` formats.
272283

273284
.. versionchanged:: 3.6
274285
Added support for the ``'e'`` format.
275286

287+
.. versionchanged:: 3.14
288+
Added support for the ``'E'`` and ``'C'`` formats.
289+
276290

277291
Notes:
278292

@@ -349,6 +363,11 @@ Notes:
349363
of bytes. As a special case, ``'0s'`` means a single, empty string (while
350364
``'0c'`` means 0 characters).
351365

366+
(10)
367+
For the ``'E'`` and ``'C'`` format characters, the packed representation uses
368+
the IEEE 754 binary32 and binary64 format for components of the complex
369+
number, regardless of the floating-point format used by the platform.
370+
352371
A format character may be preceded by an integral repeat count. For example,
353372
the format string ``'4h'`` means exactly the same as ``'hhhh'``.
354373

Lib/ctypes/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,10 @@ class c_longdouble(_SimpleCData):
208208
try:
209209
class c_double_complex(_SimpleCData):
210210
_type_ = "C"
211+
_check_size(c_double_complex)
211212
class c_float_complex(_SimpleCData):
212213
_type_ = "E"
214+
_check_size(c_float_complex)
213215
class c_longdouble_complex(_SimpleCData):
214216
_type_ = "F"
215217
except AttributeError:

Lib/test/test_struct.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from collections import abc
2+
from itertools import combinations
23
import array
34
import gc
45
import math
@@ -11,12 +12,22 @@
1112
from test import support
1213
from test.support import import_helper, suppress_immortalization
1314
from test.support.script_helper import assert_python_ok
15+
from test.support.testcase import ComplexesAreIdenticalMixin
1416

1517
ISBIGENDIAN = sys.byteorder == "big"
1618

1719
integer_codes = 'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q', 'n', 'N'
1820
byteorders = '', '@', '=', '<', '>', '!'
1921

22+
INF = float('inf')
23+
NAN = float('nan')
24+
25+
try:
26+
struct.pack('C', 1j)
27+
have_c_complex = True
28+
except struct.error:
29+
have_c_complex = False
30+
2031
def iter_integer_formats(byteorders=byteorders):
2132
for code in integer_codes:
2233
for byteorder in byteorders:
@@ -33,7 +44,7 @@ def bigendian_to_native(value):
3344
else:
3445
return string_reverse(value)
3546

36-
class StructTest(unittest.TestCase):
47+
class StructTest(ComplexesAreIdenticalMixin, unittest.TestCase):
3748
def test_isbigendian(self):
3849
self.assertEqual((struct.pack('=i', 1)[0] == 0), ISBIGENDIAN)
3950

@@ -783,6 +794,30 @@ def test_repr(self):
783794
s = struct.Struct('=i2H')
784795
self.assertEqual(repr(s), f'Struct({s.format!r})')
785796

797+
@unittest.skipUnless(have_c_complex, "requires C11 complex type support")
798+
def test_c_complex_round_trip(self):
799+
values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2,
800+
-3, INF, -INF, NAN], 2)]
801+
for z in values:
802+
for f in ['E', 'C', '>E', '>C', '<E', '<C']:
803+
with self.subTest(z=z, format=f):
804+
round_trip = struct.unpack(f, struct.pack(f, z))[0]
805+
self.assertComplexesAreIdentical(z, round_trip)
806+
807+
@unittest.skipIf(have_c_complex, "requires no C11 complex type support")
808+
def test_c_complex_error(self):
809+
msg1 = "'E' format not supported on this system"
810+
msg2 = "'C' format not supported on this system"
811+
with self.assertRaisesRegex(struct.error, msg1):
812+
struct.pack('E', 1j)
813+
with self.assertRaisesRegex(struct.error, msg1):
814+
struct.unpack('E', b'1')
815+
with self.assertRaisesRegex(struct.error, msg2):
816+
struct.pack('C', 1j)
817+
with self.assertRaisesRegex(struct.error, msg2):
818+
struct.unpack('C', b'1')
819+
820+
786821
class UnpackIteratorTest(unittest.TestCase):
787822
"""
788823
Tests for iterative unpacking (struct.Struct.iter_unpack).
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Support the :c:expr:`float complex` and :c:expr:`double complex`
2+
C types in the :mod:`struct` module if the compiler has C11 complex
3+
arithmetic. Patch by Sergey B Kirpichev.

0 commit comments

Comments
 (0)