From f5b89afde1196ec9f74b7dc0333cec9bc4d4c2db Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 19 Jun 2019 10:33:27 +0300 Subject: [PATCH] bpo-37163: Deprecate passing argument obj of dataclasses.replace() by keyword. (GH-13877) --- Lib/dataclasses.py | 14 +++++++++++++- Lib/test/test_dataclasses.py | 7 +++++++ .../2019-06-07-08-18-05.bpo-37163.36JkUh.rst | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2019-06-07-08-18-05.bpo-37163.36JkUh.rst diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index b035cbb809..18713722a7 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1206,7 +1206,7 @@ def make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, unsafe_hash=unsafe_hash, frozen=frozen) -def replace(obj, **changes): +def replace(*args, **changes): """Return a new object replacing specified fields with new values. This is especially useful for frozen classes. Example usage: @@ -1220,6 +1220,17 @@ def replace(obj, **changes): c1 = replace(c, x=3) assert c1.x == 3 and c1.y == 2 """ + if len(args) > 1: + raise TypeError(f'replace() takes 1 positional argument but {len(args)} were given') + if args: + obj, = args + elif 'obj' in changes: + obj = changes.pop('obj') + import warnings + warnings.warn("Passing 'obj' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + raise TypeError("replace() missing 1 required positional argument: 'obj'") # We're going to mutate 'changes', but that's okay because it's a # new dict, even if called with 'replace(obj, **my_changes)'. @@ -1255,3 +1266,4 @@ def replace(obj, **changes): # changes that aren't fields, this will correctly raise a # TypeError. return obj.__class__(**changes) +replace.__text_signature__ = '(obj, /, **kwargs)' diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index 53e8443c2a..cb0e18c242 100755 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -3075,6 +3075,13 @@ class TestReplace(unittest.TestCase): self.assertEqual(c1.x, 3) self.assertEqual(c1.y, 2) + self.assertRaises(TypeError, replace) + self.assertRaises(TypeError, replace, c, c) + with self.assertWarns(DeprecationWarning): + c1 = replace(obj=c, x=3) + self.assertEqual(c1.x, 3) + self.assertEqual(c1.y, 2) + def test_frozen(self): @dataclass(frozen=True) class C: diff --git a/Misc/NEWS.d/next/Library/2019-06-07-08-18-05.bpo-37163.36JkUh.rst b/Misc/NEWS.d/next/Library/2019-06-07-08-18-05.bpo-37163.36JkUh.rst new file mode 100644 index 0000000000..04cf61d3e0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-07-08-18-05.bpo-37163.36JkUh.rst @@ -0,0 +1,2 @@ +Deprecated passing ``obj`` argument of :func:`dataclasses.replace` as +keyword argument. -- 2.40.0