Skip to content

Commit 05b8cbc

Browse files
committed
Replace DictionaryObject with LookUpObject
1 parent 34fa08a commit 05b8cbc

File tree

6 files changed

+211
-114
lines changed

6 files changed

+211
-114
lines changed

src/embed_tests/ClassManagerTests.cs

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,11 +1085,13 @@ def is_enum_value_defined():
10851085
}
10861086
}
10871087

1088-
private static TestCaseData[] IDictionaryContainsTestCases => new TestCaseData[]
1089-
{
1088+
private static TestCaseData[] IDictionaryContainsTestCases =>
1089+
[
10901090
new(typeof(TestDictionary<string, string>)),
10911091
new(typeof(Dictionary<string, string>)),
1092-
};
1092+
new(typeof(TestKeyValueContainer<string, string>)),
1093+
new(typeof(DynamicClassDictionary<string, string>)),
1094+
];
10931095

10941096
[TestCaseSource(nameof(IDictionaryContainsTestCases))]
10951097
public void IDictionaryContainsMethodIsBound(Type dictType)
@@ -1108,9 +1110,9 @@ def contains(dictionary, key):
11081110

11091111
using var contains = module.GetAttr("contains");
11101112

1111-
var dictionary = (Activator.CreateInstance(dictType) as IDictionary)!;
1113+
var dictionary = Convert.ChangeType(Activator.CreateInstance(dictType), dictType);
11121114
var key1 = "key1";
1113-
dictionary.Add(key1, "value1");
1115+
(dictionary as dynamic).Add(key1, "value1");
11141116

11151117
using var pyDictionary = dictionary.ToPython();
11161118
using var pyKey1 = key1.ToPython();
@@ -1140,8 +1142,8 @@ def contains(dictionary, key):
11401142

11411143
using var contains = module.GetAttr("contains");
11421144

1143-
var dictionary = (Activator.CreateInstance(dictType) as IDictionary)!;
1144-
dictionary.Add("key1", "value1");
1145+
var dictionary = Convert.ChangeType(Activator.CreateInstance(dictType), dictType);
1146+
(dictionary as dynamic).Add("key1", "value1");
11451147

11461148
using var pyDictionary = dictionary.ToPython();
11471149

@@ -1150,9 +1152,9 @@ def contains(dictionary, key):
11501152
Assert.IsFalse(result);
11511153
}
11521154

1153-
public class TestDictionary<TValue, TKey> : IDictionary
1155+
public class TestDictionary<TKey, TValue> : IDictionary
11541156
{
1155-
private readonly Dictionary<TValue, TKey> _data = new();
1157+
private readonly Dictionary<TKey, TValue> _data = new();
11561158

11571159
public object this[object key] { get => ((IDictionary)_data)[key]; set => ((IDictionary)_data)[key] = value; }
11581160

@@ -1210,6 +1212,36 @@ public bool ContainsKey(TKey key)
12101212
return Contains(key);
12111213
}
12121214
}
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+
}
12131245
}
12141246

12151247
public class NestedTestParent

src/runtime/ClassManager.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,6 @@ internal static ClassBase CreateClass(Type type)
180180
impl = new ArrayObject(type);
181181
}
182182

183-
else if (type.IsDictionary())
184-
{
185-
impl = new DictionaryObject(type);
186-
}
187-
188183
else if (type.IsKeyValuePairEnumerable())
189184
{
190185
impl = new KeyValuePairEnumerableObject(type);
@@ -210,7 +205,19 @@ internal static ClassBase CreateClass(Type type)
210205

211206
else if (typeof(IDynamicMetaObjectProvider).IsAssignableFrom(type))
212207
{
213-
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);
214221
}
215222

216223
else

src/runtime/Types/DictionaryObject.cs

Lines changed: 0 additions & 28 deletions
This file was deleted.
Lines changed: 34 additions & 0 deletions
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

Lines changed: 2 additions & 71 deletions
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,82 +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-
// If the argument is None, we return false. Python allows using None as key,
81-
// but C# doesn't and will throw, so we shortcut here
82-
if (arg == null)
83-
{
84-
return 0;
85-
}
86-
87-
return (bool)methodInfo.Invoke(self, new[] { arg }) ? 1 : 0;
88-
}
8920
}
9021

9122
public static class KeyValuePairEnumerableObjectExtension
@@ -109,7 +40,7 @@ public static bool IsKeyValuePairEnumerable(this Type type)
10940
a.GetGenericTypeDefinition() == keyValuePairType &&
11041
a.GetGenericArguments().Length == 2)
11142
{
112-
return KeyValuePairEnumerableObject.VerifyMethodRequirements(type);
43+
return LookUpObject.VerifyMethodRequirements(type);
11344
}
11445
}
11546
}

0 commit comments

Comments
 (0)