@@ -8,43 +8,95 @@ namespace Python.Runtime.Codecs
8
8
{
9
9
class ListCodec : IPyObjectDecoder
10
10
{
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 )
12
21
{
22
+ var handle = objectType . Handle ;
13
23
//first check if the PyObject is iterable.
14
- IntPtr IterObject = Runtime . PyObject_GetIter ( objectType . Handle ) ;
24
+ IntPtr IterObject = Runtime . PyObject_GetIter ( handle ) ;
15
25
if ( IterObject == IntPtr . Zero )
16
- return false ;
26
+ return CollectionRank . None ;
17
27
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
+ }
21
36
22
- //if its not a plain IEnumerable it must be a generic type
23
- if ( ! targetType . IsGenericType ) return false ;
37
+ return CollectionRank . Iterable ;
38
+ }
24
39
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 ;
29
54
} ;
30
55
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
+ }
33
62
63
+ var maxRank = CollectionRank . None ;
34
64
//if it implements any of the standard C# collection interfaces, we can decode it.
35
65
foreach ( Type itf in targetType . GetInterfaces ( ) )
36
66
{
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 ;
39
77
}
40
78
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
+ }
44
81
45
82
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 ;
48
100
}
49
101
50
102
private class PyEnumerable : System . Collections . IEnumerable
0 commit comments