Skip to content

Commit 0d81858

Browse files
Merge pull request #42 from QuantConnect/feature-dictionary-object
Implements KeyValuePairEnumerableObject
2 parents 1391bfc + 49245d6 commit 0d81858

File tree

12 files changed

+379
-6
lines changed

12 files changed

+379
-6
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 1.0.5.29
2+
current_version = 1.0.5.30
33
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\.(?P<release>[a-z]+)(?P<dev>\d+))?
44
serialize =
55
{major}.{minor}.{patch}.{release}{dev}

conda.recipe/meta.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package:
22
name: pythonnet
3-
version: "1.0.5.29"
3+
version: "1.0.5.30"
44

55
build:
66
skip: True # [not win]

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ def run(self):
485485

486486
setup(
487487
name="pythonnet",
488-
version="1.0.5.29",
488+
version="1.0.5.30",
489489
description=".Net and Mono integration for Python",
490490
url='https://pythonnet.github.io/',
491491
license='MIT',

src/SharedAssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@
2525
// Version Information. Keeping it simple. May need to revisit for Nuget
2626
// See: https://codingforsmarties.wordpress.com/2016/01/21/how-to-version-assemblies-destined-for-nuget/
2727
// AssemblyVersion can only be numeric
28-
[assembly: AssemblyVersion("1.0.5.29")]
28+
[assembly: AssemblyVersion("1.0.5.30")]

src/clrmodule/ClrModule.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public static void initclr()
5353
{
5454
#if USE_PYTHON_RUNTIME_VERSION
5555
// Has no effect until SNK works. Keep updated anyways.
56-
Version = new Version("1.0.5.29"),
56+
Version = new Version("1.0.5.30"),
5757
#endif
5858
CultureInfo = CultureInfo.InvariantCulture
5959
};

src/runtime/Python.Runtime.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
<Reference Include="System" />
7777
</ItemGroup>
7878
<ItemGroup>
79+
<Compile Include="keyvaluepairenumerableobject.cs" />
7980
<Compile Include="finalizer.cs" />
8081
<Compile Include="Properties\AssemblyInfo.cs" />
8182
<Compile Include="..\SharedAssemblyInfo.cs">

src/runtime/classmanager.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ private static ClassBase CreateClass(Type type)
8787
impl = new ArrayObject(type);
8888
}
8989

90+
else if (type.IsKeyValuePairEnumerable())
91+
{
92+
impl = new KeyValuePairEnumerableObject(type);
93+
}
94+
9095
else if (type.IsInterface)
9196
{
9297
impl = new InterfaceObject(type);
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
5+
namespace Python.Runtime
6+
{
7+
/// <summary>
8+
/// Implements a Python type for managed KeyValuePairEnumerable (dictionaries).
9+
/// This type is essentially the same as a ClassObject, except that it provides
10+
/// sequence semantics to support natural dictionary usage (__contains__ and __len__)
11+
/// from Python.
12+
/// </summary>
13+
internal class KeyValuePairEnumerableObject : ClassObject
14+
{
15+
private static Dictionary<Tuple<Type, string>, MethodInfo> methodsByType = new Dictionary<Tuple<Type, string>, MethodInfo>();
16+
private static List<string> requiredMethods = new List<string> { "Count", "ContainsKey" };
17+
18+
internal static bool VerifyMethodRequirements(Type type)
19+
{
20+
foreach (var requiredMethod in requiredMethods)
21+
{
22+
var method = type.GetMethod(requiredMethod);
23+
if (method == null)
24+
{
25+
method = type.GetMethod($"get_{requiredMethod}");
26+
if (method == null)
27+
{
28+
return false;
29+
}
30+
}
31+
32+
var key = Tuple.Create(type, requiredMethod);
33+
methodsByType.Add(key, method);
34+
}
35+
36+
return true;
37+
}
38+
39+
internal KeyValuePairEnumerableObject(Type tp) : base(tp)
40+
{
41+
42+
}
43+
44+
internal override bool CanSubclass() => false;
45+
46+
/// <summary>
47+
/// Implements __len__ for dictionary types.
48+
/// </summary>
49+
public static int mp_length(IntPtr ob)
50+
{
51+
var obj = (CLRObject)GetManagedObject(ob);
52+
var self = obj.inst;
53+
54+
var key = Tuple.Create(self.GetType(), "Count");
55+
var methodInfo = methodsByType[key];
56+
57+
return (int)methodInfo.Invoke(self, null);
58+
}
59+
60+
/// <summary>
61+
/// Implements __contains__ for dictionary types.
62+
/// </summary>
63+
public static int sq_contains(IntPtr ob, IntPtr v)
64+
{
65+
var obj = (CLRObject)GetManagedObject(ob);
66+
var self = obj.inst;
67+
68+
var key = Tuple.Create(self.GetType(), "ContainsKey");
69+
var methodInfo = methodsByType[key];
70+
71+
var parameters = methodInfo.GetParameters();
72+
object arg;
73+
if (!Converter.ToManaged(v, parameters[0].ParameterType, out arg, false))
74+
{
75+
Exceptions.SetError(Exceptions.TypeError,
76+
$"invalid parameter type for sq_contains: should be {Converter.GetTypeByAlias(v)}, found {parameters[0].ParameterType}");
77+
}
78+
79+
return (bool)methodInfo.Invoke(self, new[] { arg }) ? 1 : 0;
80+
}
81+
}
82+
83+
public static class KeyValuePairEnumerableObjectExtension
84+
{
85+
public static bool IsKeyValuePairEnumerable(this Type type)
86+
{
87+
var iEnumerableType = typeof(IEnumerable<>);
88+
var keyValuePairType = typeof(KeyValuePair<,>);
89+
90+
var interfaces = type.GetInterfaces();
91+
foreach (var i in interfaces)
92+
{
93+
if (i.IsGenericType &&
94+
i.GetGenericTypeDefinition() == iEnumerableType)
95+
{
96+
var arguments = i.GetGenericArguments();
97+
if (arguments.Length != 1) continue;
98+
99+
var a = arguments[0];
100+
if (a.IsGenericType &&
101+
a.GetGenericTypeDefinition() == keyValuePairType &&
102+
a.GetGenericArguments().Length == 2)
103+
{
104+
return KeyValuePairEnumerableObject.VerifyMethodRequirements(type);
105+
}
106+
}
107+
}
108+
109+
return false;
110+
}
111+
}
112+
}

src/runtime/resources/clr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Code in this module gets loaded into the main clr module.
33
"""
44

5-
__version__ = "1.0.5.29"
5+
__version__ = "1.0.5.30"
66

77

88
class clrproperty(object):

src/testing/Python.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
<DebugType>pdbonly</DebugType>
7171
</PropertyGroup>
7272
<ItemGroup>
73+
<Compile Include="dictionarytest.cs" />
7374
<Compile Include="arraytest.cs" />
7475
<Compile Include="callbacktest.cs" />
7576
<Compile Include="classtest.cs" />

0 commit comments

Comments
 (0)