8000 improve CanDecode · pythonnet/pythonnet@765b6a2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 765b6a2

Browse files
committed
improve CanDecode
1 parent f80ae87 commit 765b6a2

File tree

2 files changed

+86
-21
lines changed

2 files changed

+86
-21
lines changed

src/embed_tests/Codecs.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,19 @@ raise StopIteration
133133
System.Collections.IEnumerable plainEnumerable2 = null;
134134
Assert.DoesNotThrow(() => { codec.TryDecode<System.Collections.IEnumerable>(x, out plainEnumerable2); });
135135
checkPlainEnumerable(plainEnumerable2);
136+
137+
//can convert to any generic ienumerable. If the type is not assignable from the python element
138+
//it will be an exception during TryDecode
139+
Assert.IsTrue(codec.CanDecode(foo, typeof(IEnumerable<int>)));
140+
Assert.IsTrue(codec.CanDecode(foo, typeof(IEnumerable<double>)));
141+
Assert.IsTrue(codec.CanDecode(foo, typeof(IEnumerable<string>)));
142+
143+
//cannot convert to ICollection or IList of any type since the python type is only iterable
144+
Assert.IsFalse(codec.CanDecode(foo, typeof(ICollection<string>)));
145+
Assert.IsFalse(codec.CanDecode(foo, typeof(ICollection<int>)));
146+
Assert.IsFalse(codec.CanDecode(foo, typeof(IList<int>)));
147+
148+
136149
}
137150
}
138151

src/runtime/Codecs/ListCodec.cs

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,95 @@ namespace Python.Runtime.Codecs
88
{
99
class ListCodec : IPyObjectDecoder
1010
{
11-
public bool CanDecode(PyObject objectType, Type targetType)
11+
private enum CollectionRank
12+
{
13+
//order matters, this is in increasing order of specialization
14+
None,
15+
Iterable,
16+
Sequence,
17+
List
18+
}
19+
20+
private CollectionRank GetRank(PyObject objectType)
1221
{
22+
var handle = objectType.Handle;
1323
//first check if the PyObject is iterable.
14-
IntPtr IterObject = Runtime.PyObject_GetIter(objectType.Handle);
24+
IntPtr IterObject = Runtime.PyObject_GetIter(handle);
1525
if (IterObject == IntPtr.Zero)
16-
return false;
26+
return CollectionRank.None;
1727

18-
//if it is a plain IEnumerable, we can decode it using sequence protocol.
19-
if (targetType == typeof(System.Collections.IEnumerable))
20-
return true;
28+
//now check if its a sequence
29+
if (Runtime.PySequence_Check(handle))
30+
{
31+
//last check if its a list
32+
if (Runtime.PyList_Check(handle))
33+
return CollectionRank.List;
34+
return CollectionRank.Sequence;
35+
}
2136

22-
//if its not a plain IEnumerable it must be a generic type
23-
if (!targetType.IsGenericType) return false;
37+
return CollectionRank.Iterable;
38+
}
2439

25-
Predicate<Type> IsCLRSequence = (Type type) => {
26-
return (type.GetGenericTypeDefinition() == typeof(IEnumerable<>) ||
27-
type.GetGenericTypeDefinition() == typeof(ICollection<>) ||
28-
type.GetGenericTypeDefinition() == typeof(IList<>));
40+
private CollectionRank GetRank(Type targetType)
41+
{
42+
//if it is a plain IEnumerable, we can decode it using sequence protocol.
43+
if (targetType == typeof(System.Collections.IEnumerable))
44+
return CollectionRank.Iterable;
45+
46+
Func<Type, CollectionRank> getRankOfType = (Type type) => {
47+
if (type.GetGenericTypeDefinition() == typeof(IList<>))
48+
return CollectionRank.List;
49+
if (type.GetGenericTypeDefinition() == typeof(ICollection<>))
50+
return CollectionRank.Sequence;
51+
if (type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
52+
return CollectionRank.Iterable;
53+
return CollectionRank.None;
2954
};
3055

31-
if (IsCLRSequence(targetType))
67ED
32-
return true;
56+
if (targetType.IsGenericType)
57+
{
58+
var thisRank = getRankOfType(targetType);
59+
if (thisRank != CollectionRank.None)
60+
return thisRank;
61+
}
3362

63+
var maxRank = CollectionRank.None;
3464
//if it implements any of the standard C# collection interfaces, we can decode it.
3565
foreach (Type itf in targetType.GetInterfaces())
3666
{
37-
if (IsCLRSequence(itf))
38-
return true;
67+
if (!itf.IsGenericType) continue;
68+
69+
var thisRank = getRankOfType(itf);
70+
71+
//this is the most specialized type. return early
72+
if (thisRank == CollectionRank.List) return thisRank;
73+
74+
//if it is more specialized, assign to max rank
75+
if ((int)thisRank > (int)maxRank)
76+
maxRank = thisRank;
3977
}
4078

41-
//TODO objectType should implement the Sequence protocol to be convertible to ICollection
42-
// and the list protocol to be convertible to IList. We should check for list first,
43-
// then collection, then enumerable
79+
return maxRank;
80+
}
4481

4582

46-
//if we get here we cannot decode it.
47-
return false;
83+
public bool CanDecode(PyObject objectType, Type targetType)
84+
{
85+
//get the python object rank
86+
var pyRank = GetRank(objectType);
87+
if (pyRank == CollectionRank.None)
88+
return false;
89+
90+
//get the clr object rank
91+
var clrRank = GetRank(targetType);
92+
if (clrRank == CollectionRank.None)
93+
return false;
94+
95+
//if it is a plain IEnumerable, we can decode it using sequence protocol.
96+
if (targetType == typeof(System.Collections.IEnumerable))
97+
return true;
98+
99+
return (int)pyRank >= (int)clrRank;
48100
}
49101

50102
private class PyEnumerable : System.Collections.IEnumerable

0 commit comments

Comments
 (0)
0