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

Commitce14424

Browse files
dsuarezvdenfromufa
authored and
denfromufa
committed
Implement named arguments and With semantics in C# embedding side (#461)
* Added python "with" construction* Added some unit tests for new With method* Renamed With tests for easier grouping* Support for named arguments to invoke python methods* Named argument tests cosmetic changed* Fixed failing test in python 2.7* Reset line endings in csproj to LF
1 parentd9a3666 commitce14424

File tree

5 files changed

+245
-20
lines changed

5 files changed

+245
-20
lines changed

‎src/embed_tests/Python.EmbeddingTest.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>
@@ -97,6 +97,8 @@
9797
<CompileInclude="TestPythonException.cs" />
9898
<CompileInclude="TestPythonEngineProperties.cs" />
9999
<CompileInclude="TestPyTuple.cs" />
100+
<CompileInclude="TestNamedArguments.cs" />
101+
<CompileInclude="TestPyWith.cs" />
100102
<CompileInclude="TestRuntime.cs" />
101103
</ItemGroup>
102104
<ItemGroup>
@@ -117,4 +119,4 @@
117119
<CopySourceFiles="$(TargetAssembly)"DestinationFolder="$(PythonBuildDir)" />
118120
<!--Copy SourceFiles="$(TargetAssemblyPdb)" Condition="Exists('$(TargetAssemblyPdb)')" DestinationFolder="$(PythonBuildDir)" /-->
119121
</Target>
120-
</Project>
122+
</Project>

‎src/embed_tests/TestNamedArguments.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
usingSystem;
2+
usingNUnit.Framework;
3+
usingPython.Runtime;
4+
5+
namespacePython.EmbeddingTest
6+
{
7+
publicclassTestNamedArguments
8+
{
9+
[OneTimeSetUp]
10+
publicvoidSetUp()
11+
{
12+
PythonEngine.Initialize();
13+
}
14+
15+
[OneTimeTearDown]
16+
publicvoidDispose()
17+
{
18+
PythonEngine.Shutdown();
19+
}
20+
21+
/// <summary>
22+
/// Test named arguments support through Py.kw method
23+
/// </summary>
24+
[Test]
25+
publicvoidTestKeywordArgs()
26+
{
27+
dynamica=CreateTestClass();
28+
varresult=(int)a.Test3(2,Py.kw("a4",8));
29+
30+
Assert.AreEqual(12,result);
31+
}
32+
33+
34+
/// <summary>
35+
/// Test keyword arguments with .net named arguments
36+
/// </summary>
37+
[Test]
38+
publicvoidTestNamedArgs()
39+
{
40+
dynamica=CreateTestClass();
41+
varresult=(int)a.Test3(2,a4:8);
42+
43+
Assert.AreEqual(12,result);
44+
}
45+
46+
47+
48+
privatestaticPyObjectCreateTestClass()
49+
{
50+
varlocals=newPyDict();
51+
52+
PythonEngine.Exec(@"
53+
class cmTest3:
54+
def Test3(self, a1 = 1, a2 = 1, a3 = 1, a4 = 1):
55+
return a1 + a2 + a3 + a4
56+
57+
a = cmTest3()
58+
",null,locals.Handle);
59+
60+
returnlocals.GetItem("a");
61+
}
62+
63+
}
64+
}

‎src/embed_tests/TestPyWith.cs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
usingSystem;
2+
usingNUnit.Framework;
3+
usingPython.Runtime;
4+
5+
namespacePython.EmbeddingTest
6+
{
7+
publicclassTestPyWith
8+
{
9+
[OneTimeSetUp]
10+
publicvoidSetUp()
11+
{
12+
PythonEngine.Initialize();
13+
}
14+
15+
[OneTimeTearDown]
16+
publicvoidDispose()
17+
{
18+
PythonEngine.Shutdown();
19+
}
20+
21+
/// <summary>
22+
/// Test that exception is raised in context manager that ignores it.
23+
/// </summary>
24+
[Test]
25+
publicvoidTestWithPositive()
26+
{
27+
varlocals=newPyDict();
28+
29+
PythonEngine.Exec(@"
30+
class CmTest:
31+
def __enter__(self):
32+
print('Enter')
33+
return self
34+
def __exit__(self, t, v, tb):
35+
# Exception not handled, return will be False
36+
print('Exit')
37+
def fail(self):
38+
return 5 / 0
39+
40+
a = CmTest()
41+
",null,locals.Handle);
42+
43+
vara=locals.GetItem("a");
44+
45+
try
46+
{
47+
Py.With(a, cmTest=>
48+
{
49+
cmTest.fail();
50+
});
51+
}
52+
catch(PythonExceptione)
53+
{
54+
Assert.IsTrue(e.Message.Contains("ZeroDivisionError"));
55+
}
56+
}
57+
58+
59+
/// <summary>
60+
/// Test that exception is not raised in context manager that handles it
61+
/// </summary>
62+
[Test]
63+
publicvoidTestWithNegative()
64+
{
65+
varlocals=newPyDict();
66+
67+
PythonEngine.Exec(@"
68+
class CmTest:
69+
def __enter__(self):
70+
print('Enter')
71+
return self
72+
def __exit__(self, t, v, tb):
73+
# Signal exception is handled by returning true
74+
return True
75+
def fail(self):
76+
return 5 / 0
77+
78+
a = CmTest()
79+
",null,locals.Handle);
80+
81+
vara=locals.GetItem("a");
82+
Py.With(a, cmTest=>
83+
{
84+
cmTest.fail();
85+
});
86+
}
87+
}
88+
}

‎src/runtime/pyobject.cs

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
usingSystem;
1+
usingSystem;
22
usingSystem.Collections;
33
usingSystem.Dynamic;
44
usingSystem.Linq.Expressions;
@@ -915,6 +915,34 @@ public override bool TrySetMember(SetMemberBinder binder, object value)
915915
returntrue;
916916
}
917917

918+
privatevoidGetArgs(object[]inargs,CallInfocallInfo,outPyTupleargs,outPyDictkwargs)
919+
{
920+
if(callInfo==null||callInfo.ArgumentNames.Count==0)
921+
{
922+
GetArgs(inargs,outargs,outkwargs);
923+
return;
924+
}
925+
926+
// Support for .net named arguments
927+
varnamedArgumentCount=callInfo.ArgumentNames.Count;
928+
varregularArgumentCount=callInfo.ArgumentCount-namedArgumentCount;
929+
930+
varargTuple=Runtime.PyTuple_New(regularArgumentCount);
931+
for(inti=0;i<regularArgumentCount;++i)
932+
{
933+
AddArgument(argTuple,i,inargs[i]);
934+
}
935+
args=newPyTuple(argTuple);
936+
937+
varnamedArgs=newobject[namedArgumentCount*2];
938+
for(inti=0;i<namedArgumentCount;++i)
939+
{
940+
namedArgs[i*2]=callInfo.ArgumentNames[i];
941+
namedArgs[i*2+1]=inargs[regularArgumentCount+i];
942+
}
943+
kwargs=Py.kw(namedArgs);
944+
}
945+
918946
privatevoidGetArgs(object[]inargs,outPyTupleargs,outPyDictkwargs)
919947
{
920948
intarg_count;
@@ -925,22 +953,10 @@ private void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs)
925953
IntPtrargtuple=Runtime.PyTuple_New(arg_count);
926954
for(vari=0;i<arg_count;i++)
927955
{
928-
IntPtrptr;
929-
if(inargs[i]isPyObject)
930-
{
931-
ptr=((PyObject)inargs[i]).Handle;
932-
Runtime.XIncref(ptr);
933-
}
934-
else
935-
{
936-
ptr=Converter.ToPython(inargs[i],inargs[i]?.GetType());
937-
}
938-
if(Runtime.PyTuple_SetItem(argtuple,i,ptr)<0)
939-
{
940-
thrownewPythonException();
941-
}
956+
AddArgument(argtuple,i,inargs[i]);
942957
}
943958
args=newPyTuple(argtuple);
959+
944960
kwargs=null;
945961
for(inti=arg_count;i<inargs.Length;i++)
946962
{
@@ -959,6 +975,32 @@ private void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs)
959975
}
960976
}
961977

978+
privatestaticvoidAddArgument(IntPtrargtuple,inti,objecttarget)
979+
{
980+
IntPtrptr=GetPythonObject(target);
981+
982+
if(Runtime.PyTuple_SetItem(argtuple,i,ptr)<0)
983+
{
984+
thrownewPythonException();
985+
}
986+
}
987+
988+
privatestaticIntPtrGetPythonObject(objecttarget)
989+
{
990+
IntPtrptr;
991+
if(targetisPyObject)
992+
{
993+
ptr=((PyObject)target).Handle;
994+
Runtime.XIncref(ptr);
995+
}
996+
else
997+
{
998+
ptr=Converter.ToPython(target,target?.GetType());
999+
}
1000+
1001+
returnptr;
1002+
}
1003+
9621004
publicoverrideboolTryInvokeMember(InvokeMemberBinderbinder,object[]args,outobjectresult)
9631005
{
9641006
if(this.HasAttr(binder.Name)&&this.GetAttr(binder.Name).IsCallable())
@@ -967,7 +1009,7 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, o
9671009
PyDictkwargs=null;
9681010
try
9691011
{
970-
GetArgs(args,outpyargs,outkwargs);
1012+
GetArgs(args,binder.CallInfo,outpyargs,outkwargs);
9711013
result=CheckNone(InvokeMethod(binder.Name,pyargs,kwargs));
9721014
}
9731015
finally
@@ -997,7 +1039,7 @@ public override bool TryInvoke(InvokeBinder binder, object[] args, out object re
9971039
PyDictkwargs=null;
9981040
try
9991041
{
1000-
GetArgs(args,outpyargs,outkwargs);
1042+
GetArgs(args,binder.CallInfo,outpyargs,outkwargs);
10011043
result=CheckNone(Invoke(pyargs,kwargs));
10021044
}
10031045
finally

‎src/runtime/pythonengine.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
usingSystem;
1+
usingSystem;
22
usingSystem.Collections.Generic;
33
usingSystem.IO;
44
usingSystem.Linq;
@@ -632,5 +632,34 @@ public static void SetArgv(IEnumerable<string> argv)
632632
Runtime.CheckExceptionOccurred();
633633
}
634634
}
635+
636+
publicstaticvoidWith(PyObjectobj,Action<dynamic>Body)
637+
{
638+
// Behavior described here:
639+
// https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
640+
641+
IntPtrtype=Runtime.PyNone;
642+
IntPtrval=Runtime.PyNone;
643+
IntPtrtraceBack=Runtime.PyNone;
644+
PythonExceptionex=null;
645+
646+
try
647+
{
648+
PyObjectenterResult=obj.InvokeMethod("__enter__");
649+
650+
Body(enterResult);
651+
}
652+
catch(PythonExceptione)
653+
{
654+
ex=e;
655+
type=ex.PyType;
656+
val=ex.PyValue;
657+
traceBack=ex.PyTB;
658+
}
659+
660+
varexitResult=obj.InvokeMethod("__exit__",newPyObject(type),newPyObject(val),newPyObject(traceBack));
661+
662+
if(ex!=null&&!exitResult.IsTrue())throwex;
663+
}
635664
}
636665
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp