From 3d3d49b4dde48208af666c75550167adc707d16b Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Thu, 26 Oct 2017 17:55:45 +0200 Subject: [PATCH] Prepare for narrative docs (#275) --- CONTRIBUTING.rst | 3 ++- README.rst | 7 +++++ docs/api.rst | 4 +-- docs/examples.rst | 4 +-- docs/extending.rst | 34 +++++++++++++++++++++++ docs/index.rst | 67 +++++++++++++++++++++++++++++++++------------- docs/overview.rst | 34 ++++++++++++++--------- docs/why.rst | 11 +++++--- 8 files changed, 124 insertions(+), 40 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index ea5d16b16..9822f8926 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -144,7 +144,8 @@ or: Local Development Environment ----------------------------- -You can (and should) run our test suite using tox_ however you’ll probably want a more traditional environment too. +You can (and should) run our test suite using tox_. +However you’ll probably want a more traditional environment too. We highly recommend to develop using the latest Python 3 release because ``attrs`` tries to take advantage of modern features whenever possible. First create a `virtual environment `_. diff --git a/README.rst b/README.rst index 8a7348347..8d5ee6db1 100644 --- a/README.rst +++ b/README.rst @@ -32,6 +32,7 @@ For that, it gives you a class decorator and a way to declaratively define the a .. code-block:: pycon >>> import attr + >>> @attr.s ... class SomeClass(object): ... a_number = attr.ib(default=42) @@ -39,19 +40,25 @@ For that, it gives you a class decorator and a way to declaratively define the a ... ... 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') diff --git a/docs/api.rst b/docs/api.rst index 67275bb38..6144ab9d1 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,7 +1,7 @@ .. _api: -API -=== +API Reference +============= .. currentmodule:: attr diff --git a/docs/examples.rst b/docs/examples.rst index af87c036e..885b368fc 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -1,7 +1,7 @@ .. _examples: -Examples -======== +``attrs`` by Example +==================== Basics diff --git a/docs/extending.rst b/docs/extending.rst index ba612384d..d460ee9b8 100644 --- a/docs/extending.rst +++ b/docs/extending.rst @@ -37,6 +37,40 @@ So it is fairly simple to build your own decorators on top of ``attrs``: f = a(b(original_f)) + +Wrapping the Decorator +---------------------- + +A more elegant way can be to wrap ``attrs`` altogether and build a class `DSL `_ on top of it. + +An example for that is the package `environ_config `_ that uses ``attrs`` under the hood to define environment-based configurations declaratively without exposing ``attrs`` APIs at all. + + +Types +----- + +``attrs`` offers two ways of attaching type information to attributes: + +- `PEP 526 `_ annotations on Python 3.6 and later, +- and the *type* argument to :func:`attr.ib`. + +This information is available to you: + +.. doctest:: + + >>> import attr + >>> @attr.s + ... class C(object): + ... x: int = attr.ib() + ... y = attr.ib(type=str) + >>> attr.fields(C).x.type + + >>> attr.fields(C).y.type + + +Currently, ``attrs`` doesn't do anything with this information but it's very useful if you'd like to write your own validators or serializers! + + .. _extending_metadata: Metadata diff --git a/docs/index.rst b/docs/index.rst index af5859ed2..ec5bf2043 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,6 +1,6 @@ -================================== -attrs: Classes Without Boilerplate -================================== +====================================== +``attrs``: Classes Without Boilerplate +====================================== Release v\ |release| (:doc:`What's new? `). @@ -8,29 +8,42 @@ Release v\ |release| (:doc:`What's new? `). :start-after: teaser-begin :end-before: -spiel-end- -If you want to know how this looks like, jump right into :doc:`overview`. -If you really want to see ``attrs`` in action, :doc:`examples` will give you a comprehensive rundown of its features. -If you're skeptical and want to know how it works first, check out ":doc:`how-does-it-work`". -.. include:: ../README.rst - :start-after: -testimonials- - :end-before: -end- +Getting Started +=============== +``attrs`` is a Python-only package `hosted on PyPI `_. +The recommended installation method is `pip `_-installing into a `virtualenv `_: -User's Guide -============ +.. code-block:: console -.. toctree:: - :maxdepth: 2 + $ pip install attrs - overview - why - examples - api - extending - how-does-it-work +The next three steps should bring you up and running in no time: + +- :doc:`overview` will show you a simple example of ``attrs`` in action and introduce you to its philosophy. + Afterwards, you can start writing your own classes, understand what drives ``attrs``'s design, and know what ``@attr.s`` and ``attr.ib()`` stand for. +- :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 ``nametuple``\ s! + + +If you need any help while getting started, feel free to use the ``python-attrs`` tag on `StackOverflow `_ and someone will surely help you out! + + +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. +- ``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. +.. include:: ../README.rst + :start-after: -testimonials- + :end-before: -end- + .. include:: ../README.rst :start-after: -project-information- @@ -43,6 +56,22 @@ User's Guide changelog +---- + + +Full Table of Contents +====================== + +.. toctree:: + :maxdepth: 2 + + overview + why + examples + api + extending + how-does-it-work + Indices and tables ================== diff --git a/docs/overview.rst b/docs/overview.rst index 1dcc473cb..47b022ff3 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -14,27 +14,27 @@ In order to fulfill its ambitious goal of bringing back the joy to writing class Philosophy ========== -It's about regular classes. - ``attrs`` for creating well-behaved classes with a type, attributes, methods, and everything that comes with a class. +**It's about regular classes.** + ``attrs`` is for creating well-behaved classes with a type, attributes, methods, and everything that comes with a class. It can be used for data-only containers like ``namedtuple``\ s or ``types.SimpleNamespace`` but they're just a sub-genre of what ``attrs`` is good for. -The class belongs to the users. +**The class belongs to the users.** You define a class and ``attrs`` adds static methods to that class based on the attributes you declare. The end. - It doesn't add meta classes. + It doesn't add metaclasses. It doesn't add classes you've never heard of to your inheritance tree. An ``attrs`` class in runtime is indistiguishable from a regular class: because it *is* a regular class with a few boilerplate-y methods attached. -Be light on API impact. +**Be light on API impact.** As convenient as it seems at first, ``attrs`` will *not* tack on any methods to your classes save the dunder ones. Hence all the useful :ref:`tools ` that come with ``attrs`` live in functions that operate on top of instances. Since they take an ``attrs`` instance as their first argument, you can attach them to your classes with one line of code. -Performance matters. +**Performance matters.** ``attrs`` runtime impact is very close to zero because all the work is done when the class is defined. Once you're instantiating it, ``attrs`` is out of the picture completely. -No surprises. +**No surprises.** ``attrs`` creates classes that arguably work the way a Python beginner would reasonably expect them to work. It doesn't try to guess what you mean because explicit is better than implicit. It doesn't try to be clever because software shouldn't be clever. @@ -47,7 +47,12 @@ What ``attrs`` Is Not ``attrs`` does *not* invent some kind of magic system that pulls classes out of its hat using meta classes, runtime introspection, and shaky interdependencies. -All ``attrs`` does is take your declaration, write dunder methods based on that information, and attach them to your class. +All ``attrs`` does is: + +1. take your declaration, +2. write dunder methods based on that information, +3. and attach them to your class. + It does *nothing* dynamic at runtime, hence zero runtime overhead. It's still *your* class. Do with it as you please. @@ -70,10 +75,13 @@ Therefore, the following class definition is identical to the previous one: >>> from attr import attrs, attrib, Factory >>> @attrs - ... class C(object): - ... x = attrib(default=42) - ... y = attrib(default=Factory(list)) - >>> C() - C(x=42, y=[]) + ... class SomeClass(object): + ... a_number = attrib(default=42) + ... list_of_numbers = attrib(default=Factory(list)) + ... + ... def hard_math(self, another_number): + ... return self.a_number + sum(self.list_of_numbers) * another_number + >>> SomeClass(1, [1, 2, 3]) + SomeClass(a_number=1, list_of_numbers=[1, 2, 3]) Use whichever variant fits your taste better. diff --git a/docs/why.rst b/docs/why.rst index 9266a988c..9c64cb93c 100644 --- a/docs/why.rst +++ b/docs/why.rst @@ -227,9 +227,6 @@ which is quite a mouthful and it doesn't even use any of ``attrs``'s more advanc Also: no tests whatsoever. And who will guarantee you, that you don't accidentally flip the ``<`` in your tenth implementation of ``__gt__``? -If you don't care and like typing, we're not gonna stop you. -But if you ever get sick of the repetitiveness, ``attrs`` will be waiting for you. - It also should be noted that ``attrs`` is not an all-or-nothing solution. You can freely choose which features you want and disable those that you want more control over: @@ -244,3 +241,11 @@ You can freely choose which features you want and disable those that you want mo ... return "" % (self.a,) >>> SmartClass(1, 2) + +.. admonition:: Summary + + If you don't care and like typing, we're not gonna stop you. + + However it takes a lot of bias and determined rationalization to claim that ``attrs`` raises the mental burden on a project given how difficult it is to find the important bits in a hand-written class and how annoying it is to ensure you've copy-pasted your code correctly over all your classes. + + In any case, if you ever get sick of the repetitiveness and drowning important code in a sea of boilerplate, ``attrs`` will be waiting for you.