-
-
Notifications
You must be signed in to change notification settings - Fork 32k
bpo-34213: frozen dataclass with "object" attr bug #8452
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR.
Lib/dataclasses.py
Outdated
# If we're a frozen class, then assign to our fields in __init__ | ||
# via object.__setattr__. Otherwise, just use a simple | ||
# assignment. | ||
# | ||
# self_name is what "self" is called in this function: don't | ||
# hard-code "self", since that might be a field name. | ||
if frozen: | ||
return f'object.__setattr__({self_name},{name!r},{value})' | ||
return f'{object_expression}.__setattr__({self_name},{name!r},{value})' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the only change you need to make in this file is to use __builtins__.object
instead of object
here. Since identifiers that start with double underscores are reserved for Python, we don't need to support a field named __builtins__
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's indeed a right thing do, I completely forgot about __builtins__
.
However, surprisingly (at least for me) you should do __builtins__['object']__
, not __builtins__.object
since __builtins__
is a dictionary inside exec
, not a module: “If the globals dictionary does not contain a value for the key __builtins__
, a reference to the dictionary of the built-in module builtins is inserted under that key.” — https://docs.python.org/3/library/functions.html#exec
@@ -0,0 +1,2 @@ | |||
Frozen dataclasses didn't work properly with an attribute called "object". | |||
Now they do. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change to: Allow frozen dataclasses to have a field named "object". Previously this conflicted with an internal use of "object".
Lib/test/test_dataclasses.py
Outdated
object: str | ||
c=C('foo') | ||
self.assertEqual(c.object, 'foo') | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to these tests, please add a test for field names that are all identifiers in __builtins__
which don't start with double underscores, except for None
, True
, and False
. You can use make_dataclass
to dynamically generate a class with those members. I think this will work to generate the field names: [n for n in __builtins__.__dict__.keys() if not n.startswith('__') and not n in ('None', 'False', 'True')]
. You'll want to do this for both a normal dataclass and a frozen one. Also, you'll want to test order=True
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like:
names = [n for n in __builtins__.__dict__.keys() if not n.startswith('__') and not n in ('None', 'False', 'True')]
C = make_dataclass('C', names)
C(*([0] * len(names)))
Which works in 3.7.0, and
names = [n for n in __builtins__.__dict__.keys() if not n.startswith('__') and not n in ('None', 'False', 'True')]
C = make_dataclass('C', names, frozen=True)
C(*([0] * len(names)))
Which fails in 3.7.0.
[Edit: fix frozen example]
Lib/test/test_dataclasses.py
Outdated
@dataclass(frozen=True) | ||
class C: | ||
object: str | ||
c=C('foo') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add spaces around =
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only reason I omitted spaces is they are already omitted in test_field_named_self
which I used as a guideline. Should I fix that c=C
as well? In this PR or in a separate one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd just fix this particular case here, and another PR for all other cases of the same problem.
Lib/test/test_dataclasses.py
Outdated
@dataclass | ||
class C: | ||
object: str | ||
c=C('foo') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add spaces around =
.
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
I have made the requested changes; please review again. |
Thanks for making the requested changes! @ericvsmith: please review the changes made to this pull request. |
Lib/dataclasses.py
Outdated
@@ -365,7 +365,7 @@ def _field_assign(frozen, name, value, self_name): | |||
# self_name is what "self" is called in this function: don't | |||
# hard-code "self", since that might be a field name. | |||
if frozen: | |||
return f'object.__setattr__({self_name},{name!r},{value})' | |||
return f'__builtins__["object"].__setattr__({self_name},{name!r},{value})' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per your other comment: I did not know this was how you had to access __builtins__
inside exec
. Although we could get around this by passing adding {"__builtins__": __builtins__}
to the globals passed in to exec
. I'll have to think about that: there might be other reasons to set globals["__builtins__"]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's remarkable, isn't that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, it is dict
only if you provide the globals
argument:
>>> exec('print(type(__builtins__))')
<class 'module'>
>>> exec('print(type(__builtins__))', None, None)
<class 'module'>
>>> exec('print(type(__builtins__))', {}, None)
<class 'dict'>
That means that the right thing to do is to include __builtins__
in the globals
dict or always use {}
instead of None
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for making these changes. When I have some free time, I'll run some tests and think about setting __builtins__
in globals in the exec
call. If you haven't heard anything in a week or two, please ping me.
Looks like is misunderstood the From what I got, For example, if I run That is more or less described in the documentation — https://docs.python.org/3/library/builtins.html Next, the default for any code in
This behavior is described here — https://docs.python.org/3/library/functions.html#exec Having all the in mind, I concluded that we should use |
Hello, and thanks for your contribution! I'm a bot set up to make sure that the project can legally accept your contribution by verifying you have signed the PSF contributor agreement (CLA). Unfortunately we couldn't find an account corresponding to your GitHub username on bugs.python.org (b.p.o) to verify you have signed the CLA (this might be simply due to a missing "GitHub Name" entry in your b.p.o account settings). This is necessary for legal reasons before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue. When your account is ready, please add a comment in this pull request Thanks again for your contribution, we look forward to reviewing it! |
|
Hello, and thanks for your contribution! I'm a bot set up to make sure that the project can legally accept your contribution by verifying you have signed the PSF contributor agreement (CLA). Unfortunately we couldn't find an account corresponding to your GitHub username on bugs.python.org (b.p.o) to verify you have signed the CLA (this might be simply due to a missing "GitHub Name" entry in your b.p.o account settings). This is necessary for legal reasons before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue. When your account is ready, please add a comment in this pull request Thanks again for your contribution, we look forward to reviewing it! |
Fixed. |
@ericvsmith, you asked to ping you :). |
Thanks @VadimPushtaev for the PR, and @ericvsmith for merging it 🌮🎉.. I'm working now to backport this PR to: 3.7. |
GH-8745 is a backport of this pull request to the 3.7 branch. |
…thonGH-8452) (cherry picked from commit 4d12e4d) Co-authored-by: Vadim Pushtaev <[email protected]>
Thanks @VadimPushtaev for the PR, and @ericvsmith for merging it 🌮🎉.. I'm working now to backport this PR to: 3.7. |
GH-8746 is a backport of this pull request to the 3.7 branch. |
…thonGH-8452) (cherry picked from commit 4d12e4d) Co-authored-by: Vadim Pushtaev <[email protected]>
…-8452) (cherry picked from commit 4d12e4d) Co-authored-by: Vadim Pushtaev <[email protected]>
For frozen dataclasses, attributes are set via
object.__setattr__(name, value)
in__init__
. That won't work ifobject
is a function argument which is entirely possible ifobject
is an attribute name.The same problem is already resolved for
self
which is replaced by__dataclass_self__
ifself
is an attribute name.My solution is to store a reference to
object
inself
and useself.__dataclass_object__
(or even__dataclass_self__.__dataclass_object__
) ifobject
doesn't work.https://bugs.python.org/issue34213