8000 better support for multiple inheritance · pythonnet/pythonnet@a2c80cd · GitHub
[go: up one dir, main page]

Skip to content

Commit a2c80cd

Browse files
committed
better support for multiple inheritance
1 parent eb4ce37 commit a2c80cd

File tree

10 files changed

+117
-30
lines changed

10 files changed

+117
-30
lines changed

src/python_tests_runner/PythonTestRunner.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public class PythonTestRunner
1717
[OneTimeSetUp]
1818
public void SetUp()
1919
{
20+
Python.Runtime.Runtime.PythonDLL =
21+
"/Library/Frameworks/Python.framework/Versions/3.10/lib/libpython3.10.dylib";
2022
PythonEngine.Initialize();
2123
}
2224

@@ -35,6 +37,8 @@ static IEnumerable<string[]> PythonTestCases()
3537
// Add the test that you want to debug here.
3638
yield return new[] { "test_indexer", "test_boolean_indexer" };
3739
yield return new[] { "test_delegate", "test_bool_delegate" };
40+
yield return new[] { "test_subclass", "test_virtual_generic_method" };
41+
yield return new[] { "test_subclass", "test_interface_and_class_impl2" };
3842
}
3943

4044
/// <summary>

src/runtime/Runtime.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1833,6 +1833,12 @@ internal static void SetNoSiteFlag()
18331833
return *Delegates.Py_NoSiteFlag;
18341834
});
18351835
}
1836+
1837+
internal static uint PyTuple_GetSize(BorrowedReference tuple)
1838+
{
1839+
IntPtr r = Delegates.PyTuple_Size(tuple);
1840+
return (uint)r.ToInt32();
1841+
}
18361842
}
18371843

18381844
internal class BadPythonDllException : MissingMethodException

src/runtime/StateSerialization/MaybeType.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ internal struct MaybeType : ISerializable
1515
const string SerializationName = "n";
1616
readonly string name;
1717
readonly Type type;
18-
1918
public string DeletedMessage
2019
{
2120
get
@@ -38,6 +37,7 @@ public Type Value
3837

3938
public string Name => name;
4039
public bool Valid => type != null;
40+
public Type ValueOrNull => type;
4141

4242
public override string ToString()
4343
{
@@ -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: 4 additions & 3 deletions
Original fil F438 e line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ static PyTuple GetBaseTypeTuple(Type clrType)
374374
return new PyTuple(bases);
375375
}
376376

377-
internal static NewReference CreateSubType(BorrowedReference py_name, BorrowedReference py_base_type, BorrowedReference dictRef)
377+
internal static NewReference CreateSubType(BorrowedReference py_name, IEnumerable<ClassBase> py_base_type, IEnumerable<Type> interfaces, BorrowedReference dictRef)
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,9 +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) is ClassBase baseClass)
418+
var baseClass = py_base_type.FirstOrDefault();
419+
if (null == baseClass)
419420
{
420-
return ReflectedClrType.CreateSubclass(baseClass, name,
421+
return ReflectedClrType.CreateSubclass(baseClass, interfaces, name,
421422
ns: (string?)namespaceStr,
422423
assembly: (string?)assembly,
423424
dict: dictRef);

src/runtime/Types/ClassDerived.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ internal static NewReference ToPython(IPythonDerivedType obj)
144144
/// </summary>
145145
internal static Type CreateDerivedType(string name,
146146
Type baseType,
147+
IEnumerable<Type> interfaces2,
147148
BorrowedReference py_dict,
148149
string? namespaceStr,
149150
string? assemblyName,
@@ -163,8 +164,8 @@ internal static Type CreateDerivedType(string name,
163164
ModuleBuilder moduleBuilder = GetModuleBuilder(assemblyName, moduleName);
164165

165166
Type baseClass = baseType;
166-
var interfaces = new List<Type> { typeof(IPythonDerivedType) };
167-
167+
var interfaces = new HashSet<Type> { typeof(IPythonDerivedType) };
168+
foreach(var t in interfaces2) interfaces.Add(t);
168169
// if the base type is an interface then use System.Object as the base class
169170
// and add the base type to the list of interfaces this new class will implement.
170171
if (baseType.IsInterface)
@@ -215,7 +216,7 @@ internal static Type CreateDerivedType(string name,
215216
}
216217

217218
// override any virtual methods not already overridden by the properties above
218-
MethodInfo[] methods = baseType.GetMethods();
219+
var methods = baseType.GetMethods().Concat(interfaces.SelectMany(x => x.GetMethods()));
219220
var virtualMethods = new HashSet<string>();
220221
foreach (MethodInfo method in methods)
221222
{

src/runtime/Types/MetaType.cs

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
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

@@ -84,36 +87,58 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args,
8487
// That type must itself have a managed implementation. We check
8588
// that by making sure its metatype is the CLR metatype.
8689

87-
if (Runtime.PyTuple_Size(bases) != 1)
90+
91+
List<Type> interfaces = new List<Type>();
92+
List<ClassBase> baseType = new List<ClassBase>();
93+
94+
var cnt = Runtime.PyTuple_GetSize(bases);
95+
96+
for (uint i = 0; i < cnt; i++)
97+
{
98+
var base_type2 = Runtime.PyTuple_GetItem(bases, (int)i);
99+
var cb2 = (ClassBase) GetManagedObject(base_type2);
100+
if (cb2 != null)
101+
{
102+
if (cb2.type.Valid && cb2.type.Value.IsInterface)
103+
interfaces.Add(cb2.type.Value);
104+
else baseType.Add(cb2);
105+
}
106+
}
107+
108+
if (baseType.Count == 0)
109+
{
110+
baseType.Add(new ClassBase(typeof(object)));
111+
}
112+
113+
114+
if (baseType.Count > 1)
88115
{
89116
return Exceptions.RaiseTypeError("cannot use multiple inheritance with managed classes");
90117
}
91118

92-
BorrowedReference base_type = Runtime.PyTuple_GetItem(bases, 0);
93-
BorrowedReference mt = Runtime.PyObject_TYPE(base_type);
119+
/*
120+
BorrowedReference mt = Runtime.PyObject_TYPE(baseType);
94121
95122
if (!(mt == PyCLRMetaType || mt == Runtime.PyTypeType))
96123
{
97124
return Exceptions.RaiseTypeError("invalid metatype");
98-
}
125+
}*/
99126

100127
// Ensure that the reflected type is appropriate for subclassing,
101128
// disallowing subclassing of delegates, enums and array types.
102129

103-
if (GetManagedObject(base_type) is ClassBase cb)
130+
var cb = baseType.First();
131+
try
104132
{
105-
try
133+
if (!cb.CanSubclass())
106134
{
107-
if (!cb.CanSubclass())
108-
{
109-
return Exceptions.RaiseTypeError("delegates, enums and array types cannot be subclassed");
110-
}
111-
}
112-
catch (SerializationException)
113-
{
114-
return Exceptions.RaiseTypeError($"Underlying C# Base class {cb.type} has been deleted");
135+
return Exceptions.RaiseTypeError("delegates, enums and array types cannot be subclassed");
115136
}
116137
}
138+
catch (SerializationException)
139+
{
140+
return Exceptions.RaiseTypeError($"Underlying C# Base class {cb.type} has been deleted");
141+
}
117142

118143
BorrowedReference slots = Runtime.PyDict_GetItem(dict, PyIdentifier.__slots__);
119144
if (slots != null)
@@ -127,21 +152,25 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args,
127152
// into python.
128153
if (null != dict)
129154
{
155+
var btt = baseType.FirstOrDefault().type.ValueOrNull;
156+
var ctor = btt?.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
157+
.FirstOrDefault(x => x.GetParameters().Any() == false);
130158
using var clsDict = new PyDict(dict);
131159

132160
if (clsDict.HasKey("__assembly__") || clsDict.HasKey("__namespace__")
133-
|| (cb.type.Valid && cb.type.Value != null && cb.type.Value.GetConstructor(Array.Empty<Type>()) != null))
161+
|| (ctor != null))
134162
{
135163
if (!clsDict.HasKey("__namespace__"))
136164
{
137165
clsDict["__namespace__"] =
138166
(clsDict["__module__"].ToString()).ToPython();
139167
}
140-
return TypeManager.CreateSubType(name, base_type, clsDict);
168+
return TypeManager.CreateSubType(name, baseType, interfaces, clsDict);
141169
}
142170

143171
}
144172

173+
var base_type = Runtime.PyTuple_GetItem(bases, 0);
145174
// otherwise just create a basic type without reflecting back into the managed side.
146175
IntPtr func = Util.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_new);
147176
NewReference type = 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-
internal static NewReference CreateSubclass(ClassBase baseClass,
71+
internal static NewReference CreateSubclass(ClassBase baseClass, IEnumerable<Type> interfaces,
7272
string name, string? assembly, string? ns,
7373
BorrowedReference dict)
7474
{
7575
try
7676
{
7777
Type subType = ClassDerivedObject.CreateDerivedType(name,
7878
baseClass.type.Value,
79+
interfaces,
7980
dict,
8081
ns,
8182
assembly);

src/testing/generictest.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ public string Overloaded<Q>(int arg1, int arg2, string arg3)
9595
{
9696
return arg3;
9797
}
98+
99+
public virtual Q VirtualOverloaded<Q>(Q arg)
100+
{
101+
return arg;
102+
}
98103
}
99104

100105
public class GenericStaticMethodTest<T>
@@ -118,10 +123,7 @@ public static Q Overloaded<Q>(Q arg)
118123
return arg;
119124
}
120125

121-
public static U Overloaded<Q, U>(Q arg1, U arg2)
122-
{
123-
return arg2;
124-
}
126+
125127

126128
public static string Overloaded<Q>(int arg1, int arg2, string arg3)
127129
{

src/testing/subclasstest.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,25 @@ public static int test_event(IInterfaceTest x, int value)
124124
return et.value;
125125
}
126126
}
127+
128+
public interface ISimpleInterface
129+
{
130+
bool Ok();
131+
}
132+
133+
public class SimpleClass
134+
{
135+
136+
public static void TestObject(object obj)
137+
{
138+
if (obj is ISimpleInterface)
139+
{
140+
141+
}
142+
else
143+
{
144+
throw new Exception();
145+
}
146+
}
147+
}
127148
}

tests/test_subclass.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import System
1010
import pytest
1111from Python.Test import (IInterfaceTest, SubClassTest, EventArgsTest,
12-
FunctionsTest, GenericVirtualMethodTest)
12+
FunctionsTest, GenericVirtualMethodTest, ISimpleInterface, SimpleClass)
1313
from System.Collections.Generic import List
1414

1515

@@ -272,6 +272,28 @@ class OverloadingSubclass2(OverloadingSubclass):
272272
obj = OverloadingSubclass()
273273
assert obj.VirtMethod[int](5) == 5
274274

275+
def test_interface_and_class_impl():
276+
class OverloadingSubclass(GenericVirtualMethodTest):
277+
__namespace__ = "test_virtual_generic_method_cls"
278+
class OverloadingSubclass2(OverloadingSubclass):
279+
pass
280+
obj = OverloadingSubclass()
281+
assert obj.VirtMethod[int](5) == 5
282+
283+
def test_interface_and_class_impl2():
284+
class DualSubClass(ISimpleInterface, SimpleClass):
285+
def Ok(self):
286+
return True
287+
class DualSubClass2(ISimpleInterface):
288+
def Ok(self):
289+
return True
290+
291+
obj = DualSubClass()
292+
SimpleClass.TestObject(obj)
293+
obj = DualSubClass2()
294+
SimpleClass.TestObject(obj)
295+
296+
275297
def test_construction_from_clr():
276298
import clr
277299
calls = []

0 commit comments

Comments
 (0)
0