test_buffers.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. # -*- coding: utf-8 -*-
  2. import io
  3. import struct
  4. import ctypes
  5. import pytest
  6. import env # noqa: F401
  7. from pybind11_tests import buffers as m
  8. from pybind11_tests import ConstructorStats
  9. np = pytest.importorskip("numpy")
  10. def test_from_python():
  11. with pytest.raises(RuntimeError) as excinfo:
  12. m.Matrix(np.array([1, 2, 3])) # trying to assign a 1D array
  13. assert str(excinfo.value) == "Incompatible buffer format!"
  14. m3 = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32)
  15. m4 = m.Matrix(m3)
  16. for i in range(m4.rows()):
  17. for j in range(m4.cols()):
  18. assert m3[i, j] == m4[i, j]
  19. cstats = ConstructorStats.get(m.Matrix)
  20. assert cstats.alive() == 1
  21. del m3, m4
  22. assert cstats.alive() == 0
  23. assert cstats.values() == ["2x3 matrix"]
  24. assert cstats.copy_constructions == 0
  25. # assert cstats.move_constructions >= 0 # Don't invoke any
  26. assert cstats.copy_assignments == 0
  27. assert cstats.move_assignments == 0
  28. # https://foss.heptapod.net/pypy/pypy/-/issues/2444
  29. def test_to_python():
  30. mat = m.Matrix(5, 4)
  31. assert memoryview(mat).shape == (5, 4)
  32. assert mat[2, 3] == 0
  33. mat[2, 3] = 4.0
  34. mat[3, 2] = 7.0
  35. assert mat[2, 3] == 4
  36. assert mat[3, 2] == 7
  37. assert struct.unpack_from("f", mat, (3 * 4 + 2) * 4) == (7,)
  38. assert struct.unpack_from("f", mat, (2 * 4 + 3) * 4) == (4,)
  39. mat2 = np.array(mat, copy=False)
  40. assert mat2.shape == (5, 4)
  41. assert abs(mat2).sum() == 11
  42. assert mat2[2, 3] == 4 and mat2[3, 2] == 7
  43. mat2[2, 3] = 5
  44. assert mat2[2, 3] == 5
  45. cstats = ConstructorStats.get(m.Matrix)
  46. assert cstats.alive() == 1
  47. del mat
  48. pytest.gc_collect()
  49. assert cstats.alive() == 1
  50. del mat2 # holds a mat reference
  51. pytest.gc_collect()
  52. assert cstats.alive() == 0
  53. assert cstats.values() == ["5x4 matrix"]
  54. assert cstats.copy_constructions == 0
  55. # assert cstats.move_constructions >= 0 # Don't invoke any
  56. assert cstats.copy_assignments == 0
  57. assert cstats.move_assignments == 0
  58. def test_inherited_protocol():
  59. """SquareMatrix is derived from Matrix and inherits the buffer protocol"""
  60. matrix = m.SquareMatrix(5)
  61. assert memoryview(matrix).shape == (5, 5)
  62. assert np.asarray(matrix).shape == (5, 5)
  63. def test_pointer_to_member_fn():
  64. for cls in [m.Buffer, m.ConstBuffer, m.DerivedBuffer]:
  65. buf = cls()
  66. buf.value = 0x12345678
  67. value = struct.unpack("i", bytearray(buf))[0]
  68. assert value == 0x12345678
  69. def test_readonly_buffer():
  70. buf = m.BufferReadOnly(0x64)
  71. view = memoryview(buf)
  72. assert view[0] == b"d" if env.PY2 else 0x64
  73. assert view.readonly
  74. with pytest.raises(TypeError):
  75. view[0] = b"\0" if env.PY2 else 0
  76. def test_selective_readonly_buffer():
  77. buf = m.BufferReadOnlySelect()
  78. memoryview(buf)[0] = b"d" if env.PY2 else 0x64
  79. assert buf.value == 0x64
  80. io.BytesIO(b"A").readinto(buf)
  81. assert buf.value == ord(b"A")
  82. buf.readonly = True
  83. with pytest.raises(TypeError):
  84. memoryview(buf)[0] = b"\0" if env.PY2 else 0
  85. with pytest.raises(TypeError):
  86. io.BytesIO(b"1").readinto(buf)
  87. def test_ctypes_array_1d():
  88. char1d = (ctypes.c_char * 10)()
  89. int1d = (ctypes.c_int * 15)()
  90. long1d = (ctypes.c_long * 7)()
  91. for carray in (char1d, int1d, long1d):
  92. info = m.get_buffer_info(carray)
  93. assert info.itemsize == ctypes.sizeof(carray._type_)
  94. assert info.size == len(carray)
  95. assert info.ndim == 1
  96. assert info.shape == [info.size]
  97. assert info.strides == [info.itemsize]
  98. assert not info.readonly
  99. def test_ctypes_array_2d():
  100. char2d = ((ctypes.c_char * 10) * 4)()
  101. int2d = ((ctypes.c_int * 15) * 3)()
  102. long2d = ((ctypes.c_long * 7) * 2)()
  103. for carray in (char2d, int2d, long2d):
  104. info = m.get_buffer_info(carray)
  105. assert info.itemsize == ctypes.sizeof(carray[0]._type_)
  106. assert info.size == len(carray) * len(carray[0])
  107. assert info.ndim == 2
  108. assert info.shape == [len(carray), len(carray[0])]
  109. assert info.strides == [info.itemsize * len(carray[0]), info.itemsize]
  110. assert not info.readonly
  111. @pytest.mark.skipif(
  112. "env.PYPY and env.PY2", reason="PyPy2 bytes buffer not reported as readonly"
  113. )
  114. def test_ctypes_from_buffer():
  115. test_pystr = b"0123456789"
  116. for pyarray in (test_pystr, bytearray(test_pystr)):
  117. pyinfo = m.get_buffer_info(pyarray)
  118. if pyinfo.readonly:
  119. cbytes = (ctypes.c_char * len(pyarray)).from_buffer_copy(pyarray)
  120. cinfo = m.get_buffer_info(cbytes)
  121. else:
  122. cbytes = (ctypes.c_char * len(pyarray)).from_buffer(pyarray)
  123. cinfo = m.get_buffer_info(cbytes)
  124. assert cinfo.size == pyinfo.size
  125. assert cinfo.ndim == pyinfo.ndim
  126. assert cinfo.shape == pyinfo.shape
  127. assert cinfo.strides == pyinfo.strides
  128. assert not cinfo.readonly