Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
begin to implement list codec
  • Loading branch information
koubaa committed Feb 16, 2021
commit b500aa1837bfb83d367e9fc0e2ca7e777e6b0bfa
27 changes: 26 additions & 1 deletion src/embed_tests/Codecs.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Python.EmbeddingTest {
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using NUnit.Framework;
using Python.Runtime;
using Python.Runtime.Codecs;
Expand Down Expand Up @@ -82,6 +82,31 @@ static void TupleRoundtripGeneric<T, TTuple>() {
Assert.AreEqual(expected: tuple, actual: restored);
}
}

[Test]
public void ListCodecTest()
{
var codec = ListCodec.Instance;
var items = new List<PyObject>() { new PyInt(1), new PyInt(2), new PyInt(3) };

var x = new PyList(items.ToArray());
Assert.IsTrue(codec.CanDecode(x, typeof(List<int>)));
Assert.IsTrue(codec.CanDecode(x, typeof(IList<bool>)));
Assert.IsTrue(codec.CanDecode(x, typeof(System.Collections.IEnumerable)));
Assert.IsTrue(codec.CanDecode(x, typeof(IEnumerable<int>)));
Assert.IsTrue(codec.CanDecode(x, typeof(ICollection<float>)));
Assert.IsFalse(codec.CanDecode(x, typeof(bool)));

System.Collections.IEnumerable plainEnumerable = null;
Assert.DoesNotThrow(() => { codec.TryDecode<System.Collections.IEnumerable>(x, out plainEnumerable); });
Assert.IsNotNull(plainEnumerable);
IList<object> list = null;
list = plainEnumerable.Cast<object>().ToList();
Assert.AreEqual(list.Count, 3);
Assert.AreEqual(list[0], 1);
Assert.AreEqual(list[1], 2);
Assert.AreEqual(list[2], 3);
}
}

/// <summary>
Expand Down
101 changes: 101 additions & 0 deletions src/runtime/Codecs/ListCodec.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Python.Runtime.Codecs
{
class ListCodec : IPyObjectDecoder
{
public bool CanDecode(PyObject objectType, Type targetType)
{
//first check if the PyObject is iterable.
IntPtr IterObject = Runtime.PyObject_GetIter(objectType.Handle);
if (IterObject == IntPtr.Zero)
return false;

//if it is a plain IEnumerable, we can decode it using sequence protocol.
if (targetType == typeof(System.Collections.IEnumerable))
return true;

//if its not a plain IEnumerable it must be a generic type
if (!targetType.IsGenericType) return false;

Predicate<Type> IsCLRSequence = (Type type) => {
return (type.GetGenericTypeDefinition() == typeof(IEnumerable<>) ||
type.GetGenericTypeDefinition() == typeof(ICollection<>) ||
type.GetGenericTypeDefinition() == typeof(IList<>));
};

if (IsCLRSequence(targetType))
return true;

//if it implements any of the standard C# collection interfaces, we can decode it.
foreach (Type itf in targetType.GetInterfaces())
{
if (IsCLRSequence(itf))
return true;
}

//TODO objectType should implement the Sequence protocol to be convertible to ICollection
// and the list protocol to be convertible to IList. We should check for list first,
// then collection, then enumerable


//if we get here we cannot decode it.
return false;
}

private class PyEnumerable : System.Collections.IEnumerable
{
PyObject iterObject;
internal PyEnumerable(PyObject pyObj)
{
iterObject = new PyObject(Runtime.PyObject_GetIter(pyObj.Handle));
}

public IEnumerator GetEnumerator()
{
IntPtr item;
while ((item = Runtime.PyIter_Next(iterObject.Handle)) != IntPtr.Zero)
{
object obj = null;
if (!Converter.ToManaged(item, typeof(object), out obj, true))
{
Runtime.XDecref(item);
break;
}

Runtime.XDecref(item);
yield return obj;
}
}
}

private object ToPlainEnumerable(PyObject pyObj)
{
return new PyEnumerable(pyObj);
}

public bool TryDecode<T>(PyObject pyObj, out T value)
{
object var = null;
//first see if T is a plan IEnumerable
if (typeof(T) == typeof(System.Collections.IEnumerable))
{
var = ToPlainEnumerable(pyObj);
}

value = (T)var;
return false;
}

public static ListCodec Instance { get; } = new ListCodec();

public static void Register()
{
PyObjectConversions.RegisterDecoder(Instance);
}
}
}