gh-110273: dataclasses.replace() now raise TypeError for all invalid arguments (GH-110274)

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.
This commit is contained in:
Serhiy Storchaka 2023-10-04 09:20:14 +03:00 committed by GitHub
parent bfe7e72522
commit 5b9a3fd6a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 14 additions and 11 deletions

View file

@ -1567,15 +1567,15 @@ def _replace(obj, /, **changes):
if not f.init: if not f.init:
# Error if this field is specified in changes. # Error if this field is specified in changes.
if f.name in changes: if f.name in changes:
raise ValueError(f'field {f.name} is declared with ' raise TypeError(f'field {f.name} is declared with '
'init=False, it cannot be specified with ' f'init=False, it cannot be specified with '
'replace()') f'replace()')
continue continue
if f.name not in changes: if f.name not in changes:
if f._field_type is _FIELD_INITVAR and f.default is MISSING: if f._field_type is _FIELD_INITVAR and f.default is MISSING:
raise ValueError(f"InitVar {f.name!r} " raise TypeError(f"InitVar {f.name!r} "
'must be specified with replace()') f'must be specified with replace()')
changes[f.name] = getattr(obj, f.name) changes[f.name] = getattr(obj, f.name)
# Create the new object, which calls __init__() and # Create the new object, which calls __init__() and

View file

@ -3965,9 +3965,9 @@ class C:
self.assertEqual((c1.x, c1.y, c1.z, c1.t), (3, 2, 10, 100)) 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) 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, z=20)
replace(c, x=3, z=20, t=50) replace(c, x=3, z=20, t=50)
@ -4020,10 +4020,10 @@ class C:
self.assertEqual((c1.x, c1.y), (5, 10)) self.assertEqual((c1.x, c1.y), (5, 10))
# Trying to replace y is an error. # 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) replace(c, x=2, y=30)
with self.assertRaisesRegex(ValueError, 'init=False'): with self.assertRaisesRegex(TypeError, 'init=False'):
replace(c, y=30) replace(c, y=30)
def test_classvar(self): def test_classvar(self):
@ -4056,8 +4056,8 @@ def __post_init__(self, y):
c = C(1, 10) c = C(1, 10)
self.assertEqual(c.x, 10) self.assertEqual(c.x, 10)
with self.assertRaisesRegex(ValueError, r"InitVar 'y' must be " with self.assertRaisesRegex(TypeError, r"InitVar 'y' must be "
"specified with replace()"): r"specified with replace\(\)"):
replace(c, x=3) replace(c, x=3)
c = replace(c, x=3, y=5) c = replace(c, x=3, y=5)
self.assertEqual(c.x, 15) self.assertEqual(c.x, 15)

View file

@ -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.