Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions Lib/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
39 changes: 34 additions & 5 deletions Lib/test/test_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -2716,15 +2716,15 @@ 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
# init=False, then the descriptor will be overwritten, anyway.
@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):
Expand All @@ -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__':
Expand Down
Original file line number Diff line number Diff line change
@@ -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.