I’m thrilled to finally present attrs 20.1.0 with the following changes:
This release is HUGE and I'm stoked we can finally get it to you! It’s the result of more than a year of development and not only does it come with many great features that users desired for a long time, it also lays the foundation for the future evolution of this package.
The final push has only been possible thanks to attrs’s Tidelift subscribers (https://tidelift.com/funding/github/pypi/attrs) and my generous GitHub Sponsors (https://github.com/sponsors/hynek). If you want to speed up the development for the forthcoming 20.2.0 release (You do! There’s a lot of great stuff coming up!), please consider supporting my work by either sponsoring me, or convincing your boss to subscribe to my projects on Tidelift (https://tidelift.com).
Hooks on Setting Attributes
You could always use validators for when a class is instantiated and you could always make classes immutable.
Now you can also freeze specific attributes and validate attribute values on assignment using the new on_setattr argument.
It takes a callable and runs it whenever the user tries to assign a new value to the attribute.
See #660 (https://github.com/python-attrs/attrs/pull/660).
Automatic Detection of Own Methods
If you want to write your own dunder method – say __init__ – you have to remember to tell attrs to not generate an own version (e.g. @attr.s(init=False)) otherwise it will happily overwrite it.
Not anymore! If you pass @attr.s(auto_detect=True), attrs will look for your own methods automatically – but only in the current class. In other words: inherited methods don’t count. It’s still a bit of hassle to set it to True, but that will be remedied in the future (see below).
See #607 (https://github.com/python-attrs/attrs/pull/607).
Attribute Collection and the MRO
attrs’s collection of attributes was slightly wrong in very specific situations involving multiple inheritance (editor’s note: don’t use multiple inheritance). Most people won't notice, but some do (https://github.com/python-attrs/attrs/issues/428) we like to do things the right way.
See #635 (https://github.com/python-attrs/attrs/pull/635).
The Road Ahead
attrs started out in February 2015. The Python landscape was very different back then and lots of things changed since. We also got some things subtly wrong that need to be fixed by passing arguments. And finally the cute attr.s and attr.ib function names didn't age as attrs gained more and more features over the years.
Therefore, we intend to add a new attrs namespace in 20.2.0 later this year, allowing you to import attrs.
But there’s more than an extra “s” planned. We want to free people from having to pass all kinds of arguments to get the “good behavior” of attrs and therefore we want to introduce new APIs that have better defaults. Thanks to the new namespace, we don’t have to break backward compatibility and the old APIs aren’t going anywhere: in fact, the new ones build on them.
To get the new APIs just right, we’ve added them to attrs 20.1.0 provisionally for you to test and give us with feedback.
The APIs are attr.define() which is supposed to become the default way to decorate classes in the future, attr.frozen() that’s the same thing as attr.define(frozen=True) and finally attr.mutable() that is an alias for define() and was wished for by immutability fans.
There’s also a new alias for attr.ib() called attr.field() since that seems to be the nomenclature the Python community has agreed on. It carries no changes except that it’s keyword-only.
To give you a taste, this is an example for a class defined using the new APIs:
@attr.define class C: x: int y: str = attr.field(default="foo")
def __repr__(self) -> str: return "attrs will not overwrite this method."
If everything goes according to plan, in 20.2.0 you’ll be able to substitute attr with attrs.
N.B. All these APIs require at least Python 3.6. While attrs will be Python 2-compatible as long as we can (https://www.attrs.org/en/stable/python-2.html), the new APIs are not.
Check out the API documentation for provisional APIs (https://www.attrs.org/en/stable/api.html#provisional-apis) and please use issue #668 (https://github.com/python-attrs/attrs/issues/668) for feedback! Relevant discussions happened in #408 (https://github.com/python-attrs/attrs/issues/408), #487 (https://github.com/python-attrs/attrs/issues/487), and finally #666 (https://github.com/python-attrs/attrs/pull/666) (yep).
The full (looong) changelog with many more features and fixes can be found at https://www.attrs.org/en/stable/changelog.html.