Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e8ea7a6
1776 Inherit Generic Virtual Method Bug: Unit Test
rmadsen-ks Apr 27, 2022
691f207
1776 Inherit Generic Virtual Method Bug: Fix
rmadsen-ks Apr 28, 2022
e27ad8c
1774-ClassWithoutnamespace
rmadsen-ks Apr 29, 2022
f1fa696
better support for multiple inheritance
rmadsen-ks May 3, 2022
6f0b354
Fixed issue with protected constructors and classes with no constructor
rmadsen-ks May 3, 2022
3963621
Added supporr for class/property/method attributes.
rmadsen-ks May 5, 2022
4e0d003
- Expanded the way attributes amy be added
rmadsen-ks May 5, 2022
ea598a2
added support for creating abstract classes using a __clr_abstract__ …
rmadsen-ks May 6, 2022
d6abbb2
Improved AttributeError when looking for a symbol that does not exist…
rmadsen-ks May 24, 2022
2a84675
Added test to detect an issue with object construction.
rmadsen-ks Jun 28, 2022
172c642
got rid of a few deprecation warnings that pollute GitHub code review
lostmsu Jun 30, 2022
32d15eb
docs: Fix a few typos
timgates42 Jul 16, 2022
9eaf35f
Added support for marking properties with python types.
rmadsen-ks Sep 2, 2022
9ed94f6
Fixed issue calling base-base class implementation of methods.
rmadsen-ks Sep 2, 2022
da146d9
Merged with Pythonnet 3.0 RC.6.
rmadsen-ks Oct 28, 2022
b280b1b
Fixed bug related to converting number to a string
rmadsen-ks Oct 28, 2022
61c5d99
Added support for Python 3.11.
rmadsen-ks Oct 28, 2022
ed89819
Merge remote-tracking branch 'github/master'
rmadsen-ks Nov 14, 2023
56c8a39
Merge branch 'master' of github.com:pythonnet/pythonnet
rmadsen-ks Nov 14, 2023
e986114
Merge remote-tracking branch 'github/master'
rmadsen-ks Aug 1, 2024
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
better support for multiple inheritance
  • Loading branch information
rmadsen-ks committed Oct 26, 2022
commit f1fa6968da3381543e3906a9cd01d4151a25b5b9
4 changes: 4 additions & 0 deletions src/python_tests_runner/PythonTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public class PythonTestRunner
[OneTimeSetUp]
public void SetUp()
{
Python.Runtime.Runtime.PythonDLL =
"/Library/Frameworks/Python.framework/Versions/3.10/lib/libpython3.10.dylib";
PythonEngine.Initialize();
}

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

/// <summary>
Expand Down
6 changes: 6 additions & 0 deletions src/runtime/Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1824,6 +1824,12 @@ internal static void SetNoSiteFlag()
return *Delegates.Py_NoSiteFlag;
});
}

internal static uint PyTuple_GetSize(BorrowedReference tuple)
{
IntPtr r = Delegates.PyTuple_Size(tuple);
return (uint)r.ToInt32();
}
}

internal class BadPythonDllException : MissingMethodException
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/StateSerialization/MaybeType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ internal struct MaybeType : ISerializable
const string SerializationName = "n";
readonly string name;
readonly Type type;

public string DeletedMessage
{
get
Expand All @@ -38,6 +37,7 @@ public Type Value

public string Name => name;
public bool Valid => type != null;
public Type ValueOrNull => type;

public override string ToString()
{
Expand All @@ -61,4 +61,4 @@ public void GetObjectData(SerializationInfo serializationInfo, StreamingContext
serializationInfo.AddValue(SerializationName, name);
}
}
}
}
7 changes: 4 additions & 3 deletions src/runtime/TypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ static PyTuple GetBaseTypeTuple(Type clrType)
return new PyTuple(bases);
}

internal static NewReference CreateSubType(BorrowedReference py_name, BorrowedReference py_base_type, BorrowedReference dictRef)
internal static NewReference CreateSubType(BorrowedReference py_name, IEnumerable<ClassBase> py_base_type, IEnumerable<Type> interfaces, BorrowedReference dictRef)
{
// Utility to create a subtype of a managed type with the ability for the
// a python subtype able to override the managed implementation
Expand Down Expand Up @@ -415,9 +415,10 @@ internal static NewReference CreateSubType(BorrowedReference py_name, BorrowedRe
}

// create the new managed type subclassing the base managed type
if (ManagedType.GetManagedObject(py_base_type) is ClassBase baseClass)
var baseClass = py_base_type.FirstOrDefault();
if (null == baseClass)
{
return ReflectedClrType.CreateSubclass(baseClass, name,
return ReflectedClrType.CreateSubclass(baseClass, interfaces, name,
ns: (string?)namespaceStr,
assembly: (string?)assembly,
dict: dictRef);
Expand Down
7 changes: 4 additions & 3 deletions src/runtime/Types/ClassDerived.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ internal static NewReference ToPython(IPythonDerivedType obj)
/// </summary>
internal static Type CreateDerivedType(string name,
Type baseType,
IEnumerable<Type> interfaces2,
BorrowedReference py_dict,
string? namespaceStr,
string? assemblyName,
Expand All @@ -163,8 +164,8 @@ internal static Type CreateDerivedType(string name,
ModuleBuilder moduleBuilder = GetModuleBuilder(assemblyName, moduleName);

Type baseClass = baseType;
var interfaces = new List<Type> { typeof(IPythonDerivedType) };

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

// override any virtual methods not already overridden by the properties above
MethodInfo[] methods = baseType.GetMethods();
var methods = baseType.GetMethods().Concat(interfaces.SelectMany(x => x.GetMethods()));
var virtualMethods = new HashSet<string>();
foreach (MethodInfo method in methods)
{
Expand Down
61 changes: 45 additions & 16 deletions src/runtime/Types/MetaType.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;

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

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

List<Type> interfaces = new List<Type>();
List<ClassBase> baseType = new List<ClassBase>();

var cnt = Runtime.PyTuple_GetSize(bases);

for (uint i = 0; i < cnt; i++)
{
var base_type2 = Runtime.PyTuple_GetItem(bases, (int)i);
var cb2 = (ClassBase) GetManagedObject(base_type2);
if (cb2 != null)
{
if (cb2.type.Valid && cb2.type.Value.IsInterface)
interfaces.Add(cb2.type.Value);
else baseType.Add(cb2);
}
}

if (baseType.Count == 0)
{
baseType.Add(new ClassBase(typeof(object)));
}


if (baseType.Count > 1)
{
return Exceptions.RaiseTypeError("cannot use multiple inheritance with managed classes");
}

BorrowedReference base_type = Runtime.PyTuple_GetItem(bases, 0);
BorrowedReference mt = Runtime.PyObject_TYPE(base_type);
/*
BorrowedReference mt = Runtime.PyObject_TYPE(baseType);

if (!(mt == PyCLRMetaType || mt == Runtime.PyTypeType))
{
return Exceptions.RaiseTypeError("invalid metatype");
}
}*/

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

if (GetManagedObject(base_type) is ClassBase cb)
var cb = baseType.First();
try
{
try
if (!cb.CanSubclass())
{
if (!cb.CanSubclass())
{
return Exceptions.RaiseTypeError("delegates, enums and array types cannot be subclassed");
}
}
catch (SerializationException)
{
return Exceptions.RaiseTypeError($"Underlying C# Base class {cb.type} has been deleted");
return Exceptions.RaiseTypeError("delegates, enums and array types cannot be subclassed");
}
}
catch (SerializationException)
{
return Exceptions.RaiseTypeError($"Underlying C# Base class {cb.type} has been deleted");
}

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

if (clsDict.HasKey("__assembly__") || clsDict.HasKey("__namespace__")
|| (cb.type.Valid && cb.type.Value != null && cb.type.Value.GetConstructor(Array.Empty<Type>()) != null))
|| (ctor != null))
{
if (!clsDict.HasKey("__namespace__"))
{
clsDict["__namespace__"] =
(clsDict["__module__"].ToString()).ToPython();
}
return TypeManager.CreateSubType(name, base_type, clsDict);
return TypeManager.CreateSubType(name, baseType, interfaces, clsDict);
}

}

var base_type = Runtime.PyTuple_GetItem(bases, 0);
// otherwise just create a basic type without reflecting back into the managed side.
IntPtr func = Util.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_new);
NewReference type = NativeCall.Call_3(func, tp, args, kw);
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/Types/ReflectedClrType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,15 @@ internal void Restore(ClassBase cb)
TypeManager.InitializeClass(this, cb, cb.type.Value);
}

internal static NewReference CreateSubclass(ClassBase baseClass,
internal static NewReference CreateSubclass(ClassBase baseClass, IEnumerable<Type> interfaces,
string name, string? assembly, string? ns,
BorrowedReference dict)
{
try
{
Type subType = ClassDerivedObject.CreateDerivedType(name,
baseClass.type.Value,
interfaces,
dict,
ns,
assembly);
Expand Down
10 changes: 6 additions & 4 deletions src/testing/generictest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ public string Overloaded<Q>(int arg1, int arg2, string arg3)
{
return arg3;
}

public virtual Q VirtualOverloaded<Q>(Q arg)
{
return arg;
}
}

public class GenericStaticMethodTest<T>
Expand All @@ -118,10 +123,7 @@ public static Q Overloaded<Q>(Q arg)
return arg;
}

public static U Overloaded<Q, U>(Q arg1, U arg2)
{
return arg2;
}


public static string Overloaded<Q>(int arg1, int arg2, string arg3)
{
Expand Down
21 changes: 21 additions & 0 deletions src/testing/subclasstest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,25 @@ public static int test_event(IInterfaceTest x, int value)
return et.value;
}
}

public interface ISimpleInterface
{
bool Ok();
}

public class SimpleClass
{

public static void TestObject(object obj)
{
if (obj is ISimpleInterface)
{

}
else
{
throw new Exception();
}
}
}
}
24 changes: 23 additions & 1 deletion tests/test_subclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import System
import pytest
from Python.Test import (IInterfaceTest, SubClassTest, EventArgsTest,
FunctionsTest, GenericVirtualMethodTest)
FunctionsTest, GenericVirtualMethodTest, ISimpleInterface, SimpleClass)
from System.Collections.Generic import List


Expand Down Expand Up @@ -272,6 +272,28 @@ class OverloadingSubclass2(OverloadingSubclass):
obj = OverloadingSubclass()
assert obj.VirtMethod[int](5) == 5

def test_interface_and_class_impl():
class OverloadingSubclass(GenericVirtualMethodTest):
__namespace__ = "test_virtual_generic_method_cls"
class OverloadingSubclass2(OverloadingSubclass):
pass
obj = OverloadingSubclass()
assert obj.VirtMethod[int](5) == 5

def test_interface_and_class_impl2():
class DualSubClass(ISimpleInterface, SimpleClass):
def Ok(self):
return True
class DualSubClass2(ISimpleInterface):
def Ok(self):
return True

obj = DualSubClass()
SimpleClass.TestObject(obj)
obj = DualSubClass2()
SimpleClass.TestObject(obj)


def test_construction_from_clr():
import clr
calls = []
Expand Down