Skip to content

Commit f2fa831

Browse files
committed
Address peer review and add more unit tests
1 parent 3a65eec commit f2fa831

File tree

2 files changed

+64
-11
lines changed

2 files changed

+64
-11
lines changed

src/embed_tests/TestPropertyAccess.cs

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,34 @@ def GetValue(self, fixture):
10351035
}
10361036
}
10371037

1038+
[Test]
1039+
public void TestGetNullPublicDynamicObjectPropertyWorks()
1040+
{
1041+
dynamic model = PyModule.FromString("module", @"
1042+
from clr import AddReference
1043+
AddReference(""Python.EmbeddingTest"")
1044+
AddReference(""System"")
1045+
1046+
from Python.EmbeddingTest import *
1047+
1048+
class TestGetNullPublicDynamicObjectPropertyWorks:
1049+
def GetValue(self, fixture):
1050+
return fixture.DynamicProperty
1051+
1052+
def IsNone(self, fixture):
1053+
return fixture.DynamicProperty is None
1054+
").GetAttr("TestGetNullPublicDynamicObjectPropertyWorks").Invoke();
1055+
1056+
dynamic fixture = new DynamicFixture();
1057+
fixture.DynamicProperty = null;
1058+
1059+
using (Py.GIL())
1060+
{
1061+
Assert.IsNull(model.GetValue(fixture));
1062+
Assert.IsTrue(model.IsNone(fixture).As<bool>());
1063+
}
1064+
}
1065+
10381066
[Test]
10391067
public void TestGetNonExistingPublicDynamicObjectPropertyThrows()
10401068
{
@@ -1115,6 +1143,33 @@ def GetPythonValue(self):
11151143
}
11161144
}
11171145

1146+
[Test]
1147+
public void TestSetNullPublicDynamicObjectPropertyWorks()
1148+
{
1149+
dynamic model = PyModule.FromString("module", $@"
1150+
from clr import AddReference
1151+
AddReference(""Python.EmbeddingTest"")
1152+
AddReference(""System"")
1153+
1154+
from datetime import datetime
1155+
import System
1156+
from Python.EmbeddingTest import *
1157+
1158+
class TestSetNullPublicDynamicObjectPropertyWorks:
1159+
def SetValue(self, fixture):
1160+
fixture.DynamicProperty = None
1161+
").GetAttr("TestSetNullPublicDynamicObjectPropertyWorks").Invoke();
1162+
1163+
dynamic fixture = new DynamicFixture();
1164+
1165+
using (Py.GIL())
1166+
{
1167+
model.SetValue(fixture);
1168+
1169+
Assert.IsTrue(fixture.DynamicProperty.IsNone());
1170+
}
1171+
}
1172+
11181173
[Test]
11191174
public void TestSetPublicNonDynamicObjectPropertyToActualPropertyWorks()
11201175
{
@@ -1128,10 +1183,10 @@ from datetime import datetime
11281183
import System
11291184
from Python.EmbeddingTest import *
11301185
1131-
class TestGetPublicDynamicObjectPropertyWorks:
1186+
class TestSetPublicNonDynamicObjectPropertyToActualPropertyWorks:
11321187
def SetValue(self, fixture):
11331188
fixture.NonDynamicProperty = ""{expected}""
1134-
").GetAttr("TestGetPublicDynamicObjectPropertyWorks").Invoke();
1189+
").GetAttr("TestSetPublicNonDynamicObjectPropertyToActualPropertyWorks").Invoke();
11351190

11361191
var fixture = new DynamicFixture();
11371192

src/runtime/Types/DynamicClassObject.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Dynamic;
4-
using System.Linq;
54
using System.Runtime.CompilerServices;
65

7-
using Fasterflect;
8-
96
using RuntimeBinder = Microsoft.CSharp.RuntimeBinder;
107

118
namespace Python.Runtime
@@ -23,12 +20,12 @@ internal DynamicClassObject(Type tp) : base(tp)
2320
{
2421
}
2522

26-
private static Dictionary<Tuple<Type, string>, CallSite<Func<CallSite, object, object>>> _getAttrCallSites = new();
27-
private static Dictionary<Tuple<Type, string>, CallSite<Func<CallSite, object, object, object>>> _setAttrCallSites = new();
23+
private static Dictionary<ValueTuple<Type, string>, CallSite<Func<CallSite, object, object>>> _getAttrCallSites = new();
24+
private static Dictionary<ValueTuple<Type, string>, CallSite<Func<CallSite, object, object, object>>> _setAttrCallSites = new();
2825

2926
private static CallSite<Func<CallSite, object, object>> GetAttrCallSite(string name, Type objectType)
3027
{
31-
var key = Tuple.Create(objectType, name);
28+
var key = ValueTuple.Create(objectType, name);
3229
if (!_getAttrCallSites.TryGetValue(key, out var callSite))
3330
{
3431
var binder = RuntimeBinder.Binder.GetMember(
@@ -45,7 +42,7 @@ private static CallSite<Func<CallSite, object, object>> GetAttrCallSite(string n
4542

4643
private static CallSite<Func<CallSite, object, object, object>> SetAttrCallSite(string name, Type objectType)
4744
{
48-
var key = Tuple.Create(objectType, name);
45+
var key = ValueTuple.Create(objectType, name);
4946
if (!_setAttrCallSites.TryGetValue(key, out var callSite))
5047
{
5148
var binder = RuntimeBinder.Binder.SetMember(
@@ -111,15 +108,16 @@ public static int tp_setattro(BorrowedReference ob, BorrowedReference key, Borro
111108
var name = Runtime.GetManagedString(key);
112109

113110
// If the key corresponds to a member of the class, we let the default implementation handle it.
114-
if (clrObj.inst.GetType().GetMember(name).Length != 0)
111+
var clrObjectType = clrObj.inst.GetType();
112+
if (clrObjectType.GetMember(name).Length != 0)
115113
{
116114
return Runtime.PyObject_GenericSetAttr(ob, key, val);
117115
}
118116

119117
// If the value is a managed object, we get it from the reference. If it is a Python object, we assign it as is.
120118
var value = ((CLRObject)GetManagedObject(val))?.inst ?? PyObject.FromNullableReference(val);
121119

122-
var callsite = SetAttrCallSite(name, clrObj.inst.GetType());
120+
var callsite = SetAttrCallSite(name, clrObjectType);
123121
callsite.Target(callsite, clrObj.inst, value);
124122

125123
return 0;

0 commit comments

Comments
 (0)