@@ -6,28 +6,34 @@ namespace Python.EmbeddingTest {
66 using Python . Runtime ;
77 using Python . Runtime . Codecs ;
88
9- public class Codecs {
9+ public class Codecs
10+ {
1011 [ SetUp ]
11- public void SetUp ( ) {
12+ public void SetUp ( )
13+ {
1214 PythonEngine . Initialize ( ) ;
1315 }
1416
1517 [ TearDown ]
16- public void Dispose ( ) {
18+ public void Dispose ( )
19+ {
1720 PythonEngine . Shutdown ( ) ;
1821 }
1922
2023 [ Test ]
21- public void ConversionsGeneric ( ) {
22- ConversionsGeneric < ValueTuple < int , string , object > , ValueTuple > ( ) ;
24+ public void TupleConversionsGeneric ( )
25+ {
26+ TupleConversionsGeneric < ValueTuple < int , string , object > , ValueTuple > ( ) ;
2327 }
2428
25- static void ConversionsGeneric < T , TTuple > ( ) {
29+ static void TupleConversionsGeneric < T , TTuple > ( )
30+ {
2631 TupleCodec < TTuple > . Register ( ) ;
2732 var tuple = Activator . CreateInstance ( typeof ( T ) , 42 , "42" , new object ( ) ) ;
2833 T restored = default ;
2934 using ( Py . GIL ( ) )
30- using ( var scope = Py . CreateScope ( ) ) {
35+ using ( var scope = Py . CreateScope ( ) )
36+ {
3137 void Accept ( T value ) => restored = value ;
3238 var accept = new Action < T > ( Accept ) . ToPython ( ) ;
3339 scope . Set ( nameof ( tuple ) , tuple ) ;
@@ -38,15 +44,18 @@ static void ConversionsGeneric<T, TTuple>() {
3844 }
3945
4046 [ Test ]
41- public void ConversionsObject ( ) {
42- ConversionsObject < ValueTuple < int , string , object > , ValueTuple > ( ) ;
47+ public void TupleConversionsObject ( )
48+ {
49+ TupleConversionsObject < ValueTuple < int , string , object > , ValueTuple > ( ) ;
4350 }
44- static void ConversionsObject < T , TTuple > ( ) {
51+ static void TupleConversionsObject < T , TTuple > ( )
52+ {
4553 TupleCodec < TTuple > . Register ( ) ;
4654 var tuple = Activator . CreateInstance ( typeof ( T ) , 42 , "42" , new object ( ) ) ;
4755 T restored = default ;
4856 using ( Py . GIL ( ) )
49- using ( var scope = Py . CreateScope ( ) ) {
57+ using ( var scope = Py . CreateScope ( ) )
58+ {
5059 void Accept ( object value ) => restored = ( T ) value ;
5160 var accept = new Action < object > ( Accept ) . ToPython ( ) ;
5261 scope . Set ( nameof ( tuple ) , tuple ) ;
@@ -57,38 +66,42 @@ static void ConversionsObject<T, TTuple>() {
5766 }
5867
5968 [ Test ]
60- public void TupleRoundtripObject ( ) {
69+ public void TupleRoundtripObject ( )
70+ {
6171 TupleRoundtripObject < ValueTuple < int , string , object > , ValueTuple > ( ) ;
6272 }
63- static void TupleRoundtripObject < T , TTuple > ( ) {
73+ static void TupleRoundtripObject < T , TTuple > ( )
74+ {
6475 var tuple = Activator . CreateInstance ( typeof ( T ) , 42 , "42" , new object ( ) ) ;
65- using ( Py . GIL ( ) ) {
76+ using ( Py . GIL ( ) )
77+ {
6678 var pyTuple = TupleCodec < TTuple > . Instance . TryEncode ( tuple ) ;
6779 Assert . IsTrue ( TupleCodec < TTuple > . Instance . TryDecode ( pyTuple , out object restored ) ) ;
6880 Assert . AreEqual ( expected : tuple , actual : restored ) ;
6981 }
7082 }
7183
7284 [ Test ]
73- public void TupleRoundtripGeneric ( ) {
85+ public void TupleRoundtripGeneric ( )
86+ {
7487 TupleRoundtripGeneric < ValueTuple < int , string , object > , ValueTuple > ( ) ;
7588 }
7689
77- static void TupleRoundtripGeneric < T , TTuple > ( ) {
90+ static void TupleRoundtripGeneric < T , TTuple > ( )
91+ {
7892 var tuple = Activator . CreateInstance ( typeof ( T ) , 42 , "42" , new object ( ) ) ;
79- using ( Py . GIL ( ) ) {
93+ using ( Py . GIL ( ) )
94+ {
8095 var pyTuple = TupleCodec < TTuple > . Instance . TryEncode ( tuple ) ;
8196 Assert . IsTrue ( TupleCodec < TTuple > . Instance . TryDecode ( pyTuple , out T restored ) ) ;
8297 Assert . AreEqual ( expected : tuple , actual : restored ) ;
8398 }
8499 }
85100
86- static PyObject GetPythonIterable ( )
101+ static string GetIntIterableCommands ( string instanceName )
87102 {
88- var locals = new PyDict ( ) ;
89- using ( Py . GIL ( ) )
90- {
91- PythonEngine . Exec ( @"
103+ var builder = new System . Text . StringBuilder ( ) ;
104+ builder . AppendLine ( @"
92105class foo():
93106 def __init__(self):
94107 self.counter = 0
@@ -98,9 +111,18 @@ def __next__(self):
98111 if self.counter == 3:
99112 raise StopIteration
100113 self.counter = self.counter + 1
101- return self.counter
102- foo_instance = foo()
103- " , null , locals . Handle ) ;
114+ return self.counter" ) ;
115+
116+ builder . AppendLine ( instanceName + " = foo()" ) ;
117+ return builder . ToString ( ) ;
118+ }
119+
120+ static PyObject GetPythonIterable ( )
121+ {
122+ var locals = new PyDict ( ) ;
123+ using ( Py . GIL ( ) )
124+ {
125+ PythonEngine . Exec ( GetIntIterableCommands ( "foo_instance" ) , null , locals . Handle ) ;
104126 }
105127
106128 return locals . GetItem ( "foo_instance" ) ;
@@ -113,16 +135,18 @@ public void ListDecoderTest()
113135 var items = new List < PyObject > ( ) { new PyInt ( 1 ) , new PyInt ( 2 ) , new PyInt ( 3 ) } ;
114136
115137 var pyList = new PyList ( items . ToArray ( ) ) ;
116- Assert . IsTrue ( codec . CanDecode ( pyList , typeof ( IList < bool > ) ) ) ;
117- Assert . IsTrue ( codec . CanDecode ( pyList , typeof ( IList < int > ) ) ) ;
118- Assert . IsFalse ( codec . CanDecode ( pyList , typeof ( System . Collections . IEnumerable ) ) ) ;
119- Assert . IsFalse ( codec . CanDecode ( pyList , typeof ( IEnumerable < int > ) ) ) ;
120- Assert . IsFalse ( codec . CanDecode ( pyList , typeof ( ICollection < float > ) ) ) ;
121- Assert . IsFalse ( codec . CanDecode ( pyList , typeof ( bool ) ) ) ;
138+
139+ var pyListType = pyList . GetPythonType ( ) ;
140+ Assert . IsTrue ( codec . CanDecode ( pyListType , typeof ( IList < bool > ) ) ) ;
141+ Assert . IsTrue ( codec . CanDecode ( pyListType , typeof ( IList < int > ) ) ) ;
142+ Assert . IsFalse ( codec . CanDecode ( pyListType , typeof ( System . Collections . IEnumerable ) ) ) ;
143+ Assert . IsFalse ( codec . CanDecode ( pyListType , typeof ( IEnumerable < int > ) ) ) ;
144+ Assert . IsFalse ( codec . CanDecode ( pyListType , typeof ( ICollection < float > ) ) ) ;
145+ Assert . IsFalse ( codec . CanDecode ( pyListType , typeof ( bool ) ) ) ;
122146
123147 //we'd have to copy into a list instance to do this, it would not be lossless.
124148 //lossy converters can be implemented outside of the python.net core library
125- Assert . IsFalse ( codec . CanDecode ( pyList , typeof ( List < int > ) ) ) ;
149+ Assert . IsFalse ( codec . CanDecode ( pyListType , typeof ( List < int > ) ) ) ;
126150
127151 //convert to list of int
128152 IList < int > intList = null ;
@@ -137,11 +161,12 @@ public void ListDecoderTest()
137161 IList < string > stringList = null ;
138162 Assert . DoesNotThrow ( ( ) => { codec . TryDecode ( pyList , out stringList ) ; } ) ;
139163 Assert . AreEqual ( stringList . Count , 3 ) ;
140- Assert . Throws ( typeof ( PythonException ) , ( ) => { var x = stringList [ 0 ] ; } ) ;
164+ Assert . Throws ( typeof ( PythonException ) , ( ) => { var x = stringList [ 0 ] ; } ) ;
141165
142166 //can't convert python iterable to list (this will require a copy which isn't lossless)
143167 var foo = GetPythonIterable ( ) ;
144- Assert . IsFalse ( codec . CanDecode ( foo , typeof ( IList < int > ) ) ) ;
168+ var fooType = foo . GetPythonType ( ) ;
169+ Assert . IsFalse ( codec . CanDecode ( fooType , typeof ( IList < int > ) ) ) ;
145170 }
146171
147172 [ Test ]
@@ -189,18 +214,20 @@ public void SequenceDecoderTest()
189214 //can't convert python iterable to collection (this will require a copy which isn't lossless)
190215 //python iterables do not satisfy the python sequence protocol
191216 var foo = GetPythonIterable ( ) ;
192- Assert . IsFalse ( codec . CanDecode ( foo , typeof ( ICollection < int > ) ) ) ;
217+ var fooType = foo . GetPythonType ( ) ;
218+ Assert . IsFalse ( codec . CanDecode ( fooType , typeof ( ICollection < int > ) ) ) ;
193219
194220 //python tuples do satisfy the python sequence protocol
195221 var pyTuple = new PyObject ( Runtime . PyTuple_New ( 3 ) ) ;
222+ var pyTupleType = pyTuple . GetPythonType ( ) ;
196223
197224 Runtime . PyTuple_SetItem ( pyTuple . Handle , 0 , items [ 0 ] . Handle ) ;
198225 Runtime . PyTuple_SetItem ( pyTuple . Handle , 1 , items [ 1 ] . Handle ) ;
199226 Runtime . PyTuple_SetItem ( pyTuple . Handle , 2 , items [ 2 ] . Handle ) ;
200227
201- Assert . IsTrue ( codec . CanDecode ( pyTuple , typeof ( ICollection < float > ) ) ) ;
202- Assert . IsTrue ( codec . CanDecode ( pyTuple , typeof ( ICollection < int > ) ) ) ;
203- Assert . IsTrue ( codec . CanDecode ( pyTuple , typeof ( ICollection < string > ) ) ) ;
228+ Assert . IsTrue ( codec . CanDecode ( pyTupleType , typeof ( ICollection < float > ) ) ) ;
229+ Assert . IsTrue ( codec . CanDecode ( pyTupleType , typeof ( ICollection < int > ) ) ) ;
230+ Assert . IsTrue ( codec . CanDecode ( pyTupleType , typeof ( ICollection < string > ) ) ) ;
204231
205232 //convert to collection of int
206233 ICollection < int > intCollection2 = null ;
@@ -226,46 +253,6 @@ public void SequenceDecoderTest()
226253 Runtime . CheckExceptionOccurred ( ) ;
227254
228255 }
229- }
230-
231- /// <summary>
232- /// "Decodes" only objects of exact type <typeparamref name="T"/>.
233- /// Result is just the raw proxy to the encoder instance itself.
234- /// </summary>
235- class ObjectToEncoderInstanceEncoder < T > : IPyObjectEncoder
236- {
237- public bool CanEncode ( Type type ) => type == typeof ( T ) ;
238- public PyObject TryEncode ( object value ) => PyObject . FromManagedObject ( this ) ;
239- }
240-
241- /// <summary>
242- /// Decodes object of specified Python type to the predefined value <see cref="DecodeResult"/>
243- /// </summary>
244- /// <typeparam name="TTarget">Type of the <see cref="DecodeResult"/></typeparam>
245- class DecoderReturningPredefinedValue < TTarget > : IPyObjectDecoder
246- {
247- public PyObject TheOnlySupportedSourceType { get ; }
248- public TTarget DecodeResult { get ; }
249-
250- public DecoderReturningPredefinedValue ( PyObject objectType , TTarget decodeResult )
251- {
252- this . TheOnlySupportedSourceType = objectType ;
253- this . DecodeResult = decodeResult ;
254- }
255-
256- public bool CanDecode ( PyObject objectType , Type targetType )
257- => objectType . Handle == TheOnlySupportedSourceType . Handle
258- && targetType == typeof ( TTarget ) ;
259- public bool TryDecode < T > ( PyObject pyObj , out T value )
260- {
261- if ( typeof ( T ) != typeof ( TTarget ) )
262- throw new ArgumentException ( nameof ( T ) ) ;
263- value = ( T ) ( object ) DecodeResult ;
264- return true ;
265- }
266- }
267- }
268-
269256
270257 [ Test ]
271258 public void IterableDecoderTest ( )
@@ -274,11 +261,12 @@ public void IterableDecoderTest()
274261 var items = new List < PyObject > ( ) { new PyInt ( 1 ) , new PyInt ( 2 ) , new PyInt ( 3 ) } ;
275262
276263 var pyList = new PyList ( items . ToArray ( ) ) ;
277- Assert . IsFalse ( codec . CanDecode ( pyList , typeof ( IList < bool > ) ) ) ;
278- Assert . IsTrue ( codec . CanDecode ( pyList , typeof ( System . Collections . IEnumerable ) ) ) ;
279- Assert . IsTrue ( codec . CanDecode ( pyList , typeof ( IEnumerable < int > ) ) ) ;
280- Assert . IsFalse ( codec . CanDecode ( pyList , typeof ( ICollection < float > ) ) ) ;
281- Assert . IsFalse ( codec . CanDecode ( pyList , typeof ( bool ) ) ) ;
264+ var pyListType = pyList . GetPythonType ( ) ;
265+ Assert . IsFalse ( codec . CanDecode ( pyListType , typeof ( IList < bool > ) ) ) ;
266+ Assert . IsTrue ( codec . CanDecode ( pyListType , typeof ( System . Collections . IEnumerable ) ) ) ;
267+ Assert . IsTrue ( codec . CanDecode ( pyListType , typeof ( IEnumerable < int > ) ) ) ;
268+ Assert . IsFalse ( codec . CanDecode ( pyListType , typeof ( ICollection < float > ) ) ) ;
269+ Assert . IsFalse ( codec . CanDecode ( pyListType , typeof ( bool ) ) ) ;
282270
283271 //ensure a PyList can be converted to a plain IEnumerable
284272 System . Collections . IEnumerable plainEnumerable1 = null ;
@@ -287,9 +275,9 @@ public void IterableDecoderTest()
287275
288276 //can convert to any generic ienumerable. If the type is not assignable from the python element
289277 //it will lead to an empty iterable when decoding. TODO - should it throw?
290- Assert . IsTrue ( codec . CanDecode ( pyList , typeof ( IEnumerable < int > ) ) ) ;
291- Assert . IsTrue ( codec . CanDecode ( pyList , typeof ( IEnumerable < double > ) ) ) ;
292- Assert . IsTrue ( codec . CanDecode ( pyList , typeof ( IEnumerable < string > ) ) ) ;
278+ Assert . IsTrue ( codec . CanDecode ( pyListType , typeof ( IEnumerable < int > ) ) ) ;
279+ Assert . IsTrue ( codec . CanDecode ( pyListType , typeof ( IEnumerable < double > ) ) ) ;
280+ Assert . IsTrue ( codec . CanDecode ( pyListType , typeof ( IEnumerable < string > ) ) ) ;
293281
294282 IEnumerable < int > intEnumerable = null ;
295283 Assert . DoesNotThrow ( ( ) => { codec . TryDecode ( pyList , out intEnumerable ) ; } ) ;
@@ -320,18 +308,57 @@ public void IterableDecoderTest()
320308
321309 //ensure a python class which implements the iterator protocol can be converter to a plain IEnumerable
322310 var foo = GetPythonIterable ( ) ;
311+ var fooType = foo . GetPythonType ( ) ;
323312 System . Collections . IEnumerable plainEnumerable2 = null ;
324313 Assert . DoesNotThrow ( ( ) => { codec . TryDecode ( pyList , out plainEnumerable2 ) ; } ) ;
325314 CollectionAssert . AreEqual ( plainEnumerable2 , new List < object > { 1 , 2 , 3 } ) ;
326315
327316 //can convert to any generic ienumerable. If the type is not assignable from the python element
328317 //it will be an exception during TryDecode
329- Assert . IsTrue ( codec . CanDecode ( foo , typeof ( IEnumerable < int > ) ) ) ;
330- Assert . IsTrue ( codec . CanDecode ( foo , typeof ( IEnumerable < double > ) ) ) ;
331- Assert . IsTrue ( codec . CanDecode ( foo , typeof ( IEnumerable < string > ) ) ) ;
318+ Assert . IsTrue ( codec . CanDecode ( fooType , typeof ( IEnumerable < int > ) ) ) ;
319+ Assert . IsTrue ( codec . CanDecode ( fooType , typeof ( IEnumerable < double > ) ) ) ;
320+ Assert . IsTrue ( codec . CanDecode ( fooType , typeof ( IEnumerable < string > ) ) ) ;
332321
333322 Assert . DoesNotThrow ( ( ) => { codec . TryDecode ( pyList , out intEnumerable ) ; } ) ;
334323 CollectionAssert . AreEqual ( intEnumerable , new List < object > { 1 , 2 , 3 } ) ;
335324 }
336325 }
326+ }
327+
328+ /// <summary>
329+ /// "Decodes" only objects of exact type <typeparamref name="T"/>.
330+ /// Result is just the raw proxy to the encoder instance itself.
331+ /// </summary>
332+ class ObjectToEncoderInstanceEncoder < T > : IPyObjectEncoder
333+ {
334+ public bool CanEncode ( Type type ) => type == typeof ( T ) ;
335+ public PyObject TryEncode ( object value ) => PyObject . FromManagedObject ( this ) ;
336+ }
337+
338+ /// <summary>
339+ /// Decodes object of specified Python type to the predefined value <see cref="DecodeResult"/>
340+ /// </summary>
341+ /// <typeparam name="TTarget">Type of the <see cref="DecodeResult"/></typeparam>
342+ class DecoderReturningPredefinedValue < TTarget > : IPyObjectDecoder
343+ {
344+ public PyObject TheOnlySupportedSourceType { get ; }
345+ public TTarget DecodeResult { get ; }
346+
347+ public DecoderReturningPredefinedValue ( PyObject objectType , TTarget decodeResult )
348+ {
349+ this . TheOnlySupportedSourceType = objectType ;
350+ this . DecodeResult = decodeResult ;
351+ }
352+
353+ public bool CanDecode ( PyObject objectType , Type targetType )
354+ => objectType . Handle == TheOnlySupportedSourceType . Handle
355+ && targetType == typeof ( TTarget ) ;
356+ public bool TryDecode < T > ( PyObject pyObj , out T value )
357+ {
358+ if ( typeof ( T ) != typeof ( TTarget ) )
359+ throw new ArgumentException ( nameof ( T ) ) ;
360+ value = ( T ) ( object ) DecodeResult ;
361+ return true ;
362+ }
363+ }
337364}
0 commit comments