@@ -31,7 +31,6 @@ static void TupleConversionsGeneric<T, TTuple>()
3131 TupleCodec < TTuple > . Register ( ) ;
3232 var tuple = Activator . CreateInstance ( typeof ( T ) , 42 , "42" , new object ( ) ) ;
3333 T restored = default ;
34- using ( Py . GIL ( ) )
3534 using ( var scope = Py . CreateScope ( ) )
3635 {
3736 void Accept ( T value ) => restored = value ;
@@ -53,7 +52,6 @@ static void TupleConversionsObject<T, TTuple>()
5352 TupleCodec < TTuple > . Register ( ) ;
5453 var tuple = Activator . CreateInstance ( typeof ( T ) , 42 , "42" , new object ( ) ) ;
5554 T restored = default ;
56- using ( Py . GIL ( ) )
5755 using ( var scope = Py . CreateScope ( ) )
5856 {
5957 void Accept ( object value ) => restored = ( T ) value ;
@@ -73,12 +71,9 @@ public void TupleRoundtripObject()
7371 static void TupleRoundtripObject < T , TTuple > ( )
7472 {
7573 var tuple = Activator . CreateInstance ( typeof ( T ) , 42 , "42" , new object ( ) ) ;
76- using ( Py . GIL ( ) )
77- {
78- var pyTuple = TupleCodec < TTuple > . Instance . TryEncode ( tuple ) ;
79- Assert . IsTrue ( TupleCodec < TTuple > . Instance . TryDecode ( pyTuple , out object restored ) ) ;
80- Assert . AreEqual ( expected : tuple , actual : restored ) ;
81- }
74+ var pyTuple = TupleCodec < TTuple > . Instance . TryEncode ( tuple ) ;
75+ Assert . IsTrue ( TupleCodec < TTuple > . Instance . TryDecode ( pyTuple , out object restored ) ) ;
76+ Assert . AreEqual ( expected : tuple , actual : restored ) ;
8277 }
8378
8479 [ Test ]
@@ -90,21 +85,12 @@ public void TupleRoundtripGeneric()
9085 static void TupleRoundtripGeneric < T , TTuple > ( )
9186 {
9287 var tuple = Activator . CreateInstance ( typeof ( T ) , 42 , "42" , new object ( ) ) ;
93- using ( Py . GIL ( ) )
94- {
95- var pyTuple = TupleCodec < TTuple > . Instance . TryEncode ( tuple ) ;
96- Assert . IsTrue ( TupleCodec < TTuple > . Instance . TryDecode ( pyTuple , out T restored ) ) ;
97- Assert . AreEqual ( expected : tuple , actual : restored ) ;
98- }
88+ var pyTuple = TupleCodec < TTuple > . Instance . TryEncode ( tuple ) ;
89+ Assert . IsTrue ( TupleCodec < TTuple > . Instance . TryDecode ( pyTuple , out T restored ) ) ;
90+ Assert . AreEqual ( expected : tuple , actual : restored ) ;
9991 }
10092
101- static PyObject GetPythonIterable ( )
102- {
103- using ( Py . GIL ( ) )
104- {
105- return PythonEngine . Eval ( "map(lambda x: x, [1,2,3])" ) ;
106- }
107- }
93+ static PyObject GetPythonIterable ( ) => PythonEngine . Eval ( "map(lambda x: x, [1,2,3])" ) ;
10894
10995 [ Test ]
11096 public void ListDecoderTest ( )
@@ -330,7 +316,6 @@ public void ExceptionEncoded()
330316 PyObjectConversions . RegisterEncoder ( new ValueErrorCodec ( ) ) ;
331317 void CallMe ( ) => throw new ValueErrorWrapper ( TestExceptionMessage ) ;
332318 var callMeAction = new Action ( CallMe ) ;
333- using var _ = Py . GIL ( ) ;
334319 using var scope = Py . CreateScope ( ) ;
335320 scope . Exec ( @"
336321def call(func):
@@ -348,7 +333,6 @@ def call(func):
348333 public void ExceptionDecoded ( )
349334 {
350335 PyObjectConversions . RegisterDecoder ( new ValueErrorCodec ( ) ) ;
351- using var _ = Py . GIL ( ) ;
352336 using var scope = Py . CreateScope ( ) ;
353337 var error = Assert . Throws < ValueErrorWrapper > ( ( )
354338 => PythonEngine . Exec ( $ "raise ValueError('{ TestExceptionMessage } ')") ) ;
@@ -371,6 +355,16 @@ from datetime import datetime
371355 scope . Exec ( "Codecs.AcceptsDateTime(datetime(2021, 1, 22))" ) ;
372356 }
373357
358+ [ Test ]
359+ public void ExceptionDecodedNoInstance ( )
360+ {
361+ PyObjectConversions . RegisterDecoder ( new InstancelessExceptionDecoder ( ) ) ;
362+ using var scope = Py . CreateScope ( ) ;
363+ var error = Assert . Throws < ValueErrorWrapper > ( ( ) => PythonEngine . Exec (
364+ $ "[].__iter__().__next__()") ) ;
365+ Assert . AreEqual ( TestExceptionMessage , error . Message ) ;
366+ }
367+
374368 public static void AcceptsDateTime ( DateTime v ) { }
375369
376370 class ValueErrorWrapper : Exception
@@ -381,7 +375,8 @@ public ValueErrorWrapper(string message) : base(message) { }
381375 class ValueErrorCodec : IPyObjectEncoder , IPyObjectDecoder
382376 {
383377 public bool CanDecode ( PyObject objectType , Type targetType )
384- => this . CanEncode ( targetType ) && objectType . Equals ( PythonEngine . Eval ( "ValueError" ) ) ;
378+ => this . CanEncode ( targetType )
379+ && PythonReferenceComparer . Instance . Equals ( objectType , PythonEngine . Eval ( "ValueError" ) ) ;
385380
386381 public bool CanEncode ( Type type ) => type == typeof ( ValueErrorWrapper )
387382 || typeof ( ValueErrorWrapper ) . IsSubclassOf ( type ) ;
@@ -399,6 +394,26 @@ public PyObject TryEncode(object value)
399394 return PythonEngine . Eval ( "ValueError" ) . Invoke ( error . Message . ToPython ( ) ) ;
400395 }
401396 }
397+
398+ class InstancelessExceptionDecoder : IPyObjectDecoder
399+ {
400+ readonly PyObject PyErr = Py . Import ( "clr.interop" ) . GetAttr ( "PyErr" ) ;
401+
402+ public bool CanDecode ( PyObject objectType , Type targetType )
403+ => PythonReferenceComparer . Instance . Equals ( PyErr , objectType ) ;
404+
405+ public bool TryDecode < T > ( PyObject pyObj , out T value )
406+ {
407+ if ( pyObj . HasAttr ( "value" ) )
408+ {
409+ value = default ;
410+ return false ;
411+ }
412+
413+ value = ( T ) ( object ) new ValueErrorWrapper ( TestExceptionMessage ) ;
414+ return true ;
415+ }
416+ }
402417 }
403418
404419 /// <summary>
0 commit comments