From 48bb33a583b47af5378f60bf9ba244f6d693d670 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 28 Sep 2026 12:52:35 +0200 Subject: [PATCH 1/2] Add test for `PyBuffer.GetPointer` --- src/embed_tests/TestPyBuffer.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/embed_tests/TestPyBuffer.cs b/src/embed_tests/TestPyBuffer.cs index 2239b6eab..2a173fc97 100644 --- a/src/embed_tests/TestPyBuffer.cs +++ b/src/embed_tests/TestPyBuffer.cs @@ -58,7 +58,22 @@ public void TestBufferRead() } string result = new UTF8Encoding().GetString(managedArray); - Assert.That(result == " " + bufferTestString.Substring(1), Is.True); + Assert.That(result, Is.EqualTo($" {bufferTestString.Substring(1)}")); + } + + [Test] + public void GetPointer() + { + // create 3D NumPy array + int[] shape = [10, 10, 10]; + using PyObject ndArray = np.zeros(shape); + + using PyBuffer buf = ndArray.GetBuffer(PyBUF.STRIDES); + + // this throws System.ExecutionEngineException + var ptr = buf.GetPointer([0, 0, 0]); + + Assert.That(ptr, Is.Not.EqualTo(IntPtr.Zero)); } [Test] From ea384d9c5f1d2f370363f299d6a1328de08befcb Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 28 Sep 2026 16:22:40 +0200 Subject: [PATCH 2/2] Add more checks and tests --- src/embed_tests/TestPyBuffer.cs | 32 +++++++++++++++++++++++++---- src/runtime/PythonTypes/PyBuffer.cs | 29 +++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/embed_tests/TestPyBuffer.cs b/src/embed_tests/TestPyBuffer.cs index 2a173fc97..3835f2f89 100644 --- a/src/embed_tests/TestPyBuffer.cs +++ b/src/embed_tests/TestPyBuffer.cs @@ -2,11 +2,14 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading; + using NUnit.Framework; + using Python.Runtime; using Python.Runtime.Codecs; -namespace Python.EmbeddingTest { +namespace Python.EmbeddingTest +{ class TestPyBuffer { [OneTimeSetUp] @@ -70,16 +73,37 @@ public void GetPointer() using PyBuffer buf = ndArray.GetBuffer(PyBUF.STRIDES); - // this throws System.ExecutionEngineException var ptr = buf.GetPointer([0, 0, 0]); Assert.That(ptr, Is.Not.EqualTo(IntPtr.Zero)); + + var ptrEnd = buf.GetPointer([9, 9, 9]); + Assert.That(ptrEnd, Is.Not.EqualTo(IntPtr.Zero)); + + Assert.Throws(() => buf.GetPointer(null!)); + + Assert.Throws(() => buf.GetPointer([0, 0])); + Assert.Throws(() => buf.GetPointer([0, 0, 0, 0])); + + Assert.Throws(() => buf.GetPointer([-1, 0, 0])); + Assert.Throws(() => buf.GetPointer([0, -1, 0])); + Assert.Throws(() => buf.GetPointer([0, 0, -1])); + + Assert.Throws(() => buf.GetPointer([10, 0, 0])); + Assert.Throws(() => buf.GetPointer([0, 10, 0])); + Assert.Throws(() => buf.GetPointer([0, 0, 10])); + + using PyBuffer buf2 = ndArray.GetBuffer(PyBUF.ND); + + Assert.Throws( + () => buf2.GetPointer([0, 0, 0]) + ); } [Test] public void ArrayHasBuffer() { - var array = new[,] {{1, 2}, {3,4}}; + var array = new[,] { { 1, 2 }, { 3, 4 } }; var memoryView = PythonEngine.Eval("memoryview"); var mem = memoryView.Invoke(array.ToPython()); Assert.That(mem[(0, 0).ToPython()].As(), Is.EqualTo(1)); @@ -128,7 +152,7 @@ public void Finalization() [Test] public void MultidimensionalNumPyArray() { - var ndarray = np.arange(24).reshape(1,2,3,4).T; + var ndarray = np.arange(24).reshape(1, 2, 3, 4).T; PyObject ndim = ndarray.ndim; PyObject shape = ndarray.shape; PyObject strides = ndarray.strides; diff --git a/src/runtime/PythonTypes/PyBuffer.cs b/src/runtime/PythonTypes/PyBuffer.cs index 7c1253f5e..3bf9d1539 100644 --- a/src/runtime/PythonTypes/PyBuffer.cs +++ b/src/runtime/PythonTypes/PyBuffer.cs @@ -110,10 +110,37 @@ public bool IsContiguous(BufferOrderStyle order) /// public IntPtr GetPointer(long[] indices) { - if (indices is null) throw new ArgumentNullException(nameof(indices)); + if (indices is null) + throw new ArgumentNullException(nameof(indices)); + + if (Strides is null) + throw new InvalidOperationException($"{nameof(GetPointer)} requires {nameof(Strides)} to be filled"); + + if (indices.Length != Dimensions) + throw new ArgumentOutOfRangeException($"{nameof(indices)} must be exactly {Dimensions} long"); + + if (Dimensions == 0 && Shape is null) + throw new InvalidOperationException($"{nameof(GetPointer)} requires {nameof(Shape)} to be filled for dimensional buffers"); + ThrowIfDisposed(); + if (Runtime.PyVersion < new Version(3, 7)) throw new NotSupportedException("GetPointer requires at least Python 3.7"); + + var indicesArr = new nint[Dimensions]; + + for (var i = 0; i < Dimensions; i++) + { + indicesArr[i] = checked((nint)indices[i]); + + if (indicesArr[i] < 0 || indicesArr[i] >= Shape![i]) + { + throw new ArgumentOutOfRangeException( + $"Index {i} has a value of {indicesArr[i]} not in [0, {Shape![i]})" + ); + } + } + return Runtime.PyBuffer_GetPointer(ref _view, indices.Select(x => checked((nint)x)).ToArray()); }