pythonpython-attrs

In python Attrs package, how to add a field in the field_transformer function?


The documentation for the python Attrs, the Automatic Field Transformation and Modification section states

...You can add converters, change types, and even remove attributes completely or create new ones!" (emphasis added).

It also states in the API documentation regarding Attribute that "You should never instantiate this class yourself."

However, if you attempt to add a field in the field_transformer function you cannot, at least with the attrs.field() function.

    import attrs
    def transformer(cls, fields):
        fields.append(attrs.field(name='bar'))
        return fields

    
    @attrs.define(field_transformer=transformer)
    class A:
        pass
    
    A(bar=1)

This fails with a TypeError: field() got an unexpected keyword argument 'name'. If you use "alias" instead you get AttributeError: '_CountingAttr' object has no attribute 'name'.

What is the proper way to add a field in field_transformer function?

Edit: I was able to get things to work with the following:

import attrs
def transformer(cls, fields):        
    ca = attrs.field()
    f = attrs.Attribute.from_counting_attr(name="foo", ca=ca, type=int)
    return [f, *fields]

@attrs.define(field_transformer=transformer)
class A:
    pass

A(foo=1)

Solution

  • Method of adding class attribute dynamically with attrs

    Here's another method of how you can add a field to the class with attrs.

    from attrs import define, field, make_class
    
    class Foo:
        pass
    
    Foo = make_class(Foo.__name__, {'bar': field(alias='bar')}, 
                     bases=(Foo,), field_transformer=None)
    
    foo = Foo(bar=1) 
    foo # Foo(bar=1)
    

    Alternatively, if your class already has other attributes defined, then the following would work.

    @define()
    class Foo:
        foo: int
    
    Foo = make_class(Foo.__name__, {'bar': field(alias='bar')}, 
                     bases=(Foo,), field_transformer=None)
    
    foo = Foo(foo=10, bar=1) 
    foo # Foo(foo=10, bar=1)
    

    NOTE: If you must use a transforming function (say transformer_func), then use field_transformer=transformer_func inside make_class function.

    Reference