Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita404d6e

Browse files
authored
1783 Implement Interface And Inherit Class (#2028)
* 1783 Implement Interface And Inherit ClassAdded support for multiple inheritance when inheriting from one base class and/or multiple interfaces.Added a unit test verifying that it works with a simple class and interface. This unit test would previously have failed since multiple types are in the class super class list.
1 parentc2fa467 commita404d6e

File tree

9 files changed

+105
-43
lines changed

9 files changed

+105
-43
lines changed

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1616
- Fixed error occuring when inheriting a class containing a virtual generic method.
1717
- Make a second call to`pythonnet.load` a no-op, as it was intended.
1818

19+
- Added support for multiple inheritance when inheriting from a class and/or multiple interfaces.
20+
1921
##[3.0.1](https://github.com/pythonnet/pythonnet/releases/tag/v3.0.1) - 2022-11-03
2022

2123
###Added

‎src/python_tests_runner/PythonTestRunner.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ static IEnumerable<string[]> PythonTestCases()
3535
// Add the test that you want to debug here.
3636
yieldreturnnew[]{"test_indexer","test_boolean_indexer"};
3737
yieldreturnnew[]{"test_delegate","test_bool_delegate"};
38+
yieldreturnnew[]{"test_subclass","test_implement_interface_and_class"};
3839
}
3940

4041
/// <summary>

‎src/runtime/StateSerialization/MaybeType.cs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal struct MaybeType : ISerializable
1515
conststringSerializationName="n";
1616
readonlystringname;
1717
readonlyTypetype;
18-
18+
1919
publicstringDeletedMessage
2020
{
2121
get
@@ -61,4 +61,4 @@ public void GetObjectData(SerializationInfo serializationInfo, StreamingContext
6161
serializationInfo.AddValue(SerializationName,name);
6262
}
6363
}
64-
}
64+
}

‎src/runtime/TypeManager.cs‎

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ static PyTuple GetBaseTypeTuple(Type clrType)
374374
returnnewPyTuple(bases);
375375
}
376376

377-
internalstaticNewReferenceCreateSubType(BorrowedReferencepy_name,BorrowedReferencepy_base_type,BorrowedReferencedictRef)
377+
internalstaticNewReferenceCreateSubType(BorrowedReferencepy_name,ClassBasepy_base_type,IList<Type>interfaces,BorrowedReferencedictRef)
378378
{
379379
// Utility to create a subtype of a managed type with the ability for the
380380
// a python subtype able to override the managed implementation
@@ -415,17 +415,10 @@ internal static NewReference CreateSubType(BorrowedReference py_name, BorrowedRe
415415
}
416416

417417
// create the new managed type subclassing the base managed type
418-
if(ManagedType.GetManagedObject(py_base_type)isClassBasebaseClass)
419-
{
420-
returnReflectedClrType.CreateSubclass(baseClass,name,
421-
ns:(string?)namespaceStr,
422-
assembly:(string?)assembly,
423-
dict:dictRef);
424-
}
425-
else
426-
{
427-
returnExceptions.RaiseTypeError("invalid base class, expected CLR class type");
428-
}
418+
returnReflectedClrType.CreateSubclass(py_base_type,interfaces,name,
419+
ns:(string?)namespaceStr,
420+
assembly:(string?)assembly,
421+
dict:dictRef);
429422
}
430423

431424
internalstaticIntPtrWriteMethodDef(IntPtrmdef,IntPtrname,IntPtrfunc,PyMethodFlagsflags,IntPtrdoc)

‎src/runtime/Types/ClassDerived.cs‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ internal static NewReference ToPython(IPythonDerivedType obj)
144144
/// </summary>
145145
internalstaticTypeCreateDerivedType(stringname,
146146
TypebaseType,
147+
IList<Type>typeInterfaces,
147148
BorrowedReferencepy_dict,
148149
string?namespaceStr,
149150
string?assemblyName,
@@ -163,7 +164,9 @@ internal static Type CreateDerivedType(string name,
163164
ModuleBuildermoduleBuilder=GetModuleBuilder(assemblyName,moduleName);
164165

165166
TypebaseClass=baseType;
166-
varinterfaces=newList<Type>{typeof(IPythonDerivedType)};
167+
varinterfaces=newHashSet<Type>{typeof(IPythonDerivedType)};
168+
foreach(varinterfaceTypeintypeInterfaces)
169+
interfaces.Add(interfaceType);
167170

168171
// if the base type is an interface then use System.Object as the base class
169172
// and add the base type to the list of interfaces this new class will implement.
@@ -214,8 +217,9 @@ internal static Type CreateDerivedType(string name,
214217
}
215218
}
216219

217-
// override any virtual methods not already overridden by the properties above
218-
MethodInfo[]methods=baseType.GetMethods();
220+
// override any virtual not already overridden by the properties above
221+
// also override any interface method.
222+
varmethods=baseType.GetMethods().Concat(interfaces.SelectMany(x=>x.GetMethods()));
219223
varvirtualMethods=newHashSet<string>();
220224
foreach(MethodInfomethodinmethods)
221225
{

‎src/runtime/Types/MetaType.cs‎

Lines changed: 67 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
usingSystem;
2+
usingSystem.Collections.Generic;
23
usingSystem.Diagnostics;
4+
usingSystem.Linq;
5+
usingSystem.Reflection;
36
usingSystem.Runtime.InteropServices;
47
usingSystem.Runtime.Serialization;
58

@@ -79,41 +82,80 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args,
7982
BorrowedReferencebases=Runtime.PyTuple_GetItem(args,1);
8083
BorrowedReferencedict=Runtime.PyTuple_GetItem(args,2);
8184

82-
// 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+
varinterfaces=newList<Type>();
8687

87-
if(Runtime.PyTuple_Size(bases)!=1)
88-
{
89-
returnExceptions.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+
varbaseTypes=newList<ClassBase>();
9191

92-
BorrowedReferencebase_type=Runtime.PyTuple_GetItem(bases,0);
93-
BorrowedReferencemt=Runtime.PyObject_TYPE(base_type);
94-
95-
if(!(mt==PyCLRMetaType||mt==Runtime.PyTypeType))
92+
varbaseClassCount=Runtime.PyTuple_Size(bases);
93+
if(baseClassCount==0)
9694
{
97-
returnExceptions.RaiseTypeError("invalid metatype");
95+
returnExceptions.RaiseTypeError("zero base classes");
9896
}
9997

100-
// Ensure that the reflected type is appropriate for subclassing,
101-
// disallowing subclassing of delegates, enums and array types.
102-
103-
if(GetManagedObject(base_type)isClassBasecb)
98+
for(ninti=0;i<baseClassCount;i++)
10499
{
105-
try
100+
varbaseTypeIt=Runtime.PyTuple_GetItem(bases,(int)i);
101+
102+
if(GetManagedObject(baseTypeIt)isClassBaseclassBaseIt)
106103
{
107-
if(!cb.CanSubclass())
104+
if(!classBaseIt.type.Valid)
105+
{
106+
returnExceptions.RaiseTypeError("Invalid type used as a super type.");
107+
}
108+
if(classBaseIt.type.Value.IsInterface)
108109
{
109-
returnExceptions.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+
returnExceptions.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(newClassBase(typeof(object)));
126+
}
127+
128+
// Multiple inheritance is not supported, unless the other types are interfaces
129+
if(baseTypes.Count>1)
130+
{
131+
vartypes=string.Join(", ",baseTypes.Select(baseType=>baseType.type.Value));
132+
returnExceptions.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+
varduplicateTypes=interfaces.GroupBy(type=>type)
140+
.Where(typeGroup=>typeGroup.Count()>1)
141+
.Select(typeGroup=>typeGroup.Key);
142+
varduplicateTypesString=string.Join(", ",duplicateTypes);
143+
144+
returnExceptions.RaiseTypeError($"An interface can only be implemented once. Duplicate types:{duplicateTypesString}");
145+
}
146+
147+
varcb=baseTypes[0];
148+
try
149+
{
150+
if(!cb.CanSubclass())
113151
{
114-
returnExceptions.RaiseTypeError($"Underlying C# Base class{cb.type} has been deleted");
152+
returnExceptions.RaiseTypeError("delegates, enums and array types cannot be subclassed");
115153
}
116154
}
155+
catch(SerializationException)
156+
{
157+
returnExceptions.RaiseTypeError($"Underlying C# Base class{cb.type} has been deleted");
158+
}
117159

118160
BorrowedReferenceslots=Runtime.PyDict_GetItem(dict,PyIdentifier.__slots__);
119161
if(slots!=null)
@@ -130,10 +172,12 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args,
130172
usingvarclsDict=newPyDict(dict);
131173
if(clsDict.HasKey("__assembly__")||clsDict.HasKey("__namespace__"))
132174
{
133-
returnTypeManager.CreateSubType(name,base_type,clsDict);
175+
returnTypeManager.CreateSubType(name,baseTypes[0],interfaces,clsDict);
134176
}
135177
}
136178

179+
varbase_type=Runtime.PyTuple_GetItem(bases,0);
180+
137181
// otherwise just create a basic type without reflecting back into the managed side.
138182
IntPtrfunc=Util.ReadIntPtr(Runtime.PyTypeType,TypeOffset.tp_new);
139183
NewReferencetype=NativeCall.Call_3(func,tp,args,kw);

‎src/runtime/Types/ReflectedClrType.cs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,15 @@ internal void Restore(ClassBase cb)
6868
TypeManager.InitializeClass(this,cb,cb.type.Value);
6969
}
7070

71-
internalstaticNewReferenceCreateSubclass(ClassBasebaseClass,
71+
internalstaticNewReferenceCreateSubclass(ClassBasebaseClass,IList<Type>interfaces,
7272
stringname,string?assembly,string?ns,
7373
BorrowedReferencedict)
7474
{
7575
try
7676
{
7777
TypesubType=ClassDerivedObject.CreateDerivedType(name,
7878
baseClass.type.Value,
79+
interfaces,
7980
dict,
8081
ns,
8182
assembly);

‎src/testing/classtest.cs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
usingSystem;
12
usingSystem.Collections;
23

34
namespacePython.Test
@@ -59,4 +60,15 @@ public ClassCtorTest2(string v)
5960
internalclassInternalClass
6061
{
6162
}
63+
64+
publicclassSimpleClass
65+
{
66+
publicstaticvoidTestObject(objectobj)
67+
{
68+
if((!(objisISayHello1&&objisSimpleClass)))
69+
{
70+
thrownewException("Expected ISayHello and SimpleClass instance");
71+
}
72+
}
73+
}
6274
}

‎tests/test_subclass.py‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
importSystem
1010
importpytest
1111
fromPython.Testimport (IInterfaceTest,SubClassTest,EventArgsTest,
12-
FunctionsTest,IGenericInterface,GenericVirtualMethodTest)
12+
FunctionsTest,IGenericInterface,GenericVirtualMethodTest,SimpleClass,ISayHello1)
1313
fromSystem.Collections.GenericimportList
1414

1515

@@ -338,4 +338,9 @@ class OverloadingSubclass2(OverloadingSubclass):
338338
obj=OverloadingSubclass2()
339339
assertobj.VirtMethod[int](5)==5
340340

341-
341+
deftest_implement_interface_and_class():
342+
classDualSubClass0(ISayHello1,SimpleClass):
343+
__namespace__="Test"
344+
defSayHello(self):
345+
return"hello"
346+
obj=DualSubClass0()

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp