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

1783 Implement Interface And Inherit Class#2028

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
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
2 changes: 2 additions & 0 deletionsCHANGELOG.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,6 +16,8 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Fixed error occuring when inheriting a class containing a virtual generic method.
- Make a second call to `pythonnet.load` a no-op, as it was intended.

- Added support for multiple inheritance when inheriting from a class and/or multiple interfaces.

## [3.0.1](https://github.com/pythonnet/pythonnet/releases/tag/v3.0.1) - 2022-11-03

### Added
Expand Down
1 change: 1 addition & 0 deletionssrc/python_tests_runner/PythonTestRunner.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,6 +35,7 @@ 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_implement_interface_and_class" };
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletionssrc/runtime/StateSerialization/MaybeType.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,7 @@ internal struct MaybeType : ISerializable
const string SerializationName = "n";
readonly string name;
readonly Type type;

public string DeletedMessage
{
get
Expand DownExpand Up@@ -61,4 +61,4 @@ public void GetObjectData(SerializationInfo serializationInfo, StreamingContext
serializationInfo.AddValue(SerializationName, name);
}
}
}
}
17 changes: 5 additions & 12 deletionssrc/runtime/TypeManager.cs
View file
Open in desktop
Original file line numberDiff line numberDiff 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,ClassBase py_base_type, IList<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 DownExpand Up@@ -415,17 +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)
{
return ReflectedClrType.CreateSubclass(baseClass, name,
ns: (string?)namespaceStr,
assembly: (string?)assembly,
dict: dictRef);
}
else
{
return Exceptions.RaiseTypeError("invalid base class, expected CLR class type");
}
return ReflectedClrType.CreateSubclass(py_base_type, interfaces, name,
ns: (string?)namespaceStr,
assembly: (string?)assembly,
dict: dictRef);
}

internal static IntPtr WriteMethodDef(IntPtr mdef, IntPtr name, IntPtr func, PyMethodFlags flags, IntPtr doc)
Expand Down
10 changes: 7 additions & 3 deletionssrc/runtime/Types/ClassDerived.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -144,6 +144,7 @@ internal static NewReference ToPython(IPythonDerivedType obj)
/// </summary>
internal static Type CreateDerivedType(string name,
Type baseType,
IList<Type> typeInterfaces,
BorrowedReference py_dict,
string? namespaceStr,
string? assemblyName,
Expand All@@ -163,7 +164,9 @@ 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 interfaceType in typeInterfaces)
interfaces.Add(interfaceType);

// 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.
Expand DownExpand Up@@ -214,8 +217,9 @@ internal static Type CreateDerivedType(string name,
}
}

// override any virtual methods not already overridden by the properties above
MethodInfo[] methods = baseType.GetMethods();
// override any virtual not already overridden by the properties above
// also override any interface method.
var methods = baseType.GetMethods().Concat(interfaces.SelectMany(x => x.GetMethods()));
var virtualMethods = new HashSet<string>();
foreach (MethodInfo method in methods)
{
Expand Down
90 changes: 67 additions & 23 deletionssrc/runtime/Types/MetaType.cs
View file
Open in desktop
Original file line numberDiff line numberDiff 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 DownExpand Up@@ -79,41 +82,80 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args,
BorrowedReference bases = Runtime.PyTuple_GetItem(args, 1);
BorrowedReference dict = Runtime.PyTuple_GetItem(args, 2);

// We do not support multiple inheritance, so the bases argument
// should be a 1-item tuple containing the type we are subtyping.
// That type must itself have a managed implementation. We check
// that by making sure its metatype is the CLR metatype.
// Extract interface types and base class types.
var interfaces = new List<Type>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

IMHO, repeating the same base interface multiple times should be an error.

And it might be, add a test.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Test added.


if (Runtime.PyTuple_Size(bases) != 1)
{
return Exceptions.RaiseTypeError("cannot use multiple inheritance with managed classes");
}
// More than one base type case be declared, but an exception will be thrown
// if more than one is a class/not an interface.
var baseTypes = new List<ClassBase>();

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

if (!(mt == PyCLRMetaType || mt == Runtime.PyTypeType))
var baseClassCount = Runtime.PyTuple_Size(bases);
if (baseClassCount == 0)
{
return Exceptions.RaiseTypeError("invalid metatype");
return Exceptions.RaiseTypeError("zero base classes");
}

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

if (GetManagedObject(base_type) is ClassBase cb)
for (nint i = 0; i < baseClassCount; i++)
{
try
var baseTypeIt = Runtime.PyTuple_GetItem(bases, (int)i);

if (GetManagedObject(baseTypeIt) is ClassBase classBaseIt)
{
if (!cb.CanSubclass())
if (!classBaseIt.type.Valid)
{
return Exceptions.RaiseTypeError("Invalid type used as a super type.");
}
if (classBaseIt.type.Value.IsInterface)
{
return Exceptions.RaiseTypeError("delegates, enums and array types cannot be subclassed");
interfaces.Add(classBaseIt.type.Value);
}
else
{
baseTypes.Add(classBaseIt);
}
}
else
{
return Exceptions.RaiseTypeError("Non .NET type used as super class for meta type. This is not supported.");
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

What if it is not a .NET class or interface? You can't simply ignore it.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Added exceptions here in the latest version.

catch (SerializationException)
}
// if the base type count is 0, there might still be interfaces to implement.
if (baseTypes.Count == 0)
{
baseTypes.Add(new ClassBase(typeof(object)));
}

// Multiple inheritance is not supported, unless the other types are interfaces
if (baseTypes.Count > 1)
{
var types = string.Join(", ", baseTypes.Select(baseType => baseType.type.Value));
return Exceptions.RaiseTypeError($"Multiple inheritance with managed classes cannot be used. Types: {types} ");
}

// check if the list of interfaces contains no duplicates.
if (interfaces.Distinct().Count() != interfaces.Count)
{
// generate a string containing the problematic types.
var duplicateTypes = interfaces.GroupBy(type => type)
.Where(typeGroup => typeGroup.Count() > 1)
.Select(typeGroup => typeGroup.Key);
var duplicateTypesString = string.Join(", ", duplicateTypes);

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

var cb = baseTypes[0];
try
{
if (!cb.CanSubclass())
{
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@@ -130,10 +172,12 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args,
using var clsDict = new PyDict(dict);
if (clsDict.HasKey("__assembly__") || clsDict.HasKey("__namespace__"))
{
return TypeManager.CreateSubType(name,base_type, clsDict);
return TypeManager.CreateSubType(name,baseTypes[0], interfaces, clsDict);
}
}

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

bases here can be empty since you removed check above.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

How can bases be length 0? This should only be called when a C# type is subclassed, right? In this case, it should at least be length 1.


// 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 deletionsrc/runtime/Types/ReflectedClrType.cs
View file
Open in desktop
Original file line numberDiff line numberDiff 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, IList<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
12 changes: 12 additions & 0 deletionssrc/testing/classtest.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections;

namespace Python.Test
Expand DownExpand Up@@ -59,4 +60,15 @@ public ClassCtorTest2(string v)
internal class InternalClass
{
}

public class SimpleClass
{
public static void TestObject(object obj)
{
if ((!(obj is ISayHello1 && obj is SimpleClass)))
{
throw new Exception("Expected ISayHello and SimpleClass instance");
}
}
}
}
9 changes: 7 additions & 2 deletionstests/test_subclass.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@
import System
import pytest
from Python.Test import (IInterfaceTest, SubClassTest, EventArgsTest,
FunctionsTest, IGenericInterface, GenericVirtualMethodTest)
FunctionsTest, IGenericInterface, GenericVirtualMethodTest, SimpleClass, ISayHello1)
from System.Collections.Generic import List


Expand DownExpand Up@@ -338,4 +338,9 @@ class OverloadingSubclass2(OverloadingSubclass):
obj = OverloadingSubclass2()
assert obj.VirtMethod[int](5) == 5


def test_implement_interface_and_class():
class DualSubClass0(ISayHello1, SimpleClass):
__namespace__ = "Test"
def SayHello(self):
return "hello"
obj = DualSubClass0()

[8]ページ先頭

©2009-2025 Movatter.jp