8000 implement list codec by koubaa · Pull Request #1084 · pythonnet/pythonnet · GitHub
[go: up one dir, main page]

Skip to content

implement list codec #1084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Feb 18, 2021
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
Prev Previous commit
Next Next commit
improve CanDecode
  • Loading branch information
koubaa committed Feb 16, 2021
commit 765b6a221bf6414d147e96c0310c8b1b0045d26d
13 changes: 13 additions & 0 deletions src/embed_tests/Codecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,19 @@ raise StopIteration
System.Collections.IEnumerable plainEnumerable2 = null;
Assert.DoesNotThrow(() => { codec.TryDecode<System.Collections.IEnumerable>(x, out plainEnumerable2); });
checkPlainEnumerable(plainEnumerable2);

//can convert to any generic ienumerable. If the type is not assignable from the python element
//it will be an exception during TryDecode
Assert.IsTrue(codec.CanDecode(foo, typeof(IEnumerable<int>)));
Assert.IsTrue(codec.CanDecode(foo, typeof(IEnumerable<double>)));
Assert.IsTrue(codec.CanDecode(foo, typeof(IEnumerable<string>)));

//cannot convert to ICollection or IList of any type since the python type is only iterable
Assert.IsFalse(codec.CanDecode(foo, typeof(ICollection<string>)));
Assert.IsFalse(codec.CanDecode(foo, typeof(ICollection<int>)));
Assert.IsFalse(codec.CanDecode(foo, typeof(IList<int>)));


}
}

Expand Down
94 changes: 73 additions & 21 deletions src/runtime/Codecs/ListCodec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,95 @@ namespace Python.Runtime.Codecs
{
class ListCodec : IPyObjectDecoder
{
public bool CanDecode(PyObject objectType, Type targetType)
private enum CollectionRank
{
//order matters, this is in increasing order of specialization
None,
Iterable,
Sequence,
List
}

private CollectionRank GetRank(PyObject objectType)
{
var handle = objectType.Handle;
//first check if the PyObject is iterable.
IntPtr IterObject = Runtime.PyObject_GetIter(objectType.Handle);
IntPtr IterObject = Runtime.PyObject_GetIter(handle);
if (IterObject == IntPtr.Zero)
return false;
return CollectionRank.None;

//if it is a plain IEnumerable, we can decode it using sequence protocol.
if (targetType == typeof(System.Collections.IEnumerable))
return true;
//now check if its a sequence
if (Runtime.PySequence_Check(handle))
{
//last check if its a list
if (Runtime.PyList_Check(handle))
return CollectionRank.List;
return CollectionRank.Sequence;
}

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

Predicate<Type> IsCLRSequence = (Type type) => {
return (type.GetGenericTypeDefinition() == typeof(IEnumerable<>) ||
type.GetGenericTypeDefinition() == typeof(ICollection<>) ||
type.GetGenericTypeDefinition() == typeof(IList<>));
private CollectionRank GetRank(Type targetType)
{
//if it is a plain IEnumerable, we can decode it using sequence protocol.
if (targetType == typeof(System.Collections.IEnumerable))
return CollectionRank.Iterable;

Func<Type, CollectionRank> getRankOfType = (Type type) => {
if (type.GetGenericTypeDefinition() == typeof(IList<>))
return CollectionRank.List;
if (type.GetGenericTypeDefinition() == typeof(ICollection<>))
return CollectionRank.Sequence;
if (type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
return CollectionRank.Iterable;
return CollectionRank.None;
};

if (IsCLRSequence(targetType))
return true;
if (targetType.IsGenericType)
{
var thisRank = getRankOfType(targetType);
if (thisRank != CollectionRank.None)
return thisRank;
}

var maxRank = CollectionRank.None;
//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;
if (!itf.IsGenericType) continue;

var thisRank = getRankOfType(itf);

//this is the most specialized type. return early
if (thisRank == CollectionRank.List) return thisRank;

//if it is more specialized, assign to max rank
if ((int)thisRank > (int)maxRank)
maxRank = thisRank;
}

//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
return maxRank;
}


//if we get here we cannot decode it.
return false;
public bool CanDecode(PyObject objectType, Type targetType)
{
//get the python object rank
var pyRank = GetRank(objectType);
if (pyRank == CollectionRank.None)
return false;

//get the clr object rank
var clrRank = GetRank(targetType);
if (clrRank == CollectionRank.None)
return false;

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

return (int)pyRank >= (int)clrRank;
}

private class PyEnumerable : System.Collections.IEnumerable
Expand Down
0