@@ -136,6 +136,43 @@ def v2(self, _, __):
136136
137137 assert _AndValidator ((v , v2 )) == a ._validator
138138
139+ def test_converter_decorator_single (self ):
140+ """
141+ If _CountingAttr.converter is used as a decorator and there is no
142+ decorator set, the decorated method is used as the converter.
143+ """
144+ a = attr .ib ()
145+
146+ @a .converter
147+ def v (self , value , field ):
148+ pass
149+
150+ assert isinstance (a ._converter , attr .Converter )
151+ assert a ._converter .takes_self
152+ assert a ._converter .takes_field
153+
154+ @pytest .mark .parametrize (
155+ "wrap" , [lambda v : v , lambda v : [v ], attr .converters .pipe ]
156+ )
157+ def test_converter_decorator (self , wrap ):
158+ """
159+ If _CountingAttr.converter is used as a decorator and there is already
160+ a decorator set, the decorators are composed using `pipe`.
161+ """
162+
163+ def v (_ ):
164+ pass
165+
166+ a = attr .ib (converter = wrap (v ))
167+
168+ @a .converter
169+ def v2 (self , value , field ):
170+ pass
171+
172+ assert isinstance (a ._converter , attr .Converter )
173+ assert a ._converter .takes_self
174+ assert a ._converter .takes_field
175+
139176 def test_default_decorator_already_set (self ):
140177 """
141178 Raise DefaultAlreadySetError if the decorator is used after a default
@@ -1720,6 +1757,23 @@ class C:
17201757
17211758 assert 84 == C (2 ).x
17221759
1760+ def test_converter_decorated (self ):
1761+ """
1762+ Same as Converter with both `takes_field` and `takes_self`
1763+ """
1764+
1765+ @attr .define
1766+ class C :
1767+ factor : int = 5
1768+ x : int = attr .field (default = 0 , metadata = {"offset" : 200 })
1769+
1770+ @x .converter
1771+ def _convert_x (self , field , value ):
1772+ assert isinstance (field , attr .Attribute )
1773+ return int (value ) * self .factor + field .metadata ["offset" ]
1774+
1775+ assert 410 == C (x = "42" ).x
1776+
17231777 @given (integers (), booleans ())
17241778 def test_convert_property (self , val , init ):
17251779 """
0 commit comments