Skip to content
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.
Comment thread
Martin-Molinero marked this conversation as resolved.
Outdated
/// </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<,>);
Comment thread
Martin-Molinero marked this conversation as 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