[Python-checkins] bpo-33175: dataclasses should look up __set_name__ on class, not instance (GH-6305)

Miss Islington (bot) webhook-mailer at python.org
Thu Mar 29 11:32:40 EDT 2018


https://github.com/python/cpython/commit/faa6f5c74ca56d4cc0dd9db78d5c8e864b617d0c
commit: faa6f5c74ca56d4cc0dd9db78d5c8e864b617d0c
branch: 3.7
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2018-03-29T08:32:36-07:00
summary:

bpo-33175: dataclasses should look up __set_name__ on class, not instance (GH-6305)

(cherry picked from commit 521995205a2cb6b504fe0e39af22a81f785350a3)

Co-authored-by: Eric V. Smith <ericvsmith at users.noreply.github.com>

files:
A Misc/NEWS.d/next/Library/2018-03-29-04-32-25.bpo-33175._zs1yM.rst
M Lib/dataclasses.py
M Lib/test/test_dataclasses.py

diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index 8c197fe73904..bd7252c683ca 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -248,11 +248,11 @@ def __repr__(self):
     #  the default value, so the end result is a descriptor that had
     #  __set_name__ called on it at the right time.
     def __set_name__(self, owner, name):
-        func = getattr(self.default, '__set_name__', None)
+        func = getattr(type(self.default), '__set_name__', None)
         if func:
             # There is a __set_name__ method on the descriptor,
             #  call it.
-            func(owner, name)
+            func(self.default, owner, name)
 
 
 class _DataclassParams:
diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py
index 2745eaf6893b..5cd424cf5760 100755
--- a/Lib/test/test_dataclasses.py
+++ b/Lib/test/test_dataclasses.py
@@ -2705,7 +2705,7 @@ def test_set_name(self):
         # Create a descriptor.
         class D:
             def __set_name__(self, owner, name):
-                self.name = name
+                self.name = name + 'x'
             def __get__(self, instance, owner):
                 if instance is not None:
                     return 1
@@ -2716,7 +2716,7 @@ def __get__(self, instance, owner):
         @dataclass
         class C:
             c: int=D()
-        self.assertEqual(C.c.name, 'c')
+        self.assertEqual(C.c.name, 'cx')
 
         # Now test with a default value and init=False, which is the
         #  only time this is really meaningful.  If not using
@@ -2724,7 +2724,7 @@ class C:
         @dataclass
         class C:
             c: int=field(default=D(), init=False)
-        self.assertEqual(C.c.name, 'c')
+        self.assertEqual(C.c.name, 'cx')
         self.assertEqual(C().c, 1)
 
     def test_non_descriptor(self):
@@ -2733,12 +2733,41 @@ def test_non_descriptor(self):
 
         class D:
             def __set_name__(self, owner, name):
-                self.name = name
+                self.name = name + 'x'
 
         @dataclass
         class C:
             c: int=field(default=D(), init=False)
-        self.assertEqual(C.c.name, 'c')
+        self.assertEqual(C.c.name, 'cx')
+
+    def test_lookup_on_instance(self):
+        # See bpo-33175.
+        class D:
+            pass
+
+        d = D()
+        # Create an attribute on the instance, not type.
+        d.__set_name__ = Mock()
+
+        # Make sure d.__set_name__ is not called.
+        @dataclass
+        class C:
+            i: int=field(default=d, init=False)
+
+        self.assertEqual(d.__set_name__.call_count, 0)
+
+    def test_lookup_on_class(self):
+        # See bpo-33175.
+        class D:
+            pass
+        D.__set_name__ = Mock()
+
+        # Make sure D.__set_name__ is called.
+        @dataclass
+        class C:
+            i: int=field(default=D(), init=False)
+
+        self.assertEqual(D.__set_name__.call_count, 1)
 
 
 if __name__ == '__main__':
diff --git a/Misc/NEWS.d/next/Library/2018-03-29-04-32-25.bpo-33175._zs1yM.rst b/Misc/NEWS.d/next/Library/2018-03-29-04-32-25.bpo-33175._zs1yM.rst
new file mode 100644
index 000000000000..c872499cd679
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-03-29-04-32-25.bpo-33175._zs1yM.rst
@@ -0,0 +1,2 @@
+In dataclasses, Field.__set_name__ now looks up the __set_name__ special
+method on the class, not the instance, of the default value.



More information about the Python-checkins mailing list