Skip to content

Commit 793adce

Browse files
authored
Merge pull request #74 from jhonabreul/bug-python-object-reference-security-dynamic-properties
Keep dynamic class properties as Python objects
2 parents 55ac3b4 + 7ba0e1c commit 793adce

File tree

5 files changed

+74
-11
lines changed

5 files changed

+74
-11
lines changed

src/embed_tests/TestPropertyAccess.cs

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,67 @@ def GetValue(self, fixture):
10961096
}
10971097
}
10981098

1099+
public class CSharpTestClass
1100+
{
1101+
public string CSharpProperty { get; set; }
1102+
}
1103+
1104+
[Test]
1105+
public void TestKeepsPythonReferenceForDynamicPropertiesFromPythonClassDerivedFromCSharpClass()
1106+
{
1107+
var expectedCSharpPropertyValue = "C# property";
1108+
var expectedPythonPropertyValue = "Python property";
1109+
1110+
var testModule = PyModule.FromString("module", $@"
1111+
from clr import AddReference
1112+
AddReference(""Python.EmbeddingTest"")
1113+
AddReference(""System"")
1114+
1115+
from Python.EmbeddingTest import TestPropertyAccess
1116+
1117+
class PythonTestClass(TestPropertyAccess.CSharpTestClass):
1118+
def __init__(self):
1119+
super().__init__()
1120+
1121+
def SetPythonObjectToFixture(fixture: TestPropertyAccess.DynamicFixture) -> None:
1122+
obj = PythonTestClass()
1123+
obj.CSharpProperty = '{expectedCSharpPropertyValue}'
1124+
obj.PythonProperty = '{expectedPythonPropertyValue}'
1125+
fixture.PythonClassObject = obj
1126+
1127+
def AssertPythonClassObjectType(fixture: TestPropertyAccess.DynamicFixture) -> None:
1128+
if type(fixture.PythonClassObject) != PythonTestClass:
1129+
raise Exception('PythonClassObject is not of type PythonTestClass')
1130+
1131+
def AccessCSharpProperty(fixture: TestPropertyAccess.DynamicFixture) -> str:
1132+
return fixture.PythonClassObject.CSharpProperty
1133+
1134+
def AccessPythonProperty(fixture: TestPropertyAccess.DynamicFixture) -> str:
1135+
return fixture.PythonClassObject.PythonProperty
1136+
");
1137+
1138+
dynamic fixture = new DynamicFixture();
1139+
1140+
using (Py.GIL())
1141+
{
1142+
dynamic SetPythonObjectToFixture = testModule.GetAttr("SetPythonObjectToFixture");
1143+
SetPythonObjectToFixture(fixture);
1144+
1145+
dynamic AssertPythonClassObjectType = testModule.GetAttr("AssertPythonClassObjectType");
1146+
Assert.DoesNotThrow(() => AssertPythonClassObjectType(fixture));
1147+
1148+
// Access the C# class property
1149+
dynamic AccessCSharpProperty = testModule.GetAttr("AccessCSharpProperty");
1150+
Assert.AreEqual(expectedCSharpPropertyValue, AccessCSharpProperty(fixture).As<string>());
1151+
Assert.AreEqual(expectedCSharpPropertyValue, fixture.PythonClassObject.CSharpProperty.As<string>());
1152+
1153+
// Access the Python class property
1154+
dynamic AccessPythonProperty = testModule.GetAttr("AccessPythonProperty");
1155+
Assert.AreEqual(expectedPythonPropertyValue, AccessPythonProperty(fixture).As<string>());
1156+
Assert.AreEqual(expectedPythonPropertyValue, fixture.PythonClassObject.PythonProperty.As<string>());
1157+
}
1158+
}
1159+
10991160
private static TestCaseData[] DynamicPropertiesSetterTestCases() => new[]
11001161
{
11011162
new TestCaseData("True", null),
@@ -1136,10 +1197,15 @@ def GetPythonValue(self):
11361197
using (Py.GIL())
11371198
{
11381199
model.SetValue(fixture);
1200+
11391201
var expectedAsPyObject = model.GetPythonValue() as PyObject;
1140-
var expected = expectedType != null ? expectedAsPyObject.AsManagedObject(expectedType) : expectedAsPyObject;
1202+
Assert.AreEqual(expectedAsPyObject, fixture.DynamicProperty);
1203+
1204+
if (expectedType != null)
1205+
{
1206+
Assert.AreEqual(expectedAsPyObject.AsManagedObject(expectedType), fixture.DynamicProperty.AsManagedObject(expectedType));
1207+
}
11411208

1142-
Assert.AreEqual(expected, fixture.DynamicProperty);
11431209
}
11441210
}
11451211

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.20" GeneratePathProperty="true">
16+
<PackageReference Include="quantconnect.pythonnet" Version="2.0.21" 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.20\lib\net5.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
28+
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.21\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.20")]
8-
[assembly: AssemblyFileVersion("2.0.20")]
7+
[assembly: AssemblyVersion("2.0.21")]
8+
[assembly: AssemblyFileVersion("2.0.21")]

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.20</Version>
8+
<Version>2.0.21</Version>
99
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
1010
<PackageLicenseFile>LICENSE</PackageLicenseFile>
1111
<RepositoryUrl>https://github.com/pythonnet/pythonnet</RepositoryUrl>

src/runtime/Types/DynamicClassObject.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,10 @@ public static int tp_setattro(BorrowedReference ob, BorrowedReference key, Borro
112112
return Runtime.PyObject_GenericSetAttr(ob, key, val);
113113
}
114114

115-
// If the value is a managed object, we get it from the reference. If it is a Python object, we assign it as is.
116-
var value = ((CLRObject)GetManagedObject(val))?.inst ?? PyObject.FromNullableReference(val);
117-
118115
var callsite = SetAttrCallSite(name, clrObjectType);
119116
try
120117
{
121-
callsite.Target(callsite, clrObj.inst, value);
118+
callsite.Target(callsite, clrObj.inst, PyObject.FromNullableReference(val));
122119
}
123120
// Catch C# exceptions and raise them as Python exceptions.
124121
catch (Exception exception)

0 commit comments

Comments
 (0)