From 7a89f4d1c7eff6e8196fab8dc3f1074d8ac59444 Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 14 Mar 2024 17:31:04 +0800 Subject: [PATCH 1/5] gh-116647: Fix recursive child in dataclasses --- Lib/dataclasses.py | 2 +- Lib/test/test_dataclasses/__init__.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 45ce5a98b51ae0..f7fb540d363bbd 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1073,7 +1073,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen, # Create __eq__ method. There's no need for a __ne__ method, # since python will call __eq__ and negate it. cmp_fields = (field for field in field_list if field.compare) - terms = [f'self.{field.name}==other.{field.name}' for field in cmp_fields] + terms = [f'(self.{field.name},)==(other.{field.name},)' for field in cmp_fields] field_comparisons = ' and '.join(terms) or 'True' body = [f'if other.__class__ is self.__class__:', f' return {field_comparisons}', diff --git a/Lib/test/test_dataclasses/__init__.py b/Lib/test/test_dataclasses/__init__.py index ede74b0dd15ccf..cc667742f30928 100644 --- a/Lib/test/test_dataclasses/__init__.py +++ b/Lib/test/test_dataclasses/__init__.py @@ -2471,6 +2471,15 @@ def __repr__(self): class TestEq(unittest.TestCase): + def test_recursive_eq(self): + # Test a class with recursive child + @dataclass + class C: + recursive: object = ... + c = C() + c.recursive = c + self.assertEqual(c, c) + def test_no_eq(self): # Test a class with no __eq__ and eq=False. @dataclass(eq=False) From 8d3e13132d330b2ce0408687cc885b2deea93ae0 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 09:38:52 +0000 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2024-03-14-09-38-51.gh-issue-116647.h0d_zj.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2024-03-14-09-38-51.gh-issue-116647.h0d_zj.rst diff --git a/Misc/NEWS.d/next/Library/2024-03-14-09-38-51.gh-issue-116647.h0d_zj.rst b/Misc/NEWS.d/next/Library/2024-03-14-09-38-51.gh-issue-116647.h0d_zj.rst new file mode 100644 index 00000000000000..081f36bff91633 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-03-14-09-38-51.gh-issue-116647.h0d_zj.rst @@ -0,0 +1 @@ +Fix recursive child in dataclasses From c7f897f2ffe3a9632c22772a3dc98699c6c7eebb Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 15 Mar 2024 10:52:43 +0800 Subject: [PATCH 3/5] gh-116647: reverting the code --- Lib/dataclasses.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index f7fb540d363bbd..07993d95deb622 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1072,17 +1072,13 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen, if eq: # Create __eq__ method. There's no need for a __ne__ method, # since python will call __eq__ and negate it. - cmp_fields = (field for field in field_list if field.compare) - terms = [f'(self.{field.name},)==(other.{field.name},)' for field in cmp_fields] - field_comparisons = ' and '.join(terms) or 'True' - body = [f'if other.__class__ is self.__class__:', - f' return {field_comparisons}', - f'return NotImplemented'] - func = _create_fn('__eq__', - ('self', 'other'), - body, - globals=globals) - _set_new_attribute(cls, '__eq__', func) + flds = [f for f in field_list if f.compare] + self_tuple = _tuple_str('self', flds) + other_tuple = _tuple_str('other', flds) + _set_new_attribute(cls, '__eq__', + _cmp_fn('__eq__', '==', + self_tuple, other_tuple, + globals=globals)) if order: # Create and set the ordering methods. From 30082551f7918ebbdf26c49d66bed0af57f1402f Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 18 Mar 2024 13:49:50 +0800 Subject: [PATCH 4/5] gh-116647: fix improve efficiency --- Lib/dataclasses.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 07993d95deb622..3f03e327c069b0 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1072,13 +1072,19 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen, if eq: # Create __eq__ method. There's no need for a __ne__ method, # since python will call __eq__ and negate it. - flds = [f for f in field_list if f.compare] - self_tuple = _tuple_str('self', flds) - other_tuple = _tuple_str('other', flds) - _set_new_attribute(cls, '__eq__', - _cmp_fn('__eq__', '==', - self_tuple, other_tuple, - globals=globals)) + cmp_fields = (field for field in field_list if field.compare) + terms = [f'self.{field.name}==other.{field.name}' for field in cmp_fields] + field_comparisons = ' and '.join(terms) or 'True' + body = [f'if id(self) == id(other):', + f' return True', + f'if other.__class__ is self.__class__:', + f' return {field_comparisons}', + f'return NotImplemented'] + func = _create_fn('__eq__', + ('self', 'other'), + body, + globals=globals) + _set_new_attribute(cls, '__eq__', func) if order: # Create and set the ordering methods. From 5bdd01035508880050d5c26a5f5f3c61e7cb3ef9 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 19 Mar 2024 09:40:46 +0800 Subject: [PATCH 5/5] gh-116647: fix improve code style --- Lib/dataclasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 3f03e327c069b0..5be021a65d73f0 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1075,7 +1075,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen, cmp_fields = (field for field in field_list if field.compare) terms = [f'self.{field.name}==other.{field.name}' for field in cmp_fields] field_comparisons = ' and '.join(terms) or 'True' - body = [f'if id(self) == id(other):', + body = [f'if self is other:', f' return True', f'if other.__class__ is self.__class__:', f' return {field_comparisons}',