Skip to content

Commit d45c39a

Browse files
committed
allows codecs to encode and decode thrown exceptions
1 parent 663df73 commit d45c39a

3 files changed

Lines changed: 55 additions & 2 deletions

File tree

src/embed_tests/Codecs.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,59 @@ static void TupleRoundtripGeneric<T, TTuple>() {
8282
Assert.AreEqual(expected: tuple, actual: restored);
8383
}
8484
}
85+
86+
const string TestExceptionMessage = "Hello World!";
87+
[Test]
88+
public void ExceptionEncoded() {
89+
PyObjectConversions.RegisterEncoder(new ValueErrorCodec());
90+
void CallMe() => throw new ValueErrorWrapper(TestExceptionMessage);
91+
var callMeAction = new Action(CallMe);
92+
using var _ = Py.GIL();
93+
using var scope = Py.CreateScope();
94+
scope.Exec(@"
95+
def call(func):
96+
try:
97+
func()
98+
except ValueError as e:
99+
return str(e)
100+
");
101+
var callFunc = scope.Get("call");
102+
string message = callFunc.Invoke(callMeAction.ToPython()).As<string>();
103+
Assert.AreEqual(TestExceptionMessage, message);
104+
}
105+
106+
[Test]
107+
public void ExceptionDecoded() {
108+
PyObjectConversions.RegisterDecoder(new ValueErrorCodec());
109+
using var _ = Py.GIL();
110+
using var scope = Py.CreateScope();
111+
var error = Assert.Throws<ValueErrorWrapper>(() => PythonEngine.Exec(
112+
$"raise ValueError('{TestExceptionMessage}')"));
113+
Assert.AreEqual(TestExceptionMessage, error.Message);
114+
}
115+
116+
class ValueErrorWrapper : Exception {
117+
public ValueErrorWrapper(string message) : base(message) { }
118+
}
119+
120+
class ValueErrorCodec : IPyObjectEncoder, IPyObjectDecoder {
121+
public bool CanDecode(PyObject objectType, Type targetType)
122+
=> this.CanEncode(targetType) && objectType.Equals(PythonEngine.Eval("ValueError"));
123+
124+
public bool CanEncode(Type type) => type == typeof(ValueErrorWrapper)
125+
|| typeof(ValueErrorWrapper).IsSubclassOf(type);
126+
127+
public bool TryDecode<T>(PyObject pyObj, out T value) {
128+
var message = pyObj.GetAttr("args")[0].As<string>();
129+
value = (T)(object)new ValueErrorWrapper(message);
130+
return true;
131+
}
132+
133+
public PyObject TryEncode(object value) {
134+
var error = (ValueErrorWrapper)value;
135+
return PythonEngine.Eval("ValueError").Invoke(error.Message.ToPython());
136+
}
137+
}
85138
}
86139

87140
/// <summary>

src/embed_tests/Python.EmbeddingTest.15.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
2424
<PythonBuildDir Condition="'$(TargetFramework)'=='net40' AND '$(PythonBuildDir)' == ''">$(SolutionDir)\bin\</PythonBuildDir>
2525
<PublishDir Condition="'$(TargetFramework)'!='net40'">$(OutputPath)\$(TargetFramework)_publish</PublishDir>
26-
<LangVersion>7.3</LangVersion>
26+
<LangVersion>8.0</LangVersion>
2727
<ErrorReport>prompt</ErrorReport>
2828
<CustomDefineConstants Condition="'$(CustomDefineConstants)' == ''">$(PYTHONNET_DEFINE_CONSTANTS)</CustomDefineConstants>
2929
<BaseDefineConstants>XPLAT</BaseDefineConstants>

src/runtime/exceptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ public static void SetError(Exception e)
283283
return;
284284
}
285285

286-
IntPtr op = CLRObject.GetInstHandle(e);
286+
IntPtr op = Converter.ToPython(e);
287287
IntPtr etype = Runtime.PyObject_GetAttrString(op, "__class__");
288288
Runtime.PyErr_SetObject(etype, op);
289289
Runtime.XDecref(etype);

0 commit comments

Comments
 (0)