Skip to content

ENH: Support importing complex arrays from standard library #31204

Closed

Description

@hpkfft

Proposed new feature or change:

Python 3.15 adds support for complex arrays. It would be nice for NumPy to support importing these via the buffer protocol. Currently:

>>> import array
>>> import numpy as np
>>> a = array.array('D', [1.0+2.0j, 3.0+4.0j])
>>> a
array('D', [(1+2j), (3+4j)])
>>> np.array(a)
Traceback (most recent call last):
  File "/usr/local/lib/python3.15/site-packages/numpy/_core/_internal.py", line 629, in _dtype_from_pep3118
    dtype, align = __dtype_from_pep3118(stream, is_subdtype=False)
                   ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.15/site-packages/numpy/_core/_internal.py", line 705, in __dtype_from_pep3118
    raise ValueError(
        f"Unknown PEP 3118 data type specifier {stream.s!r}"
    )
ValueError: Unknown PEP 3118 data type specifier 'D'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<python-input-4>", line 1, in <module>
    np.array(a)
    ~~~~~~~~^^^
ValueError: 'D' is not a valid PEP 3118 buffer format string

Activity

  1. jorenham commented on Apr 10, 2026

    @jorenham
    Member

    Related: #28360

  2. hpkfft commented on Apr 12, 2026

    @hpkfft
    ContributorAuthor

    Note that for exporting from NumPy using the buffer protocol, "Zd" is specified rather than "D".

    case NPY_CFLOAT: if (_append_str(str, "Zf") < 0) return -1; break;
    case NPY_CDOUBLE: if (_append_str(str, "Zd") < 0) return -1; break;
    case NPY_CLONGDOUBLE: if (_append_str(str, "Zg") < 0) return -1; break;

  3. seberg commented on Apr 12, 2026

    @seberg
    Member

    Hummm, yeah, I guess since Python added those to the struct module it makes sense to support them at import (even if that means two names).

    We cannot change the export, Python is the one that is deviating from the buffer protocol PEP, even if that part never made it to the docs: https://peps.python.org/pep-3118/#additions-to-the-struct-string-syntax

  4. hpkfft commented on Apr 12, 2026

    @hpkfft
    ContributorAuthor

    Yeah, in retrospect it seems it was unwise for Python to reject the "additions to the struct syntax" part of the PEP without providing a syntax for complex numbers....

  5. seberg commented on Apr 12, 2026

    @seberg
    Member

    The PEP states clearly the Zd syntax (at the time D didn't exist after all)? And it doesn't seem so terrible...
    Did Python notice that it breaks with that precedence or did this just sneak in as following the struct module?

    Not that I am sure it is a big issue, importing one can certainly add trivially. But exporting we can't change unless everyone else understands D (unless we force everyone to switch over for 3.15? Doesn't seem great either.).

  6. hpkfft commented on Apr 12, 2026

    @hpkfft
    ContributorAuthor

    Python would argue that a PEP is just a proposal, and the Z prefix was deliberately rejected. The PEP link you gave above is to a historical document, not to canonical documentation. (Hey, don't shoot the messenger! I'm not saying NumPy was wrong to adopt the Z prefix--it was the only thing that had been suggested, so why invent some other syntax?)

    The struct module added support for the 'F' and 'D' formats in Python 3.14. Also, it supports 'e' since Python 3.6.
    The array module adds support for 'F' and 'D' in Python 3.15. Also, Python 3.15 adds half-precision 'e'.
    The ctypes module supports 'F', 'D', 'G'. I've seen discussion on adding 'e' and saw no objections--it's just a matter of time to get it done.

  7. seberg commented on Apr 12, 2026

    @seberg
    Member

    I would say it is canonical and NumPy is more of a canonical implementation for it than Python itself. I have no problem accepting to override the PEP, but the PRs don't actually discuss any of it, so right now it isn't clear to me "their" opinion is (i.e. I am not even sure anyone has an opinion on it yet).

    I asked on the PR, but should probably escalate to an issue just to clarify things.

  8. jorenham commented on Apr 12, 2026

    @jorenham
    Member

    I just ran into a related CPython bug: python/cpython#148464

  9. hpkfft commented on Apr 12, 2026

    @hpkfft
    ContributorAuthor

    Regarding PEP 3118, see also: python/peps#4200

    As you know, there's also a Python discussion about the buffer protocol and arbitrary types

  10. skirpichev commented on Apr 13, 2026

    @skirpichev

    I asked on the PR

    JFR, here this conversation: python/cpython#146237 (comment)

    I asked on the PR, but should probably escalate to an issue just to clarify things.

    I don't think that issue is an appropriate format right now. Better to discuss this first on d.p.o with a wider audience.

    For me it seems, that this part of the PEP wasn't discussed during it's review. That's why it's not a specification at all, even for complex types alone.

  11. added this to the 2.5.0 Release milestone on Apr 14, 2026
  12. hpkfft commented on May 8, 2026

    @hpkfft
    ContributorAuthor

    In Python 3.15.0b1, NumPy can both import and export Python standard library arrays:

    >>> a = array.array('Zd', [1.0+2.0j, 3.0+4.0j])
    >>> np.array(a)
    array([1.+2.j, 3.+4.j])
    
    >>> b = np.array([5.0+6.0j, 7.0+8.0j], dtype=np.complex128)
    >>> array.array('Zd', b)
    array('Zd', [(5+6j), (7+8j)])
    

    So, I will close this enhancement request. (Feel free to reopen if I'm missing something.)

  13. seberg commented on May 8, 2026

    @seberg
    Member

    Since you got it running, does it also work for:

    >>> a = array.array('D', [1.0+2.0j, 3.0+4.0j])
    >>> np.array(a)
    

    my impression from the PR was it doesn't.

    But yeah... I guess it is settled to the point where we probably don't have to do anything.

  14. hpkfft commented on May 8, 2026

    @hpkfft
    ContributorAuthor

    You are correct; the Python standard library array does not support 'D':

    >>> import array
    >>> a = array.array('D', [1.0+2.0j, 3.0+4.0j])
    Traceback (most recent call last):
      File "<python-input-1>", line 1, in <module>
        a = array.array('D', [1.0+2.0j, 3.0+4.0j])
    ValueError: bad typecode (must be b, B, u, w, h, H, i, I, l, L, q, Q, e, f, d, Zf or Zd)
    

    See also: #28360 (comment)

  15. seberg commented on May 8, 2026

    @seberg
    Member

    Ah, OK, sorry. At some point it had looked like they support both side by side, and I thought that would go into the buffer protocol. Nvm, then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions