Skip to content

Commit

Permalink
Prepare for narrative docs (#275)
Browse files Browse the repository at this point in the history
  • Loading branch information
hynek committed Oct 26, 2017
1 parent 3040bda commit 3d3d49b
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 40 deletions.
3 changes: 2 additions & 1 deletion CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://virtualenv.pypa.io/>`_.
Expand Down
7 changes: 7 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,33 @@ 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)
... list_of_numbers = attr.ib(default=attr.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')
Expand Down
4 changes: 2 additions & 2 deletions docs/api.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.. _api:

API
===
API Reference
=============

.. currentmodule:: attr

Expand Down
4 changes: 2 additions & 2 deletions docs/examples.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.. _examples:

Examples
========
``attrs`` by Example
====================


Basics
Expand Down
34 changes: 34 additions & 0 deletions docs/extending.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://en.wikipedia.org/wiki/Domain-specific_language>`_ on top of it.

An example for that is the package `environ_config <https://github.com/hynek/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 <https://www.python.org/dev/peps/pep-0526/>`_ 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
<class 'int'>
>>> attr.fields(C).y.type
<class 'str'>

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
Expand Down
67 changes: 48 additions & 19 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,36 +1,49 @@
==================================
attrs: Classes Without Boilerplate
==================================
======================================
``attrs``: Classes Without Boilerplate
======================================

Release v\ |release| (:doc:`What's new? <changelog>`).

.. include:: ../README.rst
: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 <https://pypi.org/project/attrs/>`_.
The recommended installation method is `pip <https://pip.pypa.io/en/stable/>`_-installing into a `virtualenv <https://hynek.me/articles/virtualenv-lives/>`_:

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 <https://stackoverflow.com/questions/tagged/python-attrs>`_ 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-

Expand All @@ -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
==================
Expand Down
34 changes: 21 additions & 13 deletions docs/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <helpers>` 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.
Expand All @@ -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.
Expand All @@ -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.
11 changes: 8 additions & 3 deletions docs/why.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -244,3 +241,11 @@ You can freely choose which features you want and disable those that you want mo
... return "<SmartClass(a=%d)>" % (self.a,)
>>> SmartClass(1, 2)
<SmartClass(a=1)>

.. 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.

0 comments on commit 3d3d49b

Please sign in to comment.