10000 Explicit interface implementations by danabr · Pull Request #1233 · pythonnet/pythonnet · GitHub
[go: up one dir, main page]

Skip to content

Explicit interface implementations #1233

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

danabr
Copy link
Contributor
@danabr danabr commented Sep 23, 2020

What does this implement/fix? Explain your changes.

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.

Does this close any currently open issues?

No

Any other comments?

Checklist

Check all those that are applicable and complete.

  • Make sure to include one or more tests for your change
  • If an enhancement PR, please create docs and at best an example
  • Add yourself to AUTHORS
  • Updated the CHANGELOG

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.
@dnfadmin
Copy link
dnfadmin commented Sep 23, 2020

CLA assistant check
All CLA requirements met.

@codecov-commenter
Copy link
codecov-commenter commented Sep 23, 2020

Codecov Report

Merging #1233 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #1233   +/-   ##
=======================================
  Coverage   86.25%   86.25%           
=======================================
  Files           1        1           
  Lines         291      291           
=======================================
  Hits          251      251           
  Misses         40       40           
Flag Coverage Δ
#setup_linux 64.94% <ø> (ø)
#setup_windows 72.50% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.


Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 451fae6...080d0c1. Read the comment docs.

@lostmsu
Copy link
Member
lostmsu commented Sep 24, 2020

I am not convinced this should be the expected behavior. Technically explicit interface implementations are private methods, and Python.NET does not expose them by design.

Is there a scenario, where you can not cast your instance to the desired interface? Or do you just want duck typing for some reason?

@danabr
Copy link
Contributor Author
danabr commented Sep 24, 2020

Ah, that explains it. I ran into this when trying to switch out IronPython in a project. Since it worked there I got the impression that it would work with Python.NET also. I did not know that external interface implementations are private. Thanks for the explanation! I'll see if there is a way I can do the casting automatically in our module loader, so that end users don't have to worry about it.

@danabr danabr closed this Sep 24, 2020
@filmor
Copy link
Member
filmor commented Sep 24, 2020

@lostmsu Can you link some docs regarding the privacy of explicit interface implementations? I thought they were mainly used to get around name and signature conflicts, I wasn't aware of a semantic significance.

@danabr
Copy link
Contributor Author
danabr commented Sep 24, 2020

@lostmsu : There's still an issue when a method returns an interface. If the returned object implements its interface explicitly, its methods becomes invisible to Python:

    public interface ISayHello1
    {
        string SayHello();
    }

    public class InterfaceTest2 : ISayHello1
    {
        public ISayHello1 GetISayHello1()
        {
            return this;
        }

        string ISayHello1.SayHello()
        {
            return "ISayHello1";
        }
    }
 def test_interface_object_returned_through_method():
    """Test explicitly implemented methods are visible"""
    from Python.Test import InterfaceTest2

    ob = InterfaceTest2()
    hello1 = ob.GetISayHello1()
    assert hello1.SayHello() == 'ISayHello1'

This test fails with:

>       assert hello1.SayHello() == 'ISayHello1'
E       AttributeError: 'InterfaceTest2' object has no attribute 'SayHello'

The caller of GetISayHello1 shouldn't really need to cast the returned object to ISayHello1. The method is already declared to return that type! It also adds to the confusion that casting is only sometimes necessary (namely when the interface has been explicitly implemented, something the caller should not need to know about).

I'd be happy for advise on how to handle this.

@lostmsu
Copy link
Member
lostmsu commented Sep 24, 2020

@danabr this actually seems a legitimate reason for a change. When method explicitly returns an interface type, Python should see the object as an instance of that interface with all its methods available. @filmor thoughts?

@filmor
Copy link
Member
filmor commented Sep 24, 2020

As said, I'm not aware of any semantic difference between the "usual" and explicit implementation of an interface, so IMO this change makes sense.

@filmor filmor reopened this Sep 24, 2020
@danabr
Copy link
Contributor Author
danabr commented Sep 24, 2020

The C# documentation ([1]) does say that:

It's possible to implement an interface member explicitly—creating a class member that is only called through the interface, and is specific to that interface.
[...]
The class member IControl.Paint is only available through the IControl interface, and ISurface.Paint is only available through ISurface. Both method implementations are separate, and neither are available directly on the class.

So behaving differently in Python.NET might not be a good idea, although perhaps never a problem in practice. Could the issue with returning an interface from a method be solved some other way? e.g. by doing the same thing that we do when casting to the interface in Python when we convert the return value of a method to a Python value?

[1] https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation

@lostmsu
Copy link
Member
lostmsu commented Sep 24, 2020

@danabr you would need to postprocess after this line:

result = binding.info.Invoke(binding.inst, BindingFlags.Default, null, binding.args, null);

and maybe in some other places.

@danabr
Copy link
Contributor Author
danabr commented Sep 25, 2020

@lostmsu , @filmor : An approach "casting" the object to the interface type before returning it has been implemented in #1240. It comes with its own sets of things to consider. I'd be happy to hear your thoughts on that approach.

@lostmsu lostmsu closed this Oct 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants
0