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

Commit3dff8b5

Browse files
committed
enable expanding set of marshaling conversions via PyObjectConversions
added sample TupleCodec (only supporting ValueTuple)
1 parentf0e9c38 commit3dff8b5

File tree

10 files changed

+446
-8
lines changed

10 files changed

+446
-8
lines changed

‎src/embed_tests/Codecs.cs‎

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
namespacePython.EmbeddingTest{
2+
usingSystem;
3+
usingSystem.Collections.Generic;
4+
usingSystem.Text;
5+
usingNUnit.Framework;
6+
usingPython.Runtime;
7+
usingPython.Runtime.Codecs;
8+
9+
publicclassCodecs{
10+
[SetUp]
11+
publicvoidSetUp(){
12+
PythonEngine.Initialize();
13+
}
14+
15+
[TearDown]
16+
publicvoidDispose(){
17+
PythonEngine.Shutdown();
18+
}
19+
20+
[Test]
21+
publicvoidConversionsGeneric(){
22+
ConversionsGeneric<ValueTuple<int,string,object>,ValueTuple>();
23+
}
24+
25+
staticvoidConversionsGeneric<T,TTuple>(){
26+
TupleCodec<TTuple>.Register();
27+
vartuple=Activator.CreateInstance(typeof(T),42,"42",newobject());
28+
Trestored=default;
29+
using(Py.GIL())
30+
using(varscope=Py.CreateScope()){
31+
voidAccept(Tvalue)=>restored=value;
32+
varaccept=newAction<T>(Accept).ToPython();
33+
scope.Set(nameof(tuple),tuple);
34+
scope.Set(nameof(accept),accept);
35+
scope.Exec($"{nameof(accept)}({nameof(tuple)})");
36+
Assert.AreEqual(expected:tuple,actual:restored);
37+
}
38+
}
39+
40+
[Test]
41+
publicvoidConversionsObject(){
42+
ConversionsObject<ValueTuple<int,string,object>,ValueTuple>();
43+
}
44+
staticvoidConversionsObject<T,TTuple>(){
45+
TupleCodec<TTuple>.Register();
46+
vartuple=Activator.CreateInstance(typeof(T),42,"42",newobject());
47+
Trestored=default;
48+
using(Py.GIL())
49+
using(varscope=Py.CreateScope()){
50+
voidAccept(objectvalue)=>restored=(T)value;
51+
varaccept=newAction<object>(Accept).ToPython();
52+
scope.Set(nameof(tuple),tuple);
53+
scope.Set(nameof(accept),accept);
54+
scope.Exec($"{nameof(accept)}({nameof(tuple)})");
55+
Assert.AreEqual(expected:tuple,actual:restored);
56+
}
57+
}
58+
59+
[Test]
60+
publicvoidTupleRoundtripObject(){
61+
TupleRoundtripObject<ValueTuple<int,string,object>,ValueTuple>();
62+
}
63+
staticvoidTupleRoundtripObject<T,TTuple>(){
64+
vartuple=Activator.CreateInstance(typeof(T),42,"42",newobject());
65+
using(Py.GIL()){
66+
varpyTuple=TupleCodec<TTuple>.Instance.TryEncode(tuple);
67+
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple,outobjectrestored));
68+
Assert.AreEqual(expected:tuple,actual:restored);
69+
}
70+
}
71+
72+
[Test]
73+
publicvoidTupleRoundtripGeneric(){
74+
TupleRoundtripGeneric<ValueTuple<int,string,object>,ValueTuple>();
75+
}
76+
77+
staticvoidTupleRoundtripGeneric<T,TTuple>(){
78+
vartuple=Activator.CreateInstance(typeof(T),42,"42",newobject());
79+
using(Py.GIL()){
80+
varpyTuple=TupleCodec<TTuple>.Instance.TryEncode(tuple);
81+
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple,outTrestored));
82+
Assert.AreEqual(expected:tuple,actual:restored);
83+
}
84+
}
85+
}
86+
}

‎src/embed_tests/Python.EmbeddingTest.15.csproj‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<SolutionDirCondition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
2424
<PythonBuildDirCondition="'$(TargetFramework)'=='net40' AND '$(PythonBuildDir)' == ''">$(SolutionDir)\bin\</PythonBuildDir>
2525
<PublishDirCondition="'$(TargetFramework)'!='net40'">$(OutputPath)\$(TargetFramework)_publish</PublishDir>
26-
<LangVersion>6</LangVersion>
26+
<LangVersion>7.3</LangVersion>
2727
<ErrorReport>prompt</ErrorReport>
2828
<CustomDefineConstantsCondition="'$(CustomDefineConstants)' == ''">$(PYTHONNET_DEFINE_CONSTANTS)</CustomDefineConstants>
2929
<BaseDefineConstants>XPLAT</BaseDefineConstants>
@@ -81,6 +81,7 @@
8181
<PackageReferenceInclude="NUnit.ConsoleRunner"Version="3.7.0" />
8282
<PackageReferenceInclude="NUnit3TestAdapter"Version="3.8.0" />
8383
<PackageReferenceInclude="NUnitLite"Version="3.7.2" />
84+
<PackageReferenceInclude="System.ValueTuple"Version="4.5.0" />
8485
</ItemGroup>
8586
<ItemGroupCondition="'$(TargetFramework)' == 'netcoreapp2.0'">
8687
<PackageReferenceInclude="Microsoft.NET.Test.Sdk"Version="15.0.0" />

‎src/embed_tests/Python.EmbeddingTest.csproj‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<NoWarn>1591</NoWarn>
1515
<SolutionDirCondition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
1616
<PythonBuildDirCondition=" '$(PythonBuildDir)' == ''">$(SolutionDir)\bin\</PythonBuildDir>
17-
<LangVersion>6</LangVersion>
17+
<LangVersion>7.3</LangVersion>
1818
<RestorePackages>true</RestorePackages>
1919
<ErrorReport>prompt</ErrorReport>
2020
</PropertyGroup>
@@ -80,6 +80,7 @@
8080
<NoneInclude="packages.config" />
8181
</ItemGroup>
8282
<ItemGroup>
83+
<CompileInclude="Codecs.cs" />
8384
<CompileInclude="dynamic.cs" />
8485
<CompileInclude="pyimport.cs" />
8586
<CompileInclude="pyinitialize.cs" />

‎src/embed_tests/packages.config‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
33
<packageid="NUnit"version="3.7.1"targetFramework="net40" />
44
<packageid="NUnit.ConsoleRunner"version="3.7.0"targetFramework="net40" />
5-
</packages>
5+
<packageid="System.ValueTuple"version="4.5.0"targetFramework="net40" />
6+
</packages>

‎src/runtime/Codecs/TupleCodecs.cs‎

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
namespacePython.Runtime.Codecs
2+
{
3+
usingSystem;
4+
usingSystem.Collections.Generic;
5+
usingSystem.Linq;
6+
usingSystem.Reflection;
7+
8+
publicsealedclassTupleCodec<TTuple>:IPyObjectEncoder,IPyObjectDecoder
9+
{
10+
TupleCodec(){}
11+
publicstaticTupleCodec<TTuple>Instance{get;}=newTupleCodec<TTuple>();
12+
13+
publicboolCanEncode(Typetype)
14+
=>type.Namespace==typeof(TTuple).Namespace&&type.Name.StartsWith(typeof(TTuple).Name+'`')
15+
||type==typeof(object)||type==typeof(TTuple);
16+
17+
publicPyObjectTryEncode(objectvalue)
18+
{
19+
if(value==null)returnnull;
20+
21+
vartupleType=value.GetType();
22+
if(tupleType==typeof(object))returnnull;
23+
if(!this.CanEncode(tupleType))returnnull;
24+
if(tupleType==typeof(TTuple))returnnewPyTuple();
25+
26+
longfieldCount=tupleType.GetGenericArguments().Length;
27+
vartuple=Runtime.PyTuple_New(fieldCount);
28+
Exceptions.ErrorCheck(tuple);
29+
intfieldIndex=0;
30+
foreach(FieldInfofieldintupleType.GetFields())
31+
{
32+
varitem=field.GetValue(value);
33+
IntPtrpyItem=Converter.ToPython(item);
34+
Runtime.XIncref(pyItem);
35+
Runtime.PyTuple_SetItem(tuple,fieldIndex,pyItem);
36+
fieldIndex++;
37+
}
38+
returnnewPyTuple(Runtime.SelfIncRef(tuple));
39+
}
40+
41+
publicboolCanDecode(PyObjectobjectType,TypetargetType)
42+
=>objectType.Handle==Runtime.PyTuple&&this.CanEncode(targetType);
43+
44+
publicboolTryDecode<T>(PyObjectpyObj,outTvalue)
45+
{
46+
if(pyObj==null)thrownewArgumentNullException(nameof(pyObj));
47+
48+
value=default;
49+
50+
if(!Runtime.PyTuple_Check(pyObj.Handle))returnfalse;
51+
52+
if(typeof(T)==typeof(object))
53+
{
54+
boolconverted=Decode(pyObj,outobjectresult);
55+
if(converted)
56+
{
57+
value=(T)result;
58+
returntrue;
59+
}
60+
61+
returnfalse;
62+
}
63+
64+
varitemTypes=typeof(T).GetGenericArguments();
65+
longitemCount=Runtime.PyTuple_Size(pyObj.Handle);
66+
if(itemTypes.Length!=itemCount)returnfalse;
67+
68+
if(itemCount==0)
69+
{
70+
value=(T)EmptyTuple;
71+
returntrue;
72+
}
73+
74+
varelements=newobject[itemCount];
75+
for(intitemIndex=0;itemIndex<itemTypes.Length;itemIndex++)
76+
{
77+
IntPtrpyItem=Runtime.PyTuple_GetItem(pyObj.Handle,itemIndex);
78+
if(!Converter.ToManaged(pyItem,itemTypes[itemIndex],outelements[itemIndex],setError:false))
79+
{
80+
returnfalse;
81+
}
82+
}
83+
varfactory=tupleCreate[itemCount].MakeGenericMethod(itemTypes);
84+
value=(T)factory.Invoke(null,elements);
85+
returntrue;
86+
}
87+
88+
staticboolDecode(PyObjecttuple,outobjectvalue)
89+
{
90+
longitemCount=Runtime.PyTuple_Size(tuple.Handle);
91+
if(itemCount==0)
92+
{
93+
value=EmptyTuple;
94+
returntrue;
95+
}
96+
varelements=newobject[itemCount];
97+
varitemTypes=newType[itemCount];
98+
value=null;
99+
for(intitemIndex=0;itemIndex<elements.Length;itemIndex++)
100+
{
101+
varpyItem=Runtime.PyTuple_GetItem(tuple.Handle,itemIndex);
102+
if(!Converter.ToManaged(pyItem,typeof(object),outelements[itemIndex],setError:false))
103+
{
104+
returnfalse;
105+
}
106+
107+
itemTypes[itemIndex]=elements[itemIndex]?.GetType()??typeof(object);
108+
}
109+
110+
varfactory=tupleCreate[itemCount].MakeGenericMethod(itemTypes);
111+
value=factory.Invoke(null,elements);
112+
returntrue;
113+
}
114+
115+
staticreadonlyMethodInfo[]tupleCreate=
116+
typeof(TTuple).GetMethods(BindingFlags.Public|BindingFlags.Static)
117+
.Where(m=>m.Name==nameof(Tuple.Create))
118+
.OrderBy(m=>m.GetParameters().Length)
119+
.ToArray();
120+
121+
staticreadonlyobjectEmptyTuple=tupleCreate[0].Invoke(null,parameters:newobject[0]);
122+
123+
publicstaticvoidRegister()
124+
{
125+
PyObjectConversions.RegisterEncoder(Instance);
126+
PyObjectConversions.RegisterDecoder(Instance);
127+
}
128+
}
129+
}

‎src/runtime/Python.Runtime.csproj‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<ProjectDefaultTargets="Build"xmlns="http://schemas.microsoft.com/developer/msbuild/2003"ToolsVersion="4.0">
33
<PropertyGroup>
44
<ConfigurationCondition=" '$(Configuration)' == ''">Debug</Configuration>
@@ -76,6 +76,8 @@
7676
<ReferenceInclude="System" />
7777
</ItemGroup>
7878
<ItemGroup>
79+
<CompileInclude="Codecs\TupleCodecs.cs" />
80+
<CompileInclude="converterextensions.cs" />
7981
<CompileInclude="finalizer.cs" />
8082
<CompileInclude="Properties\AssemblyInfo.cs" />
8183
<CompileInclude="..\SharedAssemblyInfo.cs">
@@ -172,4 +174,4 @@
172174
<CopySourceFiles="$(TargetAssembly)"DestinationFolder="$(PythonBuildDir)" />
173175
<!--Copy SourceFiles="$(TargetAssemblyPdb)" Condition="Exists('$(TargetAssemblyPdb)')" DestinationFolder="$(PythonBuildDir)" /-->
174176
</Target>
175-
</Project>
177+
</Project>

‎src/runtime/converter.cs‎

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,16 @@ internal static IntPtr ToPython(object value, Type type)
135135
returnresult;
136136
}
137137

138-
if(valueisIList&&!(valueisINotifyPropertyChanged)&&value.GetType().IsGenericType)
139-
{
138+
if(Type.GetTypeCode(type)==TypeCode.Object&&value.GetType()!=typeof(object)){
139+
varencoded=PyObjectConversions.TryEncode(value,type);
140+
if(encoded!=null){
141+
Runtime.XIncref(encoded.Handle);
142+
returnencoded.Handle;
143+
}
144+
}
145+
146+
if(valueisIList&&!(valueisINotifyPropertyChanged)&&value.GetType().IsGenericType)
147+
{
140148
using(varresultlist=newPyList())
141149
{
142150
foreach(objectoin(IEnumerable)value)
@@ -437,9 +445,21 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
437445
returnfalse;
438446
}
439447

448+
TypeCodetypeCode=Type.GetTypeCode(obType);
449+
if(typeCode==TypeCode.Object)
450+
{
451+
IntPtrpyType=Runtime.PyObject_TYPE(value);
452+
if(PyObjectConversions.TryDecode(value,pyType,obType,outresult))
453+
{
454+
returntrue;
455+
}
456+
}
457+
440458
returnToPrimitive(value,obType,outresult,setError);
441459
}
442460

461+
internaldelegateboolTryConvertFromPythonDelegate(IntPtrpyObj,outobjectresult);
462+
443463
/// <summary>
444464
/// Convert a Python value to an instance of a primitive managed type.
445465
/// </summary>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp