Skip to content

Commit 131b466

Browse files
authored
Fix GetBuffer throwing ArgumentOutOfRangeException (#2120)
* fix: incorrect length for buffer copy * test: add PyBuffer test for strides * docs: update `CHANGELOG` and `AUTHORS`
1 parent a404d6e commit 131b466

File tree

4 files changed

+42
-4
lines changed

4 files changed

+42
-4
lines changed

AUTHORS.md

+1
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,4 @@
8585
- ([@alxnull](https://github.com/alxnull))
8686
- ([@gpetrou](https://github.com/gpetrou))
8787
- Ehsan Iran-Nejad ([@eirannejad](https://github.com/eirannejad))
88+
- ([@legomanww](https://github.com/legomanww))

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1717
- Make a second call to `pythonnet.load` a no-op, as it was intended.
1818

1919
- Added support for multiple inheritance when inheriting from a class and/or multiple interfaces.
20+
- Fixed error occuring when calling `GetBuffer` for anything other than `PyBUF.SIMPLE`
2021

2122
## [3.0.1](https://github.com/pythonnet/pythonnet/releases/tag/v3.0.1) - 2022-11-03
2223

src/embed_tests/TestPyBuffer.cs

+36
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,26 @@ public void Finalization()
110110
Assert.AreEqual(1, arr.Refcount);
111111
}
112112

113+
[Test]
114+
public void MultidimensionalNumPyArray()
115+
{
116+
var ndarray = np.arange(24).reshape(1,2,3,4).T;
117+
PyObject ndim = ndarray.ndim;
118+
PyObject shape = ndarray.shape;
119+
PyObject strides = ndarray.strides;
120+
PyObject contiguous = ndarray.flags["C_CONTIGUOUS"];
121+
122+
using PyBuffer buf = ndarray.GetBuffer(PyBUF.STRIDED);
123+
124+
Assert.Multiple(() =>
125+
{
126+
Assert.That(buf.Dimensions, Is.EqualTo(ndim.As<int>()));
127+
Assert.That(buf.Shape, Is.EqualTo(shape.As<long[]>()));
128+
Assert.That(buf.Strides, Is.EqualTo(strides.As<long[]>()));
129+
Assert.That(buf.IsContiguous(BufferOrderStyle.C), Is.EqualTo(contiguous.As<bool>()));
130+
});
131+
}
132+
113133
[MethodImpl(MethodImplOptions.NoInlining)]
114134
static void MakeBufAndLeak(PyObject bufProvider)
115135
{
@@ -121,5 +141,21 @@ static PyObject ByteArrayFromAsciiString(string str)
121141
using var scope = Py.CreateScope();
122142
return Runtime.Runtime.PyByteArray_FromStringAndSize(str).MoveToPyObject();
123143
}
144+
145+
dynamic np
146+
{
147+
get
148+
{
149+
try
150+
{
151+
return Py.Import("numpy");
152+
}
153+
catch (PythonException)
154+
{
155+
Assert.Inconclusive("Numpy or dependency not installed");
156+
return null;
157+
}
158+
}
159+
}
124160
}
125161
}

src/runtime/PythonTypes/PyBuffer.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public sealed class PyBuffer : IDisposable
1111
private PyObject _exporter;
1212
private Py_buffer _view;
1313

14-
unsafe internal PyBuffer(PyObject exporter, PyBUF flags)
14+
internal PyBuffer(PyObject exporter, PyBUF flags)
1515
{
1616
_view = new Py_buffer();
1717

@@ -25,17 +25,17 @@ unsafe internal PyBuffer(PyObject exporter, PyBUF flags)
2525
var intPtrBuf = new IntPtr[_view.ndim];
2626
if (_view.shape != IntPtr.Zero)
2727
{
28-
Marshal.Copy(_view.shape, intPtrBuf, 0, (int)_view.len * sizeof(IntPtr));
28+
Marshal.Copy(_view.shape, intPtrBuf, 0, _view.ndim);
2929
Shape = intPtrBuf.Select(x => (long)x).ToArray();
3030
}
3131

3232
if (_view.strides != IntPtr.Zero) {
33-
Marshal.Copy(_view.strides, intPtrBuf, 0, (int)_view.len * sizeof(IntPtr));
33+
Marshal.Copy(_view.strides, intPtrBuf, 0, _view.ndim);
3434
Strides = intPtrBuf.Select(x => (long)x).ToArray();
3535
}
3636

3737
if (_view.suboffsets != IntPtr.Zero) {
38-
Marshal.Copy(_view.suboffsets, intPtrBuf, 0, (int)_view.len * sizeof(IntPtr));
38+
Marshal.Copy(_view.suboffsets, intPtrBuf, 0, _view.ndim);
3939
SubOffsets = intPtrBuf.Select(x => (long)x).ToArray();
4040
}
4141
}

0 commit comments

Comments
 (0)