Skip to content

Multiple fixes related to Dictionary.Keys bug #1786

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

Merged
merged 5 commits into from
May 20, 2022
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- `__name__` and `__signature__` to reflected .NET methods
- .NET collection types now implement standard Python collection interfaces from `collections.abc`.
See [Mixins/collections.py](src/runtime/Mixins/collections.py).
- you can cast objects to generic .NET interfaces without specifying generic arguments as long as there is no ambiguity.
- .NET arrays implement Python buffer protocol
- Python integer interoperability with `System.Numerics.BigInteger`
- Python.NET will correctly resolve .NET methods, that accept `PyList`, `PyInt`,
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/ClassManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ internal static void RestoreRuntimeData(ClassManagerState storage)
/// Return the ClassBase-derived instance that implements a particular
/// reflected managed type, creating it if it doesn't yet exist.
/// </summary>
internal static ReflectedClrType GetClass(Type type) => ReflectedClrType.GetOrCreate(type);
internal static BorrowedReference GetClass(Type type) => ReflectedClrType.GetOrCreate(type);

internal static ClassBase GetClassImpl(Type type)
{
Expand Down
13 changes: 11 additions & 2 deletions src/runtime/Mixins/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

class IteratorMixin(col.Iterator):
def close(self):
self.Dispose()
if hasattr(self, 'Dispose'):
self.Dispose()
else:
from System import IDisposable
IDisposable(self).Dispose()

class IterableMixin(col.Iterable):
pass
Expand All @@ -16,7 +20,12 @@ class SizedMixin(col.Sized):
def __len__(self): return self.Count

class ContainerMixin(col.Container):
def __contains__(self, item): return self.Contains(item)
def __contains__(self, item):
if hasattr('self', 'Contains'):
return self.Contains(item)
else:
from System.Collections.Generic import ICollection
return ICollection(self).Contains(item)

try:
abc_Collection = col.Collection
Expand Down
17 changes: 17 additions & 0 deletions src/runtime/TypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,23 @@ static void GetPythonTypeName(Type clrType, System.Text.StringBuilder target)
}

target.Append(']');

int nestedStart = fullName.IndexOf('+');
while (nestedStart >= 0)
{
target.Append('.');
int nextNested = fullName.IndexOf('+', nestedStart + 1);
if (nextNested < 0)
{
target.Append(fullName.Substring(nestedStart + 1));
}
else
{
target.Append(fullName.Substring(nestedStart + 1, length: nextNested - nestedStart - 1));
}
nestedStart = nextNested;
}

return;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/Types/ClassObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public override NewReference type_subscript(BorrowedReference idx)
return Exceptions.RaiseTypeError("type expected");
}
Type a = t.MakeArrayType();
PyType o = ClassManager.GetClass(a);
BorrowedReference o = ClassManager.GetClass(a);
return new NewReference(o);
}

Expand Down
4 changes: 2 additions & 2 deletions src/runtime/Types/ClrObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ internal static NewReference GetReference(object ob, BorrowedReference pyType)

internal static NewReference GetReference(object ob, Type type)
{
PyType cc = ClassManager.GetClass(type);
BorrowedReference cc = ClassManager.GetClass(type);
return Create(ob, cc);
}

internal static NewReference GetReference(object ob)
{
PyType cc = ClassManager.GetClass(ob.GetType());
BorrowedReference cc = ClassManager.GetClass(ob.GetType());
return Create(ob, cc);
}

Expand Down
49 changes: 49 additions & 0 deletions src/runtime/Types/GenericType.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;

namespace Python.Runtime
{
Expand All @@ -20,10 +21,58 @@ internal GenericType(Type tp) : base(tp)
/// </summary>
public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, BorrowedReference kw)
{
var self = (GenericType)GetManagedObject(tp)!;
if (!self.type.Valid)
{
return Exceptions.RaiseTypeError(self.type.DeletedMessage);
}
var type = self.type.Value;

if (type.IsInterface && !type.IsConstructedGenericType)
{
var nargs = Runtime.PyTuple_Size(args);
if (nargs == 1)
{
var instance = Runtime.PyTuple_GetItem(args, 0);
return AsGenericInterface(instance, type);
}
}

Exceptions.SetError(Exceptions.TypeError, "cannot instantiate an open generic type");

return default;
}

static NewReference AsGenericInterface(BorrowedReference instance, Type targetType)
{
if (GetManagedObject(instance) is not CLRObject obj)
{
return Exceptions.RaiseTypeError("only .NET objects can be cast to .NET interfaces");
}

Type[] supportedInterfaces = obj.inst.GetType().GetInterfaces();
Type[] constructedInterfaces = supportedInterfaces
.Where(i => i.IsConstructedGenericType && i.GetGenericTypeDefinition() == targetType)
.ToArray();

if (constructedInterfaces.Length == 1)
{
BorrowedReference pythonic = ClassManager.GetClass(constructedInterfaces[0]);
using var args = Runtime.PyTuple_New(1);
Runtime.PyTuple_SetItem(args.Borrow(), 0, instance);
return Runtime.PyObject_CallObject(pythonic, args.Borrow());
}

if (constructedInterfaces.Length > 1)
{
string interfaces = string.Join(", ", constructedInterfaces.Select(TypeManager.GetPythonTypeName));
return Exceptions.RaiseTypeError("Ambiguous cast to .NET interface. "
+ $"Object implements: {interfaces}");
}

return Exceptions.RaiseTypeError("object does not implement "
+ TypeManager.GetPythonTypeName(targetType));
}

/// <summary>
/// Implements __call__ for reflected generic types.
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/Types/MethodObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference
&& obj.inst is IPythonDerivedType
&& self.type.Value.IsInstanceOfType(obj.inst))
{
var basecls = ClassManager.GetClass(self.type.Value);
var basecls = ReflectedClrType.GetOrCreate(self.type.Value);
return new MethodBinding(self, new PyObject(ob), basecls).Alloc();
}

Expand Down
1 change: 1 addition & 0 deletions src/runtime/Types/ReflectedClrType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ internal sealed class ReflectedClrType : PyType
{
private ReflectedClrType(StolenReference reference) : base(reference, prevalidated: true) { }
internal ReflectedClrType(ReflectedClrType original) : base(original, prevalidated: true) { }
internal ReflectedClrType(BorrowedReference original) : base(original) { }
ReflectedClrType(SerializationInfo info, StreamingContext context) : base(info, context) { }

internal ClassBase Impl => (ClassBase)ManagedType.GetManagedObject(this)!;
Expand Down
7 changes: 7 additions & 0 deletions tests/test_collection_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,10 @@ def test_dict_items():
k,v = items[0]
assert k == 42
assert v == "a"

# regression test for https://github.com/pythonnet/pythonnet/issues/1785
def test_dict_in_keys():
d = C.Dictionary[str, int]()
d["a"] = 42
assert "a" in d.Keys
assert "b" not in d.Keys