]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-41747: Ensure all dataclass methods uses their parents' qualname (GH-22155)
authorBatuhan Taskaya <batuhanosmantaskaya@gmail.com>
Wed, 21 Oct 2020 13:49:22 +0000 (16:49 +0300)
committerGitHub <noreply@github.com>
Wed, 21 Oct 2020 13:49:22 +0000 (09:49 -0400)
* bpo-41747: Ensure all dataclass methods uses their parents' qualname

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Lib/dataclasses.py
Lib/test/test_dataclasses.py
Misc/NEWS.d/next/Library/2020-09-08-23-41-29.bpo-41747.M6wLKv.rst [new file with mode: 0644]

index adfb9b7240b9f9a3d5923bacce881326784a4c77..0c4b47564b0605b8820c197704f267d33683d556 100644 (file)
@@ -8,7 +8,7 @@ import builtins
 import functools
 import abc
 import _thread
-from types import GenericAlias
+from types import FunctionType, GenericAlias
 
 
 __all__ = ['dataclass',
@@ -757,12 +757,19 @@ def _get_field(cls, a_name, a_type):
 
     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):
     # Never overwrites an existing attribute.  Returns True if the
     # attribute already exists.
     if name in cls.__dict__:
         return True
+    _set_qualname(cls, value)
     setattr(cls, name, value)
     return False
 
@@ -777,7 +784,7 @@ def _hash_set_none(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)]
-    return _hash_fn(flds, globals)
+    return _set_qualname(cls, _hash_fn(flds, globals))
 
 def _hash_exception(cls, fields, globals):
     # Raise an exception.
index 7c1d9c568f4ef6feb047b2ec9df9982840ed0214..8887eb6461bc3401fc4ffebf22125f5a8b552d1f 100644 (file)
@@ -1936,6 +1936,30 @@ class TestCase(unittest.TestCase):
                     self.assertEqual(new_sample.x, another_new_sample.x)
                     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):
     def test_field_without_annotation(self):
diff --git a/Misc/NEWS.d/next/Library/2020-09-08-23-41-29.bpo-41747.M6wLKv.rst b/Misc/NEWS.d/next/Library/2020-09-08-23-41-29.bpo-41747.M6wLKv.rst
new file mode 100644 (file)
index 0000000..0869462
--- /dev/null
@@ -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.