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 broken if a class has any ClassVars #77977

Closed
ljodal mannequin opened this issue Jun 7, 2018 · 9 comments
Closed

dataclasses.replace broken if a class has any ClassVars #77977

ljodal mannequin opened this issue Jun 7, 2018 · 9 comments
Assignees
Labels
3.7 (EOL) end of life 3.8 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@ljodal
Copy link
Mannequin

ljodal mannequin commented Jun 7, 2018

BPO 33796
Nosy @ericvsmith, @ned-deily, @miss-islington, @ljodal
PRs
  • bpo-33796: Ignore ClassVar for replace(). #7488
  • [3.7] bpo-33796: Ignore ClassVar for dataclasses.replace(). (GH-7488) #7493
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/ericvsmith'
    closed_at = <Date 2018-06-07.20:30:17.857>
    created_at = <Date 2018-06-07.14:34:49.831>
    labels = ['3.7', '3.8', 'type-bug', 'library']
    title = 'dataclasses.replace broken if a class has any ClassVars'
    updated_at = <Date 2018-06-07.20:30:17.855>
    user = 'https://github.com/ljodal'

    bugs.python.org fields:

    activity = <Date 2018-06-07.20:30:17.855>
    actor = 'eric.smith'
    assignee = 'eric.smith'
    closed = True
    closed_date = <Date 2018-06-07.20:30:17.857>
    closer = 'eric.smith'
    components = ['Library (Lib)']
    creation = <Date 2018-06-07.14:34:49.831>
    creator = 'sigurd'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 33796
    keywords = ['patch']
    message_count = 9.0
    messages = ['318942', '318948', '318949', '318950', '318955', '318959', '318966', '318970', '318971']
    nosy_count = 4.0
    nosy_names = ['eric.smith', 'ned.deily', 'miss-islington', 'sigurd']
    pr_nums = ['7488', '7493']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue33796'
    versions = ['Python 3.7', 'Python 3.8']

    @ljodal
    Copy link
    Mannequin Author

    ljodal mannequin commented Jun 7, 2018

    The dataclasses.replace function does not work for classes that have class variables. See the console output below for an example.

    $ python
    Python 3.7.0b5+ (heads/3.7:3c417610ad, Jun  7 2018, 16:21:29) 
    [Clang 9.1.0 (clang-902.0.39.2)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import typing
    >>> import dataclasses
    >>> @dataclasses.dataclass(frozen=True)
    ... class Test:
    ...   a: int
    ...   class_var: typing.ClassVar[str] = 'foo'
    ... 
    >>> obj = Test(a=1)
    >>> dataclasses.replace(obj, a=2)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/Users/sigurdljodal/.pyenv/versions/3.7-dev/lib/python3.7/dataclasses.py", line 1179, in replace
        return obj.__class__(**changes)
    TypeError: __init__() got an unexpected keyword argument 'class_var'

    @ljodal ljodal mannequin added the 3.7 (EOL) end of life label Jun 7, 2018
    @ericvsmith ericvsmith self-assigned this Jun 7, 2018
    @ericvsmith
    Copy link
    Member

    Thanks for the report. This is the same error you get when using any non-field:

    >>> @dataclass
    ... class C:
    ...   i: int
    ...
    >>> c = C(4)
    >>> replace(c, i=3)
    C(i=3)
    >>> replace(c, j=3)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "C:\home\eric\local\python\cpython\lib\dataclasses.py", line 1179, in replace
        return obj.__class__(**changes)
    TypeError: __init__() got an unexpected keyword argument 'j'

    I think the TypeError is correct in both cases. The error message might not be the best.

    Are you suggesting that this shouldn't raise a TypeError? Since a ClassVar is not an instance variable, I don't think it makes any sense to replace() it.

    @ljodal
    Copy link
    Mannequin Author

    ljodal mannequin commented Jun 7, 2018

    I think you misunderstood the issue here. I'm not trying to replace the class variable itself, I'm changing another field on a class that has a class variable. The problem is that the replace method includes the class variable in the change dict when copying fields that are not included in the function call.

    My call is: dataclasses.replace(obj, a=2) where I try to replace a, but it fails because class_var is included in the attributes to __init__ by the replace function.

    The problem is on this line: https://github.com/python/cpython/blob/master/Lib/dataclasses.py#L1162
    The _FIELDS attribute includes the class variable as a field, and it is therefore added to the changes dict.

    @ericvsmith
    Copy link
    Member

    Ah, you're right. I did misunderstand. Thanks for correcting me. PR soon.

    @ericvsmith ericvsmith added release-blocker 3.8 only security fixes labels Jun 7, 2018
    @ericvsmith ericvsmith changed the title dataclasses.replace broken with class variables dataclasses.replace broken if a class has any ClassVars Jun 7, 2018
    @ericvsmith
    Copy link
    Member

    Ned: How do you feel about backporting this to 3.7?

    It's an unfortunate bug, but it's your call for 3.7.0 vs. 3.7.1.

    @ericvsmith
    Copy link
    Member

    New changeset e7adf2b by Eric V. Smith in branch 'master':
    bpo-33796: Ignore ClassVar for dataclasses.replace(). (GH-7488)
    e7adf2b

    @ned-deily
    Copy link
    Member

    Let's fix it now.

    @miss-islington
    Copy link
    Contributor

    New changeset 0aee3be by Miss Islington (bot) in branch '3.7':
    bpo-33796: Ignore ClassVar for dataclasses.replace(). (GH-7488)
    0aee3be

    @ericvsmith
    Copy link
    Member

    Thanks, Ned. I've backported it.

    @ericvsmith ericvsmith added the stdlib Python modules in the Lib dir label Jun 7, 2018
    @ericvsmith ericvsmith added type-bug An unexpected behavior, bug, or error and removed release-blocker labels Jun 7, 2018
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.7 (EOL) end of life 3.8 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    3 participants