bpo-33175: dataclasses should look up __set_name__ on class, not instance (GH-6305)
This commit is contained in:
parent
b9e7fe38a0
commit
521995205a
|
@ -248,11 +248,11 @@ class Field:
|
||||||
# the default value, so the end result is a descriptor that had
|
# the default value, so the end result is a descriptor that had
|
||||||
# __set_name__ called on it at the right time.
|
# __set_name__ called on it at the right time.
|
||||||
def __set_name__(self, owner, name):
|
def __set_name__(self, owner, name):
|
||||||
func = getattr(self.default, '__set_name__', None)
|
func = getattr(type(self.default), '__set_name__', None)
|
||||||
if func:
|
if func:
|
||||||
# There is a __set_name__ method on the descriptor,
|
# There is a __set_name__ method on the descriptor,
|
||||||
# call it.
|
# call it.
|
||||||
func(owner, name)
|
func(self.default, owner, name)
|
||||||
|
|
||||||
|
|
||||||
class _DataclassParams:
|
class _DataclassParams:
|
||||||
|
|
|
@ -2705,7 +2705,7 @@ class TestDescriptors(unittest.TestCase):
|
||||||
# Create a descriptor.
|
# Create a descriptor.
|
||||||
class D:
|
class D:
|
||||||
def __set_name__(self, owner, name):
|
def __set_name__(self, owner, name):
|
||||||
self.name = name
|
self.name = name + 'x'
|
||||||
def __get__(self, instance, owner):
|
def __get__(self, instance, owner):
|
||||||
if instance is not None:
|
if instance is not None:
|
||||||
return 1
|
return 1
|
||||||
|
@ -2716,7 +2716,7 @@ class TestDescriptors(unittest.TestCase):
|
||||||
@dataclass
|
@dataclass
|
||||||
class C:
|
class C:
|
||||||
c: int=D()
|
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
|
# Now test with a default value and init=False, which is the
|
||||||
# only time this is really meaningful. If not using
|
# only time this is really meaningful. If not using
|
||||||
|
@ -2724,7 +2724,7 @@ class TestDescriptors(unittest.TestCase):
|
||||||
@dataclass
|
@dataclass
|
||||||
class C:
|
class C:
|
||||||
c: int=field(default=D(), init=False)
|
c: int=field(default=D(), init=False)
|
||||||
self.assertEqual(C.c.name, 'c')
|
self.assertEqual(C.c.name, 'cx')
|
||||||
self.assertEqual(C().c, 1)
|
self.assertEqual(C().c, 1)
|
||||||
|
|
||||||
def test_non_descriptor(self):
|
def test_non_descriptor(self):
|
||||||
|
@ -2733,12 +2733,41 @@ class TestDescriptors(unittest.TestCase):
|
||||||
|
|
||||||
class D:
|
class D:
|
||||||
def __set_name__(self, owner, name):
|
def __set_name__(self, owner, name):
|
||||||
self.name = name
|
self.name = name + 'x'
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class C:
|
class C:
|
||||||
c: int=field(default=D(), init=False)
|
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__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue