Python Classes Without Boilerplate

Overview

attrs

Documentation Status CI Status Test Coverage Code style: black

attrs is the Python package that will bring back the joy of writing classes by relieving you from the drudgery of implementing object protocols (aka dunder methods).

Its main goal is to help you to write concise and correct software without slowing down your code.

For that, it gives you a class decorator and a way to declaratively define the attributes on that class:

>>> import attr

>>> @attr.s
... class SomeClass(object):
...     a_number = attr.ib(default=42)
...     list_of_numbers = attr.ib(factory=list)
...
...     def hard_math(self, another_number):
...         return self.a_number + sum(self.list_of_numbers) * another_number


>>> sc = SomeClass(1, [1, 2, 3])
>>> sc
SomeClass(a_number=1, list_of_numbers=[1, 2, 3])

>>> sc.hard_math(3)
19
>>> sc == SomeClass(1, [1, 2, 3])
True
>>> sc != SomeClass(2, [3, 2, 1])
True

>>> attr.asdict(sc)
{'a_number': 1, 'list_of_numbers': [1, 2, 3]}

>>> SomeClass()
SomeClass(a_number=42, list_of_numbers=[])

>>> C = attr.make_class("C", ["a", "b"])
>>> C("foo", "bar")
C(a='foo', b='bar')

After declaring your attributes attrs gives you:

  • a concise and explicit overview of the class's attributes,
  • a nice human-readable __repr__,
  • a complete set of comparison methods (equality and ordering),
  • an initializer,
  • and much more,

without writing dull boilerplate code again and again and without runtime performance penalties.

On Python 3.6 and later, you can often even drop the calls to attr.ib() by using type annotations.

This gives you the power to use actual classes with actual types in your code instead of confusing tuples or confusingly behaving namedtuples. Which in turn encourages you to write small classes that do one thing well. Never again violate the single responsibility principle just because implementing __init__ et al is a painful drag.

Getting Help

Please use the python-attrs tag on StackOverflow to get help.

Answering questions of your fellow developers is also a great way to help the project!

Project Information

attrs is released under the MIT license, its documentation lives at Read the Docs, the code on GitHub, and the latest release on PyPI. It’s rigorously tested on Python 2.7, 3.5+, and PyPy.

We collect information on third-party extensions in our wiki. Feel free to browse and add your own!

If you'd like to contribute to attrs you're most welcome and we've written a little guide to get you started!

attrs for Enterprise

Available as part of the Tidelift Subscription.

The maintainers of attrs and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. Learn more.

Comments
  • Add PEP484 stubs

    Add PEP484 stubs

    Here's a first take at PEP484-compatible pyi stubs. The discussion in #215 has been mostly focused on runtime type checking, so I wanted to make sure that the plan discussed there will work with static analysis as well.

    Here's a quick test of the stubs that I put together to run against mypy:

    from typing import cast
    import attr
    
    @attr.s
    class C(object):
        x : int = attr.ib(convert=int)
        y: str = attr.ib()
    
    c = C('1', 'foo')  # E: Too many arguments for "C"
    c.x = 2
    c.y = 'bar'
    c > C()  # E: Unsupported left operand type for > ("C")
    str(c)
    
    # errors below are correctly detected:
    c.x = 'foo'  # E: Incompatible types in assignment (expression has type "str", variable has type "int")
    c.y = 1  # E: Incompatible types in assignment (expression has type "int", variable has type "str")
    
    D = attr.make_class('D', {'x': cast(attr._CountingAttr, attr.ib())})
    d = D(2)
    # errors below are not correctly detected (mypy knows nothing about D other than that it's a type)
    d.x ='foo'
    

    mypy knows nothing about the dunder methods added by attrs, so cmp methods and __init__ fail. This will have to be solved with a mypy plugin, which I'll look into next. Same goes for attr.make_class though that's likely to be more difficult.

    One important thing to note: in order to ensure that y: str = attr.ib() does not trigger a mypy error, the return type of attr.ib is Any instead of _CountingAttr. A side effect of this is that it complicates passing the result of attr.ib to functions that expect _CountingAttr instances. There are a handful of strategies to solve this:

    1. use typing.cast: ugly and verbose
    2. create, or choose an existing, alias for attr.ib that will return _CountingAttr, to use with functions like make_class.
    3. add another argument (make=True?) whose presence invokes an @overload of attr.ib that returns _CountingAttr
    4. slight variation on 2. create a public alias for _CountingAttr and copy the keyword defaults from attr.ib to _CountingAttr.__init__. In other words, if you use static type checking and are working with a function that expects _CountingAttr instances, use _CountingAttr() directly instead of attr.ib()

    ~~I favor the 3rd solution.~~ After some consideration I favor 4.

    If you're interested in accepting this, the next things to figure out are:

    • how to make tests for this, and run them on travis
    • how best to install the stubs (should they be moved to python-typeshed?)

    Let me know what you think.

    edit added option 4.

    Feature 
    opened by chadrik 142
  • init hooks

    init hooks

    Sometimes I want to write an attr.s class that has some behavior in its constructor. Particularly:

    • I want to validate between/among different related values passed to the c'tor
    • I want to establish a bidirectional relationship to one of the arguments passed to the c'tor (for example: protocol/transport hook-up)
    • I want to do some gross I/O because it's a handle for some external gross I/O object (i.e. I want to open something, for example) (sorry)

    Arguably, many of these things could be expressed as a function, but some of them involve preserving valid structural relationships between the object and its dependencies, so I'd really prefer to be able to use __init__ in some cases.

    I know, I can write my own __init__. I know! But if I do that, I give up not just on the convenience of attr.s writing the __init__ for me, but also the useful behavior of attr.s: i.e. validators, default factories.

    All of my use-cases here would be easily accomplished with either a pre-__init__ and post-__init__ hook. Most of the ones I'm actually interested in would all be achievable with a post-__init__ hook; I think pre-__init__ might be pointless given the availability of convert=.

    This has been discussed already, but some of the other issues (#33 #24 #38 #38 #58) which have raised the possibility of a post-__init__ hook have been somewhat conflated with better support for inheritance. As we all know, inheritance is deprecated and will be removed in python 3.7 so we don't need to focus on that.

    Feature 
    opened by glyph 96
  • Allow user to customize how an attribute is compared (#435)

    Allow user to customize how an attribute is compared (#435)

    Apologies for resubmitting a PR. I had made a mess in my fork and this was a side-effect.

    The idea of this PR is to add a comparator argument to attr.ib( ), in which the user could provide a simple class implementing equality and ordering methods to suit their needs.

    • [x] Added tests for changed code.
    • [ ] New features have been added to our Hypothesis testing strategy.
    • [x] Changes or additions to public APIs are reflected in our type stubs (files ending in .pyi).
      • [ ] ...and used in the stub test file tests/typing_example.py.
    • [ ] Updated documentation for changed code.
      • [ ] New functions/classes have to be added to docs/api.rst by hand.
      • [ ] Changes to the signature of @attr.s() have to be added by hand too.
      • [x] Changed/added classes/methods/functions have appropriate versionadded, versionchanged, or deprecated directives. Find the appropriate next version in our __init__.py file.
    • [ ] Documentation in .rst files is written using semantic newlines.
    • [x] Changes (and possible deprecations) have news fragments in changelog.d.
    Feature 
    opened by botant 79
  • [RFC] First class type support

    [RFC] First class type support

    This is kind of part of #214 but it stands for itself too.

    Something like:

    @attr.s(check_types=True)
    class C:
        x = attr.ib(type=int)
    

    ought to work.

    Regardless of checking the type, the type information has to be accessible somewhere so we can have first-class deserialization support.

    Feature 
    opened by hynek 59
  • [RFC] Inconvenient defaults?

    [RFC] Inconvenient defaults?

    As part of “Projectimport attrs” (see also #408) we get a unique opportunity to change default options/behaviors that grew over the years but couldn't be fixed due to backward-compatibility restrictions.

    The rough plan is create a new, friendlier API on top of @attr.s and attr.ib() that won't go anywhere.

    The following is cast in stone:

    • auto_attribs=True
    • consistent (Python 3-style) hashing behavior on every Python version
    • #428

    The following would be really nice to have:

    • #368 but it might need too much thinking/research

    What else is bugging you?


    One thing that I just can't make up my mind is related to #223: should we make slots=True the default? I’m quite confident that in 99,9% of cases it's the right thing to do and will guide people to write better classes.

    However on the other hand, our approach of rewriting classes breaks in certain scenarios, usually involving metaclasses.

    So the question is, whether we want to tolerate a higher rate of bogus bug reports/help requests or make the default case nicer?

    I welcome your input.


    Finally a controversial idea: we could make import attrs Python 3 only. There isn't much baggage we'd get rid of but there is some and 2020 is less than a year ahead. It would also allow us to embrace enums as part of our API.

    Feature Thinking 
    opened by hynek 53
  • Find better name for attr.dataclass

    Find better name for attr.dataclass

    So attr.dataclass was meant more or less as a joke, but I just caught myself using it too but the name is bad, because it’s confusing. It implies some kind of symmetry or compatibility to DCs which it doesn’t.

    So here’s a nice bike shed, please help me finding a short, memorable, and meaningful color for it!

    Cleanup 
    opened by hynek 52
  • Improve (de)serialization and validation capabilities

    Improve (de)serialization and validation capabilities

    Improve (de)serialization and validation capabilities by adding hooks that can automatically update a class’ attributes and that can change the way how objects are serialized by asdict().

    See: #649

    Pull Request Check List

    This is just a friendly reminder about the most common mistakes. Please make sure that you tick all boxes. But please read our contribution guide at least once, it will save you unnecessary review cycles!

    If an item doesn't apply to your pull request, check it anyway to make it apparent that there's nothing left to do.

    • [x] Added tests for changed code.
    • [x] New features have been added to our Hypothesis testing strategy.
    • [x] Changes or additions to public APIs are reflected in our type stubs (files ending in .pyi).
      • [x] ...and used in the stub test file tests/typing_example.py.
    • [x] Updated documentation for changed code.
      • [x] New functions/classes have to be added to docs/api.rst by hand.
      • [x] Changes to the signature of @attr.s() have to be added by hand too.
      • [x] Changed/added classes/methods/functions have appropriate versionadded, versionchanged, or deprecated directives. Find the appropriate next version in our __init__.py file.
    • [x] Documentation in .rst files is written using semantic newlines.
    • [x] Changes (and possible deprecations) have news fragments in changelog.d.

    If you have any questions to any of the points above, just submit and ask! This checklist is here to help you, not to deter you from contributing!

    opened by sscherfke 39
  • attr.Factory is a little wordy

    attr.Factory is a little wordy

    attrs is a huge net win on an object like this:

    @attr.s
    class Point(object):
        def __init__(self, x, y, z):
            self.x = x
            self.y = y
            self.z = z
    
    import attr
    @attr.s
    class Point(object):
        x = attr.ib()
        y = attr.ib()
        z = attr.ib()
    

    but it's a lot less clear when you have something like this:

    class Cache(object):
        def __init__(self):
            self._stored = []
            self._by_name = {}
            self._by_id = {}
    

    which becomes

    @attr.s
    class Cache(object):
        _stored = attr.ib(default=attr.Factory(list))
        _by_name = attr.ib(default=attr.Factory(dict))
        _by_id = attr.ib(default=attr.Factory(dict))
    

    I think an alias for this behavior, like:

    @attr.s
    class Cache(object):
        _stored = attr.ib(new=list)
        _by_name = attr.ib(new=dict)
        _by_id = attr.ib(new=dict)
    

    could make initializing these types of mutable objects a lot less verbose.

    opened by glyph 39
  • attrs._make should document why it is more restrictive with __eq__ than other comparisons

    attrs._make should document why it is more restrictive with __eq__ than other comparisons

    The type check in __eq__ for generated classes is different than in other comparison methods.

    For all other methods, isinstance(other, self.__class__) is used, which means subclasses will participate in the "happy" branch of the comparison.

    For __eq__ though, other.__class__ is self.__class__ is used, so a trivial subclass will not compare equal, leading to the quite confusing:

    >>> import attr; Parent = attr.make_class("Parent", dict(foo=attr.ib())); Child = type("Child", (Parent,), {}); print (Parent(foo=1) == Parent(foo=1), Parent(foo=1) == Child(foo=1), Parent(foo=1) < Parent(foo=2), Child(foo=1) < Parent(foo=2))
    (True, False, True, True)
    

    This strikes me as a bug (the incongruity), and that __eq__ should use the same check, but even if it isn't, it likely bears mentioning that there's a difference.

    It even seems like dataclasses have even done something oddly similar, maybe just straight copying the code here?

    https://github.com/python-attrs/attrs/commit/d134ce45fc98323576a19f03e39669dce615c4e1 looks like it's the commit that originally made the change (though since then __eq__ now is hand-generated).

    Bug Documentation 
    opened by Julian 38
  • don't use __annotations__

    don't use __annotations__

    https://github.com/python-attrs/attrs/blob/aa501176fac1f12912f863fd2f5c4d1d08410322/src/attr/_make.py#L213 uses __annotations__

    However, it looks as though an upcoming incompatible change to Python will change __annotations__ into a list of strings.

    It seems like the correct public API to use to retrieve the information attrs wants would be get_type_hints.

    As a bonus, fixing this might make #265 easier to implement.

    Cleanup 
    opened by glyph 37
  • Validating, __init__-based assoc.

    Validating, __init__-based assoc.

    Here is an __init__-based assoc implementation that handles private attributes.

    Using the same three classes from #116, I've done benchmark runs for all-public and all-private attributes on the classes. Private attributes add a slight overhead now, the old implementation was unaffected. Underscores in front of class names indicate private attributes were used.

                    A                       B                      C
    --------------------------------------------------------------------------------------
    Old assoc:      10.6  us +- 0.3  us     12.7  us +- 0.2  us    14.8  us +- 0.3  us
    New assoc:       2.07 us +- 0.06 us      5.23 us +- 0.13 us     8.83 us +- 0.28 us
    
    
                    _A                      _B                     _C
    --------------------------------------------------------------------------------------
    Old assoc:      10.5  us +- 0.2  us     12.6  us +- 0.2 us     14.9 us +- 0.3 us
    New assoc:       2.58 us +- 0.07 us      6.16 us +- 0.12 us    10.7 us +- 0.2 us
    

    I'm pretty sure we could handle the users supplying both private and public attribute names (i.e. both setter and init syntax) with a tiny performance cost, but is it worth it? First of all,

    There should be one-- and preferably only one --obvious way to do it.

    second, could there be corner cases? I can't think of any (

    class A:
        _a = attr.ib()
       __a = attr.ib()
    

    isn't one of them, fortunately :) but maybe there are some?

    Doc change and CHANGELOG entry to come when we're satisfied with the implementation.

    opened by Tinche 37
  • Fix object index in API docs by splitting API docs in two

    Fix object index in API docs by splitting API docs in two

    So this has turned into a total nightmare thanks to the different behavior of defining functions (and everything else) using .. autofunction vs .. function and the interaction with .. module xyz (can exist only once, but implies currentmodule).

    I think instead of merging this crap, I'll split api.rst into api.rst and api-attr.rst. That should help overview too.

    closes #1077

    opened by hynek 2
  • Allow to pass a callable for field-level hash

    Allow to pass a callable for field-level hash

    Apparently, being able to customize the hashing of individual fields is something interesting for NumPy users: https://stackoverflow.com/q/74975393/476759


    I guess the idea would be to create a __hash__ that looks like this:

    def __hash__(self):
        return hash((self.a, custom_hash_for_b(self.b)))
    

    while injecting custom_hash_for_b into eval's globals.

    Feature 
    opened by hynek 1
  • Add literal string support to include and exclude filters

    Add literal string support to include and exclude filters

    Summary

    It's a little too cumbersome that helper util filters only accepts type and Attribute as input, if there are too many fields to include or exclude using asdict.

    For example,

    from attrs import asdict, fields
    
    @define
    Class D:
        a = attr.ib()
        b = attr.ib()
        c = attr.ib()
        d = attr.ib()
        e = attr.ib()
        f = attr.ib()
    
    data = D(...)
    fs = fields(D)
    asdict(data, filter=exclude(fs.a, fs.c, fs.f))
    

    This PR adds literal string name of Attribute as input argument to exclude and include which provides a more convenience approach for usages of filter. The following codes are similar to the above:

    from attrs import asdict
    
    @define
    Class D:
        a = attr.ib()
        b = attr.ib()
        c = attr.ib()
        d = attr.ib()
        e = attr.ib()
        f = attr.ib()
    
    data = D(...)
    asdict(data, filter=exclude("a", "c", "f"))
    

    And typo issues of input arguments wouldn't cause extra side effects.

    This's my first PR to attrs, please feel free to inform me where I'm wrong and what I missed. Thanks!

    Pull Request Check List

    • [x] Added tests for changed code. Our CI fails if coverage is not 100%.
    • [ ] New features have been added to our Hypothesis testing strategy.
    • [x] Changes or additions to public APIs are reflected in our type stubs (files ending in .pyi).
      • [x] ...and used in the stub test file tests/typing_example.py.
      • [ ] If they've been added to attr/__init__.pyi, they've also been re-imported in attrs/__init__.pyi.
    • [ ] Updated documentation for changed code.
      • [ ] New functions/classes have to be added to docs/api.rst by hand.
      • [ ] Changes to the signature of @attr.s() have to be added by hand too.
      • [x] Changed/added classes/methods/functions have appropriate versionadded, versionchanged, or deprecated directives. Find the appropriate next version in our __init__.py file.
    • [x] Documentation in .rst files is written using semantic newlines.
    • [x] Changes (and possible deprecations) have news fragments in changelog.d.
    • [x] Consider granting push permissions to the PR branch, so maintainers can fix minor issues themselves without pestering you.
    opened by lqhuang 2
  • Class helper functions - Support for generic classes

    Class helper functions - Support for generic classes

    I noticed that functions like attr.has, attr.fields, attr.fields_dict doesn't work as i expected when i pass in class with generic type defined:

    
    from attr import frozen, evolve, mutable, has, fields, fields_dict, Attribute
    
    T = TypeVar("T")
    
    @frozen
    class A(Generic[T]):
        a: int
        b: T
    
    has(A) => True
    has(A[str]) => False (should be True)
    
    fields(A) => Sequence[Attribute]
    fields(A[str]) => TypeError: Passed object must be a class (should be Sequence[Attribute])
    
    fields_dict(A) => Mapping[str, Attribute]
    fields(A[str]) => TypeError: Passed object must be a class (should be Mapping[str, Attribute])```
    opened by LukasKrocek 1
Releases(22.2.0)
  • 22.2.0(Dec 21, 2022)

    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

    Source code(tar.gz)
    Source code(zip)
  • 22.1.0(Jul 28, 2022)

    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
    Source code(tar.gz)
    Source code(zip)
  • 21.4.0(Dec 29, 2021)

    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
    Source code(tar.gz)
    Source code(zip)
  • 21.3.0(Dec 28, 2021)

    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 years[^issue], 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=[]).

    [^issue]: I have an issue from 2018 that I wanted to "come back the moment this lands".

    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
    Source code(tar.gz)
    Source code(zip)
  • 21.2.0(May 7, 2021)

    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.

    Source code(tar.gz)
    Source code(zip)
  • 21.1.0(May 6, 2021)

    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

    Source code(tar.gz)
    Source code(zip)
  • 20.3.0(Nov 5, 2020)

    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
    Source code(tar.gz)
    Source code(zip)
  • 20.2.0(Sep 5, 2020)

    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

    Source code(tar.gz)
    Source code(zip)
  • 20.1.0(Aug 20, 2020)

    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

    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. #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, #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, #635

    • On Python 3, all generated methods now have a docstring explaining that they have been created by attrs. #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, #513, #642

    • Fixed a ValueError: Cell is empty bug that could happen in some rare edge cases. #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 (e.g. 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

    • 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

    • 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). #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, #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

    • 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

    Source code(tar.gz)
    Source code(zip)
  • 17.3.0.post1(Nov 8, 2017)

Owner
The attrs Cabal
Dedicated to empower Python developers to write better code with less effort.
The attrs Cabal
Tool to generate wrappers for Linux libraries allowing for dlopen()ing them without writing any boilerplate

Dynload wrapper This program will generate a wrapper to make it easy to dlopen() shared objects on Linux without writing a ton of boilerplate code. Th

Hein-Pieter van Braam 25 Oct 24, 2022
Paprika is a python library that reduces boilerplate. Heavily inspired by Project Lombok.

Image courtesy of Anna Quaglia (Photographer) Paprika Paprika is a python library that reduces boilerplate. It is heavily inspired by Project Lombok.

Rayan Hatout 55 Dec 26, 2022
Automate the boilerplate while initializing your Python project

Rubric >> Automate the boilerplate while initializing your Python project << Preface Rubric is an opinionated project initializer for Python. It assum

Redowan Delowar 23 Dec 16, 2022
Izy - Python functions and classes that make python even easier than it is

izy Python functions and classes that make it even easier! You will wonder why t

null 5 Jul 4, 2022
Macros in Python: quasiquotes, case classes, LINQ and more!

MacroPy3 1.1.0b2 MacroPy is an implementation of Syntactic Macros in the Python Programming Language. MacroPy provides a mechanism for user-defined fu

Li Haoyi 3.2k Jan 6, 2023
Simple but maybe too simple config management through python data classes. We use it for machine learning.

??‍✈️ Coqpit Simple, light-weight and no dependency config handling through python data classes with to/from JSON serialization/deserialization. Curre

coqui 67 Nov 29, 2022
Python Interactive Graphical System made during Computer Graphics classes (INE5420-2021.1)

PY-IGS - The PYthon Interactive Graphical System The PY-IGS Installation To install this software you will need these dependencies (with their thevelo

Enzo Coelho Albornoz 4 Dec 3, 2021
This is a backport of the BaseExceptionGroup and ExceptionGroup classes from Python 3.11.

This is a backport of the BaseExceptionGroup and ExceptionGroup classes from Python 3.11. It contains the following: The exceptiongroup.BaseExceptionG

Alex Grönholm 19 Dec 15, 2022
WATTS provides a set of Python classes that can manage simulation workflows for multiple codes where information is exchanged at a coarse level

WATTS (Workflow and Template Toolkit for Simulation) provides a set of Python classes that can manage simulation workflows for multiple codes where information is exchanged at a coarse level.

null 13 Dec 23, 2022
Simple tooling for marking deprecated functions or classes and re-routing to the new successors' instance.

pyDeprecate Simple tooling for marking deprecated functions or classes and re-routing to the new successors' instance

Jirka Borovec 45 Nov 24, 2022
An extended version of the hotkeys demo code using action classes

An extended version of the hotkeys application using action classes. In adafruit's Hotkeys code, a macro is using a series of integers, assumed to be

Neradoc 5 May 1, 2022
Pyrmanent - Make all your classes permanent in a flash 💾

Pyrmanent A base class to make your Python classes permanent in a flash. Features Easy to use. Great compatibility. No database needed. Ask for new fe

Sergio Abad 4 Jan 7, 2022
On this repo, you'll find every codes I made during my NSI classes (informatical courses)

??‍?? ??‍?? school-codes On this repo, you'll find every codes I made during my NSI classes (informatical courses) French for now since this repo is d

EDM 1.15 3 Dec 17, 2022
Simple Python-based web application to allow UGM students to fill their QR presence list without having another device in hand.

Praesentia Praesentia is a simple Python-based web application to allow UGM students to fill their QR presence list without having another device in h

loncat 20 Sep 29, 2022
A practice program to find the LCM i.e Lowest Common Multiplication of two numbers using python without library.

Finding-LCM-using-python-from-scratch Here, I write a practice program to find the LCM i.e Lowest Common Multiplication of two numbers using python wi

Sachin Vinayak Dabhade 4 Sep 24, 2021
Python module to work with Magneto Database directly without using broken Magento 2 core

Python module to work with Magneto Database directly without using broken Magento 2 core

Egor Shitikov 13 Nov 10, 2022
Manually Install Python 2.7 pip without any problem !

Python2.7_install_pip Manually Install Python 2.7 pip without any problem ! Download installPip.py to your system and Run the code using this Command

Ali Jafari 1 Dec 9, 2021
Canim1 - Simple python tool to search for packages without m1 wheels in poetry lockfiles

canim1 Usage Clone the repo. Run poetry install. Then you can use the tool: ❯ po

Korijn van Golen 1 Jan 25, 2022
A python script providing an idea of how a MindSphere application, e.g., a dashboard, can be displayed around the clock without the need of manual re-authentication on enforced session expiration

A python script providing an idea of how a MindSphere application, e.g., a dashboard, can be displayed around the clock without the need of manual re-authentication on enforced session expiration

MindSphere 3 Jun 3, 2022