From 40eac9365be7fe72afae4d8c200f514c57c7d3cf Mon Sep 17 00:00:00 2001 From: Daniel Abrahamsson Date: Wed, 23 Sep 2020 12:26:24 +0200 Subject: [PATCH 1/3] Expose methods/props of explicitly implemented interfaces to Python Previously, if a class implemented an interface explicitly, those methods and properties become invisible to Python. To use them, you had to make an explicit cast to the interface type. --- src/runtime/classmanager.cs | 43 +++++++++++++++++------------------- src/testing/interfacetest.cs | 25 ++++++++++++++++++++- src/tests/test_interface.py | 16 ++++++++++++++ 3 files changed, 60 insertions(+), 24 deletions(-) diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs index 0b084a49d..3110df25e 100644 --- a/src/runtime/classmanager.cs +++ b/src/runtime/classmanager.cs @@ -241,32 +241,29 @@ private static ClassInfo GetClassInfo(Type type) } } - if (type.IsInterface) + // Interface inheritance seems to be a different animal: + // more contractual, less structural. Thus, a Type that + // represents an interface that inherits from another + // interface does not return the inherited interface's + // methods in GetMembers. For example ICollection inherits + // from IEnumerable, but ICollection's GetMemebers does not + // return GetEnumerator. + // + // Not sure if this is the correct way to fix this, but it + // seems to work. Thanks to Bruce Dodson for the fix. + + Type[] inheritedInterfaces = type.GetInterfaces(); + + for (i = 0; i < inheritedInterfaces.Length; ++i) { - // Interface inheritance seems to be a different animal: - // more contractual, less structural. Thus, a Type that - // represents an interface that inherits from another - // interface does not return the inherited interface's - // methods in GetMembers. For example ICollection inherits - // from IEnumerable, but ICollection's GetMemebers does not - // return GetEnumerator. - // - // Not sure if this is the correct way to fix this, but it - // seems to work. Thanks to Bruce Dodson for the fix. - - Type[] inheritedInterfaces = type.GetInterfaces(); - - for (i = 0; i < inheritedInterfaces.Length; ++i) + Type inheritedType = inheritedInterfaces[i]; + MemberInfo[] imembers = inheritedType.GetMembers(flags); + for (n = 0; n < imembers.Length; n++) { - Type inheritedType = inheritedInterfaces[i]; - MemberInfo[] imembers = inheritedType.GetMembers(flags); - for (n = 0; n < imembers.Length; n++) + m = imembers[n]; + if (local[m.Name] == null) { - m = imembers[n]; - if (local[m.Name] == null) - { - items.Add(m); - } + items.Add(m); } } } diff --git a/src/testing/interfacetest.cs b/src/testing/interfacetest.cs index 2c24596bc..a52d30021 100644 --- a/src/testing/interfacetest.cs +++ b/src/testing/interfacetest.cs @@ -22,7 +22,16 @@ public interface ISayHello2 string SayHello(); } - public class InterfaceTest : ISayHello1, ISayHello2 + public interface IInterfaceResolutionTest + { + string TestMethod1(); + string TestMethod2(); + + string TestProperty1 { get; } + string TestProperty2 { get; } + } + + public class InterfaceTest : ISayHello1, ISayHello2, IInterfaceResolutionTest { public InterfaceTest() { @@ -43,6 +52,20 @@ string ISayHello2.SayHello() return "hello 2"; } + public string TestMethod1() + { + return "TestMethod1"; + } + + string IInterfaceResolutionTest.TestMethod2() + { + return "TestMethod2"; + } + + public string TestProperty1 => "TestProperty1"; + + string IInterfaceResolutionTest.TestProperty2 => "TestProperty2"; + public interface IPublic { } diff --git a/src/tests/test_interface.py b/src/tests/test_interface.py index 6b72c1a12..73892093b 100644 --- a/src/tests/test_interface.py +++ b/src/tests/test_interface.py @@ -67,3 +67,19 @@ def test_explicit_cast_to_interface(): assert i2.SayHello() == 'hello 2' assert hasattr(i2, 'SayHello') assert not hasattr(i2, 'HelloProperty') + + +def test_interface_method_and_property_lookup(): + """Test methods and properties in interfaces can be accessed""" + from Python.Test import InterfaceTest + + ob = InterfaceTest() + assert hasattr(ob, 'TestMethod1') + assert ob.TestMethod1() == 'TestMethod1' + assert hasattr(ob, 'TestMethod2') + assert ob.TestMethod2() == 'TestMethod2' + assert hasattr(ob, 'TestProperty1') + assert ob.TestProperty1 == 'TestProperty1' + assert hasattr(ob, 'TestProperty2') + assert ob.TestProperty2 == 'TestProperty2' + From ad28e7d14b4b30401b88ad43f99a3ed4ae66bf63 Mon Sep 17 00:00:00 2001 From: Daniel Abrahamsson Date: Wed, 23 Sep 2020 12:45:18 +0200 Subject: [PATCH 2/3] Add danabr to AUTHORS --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 23327f84c..9109c65c2 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -28,6 +28,7 @@ - Christoph Gohlke ([@cgohlke](https://github.com/cgohlke)) - Christopher Bremner ([@chrisjbremner](https://github.com/chrisjbremner)) - Christopher Pow ([@christopherpow](https://github.com/christopherpow)) +- Daniel Abrahamsson ([@danabr](https://github.com/danabr)) - Daniel Fernandez ([@fdanny](https://github.com/fdanny)) - Daniel Santana ([@dgsantana](https://github.com/dgsantana)) - Dave Hirschfeld ([@dhirschfeld](https://github.com/dhirschfeld)) From 080d0c1d13e4ade6bdf7416d302decb33b5667a3 Mon Sep 17 00:00:00 2001 From: Daniel Abrahamsson Date: Wed, 23 Sep 2020 12:46:44 +0200 Subject: [PATCH 3/3] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ba1594ca..0505ed540 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ details about the cause of the failure - Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash - Fix incorrect dereference in params array handling - Fix `object[]` parameters taking precedence when should not in overload resolution +- Fix methods and properties of explictly implemented interfaces being invisible ## [2.5.0][] - 2020-06-14