diff --git a/DSPythonNet3/DSPythonNet3.csproj b/DSPythonNet3/DSPythonNet3.csproj index 2554157..4fa0fa6 100644 --- a/DSPythonNet3/DSPythonNet3.csproj +++ b/DSPythonNet3/DSPythonNet3.csproj @@ -1,4 +1,4 @@ - + {F1541C2D-80A9-4FE7-8D9E-75A8B9FF3479} Library @@ -10,6 +10,7 @@ + diff --git a/DSPythonNet3/Encoders/ListEncodeDecoder.cs b/DSPythonNet3/Encoders/ListEncodeDecoder.cs index 91b8c18..fd2a48d 100644 --- a/DSPythonNet3/Encoders/ListEncodeDecoder.cs +++ b/DSPythonNet3/Encoders/ListEncodeDecoder.cs @@ -28,18 +28,18 @@ public bool TryDecode(PyObject pyObj, out T value) return false; } - using (var pyList = PyList.AsList(pyObj)) + if (typeof(T).IsGenericType) { - if (typeof(T).IsGenericType) + using (var pyList = PyList.AsList(pyObj)) { value = pyList.ToList(); } - else - { - value = (T)pyList.ToList(); - } return true; } + + var converted = ConvertToArrayList(pyObj); + value = (T)converted; + return true; } public PyObject TryEncode(object value) @@ -57,5 +57,54 @@ bool IPyObjectDecoder.CanDecode(PyType objectType, Type targetType) } return decodableTypes.IndexOf(targetType) >= 0; } + + private static IList ConvertToArrayList(PyObject pyObj) + { + using var pyList = PyList.AsList(pyObj); + var result = new ArrayList(); + foreach (PyObject item in pyList) + { + using (item) + { + result.Add(ConvertItem(item)); + } + } + + return result; + } + + private static object ConvertItem(PyObject item) + { + if (TryGetClrObject(item, out var clrObject)) + { + return clrObject; + } + + if (PyString.IsStringType(item)) + { + return item.AsManagedObject(typeof(string)); + } + + if (PyList.IsListType(item) || PyTuple.IsTupleType(item)) + { + return ConvertToArrayList(item); + } + + return item.AsManagedObject(typeof(object)); + } + + private static bool TryGetClrObject(PyObject pyObj, out object clrObject) + { + try + { + clrObject = pyObj.GetManagedObject(); + return clrObject != null; + } + catch + { + clrObject = null; + return false; + } + } } } diff --git a/DSpythonNet3Tests/ListEncoderDecoderTests.cs b/DSpythonNet3Tests/ListEncoderDecoderTests.cs new file mode 100644 index 0000000..b4d7f8c --- /dev/null +++ b/DSpythonNet3Tests/ListEncoderDecoderTests.cs @@ -0,0 +1,72 @@ +using System.Collections; +using NUnit.Framework; +using DSPythonNet3; + +namespace DSPythonNet3Tests +{ + public class ListEncoderDecoderTests + { + [Test] + public void TryDecode_ConvertsNestedPythonListsToClrLists() + { + DSPythonNet3Evaluator.InitializePython(); + + string code = @" +import clr +clr.AddReference('DSCoreNodes') +from DSCore import List + +data = [[[1, 2], [3]], [[4, 5], [6]]] +OUT = data, List.Flatten(data, 1), List.Flatten(data, 2), List.Flatten(data, -1) +"; + var empty = new ArrayList(); + var expected = new ArrayList + { + new ArrayList + { + new ArrayList + { + new ArrayList { 1, 2 }, + new ArrayList { 3 } + }, + new ArrayList + { + new ArrayList { 4, 5 }, + new ArrayList { 6 } + } + }, + new ArrayList + { + new ArrayList { 1, 2 }, + new ArrayList { 3 }, + new ArrayList { 4, 5 }, + new ArrayList { 6 } + }, + new ArrayList { 1, 2, 3, 4, 5, 6 }, + new ArrayList { 1, 2, 3, 4, 5, 6 } + }; + + var result = DSPythonNet3Evaluator.EvaluatePythonScript(code, empty, empty); + Assert.That(result, Is.InstanceOf()); + + var normalizedResult = NormalizeResult(result); + CollectionAssert.AreEqual(expected, normalizedResult as IEnumerable); + } + + private static object NormalizeResult(object value) + { + if (value is string || value is not IEnumerable enumerable) + { + return value; + } + + var list = new ArrayList(); + foreach (var item in enumerable) + { + list.Add(NormalizeResult(item)); + } + + return list; + } + } +}