https://github.com/python/cpython/commit/116caab464af216d30f0b938703ab65fd2cf5edf
commit: 116caab464af216d30f0b938703ab65fd2cf5edf
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-13T12:53:48+03:00
summary:

gh-78526: Add tests for PEP 468 and PEP 520 (GH-155387)

Test that the order of keyword arguments and the order of class attribute
definitions are preserved.

files:
M Lib/test/test_call.py
M Lib/test/test_class.py

diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py
index 1e42356be21ddd..76f1c351e15908 100644
--- a/Lib/test/test_call.py
+++ b/Lib/test/test_call.py
@@ -47,6 +47,54 @@ def fn(**kw):
         self.assertIsInstance(res, dict)
         self.assertEqual(list(res.items()), expected)
 
+    def test_kwargs_order_preserved(self):
+        # PEP 468: Preserving Keyword Argument Order
+        def fn(**kw):
+            return list(kw)
+
+        self.assertEqual(fn(b=1, a=2, c=3), ['b', 'a', 'c'])
+        self.assertEqual(fn(c=3, a=2, b=1), ['c', 'a', 'b'])
+        # Unpacked mappings are merged in place, keeping their own order.
+        self.assertEqual(fn(z=0, **{'x': 1, 'a': 2}, y=3),
+                         ['z', 'x', 'a', 'y'])
+        self.assertEqual(fn(**{'b': 1}, **{'a': 2}), ['b', 'a'])
+        # Named parameters are removed from **kwargs without reordering
+        # the rest.
+        def fn2(a, c=None, **kw):
+            return list(kw)
+
+        self.assertEqual(fn2(d=1, a=2, b=3, c=4, e=5), ['d', 'b', 'e'])
+
+    def test_kwargs_order_preserved_in_methods(self):
+        # PEP 468: Preserving Keyword Argument Order
+        class C:
+            def __init__(self, **kw):
+                self.init_kw = list(kw)
+
+            def meth(self, **kw):
+                return list(kw)
+
+            @classmethod
+            def cmeth(cls, **kw):
+                return list(kw)
+
+            @staticmethod
+            def smeth(**kw):
+                return list(kw)
+
+        c = C(b=1, a=2, c=3)
+        self.assertEqual(c.init_kw, ['b', 'a', 'c'])
+        self.assertEqual(c.meth(b=1, a=2, c=3), ['b', 'a', 'c'])
+        self.assertEqual(C.cmeth(b=1, a=2, c=3), ['b', 'a', 'c'])
+        self.assertEqual(C.smeth(b=1, a=2, c=3), ['b', 'a', 'c'])
+
+    def test_kwargs_order_preserved_in_c_functions(self):
+        # PEP 468: Preserving Keyword Argument Order
+        self.assertEqual(list(dict(b=1, a=2, c=3)), ['b', 'a', 'c'])
+        self.assertEqual(list(dict(**{'b': 1}, a=2)), ['b', 'a'])
+        self.assertEqual(list(collections.OrderedDict(b=1, a=2, c=3)),
+                         ['b', 'a', 'c'])
+
     def test_frames_are_popped_after_failed_calls(self):
         # GH-93252: stuff blows up if we don't pop the new frame after
         # recovering from failed calls:
diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py
index e07efd26966945..7bd6d966e8536b 100644
--- a/Lib/test/test_class.py
+++ b/Lib/test/test_class.py
@@ -1052,5 +1052,65 @@ def __init__(self):
                 self.fail("MemoryError was not raised during deallocation")
             self.fail("the dictionary was not cleared")
 
+class DefinitionOrderTests(unittest.TestCase):
+    # PEP 520: Preserving Class Attribute Definition Order
+
+    @staticmethod
+    def defined_names(namespace):
+        # Skip the names added by the compiler, like __firstlineno__.
+        return [name for name in namespace if not name.startswith('__')]
+
+    def test_definition_order(self):
+        class C:
+            b = 1
+            a = 2
+            def m(self): pass
+            @staticmethod
+            def s(): pass
+            z = 3
+
+        self.assertEqual(self.defined_names(C.__dict__),
+                         ['b', 'a', 'm', 's', 'z'])
+
+    def test_definition_order_redefinition(self):
+        class C:
+            b = 1
+            a = 2
+            b = 3
+
+        self.assertEqual(self.defined_names(C.__dict__), ['b', 'a'])
+        self.assertEqual(C.b, 3)
+
+    def test_definition_order_after_deletion(self):
+        class C:
+            a = 1
+            b = 2
+            del a
+            a = 3
+
+        self.assertEqual(self.defined_names(C.__dict__), ['b', 'a'])
+
+    def test_definition_order_in_namespace(self):
+        namespaces = []
+        class Meta(type):
+            def __new__(mcls, name, bases, namespace, **kwds):
+                namespaces.append(list(namespace))
+                return super().__new__(mcls, name, bases, namespace, **kwds)
+
+        class C(metaclass=Meta):
+            b = 1
+            a = 2
+            def m(self): pass
+
+        self.assertEqual(self.defined_names(namespaces[0]), ['b', 'a', 'm'])
+
+    def test_prepare_preserves_order(self):
+        namespace = type.__prepare__('C', ())
+        namespace['b'] = 1
+        namespace['a'] = 2
+        namespace['b'] = 3
+        self.assertEqual(list(namespace), ['b', 'a'])
+
+
 if __name__ == '__main__':
     unittest.main()

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to