Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
11 changes: 9 additions & 2 deletions Doc/library/array.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ defined:
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'D'`` | double complex | complex | 16 | \(4) |
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'Zf'`` | float complex | complex | 8 | \(4) |
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'Zd'`` | double complex | complex | 16 | \(4) |
+-----------+--------------------+-------------------+-----------------------+-------+


Notes:
Expand Down Expand Up @@ -80,7 +84,7 @@ Notes:
.. versionadded:: 3.15

(4)
Complex types (``F`` and ``D``) are available unconditionally,
Complex types (``F``, ``D``, ``Zf`` and ``Zd``) are available unconditionally,
regardless on support for complex types (the Annex G of the C11 standard)
by the C compiler.
As specified in the C11 standard, each complex type is represented by a
Expand All @@ -105,7 +109,10 @@ The module defines the following item:

.. data:: typecodes

A string with all available type codes.
A tuple with all available type codes.

.. versionchanged:: next
The type changed from :str:`str` to :class:`tuple`.


The module defines the following type:
Expand Down
9 changes: 8 additions & 1 deletion Doc/library/struct.rst
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ platform-dependent.
+--------+--------------------------+--------------------+----------------+------------+
| ``D`` | :c:expr:`double complex` | complex | 16 | \(10) |
+--------+--------------------------+--------------------+----------------+------------+
| ``Zf`` | :c:expr:`float complex` | complex | 8 | \(10) |
+--------+--------------------------+--------------------+----------------+------------+
| ``Zd`` | :c:expr:`double complex` | complex | 16 | \(10) |
+--------+--------------------------+--------------------+----------------+------------+
Comment on lines +267 to +270

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I suggest leave one entry per data type with either one-letter or two-letters code. And mention an alternative in the note, perhaps with a deprecation. Technically, no new types here - just aliases.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I would prefer this decision after Python 3.15 beta1, and don't put any preference for now in the beta1.

| ``s`` | :c:expr:`char[]` | bytes | | \(9) |
+--------+--------------------------+--------------------+----------------+------------+
| ``p`` | :c:expr:`char[]` | bytes | | \(8) |
Expand All @@ -280,6 +284,9 @@ platform-dependent.
.. versionchanged:: 3.14
Added support for the ``'F'`` and ``'D'`` formats.

.. versionchanged:: next
Added support for the ``'Zf'`` and ``'Zd'`` formats.

.. seealso::

The :mod:`array` and :ref:`ctypes <ctypes-fundamental-data-types>` modules,
Expand Down Expand Up @@ -368,7 +375,7 @@ Notes:
For the ``'F'`` and ``'D'`` 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.
Note that complex types (``F`` and ``D``) are available unconditionally,
Note that complex types (``F``/``Zf`` and ``D``/``Zd``) are available unconditionally,
despite complex types being an optional feature in C.
As specified in the C11 standard, each complex type is represented by a
two-element C array containing, respectively, the real and imaginary parts.
Expand Down
9 changes: 7 additions & 2 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -679,13 +679,18 @@ array
-----

* Support the :c:expr:`float complex` and :c:expr:`double complex` C types:
formatting characters ``'F'`` and ``'D'`` respectively.
(Contributed by Sergey B Kirpichev in :gh:`146151`.)
formatting characters ``'F'``/``'Zd'`` and ``'D'``/``'Zd'`` respectively.
Comment thread
vstinner marked this conversation as resolved.
Resolved
(Contributed by Sergey B Kirpichev and Victor Stinner in :gh:`146151`
and :gh:`148675`.)
Comment thread
vstinner marked this conversation as resolved.
Resolved

* Support half-floats (16-bit IEEE 754 binary interchange format): formatting
character ``'e'``.
(Contributed by Sergey B Kirpichev in :gh:`146238`.)

* The :data:`array.typecodes` type changed from :class:`str` to :class:`tuple`
to support type codes longer than 1 character (``Zf`` and ``Zd``).
(Contributed by Victor Stinner in :gh:`148675`.)


base64
------
Expand Down
35 changes: 32 additions & 3 deletions Lib/test/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ class ArraySubclassWithKwargs(array.array):
def __init__(self, typecode, newarg=None):
array.array.__init__(self)

typecodes = 'uwbBhHiIlLfdqQFDe'
typecodes = (
'u', 'w', 'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L',
'f', 'd', 'q', 'Q', 'F', 'D', 'e', 'Zf', 'Zd')


class MiscTest(unittest.TestCase):

Expand All @@ -42,8 +45,9 @@ def test_array_is_sequence(self):
def test_bad_constructor(self):
self.assertRaises(TypeError, array.array)
self.assertRaises(TypeError, array.array, spam=42)
self.assertRaises(TypeError, array.array, 'xx')
self.assertRaises(ValueError, array.array, 'xx')
self.assertRaises(ValueError, array.array, 'x')
self.assertRaises(ValueError, array.array, 'Z')

@support.cpython_only
def test_disallow_instantiation(self):
Expand Down Expand Up @@ -85,6 +89,12 @@ def __index__(self):
with self.assertRaises(TypeError):
a.fromlist(lst)

def test_typecodes(self):
self.assertIsInstance(array.typecodes, tuple)
for typecode in array.typecodes:
self.assertIsInstance(typecode, str)
self.assertGreaterEqual(len(typecode), 1)


# Machine format codes.
#
Expand Down Expand Up @@ -208,6 +218,14 @@ def test_numbers(self):
[9006104071832581.0j, float('inf'), complex('1-infj'), -0.0]),
(['D'], IEEE_754_DOUBLE_COMPLEX_BE, '>DDDD',
[9006104071832581.0j, float('inf'), complex('1-infj'), -0.0]),
(['Zf'], IEEE_754_FLOAT_COMPLEX_LE, '<ZfZfZfZf',
[16711938.0j, float('inf'), complex('1-infj'), -0.0]),
(['Zf'], IEEE_754_FLOAT_COMPLEX_BE, '>ZfZfZfZf',
[16711938.0j, float('inf'), complex('1-infj'), -0.0]),
(['Zd'], IEEE_754_DOUBLE_COMPLEX_LE, '<ZdZdZdZd',
[9006104071832581.0j, float('inf'), complex('1-infj'), -0.0]),
(['Zd'], IEEE_754_DOUBLE_COMPLEX_BE, '>ZdZdZdZd',
[9006104071832581.0j, float('inf'), complex('1-infj'), -0.0]),
)
for testcase in testcases:
valid_typecodes, mformat_code, struct_fmt, values = testcase
Expand Down Expand Up @@ -1237,6 +1255,9 @@ def test_free_after_iterating(self):
support.check_free_after_iterating(self, reversed, array.array,
(self.typecode,))

def test_known_typecode(self):
self.assertIn(self.typecode, array.typecodes)

class StringTest(BaseTest):

def test_setitem(self):
Expand Down Expand Up @@ -1576,7 +1597,7 @@ def test_nan(self):
def test_byteswap(self):
a = array.array(self.typecode, self.example)
self.assertRaises(TypeError, a.byteswap, 42)
if a.itemsize in (1, 2, 4, 8):
if a.itemsize in (1, 2, 4, 8, 16):
b = array.array(self.typecode, self.example)
b.byteswap()
if a.itemsize == 1:
Expand Down Expand Up @@ -1631,6 +1652,14 @@ class ComplexDoubleTest(CFPTest, unittest.TestCase):
typecode = 'D'
minitemsize = 16

class ComplexZfFloatTest(CFPTest, unittest.TestCase):
typecode = 'Zf'
minitemsize = 8

class ComplexZdDoubleTest(CFPTest, unittest.TestCase):
typecode = 'Zd'
minitemsize = 16


class LargeArrayTest(unittest.TestCase):
typecode = 'b'
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_ctypes/test_c_simple_type_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ class F(metaclass=PyCSimpleType):
if not MS_WINDOWS:
expected_type_chars.remove('X')
self.assertIn("'" + ''.join(expected_type_chars) + "'", message)
if hasattr(ctypes, 'c_float_complex'):
self.assertIn("'Zf', 'Zd', 'Zg'", message)

def test_creating_pointer_in_dunder_init_3(self):
"""Check if interfcase subclasses properly creates according internal
Expand Down
14 changes: 13 additions & 1 deletion Lib/test/test_ctypes/test_numbers.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,20 @@ def test_floats(self):
@unittest.skipUnless(hasattr(ctypes, "c_double_complex"),
"requires C11 complex type")
def test_complex(self):
class c_double_complex2(ctypes._SimpleCData):
_type_ = "Zd"
ctypes._check_size(c_double_complex2)

class c_float_complex2(ctypes._SimpleCData):
_type_ = "Zf"
ctypes._check_size(c_float_complex2)

class c_longdouble_complex2(ctypes._SimpleCData):
_type_ = "Zg"

for t in [ctypes.c_double_complex, ctypes.c_float_complex,
ctypes.c_longdouble_complex]:
ctypes.c_longdouble_complex,
c_double_complex2, c_float_complex2, c_longdouble_complex2]:
self.assertEqual(t(1).value, 1+0j)
self.assertEqual(t(1.0).value, 1+0j)
self.assertEqual(t(1+0.125j).value, 1+0.125j)
Expand Down
6 changes: 5 additions & 1 deletion Lib/test/test_memoryview.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ def check_equal(view, is_equal):
check_equal(m, True)

# Test complex formats
for complex_format in 'FD':
for complex_format in ('F', 'D', 'Zf', 'Zd'):
with self.subTest(format=complex_format):
data = struct.pack(complex_format * 3, 1.0, 2.0, float('nan'))
m = memoryview(data).cast(complex_format)
Expand Down Expand Up @@ -723,6 +723,10 @@ def test_complex_types(self):
double_complex_view = memoryview(double_complex_data).cast('D')
self.assertEqual(float_complex_view.nbytes * 2, double_complex_view.nbytes)
self.assertListEqual(float_complex_view.tolist(), double_complex_view.tolist())
float_complex_view = memoryview(float_complex_data).cast('Zf')
double_complex_view = memoryview(double_complex_data).cast('Zd')
self.assertEqual(float_complex_view.nbytes * 2, double_complex_view.nbytes)
self.assertListEqual(float_complex_view.tolist(), double_complex_view.tolist())

def test_memoryview_hex(self):
# Issue #9951: memoryview.hex() segfaults with non-contiguous buffers.
Expand Down
6 changes: 5 additions & 1 deletion Lib/test/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,11 @@ 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 ['F', 'D', '>F', '>D', '<F', '<D']:
for f in [
'F', 'D', 'Zf', 'Zd',
'>F', '>D', '>Zf', '>Zd',
'<F', '<D', '<Zf', '<Zd',
]:
with self.subTest(z=z, format=f):
round_trip = struct.unpack(f, struct.pack(f, z))[0]
self.assertComplexesAreIdentical(z, round_trip)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:mod:`array`, :mod:`ctypes`, :mod:`struct`: Add support for ``Zd`` and ``Zf``
format for double complex and float complex. Add also support for ``Zg``
format for long double complex to :mod:`ctypes`. Patch by Victor Stinner.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The :data:`array.typecodes` type changed from :class:`str` to :class:`tuple`
to support type codes longer than 1 character (``Zf`` and ``Zd``). Patch by
Victor Stinner.
31 changes: 18 additions & 13 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2315,7 +2315,6 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds)
{
PyObject *proto;
const char *proto_str;
Py_ssize_t proto_len;
PyMethodDef *ml;
struct fielddesc *fmt;

Expand All @@ -2333,27 +2332,33 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds)
return -1;
}
if (PyUnicode_Check(proto)) {
proto_str = PyUnicode_AsUTF8AndSize(proto, &proto_len);
proto_str = PyUnicode_AsUTF8(proto);
if (!proto_str)
goto error;
} else {
PyErr_SetString(PyExc_TypeError,
"class must define a '_type_' string attribute");
goto error;
}
if (proto_len != 1) {
PyErr_SetString(PyExc_ValueError,
"class must define a '_type_' attribute "
"which must be a string of length 1");
goto error;
}
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 the\n"
"supported types: '%s'.",
_ctypes_get_simple_type_chars());
const char *complex_formats = _ctypes_get_complex_type_formats();
if (complex_formats) {
PyErr_Format(PyExc_AttributeError,
"class must define a '_type_' attribute which must be\n"
"a single character string containing one of the\n"
"supported types: '%s', or one of these strings:\n"
"%s.",
_ctypes_get_simple_type_chars(),
complex_formats);
Comment thread
vstinner marked this conversation as resolved.
Resolved
}
else {
PyErr_Format(PyExc_AttributeError,
"class must define a '_type_' attribute which must be\n"
"a single character string containing one of the\n"
"supported types: '%s'.\n",
_ctypes_get_simple_type_chars());
}
Comment thread
vstinner marked this conversation as resolved.
Resolved
goto error;
}

Expand Down
6 changes: 3 additions & 3 deletions Modules/_ctypes/callproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -641,9 +641,9 @@ union result {
double d;
float f;
void *p;
double D[2];
float F[2];
long double G[2];
double Zd[2];
float Zf[2];
long double Zg[2];
};

struct argument {
Expand Down
Loading
Loading