Skip to content

Commit 55ac3b4

Browse files
Merge pull request #73 from jhonabreul/bug-python-exceptions-dynamic-objects
Throw C# exceptions as Python exceptions in dynamic objects
2 parents 9daa794 + 01dc9b3 commit 55ac3b4

File tree

5 files changed

+51
-6
lines changed

5 files changed

+51
-6
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/perf_tests/Python.PerformanceTests.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1414
</PackageReference>
1515
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.*" />
16-
<PackageReference Include="quantconnect.pythonnet" Version="2.0.19" GeneratePathProperty="true">
16+
<PackageReference Include="quantconnect.pythonnet" Version="2.0.20" GeneratePathProperty="true">
1717
<IncludeAssets>compile</IncludeAssets>
1818
</PackageReference>
1919
</ItemGroup>
@@ -25,7 +25,7 @@
2525
</Target>
2626

2727
<Target Name="CopyBaseline" AfterTargets="Build">
28-
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.19\lib\net5.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
28+
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.20\lib\net5.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
2929
</Target>
3030

3131
<Target Name="CopyNewBuild" AfterTargets="Build">

src/runtime/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
[assembly: InternalsVisibleTo("Python.EmbeddingTest, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")]
55
[assembly: InternalsVisibleTo("Python.Test, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")]
66

7-
[assembly: AssemblyVersion("2.0.19")]
8-
[assembly: AssemblyFileVersion("2.0.19")]
7+
[assembly: AssemblyVersion("2.0.20")]
8+
[assembly: AssemblyFileVersion("2.0.20")]

src/runtime/Python.Runtime.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<RootNamespace>Python.Runtime</RootNamespace>
66
<AssemblyName>Python.Runtime</AssemblyName>
77
<PackageId>QuantConnect.pythonnet</PackageId>
8-
<Version>2.0.19</Version>
8+
<Version>2.0.20</Version>
99
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
1010
<PackageLicenseFile>LICENSE</PackageLicenseFile>
1111
<RepositoryUrl>https://github.com/pythonnet/pythonnet</RepositoryUrl>

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)