[Python-checkins] bpo-34213: Allow dataclasses to work with a field named 'object'. (GH-8452)

Miss Islington (bot) webhook-mailer at python.org
Sun Aug 12 23:32:51 EDT 2018


https://github.com/python/cpython/commit/32e58fc32188753d2a3604feacdf9540fe9515fb
commit: 32e58fc32188753d2a3604feacdf9540fe9515fb
branch: 3.7
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2018-08-12T20:32:44-07:00
summary:

bpo-34213: Allow dataclasses to work with a field named 'object'. (GH-8452)

(cherry picked from commit 4d12e4dc28b7c782c368bae2e8fd3815167ed37d)

Co-authored-by: Vadim Pushtaev <pushtaev.vm at gmail.com>

files:
A Misc/NEWS.d/next/Library/2018-07-25-00-40-14.bpo-34213.O15MgP.rst
M Lib/dataclasses.py
M Lib/test/test_dataclasses.py

diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index e00a125bbd87..a43d07693ac3 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -4,6 +4,7 @@
 import types
 import inspect
 import keyword
+import builtins
 
 __all__ = ['dataclass',
            'field',
@@ -343,6 +344,11 @@ def _create_fn(name, args, body, *, globals=None, locals=None,
     # worries about external callers.
     if locals is None:
         locals = {}
+    # __builtins__ may be the "builtins" module or
+    # the value of its "__dict__",
+    # so make sure "__builtins__" is the module.
+    if globals is not None and '__builtins__' not in globals:
+        globals['__builtins__'] = builtins
     return_annotation = ''
     if return_type is not MISSING:
         locals['_return_type'] = return_type
@@ -365,7 +371,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})'
     return f'{self_name}.{name}={value}'
 
 
diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py
index c5140e8d1d9c..4c93513956a2 100755
--- a/Lib/test/test_dataclasses.py
+++ b/Lib/test/test_dataclasses.py
@@ -6,6 +6,7 @@
 
 import pickle
 import inspect
+import builtins
 import unittest
 from unittest.mock import Mock
 from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional
@@ -192,6 +193,55 @@ 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')
+
+    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_names = sorted(
+            b for b in builtins.__dict__.keys()
+            if not b.startswith('__') and b not in exclusions
+        )
+        attributes = [(name, str) for name in builtins_names]
+        C = make_dataclass('C', attributes)
+
+        c = C(*[name for name in builtins_names])
+
+        for name in builtins_names:
+            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_names = sorted(
+            b for b in builtins.__dict__.keys()
+            if not b.startswith('__') and b not in exclusions
+        )
+        attributes = [(name, str) for name in builtins_names]
+        C = make_dataclass('C', attributes, frozen=True)
+
+        c = C(*[name for name in builtins_names])
+
+        for name in builtins_names:
+            self.assertEqual(getattr(c, name), name)
+
     def test_0_field_compare(self):
         # Ensure that order=False is the default.
         @dataclass
diff --git a/Misc/NEWS.d/next/Library/2018-07-25-00-40-14.bpo-34213.O15MgP.rst b/Misc/NEWS.d/next/Library/2018-07-25-00-40-14.bpo-34213.O15MgP.rst
new file mode 100644
index 000000000000..28012af45728
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-07-25-00-40-14.bpo-34213.O15MgP.rst
@@ -0,0 +1 @@
+Allow frozen dataclasses to have a field named "object". Previously this conflicted with an internal use of "object".



More information about the Python-checkins mailing list