classification
Title: dataclasses.field does not preserve empty metadata object
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.8, Python 3.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: eric.smith Nosy List: chrahunt, eric.smith
Priority: normal Keywords: patch, patch, patch

Created on 2019-02-11 01:06 by chrahunt, last changed 2019-02-12 12:12 by eric.smith. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 11815 merged chrahunt, 2019-02-11 02:08
PR 11815 merged chrahunt, 2019-02-11 02:08
PR 11815 merged chrahunt, 2019-02-11 02:08
PR 11826 merged miss-islington, 2019-02-12 11:51
Messages (4)
msg335184 - (view) Author: Christopher Hunt (chrahunt) * Date: 2019-02-11 01:06
The metadata argument to dataclasses.field is not preserved in the resulting Field.metadata attribute if the argument is a mapping with length 0.

The docs for dataclasses.field state:

> metadata: This can be a mapping or None. None is treated as an empty dict. This value is wrapped in MappingProxyType() to make it read-only, and exposed on the Field object.

The docs for MappingProxyType() state:

> Read-only proxy of a mapping. It provides a dynamic view on the mapping’s entries, which means that when the mapping changes, the view reflects these changes.

I assumed that the mapping provided could be updated after class initialization and the changes would reflect in the field's metadata attribute. Indeed this is the case when the mapping is non-empty, but not when the mapping is initially empty.

For example:

    $ python
    Python 3.8.0a1+ (heads/master:9db56fb8fa, Feb 10 2019, 19:54:10)
    [GCC 7.3.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from dataclasses import field
    >>> d = {}
    >>> v = field(metadata=d)
    >>> d['i'] = 1
    >>> v.metadata
    mappingproxy({})
    >>> v = field(metadata=d)
    >>> v.metadata
    mappingproxy({'i': 1})
    >>> d['j'] = 2
    >>> v.metadata
    mappingproxy({'i': 1, 'j': 2})

In my case I have a LazyDict into which I was trying to save partial(callback, field). I could not have the field before it was initialized so I tried:

    d = {}
    member: T = field(metadata=d)
    d['key'] = partial(callback, field)

and it failed same as above.

As a workaround, one can set a dummy value in the mapping prior to calling dataclasses.field and then remove/overwrite it afterwards.
msg335296 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2019-02-12 11:50
New changeset b01786c8812c4cc24dd561b5941025bdd6f444c0 by Eric V. Smith (Christopher Hunt) in branch 'master':
 bpo-35960: Fix dataclasses.field throwing away empty metadata.  (GH-11815)
https://github.com/python/cpython/commit/b01786c8812c4cc24dd561b5941025bdd6f444c0
msg335297 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2019-02-12 12:11
New changeset 0a834c18cdafa5447d4436878e64aa7a4e4dc817 by Eric V. Smith (Miss Islington (bot)) in branch '3.7':
bpo-35960: Fix dataclasses.field throwing away empty metadata.  (GH-11815) (GH-11826)
https://github.com/python/cpython/commit/0a834c18cdafa5447d4436878e64aa7a4e4dc817
msg335298 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2019-02-12 12:12
Thanks for the patch!
History
Date User Action Args
2019-02-12 12:12:47eric.smithsetstatus: open -> closed
messages: + msg335298

keywords: patch, patch, patch
resolution: fixed
stage: patch review -> resolved
2019-02-12 12:11:52eric.smithsetmessages: + msg335297
2019-02-12 11:51:09miss-islingtonsetpull_requests: + pull_request11857
2019-02-12 11:50:57eric.smithsetmessages: + msg335296
2019-02-11 11:58:57eric.smithsetkeywords: patch, patch, patch
assignee: eric.smith
2019-02-11 02:58:07xtreaksetkeywords: patch, patch, patch
nosy: + eric.smith
2019-02-11 02:08:45chrahuntsetkeywords: + patch
stage: patch review
pull_requests: + pull_request11833
2019-02-11 02:08:41chrahuntsetkeywords: + patch
stage: (no value)
pull_requests: + pull_request11832
2019-02-11 02:08:38chrahuntsetkeywords: + patch
stage: (no value)
pull_requests: + pull_request11831
2019-02-11 01:06:54chrahuntcreate