Skip to content
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

dataclasses.replace() should raise TypeError for all invalid or missed required keyword arguments #110273

Closed
serhiy-storchaka opened this issue Oct 3, 2023 · 2 comments
Assignees
Labels
type-bug An unexpected behavior, bug, or error

Comments

@serhiy-storchaka
Copy link
Member

serhiy-storchaka commented Oct 3, 2023

Bug report

When you call a function with incorrect key arguments or missing a required argument, you get a TypeError. But it is not always so with dataclasses.replace(). It raises a ValueError if a keyword argument for an InitVar field is missed or if a keyword argument for a field declared with init=False is specified.

>>> from dataclasses import *
>>> @dataclass
... class C:
...     x: int
...     y: InitVar[int]
...     z: int = field(init=False, default=100)
... 
>>> c = C(x=11, y=22)
>>> replace(c, x=1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/serhiy/py/cpython3.12/Lib/dataclasses.py", line 1570, in replace
    raise ValueError(f"InitVar {f.name!r} "
ValueError: InitVar 'y' must be specified with replace()
>>> replace(c, x=1, y=2, z=3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/serhiy/py/cpython3.12/Lib/dataclasses.py", line 1563, in replace
    raise ValueError(f'field {f.name} is declared with '
ValueError: field z is declared with init=False, it cannot be specified with replace()

It is not even consistent with constructors:

>>> C(x=1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: C.__init__() missing 1 required positional argument: 'y'
>>> C(x=1, y=2, z=3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: C.__init__() got an unexpected keyword argument 'z'

And it raises a TypeError for unexpected keyword arguments.

>>> replace(c, x=1, y=2, t=3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/serhiy/py/cpython3.12/Lib/dataclasses.py", line 1579, in replace
    return obj.__class__(**changes)
           ^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: C.__init__() got an unexpected keyword argument 't'

I think that dataclasses.replace() should raise TypeError in all these cases.

Linked PRs

@serhiy-storchaka serhiy-storchaka added the type-bug An unexpected behavior, bug, or error label Oct 3, 2023
serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Oct 3, 2023
…valid 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.
@serhiy-storchaka
Copy link
Member Author

Sorry, @ericsnowcurrently, I meant @ericvsmith.

@rhettinger
Copy link
Contributor

Let's leave these APIs alone (namedtuple and dataclasses). They are old, stable, and have never caused an issue for real users.

@ericvsmith ericvsmith self-assigned this Oct 3, 2023
serhiy-storchaka added a commit that referenced this issue Oct 4, 2023
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

3 participants