From ec15cce2c4213fd692d5796a5d9ed70057ca259a Mon Sep 17 00:00:00 2001 From: Dream <2468001320@qq.com> Date: Sat, 29 Aug 2026 16:29:39 +0800 Subject: [PATCH 1/8] fix: compare Attribute instances by identity in filters - src/attr/filters.py --- src/attr/filters.py | 144 ++++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/src/attr/filters.py b/src/attr/filters.py index 689b1705a..664f65be3 100644 --- a/src/attr/filters.py +++ b/src/attr/filters.py @@ -1,72 +1,72 @@ -# SPDX-License-Identifier: MIT - -""" -Commonly useful filters for `attrs.asdict` and `attrs.astuple`. -""" - -from ._make import Attribute - - -def _split_what(what): - """ - Returns a tuple of `frozenset`s of classes and attributes. - """ - return ( - frozenset(cls for cls in what if isinstance(cls, type)), - frozenset(cls for cls in what if isinstance(cls, str)), - frozenset(cls for cls in what if isinstance(cls, Attribute)), - ) - - -def include(*what): - """ - Create a filter that only allows *what*. - - Args: - what (list[type, str, attrs.Attribute]): - What to include. Can be a type, a name, or an attribute. - - Returns: - Callable: - A callable that can be passed to `attrs.asdict`'s and - `attrs.astuple`'s *filter* argument. - - .. versionchanged:: 23.1.0 Accept strings with field names. - """ - cls, names, attrs = _split_what(what) - - def include_(attribute, value): - return ( - value.__class__ in cls - or attribute.name in names - or attribute in attrs - ) - - return include_ - - -def exclude(*what): - """ - Create a filter that does **not** allow *what*. - - Args: - what (list[type, str, attrs.Attribute]): - What to exclude. Can be a type, a name, or an attribute. - - Returns: - Callable: - A callable that can be passed to `attrs.asdict`'s and - `attrs.astuple`'s *filter* argument. - - .. versionchanged:: 23.3.0 Accept field name string as input argument - """ - cls, names, attrs = _split_what(what) - - def exclude_(attribute, value): - return not ( - value.__class__ in cls - or attribute.name in names - or attribute in attrs - ) - - return exclude_ +# SPDX-License-Identifier: MIT + +""" +Commonly useful filters for `attrs.asdict` and `attrs.astuple`. +""" + +from ._make import Attribute + + +def _split_what(what): + """ + Returns a tuple of `frozenset`s of classes and attributes. + """ + return ( + frozenset(cls for cls in what if isinstance(cls, type)), + frozenset(cls for cls in what if isinstance(cls, str)), + frozenset(cls for cls in what if isinstance(cls, Attribute)), + ) + + +def include(*what): + """ + Create a filter that only allows *what*. + + Args: + what (list[type, str, attrs.Attribute]): + What to include. Can be a type, a name, or an attribute. + + Returns: + Callable: + A callable that can be passed to `attrs.asdict`'s and + `attrs.astuple`'s *filter* argument. + + .. versionchanged:: 23.1.0 Accept strings with field names. + """ + cls, names, attrs = _split_what(what) + + def include_(attribute, value): + return ( + value.__class__ in cls + or attribute.name in names + or any(attribute is a for a in attrs) + ) + + return include_ + + +def exclude(*what): + """ + Create a filter that does **not** allow *what*. + + Args: + what (list[type, str, attrs.Attribute]): + What to exclude. Can be a type, a name, or an attribute. + + Returns: + Callable: + A callable that can be passed to `attrs.asdict`'s and + `attrs.astuple`'s *filter* argument. + + .. versionchanged:: 23.3.0 Accept field name string as input argument + """ + cls, names, attrs = _split_what(what) + + def exclude_(attribute, value): + return not ( + value.__class__ in cls + or attribute.name in names + or any(attribute is a for a in attrs) + ) + + return exclude_ From cb325406937643cbb8ca250f9d00714105e14780 Mon Sep 17 00:00:00 2001 From: Dream <2468001320@qq.com> Date: Sat, 29 Aug 2026 16:29:41 +0800 Subject: [PATCH 2/8] fix: compare Attribute instances by identity in filters - tests/test_filters.py --- tests/test_filters.py | 282 +++++++++++++++++++++++------------------- 1 file changed, 156 insertions(+), 126 deletions(-) diff --git a/tests/test_filters.py b/tests/test_filters.py index 08314fa88..15de22739 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -1,126 +1,156 @@ -# SPDX-License-Identifier: MIT - -""" -Tests for `attr.filters`. -""" - -import pytest - -import attr - -from attr import fields -from attr.filters import _split_what, exclude, include - - -@attr.s -class C: - a = attr.ib() - b = attr.ib() - - -class TestSplitWhat: - """ - Tests for `_split_what`. - """ - - def test_splits(self): - """ - Splits correctly. - """ - assert ( - frozenset((int, str)), - frozenset(("abcd", "123")), - frozenset((fields(C).a,)), - ) == _split_what((str, "123", fields(C).a, int, "abcd")) - - -class TestInclude: - """ - Tests for `include`. - """ - - @pytest.mark.parametrize( - ("incl", "value"), - [ - ((int,), 42), - ((str,), "hello"), - ((str, fields(C).a), 42), - ((str, fields(C).b), "hello"), - (("a",), 42), - (("a",), "hello"), - (("a", str), 42), - (("a", fields(C).b), "hello"), - ], - ) - def test_allow(self, incl, value): - """ - Return True if a class or attribute is included. - """ - i = include(*incl) - assert i(fields(C).a, value) is True - - @pytest.mark.parametrize( - ("incl", "value"), - [ - ((str,), 42), - ((int,), "hello"), - ((str, fields(C).b), 42), - ((int, fields(C).b), "hello"), - (("b",), 42), - (("b",), "hello"), - (("b", str), 42), - (("b", fields(C).b), "hello"), - ], - ) - def test_drop_class(self, incl, value): - """ - Return False on non-included classes and attributes. - """ - i = include(*incl) - assert i(fields(C).a, value) is False - - -class TestExclude: - """ - Tests for `exclude`. - """ - - @pytest.mark.parametrize( - ("excl", "value"), - [ - ((str,), 42), - ((int,), "hello"), - ((str, fields(C).b), 42), - ((int, fields(C).b), "hello"), - (("b",), 42), - (("b",), "hello"), - (("b", str), 42), - (("b", fields(C).b), "hello"), - ], - ) - def test_allow(self, excl, value): - """ - Return True if class or attribute is not excluded. - """ - e = exclude(*excl) - assert e(fields(C).a, value) is True - - @pytest.mark.parametrize( - ("excl", "value"), - [ - ((int,), 42), - ((str,), "hello"), - ((str, fields(C).a), 42), - ((str, fields(C).b), "hello"), - (("a",), 42), - (("a",), "hello"), - (("a", str), 42), - (("a", fields(C).b), "hello"), - ], - ) - def test_drop_class(self, excl, value): - """ - Return True on non-excluded classes and attributes. - """ - e = exclude(*excl) - assert e(fields(C).a, value) is False +# SPDX-License-Identifier: MIT + +""" +Tests for `attr.filters`. +""" + +import pytest + +import attr + +from attr import fields +from attr.filters import _split_what, exclude, include + + +@attr.s +class C: + a = attr.ib() + b = attr.ib() + + +class TestSplitWhat: + """ + Tests for `_split_what`. + """ + + def test_splits(self): + """ + Splits correctly. + """ + assert ( + frozenset((int, str)), + frozenset(("abcd", "123")), + frozenset((fields(C).a,)), + ) == _split_what((str, "123", fields(C).a, int, "abcd")) + + +class TestInclude: + """ + Tests for `include`. + """ + + @pytest.mark.parametrize( + ("incl", "value"), + [ + ((int,), 42), + ((str,), "hello"), + ((str, fields(C).a), 42), + ((str, fields(C).b), "hello"), + (("a",), 42), + (("a",), "hello"), + (("a", str), 42), + (("a", fields(C).b), "hello"), + ], + ) + def test_allow(self, incl, value): + """ + Return True if a class or attribute is included. + """ + i = include(*incl) + assert i(fields(C).a, value) is True + + @pytest.mark.parametrize( + ("incl", "value"), + [ + ((str,), 42), + ((int,), "hello"), + ((str, fields(C).b), 42), + ((int, fields(C).b), "hello"), + (("b",), 42), + (("b",), "hello"), + (("b", str), 42), + (("b", fields(C).b), "hello"), + ], + ) + def test_drop_class(self, incl, value): + """ + Return False on non-included classes and attributes. + """ + i = include(*incl) + assert i(fields(C).a, value) is False + + +class TestExclude: + """ + Tests for `exclude`. + """ + + @pytest.mark.parametrize( + ("excl", "value"), + [ + ((str,), 42), + ((int,), "hello"), + ((str, fields(C).b), 42), + ((int, fields(C).b), "hello"), + (("b",), 42), + (("b",), "hello"), + (("b", str), 42), + (("b", fields(C).b), "hello"), + ], + ) + def test_allow(self, excl, value): + """ + Return True if class or attribute is not excluded. + """ + e = exclude(*excl) + assert e(fields(C).a, value) is True + + @pytest.mark.parametrize( + ("excl", "value"), + [ + ((int,), 42), + ((str,), "hello"), + ((str, fields(C).a), 42), + ((str, fields(C).b), "hello"), + (("a",), 42), + (("a",), "hello"), + (("a", str), 42), + (("a", fields(C).b), "hello"), + ], + ) + def test_drop_class(self, excl, value): + """ + Return True on non-excluded classes and attributes. + """ + e = exclude(*excl) + assert e(fields(C).a, value) is False + + +@attr.s +class C2: + a = attr.ib() + repeated = attr.ib() + + +@attr.s +class D2: + b = attr.ib() + repeated = attr.ib() + + +class TestSameNameAcrossClasses: + """ + Two classes may define attributes with the same name but they are + different `Attribute` instances; excluding one must not exclude the + other (https://github.com/python-attrs/attrs/issues/864). + """ + + def test_exclude_identity_not_equality(self): + e = exclude(fields(C2).repeated) + assert e(fields(C2).repeated, "x") is False + assert e(fields(D2).repeated, "x") is True + + def test_include_identity_not_equality(self): + i = include(fields(C2).repeated) + assert i(fields(C2).repeated, "x") is True + assert i(fields(D2).repeated, "x") is False From ca6bfe16642b2d7fdee1031ddba90325b8e7061a Mon Sep 17 00:00:00 2001 From: Dream <2468001320@qq.com> Date: Sat, 3 Oct 2026 18:52:57 +0800 Subject: [PATCH 3/8] add changelog fragment for #1616 --- changelog.d/1616.change.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1616.change.md diff --git a/changelog.d/1616.change.md b/changelog.d/1616.change.md new file mode 100644 index 000000000..a7b4e981f --- /dev/null +++ b/changelog.d/1616.change.md @@ -0,0 +1 @@ +Fixed a bug where `include()`/`exclude()` filters compared `Attribute` instances by value; they are now compared by identity so distinct attributes with equal fields are no longer conflated. From b6dcfff033b7e61a7b5305964dca1c263ab5f293 Mon Sep 17 00:00:00 2001 From: Dream <2468001320@qq.com> Date: Sat, 3 Oct 2026 18:59:23 +0800 Subject: [PATCH 4/8] fix: restore LF line endings in src/attr/filters.py (CRLF churn removed) --- src/attr/filters.py | 144 ++++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/src/attr/filters.py b/src/attr/filters.py index 664f65be3..3d9f81dbe 100644 --- a/src/attr/filters.py +++ b/src/attr/filters.py @@ -1,72 +1,72 @@ -# SPDX-License-Identifier: MIT - -""" -Commonly useful filters for `attrs.asdict` and `attrs.astuple`. -""" - -from ._make import Attribute - - -def _split_what(what): - """ - Returns a tuple of `frozenset`s of classes and attributes. - """ - return ( - frozenset(cls for cls in what if isinstance(cls, type)), - frozenset(cls for cls in what if isinstance(cls, str)), - frozenset(cls for cls in what if isinstance(cls, Attribute)), - ) - - -def include(*what): - """ - Create a filter that only allows *what*. - - Args: - what (list[type, str, attrs.Attribute]): - What to include. Can be a type, a name, or an attribute. - - Returns: - Callable: - A callable that can be passed to `attrs.asdict`'s and - `attrs.astuple`'s *filter* argument. - - .. versionchanged:: 23.1.0 Accept strings with field names. - """ - cls, names, attrs = _split_what(what) - - def include_(attribute, value): - return ( - value.__class__ in cls - or attribute.name in names - or any(attribute is a for a in attrs) - ) - - return include_ - - -def exclude(*what): - """ - Create a filter that does **not** allow *what*. - - Args: - what (list[type, str, attrs.Attribute]): - What to exclude. Can be a type, a name, or an attribute. - - Returns: - Callable: - A callable that can be passed to `attrs.asdict`'s and - `attrs.astuple`'s *filter* argument. - - .. versionchanged:: 23.3.0 Accept field name string as input argument - """ - cls, names, attrs = _split_what(what) - - def exclude_(attribute, value): - return not ( - value.__class__ in cls - or attribute.name in names - or any(attribute is a for a in attrs) - ) - - return exclude_ +# SPDX-License-Identifier: MIT + +""" +Commonly useful filters for `attrs.asdict` and `attrs.astuple`. +""" + +from ._make import Attribute + + +def _split_what(what): + """ + Returns a tuple of `frozenset`s of classes and attributes. + """ + return ( + frozenset(cls for cls in what if isinstance(cls, type)), + frozenset(cls for cls in what if isinstance(cls, str)), + frozenset(cls for cls in what if isinstance(cls, Attribute)), + ) + + +def include(*what): + """ + Create a filter that only allows *what*. + + Args: + what (list[type, str, attrs.Attribute]): + What to include. Can be a type, a name, or an attribute. + + Returns: + Callable: + A callable that can be passed to `attrs.asdict`'s and + `attrs.astuple`'s *filter* argument. + + .. versionchanged:: 23.1.0 Accept strings with field names. + """ + cls, names, attrs = _split_what(what) + + def include_(attribute, value): + return ( + value.__class__ in cls + or attribute.name in names + or any(attribute is a for a in attrs) + ) + + return include_ + + +def exclude(*what): + """ + Create a filter that does **not** allow *what*. + + Args: + what (list[type, str, attrs.Attribute]): + What to exclude. Can be a type, a name, or an attribute. + + Returns: + Callable: + A callable that can be passed to `attrs.asdict`'s and + `attrs.astuple`'s *filter* argument. + + .. versionchanged:: 23.3.0 Accept field name string as input argument + """ + cls, names, attrs = _split_what(what) + + def exclude_(attribute, value): + return not ( + value.__class__ in cls + or attribute.name in names + or any(attribute is a for a in attrs) + ) + + return exclude_ From 69cb10e362de67a1edcdbfca840d813f321973ea Mon Sep 17 00:00:00 2001 From: Dream <2468001320@qq.com> Date: Sat, 3 Oct 2026 18:59:26 +0800 Subject: [PATCH 5/8] fix: restore LF line endings in tests/test_filters.py (CRLF churn removed) --- tests/test_filters.py | 312 +++++++++++++++++++++--------------------- 1 file changed, 156 insertions(+), 156 deletions(-) diff --git a/tests/test_filters.py b/tests/test_filters.py index 15de22739..1789ca03b 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -1,156 +1,156 @@ -# SPDX-License-Identifier: MIT - -""" -Tests for `attr.filters`. -""" - -import pytest - -import attr - -from attr import fields -from attr.filters import _split_what, exclude, include - - -@attr.s -class C: - a = attr.ib() - b = attr.ib() - - -class TestSplitWhat: - """ - Tests for `_split_what`. - """ - - def test_splits(self): - """ - Splits correctly. - """ - assert ( - frozenset((int, str)), - frozenset(("abcd", "123")), - frozenset((fields(C).a,)), - ) == _split_what((str, "123", fields(C).a, int, "abcd")) - - -class TestInclude: - """ - Tests for `include`. - """ - - @pytest.mark.parametrize( - ("incl", "value"), - [ - ((int,), 42), - ((str,), "hello"), - ((str, fields(C).a), 42), - ((str, fields(C).b), "hello"), - (("a",), 42), - (("a",), "hello"), - (("a", str), 42), - (("a", fields(C).b), "hello"), - ], - ) - def test_allow(self, incl, value): - """ - Return True if a class or attribute is included. - """ - i = include(*incl) - assert i(fields(C).a, value) is True - - @pytest.mark.parametrize( - ("incl", "value"), - [ - ((str,), 42), - ((int,), "hello"), - ((str, fields(C).b), 42), - ((int, fields(C).b), "hello"), - (("b",), 42), - (("b",), "hello"), - (("b", str), 42), - (("b", fields(C).b), "hello"), - ], - ) - def test_drop_class(self, incl, value): - """ - Return False on non-included classes and attributes. - """ - i = include(*incl) - assert i(fields(C).a, value) is False - - -class TestExclude: - """ - Tests for `exclude`. - """ - - @pytest.mark.parametrize( - ("excl", "value"), - [ - ((str,), 42), - ((int,), "hello"), - ((str, fields(C).b), 42), - ((int, fields(C).b), "hello"), - (("b",), 42), - (("b",), "hello"), - (("b", str), 42), - (("b", fields(C).b), "hello"), - ], - ) - def test_allow(self, excl, value): - """ - Return True if class or attribute is not excluded. - """ - e = exclude(*excl) - assert e(fields(C).a, value) is True - - @pytest.mark.parametrize( - ("excl", "value"), - [ - ((int,), 42), - ((str,), "hello"), - ((str, fields(C).a), 42), - ((str, fields(C).b), "hello"), - (("a",), 42), - (("a",), "hello"), - (("a", str), 42), - (("a", fields(C).b), "hello"), - ], - ) - def test_drop_class(self, excl, value): - """ - Return True on non-excluded classes and attributes. - """ - e = exclude(*excl) - assert e(fields(C).a, value) is False - - -@attr.s -class C2: - a = attr.ib() - repeated = attr.ib() - - -@attr.s -class D2: - b = attr.ib() - repeated = attr.ib() - - -class TestSameNameAcrossClasses: - """ - Two classes may define attributes with the same name but they are - different `Attribute` instances; excluding one must not exclude the - other (https://github.com/python-attrs/attrs/issues/864). - """ - - def test_exclude_identity_not_equality(self): - e = exclude(fields(C2).repeated) - assert e(fields(C2).repeated, "x") is False - assert e(fields(D2).repeated, "x") is True - - def test_include_identity_not_equality(self): - i = include(fields(C2).repeated) - assert i(fields(C2).repeated, "x") is True - assert i(fields(D2).repeated, "x") is False +# SPDX-License-Identifier: MIT + +""" +Tests for `attr.filters`. +""" + +import pytest + +import attr + +from attr import fields +from attr.filters import _split_what, exclude, include + + +@attr.s +class C: + a = attr.ib() + b = attr.ib() + + +class TestSplitWhat: + """ + Tests for `_split_what`. + """ + + def test_splits(self): + """ + Splits correctly. + """ + assert ( + frozenset((int, str)), + frozenset(("abcd", "123")), + frozenset((fields(C).a,)), + ) == _split_what((str, "123", fields(C).a, int, "abcd")) + + +class TestInclude: + """ + Tests for `include`. + """ + + @pytest.mark.parametrize( + ("incl", "value"), + [ + ((int,), 42), + ((str,), "hello"), + ((str, fields(C).a), 42), + ((str, fields(C).b), "hello"), + (("a",), 42), + (("a",), "hello"), + (("a", str), 42), + (("a", fields(C).b), "hello"), + ], + ) + def test_allow(self, incl, value): + """ + Return True if a class or attribute is included. + """ + i = include(*incl) + assert i(fields(C).a, value) is True + + @pytest.mark.parametrize( + ("incl", "value"), + [ + ((str,), 42), + ((int,), "hello"), + ((str, fields(C).b), 42), + ((int, fields(C).b), "hello"), + (("b",), 42), + (("b",), "hello"), + (("b", str), 42), + (("b", fields(C).b), "hello"), + ], + ) + def test_drop_class(self, incl, value): + """ + Return False on non-included classes and attributes. + """ + i = include(*incl) + assert i(fields(C).a, value) is False + + +class TestExclude: + """ + Tests for `exclude`. + """ + + @pytest.mark.parametrize( + ("excl", "value"), + [ + ((str,), 42), + ((int,), "hello"), + ((str, fields(C).b), 42), + ((int, fields(C).b), "hello"), + (("b",), 42), + (("b",), "hello"), + (("b", str), 42), + (("b", fields(C).b), "hello"), + ], + ) + def test_allow(self, excl, value): + """ + Return True if class or attribute is not excluded. + """ + e = exclude(*excl) + assert e(fields(C).a, value) is True + + @pytest.mark.parametrize( + ("excl", "value"), + [ + ((int,), 42), + ((str,), "hello"), + ((str, fields(C).a), 42), + ((str, fields(C).b), "hello"), + (("a",), 42), + (("a",), "hello"), + (("a", str), 42), + (("a", fields(C).b), "hello"), + ], + ) + def test_drop_class(self, excl, value): + """ + Return True on non-excluded classes and attributes. + """ + e = exclude(*excl) + assert e(fields(C).a, value) is False + + +@attr.s +class C2: + a = attr.ib() + repeated = attr.ib() + + +@attr.s +class D2: + b = attr.ib() + repeated = attr.ib() + + +class TestSameNameAcrossClasses: + """ + Two classes may define attributes with the same name but they are + different `Attribute` instances; excluding one must not exclude the + other (https://github.com/python-attrs/attrs/issues/864). + """ + + def test_exclude_identity_not_equality(self): + e = exclude(fields(C2).repeated) + assert e(fields(C2).repeated, "x") is False + assert e(fields(D2).repeated, "x") is True + + def test_include_identity_not_equality(self): + i = include(fields(C2).repeated) + assert i(fields(C2).repeated, "x") is True + assert i(fields(D2).repeated, "x") is False From d90cf81f2acf783eeb16292fc845291b697bc7cd Mon Sep 17 00:00:00 2001 From: Dream <2468001320@qq.com> Date: Sat, 3 Oct 2026 21:29:23 +0800 Subject: [PATCH 6/8] docs: add missing docstrings (interrogate 100% gate) --- tests/test_filters.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_filters.py b/tests/test_filters.py index 1789ca03b..c8cb542af 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -14,6 +14,7 @@ @attr.s class C: + """C.""" a = attr.ib() b = attr.ib() @@ -128,12 +129,14 @@ def test_drop_class(self, excl, value): @attr.s class C2: + """C2.""" a = attr.ib() repeated = attr.ib() @attr.s class D2: + """D2.""" b = attr.ib() repeated = attr.ib() @@ -146,11 +149,13 @@ class TestSameNameAcrossClasses: """ def test_exclude_identity_not_equality(self): + """Test Exclude identity not equality.""" e = exclude(fields(C2).repeated) assert e(fields(C2).repeated, "x") is False assert e(fields(D2).repeated, "x") is True def test_include_identity_not_equality(self): + """Test Include identity not equality.""" i = include(fields(C2).repeated) assert i(fields(C2).repeated, "x") is True assert i(fields(D2).repeated, "x") is False From c7dfaeef7d811a33acc615414be04b0d1f1c3b11 Mon Sep 17 00:00:00 2001 From: Dream <2468001320@qq.com> Date: Sat, 3 Oct 2026 21:29:27 +0800 Subject: [PATCH 7/8] docs: add missing docstrings (interrogate 100% gate) --- src/attr/filters.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/attr/filters.py b/src/attr/filters.py index 3d9f81dbe..ff8f57581 100644 --- a/src/attr/filters.py +++ b/src/attr/filters.py @@ -36,6 +36,7 @@ def include(*what): cls, names, attrs = _split_what(what) def include_(attribute, value): + """Include.""" return ( value.__class__ in cls or attribute.name in names @@ -63,6 +64,7 @@ def exclude(*what): cls, names, attrs = _split_what(what) def exclude_(attribute, value): + """Exclude.""" return not ( value.__class__ in cls or attribute.name in names From e141819a8cce9f2f74eb7498f2e86a908a88ddf4 Mon Sep 17 00:00:00 2001 From: Dream <2468001320@qq.com> Date: Sat, 3 Oct 2026 23:38:57 +0800 Subject: [PATCH 8/8] style: apply ruff format to docstring additions --- tests/test_filters.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_filters.py b/tests/test_filters.py index c8cb542af..0aea99101 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -15,6 +15,7 @@ @attr.s class C: """C.""" + a = attr.ib() b = attr.ib() @@ -130,6 +131,7 @@ def test_drop_class(self, excl, value): @attr.s class C2: """C2.""" + a = attr.ib() repeated = attr.ib() @@ -137,6 +139,7 @@ class C2: @attr.s class D2: """D2.""" + b = attr.ib() repeated = attr.ib()