Skip to content

Commit

Permalink
pythongh-121249: Support _Complex types in the struct module (python#…
Browse files Browse the repository at this point in the history
…121613)

Co-authored-by: Peter Bierma <[email protected]>
Co-authored-by: Bénédikt Tran <[email protected]>
Co-authored-by: Victor Stinner <[email protected]>
  • Loading branch information
4 people authored Oct 7, 2024
1 parent f55273b commit 7487db4
Show file tree
Hide file tree
Showing 5 changed files with 321 additions and 29 deletions.
19 changes: 19 additions & 0 deletions Doc/library/struct.rst
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,26 @@ platform-dependent.
| ``P`` | :c:expr:`void \*` | integer | | \(5) |
+--------+--------------------------+--------------------+----------------+------------+

Additionally, if IEC 60559 compatible complex arithmetic (Annex G of the
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) |
+--------+--------------------------+--------------------+----------------+------------+
| ``C`` | :c:expr:`double complex` | complex | 16 | \(10) |
+--------+--------------------------+--------------------+----------------+------------+

.. versionchanged:: 3.3
Added support for the ``'n'`` and ``'N'`` formats.

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

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


Notes:

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

(10)
For the ``'E'`` and ``'C'`` format characters, the packed representation uses
the IEEE 754 binary32 and binary64 format for components of the complex
number, regardless of the floating-point format used by the platform.

A format character may be preceded by an integral repeat count. For example,
the format string ``'4h'`` means exactly the same as ``'hhhh'``.

Expand Down
2 changes: 2 additions & 0 deletions Lib/ctypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,10 @@ class c_longdouble(_SimpleCData):
try:
class c_double_complex(_SimpleCData):
_type_ = "C"
_check_size(c_double_complex)
class c_float_complex(_SimpleCData):
_type_ = "E"
_check_size(c_float_complex)
class c_longdouble_complex(_SimpleCData):
_type_ = "F"
except AttributeError:
Expand Down
37 changes: 36 additions & 1 deletion Lib/test/test_struct.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections import abc
from itertools import combinations
import array
import gc
import math
Expand All @@ -11,12 +12,22 @@
from test import support
from test.support import import_helper, suppress_immortalization
from test.support.script_helper import assert_python_ok
from test.support.testcase import ComplexesAreIdenticalMixin

ISBIGENDIAN = sys.byteorder == "big"

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

INF = float('inf')
NAN = float('nan')

try:
struct.pack('C', 1j)
have_c_complex = True
except struct.error:
have_c_complex = False

def iter_integer_formats(byteorders=byteorders):
for code in integer_codes:
for byteorder in byteorders:
Expand All @@ -33,7 +44,7 @@ def bigendian_to_native(value):
else:
return string_reverse(value)

class StructTest(unittest.TestCase):
class StructTest(ComplexesAreIdenticalMixin, unittest.TestCase):
def test_isbigendian(self):
self.assertEqual((struct.pack('=i', 1)[0] == 0), ISBIGENDIAN)

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

@unittest.skipUnless(have_c_complex, "requires C11 complex type support")
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']:
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"
with self.assertRaisesRegex(struct.error, msg1):
struct.pack('E', 1j)
with self.assertRaisesRegex(struct.error, msg1):
struct.unpack('E', b'1')
with self.assertRaisesRegex(struct.error, msg2):
struct.pack('C', 1j)
with self.assertRaisesRegex(struct.error, msg2):
struct.unpack('C', b'1')


class UnpackIteratorTest(unittest.TestCase):
"""
Tests for iterative unpacking (struct.Struct.iter_unpack).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Support the :c:expr:`float complex` and :c:expr:`double complex`
C types in the :mod:`struct` module if the compiler has C11 complex
arithmetic. Patch by Sergey B Kirpichev.
Loading

0 comments on commit 7487db4

Please sign in to comment.