From ed10ae712b49c39ef5260aa002fe79aff22b208b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 3 Oct 2023 14:07:09 +0300 Subject: [PATCH] gh-110273: dataclasses.replace() now raise TypeError for all invalid arguments dataclasses.replace() now raises TypeError instead of ValueError if specify keyword argument for a field declared with init=False or miss keyword argument for required InitVar field. --- Lib/dataclasses.py | 10 +++++----- Lib/test/test_dataclasses/__init__.py | 12 ++++++------ .../2023-10-03-14-07-05.gh-issue-110273.QaDUmS.rst | 3 +++ 3 files changed, 14 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-03-14-07-05.gh-issue-110273.QaDUmS.rst diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 84f8d68ce092a4..31dc6f8abce91a 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1567,15 +1567,15 @@ def _replace(obj, /, **changes): if not f.init: # Error if this field is specified in changes. if f.name in changes: - raise ValueError(f'field {f.name} is declared with ' - 'init=False, it cannot be specified with ' - 'replace()') + raise TypeError(f'field {f.name} is declared with ' + f'init=False, it cannot be specified with ' + f'replace()') continue if f.name not in changes: if f._field_type is _FIELD_INITVAR and f.default is MISSING: - raise ValueError(f"InitVar {f.name!r} " - 'must be specified with replace()') + raise TypeError(f"InitVar {f.name!r} " + f'must be specified with replace()') changes[f.name] = getattr(obj, f.name) # Create the new object, which calls __init__() and diff --git a/Lib/test/test_dataclasses/__init__.py b/Lib/test/test_dataclasses/__init__.py index 7c07dfc77de208..f629d7bb53959b 100644 --- a/Lib/test/test_dataclasses/__init__.py +++ b/Lib/test/test_dataclasses/__init__.py @@ -3965,9 +3965,9 @@ class C: self.assertEqual((c1.x, c1.y, c1.z, c1.t), (3, 2, 10, 100)) - with self.assertRaisesRegex(ValueError, 'init=False'): + with self.assertRaisesRegex(TypeError, 'init=False'): replace(c, x=3, z=20, t=50) - with self.assertRaisesRegex(ValueError, 'init=False'): + with self.assertRaisesRegex(TypeError, 'init=False'): replace(c, z=20) replace(c, x=3, z=20, t=50) @@ -4020,10 +4020,10 @@ class C: self.assertEqual((c1.x, c1.y), (5, 10)) # Trying to replace y is an error. - with self.assertRaisesRegex(ValueError, 'init=False'): + with self.assertRaisesRegex(TypeError, 'init=False'): replace(c, x=2, y=30) - with self.assertRaisesRegex(ValueError, 'init=False'): + with self.assertRaisesRegex(TypeError, 'init=False'): replace(c, y=30) def test_classvar(self): @@ -4056,8 +4056,8 @@ def __post_init__(self, y): c = C(1, 10) self.assertEqual(c.x, 10) - with self.assertRaisesRegex(ValueError, r"InitVar 'y' must be " - "specified with replace()"): + with self.assertRaisesRegex(TypeError, r"InitVar 'y' must be " + r"specified with replace\(\)"): replace(c, x=3) c = replace(c, x=3, y=5) self.assertEqual(c.x, 15) diff --git a/Misc/NEWS.d/next/Library/2023-10-03-14-07-05.gh-issue-110273.QaDUmS.rst b/Misc/NEWS.d/next/Library/2023-10-03-14-07-05.gh-issue-110273.QaDUmS.rst new file mode 100644 index 00000000000000..98d87da6295ee5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-03-14-07-05.gh-issue-110273.QaDUmS.rst @@ -0,0 +1,3 @@ +:func:`dataclasses.replace` now raises TypeError instead of ValueError if +specify keyword argument for a field declared with init=False or miss +keyword argument for required InitVar field.