Visitar URL original
Multiple fixes related to Dictionary.Keys bug by lostmsu · Pull Request #1786 · pythonnet/pythonnet · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
allow casting objects to generic .NET interfaces without specifying g…
…eneric arguments as long as there is no ambiguity
  • Loading branch information
lostmsu committed May 7, 2022
commit 537ddf4b7fc938bca461b1ffb532a136af7b7d67
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
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