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

Support comparison operators#1347

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

christabella
Copy link
Contributor

@christabellachristabella commentedJan 6, 2021
edited
Loading

What does this implement/fix? Explain your changes.

Continuation ofhttps://github.com/pythonnet/pythonnet/pull/1324/files but specifically for comparison operators e.g. >, >=, <, == etc. based on a discussion with@tminka --- in a nutshell, we want to check each C# class for any comparisonoperator methods when callingClassBase.tp_richcompare, before proceeding with the usual logic (see#294) which handles C# classes that implement anIComparable interface.

Would appreciate a review from@lostmsu who helped with the previous PR that this builds on. Thanks!

Does this close any currently open issues?

Closes#1312
More concrete examples can also be found in other Infer.NET tutorials e.g. having to useop_GreaterThanhttps://github.com/dotnet/infer/blob/67b4f80d97018460bcb817f76ec874d0f33f1651/test/TestPython/test_tutorials.py#L31

Any other comments?

Some remaining tasks could be:

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

The tests pass in net472 but not on netcoreapp3.1

@christabellachristabella marked this pull request as draftJanuary 6, 2021 15:55
@christabellachristabellaforce-pushed thefeat/comparison-operators branch from6196140 to3c4ea26CompareJanuary 7, 2021 07:13
@christabellachristabella marked this pull request as ready for reviewJanuary 7, 2021 07:45
@christabellachristabella mentioned this pull requestJan 7, 2021
4 tasks
@@ -21,6 +21,7 @@ internal class ClassBase : ManagedType
[NonSerialized]
internal List<string> dotNetMembers;
internal Indexer indexer;
internal Hashtable richcompare;
Copy link
Member

Choose a reason for hiding this comment

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

Why not have it asDictionary<int, MethodObject>, mapping directly from{Py_EQ, ...} to corresponding operator implementation?

christabella reacted with thumbs up emoji
// otherwise fallback to checking if an IComparable interface is handled.
if (PyToCilOpMap.ContainsKey(op)) {
string CilOp = PyToCilOpMap[op];
if (cls.richcompare.Contains(CilOp)) {
Copy link
Member

Choose a reason for hiding this comment

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

Generally, you'd docls.richcompare.TryGetValue(op, out var methodObject) instead of having separateContainsKey check.

christabella reacted with thumbs up emoji
var methodObject = (MethodObject)cls.richcompare[CilOp];
IntPtr args = other;
var free = false;
if (!Runtime.PyTuple_Check(other))
Copy link
Member

@lostmsulostmsuJan 7, 2021
edited
Loading

Choose a reason for hiding this comment

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

I don't think the code inside thisif should ever be skipped. How woulda > (1,2) work ifa was a .NET object with> operator? Because(1,2) is a tuple, they'd be passed toInvoke as two args instead of 1.

Consequently,free will alwasy betrue and not needed.

Probably a good idea to add a test. I believe you'd need anoperator >(SomeClass a, PyObject b) defined and to checkb indeed receives the tuple(1,2).

tminka reacted with thumbs up emoji
Copy link
ContributorAuthor

@christabellachristabellaJan 8, 2021
edited
Loading

Choose a reason for hiding this comment

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

Indeed ifb were a tuple thisif would be skipped. Once removing theif, the operator method does receiveb asPyObject, but I get anAccessViolationException : Attempted to read or write protected memory. so I thinkb is wrongly parsed? I suppose a tuple when converted into a PyObject should probably not look like this:

image

Copy link
ContributorAuthor

@christabellachristabellaJan 8, 2021
edited
Loading

Choose a reason for hiding this comment

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

I think that error message might have been a flaky bug, because now the variable states are the same but the error is:

Message:     Python.Runtime.PythonException : TypeError : '>=' not supported between instances of 'int' and 'tuple'

Copy link
Member

@lostmsulostmsuJan 9, 2021
edited
Loading

Choose a reason for hiding this comment

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

This is because to work withPyObject instances in C# callbacks you must hold GIL. E.g. the method body must be insideusing (Py.GIL()) { ... block. It applies to debugging too.

christabella reacted with thumbs up emoji
Copy link
ContributorAuthor

@christabellachristabellaJan 11, 2021
edited
Loading

Choose a reason for hiding this comment

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

Thanks, I didn't know about that. I put theoperator >(Obj a, PyObject b) body in aPy.GIL block and can now see thatb has a value of{(1, 2)}
image

I added 2/6 of the tuple comparison tests but it's done in a bit of a strange way. In the test I'm assuming that the PyObject is a tuple; is that okay?


(Below is resolved)

However, the same error message persists, and when I try to debug

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

I get this errorimage

Maybe I'm missing something simple again, should the GIL block should also be wrapping that statement? But that would make the change bigger than I thought. Are tuples commonly used enough for comparison operators, or can I put the tuple test in a different PR?

Copy link
Member

Choose a reason for hiding this comment

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

I don't think you need to add a test with a tuple for each comparison operator. One is enough.

christabella reacted with thumbs up emoji
Copy link
Member

@lostmsulostmsuJan 11, 2021
edited
Loading

Choose a reason for hiding this comment

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

The failure on the screenshot is only happening because you have debugger attached. It tries to callToString on somePyObject to show in Watches window, but it does not work withoutPy.GIL

christabella reacted with thumbs up emoji
Comment on lines 53 to 58
["op_Equality"] = new SlotDefinition("__eq__", TypeOffset.tp_richcompare),
["op_Inequality"] = new SlotDefinition("__ne__", TypeOffset.tp_richcompare),
["op_LessThanOrEqual"] = new SlotDefinition("__le__", TypeOffset.tp_richcompare),
["op_GreaterThanOrEqual"] = new SlotDefinition("__ge__", TypeOffset.tp_richcompare),
["op_LessThan"] = new SlotDefinition("__lt__", TypeOffset.tp_richcompare),
["op_GreaterThan"] = new SlotDefinition("__gt__", TypeOffset.tp_richcompare),
Copy link
Member

@lostmsulostmsuJan 7, 2021
edited
Loading

Choose a reason for hiding this comment

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

I think this is error-prone.op_LessThan does not really implementtp_richcompare. It would be better to have a separateComparisonOpMap,string -> string, and fixIsOperatorMethod correspondingly.

It would also remove the need to changeFixupSlots below.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Makes sense, thanks! Should thatComparisonOpMap also be merged with the other map forop_LessThan ->Py_LT?

Copy link
ContributorAuthor

@christabellachristabellaJan 8, 2021
edited
Loading

Choose a reason for hiding this comment

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

I added thisOperatorMethod.ComparisonOpMap in the latest commit, without merging it with theClassBase.CilToPyOpMap, although they both have the same keys so perhaps they should be together inOperatorMethod.

@@ -86,7 +99,7 @@ public static void FixupSlots(IntPtr pyType, Type clrType)
Debug.Assert(_opType != null);
foreach (var method in clrType.GetMethods(flags))
{
if (!IsOperatorMethod(method))
if (!IsOperatorMethod(method) || IsComparisonOp(method)) // We don't want to override ClassBase.tp_richcompare.
Copy link
Member

Choose a reason for hiding this comment

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

Direct comment to the reason why we don't want to override, otherwise it does not explain anything and simply says what code in this method does (which is generally not very useful).

Something like "comparison operators are handled byClassBase.tp_richcompare"

christabella reacted with thumbs up emoji
@@ -25,6 +25,17 @@ public class OperableObject
{
public int Num { get; set; }

public override int GetHashCode()
{
return 159832395 + Num.GetHashCode();
Copy link
Contributor

Choose a reason for hiding this comment

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

unchecked(159832395 + Num.GetHashCode())

Comment on lines 94 to 95
if (true)
{
Copy link
Member

Choose a reason for hiding this comment

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

Please, remove unnecessaryif andfree boolean (alwaystrue)

christabella reacted with thumbs up emoji
if (!IsOperatorMethod(method))
// We don't want to override slots for either non-operators or
// comparison operators, which are handled by ClassBase.tp_richcompare.
if (!IsOperatorMethod(method) || IsComparisonOp(method))
Copy link
Member

Choose a reason for hiding this comment

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

NIT:!OpMethodMap .ContainsKey(method)

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Of course, thanks!

@lostmsulostmsu merged commite44aa46 intopythonnet:masterJan 12, 2021
@christabellachristabella deleted the feat/comparison-operators branchJanuary 13, 2021 08:15
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@tminkatminkatminka left review comments

@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.

operator overloading
3 participants
@christabella@lostmsu@tminka

[8]ページ先頭

©2009-2025 Movatter.jp