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