From a4f2115fddc6291b9505d67b7c216c5ecddfde36 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 14 Aug 2020 10:08:32 +0200 Subject: [PATCH 01/11] Add attr.auto/mutable/frozen Prepare for import attrs y'all! Signed-off-by: Hynek Schlawack --- conftest.py | 6 ++- docs/api.rst | 46 ++++++++++++++++++ src/attr/__init__.py | 7 ++- src/attr/_auto.py | 86 ++++++++++++++++++++++++++++++++++ tests/test_auto.py | 108 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 src/attr/_auto.py create mode 100644 tests/test_auto.py diff --git a/conftest.py b/conftest.py index b83f69a5f..3f75ff6c8 100644 --- a/conftest.py +++ b/conftest.py @@ -16,5 +16,9 @@ def pytest_configure(config): collect_ignore = [] if sys.version_info[:2] < (3, 6): collect_ignore.extend( - ["tests/test_annotations.py", "tests/test_init_subclass.py"] + [ + "tests/test_annotations.py", + "tests/test_auto.py", + "tests/test_init_subclass.py", + ] ) diff --git a/docs/api.rst b/docs/api.rst index 18a17c50f..200d16735 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -16,6 +16,12 @@ What follows is the API explanation, if you'd like a more hands-on introduction, Core ---- + +.. warning:: + As of ``attrs`` 20.1.0, it also ships with a bunch of provisional APIs that are intended to become the main way of defining classes in the future. + + Please have a look at :ref:`prov`. + .. autodata:: attr.NOTHING .. autofunction:: attr.s(these=None, repr_ns=None, repr=None, cmp=None, hash=None, init=None, slots=False, frozen=False, weakref_slot=True, str=False, auto_attribs=False, kw_only=False, cache_hash=False, auto_exc=False, eq=None, order=None, auto_detect=False, collect_by_mro=False, getstate_setstate=None, on_setattr=None) @@ -580,6 +586,46 @@ These are helpers that you can use together with `attr.s`'s and `attr.ib`'s ``on N.B. Please use `attr.s`'s *frozen* argument to freeze whole classes; it is more efficient. +.. _prov: + +Provisional APIs +---------------- + +These are Python 3-only, keyword-only, and **provisional** APIs that call `attr.s` with different default values. + +The most notable differences are: + +- automatically detect whether or not *auto_attribs* should be `True` +- *slots=True* (see :term:`slotted classes`) +- *auto_exc=True* +- *auto_detect=True* +- *eq=True*, but *order=False* +- Some options that aren't relevant to Python 3 have been dropped. + +Please note that these are *default* and you're free to override them. + +---- + +Their behavior is scheduled to become part of the upcoming ``import attrs`` that will introduce a new namespace with nicer names and nicer defaults (see `#408 `_ and `#487 `_). + +Therefore your constructive feedback in the linked issues above is strongly encouraged! + +.. note:: + Provisional doesn't mean we will remove it (although it will be deprecated once the final form is released), but that it might change if we receive relevant feedback. + + `attr.s` and `attr.ib` (and their serious business cousins) aren't going anywhere. + The new APIs build on top of them. + +.. autofunction:: attr.auto +.. function:: attr.mutable(same_as_auto) + + Alias for `attr.auto`. + +.. function:: attr.frozen(same_as_auto) + + Behaves the same as `attr.auto` but sets *frozen=True* and *on_setattr=None*. + + Deprecated APIs --------------- diff --git a/src/attr/__init__.py b/src/attr/__init__.py index fa9414ee9..a4a03b3d3 100644 --- a/src/attr/__init__.py +++ b/src/attr/__init__.py @@ -3,6 +3,7 @@ from functools import partial from . import converters, exceptions, filters, setters, validators +from ._compat import PY2 as _PY2 from ._config import get_run_validators, set_run_validators from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types from ._make import ( @@ -39,7 +40,6 @@ ib = attr = attrib dataclass = partial(attrs, auto_attribs=True) # happy Easter ;) - __all__ = [ "Attribute", "Factory", @@ -68,3 +68,8 @@ "validate", "validators", ] + +if not _PY2: + from ._auto import auto, frozen, mutable + + __all__.extend((auto, frozen, mutable)) diff --git a/src/attr/_auto.py b/src/attr/_auto.py new file mode 100644 index 000000000..e9eba688e --- /dev/null +++ b/src/attr/_auto.py @@ -0,0 +1,86 @@ +""" +This is a Python 3-only, keyword-only, and **provisional** API that calls +`attr.s` with different default values. + +Provisional APIs that shall become "import attrs" one glorious day. +""" + +from functools import partial + +from attr.exceptions import UnannotatedAttributeError + +from . import setters +from ._make import attrs + + +def auto( + maybe_cls=None, + *, + these=None, + repr=None, + hash=None, + init=None, + slots=True, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=None, + kw_only=False, + cache_hash=False, + auto_exc=True, + eq=True, + order=False, + auto_detect=True, + getstate_setstate=None, + on_setattr=setters.validate +): + r""" + The only behavioral difference is the handling of the *auto_attribs* + option: + + :param Optional[bool] auto_attribs: If set to `True` or `False`, it behaves + exactly like `attr.s`. If left `None`, `attr.s` will try to guess: + + 1. If all attributes are annotated and no `attr.ib` is found, it assumes + *auto_attribs=True*. + 2. Otherwise it assumes *auto_attribs=False* and tries to collect + `attr.ib`\ s. + + + .. versionadded:: 20.1.0 + """ + + def do_it(auto_attribs): + return attrs( + maybe_cls=maybe_cls, + these=these, + repr=repr, + hash=hash, + init=init, + slots=slots, + frozen=frozen, + weakref_slot=weakref_slot, + str=str, + auto_attribs=auto_attribs, + kw_only=kw_only, + cache_hash=cache_hash, + auto_exc=auto_exc, + eq=eq, + order=order, + auto_detect=auto_detect, + collect_by_mro=True, + getstate_setstate=getstate_setstate, + on_setattr=on_setattr, + ) + + if auto_attribs is not None: + return do_it(auto_attribs) + + try: + return do_it(True) + except UnannotatedAttributeError: + return do_it(False) + + +mutable = auto +frozen = partial(auto, frozen=True, on_setattr=None) diff --git a/tests/test_auto.py b/tests/test_auto.py new file mode 100644 index 000000000..8373b0c61 --- /dev/null +++ b/tests/test_auto.py @@ -0,0 +1,108 @@ +""" +Python 3-only integration tests for the attr.auto provisional API. +""" + +import pytest + +import attr + + +@attr.auto +class C: + x: str + y: int + + +@attr.auto +class E(Exception): + msg: str + other: int + + +class TestAuto: + def test_simple(self): + """ + Instantiation works. + """ + C("1", 2) + + def test_no_slots(self): + """ + slots can be deactivated. + """ + + @attr.auto(slots=False) + class NoSlots: + x: int + + ns = NoSlots(1) + + assert {"x": 1} == getattr(ns, "__dict__") + + def test_validates(self): + """ + Validators at __init__ and __setattr__ work. + """ + + @attr.auto + class Validated: + x: int = attr.ib(validator=attr.validators.instance_of(int)) + + v = Validated(1) + + with pytest.raises(TypeError): + Validated(None) + + with pytest.raises(TypeError): + v.x = "1" + + def test_no_order(self): + """ + Order is off by default but can be added. + """ + with pytest.raises(TypeError): + C("1", 2) < C("2", 3) + + @attr.auto(order=True) + class Ordered: + x: int + + assert Ordered(1) < Ordered(2) + + def test_auto_attrib_detect(self): + """ + auto correctly detects if a class lacks type annotations. + """ + + @attr.auto + class OldSchool: + x = attr.ib() + + assert OldSchool(1) == OldSchool(1) + + def test_exception(self): + """ + Exceptions are detected and correctly handled. + """ + with pytest.raises(E) as ei: + raise E("yolo", 42) + + e = ei.value + + assert ("yolo", 42) == e.args + assert "yolo" == e.msg + assert 42 == e.other + + def test_frozen(self): + """ + attr.frozen freezes classes. + """ + + @attr.frozen + class F: + x: str + + f = F(1) + + with pytest.raises(attr.exceptions.FrozenInstanceError): + f.x = 2 From 95a0fe715cb2613573869a86b5af8adf3ee5b4d3 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 14 Aug 2020 13:42:27 +0200 Subject: [PATCH 02/11] Try combining coverage using 3.8, always --- .github/workflows/main.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d97a8869a..be187e9a1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,9 +35,14 @@ jobs: - name: "Run tox targets for ${{ matrix.python-version }}" run: "python -m tox" + - uses: "actions/setup-python@v2" + with: + python-version: "3.8" + - name: "Combine coverage" run: | set -xe + python -m pip install coverage[toml] python -m coverage combine python -m coverage xml if: "contains(env.USING_COVERAGE, matrix.python-version)" From d5e8c4f490ec80024460c3450c2b369b735abc82 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 14 Aug 2020 14:20:08 +0200 Subject: [PATCH 03/11] Add tests for overriding of auto_attribs --- tests/test_auto.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/test_auto.py b/tests/test_auto.py index 8373b0c61..8b21554b5 100644 --- a/tests/test_auto.py +++ b/tests/test_auto.py @@ -69,7 +69,34 @@ class Ordered: assert Ordered(1) < Ordered(2) - def test_auto_attrib_detect(self): + def test_override_auto_attribs_true(self): + """ + Don't guess if auto_attrib is set explicitly. + + Having an unannotated attr.ib fails. + """ + with pytest.raises(attr.exceptions.UnannotatedAttributeError): + + @attr.auto(auto_attribs=True) + class ThisFails: + x = attr.ib() + y: int + + def test_override_auto_attribs_false(self): + """ + Don't guess if auto_attrib is set explicitly. + + Annotated fields that don't carry an attr.ib are ignored. + """ + + @attr.auto(auto_attribs=False) + class NoFields: + x: int + y: int + + assert NoFields() == NoFields() + + def test_auto_attribs_detect(self): """ auto correctly detects if a class lacks type annotations. """ From 6372905369f8324da39fb6bde1ec4aee082fcf47 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 14 Aug 2020 15:21:23 +0200 Subject: [PATCH 04/11] Clarifications --- changelog.d/408.deprecation.rst | 3 +++ docs/api.rst | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.d/408.deprecation.rst b/changelog.d/408.deprecation.rst index f2e3cbb9c..e1beea7c7 100644 --- a/changelog.d/408.deprecation.rst +++ b/changelog.d/408.deprecation.rst @@ -6,3 +6,6 @@ If this is a problem for you for some reason, please report it to our bug tracke 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 `_. diff --git a/docs/api.rst b/docs/api.rst index 200d16735..ded5d2178 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -600,9 +600,10 @@ The most notable differences are: - *auto_exc=True* - *auto_detect=True* - *eq=True*, but *order=False* +- Validators run when you set an attribute (*on_setattr=attr.setters.validate*). - Some options that aren't relevant to Python 3 have been dropped. -Please note that these are *default* and you're free to override them. +Please note that these are *defaults* and you're free to override them, just like before. ---- From 24374beaf237765b9a9e560e4074413418b05e47 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 14 Aug 2020 15:54:56 +0200 Subject: [PATCH 05/11] Add newsfragment --- changelog.d/666.change.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog.d/666.change.rst diff --git a/changelog.d/666.change.rst b/changelog.d/666.change.rst new file mode 100644 index 000000000..75bdc3a37 --- /dev/null +++ b/changelog.d/666.change.rst @@ -0,0 +1,5 @@ +**Provisional** APIs called ``attr.auto()``, ``attr.mutable()``, and ``attr.frozen()`` have been added. + +They are only available on Python 3 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). From 65e102496f10e945a0f22f5f3c6118ba7f817aaf Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 14 Aug 2020 17:21:58 +0200 Subject: [PATCH 06/11] s/auto/define This is going be the next-gen name anyways, so no need to introduce new names. Signed-off-by: Hynek Schlawack --- changelog.d/666.change.rst | 4 +-- conftest.py | 2 +- docs/api.rst | 8 +++--- src/attr/__init__.py | 9 ++++--- src/attr/{_auto.py => _next_gen.py} | 10 ++++---- tests/{test_auto.py => test_next_gen.py} | 32 ++++++++++++------------ 6 files changed, 33 insertions(+), 32 deletions(-) rename src/attr/{_auto.py => _next_gen.py} (89%) rename tests/{test_auto.py => test_next_gen.py} (83%) diff --git a/changelog.d/666.change.rst b/changelog.d/666.change.rst index 75bdc3a37..cce15f515 100644 --- a/changelog.d/666.change.rst +++ b/changelog.d/666.change.rst @@ -1,5 +1,5 @@ -**Provisional** APIs called ``attr.auto()``, ``attr.mutable()``, and ``attr.frozen()`` have been added. +**Provisional** APIs called ``attr.define()``, ``attr.mutable()``, and ``attr.frozen()`` have been added. -They are only available on Python 3 and call ``attr.s()`` with different default values. +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). diff --git a/conftest.py b/conftest.py index 3f75ff6c8..7d3d1f802 100644 --- a/conftest.py +++ b/conftest.py @@ -18,7 +18,7 @@ def pytest_configure(config): collect_ignore.extend( [ "tests/test_annotations.py", - "tests/test_auto.py", "tests/test_init_subclass.py", + "tests/test_next_gen.py", ] ) diff --git a/docs/api.rst b/docs/api.rst index ded5d2178..3468fe4bb 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -591,7 +591,7 @@ These are helpers that you can use together with `attr.s`'s and `attr.ib`'s ``on Provisional APIs ---------------- -These are Python 3-only, keyword-only, and **provisional** APIs that call `attr.s` with different default values. +These are Python 3.6 and later-only, keyword-only, and **provisional** APIs that call `attr.s` with different default values. The most notable differences are: @@ -617,14 +617,14 @@ Therefore your constructive feedback in the linked issues above is strongly enco `attr.s` and `attr.ib` (and their serious business cousins) aren't going anywhere. The new APIs build on top of them. -.. autofunction:: attr.auto +.. autofunction:: attr.define .. function:: attr.mutable(same_as_auto) - Alias for `attr.auto`. + Alias for `attr.define`. .. function:: attr.frozen(same_as_auto) - Behaves the same as `attr.auto` but sets *frozen=True* and *on_setattr=None*. + Behaves the same as `attr.define` but sets *frozen=True* and *on_setattr=None*. Deprecated APIs diff --git a/src/attr/__init__.py b/src/attr/__init__.py index a4a03b3d3..572daaff9 100644 --- a/src/attr/__init__.py +++ b/src/attr/__init__.py @@ -1,9 +1,10 @@ from __future__ import absolute_import, division, print_function +import sys + from functools import partial from . import converters, exceptions, filters, setters, validators -from ._compat import PY2 as _PY2 from ._config import get_run_validators, set_run_validators from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types from ._make import ( @@ -69,7 +70,7 @@ "validators", ] -if not _PY2: - from ._auto import auto, frozen, mutable +if sys.version_info[:2] >= (3, 6): + from ._next_gen import define, frozen, mutable - __all__.extend((auto, frozen, mutable)) + __all__.extend((define, frozen, mutable)) diff --git a/src/attr/_auto.py b/src/attr/_next_gen.py similarity index 89% rename from src/attr/_auto.py rename to src/attr/_next_gen.py index e9eba688e..8312d2157 100644 --- a/src/attr/_auto.py +++ b/src/attr/_next_gen.py @@ -1,6 +1,6 @@ """ -This is a Python 3-only, keyword-only, and **provisional** API that calls -`attr.s` with different default values. +This is a Python 3.6 and later-only, keyword-only, and **provisional** API that +calls `attr.s` with different default values. Provisional APIs that shall become "import attrs" one glorious day. """ @@ -13,7 +13,7 @@ from ._make import attrs -def auto( +def define( maybe_cls=None, *, these=None, @@ -82,5 +82,5 @@ def do_it(auto_attribs): return do_it(False) -mutable = auto -frozen = partial(auto, frozen=True, on_setattr=None) +mutable = define +frozen = partial(define, frozen=True, on_setattr=None) diff --git a/tests/test_auto.py b/tests/test_next_gen.py similarity index 83% rename from tests/test_auto.py rename to tests/test_next_gen.py index 8b21554b5..c1a85a922 100644 --- a/tests/test_auto.py +++ b/tests/test_next_gen.py @@ -1,5 +1,5 @@ """ -Python 3-only integration tests for the attr.auto provisional API. +Python 3-only integration tests for provisional next generation APIs. """ import pytest @@ -7,19 +7,13 @@ import attr -@attr.auto +@attr.define class C: x: str y: int -@attr.auto -class E(Exception): - msg: str - other: int - - -class TestAuto: +class TestNextGen: def test_simple(self): """ Instantiation works. @@ -31,7 +25,7 @@ def test_no_slots(self): slots can be deactivated. """ - @attr.auto(slots=False) + @attr.define(slots=False) class NoSlots: x: int @@ -44,7 +38,7 @@ def test_validates(self): Validators at __init__ and __setattr__ work. """ - @attr.auto + @attr.define class Validated: x: int = attr.ib(validator=attr.validators.instance_of(int)) @@ -63,7 +57,7 @@ def test_no_order(self): with pytest.raises(TypeError): C("1", 2) < C("2", 3) - @attr.auto(order=True) + @attr.define(order=True) class Ordered: x: int @@ -77,7 +71,7 @@ def test_override_auto_attribs_true(self): """ with pytest.raises(attr.exceptions.UnannotatedAttributeError): - @attr.auto(auto_attribs=True) + @attr.define(auto_attribs=True) class ThisFails: x = attr.ib() y: int @@ -89,7 +83,7 @@ def test_override_auto_attribs_false(self): Annotated fields that don't carry an attr.ib are ignored. """ - @attr.auto(auto_attribs=False) + @attr.define(auto_attribs=False) class NoFields: x: int y: int @@ -98,10 +92,10 @@ class NoFields: def test_auto_attribs_detect(self): """ - auto correctly detects if a class lacks type annotations. + define correctly detects if a class lacks type annotations. """ - @attr.auto + @attr.define class OldSchool: x = attr.ib() @@ -111,6 +105,12 @@ def test_exception(self): """ Exceptions are detected and correctly handled. """ + + @attr.define + class E(Exception): + msg: str + other: int + with pytest.raises(E) as ei: raise E("yolo", 42) From 8a4be45e519c0e947ecb6c9098e230ef34bc3ce6 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Fri, 14 Aug 2020 17:25:20 +0200 Subject: [PATCH 07/11] Missed two --- docs/api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 3468fe4bb..7797e6505 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -618,11 +618,11 @@ Therefore your constructive feedback in the linked issues above is strongly enco The new APIs build on top of them. .. autofunction:: attr.define -.. function:: attr.mutable(same_as_auto) +.. function:: attr.mutable(same_as_define) Alias for `attr.define`. -.. function:: attr.frozen(same_as_auto) +.. function:: attr.frozen(same_as_define) Behaves the same as `attr.define` but sets *frozen=True* and *on_setattr=None*. From 32cd109d12a5282b3841b8cefd44b6a4b3c6095e Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Mon, 17 Aug 2020 11:06:35 +0200 Subject: [PATCH 08/11] Add type stubs --- src/attr/__init__.pyi | 47 +++++++++++++++++++++++++++++++++++++++++ tests/typing_example.py | 27 +++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/src/attr/__init__.pyi b/src/attr/__init__.pyi index 5f3b55344..c647451dd 100644 --- a/src/attr/__init__.pyi +++ b/src/attr/__init__.pyi @@ -227,6 +227,53 @@ def attrs( getstate_setstate: Optional[bool] = ..., on_setattr: Optional[_OnSetAttrArgType] = ..., ) -> Callable[[_C], _C]: ... +@overload +def define( + maybe_cls: _C, + *, + these: Optional[Dict[str, Any]] = ..., + repr: bool = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + auto_detect: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> _C: ... +@overload +def define( + maybe_cls: None = ..., + *, + these: Optional[Dict[str, Any]] = ..., + repr: bool = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + auto_detect: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> Callable[[_C], _C]: ... + +mutable = define +frozen = define # XXX: not quite but good enough for now # TODO: add support for returning NamedTuple from the mypy plugin class _Fields(Tuple[Attribute[Any], ...]): diff --git a/tests/typing_example.py b/tests/typing_example.py index 01c061049..1f7854a2c 100644 --- a/tests/typing_example.py +++ b/tests/typing_example.py @@ -207,3 +207,30 @@ class ValidatedSetter: # def __init__(self, x: int): # self.x = x + +# Provisional APIs +@attr.define(order=True) +class NGClass: + x: int + + +# XXX: needs support in mypy +# ngc = NGClass(1) + + +@attr.mutable(slots=False) +class NGClass2: + x: int + + +# XXX: needs support in mypy +# ngc2 = NGClass2(1) + + +@attr.frozen(str=True) +class NGFrozen: + x: int + + +# XXX: needs support in mypy +# ngf = NGFrozen(1) From f94afb6a9c73cc4e394ba2061e011d1e167c653c Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Mon, 17 Aug 2020 11:11:41 +0200 Subject: [PATCH 09/11] Beg for patience --- changelog.d/666.change.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog.d/666.change.rst b/changelog.d/666.change.rst index cce15f515..747612b08 100644 --- a/changelog.d/666.change.rst +++ b/changelog.d/666.change.rst @@ -3,3 +3,6 @@ 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. From 53f4ea5b353071d9a86363d93399c2ff5203bd0f Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Mon, 17 Aug 2020 11:22:28 +0200 Subject: [PATCH 10/11] Explain second setup-python action --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index be187e9a1..07220075e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,6 +35,8 @@ jobs: - name: "Run tox targets for ${{ matrix.python-version }}" run: "python -m tox" + # We always use a modern Python version for combining coverage to prevent + # parsing errors in older versions for modern code. - uses: "actions/setup-python@v2" with: python-version: "3.8" From ac58b0c92a6d195d06f8c9423f4b6e8f225ac340 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Mon, 17 Aug 2020 16:13:25 +0200 Subject: [PATCH 11/11] fix comment --- src/attr/__init__.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attr/__init__.pyi b/src/attr/__init__.pyi index c647451dd..f3d6bb669 100644 --- a/src/attr/__init__.pyi +++ b/src/attr/__init__.pyi @@ -273,7 +273,7 @@ def define( ) -> Callable[[_C], _C]: ... mutable = define -frozen = define # XXX: not quite but good enough for now +frozen = define # they differ only in their defaults # TODO: add support for returning NamedTuple from the mypy plugin class _Fields(Tuple[Attribute[Any], ...]):