| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
| [NonSerialized] | ||
| internal List<string> dotNetMembers; | ||
| internal Indexer indexer; | ||
| internal Hashtable richcompare; |
There was a problem hiding this comment.
Why not have it as Dictionary<int, MethodObject>, mapping directly from {Py_EQ, ...} to corresponding operator implementation?
Sorry, something went wrong.
| // otherwise fallback to checking if an IComparable interface is handled. | ||
| if (PyToCilOpMap.ContainsKey(op)) { | ||
| string CilOp = PyToCilOpMap[op]; | ||
| if (cls.richcompare.Contains(CilOp)) { |
There was a problem hiding this comment.
Generally, you'd do cls.richcompare.TryGetValue(op, out var methodObject) instead of having separate ContainsKey check.
Sorry, something went wrong.
| var methodObject = (MethodObject)cls.richcompare[CilOp]; | ||
| IntPtr args = other; | ||
| var free = false; | ||
| if (!Runtime.PyTuple_Check(other)) |
There was a problem hiding this comment.
I don't think the code inside this if should ever be skipped. How would a > (1,2) work if a was a .NET object with > operator? Because (1,2) is a tuple, they'd be passed to Invoke as two args instead of 1.
Consequently, free will alwasy be true and not needed.
Probably a good idea to add a test. I believe you'd need an operator >(SomeClass a, PyObject b) defined and to check b indeed receives the tuple (1,2).
Sorry, something went wrong.
There was a problem hiding this comment.
Indeed if b were a tuple this if would be skipped. Once removing the if, the operator method does receive b as PyObject, but I get an AccessViolationException : Attempted to read or write protected memory. so I think b is wrongly parsed? I suppose a tuple when converted into a PyObject should probably not look like this:
Sorry, something went wrong.
There was a problem hiding this comment.
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'
Sorry, something went wrong.
There was a problem hiding this comment.
This is because to work with PyObject instances in C# callbacks you must hold GIL. E.g. the method body must be inside using (Py.GIL()) { ... block. It applies to debugging too.
Sorry, something went wrong.
There was a problem hiding this comment.
Thanks, I didn't know about that. I put the operator >(Obj a, PyObject b) body in a Py.GIL block and can now see that b has a value of {(1, 2)}

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
pythonnet/src/runtime/methodbinder.cs
Line 847 in d6c0081
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?
Sorry, something went wrong.
There was a problem hiding this comment.
I don't think you need to add a test with a tuple for each comparison operator. One is enough.
Sorry, something went wrong.
There was a problem hiding this comment.
The failure on the screenshot is only happening because you have debugger attached. It tries to call ToString on some PyObject to show in Watches window, but it does not work without Py.GIL
Sorry, something went wrong.
| ["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), |
There was a problem hiding this comment.
I think this is error-prone. op_LessThan does not really implement tp_richcompare. It would be better to have a separate ComparisonOpMap, string -> string, and fix IsOperatorMethod correspondingly.
It would also remove the need to change FixupSlots below.
Sorry, something went wrong.
There was a problem hiding this comment.
Makes sense, thanks! Should that ComparisonOpMap also be merged with the other map for op_LessThan -> Py_LT?
Sorry, something went wrong.
There was a problem hiding this comment.
I added this OperatorMethod.ComparisonOpMap in the latest commit, without merging it with the ClassBase.CilToPyOpMap, although they both have the same keys so perhaps they should be together in OperatorMethod.
Sorry, something went wrong.
| foreach (var method in clrType.GetMethods(flags)) | ||
| { | ||
| if (!IsOperatorMethod(method)) | ||
| if (!IsOperatorMethod(method) || IsComparisonOp(method)) // We don't want to override ClassBase.tp_richcompare. |
There was a problem hiding this comment.
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 by ClassBase.tp_richcompare"
Sorry, something went wrong.
|
|
||
| public override int GetHashCode() | ||
| { | ||
| return 159832395 + Num.GetHashCode(); |
There was a problem hiding this comment.
unchecked(159832395 + Num.GetHashCode())
Sorry, something went wrong.
| if (true) | ||
| { |
There was a problem hiding this comment.
Please, remove unnecessary if and free boolean (always true)
Sorry, something went wrong.
| 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)) |
There was a problem hiding this comment.
NIT: !OpMethodMap .ContainsKey(method)
Sorry, something went wrong.
There was a problem hiding this comment.
Of course, thanks!
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
What does this implement/fix? Explain your changes.
Continuation of https://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 comparison operator methods when calling ClassBase.tp_richcompare, before proceeding with the usual logic (see #294) which handles C# classes that implement an IComparable 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 use op_GreaterThan https://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.
The tests pass in net472 but not on netcoreapp3.1