From 21528ee2fd3cbda2c9c7a9d7d884b71fb181ff2e Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Mon, 21 Oct 2019 17:35:53 -0500 Subject: [PATCH 01/11] Add test for ienumerable method --- src/testing/methodtest.cs | 5 +++++ src/tests/test_method.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs index 91836b727..3b34dd40e 100644 --- a/src/testing/methodtest.cs +++ b/src/testing/methodtest.cs @@ -117,6 +117,11 @@ public static int[] TestOverloadedParams(int v, int[] args) return args; } + public static int TestIEnumerable(System.Collections.Generic.IEnumerable arg) + { + return 1; + } + public static string TestOverloadedNoObject(int i) { return "Got int"; diff --git a/src/tests/test_method.py b/src/tests/test_method.py index 34f460d59..57304688e 100644 --- a/src/tests/test_method.py +++ b/src/tests/test_method.py @@ -204,6 +204,11 @@ def test_null_array_conversion(): r = ob.TestNullArrayConversion(None) assert r is None +def test_ienumerable_args(): + """Test conversion of python lists and tuples to IEnumerable""" + ob = MethodTest() + x = ob.TestIEnumerable([1,2,3]) + y = ob.TestIEnumerable((1,2,3)) def test_string_params_args(): """Test use of string params.""" From b4d60c6ca6495f3e529f94f2983859f4439d021e Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Sun, 1 Dec 2019 16:13:37 -0600 Subject: [PATCH 02/11] Add tests for collection & task/action from function change enumerable method to non-static add tuple test for ienumerable --- src/testing/methodtest.cs | 24 +++++++++++++++++++++++- src/tests/test_method.py | 21 +++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs index 3b34dd40e..8fff85346 100644 --- a/src/testing/methodtest.cs +++ b/src/testing/methodtest.cs @@ -117,7 +117,29 @@ public static int[] TestOverloadedParams(int v, int[] args) return args; } - public static int TestIEnumerable(System.Collections.Generic.IEnumerable arg) + public static int TestIList(System.Collections.Generic.IList arg) + { + return 1; + } + + public static int TestICollection(System.Collections.Generic.ICollection arg) + { + return 1; + } + + public int TestAction(System.Action action) + { + action(); + return 1; + } + + public int TestTask(System.Threading.Tasks.Task task) + { + task.RunSynchronously(); + return task.Result; + } + + public int TestIEnumerable(System.Collections.Generic.IEnumerable arg) { return 1; } diff --git a/src/tests/test_method.py b/src/tests/test_method.py index 57304688e..4c821422e 100644 --- a/src/tests/test_method.py +++ b/src/tests/test_method.py @@ -204,12 +204,33 @@ def test_null_array_conversion(): r = ob.TestNullArrayConversion(None) assert r is None +def test_action(): + """Test python lambda as an action""" + ob = MethodTest() + def func(): + return + r = ob.TestAction(func) + assert r == 1 + +def test_task(): + """Test python lambda as an action""" + ob = MethodTest() + def func(): + return 1 + r = ob.TestTask(func) + assert r == 1 + def test_ienumerable_args(): """Test conversion of python lists and tuples to IEnumerable""" ob = MethodTest() x = ob.TestIEnumerable([1,2,3]) y = ob.TestIEnumerable((1,2,3)) +def test_icollection_args(): + """Test conversion of python lists and tuples to ICollection""" + ob = MethodTest() + x = ob.TestICollection([1,2,3]) + def test_string_params_args(): """Test use of string params.""" result = MethodTest.TestStringParamsArg('one', 'two', 'three') From 3f02abf9fbba66c0f44ce5a831aef95db804c52c Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Sun, 1 Dec 2019 17:04:24 -0600 Subject: [PATCH 03/11] Add more tests --- src/testing/methodtest.cs | 6 ++++++ src/tests/test_method.py | 12 ++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs index 8fff85346..3a61a017c 100644 --- a/src/testing/methodtest.cs +++ b/src/testing/methodtest.cs @@ -133,6 +133,12 @@ public int TestAction(System.Action action) return 1; } + public int TestFuncObj(System.Func func, object arg) + { + return func.Invoke(arg); + + } + public int TestTask(System.Threading.Tasks.Task task) { task.RunSynchronously(); diff --git a/src/tests/test_method.py b/src/tests/test_method.py index 4c821422e..2a6352d74 100644 --- a/src/tests/test_method.py +++ b/src/tests/test_method.py @@ -205,15 +205,23 @@ def test_null_array_conversion(): assert r is None def test_action(): - """Test python lambda as an action""" + """Test python lambda as an Action""" ob = MethodTest() def func(): return r = ob.TestAction(func) assert r == 1 +def test_func_object(): + """Test python lambda as a Func""" + ob = MethodTest() + def func(arg): + return arg + 1 + r = ob.TestFunc(func, 0) + assert r == 1 + def test_task(): - """Test python lambda as an action""" + """Test python lambda as a Task""" ob = MethodTest() def func(): return 1 From 9c3e1c51ff38d92ae15c45304b04e03a68616a5c Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Sun, 1 Dec 2019 17:04:54 -0600 Subject: [PATCH 04/11] Implement list converter --- src/runtime/converter.cs | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index e7e047419..8970652fe 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -343,6 +343,17 @@ internal static bool ToManagedValue(IntPtr value, Type obType, return ToArray(value, obType, out result, setError); } + if (obType.IsGenericType) + { + if (obType.GetGenericTypeDefinition() == typeof(IEnumerable<>) || + obType.GetGenericTypeDefinition() == typeof(ICollection<>) || + obType.GetGenericTypeDefinition() == typeof(IList<>)) + { + //We could probably convert to a thin IEnumerable/IList/ICollection implementation around a python array + //but for simplicity right now convert to a List which is a copy of the python iterable. + return ToList(value, obType, out result, setError); + } + } if (obType.IsEnum) { return ToEnum(value, obType, out result, setError); @@ -901,6 +912,48 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s return true; } + /// + /// Convert a Python value to a correctly typed managed list instance. + /// The Python value must support the Python iterator protocol or and the + /// items in the sequence must be convertible to the target array type. + /// + private static bool ToList(IntPtr value, Type obType, out object result, bool setError) { + Type elementType = obType.GetGenericArguments()[0]; + result = null; + + bool IsSeqObj = Runtime.PySequence_Check(value); + var len = IsSeqObj ? Runtime.PySequence_Size(value) : -1; + + IntPtr IterObject = Runtime.PyObject_GetIter(value); + + if (IterObject == IntPtr.Zero) { + if (setError) { + SetConversionError(value, obType); + } + return false; + } + + var listType = typeof(List<>); + var constructedListType = listType.MakeGenericType(elementType); + IList list = IsSeqObj ? (IList)Activator.CreateInstance(constructedListType, new Object[] { (int)len }) : + (IList)Activator.CreateInstance(constructedListType); + IntPtr item; + + while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero) { + object obj = null; + + if (!Converter.ToManaged(item, elementType, out obj, true)) { + Runtime.XDecref(item); + return false; + } + + list.Add(obj); + Runtime.XDecref(item); + } + Runtime.XDecref(IterObject); + result = list; + return true; + } /// /// Convert a Python value to a correctly typed managed enum instance. From b9db0f638694b096110cc3a5e3bdfbe036a25868 Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Sun, 1 Dec 2019 17:05:44 -0600 Subject: [PATCH 05/11] Implement action converter --- src/runtime/converter.cs | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 8970652fe..d3df2dfe6 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -354,6 +354,12 @@ internal static bool ToManagedValue(IntPtr value, Type obType, return ToList(value, obType, out result, setError); } } + + if (obType.FullName == "System.Action") + { + return ToAction(value, obType, out result, setError); + } + if (obType.IsEnum) { return ToEnum(value, obType, out result, setError); @@ -912,6 +918,43 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s return true; } + /// + /// Convert a Python function to a System.Action. + /// The Python value must support the Python "BLAH" protocol. + /// + private static bool ToAction(IntPtr value, Type obType, out object result, bool setError) { + + result = null; + + PyObject obj = new PyObject(value); + + var args = obType.GetGenericArguments(); + + //Temporarily only deal with non-generic actions! + if (!obj.IsCallable() || args.Length != 0) { + if (setError) { + SetConversionError(value, obType); + } + obj.Dispose(); + return false; + } + + Action action = () => { + PyObject py_action = new PyObject(value); + var py_args = new PyObject[0]; + var py_result = py_action.Invoke(py_args); + + //Discard the result since this is being converted to an Action + py_result.Dispose(); + py_action.Dispose(); + }; + + result = action; + obj.Dispose(); + return true; + } + + /// /// Convert a Python value to a correctly typed managed list instance. /// The Python value must support the Python iterator protocol or and the @@ -955,6 +998,7 @@ private static bool ToList(IntPtr value, Type obType, out object result, bool se return true; } + /// /// Convert a Python value to a correctly typed managed enum instance. /// From 4b0a1ad1f28368f31b9da286bd8cc5da5417c1ca Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Sun, 1 Dec 2019 17:10:27 -0600 Subject: [PATCH 06/11] add TODO --- src/runtime/converter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index d3df2dfe6..0afa80830 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -959,6 +959,7 @@ private static bool ToAction(IntPtr value, Type obType, out object result, bool /// Convert a Python value to a correctly typed managed list instance. /// The Python value must support the Python iterator protocol or and the /// items in the sequence must be convertible to the target array type. + /// TODO - remove duplication with ToArray! /// private static bool ToList(IntPtr value, Type obType, out object result, bool setError) { Type elementType = obType.GetGenericArguments()[0]; From 8bed1bb70229cad5d1d0884c6890c598733d0f23 Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Sun, 1 Dec 2019 17:12:08 -0600 Subject: [PATCH 07/11] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c999d668..c22ac2f16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Added automatic NuGet package generation in appveyor and local builds - Added function that sets Py_NoSiteFlag to 1. - Added support for Jetson Nano. +- Added support for methodbinding to IEnumerable, ICollection, IList, Task, Action, Func ### Changed From a4599d922bf0742f35e76f720ba0fefcdadf86af Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Mon, 2 Dec 2019 17:29:14 -0600 Subject: [PATCH 08/11] share common code between ToArray and ToList --- src/runtime/converter.cs | 88 ++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 54 deletions(-) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 0afa80830..c699088fc 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -864,15 +864,8 @@ private static void SetConversionError(IntPtr value, Type target) Exceptions.SetError(Exceptions.TypeError, $"Cannot convert {src} to {target}"); } - - /// - /// Convert a Python value to a correctly typed managed array instance. - /// The Python value must support the Python iterator protocol or and the - /// items in the sequence must be convertible to the target array type. - /// - private static bool ToArray(IntPtr value, Type obType, out object result, bool setError) + private static bool ToListOfT(IntPtr value, Type elementType, out object result) { - Type elementType = obType.GetElementType(); result = null; bool IsSeqObj = Runtime.PySequence_Check(value); @@ -880,28 +873,19 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s IntPtr IterObject = Runtime.PyObject_GetIter(value); - if(IterObject==IntPtr.Zero) { - if (setError) - { - SetConversionError(value, obType); - } + if (IterObject == IntPtr.Zero) return false; - } - - Array items; var listType = typeof(List<>); var constructedListType = listType.MakeGenericType(elementType); - IList list = IsSeqObj ? (IList) Activator.CreateInstance(constructedListType, new Object[] {(int) len}) : - (IList) Activator.CreateInstance(constructedListType); + IList list = IsSeqObj ? (IList)Activator.CreateInstance(constructedListType, new Object[] { (int)len }) : + (IList)Activator.CreateInstance(constructedListType); IntPtr item; - while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero) - { + while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero) { object obj = null; - if (!Converter.ToManaged(item, elementType, out obj, true)) - { + if (!Converter.ToManaged(item, elementType, out obj, true)) { Runtime.XDecref(item); return false; } @@ -910,12 +894,34 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s Runtime.XDecref(item); } Runtime.XDecref(IterObject); + result = list; + return true; + } + + /// + /// Convert a Python value to a correctly typed managed array instance. + /// The Python value must support the Python iterator protocol or and the + /// items in the sequence must be convertible to the target array type. + /// + private static bool ToArray(IntPtr value, Type obType, out object result, bool setError) + { + Type elementType = obType.GetElementType(); + result = null; + bool success = ToListOfT(value, elementType, out result); + if (!success) + { + if (setError) + SetConversionError(value, obType); + return false; + } - items = Array.CreateInstance(elementType, list.Count); + IList list = (IList)result; + Array items = Array.CreateInstance(elementType, list.Count); list.CopyTo(items, 0); - + result = items; return true; + } /// @@ -963,39 +969,13 @@ private static bool ToAction(IntPtr value, Type obType, out object result, bool /// private static bool ToList(IntPtr value, Type obType, out object result, bool setError) { Type elementType = obType.GetGenericArguments()[0]; - result = null; - - bool IsSeqObj = Runtime.PySequence_Check(value); - var len = IsSeqObj ? Runtime.PySequence_Size(value) : -1; - - IntPtr IterObject = Runtime.PyObject_GetIter(value); - - if (IterObject == IntPtr.Zero) { - if (setError) { + var success = ToListOfT(value, elementType, out result); + if (!success) + { + if (setError) SetConversionError(value, obType); - } return false; } - - var listType = typeof(List<>); - var constructedListType = listType.MakeGenericType(elementType); - IList list = IsSeqObj ? (IList)Activator.CreateInstance(constructedListType, new Object[] { (int)len }) : - (IList)Activator.CreateInstance(constructedListType); - IntPtr item; - - while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero) { - object obj = null; - - if (!Converter.ToManaged(item, elementType, out obj, true)) { - Runtime.XDecref(item); - return false; - } - - list.Add(obj); - Runtime.XDecref(item); - } - Runtime.XDecref(IterObject); - result = list; return true; } From 96fd89692a7ce74213e08fa6e70163f1dcc4530d Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Mon, 2 Dec 2019 20:46:18 -0600 Subject: [PATCH 09/11] improve ref counting --- src/runtime/converter.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index c699088fc..96d02cd47 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -941,7 +941,6 @@ private static bool ToAction(IntPtr value, Type obType, out object result, bool if (setError) { SetConversionError(value, obType); } - obj.Dispose(); return false; } @@ -949,14 +948,11 @@ private static bool ToAction(IntPtr value, Type obType, out object result, bool PyObject py_action = new PyObject(value); var py_args = new PyObject[0]; var py_result = py_action.Invoke(py_args); - - //Discard the result since this is being converted to an Action - py_result.Dispose(); - py_action.Dispose(); + Runtime.XDecref(value); }; + Runtime.XIncref(value); result = action; - obj.Dispose(); return true; } From c6fd7686935f5d20d41ca1f14202514dbb7cb9f2 Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Wed, 4 Dec 2019 17:48:14 -0600 Subject: [PATCH 10/11] revert whitespace --- src/runtime/converter.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 96d02cd47..c6b87ee16 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -878,14 +878,16 @@ private static bool ToListOfT(IntPtr value, Type elementType, out object result) var listType = typeof(List<>); var constructedListType = listType.MakeGenericType(elementType); - IList list = IsSeqObj ? (IList)Activator.CreateInstance(constructedListType, new Object[] { (int)len }) : - (IList)Activator.CreateInstance(constructedListType); + IList list = IsSeqObj ? (IList) Activator.CreateInstance(constructedListType, new Object[] { (int)len }) : + (IList) Activator.CreateInstance(constructedListType); IntPtr item; - while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero) { + while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero) + { object obj = null; - if (!Converter.ToManaged(item, elementType, out obj, true)) { + if (!Converter.ToManaged(item, elementType, out obj, true)) + { Runtime.XDecref(item); return false; } From e63d11edc73f7fa197e76681e0316124d53b77c2 Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Sat, 7 Dec 2019 18:51:46 -0600 Subject: [PATCH 11/11] list wrapper --- src/runtime/ListWrapper.cs | 201 +++++++++++++++++++++++++++++++++++++ src/runtime/converter.cs | 63 +++++++----- 2 files changed, 237 insertions(+), 27 deletions(-) create mode 100644 src/runtime/ListWrapper.cs diff --git a/src/runtime/ListWrapper.cs b/src/runtime/ListWrapper.cs new file mode 100644 index 000000000..5173be4b0 --- /dev/null +++ b/src/runtime/ListWrapper.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Python.Runtime { + /// + /// Implements IEnumerable for any python iterable. + /// + internal class IterableWrapper : IEnumerable { + private IntPtr iterObject; + + public IterableWrapper(IntPtr value) { + iterObject = Runtime.PyObject_GetIter(value); + if (iterObject == IntPtr.Zero) + Exceptions.RaiseTypeError("not an iterator"); + Runtime.XIncref(iterObject); + } + ~IterableWrapper() { + Runtime.XDecref(iterObject); + } + + public IEnumerator GetEnumerator() { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() { + IntPtr item; + while ((item = Runtime.PyIter_Next(iterObject)) != IntPtr.Zero) { + object obj = null; + if (!Converter.ToManaged(item, typeof(T), out obj, true)) { + Runtime.XDecref(item); + Runtime.XDecref(iterObject); + Exceptions.RaiseTypeError("wrong type in sequence"); + } + + Runtime.XDecref(item); + yield return obj; + } + } + } + + /// + /// Implements IList for any python sequence. + /// Some methods/properties are only available on certaintypes of sequences, like list + /// + internal class ListWrapper : IterableWrapper, IList + { + private IntPtr seq; + public ListWrapper(IntPtr value) : base(value) + { + this.seq = value; + Runtime.XIncref(value); + bool IsSeqObj = Runtime.PySequence_Check(value); + if (!IsSeqObj) + Exceptions.RaiseTypeError("not a sequence"); + + } + ~ListWrapper() + { + Runtime.XDecref(seq); + } + public T this[int index] + { + get + { + IntPtr item = Runtime.PySequence_GetItem(seq, index); + object obj; + + if (!Converter.ToManaged(item, typeof(T), out obj, true)) { + Runtime.XDecref(item); + Exceptions.RaiseTypeError("wrong type in sequence"); + } + + return (T)obj; + } + set + { + IntPtr pyItem = Converter.ToPython(value, typeof(T)); + if (pyItem == IntPtr.Zero) + throw new Exception("failed to set item"); + + var result = Runtime.PySequence_SetItem(seq, index, pyItem); + Runtime.XDecref(pyItem); + if (result == -1) + throw new Exception("failed to set item"); + } + } + + public int Count + { + get + { + var len = Runtime.PySequence_Size(seq); + return (int)len; + } + } + + public bool IsReadOnly + { + get + { + return Runtime.PyTuple_Check(seq); //python tuples are immutable + } + + } + + public void Add(T item) { + if (IsReadOnly) + throw new NotImplementedException(); + + //only implemented if this is a list! + if (!Runtime.PyList_Check(seq)) + throw new NotImplementedException(); + + IntPtr pyItem = Converter.ToPython(item, typeof(T)); + if (pyItem == IntPtr.Zero) + throw new Exception("failed to add item"); + + var result = Runtime.PyList_Append(seq, pyItem); + Runtime.XDecref(pyItem); + if (result == -1) + throw new Exception("failed to add item"); + } + + public void Clear() { + if (IsReadOnly) + throw new NotImplementedException(); + var result = Runtime.PySequence_DelSlice(seq, 0, Count); + if (result == -1) + throw new Exception("failed to clear sequence"); + } + + public bool Contains(T item) + { + //not sure if IEquatable is implemented and this will work! + foreach (var element in this) + if (element.Equals(item)) return true; + + return false; + } + + public void CopyTo(T[] array, int arrayIndex) + { + for (int index = 0; index < Count; index++) + { + array[index + arrayIndex] = this[index]; + } + } + + public int IndexOf(T item) { + var index = 0; + foreach (var element in this) { + if (element.Equals(item)) return index; + index++; + } + + return -1; + } + + public void Insert(int index, T item) + { + if (IsReadOnly) + throw new NotImplementedException(); + + //only implemented if this is a list! + if (!Runtime.PyList_Check(seq)) + throw new NotImplementedException(); + + IntPtr pyItem = Converter.ToPython(item, typeof(T)); + if (pyItem == IntPtr.Zero) + throw new Exception("failed to insert item"); + + var result = Runtime.PyList_Insert(seq, index, pyItem); + Runtime.XDecref(pyItem); + if (result == -1) + throw new Exception("failed to insert item"); + } + + public bool InternalRemoveAt(int index) + { + if (IsReadOnly) + throw new NotImplementedException(); + if (index >= Count || index < 0) + throw new IndexOutOfRangeException(); + + return Runtime.PySequence_DelItem(seq, index) != 0; + } + + public bool Remove(T item) + { + return InternalRemoveAt(IndexOf(item)); + } + + public void RemoveAt(int index) + { + InternalRemoveAt(index); + } + } +} diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index c6b87ee16..c96270b81 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; using System.Security; using System.ComponentModel; +using System.Linq; namespace Python.Runtime { @@ -268,7 +269,6 @@ internal static IntPtr ToPythonImplicit(object value) return ToPython(value, objectType); } - /// /// Return a managed object for the given Python object, taking funny /// byref types into account. @@ -868,35 +868,34 @@ private static bool ToListOfT(IntPtr value, Type elementType, out object result) { result = null; - bool IsSeqObj = Runtime.PySequence_Check(value); - var len = IsSeqObj ? Runtime.PySequence_Size(value) : -1; - - IntPtr IterObject = Runtime.PyObject_GetIter(value); - - if (IterObject == IntPtr.Zero) + IntPtr iterObject = Runtime.PyObject_GetIter(value); + if (iterObject == IntPtr.Zero) return false; - var listType = typeof(List<>); - var constructedListType = listType.MakeGenericType(elementType); - IList list = IsSeqObj ? (IList) Activator.CreateInstance(constructedListType, new Object[] { (int)len }) : - (IList) Activator.CreateInstance(constructedListType); - IntPtr item; - - while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero) - { - object obj = null; + bool IsSeqObj = Runtime.PySequence_Check(value); - if (!Converter.ToManaged(item, elementType, out obj, true)) - { - Runtime.XDecref(item); - return false; + try { + Type[] typeArgs = { elementType }; + if (IsSeqObj) { + var listType = typeof(ListWrapper<>); + object listObj = Activator.CreateInstance(listType.MakeGenericType(typeArgs), new Object[] { value }); + //IList list = (IList)Activator.CreateInstance(fullListType, new Object[] { value }); + + result = listObj; } + else { + var listType = typeof(IterableWrapper<>); + IEnumerable list = (IEnumerable)Activator.CreateInstance(listType.MakeGenericType(typeArgs), new Object[] { value }); + result = list; + } + } + catch (Exception) { + return false; - list.Add(obj); - Runtime.XDecref(item); } - Runtime.XDecref(IterObject); - result = list; + finally { + Runtime.XDecref(iterObject); + } return true; } @@ -917,10 +916,18 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s return false; } - IList list = (IList)result; + IList list = result as IList; + if (list == null) { + IEnumerable enumerable = (IEnumerable)result; + var listType = typeof(List<>); + var constructedListType = listType.MakeGenericType(elementType); + list = (IList)Activator.CreateInstance(constructedListType); + foreach (var item in enumerable) { + list.Add(item); + } + } Array items = Array.CreateInstance(elementType, list.Count); list.CopyTo(items, 0); - result = items; return true; @@ -947,14 +954,16 @@ private static bool ToAction(IntPtr value, Type obType, out object result, bool } Action action = () => { + var y = Runtime.Refcount(value); PyObject py_action = new PyObject(value); var py_args = new PyObject[0]; var py_result = py_action.Invoke(py_args); Runtime.XDecref(value); }; - + var x = Runtime.Refcount(value); Runtime.XIncref(value); result = action; + return true; }