Sebastian Koslowski | dc65d66 | 2019-11-24 08:33:05 +0100 | [diff] [blame] | 1 | import io |
Bruce Merry | fe0cf8b | 2017-05-17 10:52:33 +0200 | [diff] [blame] | 2 | import struct |
Sebastian Koslowski | dc65d66 | 2019-11-24 08:33:05 +0100 | [diff] [blame] | 3 | import sys |
| 4 | |
Dean Moldovan | a0c1ccf | 2016-08-12 13:50:00 +0200 | [diff] [blame] | 5 | import pytest |
Sebastian Koslowski | dc65d66 | 2019-11-24 08:33:05 +0100 | [diff] [blame] | 6 | |
Jason Rhinelander | 391c754 | 2017-07-25 16:47:36 -0400 | [diff] [blame] | 7 | from pybind11_tests import buffers as m |
| 8 | from pybind11_tests import ConstructorStats |
Dean Moldovan | a0c1ccf | 2016-08-12 13:50:00 +0200 | [diff] [blame] | 9 | |
Sebastian Koslowski | dc65d66 | 2019-11-24 08:33:05 +0100 | [diff] [blame] | 10 | PY3 = sys.version_info[0] >= 3 |
| 11 | |
Dean Moldovan | d47febc | 2017-03-10 15:42:42 +0100 | [diff] [blame] | 12 | pytestmark = pytest.requires_numpy |
| 13 | |
Dean Moldovan | a0c1ccf | 2016-08-12 13:50:00 +0200 | [diff] [blame] | 14 | with pytest.suppress(ImportError): |
| 15 | import numpy as np |
| 16 | |
| 17 | |
Dean Moldovan | a0c1ccf | 2016-08-12 13:50:00 +0200 | [diff] [blame] | 18 | def test_from_python(): |
| 19 | with pytest.raises(RuntimeError) as excinfo: |
Jason Rhinelander | 391c754 | 2017-07-25 16:47:36 -0400 | [diff] [blame] | 20 | m.Matrix(np.array([1, 2, 3])) # trying to assign a 1D array |
Dean Moldovan | a0c1ccf | 2016-08-12 13:50:00 +0200 | [diff] [blame] | 21 | assert str(excinfo.value) == "Incompatible buffer format!" |
| 22 | |
| 23 | m3 = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32) |
Jason Rhinelander | 391c754 | 2017-07-25 16:47:36 -0400 | [diff] [blame] | 24 | m4 = m.Matrix(m3) |
Dean Moldovan | a0c1ccf | 2016-08-12 13:50:00 +0200 | [diff] [blame] | 25 | |
| 26 | for i in range(m4.rows()): |
| 27 | for j in range(m4.cols()): |
| 28 | assert m3[i, j] == m4[i, j] |
| 29 | |
Jason Rhinelander | 391c754 | 2017-07-25 16:47:36 -0400 | [diff] [blame] | 30 | cstats = ConstructorStats.get(m.Matrix) |
Dean Moldovan | a0c1ccf | 2016-08-12 13:50:00 +0200 | [diff] [blame] | 31 | assert cstats.alive() == 1 |
| 32 | del m3, m4 |
| 33 | assert cstats.alive() == 0 |
| 34 | assert cstats.values() == ["2x3 matrix"] |
| 35 | assert cstats.copy_constructions == 0 |
| 36 | # assert cstats.move_constructions >= 0 # Don't invoke any |
| 37 | assert cstats.copy_assignments == 0 |
| 38 | assert cstats.move_assignments == 0 |
Wenzel Jakob | 1d1f81b | 2016-12-16 15:00:46 +0100 | [diff] [blame] | 39 | |
| 40 | |
| 41 | # PyPy: Memory leak in the "np.array(m, copy=False)" call |
| 42 | # https://bitbucket.org/pypy/pypy/issues/2444 |
| 43 | @pytest.unsupported_on_pypy |
Wenzel Jakob | 1d1f81b | 2016-12-16 15:00:46 +0100 | [diff] [blame] | 44 | def test_to_python(): |
Ansgar Burchardt | a22dd2d | 2017-09-21 23:07:48 +0200 | [diff] [blame] | 45 | mat = m.Matrix(5, 4) |
| 46 | assert memoryview(mat).shape == (5, 4) |
Wenzel Jakob | 1d1f81b | 2016-12-16 15:00:46 +0100 | [diff] [blame] | 47 | |
Jason Rhinelander | 391c754 | 2017-07-25 16:47:36 -0400 | [diff] [blame] | 48 | assert mat[2, 3] == 0 |
Ansgar Burchardt | a22dd2d | 2017-09-21 23:07:48 +0200 | [diff] [blame] | 49 | mat[2, 3] = 4.0 |
| 50 | mat[3, 2] = 7.0 |
Jason Rhinelander | 391c754 | 2017-07-25 16:47:36 -0400 | [diff] [blame] | 51 | assert mat[2, 3] == 4 |
Ansgar Burchardt | a22dd2d | 2017-09-21 23:07:48 +0200 | [diff] [blame] | 52 | assert mat[3, 2] == 7 |
| 53 | assert struct.unpack_from('f', mat, (3 * 4 + 2) * 4) == (7, ) |
| 54 | assert struct.unpack_from('f', mat, (2 * 4 + 3) * 4) == (4, ) |
Wenzel Jakob | 1d1f81b | 2016-12-16 15:00:46 +0100 | [diff] [blame] | 55 | |
Jason Rhinelander | 391c754 | 2017-07-25 16:47:36 -0400 | [diff] [blame] | 56 | mat2 = np.array(mat, copy=False) |
Ansgar Burchardt | a22dd2d | 2017-09-21 23:07:48 +0200 | [diff] [blame] | 57 | assert mat2.shape == (5, 4) |
| 58 | assert abs(mat2).sum() == 11 |
| 59 | assert mat2[2, 3] == 4 and mat2[3, 2] == 7 |
Jason Rhinelander | 391c754 | 2017-07-25 16:47:36 -0400 | [diff] [blame] | 60 | mat2[2, 3] = 5 |
| 61 | assert mat2[2, 3] == 5 |
Wenzel Jakob | 1d1f81b | 2016-12-16 15:00:46 +0100 | [diff] [blame] | 62 | |
Jason Rhinelander | 391c754 | 2017-07-25 16:47:36 -0400 | [diff] [blame] | 63 | cstats = ConstructorStats.get(m.Matrix) |
Wenzel Jakob | 1d1f81b | 2016-12-16 15:00:46 +0100 | [diff] [blame] | 64 | assert cstats.alive() == 1 |
Jason Rhinelander | 391c754 | 2017-07-25 16:47:36 -0400 | [diff] [blame] | 65 | del mat |
Wenzel Jakob | 1d1f81b | 2016-12-16 15:00:46 +0100 | [diff] [blame] | 66 | pytest.gc_collect() |
| 67 | assert cstats.alive() == 1 |
Jason Rhinelander | 391c754 | 2017-07-25 16:47:36 -0400 | [diff] [blame] | 68 | del mat2 # holds a mat reference |
Wenzel Jakob | 1d1f81b | 2016-12-16 15:00:46 +0100 | [diff] [blame] | 69 | pytest.gc_collect() |
| 70 | assert cstats.alive() == 0 |
Ansgar Burchardt | a22dd2d | 2017-09-21 23:07:48 +0200 | [diff] [blame] | 71 | assert cstats.values() == ["5x4 matrix"] |
Wenzel Jakob | 1d1f81b | 2016-12-16 15:00:46 +0100 | [diff] [blame] | 72 | assert cstats.copy_constructions == 0 |
| 73 | # assert cstats.move_constructions >= 0 # Don't invoke any |
| 74 | assert cstats.copy_assignments == 0 |
| 75 | assert cstats.move_assignments == 0 |
Bruce Merry | fe0cf8b | 2017-05-17 10:52:33 +0200 | [diff] [blame] | 76 | |
| 77 | |
| 78 | @pytest.unsupported_on_pypy |
Dean Moldovan | 427e4af | 2017-05-28 16:35:02 +0200 | [diff] [blame] | 79 | def test_inherited_protocol(): |
| 80 | """SquareMatrix is derived from Matrix and inherits the buffer protocol""" |
Dean Moldovan | 427e4af | 2017-05-28 16:35:02 +0200 | [diff] [blame] | 81 | |
Jason Rhinelander | 391c754 | 2017-07-25 16:47:36 -0400 | [diff] [blame] | 82 | matrix = m.SquareMatrix(5) |
Dean Moldovan | 427e4af | 2017-05-28 16:35:02 +0200 | [diff] [blame] | 83 | assert memoryview(matrix).shape == (5, 5) |
| 84 | assert np.asarray(matrix).shape == (5, 5) |
| 85 | |
| 86 | |
| 87 | @pytest.unsupported_on_pypy |
Jason Rhinelander | 391c754 | 2017-07-25 16:47:36 -0400 | [diff] [blame] | 88 | def test_pointer_to_member_fn(): |
| 89 | for cls in [m.Buffer, m.ConstBuffer, m.DerivedBuffer]: |
Bruce Merry | fe0cf8b | 2017-05-17 10:52:33 +0200 | [diff] [blame] | 90 | buf = cls() |
| 91 | buf.value = 0x12345678 |
| 92 | value = struct.unpack('i', bytearray(buf))[0] |
| 93 | assert value == 0x12345678 |
Sebastian Koslowski | dc65d66 | 2019-11-24 08:33:05 +0100 | [diff] [blame] | 94 | |
| 95 | |
| 96 | @pytest.unsupported_on_pypy |
| 97 | def test_readonly_buffer(): |
| 98 | buf = m.BufferReadOnly(0x64) |
| 99 | view = memoryview(buf) |
| 100 | assert view[0] == 0x64 if PY3 else b'd' |
| 101 | assert view.readonly |
| 102 | |
| 103 | |
| 104 | @pytest.unsupported_on_pypy |
| 105 | def test_selective_readonly_buffer(): |
| 106 | buf = m.BufferReadOnlySelect() |
| 107 | |
| 108 | memoryview(buf)[0] = 0x64 if PY3 else b'd' |
| 109 | assert buf.value == 0x64 |
| 110 | |
| 111 | io.BytesIO(b'A').readinto(buf) |
| 112 | assert buf.value == ord(b'A') |
| 113 | |
| 114 | buf.readonly = True |
| 115 | with pytest.raises(TypeError): |
| 116 | memoryview(buf)[0] = 0 if PY3 else b'\0' |
| 117 | with pytest.raises(TypeError): |
| 118 | io.BytesIO(b'1').readinto(buf) |