From f0beb60f55b00837c7500f5462c4a8417ea639e4 Mon Sep 17 00:00:00 2001 From: Ash Berlin-Taylor Date: Wed, 12 Mar 2025 16:46:54 +0000 Subject: [PATCH 1/2] Cope with `field_transformer` being a generator This worked on 25.1.0 but was inadvertently broken by #1401 --- src/attr/_make.py | 3 +++ tests/test_hooks.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/attr/_make.py b/src/attr/_make.py index 7439dc2ad..32a50bf15 100644 --- a/src/attr/_make.py +++ b/src/attr/_make.py @@ -450,6 +450,9 @@ def _transform_attrs( if field_transformer is not None: attrs = field_transformer(cls, attrs) + if inspect.isgenerator(attrs): + attrs = tuple(attrs) + # Check attr order after executing the field_transformer. # Mandatory vs non-mandatory attr order only matters when they are part of # the __init__ signature and when they aren't kw_only (which are moved to diff --git a/tests/test_hooks.py b/tests/test_hooks.py index 6b3420639..4dcd35240 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -217,6 +217,22 @@ class C: assert "CAttributes" == fields_type.__name__ assert issubclass(fields_type, tuple) + def test_hook_generator(self): + """ + Ensure that `attrs.fields` are correctly recorded when field_transformer is a generator + + Regression test for #1417 + """ + + def hook(cls, attribs): + yield from attribs + + @attr.s(auto_attribs=True, field_transformer=hook) + class Base: + x: int + + assert ["x"] == [a.name for a in attr.fields(Base)] + class TestAsDictHook: def test_asdict(self): From 017f9a984fab30fdf5774e966d481d4f49d07573 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Wed, 12 Mar 2025 19:03:17 +0100 Subject: [PATCH 2/2] Update src/attr/_make.py Co-authored-by: Ash Berlin-Taylor --- src/attr/_make.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/attr/_make.py b/src/attr/_make.py index 32a50bf15..e84d9792a 100644 --- a/src/attr/_make.py +++ b/src/attr/_make.py @@ -448,10 +448,7 @@ def _transform_attrs( attrs = base_attrs + own_attrs if field_transformer is not None: - attrs = field_transformer(cls, attrs) - - if inspect.isgenerator(attrs): - attrs = tuple(attrs) + attrs = tuple(field_transformer(cls, attrs)) # Check attr order after executing the field_transformer. # Mandatory vs non-mandatory attr order only matters when they are part of