-
Notifications
You must be signed in to change notification settings - Fork 209
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
Fix comparison methods for python3 #1538
Fix comparison methods for python3 #1538
Conversation
See https://portingguide.readthedocs.io/en/latest/comparisons.html#rich-comparisons for an explaination of why it was broken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some specific comments.
Also generally, I wonder if we're better off implementing cmp and then reusing that elsewhere, so the actual comparison is only detailed once, rather than having the things being compared in lots of places.
Although apparently cmp is deprecated...
But cherry picking commits from Maybe I should go back to merging it into master, no ? |
My git-fu is probably rubbish, but I normally seem to get away with it, I've done it a few times. It just depends where people are working when some of those CI type things pop up where they get fixed first. I'd like to get the bugfix into 0.10 if possible. |
functools.total_ordering is only available from python 3.2 fix flake8 formatting
79e0b5b
to
63821b9
Compare
When you call RegisterUniverse with action set to UNREGISTER, you don't want to be forced to provide a data_callback
40816eb
to
34aa0b1
Compare
Normally I have addressed all the changes you requested. You may merge now that Travis is on a happy mood 😄 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few more comments sorry.
As stated in the doc (https://docs.python.org/2.7/reference/datamodel.html?highlight=cmp#object.__cmp__) __cmp__ is only called if rich comparison is not defined and we define it as it is the only way to support python 3
It works ! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few minor comments.
Other than that, could we add some tests for these new comparisons please. You can probably use existing objects used elsewhere for the tests.
@@ -51,18 +51,37 @@ def __hash__(self): | |||
def __repr__(self): | |||
return self.__str__() | |||
|
|||
def __cmp__(self, other): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this class should implement __hash__
because anyone could write code like this which would change the value of the class :
address = MACAddress(bytearray([0x01, 0x23, 0x45, 0x67, 0x89, 0xab])) # creates a MACAddress
mac = address.mac_address # gets its representation inside the class
print(hash(address)) # print the class' hash value, for example 1043151800894266584
mac[0] = 0xff # changes the MAC address, shouldn't be possible but it is.
print(hash(address)) # prints the class' hash value, different from the first one, for example 6608185319733256019
And would get different results at the two print statements, without violating any python (implicit) rules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, this agrees it's bad:
https://stackoverflow.com/questions/13041352/python-quickly-hash-mutable-object
Going id based would solve our issue wouldn't it? Even if someone fiddles with the value, it's still the same object? Probably not ideal, but at least safer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should still try and fix this please. It looks like a Python module just hashes the value:
https://github.com/drkjam/netaddr/blob/rel-0.7.x/netaddr/eui/__init__.py#L550-L552
But I'd agree we should do something or switch to "id".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few small comments please @nils-van-zuijlen . Also could we have some tests of all this shiny new functionality please.
And add a comment for the future, when 2.7 will not be supported anymore and we will be free to use functools.total_ordering as we want, without hitting recursion limits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor bits, sorry I dropped the ball on this completely @nils-van-zuijlen
Fancy adding some tests too?
return 1 | ||
return cmp(self.mac_address, other.mac_address) | ||
def __eq__(self, other): | ||
if other.__class__ is not self.__class__: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to do:
if other is None or other.__class__ is not self.__class__:
To protect against other being None, or does None still have class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the topic of None.__class__
, anecdotally it has __class__
:
$ python2
Python 2.7.15+ (default, Nov 27 2018, 23:36:35)
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> None.__class__
<type 'NoneType'>
$ python3
Python 3.6.8 (default, Jan 14 2019, 11:02:34)
[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> None.__class__
<class 'NoneType'>
isinstance(other, self.__class__)
would take care of this for you.
If isinstance
isn't desired, it seems like a more bulletproof approach might be to wrap these comparisons in a try
except AttributeError
block, because it is ambiguous in the documentation of __class__
which instances have __class__
. In Python 3 a value not having a __class__
attribute is an even more distant edge case.
Is there some reason this doesn't this return NotImplemented
like the other operators?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't return NotImplemented
because if classes don't match, the two objects aren't equals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want to switch to isinstance @nils-van-zuijlen or is there some reason not to do so?
return self.mac_address < other.mac_address | ||
|
||
def __eq__(self, other): | ||
# These 4 can not be replaced with functools.total_ordering because of the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be the same that they could "be replaced with functools.total_ordering when support for 2.7 is dropped because NotImplemented is not supported by it before 3.4"? Or even when we're on Python 3.4 will functools.total_ordering still not be sufficient?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, we need to implement these functions ourselves because we have a special treatment applied to None that functools.total_ordering
doesn't support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this because otherwise sorting None and a MAC you'd get stuck or something? Can we have a bit of a comment explaining why in the code please @nils-van-zuijlen ?
Sorry @nils-van-zuijlen I've been rubbish again. Any chance of some tests? Apart from that it essentially looks good to go thanks! |
I have tests for most of these written and should be able to submit a PR in a few days, though I'm not sure if I can add to this or need to open a new one. @peternewman is there a need to support python older than 2.7? Quite a bit is simpler if we can assume 2.7 and recent 3. @nils-van-zuijlen 2.7 functools appears to have backports to support everything needed here A couple more fixes and bugs found with the tests... |
@nils-van-zuijlen or @brucelowekamp I think this can be closed now looking at the comparison: Would you agree this has all gone in via #1605? |
Yes it can be closed, all meaningful commits are in #1605 |
Perfect and thanks again for your efforts @nils-van-zuijlen ! |
Rebased to merge in 0.10, original PR : #1537