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 2 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="dictionaryobject.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.IsDictionary())
{
impl = new DictionaryObject(type);
}

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

namespace Python.Runtime
{
/// <summary>
/// Implements a Python type for managed 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.
Martin-Molinero marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
internal class DictionaryObject : ClassObject
{
private static Dictionary<Tuple<Type, string>, MethodInfo> methodsByType = new Dictionary<Tuple<Type, string>, MethodInfo>();
private static Dictionary<string, string> methodMap = new Dictionary<string, string>
{
{ "mp_length", "Count" },
{ "sq_contains", "ContainsKey" }
};

public List<string> MappedMethods { get; } = new List<string>();

internal DictionaryObject(Type tp) : base(tp)
{
if (!tp.IsDictionary())
{
throw new ArgumentException("object is not a dict");
}

foreach (var name in methodMap)
{
var key = Tuple.Create(type, name.Value);
MethodInfo method;
if (!methodsByType.TryGetValue(key, out method))
{
method = tp.GetMethod(name.Value);
if (method == null)
{
method = tp.GetMethod($"get_{name.Value}");
}
if (method == null)
{
continue;
}
methodsByType.Add(key, method);
}

MappedMethods.Add(name.Key);
}
}

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;

MethodInfo methodInfo;
if (!TryGetMethodInfo(self.GetType(), "Count", out methodInfo))
{
return 0;
}

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;

MethodInfo methodInfo;
if (!TryGetMethodInfo(self.GetType(), "ContainsKey", out methodInfo))
{
return 0;
}

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;
}

private static bool TryGetMethodInfo(Type type, string alias, out MethodInfo methodInfo)
{
var key = Tuple.Create(type, alias);

if (!methodsByType.TryGetValue(key, out methodInfo))
{
Exceptions.SetError(Exceptions.TypeError,
$"{nameof(type)} does not define {alias} method");

return false;
}

return true;
}
}

public static class DictionaryObjectExtension
{
public static bool IsDictionary(this Type type)
{
var iEnumerableType = typeof(IEnumerable<>);
var keyValuePairType = typeof(KeyValuePair<,>);
Martin-Molinero marked this conversation as resolved.
Show resolved Hide resolved

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 true;
}
}
}

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