From add70a4834419a73496ab9317b9877255d49cdc7 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Sat, 16 Mar 2024 17:15:37 +0100 Subject: [PATCH] evolve: remove support for passing instances per kw As per our deprecation policy. --- changelog.d/1264.breaking.md | 2 ++ src/attr/_funcs.py | 33 ++++++++++----------------------- tests/test_funcs.py | 19 +++---------------- 3 files changed, 15 insertions(+), 39 deletions(-) create mode 100644 changelog.d/1264.breaking.md diff --git a/changelog.d/1264.breaking.md b/changelog.d/1264.breaking.md new file mode 100644 index 000000000..f268e2d08 --- /dev/null +++ b/changelog.d/1264.breaking.md @@ -0,0 +1,2 @@ +`attrs.evolve()` doesn't accept the *inst* argument as a keyword argument anymore. +Pass it as the first positional argument instead. diff --git a/src/attr/_funcs.py b/src/attr/_funcs.py index 5be1adb1b..0912a4562 100644 --- a/src/attr/_funcs.py +++ b/src/attr/_funcs.py @@ -371,7 +371,8 @@ def evolve(*args, **changes): Create a new instance, based on the first positional argument with *changes* applied. - :param inst: Instance of a class with *attrs* attributes. + :param inst: Instance of a class with *attrs* attributes. *inst* must be + passed as a positional argument. :param changes: Keyword changes in the new copy. :return: A copy of inst with *changes* incorporated. @@ -387,30 +388,16 @@ def evolve(*args, **changes): *inst*. It will raise a warning until at least April 2024, after which it will become an error. Always pass the instance as a positional argument. + .. versionchanged:: 24.1.0 + *inst* can't be passed as a keyword argument anymore. """ - # Try to get instance by positional argument first. - # Use changes otherwise and warn it'll break. - if args: - try: - (inst,) = args - except ValueError: - msg = f"evolve() takes 1 positional argument, but {len(args)} were given" - raise TypeError(msg) from None - else: - try: - inst = changes.pop("inst") - except KeyError: - msg = "evolve() missing 1 required positional argument: 'inst'" - raise TypeError(msg) from None - - import warnings - - warnings.warn( - "Passing the instance per keyword argument is deprecated and " - "will stop working in, or after, April 2024.", - DeprecationWarning, - stacklevel=2, + try: + (inst,) = args + except ValueError: + msg = ( + f"evolve() takes 1 positional argument, but {len(args)} were given" ) + raise TypeError(msg) from None cls = inst.__class__ attrs = fields(cls) diff --git a/tests/test_funcs.py b/tests/test_funcs.py index 398ba3576..67c9bc9d4 100644 --- a/tests/test_funcs.py +++ b/tests/test_funcs.py @@ -781,26 +781,13 @@ class Cls2: obj1a, param1=obj2b ) - def test_inst_kw(self): - """ - If `inst` is passed per kw argument, a warning is raised. - See #1109 - """ - - @attr.s - class C: - pass - - with pytest.warns(DeprecationWarning) as wi: - evolve(inst=C()) - - assert __file__ == wi.list[0].filename - def test_no_inst(self): """ Missing inst argument raises a TypeError like Python would. """ - with pytest.raises(TypeError, match=r"evolve\(\) missing 1"): + with pytest.raises( + TypeError, match=r"evolve\(\) takes 1 positional argument" + ): evolve(x=1) def test_too_many_pos_args(self):