Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit00aed52

Browse files
committed
implements buffer interface for .NET arrays of primitive types
fixeslosttech/Gradient#27
1 parent1e32d8c commit00aed52

File tree

9 files changed

+218
-3
lines changed

9 files changed

+218
-3
lines changed

‎CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1515
- Ability to implement delegates with`ref` and`out` parameters in Python, by returning the modified parameter values in a tuple. ([#1355][i1355])
1616
-`PyType` - a wrapper for Python type objects, that also permits creating new heap types from`TypeSpec`
1717
- Improved exception handling:
18-
- exceptions can now be converted with codecs
19-
-`InnerException` and`__cause__` are propagated properly
18+
- exceptions can now be converted with codecs
19+
-`InnerException` and`__cause__` are propagated properly
20+
- .NET arrays implement Python buffer protocol
21+
2022

2123
###Changed
2224
- Drop support for Python 2, 3.4, and 3.5

‎src/embed_tests/TestExample.csrenamed to‎src/embed_tests/NumPyTests.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22
usingSystem.Collections.Generic;
33
usingNUnit.Framework;
44
usingPython.Runtime;
5+
usingPython.Runtime.Codecs;
56

67
namespacePython.EmbeddingTest
78
{
8-
publicclassTestExample
9+
publicclassNumPyTests
910
{
1011
[OneTimeSetUp]
1112
publicvoidSetUp()
1213
{
1314
PythonEngine.Initialize();
15+
TupleCodec<ValueTuple>.Register();
1416
}
1517

1618
[OneTimeTearDown]
@@ -49,5 +51,23 @@ public void TestReadme()
4951

5052
Assert.AreEqual("[ 6. 10. 12.]",(a*b).ToString().Replace(" "," "));
5153
}
54+
55+
[Test]
56+
publicvoidMultidimensionalNumPyArray()
57+
{
58+
PyObjectnp;
59+
try{
60+
np=Py.Import("numpy");
61+
}catch(PythonException){
62+
Assert.Inconclusive("Numpy or dependency not installed");
63+
return;
64+
}
65+
66+
vararray=new[,]{{1,2},{3,4}};
67+
varndarray=np.InvokeMethod("asarray",array.ToPython());
68+
Assert.AreEqual((2,2),ndarray.GetAttr("shape").As<(int,int)>());
69+
Assert.AreEqual(1,ndarray[(0,0).ToPython()].InvokeMethod("__int__").As<int>());
70+
Assert.AreEqual(array[1,0],ndarray[(1,0).ToPython()].InvokeMethod("__int__").As<int>());
71+
}
5272
}
5373
}

‎src/embed_tests/TestPyBuffer.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
usingSystem;
12
usingSystem.Text;
23
usingNUnit.Framework;
34
usingPython.Runtime;
5+
usingPython.Runtime.Codecs;
46

57
namespacePython.EmbeddingTest{
68
classTestPyBuffer
@@ -9,6 +11,7 @@ class TestPyBuffer
911
publicvoidSetUp()
1012
{
1113
PythonEngine.Initialize();
14+
TupleCodec<ValueTuple>.Register();
1215
}
1316

1417
[OneTimeTearDown]
@@ -64,5 +67,15 @@ public void TestBufferRead()
6467
}
6568
}
6669
}
70+
71+
[Test]
72+
publicvoidArrayHasBuffer()
73+
{
74+
vararray=new[,]{{1,2},{3,4}};
75+
varmemoryView=PythonEngine.Eval("memoryview");
76+
varmem=memoryView.Invoke(array.ToPython());
77+
Assert.AreEqual(1,mem[(0,0).ToPython()].As<int>());
78+
Assert.AreEqual(array[1,0],mem[(1,0).ToPython()].As<int>());
79+
}
6780
}
6881
}

‎src/runtime/Util.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ internal static class Util
1010
internalconststringMinimalPythonVersionRequired=
1111
"Only Python 3.5 or newer is supported";
1212

13+
internalstaticboolCLongIs32Bit()=>Runtime.IsWindows||Runtime.Is32Bit;
14+
1315
internalstaticInt64ReadCLong(IntPtrtp,intoffset)
1416
{
1517
// On Windows, a C long is always 32 bits.

‎src/runtime/arrayobject.cs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
usingSystem;
22
usingSystem.Collections;
3+
usingSystem.Collections.Generic;
4+
usingSystem.Runtime.InteropServices;
35

46
namespacePython.Runtime
57
{
@@ -366,5 +368,165 @@ public static int sq_contains(IntPtr ob, IntPtr v)
366368

367369
return0;
368370
}
371+
372+
#region Buffer protocol
373+
staticintGetBuffer(BorrowedReferenceobj,outPy_bufferbuffer,PyBUFflags)
374+
{
375+
buffer=default;
376+
377+
if(flags==PyBUF.SIMPLE)
378+
{
379+
Exceptions.SetError(Exceptions.BufferError,"SIMPLE not implemented");
380+
return-1;
381+
}
382+
if((flags&PyBUF.F_CONTIGUOUS)==PyBUF.F_CONTIGUOUS)
383+
{
384+
Exceptions.SetError(Exceptions.BufferError,"only C-contiguous supported");
385+
return-1;
386+
}
387+
varself=(Array)((CLRObject)GetManagedObject(obj)).inst;
388+
TypeitemType=self.GetType().GetElementType();
389+
390+
boolformatRequested=(flags&PyBUF.FORMATS)!=0;
391+
stringformat=GetFormat(itemType);
392+
if(formatRequested&&formatisnull)
393+
{
394+
Exceptions.SetError(Exceptions.BufferError,"unsupported element type: "+itemType.Name);
395+
return-1;
396+
}
397+
GCHandlegcHandle;
398+
try
399+
{
400+
gcHandle=GCHandle.Alloc(self,GCHandleType.Pinned);
401+
}catch(ArgumentExceptionex)
402+
{
403+
Exceptions.SetError(Exceptions.BufferError,ex.Message);
404+
return-1;
405+
}
406+
407+
intitemSize=Marshal.SizeOf(itemType);
408+
IntPtr[]shape=GetShape(self);
409+
IntPtr[]strides=GetStrides(shape,itemSize);
410+
buffer=newPy_buffer
411+
{
412+
buf=gcHandle.AddrOfPinnedObject(),
413+
obj=Runtime.SelfIncRef(obj.DangerousGetAddress()),
414+
len=(IntPtr)(self.LongLength*itemSize),
415+
itemsize=(IntPtr)itemSize,
416+
_readonly=false,
417+
ndim=self.Rank,
418+
format=format,
419+
shape=ToUnmanaged(shape),
420+
strides=(flags&PyBUF.STRIDES)==PyBUF.STRIDES?ToUnmanaged(strides):IntPtr.Zero,
421+
suboffsets=IntPtr.Zero,
422+
_internal=(IntPtr)gcHandle,
423+
};
424+
425+
return0;
426+
}
427+
staticvoidReleaseBuffer(BorrowedReferenceobj,refPy_bufferbuffer)
428+
{
429+
if(buffer._internal==IntPtr.Zero)return;
430+
431+
UnmanagedFree(refbuffer.shape);
432+
UnmanagedFree(refbuffer.strides);
433+
UnmanagedFree(refbuffer.suboffsets);
434+
435+
vargcHandle=(GCHandle)buffer._internal;
436+
gcHandle.Free();
437+
buffer._internal=IntPtr.Zero;
438+
}
439+
440+
staticIntPtr[]GetStrides(IntPtr[]shape,longitemSize)
441+
{
442+
varresult=newIntPtr[shape.Length];
443+
result[shape.Length-1]=newIntPtr(itemSize);
444+
for(intdim=shape.Length-2;dim>=0;dim--)
445+
{
446+
itemSize*=shape[dim+1].ToInt64();
447+
result[dim]=newIntPtr(itemSize);
448+
}
449+
returnresult;
450+
}
451+
staticIntPtr[]GetShape(Arrayarray)
452+
{
453+
varresult=newIntPtr[array.Rank];
454+
for(inti=0;i<result.Length;i++)
455+
result[i]=(IntPtr)array.GetLongLength(i);
456+
returnresult;
457+
}
458+
459+
staticvoidUnmanagedFree(refIntPtraddress)
460+
{
461+
if(address==IntPtr.Zero)return;
462+
463+
Marshal.FreeHGlobal(address);
464+
address=IntPtr.Zero;
465+
}
466+
staticunsafeIntPtrToUnmanaged<T>(T[]array)whereT: unmanaged
467+
{
468+
IntPtrresult=Marshal.AllocHGlobal(checked(Marshal.SizeOf(typeof(T))*array.Length));
469+
fixed(T*ptr=array)
470+
{
471+
var@out=(T*)result;
472+
for(inti=0;i<array.Length;i++)
473+
@out[i]=ptr[i];
474+
}
475+
returnresult;
476+
}
477+
478+
staticreadonlyDictionary<Type,string>ItemFormats=newDictionary<Type,string>
479+
{
480+
[typeof(byte)]="B",
481+
[typeof(sbyte)]="b",
482+
483+
[typeof(bool)]="?",
484+
485+
[typeof(short)]="h",
486+
[typeof(ushort)]="H",
487+
[typeof(int)]=Util.CLongIs32Bit()?"l":"i",
488+
[typeof(uint)]=Util.CLongIs32Bit()?"L":"I",
489+
[typeof(long)]=Util.CLongIs32Bit()?"q":"l",
490+
[typeof(ulong)]=Util.CLongIs32Bit()?"Q":"L",
491+
492+
[typeof(IntPtr)]="n",
493+
[typeof(UIntPtr)]="N",
494+
495+
// TODO: half = "e"
496+
[typeof(float)]="f",
497+
[typeof(double)]="d",
498+
};
499+
500+
staticstringGetFormat(TypeelementType)
501+
=>ItemFormats.TryGetValue(elementType,outstringresult)?result:null;
502+
503+
staticreadonlyGetBufferProcgetBufferProc=GetBuffer;
504+
staticreadonlyReleaseBufferProcreleaseBufferProc=ReleaseBuffer;
505+
staticreadonlyIntPtrBufferProcsAddress=AllocateBufferProcs();
506+
staticIntPtrAllocateBufferProcs()
507+
{
508+
varprocs=newPyBufferProcs
509+
{
510+
Get=Marshal.GetFunctionPointerForDelegate(getBufferProc),
511+
Release=Marshal.GetFunctionPointerForDelegate(releaseBufferProc),
512+
};
513+
IntPtrresult=Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PyBufferProcs)));
514+
Marshal.StructureToPtr(procs,result,fDeleteOld:false);
515+
returnresult;
516+
}
517+
#endregion
518+
519+
/// <summary>
520+
/// <see cref="TypeManager.InitializeSlots(IntPtr, Type, SlotsHolder)"/>
521+
/// </summary>
522+
publicstaticvoidInitializeSlots(IntPtrtype,ISet<string>initialized,SlotsHolderslotsHolder)
523+
{
524+
if(initialized.Add(nameof(TypeOffset.tp_as_buffer)))
525+
{
526+
// TODO: only for unmanaged arrays
527+
intoffset=TypeOffset.GetSlotOffset(nameof(TypeOffset.tp_as_buffer));
528+
Marshal.WriteIntPtr(type,offset,BufferProcsAddress);
529+
}
530+
}
369531
}
370532
}

‎src/runtime/bufferinterface.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,15 @@ public enum PyBUF
103103
/// </summary>
104104
FULL_RO=(INDIRECT|FORMATS),
105105
}
106+
107+
internalstructPyBufferProcs
108+
{
109+
publicIntPtrGet;
110+
publicIntPtrRelease;
111+
}
112+
113+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
114+
delegateintGetBufferProc(BorrowedReferenceobj,outPy_bufferbuffer,PyBUFflags);
115+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
116+
delegatevoidReleaseBufferProc(BorrowedReferenceobj,refPy_bufferbuffer);
106117
}

‎src/runtime/exceptions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ public static variables on the Exceptions class filled in from
413413

414414
publicstaticIntPtrAssertionError;
415415
publicstaticIntPtrAttributeError;
416+
publicstaticIntPtrBufferError;
416417
publicstaticIntPtrEOFError;
417418
publicstaticIntPtrFloatingPointError;
418419
publicstaticIntPtrEnvironmentError;

‎src/runtime/native/TypeOffset.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ static void ValidateRequiredOffsetsPresent(PropertyInfo[] offsetProperties)
159159
"GetClrType",
160160
"getPreload",
161161
"Initialize",
162+
"InitializeSlots",
162163
"ListAssemblies",
163164
"_load_clr_module",
164165
"Release",

‎src/runtime/typemanager.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,9 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
745745
seen.Add(name);
746746
}
747747

748+
varinitSlot=impl.GetMethod("InitializeSlots",BindingFlags.Static|BindingFlags.Public);
749+
initSlot?.Invoke(null,parameters:newobject[]{type,seen,slotsHolder});
750+
748751
impl=impl.BaseType;
749752
}
750753

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp