Skip to content

Releases: python-attrs/attrs

23.2.0

31 Dec 06:29
23.2.0
9e443b1
Compare
Choose a tag to compare

Highlights

Mostly typing fixes in this release, but I'm excited that we found a workaround to make functools.cached_property work with slotted classes! Also, I'm sure there's gonna be fans of the new behavior of __attrs_pre_init__ where it receives all arguments passed to __init__ if it accepts more than self.

Full changelog below!

Special Thanks

This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!

Above and Beyond

Variomedia AG (@variomedia), Tidelift (@tidelift), FilePreviews (@filepreviews), Daniel Fortunov (@asqui), and Kevin P. Fleming (@kpfleming).

Maintenance Sustainers

Adam Hill (@adamghill), Dan Groshev (@si14), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Moving Content AG (@moving-content), ProteinQure (@ProteinQure), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Ionel Cristian Mărieș (@ionelmc), The Westervelt Company (@westerveltco), Philippe Galvan (@PhilippeGalvan), Birk Jernström (@birkjernstrom), Tim Schilling (@tim-schilling), Chris Withers (@cjw296), Christopher Dignam (@chdsbd), Stefan Hagen (@sthagen), Sławomir Ehlert (@slafs), Mostafa Khalil (@khadrawy), Filip Mularczyk (@mukiblejlok), and Mike Fiedler (@miketheman).

Not to forget 6 more amazing humans who chose to be generous but anonymous!

Full Changelog

Changes

  • The type annotation for attrs.resolve_types() is now correct. #1141
  • Type stubs now use typing.dataclass_transform to decorate dataclass-like decorators, instead of the non-standard __dataclass_transform__ special form, which is only supported by Pyright. #1158
  • Fixed serialization of namedtuple fields using attrs.asdict/astuple() with retain_collection_types=True. #1165
  • attrs.AttrsInstance is now a typing.Protocol in both type hints and code. This allows you to subclass it along with another Protocol. #1172
  • If attrs detects that __attrs_pre_init__ accepts more than just self, it will call it with the same arguments as __init__ was called. This allows you to, for example, pass arguments to super().__init__(). #1187
  • Slotted classes now transform functools.cached_property decorated methods to support equivalent semantics. #1200
  • Added class_body argument to attrs.make_class() to provide additional attributes for newly created classes. It is, for example, now possible to attach methods. #1203

23.1.0

16 Apr 10:45
23.1.0
1e2f6f9
Compare
Choose a tag to compare

Highlights

A lot of features and smaller bug fixes! But also with a heavy heart, we're leaving the last dataclass-less Python version (3.6) behind, but don't worry: the old versions aren't going anywhere and thanks to the magic of package metadata, pip install attrs should still work on Python 3.6 as if nothing happened.

Special Thanks

This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!

Above and Beyond

Variomedia AG (@variomedia), Tidelift (@tidelift), Sentry (@getsentry), HiredScore (@HiredScore), FilePreviews (@filepreviews), and Daniel Fortunov (@asqui).

Maintenance Sustainers

Adam Hill (@adamghill), Dan Groshev (@si14), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Moving Content AG (@moving-content), Stein Magnus Jodal (@jodal), ProteinQure (@ProteinQure), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Tom Ballinger (@thomasballinger), @medecau, Ionel Cristian Mărieș (@ionelmc), The Westervelt Company (@westerveltco), Philippe Galvan (@PhilippeGalvan), Birk Jernström (@birkjernstrom), Tim Schilling (@tim-schilling), Chris Withers (@cjw296), Christopher Dignam (@chdsbd), and Stefan Hagen (@sthagen).

Not to forget 3 more amazing humans who chose to be generous but anonymous!

Full Changelog

Backwards-incompatible Changes

  • Python 3.6 has been dropped and packaging switched to static package data using Hatch. #993

Deprecations

  • The support for zope-interface via the attrs.validators.provides validator is now deprecated and will be removed in, or after, April 2024.

    The presence of a C-based package in our developement dependencies has caused headaches and we're not under the impression it's used a lot.

    Let us know if you're using it and we might publish it as a separate package. #1120

Changes

  • attrs.filters.exclude() and attrs.filters.include() now support the passing of attribute names as strings. #1068

  • attrs.has() and attrs.fields() now handle generic classes correctly. #1079

  • Fix frozen exception classes when raised within e.g. contextlib.contextmanager, which mutates their __traceback__ attributes. #1081

  • @frozen now works with type checkers that implement PEP-681 (ex. pyright). #1084

  • Restored ability to unpickle instances pickled before 22.2.0. #1085

  • attrs.asdict()'s and attrs.astuple()'s type stubs now accept the attrs.AttrsInstance protocol. #1090

  • Fix slots class cellvar updating closure in CPython 3.8+ even when __code__ introspection is unavailable. #1092

  • attrs.resolve_types() can now pass include_extras to typing.get_type_hints() on Python 3.9+, and does so by default. #1099

  • Added instructions for pull request workflow to CONTRIBUTING.md. #1105

  • Added type parameter to attrs.field() function for use with attrs.make_class().

    Please note that type checkers ignore type metadata passed into make_class(), but it can be useful if you're wrapping attrs. #1107

  • It is now possible for attrs.evolve() (and attr.evolve()) to change fields named inst if the instance is passed as a positional argument.

    Passing the instance using the inst keyword argument is now deprecated and will be removed in, or after, April 2024. #1117

  • attrs.validators.optional() now also accepts a tuple of validators (in addition to lists of validators). #1122

22.2.0

21 Dec 09:33
22.2.0
a9960de
Compare
Choose a tag to compare

Highlights

It's been a lot busier than the changelog indicates, but a lot of the work happened under the hood (like some impressive performance improvements). But we've got still one big new feature that's are worthy the holidays:

Fields now have an alias argument that allows you to set the field's name in the generated __init__ method. This is especially useful for those who aren't fans of attrs's behavior of stripping underscores from private attribute names.

Special Thanks

This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!

Above and Beyond

Variomedia AG (@variomedia), Tidelift (@tidelift), Sentry (@getsentry), HiredScore (@HiredScore), FilePreviews (@filepreviews), and Daniel Fortunov (@asqui).

Maintenance Sustainers

@rzijp, Adam Hill (@adamghill), Dan Groshev (@si14), Tamir Bahar (@tmr232), Adi Roiban (@adiroiban), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Moving Content AG (@moving-content), Stein Magnus Jodal (@jodal), Iwan Aucamp (@aucampia), ProteinQure (@ProteinQure), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Thomas Ballinger (@thomasballinger), @medecau, Ionel Cristian Mărieș (@ionelmc), The Westervelt Company (@westerveltco), Philippe Galvan (@PhilippeGalvan), Birk Jernström (@birkjernstrom), Jannis Leidel (@jezdez), Tim Schilling (@tim-schilling), Chris Withers (@cjw296), and Christopher Dignam (@chdsbd).

Not to forget 2 more amazing humans who chose to be generous but anonymous!

Full Changelog

Backwards-incompatible Changes

  • Python 3.5 is not supported anymore. #988

Deprecations

  • Python 3.6 is now deprecated and support will be removed in the next release. #1017

Changes

  • attrs.field() now supports an alias option for explicit __init__ argument names.

    Get __init__ signatures matching any taste, peculiar or plain! The PEP 681 compatible alias option can be use to override private attribute name mangling, or add other arbitrary field argument name overrides. #950

  • attrs.NOTHING is now an enum value, making it possible to use with e.g. typing.Literal. #983

  • Added missing re-import of attr.AttrsInstance to the attrs namespace. #987

  • Fix slight performance regression in classes with custom __setattr__ and speedup even more. #991

  • Class-creation performance improvements by switching performance-sensitive templating operations to f-strings.

    You can expect an improvement of about 5% -- even for very simple classes. #995

  • attrs.has() is now a TypeGuard for AttrsInstance. That means that type checkers know a class is an instance of an attrs class if you check it using attrs.has() (or attr.has()) first. #997

  • Made attrs.AttrsInstance stub available at runtime and fixed type errors related to the usage of attrs.AttrsInstance in Pyright. #999

  • On Python 3.10 and later, call abc.update_abstractmethods() on dict classes after creation. This improves the detection of abstractness. #1001

  • attrs's pickling methods now use dicts instead of tuples. That is safer and more robust across different versions of a class. #1009

  • Added attrs.validators.not_(wrapped_validator) to logically invert wrapped_validator by accepting only values where wrapped_validator rejects the value with a ValueError or TypeError (by default, exception types configurable). #1010

  • The type stubs for attrs.cmp_using() now have default values. #1027

  • To conform with PEP 681, attr.s() and attrs.define() now accept unsafe_hash in addition to hash. #1065

22.1.0

28 Jul 13:27
22.1.0
Compare
Choose a tag to compare

Highlights

The main features of this release are:

  • The departure of Python 2.7 (enjoy your retirement!),
  • and the arrival of Python 3.11.

We had loftier goals feature-wise, but didn't want to block others embracing Python 3.11.

❤️ Huge thanks to my GitHub sponsors, Tidelift subscribers, and Ko-fi buyers! ❤️

None of my projects would exist in their current form without you!

Full Changelog

Backwards-incompatible Changes

  • Python 2.7 is not supported anymore.

    Dealing with Python 2.7 tooling has become too difficult for a volunteer-run project.

    We have supported Python 2 more than 2 years after it was officially discontinued and feel that we have paid our dues. All version up to 21.4.0 from December 2021 remain fully functional, of course. #936

  • The deprecated cmp attribute of attrs.Attribute has been removed. This does not affect the cmp argument to attr.s that can be used as a shortcut to set eq and order at the same time. #939

Changes

  • Instantiation of frozen slotted classes is now faster. #898
  • If an eq key is defined, it is also used before hashing the attribute. #909
  • Added attrs.validators.min_len(). #916
  • attrs.validators.deep_iterable()'s member_validator argument now also accepts a list of validators and wraps them in an attrs.validators.and_(). #925
  • Added missing type stub re-imports for attrs.converters and attrs.filters. #931
  • Added missing stub for attr(s).cmp_using(). #949
  • attrs.validators._in()'s ValueError is not missing the attribute, expected options, and the value it got anymore. #951
  • Python 3.11 is now officially supported. #969

21.4.0

29 Dec 13:21
21.4.0
Compare
Choose a tag to compare

This is a quick bug fix release for a regression that causes coverage report fail under certain circumstances.

Full Changelog

Changes

  • Fixed the test suite on PyPy3.8 where cloudpickle does not work. #892
  • Fixed coverage report for projects that use attrs and don't set a --source. #895, #896

21.3.0

28 Dec 06:58
21.3.0
Compare
Choose a tag to compare

This is a big release in the history of attrs and finishes an arc that took way too long and also delayed this very overdue release. But it's done: import attrs that has been talked about for years1, but fell victim to “just this one more thing” has finally landed.

From now on, modern attrs code looks like this:

from attrs import define

@define
class HelloWorld:
    modern: bool = True

The define/field APIs have been around for over a year and were very popular, now the rest of the package followed suit. I'm very excited that attrs remains relevant and keeps evolving over now more than half a decade. If you're curious about some of the background, the docs now contain a short explanation and history lesson. As long as our users keep pushing us, we will keep pushing forward class generation in Python!

Big thanks to my GitHub Sponsors, Tidelift subscribers, and Ko-fi buyers that help me mustering the motivation for such long-running project!


Since the release took so long, there's more highlights than we can enumerate here, we'd just like to point out a breaking change in the new APIs: converters now run on setting attributes by default. If this is causing problems to you, you can disable that behavior by setting @define(on_setattr=[]).

Full Changelog

Backward-incompatible Changes

  • When using @define, converters are now run by default when setting an attribute on an instance -- additionally to validators. I.e. the new default is on_setattr=[attrs.setters.convert, attrs.setters.validate].

    This is unfortunately a breaking change, but it was an oversight, impossible to raise a DeprecationWarning about, and it's better to fix it now while the APIs are very fresh with few users. #835, #886

  • import attrs has finally landed! As of this release, you can finally import attrs using its proper name.

    Not all names from the attr namespace have been transferred; most notably attr.s and attr.ib are missing. See attrs.define and attrs.field if you haven't seen our next-generation APIs yet. A more elaborate explanation can be found On The Core API Names

    This feature is at least for one release provisional. We don't plan on changing anything, but such a big change is unlikely to go perfectly on the first strike.

    The API docs have been mostly updated, but it will be an ongoing effort to change everything to the new APIs. Please note that we have not moved -- or even removed -- anything from attr!

    Please do report any bugs or documentation inconsistencies! #887

Changes

  • attr.asdict(retain_collection_types=False) (default) dumps collection-esque keys as tuples. #646, #888
  • __match_args__ are now generated to support Python 3.10's Structural Pattern Matching. This can be controlled by the match_args argument to the class decorators on Python 3.10 and later. On older versions, it is never added and the argument is ignored. #815
  • If the class-level on_setattr is set to attrs.setters.validate (default in @define and @mutable) but no field defines a validator, pretend that it's not set. #817
  • The generated __repr__ is significantly faster on Pythons with f-strings. #819
  • Attributes transformed via field_transformer are wrapped with AttrsClass again. #824
  • Generated source code is now cached more efficiently for identical classes. #828
  • Added attrs.converters.to_bool(). #830
  • attrs.resolve_types() now resolves types of subclasses after the parents are resolved. #842 #843
  • Added new validators: lt(val) (< val), le(va) (≤ val), ge(val) (≥ val), gt(val) (> val), and maxlen(n). #845
  • attrs classes are now fully compatible with cloudpickle (no need to disable repr anymore). #857
  • Added new context manager attrs.validators.disabled() and functions attrs.validators.(set|get)_disabled(). They deprecate attrs.(set|get)_run_validators(). All functions are interoperable and modify the same internal state. They are not – and never were – thread-safe, though. #859
  • attrs.validators.matches_re() now accepts pre-compiled regular expressions in addition to pattern strings. #877
  1. I have an issue from 2018 that I wanted to "come back the moment this lands".

21.2.0

07 May 09:39
21.2.0
Compare
Choose a tag to compare

Yesterday's 21.1.0 has unfortunately two regressions that we're fixing with today's 21.2.0 release:

  • The new recursive mode for attr.evolve() broke some use cases.
  • attrs is not importable under Python 3.4 anymore. While 3.4 hasn't been supported for a while now, we don't want it throw errors after installation.

We've reverted the changes to attr.evolve() and added packaging metadata blocking Python 3.4.

Additionally, we are yanking 21.1.0 from PyPI. If you've pinned attrs to 21.1.0, this does not affect you in any way.

21.1.0

06 May 08:32
21.1.0
Compare
Choose a tag to compare

I am extremely excited to announce the release of attrs 21.1.0.

attrs is the direct ancestor of – and the inspiration for – dataclasses in the standard library and remains the more powerful option for creating regular classes without getting bogged down with writing identical boilerplate again and again: https://www.attrs.org/

Heartfelt thanks go to my generous GitHub sponsors, companies subscribing to attrs on Tidelift, and people who bought me a coffee on Ko-fi! Support like that makes me work on FOSS on a Saturday afternoon – especially when a release drags itself like this one! <3

While this release took a bit longer than I wished for, it comes with many exciting changes. The highlights alone are longer than a usual changelog:

  • The next-generation APIs (@attr.define, @attr.mutable, @attr.frozen, @attr.field) are deemed stable now. The old ones aren't going anywhere, but I encourage you to check the new ones out – they're much nicer!

  • pyright and pylance support: Eric Traut of Microsoft was kind enough to involve me in their work on the dataclass_transforms spec.

    As a result, Microsoft's type checker pyright will work with this attrs release, and so will their Python language server pylance which should be exciting to VS Code users.

    Currently it only supports a subset of attrs's features, but it's the most important ones and more will most likely follow. Some of the limitations are documented in our documentation on type annotations.

  • Customization of field comparison. This is something especially NumPy users have been asking for for a long time: you can now fully customize how a field is compared. We also ship a helper to avoid boilerplate code. So if you'd like to have an object with a NumPy array that compares correctly, this is the way:

    import attr
    import numpy
    
    @attr.define
    class C:
        an_array = attr.field(eq=attr.cmp_using(eq=numpy.array_equal))

    Check out the new documentation on comparison for details.

  • To make it more ergonomic, I've decided to un-deprecate the cmp argument again, so you can customize eq and order in one go. Sorry about the trouble! The cmp attribute remains deprecated.

  • New powerful __init__ helpers:

    1. If attrs deduces you don't want it to write a __init__ for you, it will create an __attrs_init__ instead that you can call from your custom __init__.
    2. If attrs finds a __attrs_pre_init__, it will call it without any arguments before doing any initializations. This is really only useful if you want to run super().__init__(), but that's a use-case people have asked for for years!

    See Hooking Yourself Into Initialization for details.

  • In preparation for the (rescinded) plan to make from __future__ import annotations the default in Python 3.10, attr.resolve_types() can now also be used to resolve types inside of field_transformers.

A Look Ahead

For the next release we've got even bigger plans! By stabilizing the next-generation APIs we can finally go the last step, I've been talking for years (yeah, sorry): import attrs.

attrs's playful APIs (@attr.s, @attr.ib) lost a bit of their charm as the scope of the package grew – especially after the introduction of type annotations.

While the old APIs aren't going anywhere, in the next feature release there will be additionally an attrs package that you can use as an alternative to attr. No more attr.Factory!

The new package gives us the opportunity to rethink the defaults of some functions. So if you have any pet peeves, please air them on #487.

Full Changelog

Deprecations

  • The long-awaited, much-talked-about, little-delivered import attrs is finally upon us!

    Since the NG APIs have now been proclaimed stable, the next release of attrs will allow you to actually import attrs. We're taking this opportunity to replace some defaults in our APIs that made sense in 2015, but don't in 2021.

    So please, if you have any pet peeves about defaults in attrs's APIs, now is the time to air your grievances in #487! We're not gonna get such a chance for a second time, without breaking our backward-compatibility guarantees, or long deprecation cycles. Therefore, speak now or forever hold you peace! #487

  • The cmp argument to attr.s() and attr.ib() has been undeprecated It will continue to be supported as syntactic sugar to set eq and order in one go.

    I'm terribly sorry for the hassle around this argument! The reason we're bringing it back is it's usefulness regarding customization of equality/ordering.

    The cmp attribute and argument on attr.Attribute remains deprecated and will be removed later this year. #773

Changes

  • It's now possible to customize the behavior of eq and order by passing in a callable. #435, #627

  • The instant favorite next-generation APIs are not provisional anymore!

    They are also officially supported by Mypy as of their 0.800 release.

    We hope the next release will already contain an (additional) importable package called attrs. #668, #786

  • If an attribute defines a converter, the type of its parameter is used as type annotation for its corresponding __init__ parameter.

    If an attr.converters.pipe is used, the first one's is used. #710

  • Fixed the creation of an extra slot for an attr.ib when the parent class already has a slot with the same name. #718

  • __attrs__init__() will now be injected if init=False, or if auto_detect=True and a user-defined __init__() exists.

    This enables users to do "pre-init" work in their __init__() (such as super().__init__()).

    __init__() can then delegate constructor argument processing to self.__attrs_init__(*args, **kwargs). #731

  • bool(attr.NOTHING) is now False. #732

  • It's now possible to use super() inside of properties of slotted classes. #747

  • Allow for a __attrs_pre_init__() method that -- if defined -- will get called at the beginning of the attrs-generated __init__() method. #750

  • Added forgotten attr.Attribute.evolve() to type stubs. #752

  • attrs.evolve() now works recursively with nested attrs classes. #759

  • Python 3.10 is now officially supported. #763

  • attr.resolve_types() now takes an optional attrib argument to work inside a field_transformer. #774

  • ClassVars are now also detected if they come from typing-extensions. #782

  • To make it easier to customize attribute comparison (#435), we have added the attr.cmp_with() helper.

    See the new docs on comparison for more details. #787

  • Added provisional support for static typing in pyright via the dataclass_transforms specification. Both the pyright specification and attrs implementation may change in future versions of both projects.

    Your constructive feedback is welcome in both attrs#795 and pyright#1782. #796

20.3.0

05 Nov 10:11
20.3.0
Compare
Choose a tag to compare

Backward-incompatible Changes

  • attr.define(), attr.frozen(), attr.mutable(), and attr.field() remain provisional.

    This release does not change change anything about them and they are already used widely in production though.

    If you wish to use them together with mypy, you can simply drop this plugin into your project.

    Feel free to provide feedback to them in the linked issue #668.

    We will release the attrs namespace once we have the feeling that the APIs have properly settled. #668

Changes

  • attr.s() now has a field_transformer hook that is called for all Attributes and returns a (modified or updated) list of Attribute instances. attr.asdict() has a value_serializer hook that can change the way values are converted. Both hooks are meant to help with data (de-)serialization workflows. #653
  • kw_only=True now works on Python 2. #700
  • raise from now works on frozen classes on PyPy. #703, #712
  • attr.asdict() and attr.astuple() now treat frozensets like sets with regards to the retain_collection_types argument. #704
  • The type stubs for attr.s() and attr.make_class() are not missing the collect_by_mro argument anymore. #711

20.2.0

05 Sep 10:37
20.2.0
Compare
Choose a tag to compare

Backward-incompatible Changes

  • attr.define(), attr.frozen(), attr.mutable(), and attr.field() remain provisional.

    This release fixes a bunch of bugs and ergonomics but they remain mostly unchanged.

    If you wish to use them together with mypy, you can simply drop this plugin into your project.

    Feel free to provide feedback to them in the linked issue #668.

    We will release the attrs namespace once we have the feeling that the APIs have properly settled. #668

Changes

  • attr.define() et al now correct detect __eq__ and __ne__. #671

  • attr.define() et al's hybrid behavior now also works correctly when arguments are passed. #675

  • It's possible to define custom __setattr__ methods on slotted classes again. #681

  • In 20.1.0 we introduced the inherited attribute on the attr.Attribute class to differentiate attributes that have been inherited and those that have been defined directly on the class.

    It has shown to be problematic to involve that attribute when comparing instances of attr.Attribute though, because when sub-classing, attributes from base classes are suddenly not equal to themselves in a super class.

    Therefore the inherited attribute will now be ignored when hashing and comparing instances of attr.Attribute. #684

  • zope.interface is now a "soft dependency" when running the test suite; if zope.interface is not installed when running the test suite, the interface-related tests will be automatically skipped. #685

  • The ergonomics of creating frozen classes using @define(frozen=True) and sub-classing frozen classes has been improved: you don't have to set on_setattr=None anymore. #687