using System;
using System.Linq;
namespace Python.Runtime
{
///
/// Implements reflected generic types. Note that the Python behavior
/// is the same for both generic type definitions and constructed open
/// generic types. Both are essentially factories for creating closed
/// types based on the required generic type parameters.
///
[Serializable]
internal class GenericType : ClassBase
{
internal GenericType(Type tp) : base(tp)
{
}
///
/// Implements __new__ for reflected generic types.
///
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));
}
///
/// Implements __call__ for reflected generic types.
///
public static NewReference tp_call(BorrowedReference ob, BorrowedReference args, BorrowedReference kw)
{
Exceptions.SetError(Exceptions.TypeError, "object is not callable");
return default;
}
}
}