Skip to content

Commit 8e34e1f

Browse files
committed
Throw CLR exceptions as Python exceptions in dynamic class objects
1 parent 9daa794 commit 8e34e1f

File tree

2 files changed

+46
-1
lines changed

2 files changed

+46
-1
lines changed

src/embed_tests/TestPropertyAccess.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,37 @@ def InvokeModel(self):
12471247
$"Elapsed: {stopwatch.Elapsed.TotalMilliseconds}ms for {iterations} iterations. {thousandInvocationsPerSecond} KIPS");
12481248
}
12491249

1250+
[TestCaseSource(nameof(DynamicPropertiesGetterTestCases))]
1251+
public void TestGetPublicDynamicObjectPropertyCanCatchException(object property)
1252+
{
1253+
dynamic model = PyModule.FromString("module", @"
1254+
from clr import AddReference
1255+
AddReference(""Python.EmbeddingTest"")
1256+
AddReference(""System"")
1257+
1258+
from Python.EmbeddingTest import *
1259+
1260+
class TestGetPublicDynamicObjectPropertyThrowsPythonException:
1261+
def CallDynamicMethodWithoutCatchingExceptions(self, fixture):
1262+
return fixture.DynamicMethod()
1263+
1264+
def CallDynamicMethodCatchingExceptions(self, fixture, defaultValue):
1265+
try:
1266+
return fixture.DynamicMethod()
1267+
except:
1268+
return defaultValue
1269+
").GetAttr("TestGetPublicDynamicObjectPropertyThrowsPythonException").Invoke();
1270+
1271+
dynamic fixture = new DynamicFixture();
1272+
fixture.DynamicMethod = new Func<string>(() => throw new ArgumentException("Test"));
1273+
1274+
using (Py.GIL())
1275+
{
1276+
Assert.Throws<ArgumentException>(() => model.CallDynamicMethodWithoutCatchingExceptions(fixture));
1277+
Assert.AreEqual(property, model.CallDynamicMethodCatchingExceptions(fixture, property).AsManagedObject(property.GetType()));
1278+
}
1279+
}
1280+
12501281
public interface IModel
12511282
{
12521283
void InvokeModel();

src/runtime/Types/DynamicClassObject.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ public static NewReference tp_getattro(BorrowedReference ob, BorrowedReference k
8686
{
8787
// Do nothing, AttributeError was already raised in Python side and it was not cleared.
8888
}
89+
// Catch C# exceptions and raise them as Python exceptions.
90+
catch(Exception exception)
91+
{
92+
Exceptions.Clear();
93+
Exceptions.SetError(exception);
94+
}
8995
}
9096

9197
return result;
@@ -110,7 +116,15 @@ public static int tp_setattro(BorrowedReference ob, BorrowedReference key, Borro
110116
var value = ((CLRObject)GetManagedObject(val))?.inst ?? PyObject.FromNullableReference(val);
111117

112118
var callsite = SetAttrCallSite(name, clrObjectType);
113-
callsite.Target(callsite, clrObj.inst, value);
119+
try
120+
{
121+
callsite.Target(callsite, clrObj.inst, value);
122+
}
123+
// Catch C# exceptions and raise them as Python exceptions.
124+
catch (Exception exception)
125+
{
126+
Exceptions.SetError(exception);
127+
}
114128

115129
return 0;
116130
}

0 commit comments

Comments
 (0)