diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 1e42356be21ddd6..76f1c351e159086 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 e07efd269669459..7bd6d966e8536b7 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()