Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e303acf

Browse files
authored
Add container methods to IDictionary (#99)
* Add __len__ and __contains__ to IDictionary that defines ContainsKey * Replace DictionaryObject with LookUpObject
1 parent 60e9e86 commit e303acf

File tree

5 files changed

+329
-65
lines changed

5 files changed

+329
-65
lines changed

src/embed_tests/ClassManagerTests.cs

+159
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Reflection;
@@ -1083,6 +1084,164 @@ def is_enum_value_defined():
10831084
Assert.Throws<PythonException>(() => module.InvokeMethod("is_enum_value_defined"));
10841085
}
10851086
}
1087+
1088+
private static TestCaseData[] IDictionaryContainsTestCases =>
1089+
[
1090+
new(typeof(TestDictionary<string, string>)),
1091+
new(typeof(Dictionary<string, string>)),
1092+
new(typeof(TestKeyValueContainer<string, string>)),
1093+
new(typeof(DynamicClassDictionary<string, string>)),
1094+
];
1095+
1096+
[TestCaseSource(nameof(IDictionaryContainsTestCases))]
1097+
public void IDictionaryContainsMethodIsBound(Type dictType)
1098+
{
1099+
using var _ = Py.GIL();
1100+
1101+
var module = PyModule.FromString("IDictionaryContainsMethodIsBound", $@"
1102+
from clr import AddReference
1103+
AddReference(""Python.EmbeddingTest"")
1104+
1105+
from Python.EmbeddingTest import *
1106+
1107+
def contains(dictionary, key):
1108+
return key in dictionary
1109+
");
1110+
1111+
using var contains = module.GetAttr("contains");
1112+
1113+
var dictionary = Convert.ChangeType(Activator.CreateInstance(dictType), dictType);
1114+
var key1 = "key1";
1115+
(dictionary as dynamic).Add(key1, "value1");
1116+
1117+
using var pyDictionary = dictionary.ToPython();
1118+
using var pyKey1 = key1.ToPython();
1119+
1120+
var result = contains.Invoke(pyDictionary, pyKey1).As<bool>();
1121+
Assert.IsTrue(result);
1122+
1123+
using var pyKey2 = "key2".ToPython();
1124+
result = contains.Invoke(pyDictionary, pyKey2).As<bool>();
1125+
Assert.IsFalse(result);
1126+
}
1127+
1128+
[TestCaseSource(nameof(IDictionaryContainsTestCases))]
1129+
public void CanCheckIfNoneIsInDictionary(Type dictType)
1130+
{
1131+
using var _ = Py.GIL();
1132+
1133+
var module = PyModule.FromString("CanCheckIfNoneIsInDictionary", $@"
1134+
from clr import AddReference
1135+
AddReference(""Python.EmbeddingTest"")
1136+
1137+
from Python.EmbeddingTest import *
1138+
1139+
def contains(dictionary, key):
1140+
return key in dictionary
1141+
");
1142+
1143+
using var contains = module.GetAttr("contains");
1144+
1145+
var dictionary = Convert.ChangeType(Activator.CreateInstance(dictType), dictType);
1146+
(dictionary as dynamic).Add("key1", "value1");
1147+
1148+
using var pyDictionary = dictionary.ToPython();
1149+
1150+
var result = false;
1151+
Assert.DoesNotThrow(() => result = contains.Invoke(pyDictionary, PyObject.None).As<bool>());
1152+
Assert.IsFalse(result);
1153+
}
1154+
1155+
public class TestDictionary<TKey, TValue> : IDictionary
1156+
{
1157+
private readonly Dictionary<TKey, TValue> _data = new();
1158+
1159+
public object this[object key] { get => ((IDictionary)_data)[key]; set => ((IDictionary)_data)[key] = value; }
1160+
1161+
public bool IsFixedSize => ((IDictionary)_data).IsFixedSize;
1162+
1163+
public bool IsReadOnly => ((IDictionary)_data).IsReadOnly;
1164+
1165+
public ICollection Keys => ((IDictionary)_data).Keys;
1166+
1167+
public ICollection Values => ((IDictionary)_data).Values;
1168+
1169+
public int Count => ((ICollection)_data).Count;
1170+
1171+
public bool IsSynchronized => ((ICollection)_data).IsSynchronized;
1172+
1173+
public object SyncRoot => ((ICollection)_data).SyncRoot;
1174+
1175+
public void Add(object key, object value)
1176+
{
1177+
((IDictionary)_data).Add(key, value);
1178+
}
1179+
1180+
public void Clear()
1181+
{
1182+
((IDictionary)_data).Clear();
1183+
}
1184+
1185+
public bool Contains(object key)
1186+
{
1187+
return ((IDictionary)_data).Contains(key);
1188+
}
1189+
1190+
public void CopyTo(Array array, int index)
1191+
{
1192+
((ICollection)_data).CopyTo(array, index);
1193+
}
1194+
1195+
public IDictionaryEnumerator GetEnumerator()
1196+
{
1197+
return ((IDictionary)_data).GetEnumerator();
1198+
}
1199+
1200+
public void Remove(object key)
1201+
{
1202+
((IDictionary)_data).Remove(key);
1203+
}
1204+
1205+
IEnumerator IEnumerable.GetEnumerator()
1206+
{
1207+
return ((IEnumerable)_data).GetEnumerator();
1208+
}
1209+
1210+
public bool ContainsKey(TKey key)
1211+
{
1212+
return Contains(key);
1213+
}
1214+
}
1215+
1216+
public class TestKeyValueContainer<TKey, TValue>
1217+
where TKey: class
1218+
where TValue: class
1219+
{
1220+
private readonly Dictionary<TKey, TValue> _data = new();
1221+
public int Count => _data.Count;
1222+
public bool ContainsKey(TKey key)
1223+
{
1224+
return _data.ContainsKey(key);
1225+
}
1226+
public void Add(TKey key, TValue value)
1227+
{
1228+
_data.Add(key, value);
1229+
}
1230+
}
1231+
1232+
public class DynamicClassDictionary<TKey, TValue> : TestPropertyAccess.DynamicFixture
1233+
{
1234+
private readonly Dictionary<TKey, TValue> _data = new();
1235+
public int Count => _data.Count;
1236+
public bool ContainsKey(TKey key)
1237+
{
1238+
return _data.ContainsKey(key);
1239+
}
1240+
public void Add(TKey key, TValue value)
1241+
{
1242+
_data.Add(key, value);
1243+
}
1244+
}
10861245
}
10871246

10881247
public class NestedTestParent

src/runtime/ClassManager.cs

+13-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,19 @@ internal static ClassBase CreateClass(Type type)
205205

206206
else if (typeof(IDynamicMetaObjectProvider).IsAssignableFrom(type))
207207
{
208-
impl = new DynamicClassObject(type);
208+
if (type.IsLookUp())
209+
{
210+
impl = new DynamicClassLookUpObject(type);
211+
}
212+
else
213+
{
214+
impl = new DynamicClassObject(type);
215+
}
216+
}
217+
218+
else if (type.IsLookUp())
219+
{
220+
impl = new LookUpObject(type);
209221
}
210222

211223
else
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System;
2+
3+
namespace Python.Runtime
4+
{
5+
/// <summary>
6+
/// Implements a Python type for managed DynamicClass objects that support look up (dictionaries),
7+
/// that is, they implement ContainsKey().
8+
/// This type is essentially the same as a ClassObject, except that it provides
9+
/// sequence semantics to support natural dictionary usage (__contains__ and __len__)
10+
/// from Python.
11+
/// </summary>
12+
internal class DynamicClassLookUpObject : DynamicClassObject
13+
{
14+
internal DynamicClassLookUpObject(Type tp) : base(tp)
15+
{
16+
}
17+
18+
/// <summary>
19+
/// Implements __len__ for dictionary types.
20+
/// </summary>
21+
public static int mp_length(BorrowedReference ob)
22+
{
23+
return LookUpObject.mp_length(ob);
24+
}
25+
26+
/// <summary>
27+
/// Implements __contains__ for dictionary types.
28+
/// </summary>
29+
public static int sq_contains(BorrowedReference ob, BorrowedReference v)
30+
{
31+
return LookUpObject.sq_contains(ob, v);
32+
}
33+
}
34+
}

src/runtime/Types/KeyValuePairEnumerableObject.cs

+2-64
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Reflection;
43

54
namespace Python.Runtime
65
{
@@ -10,75 +9,14 @@ namespace Python.Runtime
109
/// sequence semantics to support natural dictionary usage (__contains__ and __len__)
1110
/// from Python.
1211
/// </summary>
13-
internal class KeyValuePairEnumerableObject : ClassObject
12+
internal class KeyValuePairEnumerableObject : LookUpObject
1413
{
15-
[NonSerialized]
16-
private static Dictionary<Tuple<Type, string>, MethodInfo> methodsByType = new Dictionary<Tuple<Type, string>, MethodInfo>();
17-
private static List<string> requiredMethods = new List<string> { "Count", "ContainsKey" };
18-
19-
internal static bool VerifyMethodRequirements(Type type)
20-
{
21-
foreach (var requiredMethod in requiredMethods)
22-
{
23-
var method = type.GetMethod(requiredMethod);
24-
if (method == null)
25-
{
26-
method = type.GetMethod($"get_{requiredMethod}");
27-
if (method == null)
28-
{
29-
return false;
30-
}
31-
}
32-
33-
var key = Tuple.Create(type, requiredMethod);
34-
methodsByType.Add(key, method);
35-
}
36-
37-
return true;
38-
}
39-
4014
internal KeyValuePairEnumerableObject(Type tp) : base(tp)
4115
{
4216

4317
}
4418

4519
internal override bool CanSubclass() => false;
46-
47-
/// <summary>
48-
/// Implements __len__ for dictionary types.
49-
/// </summary>
50-
public static int mp_length(BorrowedReference ob)
51-
{
52-
var obj = (CLRObject)GetManagedObject(ob);
53-
var self = obj.inst;
54-
55-
var key = Tuple.Create(self.GetType(), "Count");
56-
var methodInfo = methodsByType[key];
57-
58-
return (int)methodInfo.Invoke(self, null);
59-
}
60-
61-
/// <summary>
62-
/// Implements __contains__ for dictionary types.
63-
/// </summary>
64-
public static int sq_contains(BorrowedReference ob, BorrowedReference v)
65-
{
66-
var obj = (CLRObject)GetManagedObject(ob);
67-
var self = obj.inst;
68-
69-
var key = Tuple.Create(self.GetType(), "ContainsKey");
70-
var methodInfo = methodsByType[key];
71-
72-
var parameters = methodInfo.GetParameters();
73-
object arg;
74-
if (!Converter.ToManaged(v, parameters[0].ParameterType, out arg, false))
75-
{
76-
Exceptions.SetError(Exceptions.TypeError,
77-
$"invalid parameter type for sq_contains: should be {Converter.GetTypeByAlias(v)}, found {parameters[0].ParameterType}");
78-
}
79-
80-
return (bool)methodInfo.Invoke(self, new[] { arg }) ? 1 : 0;
81-
}
8220
}
8321

8422
public static class KeyValuePairEnumerableObjectExtension
@@ -102,7 +40,7 @@ public static bool IsKeyValuePairEnumerable(this Type type)
10240
a.GetGenericTypeDefinition() == keyValuePairType &&
10341
a.GetGenericArguments().Length == 2)
10442
{
105-
return KeyValuePairEnumerableObject.VerifyMethodRequirements(type);
43+
return LookUpObject.VerifyMethodRequirements(type);
10644
}
10745
}
10846
}

0 commit comments

Comments
 (0)