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

Conversation

danabr
Copy link
Contributor

@danabrdanabr commentedSep 25, 2020
edited
Loading

What does this implement/fix? Explain your changes.

This allows callers to call all methods of an interface, regardless of
whether the method was implemented implicitly or explicitly. Before this
change, you had to make an explicit cast to the interface to be able to
call the explicitly implemented method. Consider the following code:

namespacePython.Test{publicinterfaceITestInterface{voidFoo();voidBar();}publicclassTestImpl:ITestInterface{publicvoidFoo(){};publicvoidITestInterface.Bar(){};publicvoidBaz(){};publicstaticITestInterfaceGetInterface(){returnnewTestImpl();}}}

And the following Python code, demonstrating the behavior before this
change:

fromPython.TestimportTestImpl,ITestInterfacetest=TestImpl.GetInterface()test.Foo()# workstest.Bar()# AttributeError: 'TestImpl' object has no attribute 'Bar'test.Baz()# works! - baz

After this change, the behavior is as follows:

test = TestImpl.GetInterface()test.Foo() # workstest.Bar() # workstest.Baz() # AttributeError: 'ITestInterface' object has no attribute 'Baz'

This is a breaking change due to thatBaz is no longer visible in
Python.

Does this close any currently open issues?

No.

Any other comments?

See#1233 for an alternative approach, that exposes methods of explictly implemented interfaces without need to cast the object first.

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 toAUTHORS
  • Updated theCHANGELOG

@codecov-commenter
Copy link

codecov-commenter commentedSep 25, 2020
edited
Loading

Codecov Report

Merging#1240 intomaster willnot change coverage.
The diff coverage isn/a.

Impacted file tree graph

@@           Coverage Diff           @@##           master    #1240   +/-   ##=======================================  Coverage   86.25%   86.25%           =======================================  Files           1        1             Lines         291      291           =======================================  Hits          251      251             Misses         40       40
FlagCoverage Δ
#setup_linux64.94% <ø> (ø)
#setup_windows72.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 byCodecov. Last updated44f1da...c46ab75. Read thecomment docs.

@danabrdanabr mentioned this pull requestSep 25, 2020
4 tasks
Copy link
Member

@lostmsulostmsu left a comment

Choose a reason for hiding this comment

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

I am actually unfamiliar with that side of Python.NET, but if we make this change, can users then somehow cast the interface back to the class? If that is not so yet, such capability must be added (perhaps as a Python property?)

@filmor , do you know if you can unwrapInterfaceObject from Python?

@danabr
Copy link
ContributorAuthor

I also found that I have failed to handle array subscription. If a method returns aISomething[] , then the individual elements will not be wrapped in the interface when you access them (e.g.arr[0]).

@danabrdanabrforce-pushed theauto-cast-ret-val-to-interface branch from10158f4 to010c290CompareSeptember 28, 2020 11:45
@danabr
Copy link
ContributorAuthor

I've addressed the issue with returning arrays of interface objects.

What remains is the ability to "downcast"/unwrap objects, as@lostmsu mentioned. As far as I have been able to see, there is no way of doing that today. One could add a property as@lostmsu suggests - the question then becomes how to name that so that the risk of colliding with one of the interface members is minimized. Perhaps there is a subset of names that would be valid in Python, but not in .NET so one can avoid collisions completely?

@danabr
Copy link
ContributorAuthor

Another approach would be to provide a service class that can be used for downcasting/unwrapping. A simple implementation of that today is:

namespacePython.Test{publicclassCast{publicstaticobjectDowncast(objectinput)// note the return type!{returninput;}}}

Which you then can use like this:

fromPython.TestimportCastob=Cast.Downcast(iface_obj)

The reason I bring this to attention is that it highlights the incompleteness of my approach in this PR. I have tried to make sure that whenever a .NET method returns an interface, the interface is all that Python sees (proper implementation hiding). However, I have done nothing about the corresponding case for classes. If a method is declared to return a value of typeParentClass and returns aChildClass, Python-land is free to access any member ofChildClass. Having different behavior for classes and interfaces does not seem like a good idea.

Either we should apply implementation hiding everywhere (like done for interfaces in this PR), or nowhere (which is the case today). The former is more in line with what you would get in C#. The latter is perhaps what users of Python.NET wants (?), since that is the behavior that is currently there.

I'd be happy to hear your thoughts on what is the right way forward.

lostmsu reacted with thumbs up emoji

@lostmsu
Copy link
Member

@danabr completeness is not necessary. If there's a bug around C#new methods (not) hiding base class methods with the same name, let us track it separately.

The problem with no hiding is that it makes calling certain overloads potentially impossible due to name conflicts.

@filmor do you know if we have anything likeDowncast methods already available?

If not, I suggestInterfaceObject to expose__implementation__ property pointing to the unwrapped instance (with converters applied) and__raw_implementation__ without any conversions.

@danabrdanabrforce-pushed theauto-cast-ret-val-to-interface branch fromdbc89a4 tod5308fcCompareSeptember 30, 2020 15:40
@danabr
Copy link
ContributorAuthor

I've added the requested properties__implementation__ and__raw_implementation__. Alas, I failed to come up with a good way to test the latter (namely that no conversions are applied). I was hoping I could make some clever use of custom encoders to test it.

What remains now is properly documenting that the properties are available. Should that be done somewhere in this repo, or on the wiki, or on pythonnet.github.io?

I also need to make a note of the changes in the CHANGELOG, and make it clear that this is a breaking change.

lostmsu reacted with thumbs up emoji

@lostmsu
Copy link
Member

lostmsu commentedSep 30, 2020
edited
Loading

@danabr to test__raw_implementation__ you can doIComparable withInt32, and ensure, that type of the raw object is actuallySystem.Int32. Currently Python.NET conversions always turn primitive .NET int types to Python'sint.

pythonnet.github.io seems to have only basic example, so you might want to create a new wiki page for the interface objects.

Yeah, a note in CHANGELOG would be helpful.

@danabrdanabrforce-pushed theauto-cast-ret-val-to-interface branch fromd5308fc toa7d2829CompareOctober 1, 2020 04:40
@danabrdanabr marked this pull request as ready for reviewOctober 1, 2020 04:41
@danabr
Copy link
ContributorAuthor

to testraw_implementation you can do IComparable with Int32, and ensure, that type of the raw object is actually System.Int32.

Great idea! I used that to add a test. I've also updated the CHANGELOG. I'll have a look at updating the Wiki if this gets merged.

Thanks for all the help and guidance,@lostmsu!

@lostmsu
Copy link
Member

@danabr seems like a few tests are failing because theInterfaceObject has notp_iter.

@danabr
Copy link
ContributorAuthor

Argh, I wonder how come I did not notice before. I do run the tests locally. But now I can reproduce it. I'll look into it.

@danabrdanabrforce-pushed theauto-cast-ret-val-to-interface branch froma7d2829 toa604b3dCompareOctober 1, 2020 14:47
@danabr
Copy link
ContributorAuthor

It was an unexpected interaction with the work in one of my other PRs. I must have forgotten to run the tests after rebasing on master. Now all tests pass locally. Hopefully they will pass in CI as well.

Comment on lines 86 to 87
Runtime.PyObject_SetAttrString(objPtr, "__implementation__", Converter.ToPython(impl));
Runtime.PyObject_SetAttrString(objPtr, "__raw_implementation__", CLRObject.GetInstHandle(impl));
Copy link
Member

Choose a reason for hiding this comment

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

These two must be computed only on demand.

Copy link
ContributorAuthor

@danabrdanabrOct 1, 2020
edited
Loading

Choose a reason for hiding this comment

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

OK. I fixed it by implementingtp_getattro. Note that the propertiesdo not show up when you dodir on an interface object. If that is desired I should probably go the long route viatp_getset.

This allows callers to call all methods of an interface, regardless ofwhether the method was implemented implicitly or explicitly. Before thischange, you had to make an explicit cast to the interface to be able tocall the explicitly implemented method. Consider the following code:```C#namespace Python.Test {    public interface ITestInterface    {        void Foo();        void Bar();    }    public class TestImpl : ITestInterface    {        public void Foo() { };        public void ITestInterface.Bar() { };        public void Baz() { };        public static ITestInterface GetInterface()        {            return new TestImpl();        }    }}```And the following Python code, demonstrating the behavior before thischange:```pythonfrom Python.Test import TestImpl, ITestInterfacetest = TestImpl.GetInterface()test.Foo() # workstest.Bar() # AttributeError: 'TestImpl' object has no attribute 'Bar'test.Baz() # works! - baz```After this change, the behavior is as follows:```test = TestImpl.GetInterface()test.Foo() # workstest.Bar() # workstest.Baz() # AttributeError: 'ITestInterface' object has no attribute 'Baz'```This is a breaking change due to that `Baz` is no longer visible inPython.
Even when a method is declared to return an array of interfaces, the CLRmay use an array of the concrete type. Keep track of the intended typein `ArrayObject` so that elements of the array can be properly wrapped in`InterfaceObject` when accessed.
@danabrdanabrforce-pushed theauto-cast-ret-val-to-interface branch from33ed60f toc46ab75CompareOctober 1, 2020 19:42
@lostmsulostmsu merged commit50d947f intopythonnet:masterOct 1, 2020
@C-SELLERSC-SELLERS mentioned this pull requestFeb 15, 2021
4 tasks
Martin-Molinero pushed a commit to QuantConnect/pythonnet that referenced this pull requestApr 26, 2022
Reflect PR#8 MISSING CONVERTER.CS L516-528 ChangesReflect PR#14Reflect PR#15Reflect PR#19Reflect PR#25Reflect PR#34Reflect PR#35Implement List Conversion, Reflect PR#37 TestsReflect PR#38 Partial: Assembly Manager ImprovementsReflect PR#38Reflect PR#42 KeyValuePairEnumerableObjectReflect PR#10 Runtime DecimalTypeAdd TimeDelta and DateTime testsFix DecimalConversion test for float conversionConverter mod tweaksAdjust a few broken PyTestsUse _pydecimal to not interfere with Lean/decimal.pyAdd MethodBinder testsMethodBinder implicit resolutionFix bad cherry pickRefactoring precedence resolutionDeal with operator bindingFix `TestNoOverloadException` unit testFix for DomainReload testsAdd InEquality Operator TestDont PyObjects precedence in Operator methodsRevert "Merge pull requestpythonnet#1240 from danabr/auto-cast-ret-val-to-interface"This reverts commit50d947f, reversingchanges made tod44f1da.Fix Primitive Conversion to IntPost rebase fixAdd PrimitiveIntConversion testAdd test for interface derived classesAdd to Authors.mdLoad in current directory into Python PathInclude Python Lib in packageUpdate as QuantConnect.PythonNet; include console exe in packageDrop MaybeType from ClassManager for performancePackage nPython from same configurationAddress KWargs and Params; also cleanupAdd unit testsAdd pytest params unit test to testrunnerRemove testing case from TestRuntime.csFix HandleParamsArrayTest caseVersion bumpUpdate QC TestsRefactor Params FixFix assembly infoHandle breaking PyTestsCleanupOptimize Params HandlingFirst reflection improvementsAdd TypeAccessor improvements and a bunch more testsMore improvementsBump version to 2.0.2Revert ClassManager changesRemove readonlyReplace FastMember with FasterflectAdd global MemberGetter/MemberSetter cacheMinor changesMake Fasterflect work with all regression testsFix performance regressionsRevert accidental pythonnet/runtime/.gitkeep removalHandle sending a python list to an enumerable expecting method- Converter with handle sending a python List to a method expecting a  csharp enumerable. Adding unit testBump version to 2.0.3Update to net5.0- Updating all projects to target net.50- Remove domain test since it's not supported in net5.0Bump pythonNet version 2.0.4Add reproducing testApply fixCatch implicit conversion throwCleanup solutionCleanup V2Assert Error messageSmall performance improvementDrop print statement from unit testBump version to 2.0.5Bump references to new versionFix for methods with different numerical precision overloads  - Fix for methods with different numerical precision overloads. Method    precedence will give higher priority to higher resolution numerical    arguments. Adding unit testVersion bump to 2.0.6KeyValuePair conversion and performance- Improve DateTime conversion performance- Add support for KeyValuePair conversions- Minor improvements for convertions and method binderTypeManager and decimal improvementsReduce unrequired castingVersion bump to 2.0.7Apply fixesProject fix for linux systemsAdd unit testConverter cleanupMore adjustments and fixesAdd additional Py test & cleanupUse the generic match when others failAdd test for non-generic choiceAddress reviewCleanupVersion bump 2.0.8Add performance test, also add cachingMake Cache static to apply to all bindersMake adjustments from testingAdd test where overload exists with an already typed generic parameteruse `ContainsGenericParameters` to check for unassigned genericsImplement fixAdd accompanying testAdd additional testsFix minor issue with py Date -> DateTimeRefactor solution, use margs directly convert in ResolveGenericMethodVersion Bump 2.0.9Add missing exception clearing. Adding unit testVersion bump 2.0.10Handle readonly conversion to list. Adding unit testsBump version to 2.0.11
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@lostmsulostmsulostmsu approved these changes

Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

3 participants
@danabr@codecov-commenter@lostmsu

[8]ページ先頭

©2009-2025 Movatter.jp