bpo-41747: Ensure all dataclass methods uses their parents' qualname (GH-22155)

* bpo-41747: Ensure all dataclass methods uses their parents' qualname

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
Batuhan Taskaya 2020-10-21 16:49:22 +03:00 committed by GitHub
parent 9a1ad2cf02
commit c7437e2c02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 36 additions and 2 deletions

View File

@ -8,7 +8,7 @@ import builtins
import functools import functools
import abc import abc
import _thread import _thread
from types import GenericAlias from types import FunctionType, GenericAlias
__all__ = ['dataclass', __all__ = ['dataclass',
@ -757,12 +757,19 @@ def _get_field(cls, a_name, a_type):
return f return f
def _set_qualname(cls, value):
# Ensure that the functions returned from _create_fn uses the proper
# __qualname__ (the class they belong to).
if isinstance(value, FunctionType):
value.__qualname__ = f"{cls.__qualname__}.{value.__name__}"
return value
def _set_new_attribute(cls, name, value): def _set_new_attribute(cls, name, value):
# Never overwrites an existing attribute. Returns True if the # Never overwrites an existing attribute. Returns True if the
# attribute already exists. # attribute already exists.
if name in cls.__dict__: if name in cls.__dict__:
return True return True
_set_qualname(cls, value)
setattr(cls, name, value) setattr(cls, name, value)
return False return False
@ -777,7 +784,7 @@ def _hash_set_none(cls, fields, globals):
def _hash_add(cls, fields, globals): def _hash_add(cls, fields, globals):
flds = [f for f in fields if (f.compare if f.hash is None else f.hash)] flds = [f for f in fields if (f.compare if f.hash is None else f.hash)]
return _hash_fn(flds, globals) return _set_qualname(cls, _hash_fn(flds, globals))
def _hash_exception(cls, fields, globals): def _hash_exception(cls, fields, globals):
# Raise an exception. # Raise an exception.

View File

@ -1936,6 +1936,30 @@ class TestCase(unittest.TestCase):
self.assertEqual(new_sample.x, another_new_sample.x) self.assertEqual(new_sample.x, another_new_sample.x)
self.assertEqual(sample.y, another_new_sample.y) self.assertEqual(sample.y, another_new_sample.y)
def test_dataclasses_qualnames(self):
@dataclass(order=True, unsafe_hash=True, frozen=True)
class A:
x: int
y: int
self.assertEqual(A.__init__.__name__, "__init__")
for function in (
'__eq__',
'__lt__',
'__le__',
'__gt__',
'__ge__',
'__hash__',
'__init__',
'__repr__',
'__setattr__',
'__delattr__',
):
self.assertEqual(getattr(A, function).__qualname__, f"TestCase.test_dataclasses_qualnames.<locals>.A.{function}")
with self.assertRaisesRegex(TypeError, r"A\.__init__\(\) missing"):
A()
class TestFieldNoAnnotation(unittest.TestCase): class TestFieldNoAnnotation(unittest.TestCase):
def test_field_without_annotation(self): def test_field_without_annotation(self):

View File

@ -0,0 +1,3 @@
Ensure all methods that generated from :func:`dataclasses.dataclass`
objects now have the proper ``__qualname__`` attribute referring to
the class they belong to. Patch by Batuhan Taskaya.