| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent d9a3666 commit ce14424
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,4 @@ | |||
| 1 | - <?xml version="1.0" encoding="utf-8"?> | ||
| 1 | + <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | 2 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> | |
| 3 | 3 | <PropertyGroup> | |
| 4 | 4 | <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |
@@ -97,6 +97,8 @@ | |||
| 97 | 97 | <Compile Include="TestPythonException.cs" /> | |
| 98 | 98 | <Compile Include="TestPythonEngineProperties.cs" /> | |
| 99 | 99 | <Compile Include="TestPyTuple.cs" /> | |
| 100 | + <Compile Include="TestNamedArguments.cs" /> | ||
| 101 | + <Compile Include="TestPyWith.cs" /> | ||
| 100 | 102 | <Compile Include="TestRuntime.cs" /> | |
| 101 | 103 | </ItemGroup> | |
| 102 | 104 | <ItemGroup> | |
@@ -117,4 +119,4 @@ | |||
| 117 | 119 | <Copy SourceFiles="$(TargetAssembly)" DestinationFolder="$(PythonBuildDir)" /> | |
| 118 | 120 | <!--Copy SourceFiles="$(TargetAssemblyPdb)" Condition="Exists('$(TargetAssemblyPdb)')" DestinationFolder="$(PythonBuildDir)" /--> | |
| 119 | 121 | </Target> | |
| 120 | - </Project> | ||
| 122 | + </Project> | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,64 @@ | |||
| 1 | + using System; | ||
| 2 | + using NUnit.Framework; | ||
| 3 | + using Python.Runtime; | ||
| 4 | + | ||
| 5 | + namespace Python.EmbeddingTest | ||
| 6 | + { | ||
| 7 | + public class TestNamedArguments | ||
| 8 | + { | ||
| 9 | + [OneTimeSetUp] | ||
| 10 | + public void SetUp() | ||
| 11 | + { | ||
| 12 | + PythonEngine.Initialize(); | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + [OneTimeTearDown] | ||
| 16 | + public void Dispose() | ||
| 17 | + { | ||
| 18 | + PythonEngine.Shutdown(); | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + /// <summary> | ||
| 22 | + /// Test named arguments support through Py.kw method | ||
| 23 | + /// </summary> | ||
| 24 | + [Test] | ||
| 25 | + public void TestKeywordArgs() | ||
| 26 | + { | ||
| 27 | + dynamic a = CreateTestClass(); | ||
| 28 | + var result = (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 | + public void TestNamedArgs() | ||
| 39 | + { | ||
| 40 | + dynamic a = CreateTestClass(); | ||
| 41 | + var result = (int)a.Test3(2, a4: 8); | ||
| 42 | + | ||
| 43 | + Assert.AreEqual(12, result); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + | ||
| 47 | + | ||
| 48 | + private static PyObject CreateTestClass() | ||
| 49 | + { | ||
| 50 | + var locals = new PyDict(); | ||
| 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 | + return locals.GetItem("a"); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + } | ||
| 64 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,88 @@ | |||
| 1 | + using System; | ||
| 2 | + using NUnit.Framework; | ||
| 3 | + using Python.Runtime; | ||
| 4 | + | ||
| 5 | + namespace Python.EmbeddingTest | ||
| 6 | + { | ||
| 7 | + public class TestPyWith | ||
| 8 | + { | ||
| 9 | + [OneTimeSetUp] | ||
| 10 | + public void SetUp() | ||
| 11 | + { | ||
| 12 | + PythonEngine.Initialize(); | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + [OneTimeTearDown] | ||
| 16 | + public void Dispose() | ||
| 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 | + public void TestWithPositive() | ||
| 26 | + { | ||
| 27 | + var locals = new PyDict(); | ||
| 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 | + var a = locals.GetItem("a"); | ||
| 44 | + | ||
| 45 | + try | ||
| 46 | + { | ||
| 47 | + Py.With(a, cmTest => | ||
| 48 | + { | ||
| 49 | + cmTest.fail(); | ||
| 50 | + }); | ||
| 51 | + } | ||
| 52 | + catch (PythonException e) | ||
| 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 | + public void TestWithNegative() | ||
| 64 | + { | ||
| 65 | + var locals = new PyDict(); | ||
| 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 | + var a = locals.GetItem("a"); | ||
| 82 | + Py.With(a, cmTest => | ||
| 83 | + { | ||
| 84 | + cmTest.fail(); | ||
| 85 | + }); | ||
| 86 | + } | ||
| 87 | + } | ||
| 88 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,4 @@ | |||
| 1 | - using System; | ||
| 1 | + using System; | ||
| 2 | 2 | using System.Collections; | |
| 3 | 3 | using System.Dynamic; | |
| 4 | 4 | using System.Linq.Expressions; | |
@@ -915,6 +915,34 @@ public override bool TrySetMember(SetMemberBinder binder, object value) | |||
| 915 | 915 | return true; | |
| 916 | 916 | } | |
| 917 | 917 | ||
| 918 | + private void GetArgs(object[] inargs, CallInfo callInfo, out PyTuple args, out PyDict kwargs) | ||
| 919 | + { | ||
| 920 | + if (callInfo == null || callInfo.ArgumentNames.Count == 0) | ||
| 921 | + { | ||
| 922 | + GetArgs(inargs, out args, out kwargs); | ||
| 923 | + return; | ||
| 924 | + } | ||
| 925 | + | ||
| 926 | + // Support for .net named arguments | ||
| 927 | + var namedArgumentCount = callInfo.ArgumentNames.Count; | ||
| 928 | + var regularArgumentCount = callInfo.ArgumentCount - namedArgumentCount; | ||
| 929 | + | ||
| 930 | + var argTuple = Runtime.PyTuple_New(regularArgumentCount); | ||
| 931 | + for (int i = 0; i < regularArgumentCount; ++i) | ||
| 932 | + { | ||
| 933 | + AddArgument(argTuple, i, inargs[i]); | ||
| 934 | + } | ||
| 935 | + args = new PyTuple(argTuple); | ||
| 936 | + | ||
| 937 | + var namedArgs = new object[namedArgumentCount * 2]; | ||
| 938 | + for (int i = 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 | + | ||
| 918 | 946 | private void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs) | |
| 919 | 947 | { | |
| 920 | 948 | int arg_count; | |
@@ -925,22 +953,10 @@ private void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs) | |||
| 925 | 953 | IntPtr argtuple = Runtime.PyTuple_New(arg_count); | |
| 926 | 954 | for (var i = 0; i < arg_count; i++) | |
| 927 | 955 | { | |
| 928 | - IntPtr ptr; | ||
| 929 | - if (inargs[i] is PyObject) | ||
| 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 | - throw new PythonException(); | ||
| 941 | - } | ||
| 956 | + AddArgument(argtuple, i, inargs[i]); | ||
| 942 | 957 | } | |
| 943 | 958 | args = new PyTuple(argtuple); | |
| 959 | + | ||
| 944 | 960 | kwargs = null; | |
| 945 | 961 | for (int i = arg_count; i < inargs.Length; i++) | |
| 946 | 962 | { | |
@@ -959,6 +975,32 @@ private void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs) | |||
| 959 | 975 | } | |
| 960 | 976 | } | |
| 961 | 977 | ||
| 978 | + private static void AddArgument(IntPtr argtuple, int i, object target) | ||
| 979 | + { | ||
| 980 | + IntPtr ptr = GetPythonObject(target); | ||
| 981 | + | ||
| 982 | + if (Runtime.PyTuple_SetItem(argtuple, i, ptr) < 0) | ||
| 983 | + { | ||
| 984 | + throw new PythonException(); | ||
| 985 | + } | ||
| 986 | + } | ||
| 987 | + | ||
| 988 | + private static IntPtr GetPythonObject(object target) | ||
| 989 | + { | ||
| 990 | + IntPtr ptr; | ||
| 991 | + if (target is PyObject) | ||
| 992 | + { | ||
| 993 | + ptr = ((PyObject)target).Handle; | ||
| 994 | + Runtime.XIncref(ptr); | ||
| 995 | + } | ||
| 996 | + else | ||
| 997 | + { | ||
| 998 | + ptr = Converter.ToPython(target, target?.GetType()); | ||
| 999 | + } | ||
| 1000 | + | ||
| 1001 | + return ptr; | ||
| 1002 | + } | ||
| 1003 | + | ||
| 962 | 1004 | public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) | |
| 963 | 1005 | { | |
| 964 | 1006 | if (this.HasAttr(binder.Name) && this.GetAttr(binder.Name).IsCallable()) | |
@@ -967,7 +1009,7 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, o | |||
| 967 | 1009 | PyDict kwargs = null; | |
| 968 | 1010 | try | |
| 969 | 1011 | { | |
| 970 | - GetArgs(args, out pyargs, out kwargs); | ||
| 1012 | + GetArgs(args, binder.CallInfo, out pyargs, out kwargs); | ||
| 971 | 1013 | result = CheckNone(InvokeMethod(binder.Name, pyargs, kwargs)); | |
| 972 | 1014 | } | |
| 973 | 1015 | finally | |
@@ -997,7 +1039,7 @@ public override bool TryInvoke(InvokeBinder binder, object[] args, out object re | |||
| 997 | 1039 | PyDict kwargs = null; | |
| 998 | 1040 | try | |
| 999 | 1041 | { | |
| 1000 | - GetArgs(args, out pyargs, out kwargs); | ||
| 1042 | + GetArgs(args, binder.CallInfo, out pyargs, out kwargs); | ||
| 1001 | 1043 | result = CheckNone(Invoke(pyargs, kwargs)); | |
| 1002 | 1044 | } | |
| 1003 | 1045 | finally | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,4 @@ | |||
| 1 | - using System; | ||
| 1 | + using System; | ||
| 2 | 2 | using System.Collections.Generic; | |
| 3 | 3 | using System.IO; | |
| 4 | 4 | using System.Linq; | |
@@ -632,5 +632,34 @@ public static void SetArgv(IEnumerable<string> argv) | |||
| 632 | 632 | Runtime.CheckExceptionOccurred(); | |
| 633 | 633 | } | |
| 634 | 634 | } | |
| 635 | + | ||
| 636 | + public static void With(PyObject obj, Action<dynamic> Body) | ||
| 637 | + { | ||
| 638 | + // Behavior described here: | ||
| 639 | + // https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers | ||
| 640 | + | ||
| 641 | + IntPtr type = Runtime.PyNone; | ||
| 642 | + IntPtr val = Runtime.PyNone; | ||
| 643 | + IntPtr traceBack = Runtime.PyNone; | ||
| 644 | + PythonException ex = null; | ||
| 645 | + | ||
| 646 | + try | ||
| 647 | + { | ||
| 648 | + PyObject enterResult = obj.InvokeMethod("__enter__"); | ||
| 649 | + | ||
| 650 | + Body(enterResult); | ||
| 651 | + } | ||
| 652 | + catch (PythonException e) | ||
| 653 | + { | ||
| 654 | + ex = e; | ||
| 655 | + type = ex.PyType; | ||
| 656 | + val = ex.PyValue; | ||
| 657 | + traceBack = ex.PyTB; | ||
| 658 | + } | ||
| 659 | + | ||
| 660 | + var exitResult = obj.InvokeMethod("__exit__", new PyObject(type), new PyObject(val), new PyObject(traceBack)); | ||
| 661 | + | ||
| 662 | + if (ex != null && !exitResult.IsTrue()) throw ex; | ||
| 663 | + } | ||
| 635 | 664 | } | |
| 636 | 665 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments