@@ -75,6 +75,8 @@ private Tuple<CollectionRank, Type> GetRankAndType(Type collectionType)
75
75
76
76
public bool CanDecode ( PyObject objectType , Type targetType )
77
77
{
78
+ //TODO - convert pyTuple to IReadOnlyList
79
+
78
80
//get the python object rank
79
81
var pyRank = GetRank ( objectType ) ;
80
82
if ( pyRank == CollectionRank . None )
@@ -92,12 +94,14 @@ public bool CanDecode(PyObject objectType, Type targetType)
92
94
return ( int ) pyRank >= ( int ) clrRank ;
93
95
}
94
96
95
- private class GenericPyEnumerable < T > : IEnumerable < T >
97
+ private class PyEnumerable < T > : IEnumerable < T >
96
98
{
97
99
protected PyObject iterObject ;
100
+ protected PyObject pyObject ;
98
101
99
- internal GenericPyEnumerable ( PyObject pyObj )
102
+ public PyEnumerable ( PyObject pyObj )
100
103
{
104
+ pyObject = pyObj ;
101
105
iterObject = new PyObject ( Runtime . PyObject_GetIter ( pyObj . Handle ) ) ;
102
106
}
103
107
@@ -109,6 +113,7 @@ IEnumerator IEnumerable.GetEnumerator()
109
113
object obj = null ;
110
114
if ( ! Converter . ToManaged ( item , typeof ( object ) , out obj , true ) )
111
115
{
116
+ Exceptions . Clear ( ) ;
112
117
Runtime . XDecref ( item ) ;
113
118
break ;
114
119
}
@@ -126,6 +131,7 @@ public IEnumerator<T> GetEnumerator()
126
131
object obj = null ;
127
132
if ( ! Converter . ToManaged ( item , typeof ( T ) , out obj , true ) )
128
133
{
134
+ Exceptions . Clear ( ) ;
129
135
Runtime . XDecref ( item ) ;
130
136
break ;
131
137
}
@@ -136,35 +142,194 @@ public IEnumerator<T> GetEnumerator()
136
142
}
137
143
}
138
144
139
- private object ToPlainEnumerable ( PyObject pyObj )
145
+ private class PyCollection < T > : PyEnumerable < T > , ICollection < T >
140
146
{
141
- return new GenericPyEnumerable < object > ( pyObj ) ;
147
+ public PyCollection ( PyObject pyObj ) : base ( pyObj )
148
+ {
149
+
150
+ }
151
+
152
+ public int Count
153
+ {
154
+ get
155
+ {
156
+ return ( int ) Runtime . PySequence_Size ( pyObject . Handle ) ;
157
+ }
158
+ }
159
+
160
+ public virtual bool IsReadOnly => false ;
161
+
162
+ public virtual void Add ( T item )
163
+ {
164
+ //not implemented for Python sequence rank
165
+ throw new NotImplementedException ( ) ;
166
+ }
167
+
168
+ public void Clear ( )
169
+ {
170
+ if ( IsReadOnly )
171
+ throw new NotImplementedException ( ) ;
172
+ var result = Runtime . PySequence_DelSlice ( pyObject . Handle , 0 , Count ) ;
173
+ if ( result == - 1 )
174
+ throw new Exception ( "failed to clear sequence" ) ;
175
+ }
176
+
177
+ public bool Contains ( T item )
178
+ {
179
+ //not sure if IEquatable is implemented and this will work!
180
+ foreach ( var element in this )
181
+ if ( element . Equals ( item ) ) return true ;
182
+
183
+ return false ;
184
+ }
185
+
186
+ protected T getItem ( int index )
187
+ {
188
+ IntPtr item = Runtime . PySequence_GetItem ( pyObject . Handle , index ) ;
189
+ object obj ;
190
+
191
+ if ( ! Converter . ToManaged ( item , typeof ( T ) , out obj , true ) )
192
+ {
193
+ Exceptions . Clear ( ) ;
194
+ Runtime . XDecref ( item ) ;
195
+ Exceptions . RaiseTypeError ( "wrong type in sequence" ) ;
196
+ }
197
+
198
+ return ( T ) obj ;
199
+ }
200
+
201
+ public void CopyTo ( T [ ] array , int arrayIndex )
202
+ {
203
+ for ( int index = 0 ; index < Count ; index ++ )
204
+ {
205
+ array [ index + arrayIndex ] = getItem ( index ) ;
206
+ }
207
+ }
208
+
209
+ protected bool removeAt ( int index )
210
+ {
211
+ if ( IsReadOnly )
212
+ throw new NotImplementedException ( ) ;
213
+ if ( index >= Count || index < 0 )
214
+ throw new IndexOutOfRangeException ( ) ;
215
+
216
+ return Runtime . PySequence_DelItem ( pyObject . Handle , index ) != 0 ;
217
+ }
218
+
219
+ protected int indexOf ( T item )
220
+ {
221
+ var index = 0 ;
222
+ foreach ( var element in this )
223
+ {
224
+ if ( element . Equals ( item ) ) return index ;
225
+ index ++ ;
226
+ }
227
+
228
+ return - 1 ;
229
+ }
230
+
231
+ public bool Remove ( T item )
232
+ {
233
+ return removeAt ( indexOf ( item ) ) ;
234
+ }
142
235
}
143
- private object ToEnumerable < T > ( PyObject pyObj )
236
+
237
+ private class PyList < T > : PyCollection < T > , IList < T >
144
238
{
145
- return new GenericPyEnumerable < T > ( pyObj ) ;
239
+ public PyList ( PyObject pyObj ) : base ( pyObj )
240
+ {
241
+
242
+ }
243
+
244
+ public T this [ int index ]
245
+ {
246
+ get
247
+ {
248
+ IntPtr item = Runtime . PySequence_GetItem ( pyObject . Handle , index ) ;
249
+ object obj ;
250
+
251
+ if ( ! Converter . ToManaged ( item , typeof ( T ) , out obj , true ) )
252
+ {
253
+ Exceptions . Clear ( ) ;
254
+ Runtime . XDecref ( item ) ;
255
+ Exceptions . RaiseTypeError ( "wrong type in sequence" ) ;
256
+ }
257
+
258
+ return ( T ) obj ;
259
+ }
260
+ set
261
+ {
262
+ IntPtr pyItem = Converter . ToPython ( value , typeof ( T ) ) ;
263
+ if ( pyItem == IntPtr . Zero )
264
+ throw new Exception ( "failed to set item" ) ;
265
+
266
+ var result = Runtime . PySequence_SetItem ( pyObject . Handle , index , pyItem ) ;
267
+ Runtime . XDecref ( pyItem ) ;
268
+ if ( result == - 1 )
269
+ throw new Exception ( "failed to set item" ) ;
270
+ }
271
+ }
272
+
273
+ public int IndexOf ( T item )
274
+ {
275
+ return indexOf ( item ) ;
276
+ }
277
+
278
+ public void Insert ( int index , T item )
279
+ {
280
+ if ( IsReadOnly )
281
+ throw new NotImplementedException ( ) ;
282
+
283
+ IntPtr pyItem = Converter . ToPython ( item , typeof ( T ) ) ;
284
+ if ( pyItem == IntPtr . Zero )
285
+ throw new Exception ( "failed to insert item" ) ;
286
+
287
+ var result = Runtime . PyList_Insert ( pyObject . Handle , index , pyItem ) ;
288
+ Runtime . XDecref ( pyItem ) ;
289
+ if ( result == - 1 )
290
+ throw new Exception ( "failed to insert item" ) ;
291
+ }
292
+
293
+ public void RemoveAt ( int index )
294
+ {
295
+ removeAt ( index ) ;
296
+ }
146
297
}
147
298
148
299
public bool TryDecode < T > ( PyObject pyObj , out T value )
149
300
{
150
- object var = null ;
151
301
//first see if T is a plan IEnumerable
152
302
if ( typeof ( T ) == typeof ( System . Collections . IEnumerable ) )
153
303
{
154
- var = new GenericPyEnumerable < object > ( pyObj ) ;
304
+ object enumerable = new PyEnumerable < object > ( pyObj ) ;
305
+ value = ( T ) enumerable ;
306
+ return true ;
155
307
}
156
308
157
309
//next use the rank to return the appropriate type
158
- var clrRank = GetRank ( typeof ( T ) ) ;
159
- if ( clrRank == CollectionRank . Iterable )
160
- var = new GenericPyEnumerable < int > ( pyObj ) ;
161
- else
310
+ var rankAndType = GetRankAndType ( typeof ( T ) ) ;
311
+ if ( rankAndType . Item1 == CollectionRank . None )
312
+ throw new Exception ( "expected collection rank" ) ;
313
+
314
+
315
+ var itemType = rankAndType . Item2 ;
316
+ Type collectionType = null ;
317
+ if ( rankAndType . Item1 == CollectionRank . Iterable )
162
318
{
163
- //var =null ;
319
+ collectionType = typeof ( PyEnumerable < > ) . MakeGenericType ( itemType ) ;
164
320
}
165
-
166
- value = ( T ) var ;
167
- return false ;
321
+ else if ( rankAndType . Item1 == CollectionRank . Sequence )
322
+ {
323
+ collectionType = typeof ( PyCollection < > ) . MakeGenericType ( itemType ) ;
324
+ }
325
+ else if ( rankAndType . Item1 == CollectionRank . List )
326
+ {
327
+ collectionType = typeof ( PyList < > ) . MakeGenericType ( itemType ) ;
328
+ }
329
+
330
+ var instance = Activator . CreateInstance ( collectionType , new [ ] { pyObj } ) ;
331
+ value = ( T ) instance ;
332
+ return true ;
168
333
}
169
334
170
335
public static ListCodec Instance { get ; } = new ListCodec ( ) ;