Skip to content

Commit

Permalink
Documentation overhaul
Browse files Browse the repository at this point in the history
  • Loading branch information
hynek committed Aug 15, 2016
1 parent 65f3f4a commit 395aa07
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 25 deletions.
43 changes: 39 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,55 @@ attrs: Attributes without boilerplate.
>>> C2("foo", "bar")
C2(a='foo', b='bar')
(If you don’t like the playful ``attr.s`` and ``attr.ib``, you can also use their no-nonsense aliases ``attr.attributes`` and ``attr.attr``).
If you don’t like the playful ``attr.s`` and ``attr.ib`` (that aren't any obscure abbreviations; just a concise and highly readable way to write ``attrs`` and ``attrib`` with an explicit namespace), ``attrs`` comes with no-nonsense aliases: ``attr.attributes`` and ``attr.attr``.
Sometimes it takes a few minutes to get used to the short forms, but in the long run, they're more readable and therefore grokkable when reading code.

You just specify the attributes to work with and ``attrs`` gives you:
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,
- an initializer,
- and much more

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

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 <https://www.destroyallsoftware.com/talks/boundaries>`_.
Never again violate the `single responsibility principle <https://en.wikipedia.org/wiki/Single_responsibility_principle>`_ just because implementing ``__init__`` et al is a painful drag.

So put down those type-less data structures and welcome some class into your life!
``attrs``\ 's main objective is to help you to write *concise* and *correct* software without slowing you down.


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 taking your declaration, writing dunder methods based on that information, and attaching 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.


Testimonials
============

I’m looking forward to is being able to program in Python-with-attrs everywhere.
It exerts a subtle, but positive, design influence in all the codebases I’ve see it used in.

-- Glyph Lefkowitz, inventor of Twisted and Software Developer at Rackspace in `The One Python Library Everyone Needs <https://glyph.twistedmatrix.com/2016/08/attrs.html>`_


I'm increasingly digging your attr.ocity. Good job!

-- Łukasz Langa, prolific CPython core developer and Production Engineer at Facebook

.. -end-
Project Information
===================

``attrs``\ ’s documentation lives at `Read the Docs <https://attrs.readthedocs.io/>`_, and the code on `GitHub <https://github.com/hynek/attrs>`_.
It’s rigorously tested on Python 2.7, 3.4+, and PyPy.
4 changes: 3 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,4 +299,6 @@ def find_version(*file_paths):


# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'https://docs.python.org/2': None}
intersphinx_mapping = {
"https://docs.python.org/3": None,
}
40 changes: 40 additions & 0 deletions docs/how-does-it-work.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.. _how:

How Does It Work?
=================

``attrs`` certainly isn't the first library that aims to simplify class definition in Python.
But its **declarative** approach combined with **no runtime overhead** lets it stand out.

Once you apply the ``@attr.s`` decorator to a class, ``attrs`` searches the class object for instances of ``attr.ib``\ s.
Internally they're a representation of the data passed into ``attr.ib`` along with a counter to preserve the order of the attributes.

In order to ensure that sub-classing works as you'd expect it to work, ``attrs`` will also walk the class hierarchy and collect all attributes of all super-classes.
Please note that ``attrs`` does *not* call ``super()`` *ever*.
It will write dunder methods to work on *all* of those attributes which of course has performance benefits due to less function calls.

Once ``attrs`` knows what attributes it has to work on, it writes the requested dunder methods and attaches them to your class.
To be very clear: if you define a class with a single attribute without a default value, the generated ``__init__`` will look *exactly* how you'd expect:

.. doctest::

>>> import attr, inspect
>>> @attr.s
... class C:
... x = attr.ib()
>>> print(inspect.getsource(C.__init__))
def __init__(self, x):
self.x = x
<BLANKLINE>

No magic, no meta programming, no expensive introspection at runtime.

****

Everything until this point happens exactly *once* when the class is defined.
As soon as a class is done, it's done.
And it's just a regular Python class like any other, except for a single ``__attrs_attrs__`` attribute that can be used for introspection or for writing your own tools and decorators on top of ``attrs`` (like :func:`attr.asdict`.

And once you start instantiating your classes, ``attrs`` is out of your way completely.

This **static** approach was very much a design goal of ``attrs`` and what I strongly believe makes it distinct.
11 changes: 7 additions & 4 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
======================================
attrs: Attributes without boilerplate.
=============================================
======================================

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


.. include:: ../README.rst
:start-after: teaser-begin
:end-before: -end-


User's Guide
------------
============

.. toctree::
:maxdepth: 1
:maxdepth: 2

why
examples
api
extending
how-does-it-work


Project Information
-------------------
===================

.. toctree::
:maxdepth: 1
Expand Down
7 changes: 5 additions & 2 deletions docs/why.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Why not…
========


If you'd like third party's account why ``attrs`` is great, have a look at Glyph's `The One Python Library Everyone Needs <https://glyph.twistedmatrix.com/2016/08/attrs.html>`_!


…tuples?
--------

Expand Down Expand Up @@ -178,8 +181,8 @@ If you don't care and like typing, I'm not gonna stop you.
But if you ever get sick of the repetitiveness, ``attrs`` will be waiting for you. :)


…characteristic
---------------
…characteristic?
----------------

`characteristic <https://characteristic.readthedocs.io/>`_ is a very similar and fairly popular project of mine.
So why the self-fork?
Expand Down
6 changes: 3 additions & 3 deletions src/attr/_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

def asdict(inst, recurse=True, filter=None, dict_factory=dict):
"""
Return the ``attrs`` attribute values of *i* as a dict. Optionally recurse
into other ``attrs``-decorated classes.
Return the ``attrs`` attribute values of *inst* as a dict. Optionally
recurse into other ``attrs``-decorated classes.
:param inst: Instance of a ``attrs``-decorated class.
:param inst: Instance of an ``attrs``-decorated class.
:param bool recurse: Recurse into classes that are also
``attrs``-decorated.
Expand Down
25 changes: 14 additions & 11 deletions src/attr/_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,23 @@ def attr(default=NOTHING, validator=None,
"""
Create a new attribute on a class.
.. warning::
.. warning::
Does *not* do anything unless the class is also decorated with
:func:`attr.s`!
:param default: Value that is used if an ``attrs``-generated
``__init__`` is used and no value is passed while instantiating or the
attribute is excluded using ``init=False``. If the value an instance
of :class:`Factory`, it callable will be use to construct a new value
(useful for mutable datatypes like lists or dicts).
:param default: A value that is used if an ``attrs``-generated ``__init__``
is used and no value is passed while instantiating or the attribute is
excluded using ``init=False``.
If the value is an instance of :class:`Factory`, its callable will be
use to construct a new value (useful for mutable datatypes like lists
or dicts).
If a default is not set (or set manually to ``attr.NOTHING``), a value
*must* be supplied when instantiating; otherwise a :exc:`TypeError`
will be raised.
:type default: Any value.
:param callable validator: :func:`callable` that is called by
Expand All @@ -69,22 +76,18 @@ def attr(default=NOTHING, validator=None,
:param bool repr: Include this attribute in the generated ``__repr__``
method.
:param bool cmp: Include this attribute in the generated comparison methods
(``__eq__`` et al).
:param bool hash: Include this attribute in the generated ``__hash__``
method.
:param bool init: Include this attribute in the generated ``__init__``
method. It is possible to set this to ``False`` and set a default
value. In that case this attributed is unconditionally initialized
with the specified default value or factory.
:param callable convert: :func:`callable` that is called by
``attrs``-generated ``__init__`` methods to convert attribute's value
to the desired format. It is given the passed-in value, and the
returned value will be used as the new value of the attribute. The
returned value will be used as the new value of the attribute. The
value is converted before being passed to the validator, if any.
"""
return _CountingAttr(
Expand Down

0 comments on commit 395aa07

Please sign in to comment.