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

Wrap returned objects in interface if method return type is interface#1240

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
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
5 changes: 5 additions & 0 deletionsCHANGELOG.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,11 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
details about the cause of the failure
- `clr.AddReference` no longer adds ".dll" implicitly
- `PyIter(PyObject)` constructor replaced with static `PyIter.GetIter(PyObject)` method
- Return values from .NET methods that return an interface are now automatically
wrapped in that interface. This is a breaking change for users that rely on being
able to access members that are part of the implementation class, but not the
interface. Use the new __implementation__ or __raw_implementation__ properties to
if you need to "downcast" to the implementation class.

### Fixed

Expand Down
3 changes: 2 additions & 1 deletionsrc/runtime/arrayobject.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -43,8 +43,9 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
{
var obj = (CLRObject)GetManagedObject(ob);
var arrObj = (ArrayObject)GetManagedObjectType(ob);
var items = obj.inst as Array;
Type itemType =obj.inst.GetType().GetElementType();
Type itemType =arrObj.type.GetElementType();
int rank = items.Rank;
int index;
object value;
Expand Down
15 changes: 15 additions & 0 deletionssrc/runtime/converter.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -173,6 +173,21 @@ internal static IntPtr ToPython(object value, Type type)
}
}

if (type.IsInterface)
{
var ifaceObj = (InterfaceObject)ClassManager.GetClass(type);
return ifaceObj.WrapObject(value);
}

// We need to special case interface array handling to ensure we
// produce the correct type. Value may be an array of some concrete
// type (FooImpl[]), but we want access to go via the interface type
// (IFoo[]).
if (type.IsArray && type.GetElementType().IsInterface)
{
return CLRObject.GetInstHandle(value, type);
}

// it the type is a python subclass of a managed type then return the
// underlying python object rather than construct a new wrapper object.
var pyderived = value as IPythonDerivedType;
Expand Down
38 changes: 37 additions & 1 deletionsrc/runtime/interfaceobject.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -71,7 +71,43 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
return IntPtr.Zero;
}

return CLRObject.GetInstHandle(obj, self.pyHandle);
return self.WrapObject(obj);
}

/// <summary>
/// Wrap the given object in an interface object, so that only methods
/// of the interface are available.
/// </summary>
public IntPtr WrapObject(object impl)
{
var objPtr = CLRObject.GetInstHandle(impl, pyHandle);
return objPtr;
}

/// <summary>
/// Expose the wrapped implementation through attributes in both
/// converted/encoded (__implementation__) and raw (__raw_implementation__) form.
/// </summary>
public static IntPtr tp_getattro(IntPtr ob, IntPtr key)
{
var clrObj = (CLRObject)GetManagedObject(ob);

if (!Runtime.PyString_Check(key))
{
return Exceptions.RaiseTypeError("string expected");
}

string name = Runtime.GetManagedString(key);
if (name == "__implementation__")
{
return Converter.ToPython(clrObj.inst);
}
else if (name == "__raw_implementation__")
{
return CLRObject.GetInstHandle(clrObj.inst);
}

return Runtime.PyObject_GenericGetAttr(ob, key);
}
}
}
19 changes: 19 additions & 0 deletionssrc/runtime/managedtype.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,25 @@ internal static ManagedType GetManagedObject(IntPtr ob)
return null;
}

/// <summary>
/// Given a Python object, return the associated managed object type or null.
/// </summary>
internal static ManagedType GetManagedObjectType(IntPtr ob)
{
if (ob != IntPtr.Zero)
{
IntPtr tp = Runtime.PyObject_TYPE(ob);
var flags = Util.ReadCLong(tp, TypeOffset.tp_flags);
if ((flags & TypeFlags.Managed) != 0)
{
tp = Marshal.ReadIntPtr(tp, TypeOffset.magic());
var gc = (GCHandle)tp;
return (ManagedType)gc.Target;
}
}
return null;
}


internal static ManagedType GetManagedObjectErr(IntPtr ob)
{
Expand Down
2 changes: 1 addition & 1 deletionsrc/runtime/methodbinder.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -744,7 +744,7 @@ internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase i
Type pt = pi[i].ParameterType;
if (pi[i].IsOut || pt.IsByRef)
{
v = Converter.ToPython(binding.args[i], pt);
v = Converter.ToPython(binding.args[i], pt.GetElementType());
Runtime.PyTuple_SetItem(t, n, v);
n++;
}
Expand Down
4 changes: 2 additions & 2 deletionssrc/runtime/typemanager.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,13 +164,13 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
// we want to do this after the slot stuff above in case the class itself implements a slot method
InitializeSlots(type, impl.GetType());

if (!clrType.GetInterfaces().Any(ifc => ifc == typeof(IEnumerable) || ifc == typeof(IEnumerator)))
if (!typeof(IEnumerable).IsAssignableFrom(clrType) &&
!typeof(IEnumerator).IsAssignableFrom(clrType))
{
// The tp_iter slot should only be set for enumerable types.
Marshal.WriteIntPtr(type, TypeOffset.tp_iter, IntPtr.Zero);
}


if (base_ != IntPtr.Zero)
{
Marshal.WriteIntPtr(type, TypeOffset.tp_base, base_);
Expand Down
22 changes: 21 additions & 1 deletionsrc/testing/interfacetest.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@ internal interface IInternalInterface
{
}


public interface ISayHello1
{
string SayHello();
Expand DownExpand Up@@ -43,6 +42,27 @@ string ISayHello2.SayHello()
return "hello 2";
}

public ISayHello1 GetISayHello1()
{
return this;
}

public void GetISayHello2(out ISayHello2 hello2)
{
hello2 = this;
}

public ISayHello1 GetNoSayHello(out ISayHello2 hello2)
{
hello2 = null;
return null;
}

public ISayHello1 [] GetISayHello1Array()
{
return new[] { this };
}

public interface IPublic
{
}
Expand Down
17 changes: 14 additions & 3 deletionssrc/testing/subclasstest.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,13 +89,24 @@ public static string test_bar(IInterfaceTest x, string s, int i)
}

// test instances can be constructed in managed code
public static IInterfaceTest create_instance(Type t)
public static SubClassTest create_instance(Type t)
{
return (SubClassTest)t.GetConstructor(new Type[] { }).Invoke(new object[] { });
}

public static IInterfaceTest create_instance_interface(Type t)
{
return (IInterfaceTest)t.GetConstructor(new Type[] { }).Invoke(new object[] { });
}

// test instances pass through managed code unchanged
public static IInterfaceTest pass_through(IInterfaceTest s)
// test instances pass through managed code unchanged ...
public static SubClassTest pass_through(SubClassTest s)
{
return s;
}

// ... but the return type is an interface type, objects get wrapped
public static IInterfaceTest pass_through_interface(IInterfaceTest s)
{
return s;
}
Expand Down
5 changes: 3 additions & 2 deletionssrc/tests/test_array.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1288,9 +1288,10 @@ def test_special_array_creation():
assert value[1].__class__ == inst.__class__
assert value.Length == 2

iface_class = ISayHello1(inst).__class__
value = Array[ISayHello1]([inst, inst])
assert value[0].__class__ ==inst.__class__
assert value[1].__class__ ==inst.__class__
assert value[0].__class__ ==iface_class
assert value[1].__class__ ==iface_class
assert value.Length == 2

inst = System.Exception("badness")
Expand Down
7 changes: 4 additions & 3 deletionssrc/tests/test_generic.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -319,7 +319,6 @@ def test_generic_method_type_handling():
assert_generic_method_by_type(ShortEnum, ShortEnum.Zero)
assert_generic_method_by_type(System.Object, InterfaceTest())
assert_generic_method_by_type(InterfaceTest, InterfaceTest(), 1)
assert_generic_method_by_type(ISayHello1, InterfaceTest(), 1)


def test_correct_overload_selection():
Expand DownExpand Up@@ -548,10 +547,11 @@ def test_method_overload_selection_with_generic_types():
value = MethodTest.Overloaded.__overloads__[vtype](input_)
assert value.value.__class__ == inst.__class__

iface_class = ISayHello1(inst).__class__
vtype = GenericWrapper[ISayHello1]
input_ = vtype(inst)
value = MethodTest.Overloaded.__overloads__[vtype](input_)
assert value.value.__class__ ==inst.__class__
assert value.value.__class__ ==iface_class

vtype = System.Array[GenericWrapper[int]]
input_ = vtype([GenericWrapper[int](0), GenericWrapper[int](1)])
Expand DownExpand Up@@ -726,11 +726,12 @@ def test_overload_selection_with_arrays_of_generic_types():
assert value[0].value.__class__ == inst.__class__
assert value.Length == 2

iface_class = ISayHello1(inst).__class__
gtype = GenericWrapper[ISayHello1]
vtype = System.Array[gtype]
input_ = vtype([gtype(inst), gtype(inst)])
value = MethodTest.Overloaded.__overloads__[vtype](input_)
assert value[0].value.__class__ ==inst.__class__
assert value[0].value.__class__ ==iface_class
assert value.Length == 2


Expand Down
53 changes: 53 additions & 0 deletionssrc/tests/test_interface.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,9 +61,62 @@ def test_explicit_cast_to_interface():
assert hasattr(i1, 'SayHello')
assert i1.SayHello() == 'hello 1'
assert not hasattr(i1, 'HelloProperty')
assert i1.__implementation__ == ob
assert i1.__raw_implementation__ == ob

i2 = Test.ISayHello2(ob)
assert type(i2).__name__ == 'ISayHello2'
assert i2.SayHello() == 'hello 2'
assert hasattr(i2, 'SayHello')
assert not hasattr(i2, 'HelloProperty')


def test_interface_object_returned_through_method():
"""Test interface type is used if method return type is interface"""
from Python.Test import InterfaceTest

ob = InterfaceTest()
hello1 = ob.GetISayHello1()
assert type(hello1).__name__ == 'ISayHello1'
assert hello1.__implementation__.__class__.__name__ == "InterfaceTest"

assert hello1.SayHello() == 'hello 1'


def test_interface_object_returned_through_out_param():
"""Test interface type is used for out parameters of interface types"""
from Python.Test import InterfaceTest

ob = InterfaceTest()
hello2 = ob.GetISayHello2(None)
assert type(hello2).__name__ == 'ISayHello2'

assert hello2.SayHello() == 'hello 2'


def test_null_interface_object_returned():
"""Test None is used also for methods with interface return types"""
from Python.Test import InterfaceTest

ob = InterfaceTest()
hello1, hello2 = ob.GetNoSayHello(None)
assert hello1 is None
assert hello2 is None

def test_interface_array_returned():
"""Test interface type used for methods returning interface arrays"""
from Python.Test import InterfaceTest

ob = InterfaceTest()
hellos = ob.GetISayHello1Array()
assert type(hellos[0]).__name__ == 'ISayHello1'
assert hellos[0].__implementation__.__class__.__name__ == "InterfaceTest"

def test_implementation_access():
"""Test the __implementation__ and __raw_implementation__ properties"""
import System
clrVal = System.Int32(100)
i = System.IComparable(clrVal)
assert 100 == i.__implementation__
assert clrVal == i.__raw_implementation__
assert i.__implementation__ != i.__raw_implementation__
9 changes: 6 additions & 3 deletionssrc/tests/test_method.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -564,8 +564,10 @@ def test_explicit_overload_selection():
value = MethodTest.Overloaded.__overloads__[InterfaceTest](inst)
assert value.__class__ == inst.__class__

iface_class = ISayHello1(InterfaceTest()).__class__
value = MethodTest.Overloaded.__overloads__[ISayHello1](inst)
assert value.__class__ == inst.__class__
assert value.__class__ != inst.__class__
assert value.__class__ == iface_class

atype = Array[System.Object]
value = MethodTest.Overloaded.__overloads__[str, int, atype](
Expand DownExpand Up@@ -718,11 +720,12 @@ def test_overload_selection_with_array_types():
assert value[0].__class__ == inst.__class__
assert value[1].__class__ == inst.__class__

iface_class = ISayHello1(inst).__class__
vtype = Array[ISayHello1]
input_ = vtype([inst, inst])
value = MethodTest.Overloaded.__overloads__[vtype](input_)
assert value[0].__class__ ==inst.__class__
assert value[1].__class__ ==inst.__class__
assert value[0].__class__ ==iface_class
assert value[1].__class__ ==iface_class


def test_explicit_overload_selection_failure():
Expand Down
12 changes: 7 additions & 5 deletionssrc/tests/test_subclass.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -104,8 +104,10 @@ def test_interface():
assert ob.bar("bar", 2) == "bar/bar"
assert FunctionsTest.test_bar(ob, "bar", 2) == "bar/bar"

x = FunctionsTest.pass_through(ob)
assert id(x) == id(ob)
# pass_through will convert from InterfaceTestClass -> IInterfaceTest,
# causing a new wrapper object to be created. Hence id will differ.
x = FunctionsTest.pass_through_interface(ob)
assert id(x) != id(ob)


def test_derived_class():
Expand DownExpand Up@@ -173,14 +175,14 @@ def test_create_instance():
assert id(x) == id(ob)

InterfaceTestClass = interface_test_class_fixture(test_create_instance.__name__)
ob2 = FunctionsTest.create_instance(InterfaceTestClass)
ob2 = FunctionsTest.create_instance_interface(InterfaceTestClass)
assert ob2.foo() == "InterfaceTestClass"
assert FunctionsTest.test_foo(ob2) == "InterfaceTestClass"
assert ob2.bar("bar", 2) == "bar/bar"
assert FunctionsTest.test_bar(ob2, "bar", 2) == "bar/bar"

y = FunctionsTest.pass_through(ob2)
assert id(y)== id(ob2)
y = FunctionsTest.pass_through_interface(ob2)
assert id(y)!= id(ob2)


def test_events():
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp