diff --git a/.bumpversion.cfg b/.bumpversion.cfg index f4750c71e..66c2a831e 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.0.5.25 +current_version = 1.0.5.26 parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\.(?P[a-z]+)(?P\d+))? serialize = {major}.{minor}.{patch}.{release}{dev} diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index d87edf13f..b3433a871 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -1,6 +1,6 @@ package: name: pythonnet - version: "1.0.5.25" + version: "1.0.5.26" build: skip: True # [not win] diff --git a/setup.py b/setup.py index 6ed8b5d0a..27fc0edf3 100644 --- a/setup.py +++ b/setup.py @@ -485,7 +485,7 @@ def run(self): setup( name="pythonnet", - version="1.0.5.25", + version="1.0.5.26", description=".Net and Mono integration for Python", url='https://pythonnet.github.io/', license='MIT', diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs index 6b846d8d5..96376e07a 100644 --- a/src/SharedAssemblyInfo.cs +++ b/src/SharedAssemblyInfo.cs @@ -25,4 +25,4 @@ // Version Information. Keeping it simple. May need to revisit for Nuget // See: https://codingforsmarties.wordpress.com/2016/01/21/how-to-version-assemblies-destined-for-nuget/ // AssemblyVersion can only be numeric -[assembly: AssemblyVersion("1.0.5.25")] +[assembly: AssemblyVersion("1.0.5.26")] diff --git a/src/clrmodule/ClrModule.cs b/src/clrmodule/ClrModule.cs index 2674e2ac8..d5112da67 100644 --- a/src/clrmodule/ClrModule.cs +++ b/src/clrmodule/ClrModule.cs @@ -53,7 +53,7 @@ public static void initclr() { #if USE_PYTHON_RUNTIME_VERSION // Has no effect until SNK works. Keep updated anyways. - Version = new Version("1.0.5.25"), + Version = new Version("1.0.5.26"), #endif CultureInfo = CultureInfo.InvariantCulture }; diff --git a/src/embed_tests/TestConverter.cs b/src/embed_tests/TestConverter.cs index 346c8afdc..0f35570de 100644 --- a/src/embed_tests/TestConverter.cs +++ b/src/embed_tests/TestConverter.cs @@ -1,4 +1,6 @@ -using NUnit.Framework; +using System; +using System.Collections.Generic; +using NUnit.Framework; using Python.Runtime; namespace Python.EmbeddingTest @@ -17,6 +19,30 @@ public void Dispose() PythonEngine.Shutdown(); } + [Test] + public void ConvertListRoundTrip() + { + var list = new List { typeof(decimal), typeof(int) }; + var py = list.ToPython(); + object result; + var converted = Converter.ToManaged(py.Handle, typeof(List), out result, false); + + Assert.IsTrue(converted); + Assert.AreEqual(result, list); + } + + [Test] + public void ConvertPyListToArray() + { + var array = new List { typeof(decimal), typeof(int) }; + var py = array.ToPython(); + object result; + var converted = Converter.ToManaged(py.Handle, typeof(Type[]), out result, false); + + Assert.IsTrue(converted); + Assert.AreEqual(result, array); + } + [Test] public void TestConvertSingleToManaged( [Values(float.PositiveInfinity, float.NegativeInfinity, float.MinValue, float.MaxValue, float.NaN, diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 7beeb8759..b394a3f46 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Globalization; using System.Runtime.InteropServices; using System.Security; @@ -166,19 +167,20 @@ internal static IntPtr ToPython(object value, Type type) return result; } - if (value is IList && value.GetType().IsGenericType) + var list = value as IList; + if (list != null && value.GetType().IsGenericType) { - using (var resultlist = new PyList()) + using (var resultList = new PyList()) { - foreach (object o in (IEnumerable)value) + for (var i = 0; i < list.Count; i++) { - using (var p = new PyObject(ToPython(o, o?.GetType()))) + using (var p = list[i].ToPython()) { - resultlist.Append(p); + resultList.Append(p); } } - Runtime.XIncref(resultlist.Handle); - return resultlist.Handle; + Runtime.XIncref(resultList.Handle); + return resultList.Handle; } } @@ -382,6 +384,15 @@ internal static bool ToManagedValue(IntPtr value, Type obType, return true; } + if(obType.IsGenericType && Runtime.PyObject_TYPE(value) == Runtime.PyListType) + { + var typeDefinition = obType.GetGenericTypeDefinition(); + if (typeDefinition == typeof(List<>)) + { + return ToList(value, obType, out result, setError); + } + } + // Common case: if the Python value is a wrapped managed object // instance, just return the wrapped object. ManagedType mt = ManagedType.GetManagedObject(value); @@ -1003,7 +1014,6 @@ 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 sequence protocol and the @@ -1015,7 +1025,7 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s int size = Runtime.PySequence_Size(value); result = null; - if (size < 0 || elementType.IsGenericType) + if (elementType.IsGenericType) { if (setError) { @@ -1026,7 +1036,49 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s Array items = Array.CreateInstance(elementType, size); - // XXX - is there a better way to unwrap this if it is a real array? + var index = 0; + result = items; + return ApplyActionToPySequence(value, obType, setError, size, elementType, o => + { + items.SetValue(o, index++); + }); + } + + /// + /// Convert a Python value to a correctly typed managed list instance. + /// The Python value must support the Python sequence protocol and the + /// items in the sequence must be convertible to the target list type. + /// + private static bool ToList(IntPtr value, Type obType, out object result, bool setError) + { + var elementType = obType.GetGenericArguments()[0]; + var size = Runtime.PySequence_Size(value); + + result = Activator.CreateInstance(obType, args: size); + var resultList = (IList)result; + return ApplyActionToPySequence(value, obType, setError, size, elementType, o => resultList.Add(o)); + } + + /// + /// Helper method that will enumerate a Python sequence convert its values to the given + /// type and send them to the provided action + /// + private static bool ApplyActionToPySequence(IntPtr value, + Type obType, + bool setError, + int size, + Type elementType, + Action action) + { + if (size < 0) + { + if (setError) + { + SetConversionError(value, obType); + } + return false; + } + for (var i = 0; i < size; i++) { object obj = null; @@ -1046,15 +1098,13 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s return false; } - items.SetValue(obj, i); + action(obj); Runtime.XDecref(item); } - result = items; return true; } - /// /// Convert a Python value to a correctly typed managed enum instance. /// diff --git a/src/runtime/resources/clr.py b/src/runtime/resources/clr.py index 2182c3267..23aad8d32 100644 --- a/src/runtime/resources/clr.py +++ b/src/runtime/resources/clr.py @@ -2,7 +2,7 @@ Code in this module gets loaded into the main clr module. """ -__version__ = "1.0.5.25" +__version__ = "1.0.5.26" class clrproperty(object):