Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Lib/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -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})'
Copy link
Member

@ericvsmith ericvsmith Jul 26, 2018

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__"].

Copy link
Contributor Author

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.

Copy link
Contributor Author

@VadimPushtaev VadimPushtaev Jul 26, 2018

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.

return f'{self_name}.{name}={value}'


Expand Down
47 changes: 47 additions & 0 deletions Lib/test/test_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,53 @@ class C:
first = next(iter(sig.parameters))
self.assertEqual('self', first)

def test_field_named_object(self):
@dataclass
class C:
object: str
c = C('foo')
self.assertEqual(c.object, 'foo')

def test_field_named_object_frozen(self):
@dataclass(frozen=True)
class C:
object: str
c = C('foo')
self.assertEqual(c.object, 'foo')

Copy link
Member

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.

Copy link
Member

@ericvsmith ericvsmith Jul 26, 2018

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]

def test_field_named_like_builtin(self):
# Attribute names can shadow built-in names
# since code generation is used.
# Ensure that this is not happening.
exclusions = {'None', 'True', 'False'}
builtins = sorted(
b for b in __builtins__.__dict__.keys()
if not b.startswith('__') and b not in exclusions
)
C = make_dataclass('C', [(name, str) for name in builtins])

c = C(*[name for name in builtins])

for name in builtins:
self.assertEqual(getattr(c, name), name)

def test_field_named_like_builtin_frozen(self):
# Attribute names can shadow built-in names
# since code generation is used.
# Ensure that this is not happening
# for frozen data classes.
exclusions = {'None', 'True', 'False'}
builtins = sorted(
b for b in __builtins__.__dict__.keys()
if not b.startswith('__') and b not in exclusions
)
C = make_dataclass('C', [(name, str) for name in builtins], frozen=True)

c = C(*[name for name in builtins])

for name in builtins:
self.assertEqual(getattr(c, name), name)

def test_0_field_compare(self):
# Ensure that order=False is the default.
@dataclass
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow frozen dataclasses to have a field named "object". Previously this conflicted with an internal use of "object".