1783 Implement Interface And Inherit Class (#2028) · pythonnet/pythonnet@a404d6e

11

using System;

2+

using System.Collections.Generic;

23

using System.Diagnostics;

4+

using System.Linq;

5+

using System.Reflection;

36

using System.Runtime.InteropServices;

47

using System.Runtime.Serialization;

58

@@ -79,41 +82,80 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args,

7982

BorrowedReference bases = Runtime.PyTuple_GetItem(args, 1);

8083

BorrowedReference dict = Runtime.PyTuple_GetItem(args, 2);

818482-

// We do not support multiple inheritance, so the bases argument

83-

// should be a 1-item tuple containing the type we are subtyping.

84-

// That type must itself have a managed implementation. We check

85-

// that by making sure its metatype is the CLR metatype.

85+

// Extract interface types and base class types.

86+

var interfaces = new List<Type>();

868787-

if (Runtime.PyTuple_Size(bases) != 1)

88-

{

89-

return Exceptions.RaiseTypeError("cannot use multiple inheritance with managed classes");

90-

}

88+

// More than one base type case be declared, but an exception will be thrown

89+

// if more than one is a class/not an interface.

90+

var baseTypes = new List<ClassBase>();

919192-

BorrowedReference base_type = Runtime.PyTuple_GetItem(bases, 0);

93-

BorrowedReference mt = Runtime.PyObject_TYPE(base_type);

94-95-

if (!(mt == PyCLRMetaType || mt == Runtime.PyTypeType))

92+

var baseClassCount = Runtime.PyTuple_Size(bases);

93+

if (baseClassCount == 0)

9694

{

97-

return Exceptions.RaiseTypeError("invalid metatype");

95+

return Exceptions.RaiseTypeError("zero base classes ");

9896

}

9997100-

// Ensure that the reflected type is appropriate for subclassing,

101-

// disallowing subclassing of delegates, enums and array types.

102-103-

if (GetManagedObject(base_type) is ClassBase cb)

98+

for (nint i = 0; i < baseClassCount; i++)

10499

{

105-

try

100+

var baseTypeIt = Runtime.PyTuple_GetItem(bases, (int)i);

101+102+

if (GetManagedObject(baseTypeIt) is ClassBase classBaseIt)

106103

{

107-

if (!cb.CanSubclass())

104+

if (!classBaseIt.type.Valid)

105+

{

106+

return Exceptions.RaiseTypeError("Invalid type used as a super type.");

107+

}

108+

if (classBaseIt.type.Value.IsInterface)

108109

{

109-

return Exceptions.RaiseTypeError("delegates, enums and array types cannot be subclassed");

110+

interfaces.Add(classBaseIt.type.Value);

110111

}

112+

else

113+

{

114+

baseTypes.Add(classBaseIt);

115+

}

116+

}

117+

else

118+

{

119+

return Exceptions.RaiseTypeError("Non .NET type used as super class for meta type. This is not supported.");

111120

}

112-

catch (SerializationException)

121+

}

122+

// if the base type count is 0, there might still be interfaces to implement.

123+

if (baseTypes.Count == 0)

124+

{

125+

baseTypes.Add(new ClassBase(typeof(object)));

126+

}

127+128+

// Multiple inheritance is not supported, unless the other types are interfaces

129+

if (baseTypes.Count > 1)

130+

{

131+

var types = string.Join(", ", baseTypes.Select(baseType => baseType.type.Value));

132+

return Exceptions.RaiseTypeError($"Multiple inheritance with managed classes cannot be used. Types: {types} ");

133+

}

134+135+

// check if the list of interfaces contains no duplicates.

136+

if (interfaces.Distinct().Count() != interfaces.Count)

137+

{

138+

// generate a string containing the problematic types.

139+

var duplicateTypes = interfaces.GroupBy(type => type)

140+

.Where(typeGroup => typeGroup.Count() > 1)

141+

.Select(typeGroup => typeGroup.Key);

142+

var duplicateTypesString = string.Join(", ", duplicateTypes);

143+144+

return Exceptions.RaiseTypeError($"An interface can only be implemented once. Duplicate types: {duplicateTypesString}");

145+

}

146+147+

var cb = baseTypes[0];

148+

try

149+

{

150+

if (!cb.CanSubclass())

113151

{

114-

return Exceptions.RaiseTypeError($"Underlying C# Base class {cb.type} has been deleted");

152+

return Exceptions.RaiseTypeError("delegates, enums and array types cannot be subclassed");

115153

}

116154

}

155+

catch (SerializationException)

156+

{

157+

return Exceptions.RaiseTypeError($"Underlying C# Base class {cb.type} has been deleted");

158+

}

117159118160

BorrowedReference slots = Runtime.PyDict_GetItem(dict, PyIdentifier.__slots__);

119161

if (slots != null)

@@ -130,10 +172,12 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args,

130172

using var clsDict = new PyDict(dict);

131173

if (clsDict.HasKey("__assembly__") || clsDict.HasKey("__namespace__"))

132174

{

133-

return TypeManager.CreateSubType(name, base_type, clsDict);

175+

return TypeManager.CreateSubType(name, baseTypes[0], interfaces, clsDict);

134176

}

135177

}

136178179+

var base_type = Runtime.PyTuple_GetItem(bases, 0);

180+137181

// otherwise just create a basic type without reflecting back into the managed side.

138182

IntPtr func = Util.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_new);

139183

NewReference type = NativeCall.Call_3(func, tp, args, kw);