Choice of complex buffer protocol format intentional break with PEP?

Hi Sebastian,

Thanks for raising this issue here.

D (double complex) and F (float complex) format were introduced in Python 3.14 which was released on October 2025, so IMO it’s too late to change the chosen format strings.

  • Python 3.14 added D and F support to the ctypes and struct module
  • Python 3.15 adds D and F support to the array module and to memoryview

Before the 3.14 final release, ctypes and struct used C (double complex) and E (float complex) formats. These formats were replaced with D and F for compatibility with numpy:

$ python3.14
>>> import numpy as np
>>> np.__version__
'2.3.5'

>>> np.typename("F")
'complex single precision'
>>> np.typename("D")
'complex double precision'

>>> np.dtype(np.complex64).char
'F'
>>> np.dtype(np.complex128).char
'D'

>>> np.array([1, 2, 3], dtype='F')
array([1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64)
>>> np.array([1, 2, 3], dtype='D')
array([1.+0.j, 2.+0.j, 3.+0.j])

>>> memoryview(np.array([1, 2, 3], dtype='F')).format
'Zf'
>>> memoryview(np.array([1, 2, 3], dtype='D')).format
'Zd'

As Sergey wrote above, in the bfloat16 format discussion, Serhiy proposed adding an API to register custom types:

It may be a solution to D vs Zd formats, maybe both can be accepted transparently.

PEP 3118 was written in 2006. It now has the header: “This PEP is a historical document”. I don’t think that using this old PEP is still relevant nowadays. Maybe numpy should reconsider its error message on unknown format.

Yeah, it’s unfortunate that numpy and Python stdlib (array, ctypes, struct and memoryview) are using different formats. But it’s maybe the opportunity to provide a generic solution to this kind of problem (API to register formats).

It’s also unfortunate that this issue wasn’t raised before, but as I wrote above, IMO it’s too late to change Python stdlib. By the way, currently the stdlib only use single letter formats which make some parts of the code simpler.

5 Likes