_dtype_ctypes.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. """
  2. Conversion from ctypes to dtype.
  3. In an ideal world, we could achieve this through the PEP3118 buffer protocol,
  4. something like::
  5. def dtype_from_ctypes_type(t):
  6. # needed to ensure that the shape of `t` is within memoryview.format
  7. class DummyStruct(ctypes.Structure):
  8. _fields_ = [('a', t)]
  9. # empty to avoid memory allocation
  10. ctype_0 = (DummyStruct * 0)()
  11. mv = memoryview(ctype_0)
  12. # convert the struct, and slice back out the field
  13. return _dtype_from_pep3118(mv.format)['a']
  14. Unfortunately, this fails because:
  15. * ctypes cannot handle length-0 arrays with PEP3118 (bpo-32782)
  16. * PEP3118 cannot represent unions, but both numpy and ctypes can
  17. * ctypes cannot handle big-endian structs with PEP3118 (bpo-32780)
  18. """
  19. # We delay-import ctypes for distributions that do not include it.
  20. # While this module is not used unless the user passes in ctypes
  21. # members, it is eagerly imported from numpy/core/__init__.py.
  22. import numpy as np
  23. def _from_ctypes_array(t):
  24. return np.dtype((dtype_from_ctypes_type(t._type_), (t._length_,)))
  25. def _from_ctypes_structure(t):
  26. for item in t._fields_:
  27. if len(item) > 2:
  28. raise TypeError(
  29. "ctypes bitfields have no dtype equivalent")
  30. if hasattr(t, "_pack_"):
  31. import ctypes
  32. formats = []
  33. offsets = []
  34. names = []
  35. current_offset = 0
  36. for fname, ftyp in t._fields_:
  37. names.append(fname)
  38. formats.append(dtype_from_ctypes_type(ftyp))
  39. # Each type has a default offset, this is platform dependent for some types.
  40. effective_pack = min(t._pack_, ctypes.alignment(ftyp))
  41. current_offset = ((current_offset + effective_pack - 1) // effective_pack) * effective_pack
  42. offsets.append(current_offset)
  43. current_offset += ctypes.sizeof(ftyp)
  44. return np.dtype(dict(
  45. formats=formats,
  46. offsets=offsets,
  47. names=names,
  48. itemsize=ctypes.sizeof(t)))
  49. else:
  50. fields = []
  51. for fname, ftyp in t._fields_:
  52. fields.append((fname, dtype_from_ctypes_type(ftyp)))
  53. # by default, ctypes structs are aligned
  54. return np.dtype(fields, align=True)
  55. def _from_ctypes_scalar(t):
  56. """
  57. Return the dtype type with endianness included if it's the case
  58. """
  59. if getattr(t, '__ctype_be__', None) is t:
  60. return np.dtype('>' + t._type_)
  61. elif getattr(t, '__ctype_le__', None) is t:
  62. return np.dtype('<' + t._type_)
  63. else:
  64. return np.dtype(t._type_)
  65. def _from_ctypes_union(t):
  66. import ctypes
  67. formats = []
  68. offsets = []
  69. names = []
  70. for fname, ftyp in t._fields_:
  71. names.append(fname)
  72. formats.append(dtype_from_ctypes_type(ftyp))
  73. offsets.append(0) # Union fields are offset to 0
  74. return np.dtype(dict(
  75. formats=formats,
  76. offsets=offsets,
  77. names=names,
  78. itemsize=ctypes.sizeof(t)))
  79. def dtype_from_ctypes_type(t):
  80. """
  81. Construct a dtype object from a ctypes type
  82. """
  83. import _ctypes
  84. if issubclass(t, _ctypes.Array):
  85. return _from_ctypes_array(t)
  86. elif issubclass(t, _ctypes._Pointer):
  87. raise TypeError("ctypes pointers have no dtype equivalent")
  88. elif issubclass(t, _ctypes.Structure):
  89. return _from_ctypes_structure(t)
  90. elif issubclass(t, _ctypes.Union):
  91. return _from_ctypes_union(t)
  92. elif isinstance(getattr(t, '_type_', None), str):
  93. return _from_ctypes_scalar(t)
  94. else:
  95. raise NotImplementedError(
  96. "Unknown ctypes type {}".format(t.__name__))