-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
bpo-37685: Fixed __eq__, __lt__ etc implementations in some classes. #14952
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
Changes from 1 commit
4b4b2f1
4ef0e23
f90a353
ed578aa
bb41e8d
d105881
e601da2
91dab71
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
They now return NotImplemented for unsupported type of the other operand.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
import re | ||
import struct | ||
import unittest | ||
from unittest.mock import ANY | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have a dedicated class for this in I think we can move it into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not think we need yet one ANY in private tests.support if we can use public unittest.mock.ANY. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If someone tried to understand this test and looked it up in the documentation, they would be confused as to what it means. I think it is much clearer to use a dedicated class for this (particularly since we already have one in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That class was added 15 days ago. Perhaps the author was not aware of It is not very difficult to add a duplicate in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't know about that, people test implementation details all the time.
Definitely they were. The original PR used If you look at the documentation for For reasons of clarity, I think it makes sense to have dedicated singletons for each of the custom comparison behaviors, even if there are other objects that also have those properties for their own reasons. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change has been extracted into #14996. Please take a look. |
||
|
||
from array import array | ||
|
||
|
@@ -353,6 +354,18 @@ def test_comparison(self): | |
self.assertTrue(timezone(ZERO) != None) | ||
self.assertFalse(timezone(ZERO) == None) | ||
|
||
tz = timezone(ZERO) | ||
largest = support.LargestObject() | ||
self.assertTrue(tz < largest) | ||
self.assertFalse(tz > largest) | ||
self.assertTrue(tz <= largest) | ||
self.assertFalse(tz >= largest) | ||
self.assertFalse(tz == largest) | ||
self.assertTrue(tz != largest) | ||
|
||
self.assertTrue(tz == ANY) | ||
self.assertFalse(tz != ANY) | ||
|
||
def test_aware_datetime(self): | ||
# test that timezone instances can be used by datetime | ||
t = datetime(1, 1, 1) | ||
|
@@ -414,10 +427,20 @@ def test_harmless_mixed_comparison(self): | |
|
||
# Comparison to objects of unsupported types should return | ||
# NotImplemented which falls back to the right hand side's __eq__ | ||
# method. In this case, ComparesEqualClass.__eq__ always returns True. | ||
# ComparesEqualClass.__ne__ always returns False. | ||
self.assertTrue(me == ComparesEqualClass()) | ||
self.assertFalse(me != ComparesEqualClass()) | ||
# method. In this case, ANY.__eq__ always returns True. | ||
# ANY.__ne__ always returns False. | ||
self.assertTrue(me == ANY) | ||
self.assertFalse(me != ANY) | ||
|
||
# If the other class explicitly defines ordering | ||
# relative to our class, it is allowed to do so | ||
largest = support.LargestObject() | ||
self.assertTrue(me < largest) | ||
self.assertFalse(me > largest) | ||
self.assertTrue(me <= largest) | ||
self.assertFalse(me >= largest) | ||
self.assertFalse(me == largest) | ||
self.assertTrue(me != largest) | ||
|
||
def test_harmful_mixed_comparison(self): | ||
me = self.theclass(1, 1, 1) | ||
|
@@ -1582,29 +1605,6 @@ class SomeClass: | |
self.assertRaises(TypeError, lambda: our < their) | ||
self.assertRaises(TypeError, lambda: their < our) | ||
|
||
# However, if the other class explicitly defines ordering | ||
# relative to our class, it is allowed to do so | ||
|
||
class LargerThanAnything: | ||
def __lt__(self, other): | ||
return False | ||
def __le__(self, other): | ||
return isinstance(other, LargerThanAnything) | ||
def __eq__(self, other): | ||
return isinstance(other, LargerThanAnything) | ||
def __gt__(self, other): | ||
return not isinstance(other, LargerThanAnything) | ||
def __ge__(self, other): | ||
return True | ||
|
||
their = LargerThanAnything() | ||
self.assertEqual(our == their, False) | ||
self.assertEqual(their == our, False) | ||
self.assertEqual(our != their, True) | ||
self.assertEqual(their != our, True) | ||
self.assertEqual(our < their, True) | ||
self.assertEqual(their < our, False) | ||
|
||
def test_bool(self): | ||
# All dates are considered true. | ||
self.assertTrue(self.theclass.min) | ||
|
@@ -3781,8 +3781,8 @@ def test_replace(self): | |
self.assertRaises(ValueError, base.replace, microsecond=1000000) | ||
|
||
def test_mixed_compare(self): | ||
t1 = time(1, 2, 3) | ||
t2 = time(1, 2, 3) | ||
t1 = self.theclass(1, 2, 3) | ||
t2 = self.theclass(1, 2, 3) | ||
self.assertEqual(t1, t2) | ||
t2 = t2.replace(tzinfo=None) | ||
self.assertEqual(t1, t2) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3093,6 +3093,21 @@ def __fspath__(self): | |
return self.path | ||
|
||
|
||
@functools.total_ordering | ||
class LargestObject: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about making these singletons?
I don't think anyone needs to subclass these and there's no need to instantiate more than one of them. Similarly we can move There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea. |
||
def __eq__(self, other): | ||
return isinstance(other, LargestObject) | ||
def __lt__(self, other): | ||
return False | ||
|
||
@functools.total_ordering | ||
class SmallestObject: | ||
def __eq__(self, other): | ||
return isinstance(other, SmallestObject) | ||
def __gt__(self, other): | ||
return False | ||
|
||
|
||
def maybe_get_event_loop_policy(): | ||
"""Return the global event loop policy if one is set, else return None.""" | ||
return asyncio.events._event_loop_policy | ||
|
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.
Heh, we recently changed these and I guess I just assumed that
_cmperror
returnedNotImplemented
.I agree with this change, but I would argue that it precludes backporting to 3.7 (and possibly 3.8), because it is a pretty significant change to the semantics of the language.
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.
This change fixes a discrepancy between Python and C implementations. Other changes are discussable, but this change is a bugfix and it should be backported.
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.
Yes, if that is the case, I agree about backporting the subset of these changes that fixes discrepancies between the Python and C implementations. We should figure out what that subset is and break it into two PRs - one for backporting and one for master (and maybe 3.8).