Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements KeyValuePairEnumerableObject #42

Merged
merged 4 commits into from
Apr 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.0.5.29
current_version = 1.0.5.30
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\.(?P<release>[a-z]+)(?P<dev>\d+))?
serialize =
{major}.{minor}.{patch}.{release}{dev}
Expand Down
2 changes: 1 addition & 1 deletion conda.recipe/meta.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package:
name: pythonnet
version: "1.0.5.29"
version: "1.0.5.30"

build:
skip: True # [not win]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ def run(self):

setup(
name="pythonnet",
version="1.0.5.29",
version="1.0.5.30",
description=".Net and Mono integration for Python",
url='https://pythonnet.github.io/',
license='MIT',
Expand Down
2 changes: 1 addition & 1 deletion src/SharedAssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@
// Version Information. Keeping it simple. May need to revisit for Nuget
// See: https://codingforsmarties.wordpress.com/2016/01/21/how-to-version-assemblies-destined-for-nuget/
// AssemblyVersion can only be numeric
[assembly: AssemblyVersion("1.0.5.29")]
[assembly: AssemblyVersion("1.0.5.30")]
2 changes: 1 addition & 1 deletion src/clrmodule/ClrModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public static void initclr()
{
#if USE_PYTHON_RUNTIME_VERSION
// Has no effect until SNK works. Keep updated anyways.
Version = new Version("1.0.5.29"),
Version = new Version("1.0.5.30"),
#endif
CultureInfo = CultureInfo.InvariantCulture
};
Expand Down
1 change: 1 addition & 0 deletions src/runtime/Python.Runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="keyvaluepairenumerableobject.cs" />
<Compile Include="finalizer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="..\SharedAssemblyInfo.cs">
Expand Down
5 changes: 5 additions & 0 deletions src/runtime/classmanager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ private static ClassBase CreateClass(Type type)
impl = new ArrayObject(type);
}

else if (type.IsKeyValuePairEnumerable())
{
impl = new KeyValuePairEnumerableObject(type);
}

else if (type.IsInterface)
{
impl = new InterfaceObject(type);
Expand Down
112 changes: 112 additions & 0 deletions src/runtime/keyvaluepairenumerableobject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Python.Runtime
{
/// <summary>
/// Implements a Python type for managed KeyValuePairEnumerable (dictionaries).
/// This type is essentially the same as a ClassObject, except that it provides
/// sequence semantics to support natural dictionary usage (__contains__ and __len__)
/// from Python.
/// </summary>
internal class KeyValuePairEnumerableObject : ClassObject
{
private static Dictionary<Tuple<Type, string>, MethodInfo> methodsByType = new Dictionary<Tuple<Type, string>, MethodInfo>();
private static List<string> requiredMethods = new List<string> { "Count", "ContainsKey" };

internal static bool VerifyMethodRequirements(Type type)
{
foreach (var requiredMethod in requiredMethods)
{
var method = type.GetMethod(requiredMethod);
if (method == null)
{
method = type.GetMethod($"get_{requiredMethod}");
if (method == null)
{
return false;
}
}

var key = Tuple.Create(type, requiredMethod);
methodsByType.Add(key, method);
}

return true;
}

internal KeyValuePairEnumerableObject(Type tp) : base(tp)
{

}

internal override bool CanSubclass() => false;

/// <summary>
/// Implements __len__ for dictionary types.
/// </summary>
public static int mp_length(IntPtr ob)
{
var obj = (CLRObject)GetManagedObject(ob);
var self = obj.inst;

var key = Tuple.Create(self.GetType(), "Count");
var methodInfo = methodsByType[key];

return (int)methodInfo.Invoke(self, null);
}

/// <summary>
/// Implements __contains__ for dictionary types.
/// </summary>
public static int sq_contains(IntPtr ob, IntPtr v)
{
var obj = (CLRObject)GetManagedObject(ob);
var self = obj.inst;

var key = Tuple.Create(self.GetType(), "ContainsKey");
var methodInfo = methodsByType[key];

var parameters = methodInfo.GetParameters();
object arg;
if (!Converter.ToManaged(v, parameters[0].ParameterType, out arg, false))
{
Exceptions.SetError(Exceptions.TypeError,
$"invalid parameter type for sq_contains: should be {Converter.GetTypeByAlias(v)}, found {parameters[0].ParameterType}");
}

return (bool)methodInfo.Invoke(self, new[] { arg }) ? 1 : 0;
}
}

public static class KeyValuePairEnumerableObjectExtension
{
public static bool IsKeyValuePairEnumerable(this Type type)
{
var iEnumerableType = typeof(IEnumerable<>);
var keyValuePairType = typeof(KeyValuePair<,>);

var interfaces = type.GetInterfaces();
foreach (var i in interfaces)
{
if (i.IsGenericType &&
i.GetGenericTypeDefinition() == iEnumerableType)
{
var arguments = i.GetGenericArguments();
if (arguments.Length != 1) continue;

var a = arguments[0];
if (a.IsGenericType &&
a.GetGenericTypeDefinition() == keyValuePairType &&
a.GetGenericArguments().Length == 2)
{
return KeyValuePairEnumerableObject.VerifyMethodRequirements(type);
}
}
}

return false;
}
}
}
2 changes: 1 addition & 1 deletion src/runtime/resources/clr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Code in this module gets loaded into the main clr module.
"""

__version__ = "1.0.5.29"
__version__ = "1.0.5.30"


class clrproperty(object):
Expand Down
1 change: 1 addition & 0 deletions src/testing/Python.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<ItemGroup>
<Compile Include="dictionarytest.cs" />
<Compile Include="arraytest.cs" />
<Compile Include="callbacktest.cs" />
<Compile Include="classtest.cs" />
Expand Down
106 changes: 106 additions & 0 deletions src/testing/dictionarytest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Python.Test
{
/// <summary>
/// Supports units tests for dictionary __contains__ and __len__
/// </summary>
public class PublicDictionaryTest
{
public IDictionary<string, int> items;

public PublicDictionaryTest()
{
items = new int[5] { 0, 1, 2, 3, 4 }
.ToDictionary(k => k.ToString(), v => v);
}
}


public class ProtectedDictionaryTest
{
protected IDictionary<string, int> items;

public ProtectedDictionaryTest()
{
items = new int[5] { 0, 1, 2, 3, 4 }
.ToDictionary(k => k.ToString(), v => v);
}
}


public class InternalDictionaryTest
{
internal IDictionary<string, int> items;

public InternalDictionaryTest()
{
items = new int[5] { 0, 1, 2, 3, 4 }
.ToDictionary(k => k.ToString(), v => v);
}
}


public class PrivateDictionaryTest
{
private IDictionary<string, int> items;

public PrivateDictionaryTest()
{
items = new int[5] { 0, 1, 2, 3, 4 }
.ToDictionary(k => k.ToString(), v => v);
}
}

public class InheritedDictionaryTest : IDictionary<string, int>
{
private readonly IDictionary<string, int> items;

public InheritedDictionaryTest()
{
items = new int[5] { 0, 1, 2, 3, 4 }
.ToDictionary(k => k.ToString(), v => v);
}

public int this[string key]
{
get { return items[key]; }
set { items[key] = value; }
}

public ICollection<string> Keys => items.Keys;

public ICollection<int> Values => items.Values;

public int Count => items.Count;

public bool IsReadOnly => false;

public void Add(string key, int value) => items.Add(key, value);

public void Add(KeyValuePair<string, int> item) => items.Add(item);

public void Clear() => items.Clear();

public bool Contains(KeyValuePair<string, int> item) => items.Contains(item);

public bool ContainsKey(string key) => items.ContainsKey(key);

public void CopyTo(KeyValuePair<string, int>[] array, int arrayIndex)
{
items.CopyTo(array, arrayIndex);
}

public IEnumerator<KeyValuePair<string, int>> GetEnumerator() => items.GetEnumerator();

public bool Remove(string key) => items.Remove(key);

public bool Remove(KeyValuePair<string, int> item) => items.Remove(item);

public bool TryGetValue(string key, out int value) => items.TryGetValue(key, out value);

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
Loading