@@ -75,6 +75,8 @@ private Tuple<CollectionRank, Type> GetRankAndType(Type collectionType)
7575
7676 public bool CanDecode ( PyObject objectType , Type targetType )
7777 {
78+ //TODO - convert pyTuple to IReadOnlyList
79+
7880 //get the python object rank
7981 var pyRank = GetRank ( objectType ) ;
8082 if ( pyRank == CollectionRank . None )
@@ -92,12 +94,14 @@ public bool CanDecode(PyObject objectType, Type targetType)
9294 return ( int ) pyRank >= ( int ) clrRank ;
9395 }
9496
95- private class GenericPyEnumerable < T > : IEnumerable < T >
97+ private class PyEnumerable < T > : IEnumerable < T >
9698 {
9799 protected PyObject iterObject ;
100+ protected PyObject pyObject ;
98101
99- internal GenericPyEnumerable ( PyObject pyObj )
102+ public PyEnumerable ( PyObject pyObj )
100103 {
104+ pyObject = pyObj ;
101105 iterObject = new PyObject ( Runtime . PyObject_GetIter ( pyObj . Handle ) ) ;
102106 }
103107
@@ -109,6 +113,7 @@ IEnumerator IEnumerable.GetEnumerator()
109113 object obj = null ;
110114 if ( ! Converter . ToManaged ( item , typeof ( object ) , out obj , true ) )
111115 {
116+ Exceptions . Clear ( ) ;
112117 Runtime . XDecref ( item ) ;
113118 break ;
114119 }
@@ -126,6 +131,7 @@ public IEnumerator<T> GetEnumerator()
126131 object obj = null ;
127132 if ( ! Converter . ToManaged ( item , typeof ( T ) , out obj , true ) )
128133 {
134+ Exceptions . Clear ( ) ;
129135 Runtime . XDecref ( item ) ;
130136 break ;
131137 }
@@ -136,35 +142,194 @@ public IEnumerator<T> GetEnumerator()
136142 }
137143 }
138144
139- private object ToPlainEnumerable ( PyObject pyObj )
145+ private class PyCollection < T > : PyEnumerable < T > , ICollection < T >
140146 {
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+ }
142235 }
143- private object ToEnumerable < T > ( PyObject pyObj )
236+
237+ private class PyList < T > : PyCollection < T > , IList < T >
144238 {
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+ }
146297 }
147298
148299 public bool TryDecode < T > ( PyObject pyObj , out T value )
149300 {
150- object var = null ;
151301 //first see if T is a plan IEnumerable
152302 if ( typeof ( T ) == typeof ( System . Collections . IEnumerable ) )
153303 {
154- var = new GenericPyEnumerable < object > ( pyObj ) ;
304+ object enumerable = new PyEnumerable < object > ( pyObj ) ;
305+ value = ( T ) enumerable ;
306+ return true ;
155307 }
156308
157309 //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 )
162318 {
163- //var = null ;
319+ collectionType = typeof ( PyEnumerable < > ) . MakeGenericType ( itemType ) ;
164320 }
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 ;
168333 }
169334
170335 public static ListCodec Instance { get ; } = new ListCodec ( ) ;
0 commit comments