gh-137658: Fix dataclass order method behaviors to align with the equality semantics#137497
gh-137658: Fix dataclass order method behaviors to align with the equality semantics#137497gesslerpd wants to merge 12 commits intopython:mainfrom
Conversation
|
Don't use a PR number as the issue prefix, things get confused. Please can you create an issue for this PR, and add a NEWS entry? Please can you use a A |
|
We need to be very careful that we're not changing semantics with this. We've had that problem before with comparisons (although of course now I can't find the issue). I don't think we added tests for this, because we didn't find the problem until after we'd released the code. |
|
@ericvsmith so we need set of tests that pass prior to this change? Potentially relevant "semantics" you mention may be shown in the tests for this change in #582? |
|
I'm specifically thinking of #128294. We don't want to do something like that again. I haven't looked at this change closely, I just want to be extra careful. |
|
@ericvsmith Thanks for that info, I looked into the situation a bit and it seems that since the behavior of 3.13 for I will file an issue for this @dataclasses.dataclass(order=True, slots=True)
class Data:
a: float
class Obj:
__slots__ = "a",
def __init__(self, a: float):
self.a = a
def __eq__(self, other):
if self is other:
return True
if self.__class__ is other.__class__:
return (
self.a == other.a
)
return NotImplemented
def __lt__(self, other):
if self.__class__ is other.__class__:
return self.a < other.a
return NotImplemented
def __le__(self, other):
if self.__class__ is other.__class__:
return self.a <= other.a
return NotImplemented
def __gt__(self, other):
if self.__class__ is other.__class__:
return self.a > other.a
return NotImplemented
def __ge__(self, other):
if self.__class__ is other.__class__:
return self.a >= other.a
return NotImplemented
nan = float("nan")
assert not (Data(nan) < Data(nan))
# assert not (Data(nan) <= Data(nan)), "fails on 3.13 even though {<, ==} are both False"
assert not (Data(nan) > Data(nan))
# assert not (Data(nan) >= Data(nan)), "fails on 3.13 even though {>, ==} are both False"
assert (Data(nan) != Data(nan))
assert not (Data(nan) == Data(nan))
assert not (Obj(nan) < Obj(nan))
assert not (Obj(nan) <= Obj(nan))
assert not (Obj(nan) > Obj(nan))
assert not (Obj(nan) >= Obj(nan))
assert (Obj(nan) != Obj(nan))
assert not (Obj(nan) == Obj(nan)) |
|
@AA-Turner thanks, added those details except the performance part. Rebranded the PR as a fix now that the linked issue is related to aligning behavior semantics with #104904 @ericvsmith I added tests to show the behavior and how it aligns with the 3.13+ equality methods. Some of the assertions would fail on |
There was a problem hiding this comment.
Pull request overview
This PR fixes a semantic misalignment in dataclass order methods (__lt__, __le__, __gt__, __ge__) that occurred after the __eq__ method was optimized in Python 3.13+ (PR #104904). The order methods previously used tuple comparison semantics, which caused incorrect behavior with NaN values - specifically, <= and >= would return True even when both </> and == returned False.
Key changes:
- Order methods now use field-by-field comparison with early exit, matching the
__eq__semantics - Added identity check (
self is other) optimization for all comparison operators - Improved performance by avoiding tuple allocation and enabling early termination
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| Lib/dataclasses.py | Rewrote order method generation to use field-by-field comparison instead of tuple comparison, adding self-identity optimization |
| Lib/test/test_dataclasses/init.py | Added comprehensive tests for self-comparison and NaN handling with both single and multi-field dataclasses |
| Misc/NEWS.d/next/Core_and_Builtins/2025-08-11-19-26-46.gh-issue-137658.VaIFLO.rst | Added release note documenting the fix |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@ericvsmith I added comments to the test (marked Hopefully the added tests/comments makes the changes easier to review. |
This change writes the order methods in a way more consistent with the current accepted semantics of
__eq__after it was optimized in #104904 .Ever since semantics of
__eq__changed in 3.13+ the order methods have still been using the direct tuple semantics causing a slight misalignment shown in the linked issue.Additional benefit, this can speed up ordering/comparison operations making the comparison similar to existing
__eq__performance.