Skip to content

Commit

Permalink
Merge pull request #79 from jhonabreul/bug-dynamic-objects-protected-…
Browse files Browse the repository at this point in the history
…properties-access

Access to protected properties of dynamic objects
  • Loading branch information
jhonabreul authored Dec 8, 2023
2 parents 715caa9 + c23c5ca commit 2df3c27
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 8 deletions.
37 changes: 37 additions & 0 deletions src/embed_tests/TestPropertyAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,12 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, o
public Dictionary<string, object> Properties { get { return _properties; } }

public string NonDynamicProperty { get; set; }

protected string NonDynamicProtectedProperty { get; set; } = "Default value";

protected static string NonDynamicProtectedStaticProperty { get; set; } = "Default value";

protected string NonDynamicProtectedField = "Default value";
}

public class TestPerson : IComparable, IComparable<TestPerson>
Expand Down Expand Up @@ -1265,6 +1271,37 @@ def SetValue(self, fixture):
}
}

[TestCase("NonDynamicProtectedProperty")]
[TestCase("NonDynamicProtectedField")]
[TestCase("NonDynamicProtectedStaticProperty")]
public void TestSetPublicNonDynamicObjectProtectedPropertyToActualPropertyWorks(string attributeName)
{
var expected = "Non Dynamic Protected Property";
dynamic model = PyModule.FromString("module", $@"
from clr import AddReference
AddReference(""Python.EmbeddingTest"")
AddReference(""System"")
from datetime import datetime
import System
from Python.EmbeddingTest import *
class RandomTestDynamicClass(TestPropertyAccess.DynamicFixture):
def SetValue(self):
self.{attributeName} = ""{expected}""
").GetAttr("RandomTestDynamicClass").Invoke();

using (Py.GIL())
{
Assert.AreNotEqual(expected, model.GetAttr(attributeName).As<string>());

model.SetValue();

Assert.AreEqual(expected, model.GetAttr(attributeName).As<string>());
Assert.IsFalse(model.Properties.ContainsKey(attributeName).As<bool>());
}
}

[Explicit]
[TestCase(true, TestName = "CSharpGetPropertyPerformance")]
[TestCase(false, TestName = "PythonGetPropertyPerformance")]
Expand Down
4 changes: 2 additions & 2 deletions src/perf_tests/Python.PerformanceTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.*" />
<PackageReference Include="quantconnect.pythonnet" Version="2.0.25" GeneratePathProperty="true">
<PackageReference Include="quantconnect.pythonnet" Version="2.0.26" GeneratePathProperty="true">
<IncludeAssets>compile</IncludeAssets>
</PackageReference>
</ItemGroup>
Expand All @@ -25,7 +25,7 @@
</Target>

<Target Name="CopyBaseline" AfterTargets="Build">
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.25\lib\net5.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.26\lib\net5.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
</Target>

<Target Name="CopyNewBuild" AfterTargets="Build">
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
[assembly: InternalsVisibleTo("Python.EmbeddingTest, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")]
[assembly: InternalsVisibleTo("Python.Test, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")]

[assembly: AssemblyVersion("2.0.25")]
[assembly: AssemblyFileVersion("2.0.25")]
[assembly: AssemblyVersion("2.0.26")]
[assembly: AssemblyFileVersion("2.0.26")]
2 changes: 1 addition & 1 deletion src/runtime/Python.Runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<RootNamespace>Python.Runtime</RootNamespace>
<AssemblyName>Python.Runtime</AssemblyName>
<PackageId>QuantConnect.pythonnet</PackageId>
<Version>2.0.25</Version>
<Version>2.0.26</Version>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/pythonnet/pythonnet</RepositoryUrl>
Expand Down
10 changes: 7 additions & 3 deletions src/runtime/Types/DynamicClassObject.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Reflection;
using System.Runtime.CompilerServices;

using RuntimeBinder = Microsoft.CSharp.RuntimeBinder;
Expand Down Expand Up @@ -87,7 +88,7 @@ public static NewReference tp_getattro(BorrowedReference ob, BorrowedReference k
// Do nothing, AttributeError was already raised in Python side and it was not cleared.
}
// Catch C# exceptions and raise them as Python exceptions.
catch(Exception exception)
catch (Exception exception)
{
Exceptions.Clear();
Exceptions.SetError(exception);
Expand All @@ -105,9 +106,12 @@ public static int tp_setattro(BorrowedReference ob, BorrowedReference key, Borro
var clrObj = (CLRObject)GetManagedObject(ob)!;
var name = Runtime.GetManagedString(key);

// If the key corresponds to a member of the class, we let the default implementation handle it.
// If the key corresponds to a valid property or field of the class, we let the default implementation handle it.
var clrObjectType = clrObj.inst.GetType();
if (clrObjectType.GetMember(name).Length != 0)
var bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
var property = clrObjectType.GetProperty(name, bindingFlags);
var field = property == null ? clrObjectType.GetField(name, bindingFlags) : null;
if ((property != null && property.SetMethod != null) || field != null)
{
return Runtime.PyObject_GenericSetAttr(ob, key, val);
}
Expand Down

0 comments on commit 2df3c27

Please sign in to comment.