| # Changelog |
| |
| Versions follow [Calendar Versioning](https://calver.org) with a strict backwards-compatibility policy. |
| |
| The **first number** of the version is the year. |
| The **second number** is incremented with each release, starting at 1 for each year. |
| The **third number** is when we need to start branches for older releases (only for emergencies). |
| |
| You can find our backwards-compatibility policy [here](https://github.com/python-attrs/attrs/blob/main/.github/SECURITY.md). |
| |
| Changes for the upcoming release can be found in the [`changelog.d` directory](https://github.com/python-attrs/attrs/tree/main/changelog.d) in our repository. |
| |
| <!-- |
| Do *NOT* add changelog entries here! |
| |
| This changelog is managed by towncrier and is compiled at release time. |
| |
| See https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md#changelog for details. |
| --> |
| |
| <!-- towncrier release notes start --> |
| |
| ## [26.1.0](https://github.com/python-attrs/attrs/tree/26.1.0) - 2026-03-19 |
| |
| ### Backwards-incompatible Changes |
| |
| - Field aliases are now resolved *before* calling `field_transformer`, so transformers receive fully populated `Attribute` objects with usable `alias` values instead of `None`. |
| The new `Attribute.alias_is_default` flag indicates whether the alias was auto-generated (`True`) or explicitly set by the user (`False`). |
| [#1509](https://github.com/python-attrs/attrs/issues/1509) |
| |
| |
| ### Changes |
| |
| - Fix type annotations for `attrs.validators.optional()`, so it no longer rejects tuples with more than one validator. |
| [#1496](https://github.com/python-attrs/attrs/issues/1496) |
| - The `attrs.validators.disabled()` contextmanager can now be nested. |
| [#1513](https://github.com/python-attrs/attrs/issues/1513) |
| - Frozen classes can set `on_setattr=attrs.setters.NO_OP` in addition to `None`. |
| [#1515](https://github.com/python-attrs/attrs/issues/1515) |
| - It's now possible to pass *attrs* **instances** in addition to *attrs* **classes** to `attrs.fields()`. |
| [#1529](https://github.com/python-attrs/attrs/issues/1529) |
| |
| |
| ## [25.4.0](https://github.com/python-attrs/attrs/tree/25.4.0) - 2025-10-06 |
| |
| ### Backwards-incompatible Changes |
| |
| - Class-level `kw_only=True` behavior is now consistent with `dataclasses`. |
| |
| Previously, a class that sets `kw_only=True` makes all attributes keyword-only, including those from base classes. |
| If an attribute sets `kw_only=False`, that setting is ignored, and it is still made keyword-only. |
| |
| Now, only the attributes defined in that class that doesn't explicitly set `kw_only=False` are made keyword-only. |
| |
| This shouldn't be a problem for most users, unless you have a pattern like this: |
| |
| ```python |
| @attrs.define(kw_only=True) |
| class Base: |
| a: int |
| b: int = attrs.field(default=1, kw_only=False) |
| |
| @attrs.define |
| class Subclass(Base): |
| c: int |
| ``` |
| |
| Here, we have a `kw_only=True` *attrs* class (`Base`) with an attribute that sets `kw_only=False` and has a default (`Base.b`), and then create a subclass (`Subclass`) with required arguments (`Subclass.c`). |
| Previously this would work, since it would make `Base.b` keyword-only, but now this fails since `Base.b` is positional, and we have a required positional argument (`Subclass.c`) following another argument with defaults. |
| [#1457](https://github.com/python-attrs/attrs/issues/1457) |
| |
| |
| ### Changes |
| |
| - Values passed to the `__init__()` method of `attrs` classes are now correctly passed to `__attrs_pre_init__()` instead of their default values (in cases where *kw_only* was not specified). |
| [#1427](https://github.com/python-attrs/attrs/issues/1427) |
| - Added support for Python 3.14 and [PEP 749](https://peps.python.org/pep-0749/). |
| [#1446](https://github.com/python-attrs/attrs/issues/1446), |
| [#1451](https://github.com/python-attrs/attrs/issues/1451) |
| - `attrs.validators.deep_mapping()` now allows to leave out either *key_validator* xor *value_validator*. |
| [#1448](https://github.com/python-attrs/attrs/issues/1448) |
| - `attrs.validators.deep_iterator()` and `attrs.validators.deep_mapping()` now accept lists and tuples for all validators and wrap them into a `attrs.validators.and_()`. |
| [#1449](https://github.com/python-attrs/attrs/issues/1449) |
| - Added a new **experimental** way to inspect classes: |
| |
| `attrs.inspect(cls)` returns the _effective_ class-wide parameters that were used by *attrs* to construct the class. |
| |
| The returned class is the same data structure that *attrs* uses internally to decide how to construct the final class. |
| [#1454](https://github.com/python-attrs/attrs/issues/1454) |
| - Fixed annotations for `attrs.field(converter=...)`. |
| Previously, a `tuple` of converters was only accepted if it had exactly one element. |
| [#1461](https://github.com/python-attrs/attrs/issues/1461) |
| - The performance of `attrs.asdict()` has been improved by 45–260%. |
| [#1463](https://github.com/python-attrs/attrs/issues/1463) |
| - The performance of `attrs.astuple()` has been improved by 49–270%. |
| [#1469](https://github.com/python-attrs/attrs/issues/1469) |
| - The type annotation for `attrs.validators.or_()` now allows for different types of validators. |
| |
| This was only an issue on Pyright. |
| [#1474](https://github.com/python-attrs/attrs/issues/1474) |
| |
| |
| ## [25.3.0](https://github.com/python-attrs/attrs/tree/25.3.0) - 2025-03-13 |
| |
| ### Changes |
| |
| - Restore support for generator-based `field_transformer`s. |
| [#1417](https://github.com/python-attrs/attrs/issues/1417) |
| |
| |
| ## [25.2.0](https://github.com/python-attrs/attrs/tree/25.2.0) - 2025-03-12 |
| |
| ### Changes |
| |
| - Checking mandatory vs non-mandatory attribute order is now performed after the field transformer, since the field transformer may change attributes and/or their order. |
| [#1147](https://github.com/python-attrs/attrs/issues/1147) |
| - `attrs.make_class()` now allows for Unicode class names. |
| [#1406](https://github.com/python-attrs/attrs/issues/1406) |
| - Speed up class creation by 30%-50% by compiling methods only once and using a variety of other techniques. |
| [#1407](https://github.com/python-attrs/attrs/issues/1407) |
| - The error message if an attribute has both an annotation and a type argument will now disclose _what_ attribute seems to be the problem. |
| [#1410](https://github.com/python-attrs/attrs/issues/1410) |
| |
| |
| ## [25.1.0](https://github.com/python-attrs/attrs/tree/25.1.0) - 2025-01-25 |
| |
| ### Changes |
| |
| - This release only ensures correct PyPI licensing metadata. |
| [#1386](https://github.com/python-attrs/attrs/issues/1386) |
| |
| |
| ## [24.3.0](https://github.com/python-attrs/attrs/tree/24.3.0) - 2024-12-16 |
| |
| ### Backwards-incompatible Changes |
| |
| - Python 3.7 has been dropped. |
| [#1340](https://github.com/python-attrs/attrs/issues/1340) |
| |
| |
| ### Changes |
| |
| - Introduce `attrs.NothingType`, for annotating types consistent with `attrs.NOTHING`. |
| [#1358](https://github.com/python-attrs/attrs/issues/1358) |
| - Allow mutating `__suppress_context__` and `__notes__` on frozen exceptions. |
| [#1365](https://github.com/python-attrs/attrs/issues/1365) |
| - `attrs.converters.optional()` works again when taking `attrs.converters.pipe()` or another Converter as its argument. |
| [#1372](https://github.com/python-attrs/attrs/issues/1372) |
| - *attrs* instances now support [`copy.replace()`](https://docs.python.org/3/library/copy.html#copy.replace). |
| [#1383](https://github.com/python-attrs/attrs/issues/1383) |
| - `attrs.validators.instance_of()`'s type hints now allow for union types. |
| For example: `instance_of(str | int)` |
| [#1385](https://github.com/python-attrs/attrs/issues/1385) |
| |
| |
| ## [24.2.0](https://github.com/python-attrs/attrs/tree/24.2.0) - 2024-08-06 |
| |
| ### Deprecations |
| |
| - Given the amount of warnings raised in the broader ecosystem, we've decided to only soft-deprecate the *hash* argument to `@define` / `@attr.s`. |
| Please don't use it in new code, but we don't intend to remove it anymore. |
| [#1330](https://github.com/python-attrs/attrs/issues/1330) |
| |
| |
| ### Changes |
| |
| - `attrs.converters.pipe()` (and its syntactic sugar of passing a list for `attrs.field()`'s / `attr.ib()`'s *converter* argument) works again when passing `attrs.setters.convert` to *on_setattr* (which is default for `attrs.define`). |
| [#1328](https://github.com/python-attrs/attrs/issues/1328) |
| - Restored support for PEP [649](https://peps.python.org/pep-0649/) / [749](https://peps.python.org/pep-0749/)-implementing Pythons -- currently 3.14-dev. |
| [#1329](https://github.com/python-attrs/attrs/issues/1329) |
| |
| |
| ## [24.1.0](https://github.com/python-attrs/attrs/tree/24.1.0) - 2024-08-03 |
| |
| ### Backwards-incompatible Changes |
| |
| - `attrs.evolve()` doesn't accept the *inst* argument as a keyword argument anymore. |
| Pass it as the first positional argument instead. |
| [#1264](https://github.com/python-attrs/attrs/issues/1264) |
| - `attrs.validators.provides()` has been removed. |
| The removed code is available as a [gist](https://gist.github.com/hynek/9eaaaeb659808f3519870dfa16d2b6b2) for convenient copy and pasting. |
| [#1265](https://github.com/python-attrs/attrs/issues/1265) |
| - All packaging metadata except from `__version__` and `__version_info__` has been removed from the `attr` and `attrs` modules (for example, `attrs.__url__`). |
| |
| Please use [`importlib.metadata`](https://docs.python.org/3/library/importlib.metadata.html) or [*importlib-metadata*](https://pypi.org/project/importlib-metadata/) instead. |
| [#1268](https://github.com/python-attrs/attrs/issues/1268) |
| - The generated `__eq__` methods have been sped up significantly by generating a chain of attribute comparisons instead of constructing and comparing tuples. |
| This change arguably makes the behavior more correct, |
| but changes it if an attribute compares equal by identity but not value, like `float('nan')`. |
| [#1310](https://github.com/python-attrs/attrs/issues/1310) |
| |
| |
| ### Deprecations |
| |
| - The *repr_ns* argument to `attr.s` is now deprecated. |
| It was a workaround for nested classes in Python 2 and is pointless in Python 3. |
| [#1263](https://github.com/python-attrs/attrs/issues/1263) |
| - The *hash* argument to `@attr.s`, `@attrs.define`, and `make_class()` is now deprecated in favor of *unsafe_hash*, as defined by PEP 681. |
| [#1323](https://github.com/python-attrs/attrs/issues/1323) |
| |
| |
| ### Changes |
| |
| - Allow original slotted `functools.cached_property` classes to be cleaned by garbage collection. |
| Allow `super()` calls in slotted cached properties. |
| [#1221](https://github.com/python-attrs/attrs/issues/1221) |
| - Our type stubs now use modern type notation and are organized such that VS Code's quick-fix prefers the `attrs` namespace. |
| [#1234](https://github.com/python-attrs/attrs/issues/1234) |
| - Preserve `AttributeError` raised by properties of slotted classes with `functools.cached_properties`. |
| [#1253](https://github.com/python-attrs/attrs/issues/1253) |
| - It is now possible to wrap a converter into an `attrs.Converter` and get the current instance and/or the current field definition passed into the converter callable. |
| |
| Note that this is not supported by any type checker, yet. |
| [#1267](https://github.com/python-attrs/attrs/issues/1267) |
| - `attrs.make_class()` now populates the `__annotations__` dict of the generated class, so that `attrs.resolve_types()` can resolve them. |
| [#1285](https://github.com/python-attrs/attrs/issues/1285) |
| - Added the `attrs.validators.or_()` validator. |
| [#1303](https://github.com/python-attrs/attrs/issues/1303) |
| - The combination of a `__attrs_pre_init__` that takes arguments, a kw-only field, and a default on that field does not crash anymore. |
| [#1319](https://github.com/python-attrs/attrs/issues/1319) |
| - `attrs.validators.in_()` now transforms certain unhashable options to tuples to keep the field hashable. |
| |
| This allows fields that use this validator to be used with, for example, `attrs.filters.include()`. |
| [#1320](https://github.com/python-attrs/attrs/issues/1320) |
| - If a class has an *inherited* method called `__attrs_init_subclass__`, it is now called once the class is done assembling. |
| |
| This is a replacement for Python's `__init_subclass__` and useful for registering classes, and similar. |
| [#1321](https://github.com/python-attrs/attrs/issues/1321) |
| |
| |
| ## [23.2.0](https://github.com/python-attrs/attrs/tree/23.2.0) - 2023-12-31 |
| |
| ### Changes |
| |
| - The type annotation for `attrs.resolve_types()` is now correct. |
| [#1141](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/1158) |
| - Fixed serialization of namedtuple fields using `attrs.asdict/astuple()` with `retain_collection_types=True`. |
| [#1165](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/1187) |
| - Slotted classes now transform `functools.cached_property` decorated methods to support equivalent semantics. |
| [#1200](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/1203) |
| |
| |
| ## [23.1.0](https://github.com/python-attrs/attrs/tree/23.1.0) - 2023-04-16 |
| |
| ### Backwards-incompatible Changes |
| |
| - Python 3.6 has been dropped and packaging switched to static package data using [Hatch](https://hatch.pypa.io/latest/). |
| [#993](https://github.com/python-attrs/attrs/issues/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 development 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](https://github.com/python-attrs/attrs/issues/1120) |
| |
| |
| ### Changes |
| |
| - `attrs.filters.exclude()` and `attrs.filters.include()` now support the passing of attribute names as strings. |
| [#1068](https://github.com/python-attrs/attrs/issues/1068) |
| - `attrs.has()` and `attrs.fields()` now handle generic classes correctly. |
| [#1079](https://github.com/python-attrs/attrs/issues/1079) |
| - Fix frozen exception classes when raised within, for example, `contextlib.contextmanager`, which mutates their `__traceback__` attributes. |
| [#1081](https://github.com/python-attrs/attrs/issues/1081) |
| - `@frozen` now works with type checkers that implement [PEP-681](https://peps.python.org/pep-0681/) (ex. [pyright](https://github.com/microsoft/pyright/)). |
| [#1084](https://github.com/python-attrs/attrs/issues/1084) |
| - Restored ability to unpickle instances pickled before 22.2.0. |
| [#1085](https://github.com/python-attrs/attrs/issues/1085) |
| - `attrs.asdict()`'s and `attrs.astuple()`'s type stubs now accept the `attrs.AttrsInstance` protocol. |
| [#1090](https://github.com/python-attrs/attrs/issues/1090) |
| - Fix slots class cellvar updating closure in CPython 3.8+ even when `__code__` introspection is unavailable. |
| [#1092](https://github.com/python-attrs/attrs/issues/1092) |
| - `attrs.resolve_types()` can now pass `include_extras` to `typing.get_type_hints()` on Python 3.9+, and does so by default. |
| [#1099](https://github.com/python-attrs/attrs/issues/1099) |
| - Added instructions for pull request workflow to `CONTRIBUTING.md`. |
| [#1105](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/1117) |
| - `attrs.validators.optional()` now also accepts a tuple of validators (in addition to lists of validators). |
| [#1122](https://github.com/python-attrs/attrs/issues/1122) |
| |
| |
| ## [22.2.0](https://github.com/python-attrs/attrs/tree/22.2.0) - 2022-12-21 |
| |
| ### Backwards-incompatible Changes |
| |
| - Python 3.5 is not supported anymore. |
| [#988](https://github.com/python-attrs/attrs/issues/988) |
| |
| |
| ### Deprecations |
| |
| - Python 3.6 is now deprecated and support will be removed in the next release. |
| [#1017](https://github.com/python-attrs/attrs/issues/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](https://peps.python.org/pep-0681/#field-specifier-parameters) *alias* option can be use to override private attribute name mangling, or add other arbitrary field argument name overrides. |
| [#950](https://github.com/python-attrs/attrs/issues/950) |
| - `attrs.NOTHING` is now an enum value, making it possible to use with, for example, [`typing.Literal`](https://docs.python.org/3/library/typing.html#typing.Literal). |
| [#983](https://github.com/python-attrs/attrs/issues/983) |
| - Added missing re-import of `attr.AttrsInstance` to the `attrs` namespace. |
| [#987](https://github.com/python-attrs/attrs/issues/987) |
| - Fix slight performance regression in classes with custom `__setattr__` and speedup even more. |
| [#991](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/995) |
| - `attrs.has()` is now a [`TypeGuard`](https://docs.python.org/3/library/typing.html#typing.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](https://github.com/python-attrs/attrs/issues/997) |
| - Made `attrs.AttrsInstance` stub available at runtime and fixed type errors related to the usage of `attrs.AttrsInstance` in Pyright. |
| [#999](https://github.com/python-attrs/attrs/issues/999) |
| - On Python 3.10 and later, call [`abc.update_abstractmethods()`](https://docs.python.org/3/library/abc.html#abc.update_abstractmethods) on dict classes after creation. |
| This improves the detection of abstractness. |
| [#1001](https://github.com/python-attrs/attrs/issues/1001) |
| - *attrs*'s pickling methods now use dicts instead of tuples. |
| That is safer and more robust across different versions of a class. |
| [#1009](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/1010) |
| - The type stubs for `attrs.cmp_using()` now have default values. |
| [#1027](https://github.com/python-attrs/attrs/issues/1027) |
| - To conform with [PEP 681](https://peps.python.org/pep-0681/), `attr.s()` and `attrs.define()` now accept *unsafe_hash* in addition to *hash*. |
| [#1065](https://github.com/python-attrs/attrs/issues/1065) |
| |
| |
| ## [22.1.0](https://github.com/python-attrs/attrs/tree/22.1.0) - 2022-07-28 |
| |
| ### 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](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/939) |
| |
| |
| ### Changes |
| |
| - Instantiation of frozen slotted classes is now faster. |
| [#898](https://github.com/python-attrs/attrs/issues/898) |
| - If an `eq` key is defined, it is also used before hashing the attribute. |
| [#909](https://github.com/python-attrs/attrs/issues/909) |
| - Added `attrs.validators.min_len()`. |
| [#916](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/925) |
| - Added missing type stub re-imports for `attrs.converters` and `attrs.filters`. |
| [#931](https://github.com/python-attrs/attrs/issues/931) |
| - Added missing stub for `attr(s).cmp_using()`. |
| [#949](https://github.com/python-attrs/attrs/issues/949) |
| - `attrs.validators._in()`'s `ValueError` is not missing the attribute, expected options, and the value it got anymore. |
| [#951](https://github.com/python-attrs/attrs/issues/951) |
| - Python 3.11 is now officially supported. |
| [#969](https://github.com/python-attrs/attrs/issues/969) |
| |
| |
| ## [21.4.0](https://github.com/python-attrs/attrs/tree/21.4.0) - 2021-12-29 |
| |
| ### Changes |
| |
| - Fixed the test suite on PyPy3.8 where `cloudpickle` does not work. |
| [#892](https://github.com/python-attrs/attrs/issues/892) |
| - Fixed `coverage report` for projects that use `attrs` and don't set a `--source`. |
| [#895](https://github.com/python-attrs/attrs/issues/895), |
| [#896](https://github.com/python-attrs/attrs/issues/896) |
| |
| |
| ## [21.3.0](https://github.com/python-attrs/attrs/tree/21.3.0) - 2021-12-28 |
| |
| ### Backward-incompatible Changes |
| |
| - When using `@define`, converters are now run by default when setting an attribute on an instance -- additionally to validators. |
| Meaning: 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](https://github.com/python-attrs/attrs/issues/835), |
| [#886](https://github.com/python-attrs/attrs/issues/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](https://www.attrs.org/en/latest/names.html) |
| |
| 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](https://github.com/python-attrs/attrs/issues/887) |
| |
| |
| ### Changes |
| |
| - `attr.asdict(retain_collection_types=False)` (default) dumps collection-esque keys as tuples. |
| [#646](https://github.com/python-attrs/attrs/issues/646), |
| [#888](https://github.com/python-attrs/attrs/issues/888) |
| - `__match_args__` are now generated to support Python 3.10's |
| [Structural Pattern Matching](https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-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](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/817) |
| - The generated `__repr__` is significantly faster on Pythons with f-strings. |
| [#819](https://github.com/python-attrs/attrs/issues/819) |
| - Attributes transformed via `field_transformer` are wrapped with `AttrsClass` again. |
| [#824](https://github.com/python-attrs/attrs/issues/824) |
| - Generated source code is now cached more efficiently for identical classes. |
| [#828](https://github.com/python-attrs/attrs/issues/828) |
| - Added `attrs.converters.to_bool()`. |
| [#830](https://github.com/python-attrs/attrs/issues/830) |
| - `attrs.resolve_types()` now resolves types of subclasses after the parents are resolved. |
| [#842](https://github.com/python-attrs/attrs/issues/842) |
| [#843](https://github.com/python-attrs/attrs/issues/843) |
| - Added new validators: `lt(val)` (\< val), `le(va)` (≤ val), `ge(val)` (≥ val), `gt(val)` (> val), and `maxlen(n)`. |
| [#845](https://github.com/python-attrs/attrs/issues/845) |
| - `attrs` classes are now fully compatible with [cloudpickle](https://github.com/cloudpipe/cloudpickle) (no need to disable `repr` anymore). |
| [#857](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/859) |
| - `attrs.validators.matches_re()` now accepts pre-compiled regular expressions in addition to pattern strings. |
| [#877](https://github.com/python-attrs/attrs/issues/877) |
| |
| --- |
| |
| ## [21.2.0](https://github.com/python-attrs/attrs/tree/21.2.0) - 2021-05-07 |
| |
| ### Backward-incompatible Changes |
| |
| - We had to revert the recursive feature for `attr.evolve()` because it broke some use-cases -- sorry! |
| [#806](https://github.com/python-attrs/attrs/issues/806) |
| - Python 3.4 is now blocked using packaging metadata because `attrs` can't be imported on it anymore. |
| To ensure that 3.4 users can keep installing `attrs` easily, we will [yank](https://pypi.org/help/#yanked) 21.1.0 from PyPI. |
| This has **no** consequences if you pin `attrs` to 21.1.0. |
| [#807](https://github.com/python-attrs/attrs/issues/807) |
| |
| |
| ## [21.1.0](https://github.com/python-attrs/attrs/tree/21.1.0) - 2021-05-06 |
| |
| ### 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](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/773) |
| |
| |
| ### Changes |
| |
| - It's now possible to customize the behavior of `eq` and `order` by passing in a callable. |
| [#435](https://github.com/python-attrs/attrs/issues/435), |
| [#627](https://github.com/python-attrs/attrs/issues/627) |
| |
| - The instant favorite next-generation APIs are not provisional anymore! |
| |
| They are also officially supported by Mypy as of their [0.800 release](https://mypy-lang.blogspot.com/2021/01/mypy-0800-released.html). |
| |
| We hope the next release will already contain an (additional) importable package called `attrs`. |
| [#668](https://github.com/python-attrs/attrs/issues/668), |
| [#786](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/731) |
| |
| - `bool(attr.NOTHING)` is now `False`. |
| [#732](https://github.com/python-attrs/attrs/issues/732) |
| |
| - It's now possible to use `super()` inside of properties of slotted classes. |
| [#747](https://github.com/python-attrs/attrs/issues/747) |
| |
| - Allow for a `__attrs_pre_init__()` method that -- if defined -- will get called at the beginning of the `attrs`-generated `__init__()` method. |
| [#750](https://github.com/python-attrs/attrs/issues/750) |
| |
| - Added forgotten `attr.Attribute.evolve()` to type stubs. |
| [#752](https://github.com/python-attrs/attrs/issues/752) |
| |
| - `attrs.evolve()` now works recursively with nested `attrs` classes. |
| [#759](https://github.com/python-attrs/attrs/issues/759) |
| |
| - Python 3.10 is now officially supported. |
| [#763](https://github.com/python-attrs/attrs/issues/763) |
| |
| - `attr.resolve_types()` now takes an optional *attrib* argument to work inside a `field_transformer`. |
| [#774](https://github.com/python-attrs/attrs/issues/774) |
| |
| - `ClassVar`s are now also detected if they come from [typing-extensions](https://pypi.org/project/typing-extensions/). |
| [#782](https://github.com/python-attrs/attrs/issues/782) |
| |
| - To make it easier to customize attribute comparison (#435), we have added the `attr.cmp_with()` helper. |
| |
| See the [new docs on comparison](https://www.attrs.org/en/stable/comparison.html) for more details. |
| [#787](https://github.com/python-attrs/attrs/issues/787) |
| |
| - Added **provisional** support for static typing in `pyright` via [PEP 681](https://peps.python.org/pep-0681/). |
| Both the `pyright` specification and `attrs` implementation may change in future versions of both projects. |
| |
| Your constructive feedback is welcome in both [attrs#795](https://github.com/python-attrs/attrs/issues/795) and [pyright#1782](https://github.com/microsoft/pyright/discussions/1782). |
| [#796](https://github.com/python-attrs/attrs/issues/796) |
| |
| |
| ## [20.3.0](https://github.com/python-attrs/attrs/tree/20.3.0) - 2020-11-05 |
| |
| ### Backward-incompatible Changes |
| |
| - `attr.define()`, `attr.frozen()`, `attr.mutable()`, and `attr.field()` remain **provisional**. |
| |
| This release does **not** 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](https://gist.github.com/hynek/1e3844d0c99e479e716169034b5fa963#file-attrs_ng_plugin-py) 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](https://github.com/python-attrs/attrs/issues/668) |
| |
| ### Changes |
| |
| - `attr.s()` now has a *field_transformer* hook that is called for all `Attribute`s 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](https://github.com/python-attrs/attrs/issues/653) |
| - `kw_only=True` now works on Python 2. |
| [#700](https://github.com/python-attrs/attrs/issues/700) |
| - `raise from` now works on frozen classes on PyPy. |
| [#703](https://github.com/python-attrs/attrs/issues/703), |
| [#712](https://github.com/python-attrs/attrs/issues/712) |
| - `attr.asdict()` and `attr.astuple()` now treat `frozenset`s like `set`s with regards to the *retain_collection_types* argument. |
| [#704](https://github.com/python-attrs/attrs/issues/704) |
| - The type stubs for `attr.s()` and `attr.make_class()` are not missing the *collect_by_mro* argument anymore. |
| [#711](https://github.com/python-attrs/attrs/issues/711) |
| |
| --- |
| |
| ## [20.2.0](https://github.com/python-attrs/attrs/tree/20.2.0) - 2020-09-05 |
| |
| ### 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](https://gist.github.com/hynek/1e3844d0c99e479e716169034b5fa963#file-attrs_ng_plugin-py) 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](https://github.com/python-attrs/attrs/issues/668) |
| |
| ### Changes |
| |
| - `attr.define()` et al now correctly detect `__eq__` and `__ne__`. |
| [#671](https://github.com/python-attrs/attrs/issues/671) |
| |
| - `attr.define()` et al's hybrid behavior now also works correctly when arguments are passed. |
| [#675](https://github.com/python-attrs/attrs/issues/675) |
| |
| - It's possible to define custom `__setattr__` methods on slotted classes again. |
| [#681](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/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](https://github.com/python-attrs/attrs/issues/687) |
| |
| --- |
| |
| ## [20.1.0](https://github.com/python-attrs/attrs/tree/20.1.0) - 2020-08-20 |
| |
| ### Backward-incompatible Changes |
| |
| - Python 3.4 is not supported anymore. |
| It has been unsupported by the Python core team for a while now, its PyPI downloads are negligible, and our CI provider removed it as a supported option. |
| |
| It's very unlikely that `attrs` will break under 3.4 anytime soon, which is why we do *not* block its installation on Python 3.4. |
| But we don't test it anymore and will block it once someone reports breakage. |
| [#608](https://github.com/python-attrs/attrs/issues/608) |
| |
| ### Deprecations |
| |
| - Less of a deprecation and more of a heads up: the next release of `attrs` will introduce an `attrs` namespace. |
| That means that you'll finally be able to run `import attrs` with new functions that aren't cute abbreviations and that will carry better defaults. |
| |
| This should not break any of your code, because project-local packages have priority before installed ones. |
| If this is a problem for you for some reason, please report it to our bug tracker and we'll figure something out. |
| |
| The old `attr` namespace isn't going anywhere and its defaults are not changing – this is a purely additive measure. |
| Please check out the linked issue for more details. |
| |
| These new APIs have been added *provisionally* as part of #666 so you can try them out today and provide feedback. |
| Learn more in the [API docs](https://www.attrs.org/en/stable/api.html). |
| [#408](https://github.com/python-attrs/attrs/issues/408) |
| |
| ### Changes |
| |
| - Added `attr.resolve_types()`. |
| It ensures that all forward-references and types in string form are resolved into concrete types. |
| |
| You need this only if you need concrete types at runtime. |
| That means that if you only use types for static type checking, you do **not** need this function. |
| [#288](https://github.com/python-attrs/attrs/issues/288), |
| [#302](https://github.com/python-attrs/attrs/issues/302) |
| |
| - Added `@attr.s(collect_by_mro=False)` argument that if set to `True` fixes the collection of attributes from base classes. |
| |
| It's only necessary for certain cases of multiple-inheritance but is kept off for now for backward-compatibility reasons. |
| It will be turned on by default in the future. |
| |
| As a side-effect, `attr.Attribute` now *always* has an `inherited` attribute indicating whether an attribute on a class was directly defined or inherited. |
| [#428](https://github.com/python-attrs/attrs/issues/428), |
| [#635](https://github.com/python-attrs/attrs/issues/635) |
| |
| - On Python 3, all generated methods now have a docstring explaining that they have been created by `attrs`. |
| [#506](https://github.com/python-attrs/attrs/issues/506) |
| |
| - It is now possible to prevent `attrs` from auto-generating the `__setstate__` and `__getstate__` methods that are required for pickling of slotted classes. |
| |
| Either pass `@attr.s(getstate_setstate=False)` or pass `@attr.s(auto_detect=True)` and implement them yourself: |
| if `attrs` finds either of the two methods directly on the decorated class, it assumes implicitly `getstate_setstate=False` (and implements neither). |
| |
| This option works with dict classes but should never be necessary. |
| [#512](https://github.com/python-attrs/attrs/issues/512), |
| [#513](https://github.com/python-attrs/attrs/issues/513), |
| [#642](https://github.com/python-attrs/attrs/issues/642) |
| |
| - Fixed a `ValueError: Cell is empty` bug that could happen in some rare edge cases. |
| [#590](https://github.com/python-attrs/attrs/issues/590) |
| |
| - `attrs` can now automatically detect your own implementations and infer `init=False`, `repr=False`, `eq=False`, `order=False`, and `hash=False` if you set `@attr.s(auto_detect=True)`. |
| `attrs` will ignore inherited methods. |
| If the argument implies more than one method (for example, `eq=True` creates both `__eq__` and `__ne__`), it's enough for *one* of them to exist and `attrs` will create *neither*. |
| |
| This feature requires Python 3. |
| [#607](https://github.com/python-attrs/attrs/issues/607) |
| |
| - Added `attr.converters.pipe()`. |
| The feature allows combining multiple conversion callbacks into one by piping the value through all of them, and retuning the last result. |
| |
| As part of this feature, we had to relax the type information for converter callables. |
| [#618](https://github.com/python-attrs/attrs/issues/618) |
| |
| - Fixed serialization behavior of non-slots classes with `cache_hash=True`. |
| The hash cache will be cleared on operations which make "deep copies" of instances of classes with hash caching, |
| though the cache will not be cleared with shallow copies like those made by `copy.copy()`. |
| |
| Previously, `copy.deepcopy()` or serialization and deserialization with `pickle` would result in an un-initialized object. |
| |
| This change also allows the creation of `cache_hash=True` classes with a custom `__setstate__`, |
| which was previously forbidden ([#494](https://github.com/python-attrs/attrs/issues/494)). |
| [#620](https://github.com/python-attrs/attrs/issues/620) |
| |
| - It is now possible to specify hooks that are called whenever an attribute is set **after** a class has been instantiated. |
| |
| You can pass `on_setattr` both to `@attr.s()` to set the default for all attributes on a class, and to `@attr.ib()` to overwrite it for individual attributes. |
| |
| `attrs` also comes with a new module `attr.setters` that brings helpers that run validators, converters, or allow to freeze a subset of attributes. |
| [#645](https://github.com/python-attrs/attrs/issues/645), |
| [#660](https://github.com/python-attrs/attrs/issues/660) |
| |
| - **Provisional** APIs called `attr.define()`, `attr.mutable()`, and `attr.frozen()` have been added. |
| |
| They are only available on Python 3.6 and later, and call `attr.s()` with different default values. |
| |
| If nothing comes up, they will become the official way for creating classes in 20.2.0 (see above). |
| |
| **Please note** that it may take some time until mypy – and other tools that have dedicated support for `attrs` – recognize these new APIs. |
| Please **do not** open issues on our bug tracker, there is nothing we can do about it. |
| [#666](https://github.com/python-attrs/attrs/issues/666) |
| |
| - We have also provisionally added `attr.field()` that supplants `attr.ib()`. |
| It also requires at least Python 3.6 and is keyword-only. |
| Other than that, it only dropped a few arguments, but changed no defaults. |
| |
| As with `attr.s()`: `attr.ib()` is not going anywhere. |
| [#669](https://github.com/python-attrs/attrs/issues/669) |
| |
| --- |
| |
| ## [19.3.0](https://github.com/python-attrs/attrs/tree/19.3.0) - 2019-10-15 |
| |
| ### Changes |
| |
| - Fixed `auto_attribs` usage when default values cannot be compared directly with `==`, such as `numpy` arrays. |
| [#585](https://github.com/python-attrs/attrs/issues/585) |
| |
| --- |
| |
| ## [19.2.0](https://github.com/python-attrs/attrs/tree/19.2.0) - 2019-10-01 |
| |
| ### Backward-incompatible Changes |
| |
| - Removed deprecated `Attribute` attribute `convert` per scheduled removal on 2019/1. |
| This planned deprecation is tracked in issue [#307](https://github.com/python-attrs/attrs/issues/307). |
| [#504](https://github.com/python-attrs/attrs/issues/504) |
| |
| - `__lt__`, `__le__`, `__gt__`, and `__ge__` do not consider subclasses comparable anymore. |
| |
| This has been deprecated since 18.2.0 and was raising a `DeprecationWarning` for over a year. |
| [#570](https://github.com/python-attrs/attrs/issues/570) |
| |
| ### Deprecations |
| |
| - The `cmp` argument to `attr.s()` and `attr.ib()` is now deprecated. |
| |
| Please use `eq` to add equality methods (`__eq__` and `__ne__`) and `order` to add ordering methods (`__lt__`, `__le__`, `__gt__`, and `__ge__`) instead – just like with [dataclasses](https://docs.python.org/3/library/dataclasses.html). |
| |
| Both are effectively `True` by default but it's enough to set `eq=False` to disable both at once. |
| Passing `eq=False, order=True` explicitly will raise a `ValueError` though. |
| |
| Since this is arguably a deeper backward-compatibility break, it will have an extended deprecation period until 2021-06-01. |
| After that day, the `cmp` argument will be removed. |
| |
| `attr.Attribute` also isn't orderable anymore. |
| [#574](https://github.com/python-attrs/attrs/issues/574) |
| |
| ### Changes |
| |
| - Updated `attr.validators.__all__` to include new validators added in [#425]. |
| [#517](https://github.com/python-attrs/attrs/issues/517) |
| - Slotted classes now use a pure Python mechanism to rewrite the `__class__` cell when rebuilding the class, so `super()` works even on environments where `ctypes` is not installed. |
| [#522](https://github.com/python-attrs/attrs/issues/522) |
| - When collecting attributes using `@attr.s(auto_attribs=True)`, attributes with a default of `None` are now deleted too. |
| [#523](https://github.com/python-attrs/attrs/issues/523), |
| [#556](https://github.com/python-attrs/attrs/issues/556) |
| - Fixed `attr.validators.deep_iterable()` and `attr.validators.deep_mapping()` type stubs. |
| [#533](https://github.com/python-attrs/attrs/issues/533) |
| - `attr.validators.is_callable()` validator now raises an exception `attr.exceptions.NotCallableError`, a subclass of `TypeError`, informing the received value. |
| [#536](https://github.com/python-attrs/attrs/issues/536) |
| - `@attr.s(auto_exc=True)` now generates classes that are hashable by ID, as the documentation always claimed it would. |
| [#543](https://github.com/python-attrs/attrs/issues/543), |
| [#563](https://github.com/python-attrs/attrs/issues/563) |
| - Added `attr.validators.matches_re()` that checks string attributes whether they match a regular expression. |
| [#552](https://github.com/python-attrs/attrs/issues/552) |
| - Keyword-only attributes (`kw_only=True`) and attributes that are excluded from the `attrs`'s `__init__` (`init=False`) now can appear before mandatory attributes. |
| [#559](https://github.com/python-attrs/attrs/issues/559) |
| - The fake filename for generated methods is now more stable. |
| It won't change when you restart the process. |
| [#560](https://github.com/python-attrs/attrs/issues/560) |
| - The value passed to `@attr.ib(repr=…)` can now be either a boolean (as before) or a callable. |
| That callable must return a string and is then used for formatting the attribute by the generated `__repr__()` method. |
| [#568](https://github.com/python-attrs/attrs/issues/568) |
| - Added `attr.__version_info__` that can be used to reliably check the version of `attrs` and write forward- and backward-compatible code. |
| Please check out the [section on deprecated APIs](https://www.attrs.org/en/stable/api-attr.html#deprecated-apis) on how to use it. |
| [#580](https://github.com/python-attrs/attrs/issues/580) |
| |
| > |
| |
| --- |
| |
| ## [19.1.0](https://github.com/python-attrs/attrs/tree/19.1.0) - 2019-03-03 |
| |
| ### Backward-incompatible Changes |
| |
| - Fixed a bug where deserialized objects with `cache_hash=True` could have incorrect hash code values. |
| This change breaks classes with `cache_hash=True` when a custom `__setstate__` is present. |
| An exception will be thrown when applying the `attrs` annotation to such a class. |
| This limitation is tracked in issue [#494](https://github.com/python-attrs/attrs/issues/494). |
| [#482](https://github.com/python-attrs/attrs/issues/482) |
| |
| ### Changes |
| |
| - Add `is_callable`, `deep_iterable`, and `deep_mapping` validators. |
| |
| - `is_callable`: validates that a value is callable |
| - `deep_iterable`: Allows recursion down into an iterable, |
| applying another validator to every member in the iterable |
| as well as applying an optional validator to the iterable itself. |
| - `deep_mapping`: Allows recursion down into the items in a mapping object, |
| applying a key validator and a value validator to the key and value in every item. |
| Also applies an optional validator to the mapping object itself. |
| |
| You can find them in the `attr.validators` package. |
| [#425] |
| |
| - Fixed stub files to prevent errors raised by mypy's `disallow_any_generics = True` option. |
| [#443](https://github.com/python-attrs/attrs/issues/443) |
| |
| - Attributes with `init=False` now can follow after `kw_only=True` attributes. |
| [#450](https://github.com/python-attrs/attrs/issues/450) |
| |
| - `attrs` now has first class support for defining exception classes. |
| |
| If you define a class using `@attr.s(auto_exc=True)` and subclass an exception, the class will behave like a well-behaved exception class including an appropriate `__str__` method, and all attributes additionally available in an `args` attribute. |
| [#500](https://github.com/python-attrs/attrs/issues/500) |
| |
| - Clarified documentation for hashing to warn that hashable objects should be deeply immutable (in their usage, even if this is not enforced). |
| [#503](https://github.com/python-attrs/attrs/issues/503) |
| |
| --- |
| |
| ## [18.2.0](https://github.com/python-attrs/attrs/tree/18.2.0) - 2018-09-01 |
| |
| ### Deprecations |
| |
| - Comparing subclasses using `<`, `>`, `<=`, and `>=` is now deprecated. |
| The docs always claimed that instances are only compared if the types are identical, so this is a first step to conform to the docs. |
| |
| Equality operators (`==` and `!=`) were always strict in this regard. |
| [#394](https://github.com/python-attrs/attrs/issues/394) |
| |
| ### Changes |
| |
| - `attrs` now ships its own [PEP 484](https://peps.python.org/pep-0484/) type hints. |
| Together with [mypy](http://mypy-lang.org)'s `attrs` plugin, you've got all you need for writing statically typed code in both Python 2 and 3! |
| |
| At that occasion, we've also added [narrative docs](https://www.attrs.org/en/stable/types.html) about type annotations in `attrs`. |
| [#238](https://github.com/python-attrs/attrs/issues/238) |
| |
| - Added *kw_only* arguments to `attr.ib` and `attr.s`, and a corresponding *kw_only* attribute to `attr.Attribute`. |
| This change makes it possible to have a generated `__init__` with keyword-only arguments on Python 3, relaxing the required ordering of default and non-default valued attributes. |
| [#281](https://github.com/python-attrs/attrs/issues/281), |
| [#411](https://github.com/python-attrs/attrs/issues/411) |
| |
| - The test suite now runs with `hypothesis.HealthCheck.too_slow` disabled to prevent CI breakage on slower computers. |
| [#364](https://github.com/python-attrs/attrs/issues/364), |
| [#396](https://github.com/python-attrs/attrs/issues/396) |
| |
| - `attr.validators.in_()` now raises a `ValueError` with a useful message even if the options are a string and the value is not a string. |
| [#383](https://github.com/python-attrs/attrs/issues/383) |
| |
| - `attr.asdict()` now properly handles deeply nested lists and dictionaries. |
| [#395](https://github.com/python-attrs/attrs/issues/395) |
| |
| - Added `attr.converters.default_if_none()` that allows to replace `None` values in attributes. |
| For example `attr.ib(converter=default_if_none(""))` replaces `None` by empty strings. |
| [#400](https://github.com/python-attrs/attrs/issues/400), |
| [#414](https://github.com/python-attrs/attrs/issues/414) |
| |
| - Fixed a reference leak where the original class would remain live after being replaced when `slots=True` is set. |
| [#407](https://github.com/python-attrs/attrs/issues/407) |
| |
| - Slotted classes can now be made weakly referenceable by passing `@attr.s(weakref_slot=True)`. |
| [#420](https://github.com/python-attrs/attrs/issues/420) |
| |
| - Added *cache_hash* option to `@attr.s` which causes the hash code to be computed once and stored on the object. |
| [#426](https://github.com/python-attrs/attrs/issues/426) |
| |
| - Attributes can be named `property` and `itemgetter` now. |
| [#430](https://github.com/python-attrs/attrs/issues/430) |
| |
| - It is now possible to override a base class' class variable using only class annotations. |
| [#431](https://github.com/python-attrs/attrs/issues/431) |
| |
| --- |
| |
| ## [18.1.0](https://github.com/python-attrs/attrs/tree/18.1.0) - 2018-05-03 |
| |
| ### Changes |
| |
| - `x=X(); x.cycle = x; repr(x)` will no longer raise a `RecursionError`, and will instead show as `X(x=...)`. |
| |
| [#95](https://github.com/python-attrs/attrs/issues/95) |
| |
| - `attr.ib(factory=f)` is now syntactic sugar for the common case of `attr.ib(default=attr.Factory(f))`. |
| |
| [#178](https://github.com/python-attrs/attrs/issues/178), |
| [#356](https://github.com/python-attrs/attrs/issues/356) |
| |
| - Added `attr.field_dict()` to return an ordered dictionary of `attrs` attributes for a class, whose keys are the attribute names. |
| |
| [#290](https://github.com/python-attrs/attrs/issues/290), |
| [#349](https://github.com/python-attrs/attrs/issues/349) |
| |
| - The order of attributes that are passed into `attr.make_class()` or the *these* argument of `@attr.s()` is now retained if the dictionary is ordered (in other words: `dict` on Python 3.6 and later, `collections.OrderedDict` otherwise). |
| |
| Before, the order was always determined by the order in which the attributes have been defined which may not be desirable when creating classes programmatically. |
| |
| [#300](https://github.com/python-attrs/attrs/issues/300), |
| [#339](https://github.com/python-attrs/attrs/issues/339), |
| [#343](https://github.com/python-attrs/attrs/issues/343) |
| |
| - In slotted classes, `__getstate__` and `__setstate__` now ignore the `__weakref__` attribute. |
| |
| [#311](https://github.com/python-attrs/attrs/issues/311), |
| [#326](https://github.com/python-attrs/attrs/issues/326) |
| |
| - Setting the cell type is now completely best effort. |
| This fixes `attrs` on Jython. |
| |
| We cannot make any guarantees regarding Jython though, because our test suite cannot run due to dependency incompatibilities. |
| |
| [#321](https://github.com/python-attrs/attrs/issues/321), |
| [#334](https://github.com/python-attrs/attrs/issues/334) |
| |
| - If `attr.s` is passed a *these* argument, it will no longer attempt to remove attributes with the same name from the class body. |
| |
| [#322](https://github.com/python-attrs/attrs/issues/322), |
| [#323](https://github.com/python-attrs/attrs/issues/323) |
| |
| - The hash of `attr.NOTHING` is now vegan and faster on 32bit Python builds. |
| |
| [#331](https://github.com/python-attrs/attrs/issues/331), |
| [#332](https://github.com/python-attrs/attrs/issues/332) |
| |
| - The overhead of instantiating frozen dict classes is virtually eliminated. |
| [#336](https://github.com/python-attrs/attrs/issues/336) |
| |
| - Generated `__init__` methods now have an `__annotations__` attribute derived from the types of the fields. |
| |
| [#363](https://github.com/python-attrs/attrs/issues/363) |
| |
| - We have restructured the documentation a bit to account for `attrs`' growth in scope. |
| Instead of putting everything into the [examples](https://www.attrs.org/en/stable/examples.html) page, we have started to extract narrative chapters. |
| |
| So far, we've added chapters on [initialization](https://www.attrs.org/en/stable/init.html) and [hashing](https://www.attrs.org/en/stable/hashing.html). |
| |
| Expect more to come! |
| |
| [#369](https://github.com/python-attrs/attrs/issues/369), |
| [#370](https://github.com/python-attrs/attrs/issues/370) |
| |
| --- |
| |
| ## [17.4.0](https://github.com/python-attrs/attrs/tree/17.4.0) - 2017-12-30 |
| |
| ### Backward-incompatible Changes |
| |
| - The traversal of MROs when using multiple inheritance was backward: |
| If you defined a class `C` that subclasses `A` and `B` like `C(A, B)`, `attrs` would have collected the attributes from `B` *before* those of `A`. |
| |
| This is now fixed and means that in classes that employ multiple inheritance, the output of `__repr__` and the order of positional arguments in `__init__` changes. |
| Because of the nature of this bug, a proper deprecation cycle was unfortunately impossible. |
| |
| Generally speaking, it's advisable to prefer `kwargs`-based initialization anyways – *especially* if you employ multiple inheritance and diamond-shaped hierarchies. |
| |
| [#298](https://github.com/python-attrs/attrs/issues/298), |
| [#299](https://github.com/python-attrs/attrs/issues/299), |
| [#304](https://github.com/python-attrs/attrs/issues/304) |
| |
| - The `__repr__` set by `attrs` no longer produces an `AttributeError` when the instance is missing some of the specified attributes (either through deleting or after using `init=False` on some attributes). |
| |
| This can break code that relied on `repr(attr_cls_instance)` raising `AttributeError` to check if any `attrs`-specified members were unset. |
| |
| If you were using this, you can implement a custom method for checking this: |
| |
| ``` |
| def has_unset_members(self): |
| for field in attr.fields(type(self)): |
| try: |
| getattr(self, field.name) |
| except AttributeError: |
| return True |
| return False |
| ``` |
| |
| [#308](https://github.com/python-attrs/attrs/issues/308) |
| |
| ### Deprecations |
| |
| - The `attr.ib(convert=callable)` option is now deprecated in favor of `attr.ib(converter=callable)`. |
| |
| This is done to achieve consistency with other noun-based arguments like *validator*. |
| |
| *convert* will keep working until at least January 2019 while raising a `DeprecationWarning`. |
| |
| [#307](https://github.com/python-attrs/attrs/issues/307) |
| |
| ### Changes |
| |
| - Generated `__hash__` methods now hash the class type along with the attribute values. |
| Until now the hashes of two classes with the same values were identical which was a bug. |
| |
| The generated method is also *much* faster now. |
| |
| [#261](https://github.com/python-attrs/attrs/issues/261), |
| [#295](https://github.com/python-attrs/attrs/issues/295), |
| [#296](https://github.com/python-attrs/attrs/issues/296) |
| |
| - `attr.ib`’s *metadata* argument now defaults to a unique empty `dict` instance instead of sharing a common empty `dict` for all. |
| The singleton empty `dict` is still enforced. |
| |
| [#280](https://github.com/python-attrs/attrs/issues/280) |
| |
| - `ctypes` is optional now however if it's missing, a bare `super()` will not work in slotted classes. |
| This should only happen in special environments like Google App Engine. |
| |
| [#284](https://github.com/python-attrs/attrs/issues/284), |
| [#286](https://github.com/python-attrs/attrs/issues/286) |
| |
| - The attribute redefinition feature introduced in 17.3.0 now takes into account if an attribute is redefined via multiple inheritance. |
| In that case, the definition that is closer to the base of the class hierarchy wins. |
| |
| [#285](https://github.com/python-attrs/attrs/issues/285), |
| [#287](https://github.com/python-attrs/attrs/issues/287) |
| |
| - Subclasses of `auto_attribs=True` can be empty now. |
| |
| [#291](https://github.com/python-attrs/attrs/issues/291), |
| [#292](https://github.com/python-attrs/attrs/issues/292) |
| |
| - Equality tests are *much* faster now. |
| |
| [#306](https://github.com/python-attrs/attrs/issues/306) |
| |
| - All generated methods now have correct `__module__`, `__name__`, and (on Python 3) `__qualname__` attributes. |
| |
| [#309](https://github.com/python-attrs/attrs/issues/309) |
| |
| --- |
| |
| ## [17.3.0](https://github.com/python-attrs/attrs/tree/17.3.0) - 2017-11-08 |
| |
| ### Backward-incompatible Changes |
| |
| - Attributes are no longer defined on the class body. |
| |
| This means that if you define a class `C` with an attribute `x`, the class will *not* have an attribute `x` for introspection. |
| Instead of `C.x`, use `attr.fields(C).x` or look at `C.__attrs_attrs__`. |
| The old behavior has been deprecated since version 16.1. |
| ([#253](https://github.com/python-attrs/attrs/issues/253)) |
| |
| ### Changes |
| |
| - `super()` and `__class__` now work with slotted classes on Python 3. |
| ([#102](https://github.com/python-attrs/attrs/issues/102), [#226](https://github.com/python-attrs/attrs/issues/226), [#269](https://github.com/python-attrs/attrs/issues/269), [#270](https://github.com/python-attrs/attrs/issues/270), [#272](https://github.com/python-attrs/attrs/issues/272)) |
| |
| - Added *type* argument to `attr.ib()` and corresponding `type` attribute to `attr.Attribute`. |
| |
| This change paves the way for automatic type checking and serialization (though as of this release `attrs` does not make use of it). |
| In Python 3.6 or higher, the value of `attr.Attribute.type` can alternately be set using variable type annotations |
| (see [PEP 526](https://peps.python.org/pep-0526/)). |
| ([#151](https://github.com/python-attrs/attrs/issues/151), [#214](https://github.com/python-attrs/attrs/issues/214), [#215](https://github.com/python-attrs/attrs/issues/215), [#239](https://github.com/python-attrs/attrs/issues/239)) |
| |
| - The combination of `str=True` and `slots=True` now works on Python 2. |
| ([#198](https://github.com/python-attrs/attrs/issues/198)) |
| |
| - `attr.Factory` is hashable again. |
| ([#204](https://github.com/python-attrs/attrs/issues/204)) |
| |
| - Subclasses now can overwrite attribute definitions of their base classes. |
| |
| That means that you can -- for example -- change the default value for an attribute by redefining it. |
| ([#221](https://github.com/python-attrs/attrs/issues/221), [#229](https://github.com/python-attrs/attrs/issues/229)) |
| |
| - Added new option *auto_attribs* to `@attr.s` that allows to collect annotated fields without setting them to `attr.ib()`. |
| |
| Setting a field to an `attr.ib()` is still possible to supply options like validators. |
| Setting it to any other value is treated like it was passed as `attr.ib(default=value)` -- passing an instance of `attr.Factory` also works as expected. |
| ([#262](https://github.com/python-attrs/attrs/issues/262), [#277](https://github.com/python-attrs/attrs/issues/277)) |
| |
| - Instances of classes created using `attr.make_class()` can now be pickled. |
| ([#282](https://github.com/python-attrs/attrs/issues/282)) |
| |
| --- |
| |
| ## [17.2.0](https://github.com/python-attrs/attrs/tree/17.2.0) - 2017-05-24 |
| |
| ### Changes: |
| |
| - Validators are hashable again. |
| Note that validators may become frozen in the future, pending availability of no-overhead frozen classes. |
| [#192](https://github.com/python-attrs/attrs/issues/192) |
| |
| --- |
| |
| ## [17.1.0](https://github.com/python-attrs/attrs/tree/17.1.0) - 2017-05-16 |
| |
| To encourage more participation, the project has also been moved into a [dedicated GitHub organization](https://github.com/python-attrs/) and everyone is most welcome to join! |
| |
| `attrs` also has a logo now! |
| |
| ```{image} https://www.attrs.org/en/latest/_static/attrs_logo.png |
| :alt: attrs logo |
| ``` |
| |
| ### Backward-incompatible Changes: |
| |
| - `attrs` will set the `__hash__()` method to `None` by default now. |
| The way hashes were handled before was in conflict with [Python's specification](https://docs.python.org/3/reference/datamodel.html#object.__hash__). |
| This *may* break some software although this breakage is most likely just surfacing of latent bugs. |
| You can always make `attrs` create the `__hash__()` method using `@attr.s(hash=True)`. |
| See [#136] for the rationale of this change. |
| |
| :::{warning} |
| Please *do not* upgrade blindly and *do* test your software! |
| *Especially* if you use instances as dict keys or put them into sets! |
| ::: |
| |
| - Correspondingly, `attr.ib`'s *hash* argument is `None` by default too and mirrors the *cmp* argument as it should. |
| |
| ### Deprecations: |
| |
| - `attr.assoc()` is now deprecated in favor of `attr.evolve()` and will stop working in 2018. |
| |
| ### Changes: |
| |
| - Fix default hashing behavior. |
| Now *hash* mirrors the value of *cmp* and classes are unhashable by default. |
| [#136] |
| [#142](https://github.com/python-attrs/attrs/issues/142) |
| - Added `attr.evolve()` that, given an instance of an `attrs` class and field changes as keyword arguments, will instantiate a copy of the given instance with the changes applied. |
| `evolve()` replaces `assoc()`, which is now deprecated. |
| `evolve()` is significantly faster than `assoc()`, and requires the class have an initializer that can take the field values as keyword arguments (like `attrs` itself can generate). |
| [#116](https://github.com/python-attrs/attrs/issues/116) |
| [#124](https://github.com/python-attrs/attrs/pull/124) |
| [#135](https://github.com/python-attrs/attrs/pull/135) |
| - `FrozenInstanceError` is now raised when trying to delete an attribute from a frozen class. |
| [#118](https://github.com/python-attrs/attrs/pull/118) |
| - Frozen-ness of classes is now inherited. |
| [#128](https://github.com/python-attrs/attrs/pull/128) |
| - `__attrs_post_init__()` is now run if validation is disabled. |
| [#130](https://github.com/python-attrs/attrs/pull/130) |
| - Added `attr.validators.in_(options)` that, given the allowed `options`, checks whether the attribute value is in it. |
| This can be used to check constants, enums, mappings, etc. |
| [#181](https://github.com/python-attrs/attrs/pull/181) |
| - Added `attr.validators.and_()` that composes multiple validators into one. |
| [#161](https://github.com/python-attrs/attrs/issues/161) |
| - For convenience, the *validator* argument of `@attr.s` now can take a list of validators that are wrapped using `and_()`. |
| [#138](https://github.com/python-attrs/attrs/issues/138) |
| - Accordingly, `attr.validators.optional()` now can take a list of validators too. |
| [#161](https://github.com/python-attrs/attrs/issues/161) |
| - Validators can now be defined conveniently inline by using the attribute as a decorator. |
| Check out the [validator examples](https://www.attrs.org/en/stable/init.html#decorator) to see it in action! |
| [#143](https://github.com/python-attrs/attrs/issues/143) |
| - `attr.Factory()` now has a *takes_self* argument that makes the initializer to pass the partially initialized instance into the factory. |
| In other words you can define attribute defaults based on other attributes. |
| [#165] |
| [#189](https://github.com/python-attrs/attrs/issues/189) |
| - Default factories can now also be defined inline using decorators. |
| They are *always* passed the partially initialized instance. |
| [#165] |
| - Conversion can now be made optional using `attr.converters.optional()`. |
| [#105](https://github.com/python-attrs/attrs/issues/105) |
| [#173](https://github.com/python-attrs/attrs/pull/173) |
| - `attr.make_class()` now accepts the keyword argument `bases` which allows for subclassing. |
| [#152](https://github.com/python-attrs/attrs/pull/152) |
| - Metaclasses are now preserved with `slots=True`. |
| [#155](https://github.com/python-attrs/attrs/pull/155) |
| |
| --- |
| |
| ## [16.3.0](https://github.com/python-attrs/attrs/tree/16.3.0) - 2016-11-24 |
| |
| ### Changes: |
| |
| - Attributes now can have user-defined metadata which greatly improves `attrs`'s extensibility. |
| [#96](https://github.com/python-attrs/attrs/pull/96) |
| |
| - Allow for a `__attrs_post_init__()` method that -- if defined -- will get called at the end of the `attrs`-generated `__init__()` method. |
| [#111](https://github.com/python-attrs/attrs/pull/111) |
| |
| - Added `@attr.s(str=True)` that will optionally create a `__str__()` method that is identical to `__repr__()`. |
| This is mainly useful with `Exception`s and other classes that rely on a useful `__str__()` implementation but overwrite the default one through a poor own one. |
| Default Python class behavior is to use `__repr__()` as `__str__()` anyways. |
| |
| If you tried using `attrs` with `Exception`s and were puzzled by the tracebacks: this option is for you. |
| |
| - `__name__` is no longer overwritten with `__qualname__` for `attr.s(slots=True)` classes. |
| [#99](https://github.com/python-attrs/attrs/issues/99) |
| |
| --- |
| |
| ## [16.2.0](https://github.com/python-attrs/attrs/tree/16.2.0) - 2016-09-17 |
| |
| ### Changes: |
| |
| - Added `attr.astuple()` that -- similarly to `attr.asdict()` -- returns the instance as a tuple. |
| [#77](https://github.com/python-attrs/attrs/issues/77) |
| - Converters now work with frozen classes. |
| [#76](https://github.com/python-attrs/attrs/issues/76) |
| - Instantiation of `attrs` classes with converters is now significantly faster. |
| [#80](https://github.com/python-attrs/attrs/pull/80) |
| - Pickling now works with slotted classes. |
| [#81](https://github.com/python-attrs/attrs/issues/81) |
| - `attr.assoc()` now works with slotted classes. |
| [#84](https://github.com/python-attrs/attrs/issues/84) |
| - The tuple returned by `attr.fields()` now also allows to access the `Attribute` instances by name. |
| Yes, we've subclassed `tuple` so you don't have to! |
| Therefore `attr.fields(C).x` is equivalent to the deprecated `C.x` and works with slotted classes. |
| [#88](https://github.com/python-attrs/attrs/issues/88) |
| |
| --- |
| |
| ## [16.1.0](https://github.com/python-attrs/attrs/tree/16.1.0) - 2016-08-30 |
| |
| ### Backward-incompatible Changes: |
| |
| - All instances where function arguments were called `cl` have been changed to the more Pythonic `cls`. |
| Since it was always the first argument, it's doubtful anyone ever called those function with in the keyword form. |
| If so, sorry for any breakage but there's no practical deprecation path to solve this ugly wart. |
| |
| ### Deprecations: |
| |
| - Accessing `Attribute` instances on class objects is now deprecated and will stop working in 2017. |
| If you need introspection please use the `__attrs_attrs__` attribute or the `attr.fields()` function that carry them too. |
| In the future, the attributes that are defined on the class body and are usually overwritten in your `__init__` method are simply removed after `@attr.s` has been applied. |
| |
| This will remove the confusing error message if you write your own `__init__` and forget to initialize some attribute. |
| Instead you will get a straightforward `AttributeError`. |
| In other words: decorated classes will work more like plain Python classes which was always `attrs`'s goal. |
| |
| - The serious-business aliases `attr.attributes` and `attr.attr` have been deprecated in favor of `attr.attrs` and `attr.attrib` which are much more consistent and frankly obvious in hindsight. |
| They will be purged from documentation immediately but there are no plans to actually remove them. |
| |
| ### Changes: |
| |
| - `attr.asdict()`'s `dict_factory` arguments is now propagated on recursion. |
| [#45](https://github.com/python-attrs/attrs/issues/45) |
| - `attr.asdict()`, `attr.has()` and `attr.fields()` are significantly faster. |
| [#48](https://github.com/python-attrs/attrs/issues/48) |
| [#51](https://github.com/python-attrs/attrs/issues/51) |
| - Add `attr.attrs` and `attr.attrib` as a more consistent aliases for `attr.s` and `attr.ib`. |
| - Add *frozen* option to `attr.s` that will make instances best-effort immutable. |
| [#60](https://github.com/python-attrs/attrs/issues/60) |
| - `attr.asdict()` now takes `retain_collection_types` as an argument. |
| If `True`, it does not convert attributes of type `tuple` or `set` to `list`. |
| [#69](https://github.com/python-attrs/attrs/issues/69) |
| |
| --- |
| |
| ## [16.0.0](https://github.com/python-attrs/attrs/tree/16.0.0) - 2016-05-23 |
| |
| ### Backward-incompatible Changes: |
| |
| - Python 3.3 and 2.6 are no longer supported. |
| They may work by chance but any effort to keep them working has ceased. |
| |
| The last Python 2.6 release was on October 29, 2013 and is no longer supported by the CPython core team. |
| Major Python packages like Django and Twisted dropped Python 2.6 a while ago already. |
| |
| Python 3.3 never had a significant user base and wasn't part of any distribution's LTS release. |
| |
| ### Changes: |
| |
| - `__slots__` have arrived! |
| Classes now can automatically be [slotted](https://docs.python.org/3/reference/datamodel.html#slots)-style (and save your precious memory) just by passing `slots=True`. |
| [#35](https://github.com/python-attrs/attrs/issues/35) |
| - Allow the case of initializing attributes that are set to `init=False`. |
| This allows for clean initializer parameter lists while being able to initialize attributes to default values. |
| [#32](https://github.com/python-attrs/attrs/issues/32) |
| - `attr.asdict()` can now produce arbitrary mappings instead of Python `dict`s when provided with a `dict_factory` argument. |
| [#40](https://github.com/python-attrs/attrs/issues/40) |
| - Multiple performance improvements. |
| |
| --- |
| |
| ## [15.2.0](https://github.com/python-attrs/attrs/tree/15.2.0) - 2015-12-08 |
| |
| ### Changes: |
| |
| - Added a `convert` argument to `attr.ib`, which allows specifying a function to run on arguments. |
| This allows for simple type conversions, for example, with `attr.ib(convert=int)`. |
| [#26](https://github.com/python-attrs/attrs/issues/26) |
| - Speed up object creation when attribute validators are used. |
| [#28](https://github.com/python-attrs/attrs/issues/28) |
| |
| --- |
| |
| ## [15.1.0](https://github.com/python-attrs/attrs/tree/15.1.0) - 2015-08-20 |
| |
| ### Changes: |
| |
| - Added `attr.validators.optional()` that wraps other validators allowing attributes to be `None`. |
| [#16](https://github.com/python-attrs/attrs/issues/16) |
| - Multi-level inheritance now works. |
| [#24](https://github.com/python-attrs/attrs/issues/24) |
| - `__repr__()` now works with non-redecorated subclasses. |
| [#20](https://github.com/python-attrs/attrs/issues/20) |
| |
| --- |
| |
| ## [15.0.0](https://github.com/python-attrs/attrs/tree/15.0.0) - 2015-04-15 |
| |
| ### Changes: |
| |
| Initial release. |
| |
| [#136]: https://github.com/python-attrs/attrs/issues/136 |
| [#165]: https://github.com/python-attrs/attrs/issues/165 |
| [#425]: https://github.com/python-attrs/attrs/issues/425 |