Skip to content

Commit f6a6968

Browse files
committed
Extend the tests
1 parent 247436a commit f6a6968

2 files changed

Lines changed: 83 additions & 6 deletions

File tree

src/testing/dlrtest.cs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,45 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Dynamic;
34

45
namespace Python.Test;
56

67
public class DynamicMappingObject : DynamicObject
78
{
8-
readonly Dictionary<string, object> storage = [];
9+
Dictionary<string, object> storage;
10+
11+
Dictionary<string, object> Storage => storage ??= [];
912

1013
// Native members for testing that regular CLR access is unaffected.
1114
public string Label = "default";
1215
public int Multiplier { get; set; } = 1;
1316
public int Multiply(int value) => value * Multiplier;
1417

1518
// Test helper: bypass normal member binding and write directly to dynamic storage.
16-
public void SetDynamicValue(string name, object value) => storage[name] = value;
19+
public void SetDynamicValue(string name, object value) => Storage[name] = value;
20+
21+
// Test helper: retrieve the actual value stored in C# (for verification that None was stored as null)
22+
public object GetDynamicValue(string name) => Storage.TryGetValue(name, out var value) ? value : null;
23+
24+
1725

1826
public override bool TryGetMember(GetMemberBinder binder, out object result)
19-
=> storage.TryGetValue(binder.Name, out result);
27+
=> Storage.TryGetValue(binder.Name, out result);
28+
29+
public object TestProp
30+
{
31+
get => "TEST_PROP";
32+
set => throw new InvalidOperationException("Can't write to TestProp");
33+
}
2034

2135
public override bool TrySetMember(SetMemberBinder binder, object value)
2236
{
23-
storage[binder.Name] = value;
37+
Storage[binder.Name] = value;
2438
return true;
2539
}
2640

2741
public override bool TryDeleteMember(DeleteMemberBinder binder)
28-
=> storage.Remove(binder.Name);
42+
=> binder is not null && Storage.Remove(binder.Name);
2943

30-
public override IEnumerable<string> GetDynamicMemberNames() => storage.Keys;
44+
public override IEnumerable<string> GetDynamicMemberNames() => Storage.Keys;
3145
}

tests/test_dynamic.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,66 @@ def test_dynamic_and_native_members_coexist():
6565
assert obj.answer == 42
6666
assert obj.Multiplier == 2
6767
assert obj.Multiply(10) == 20
68+
69+
70+
@pytest.mark.parametrize("obj", [DynamicMappingObject(), ExpandoObject()])
71+
def test_set_and_get_dynamic_property(obj):
72+
"""Test that setting and getting dynamic properties goes through DLR binder."""
73+
# Get initial value (should be None for non-existent property)
74+
assert not hasattr(obj, "MyProp")
75+
76+
# Set a dynamic property to a value
77+
obj.MyProp = 42
78+
assert obj.MyProp == 42
79+
80+
# Set to None and verify it stays None through DLR
81+
obj.MyProp = None
82+
assert obj.MyProp is None
83+
84+
# Set to another value and verify
85+
obj.MyProp = "hello"
86+
assert obj.MyProp == "hello"
87+
88+
89+
def test_update_dynamic_value():
90+
"""Setting from Python must update the backing dynamic store in C#."""
91+
obj = DynamicMappingObject()
92+
obj.SetDynamicValue("TestProp", "initial")
93+
assert obj.TestProp == "initial"
94+
95+
obj.TestProp = None
96+
97+
assert obj.TestProp is None
98+
assert obj.GetDynamicValue("TestProp") is None
99+
100+
101+
def test_derive_from_dynamic_class():
102+
class MyMappingObject(DynamicMappingObject):
103+
__namespace__ = "PythonNetTest"
104+
105+
def __init__(self):
106+
self._custom = 0
107+
108+
@property
109+
def custom_property(self):
110+
return self._custom
111+
112+
@custom_property.setter
113+
def custom_property(self, i):
114+
self._custom += i
115+
116+
117+
obj = MyMappingObject()
118+
with pytest.raises(AttributeError):
119+
x = obj.unknown_property
120+
121+
assert obj.custom_property == 0
122+
123+
obj.custom_property = 5
124+
assert obj.custom_property == 5
125+
126+
obj.custom_property = 5
127+
assert obj.custom_property == 10
128+
129+
obj.other_property = None
130+
assert obj.other_property is None

0 commit comments

Comments
 (0)