From 50696a36cbda56e64ccb7b2fc1563b372e122600 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Thu, 5 Jul 2018 07:39:24 +0200 Subject: [PATCH 1/4] Add narrative docs for type annotations --- README.rst | 4 +++- docs/index.rst | 7 ++++-- docs/types.rst | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ docs/why.rst | 2 +- 4 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 docs/types.rst diff --git a/README.rst b/README.rst index 28167482d..c1eb7bcc5 100644 --- a/README.rst +++ b/README.rst @@ -40,7 +40,7 @@ For that, it gives you a class decorator and a way to declaratively define the a >>> @attr.s ... class SomeClass(object): ... a_number = attr.ib(default=42) - ... list_of_numbers = attr.ib(default=attr.Factory(list)) + ... list_of_numbers = attr.ib(factory=list) ... ... def hard_math(self, another_number): ... return self.a_number + sum(self.list_of_numbers) * another_number @@ -78,6 +78,8 @@ After *declaring* your attributes ``attrs`` gives you: *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 ``tuple``\ s or `confusingly behaving `_ ``namedtuple``\ s. 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. diff --git a/docs/index.rst b/docs/index.rst index 8544f19b6..18ce36302 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -26,7 +26,7 @@ The next three steps should bring you up and running in no time: - :doc:`examples` will give you a comprehensive tour of ``attrs``'s features. After reading, you will know about our advanced features and how to use them. - Finally :doc:`why` gives you a rundown of potential alternatives and why we think ``attrs`` is superior. - Yes, we've heard about ``namedtuple``\ s! + Yes, we've heard about ``namedtuple``\ s and Data Classes! - If at any point you get confused by some terminology, please check out our :doc:`glossary`. @@ -36,12 +36,14 @@ If you need any help while getting started, feel free to use the ``python-attrs` Day-to-Day Usage ================ -- Once you're comfortable with the concepts, our :doc:`api` contains all information you need to use ``attrs`` to its fullest. +- :doc:`types` help you to write *correct* and *self-documenting* code. + ``attrs`` has first class support for them and even allows you to drop the calls to :func:`attr.ib` on modern Python versions! - Instance initialization is one of ``attrs`` key feature areas. Our goal is to relieve you from writing as much code as possible. :doc:`init` gives you an overview what ``attrs`` has to offer and explains some related philosophies we believe in. - If you want to put objects into sets or use them as keys in dictionaries, they have to be hashable. The simplest way to do that is to use frozen classes, but the topic is more complex than it seems and :doc:`hashing` will give you a primer on what to look out for. +- Once you're comfortable with the concepts, our :doc:`api` contains all information you need to use ``attrs`` to its fullest. - ``attrs`` is built for extension from the ground up. :doc:`extending` will show you the affordances it offers and how to make it a building block of your own projects. @@ -74,6 +76,7 @@ Full Table of Contents overview why examples + types init hashing api diff --git a/docs/types.rst b/docs/types.rst new file mode 100644 index 000000000..3ee268282 --- /dev/null +++ b/docs/types.rst @@ -0,0 +1,58 @@ +Type Annotations +================ + +``attrs`` comes with first class support for type annotations for both Python 3.6 (:pep:`526`) and legacy syntax. + +On Python 3.6 and later, you can even drop the :func:`attr.ib`\ s if you're willing to annotate *all* attributes. +That means that on modern Python versions, the declaration part of the example from the README can be simplified to: + + +.. doctest:: + + >>> import attr + >>> import typing + + >>> @attr.s(auto_attribs=True) + ... class SomeClass: + ... a_number: int = 42 + ... list_of_numbers: typing.List[int] = attr.Factory(list) + + >>> sc = SomeClass(1, [1, 2, 3]) + >>> sc + SomeClass(a_number=1, list_of_numbers=[1, 2, 3]) + >>> attr.fields(SomeClass).a_number.type + + +You can still use :func:`attr.ib` for advanced features, but you don't have to. + +Please note that these types are *only metadata* that can be queried from the class and they aren't used for anything out of the box! + + +mypy +---- + +While having a nice syntax for type metadata is great, it's even greater that `mypy `_ as of 0.570 ships with a dedicated ``attrs`` plugin which allows you to statically check your code. + +Imagine you add another line that tries to instantiate the defined class using ``SomeClass("23")``. +Mypy will catch that error for you: + +.. code-block:: bash + + $ mypy t.py + t.py:12: error: Argument 1 to "SomeClass" has incompatible type "str"; expected "int" + +This happens *without* running your code! + +And it also works with one of the two supported Python 2-style annotations. +To mypy, this code is equivalent:: + + @attr.s + class SomeClass(object): + a_number = attr.ib(default=42) # type: int + list_of_numbers = attr.ib(factory=list, type=typing.List[int]) + +***** + +The addition of static types is certainly one of the most exciting features in the Python ecosystem and helps you writing *correct* and *verified self-documenting* code. + +If you don't know where to start, Carl Meyer gave a great talk on `Type-checked Python in the Real World `_ at PyCon US 2018 that will help you to get started in no time. diff --git a/docs/why.rst b/docs/why.rst index f1bcee3c9..4019aaf05 100644 --- a/docs/why.rst +++ b/docs/why.rst @@ -138,7 +138,7 @@ With ``attrs`` your users won't notice a difference because it creates regular, …Data Classes? -------------- -`PEP 557 `_ added Data Classes to `Python 3.7 `_ that resemble ``attrs`` in many ways. +:pep:`557` added Data Classes to `Python 3.7 `_ that resemble ``attrs`` in many ways. They are the result of the Python community's `wish `_ to have an easier way to write classes in the standard library that doesn't carry the problems of ``namedtuple``\ s. To that end, ``attrs`` and its developers were involved in the PEP process and while we may disagree with some minor decisions that have been made, it's a fine library and if it stops you from abusing ``namedtuple``\ s, they are a huge win. From ad0937139dca68c987a6bb050431008a08178104 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Thu, 5 Jul 2018 11:38:04 +0200 Subject: [PATCH 2/4] Better wording --- docs/types.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/types.rst b/docs/types.rst index 3ee268282..ac22d4d8a 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -43,8 +43,8 @@ Mypy will catch that error for you: This happens *without* running your code! -And it also works with one of the two supported Python 2-style annotations. -To mypy, this code is equivalent:: +And it also works with *both* Python 2-style annotation styles. +To mypy, this code is equivalent to the one above:: @attr.s class SomeClass(object): From 0438a9608c67054eadb82f4042947b9c46bbe826 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Thu, 5 Jul 2018 11:40:02 +0200 Subject: [PATCH 3/4] Use better code-block types --- docs/types.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/types.rst b/docs/types.rst index ac22d4d8a..60db3f352 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -36,7 +36,7 @@ While having a nice syntax for type metadata is great, it's even greater that `m Imagine you add another line that tries to instantiate the defined class using ``SomeClass("23")``. Mypy will catch that error for you: -.. code-block:: bash +.. code-block:: console $ mypy t.py t.py:12: error: Argument 1 to "SomeClass" has incompatible type "str"; expected "int" @@ -44,7 +44,9 @@ Mypy will catch that error for you: This happens *without* running your code! And it also works with *both* Python 2-style annotation styles. -To mypy, this code is equivalent to the one above:: +To mypy, this code is equivalent to the one above: + +.. code-block:: python @attr.s class SomeClass(object): From 236af3591c72a3de19bbf0c20c6fd9052a0115b6 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Thu, 12 Jul 2018 12:33:00 +0200 Subject: [PATCH 4/4] Add newsfragment for #238 that refers to these docs --- changelog.d/238.change.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog.d/238.change.rst diff --git a/changelog.d/238.change.rst b/changelog.d/238.change.rst new file mode 100644 index 000000000..42b9943f7 --- /dev/null +++ b/changelog.d/238.change.rst @@ -0,0 +1,4 @@ +``attrs`` now ships its own `PEP 484 `_ type hints. +Together with `mypy `_'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 `_ about type annotations in ``attrs``.