Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
Docs aren't there yet
  • Loading branch information
hynek committed Jan 27, 2015
0 parents commit 9560908
Show file tree
Hide file tree
Showing 27 changed files with 1,847 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.tox
.coverage
*.pyc
*.egg-info
docs/_build/
23 changes: 23 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
language: python
python: 2.7
env:
- TOX_ENV=py26
- TOX_ENV=py27
- TOX_ENV=py33
- TOX_ENV=py34
- TOX_ENV=pypy
- TOX_ENV=docs
- TOX_ENV=flake8
- TOX_ENV=manifest

install:
- pip install tox coveralls

script:
- tox --hashseed 0 -e $TOX_ENV

after_success:
- coveralls

notifications:
email: false
15 changes: 15 additions & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Authors
-------

``attrs`` is written and maintained by `Hynek Schlawack <https://hynek.me/>`_.

The development is kindly supported by `Variomedia AG <https://www.variomedia.de/>`_.

It’s the spiritual successor of `characteristic <https://characteristic.readthedocs.org/>`_ and aspires to fix some of it clunkiness and unfortunate decisions. Both were inspired by Twisted’s `FancyEqMixin <https://twistedmatrix.com/documents/current/api/twisted.python.util.FancyEqMixin.html>`_ but both are implemented using class decorators because `sub-classing is bad for you <https://www.youtube.com/watch?v=3MNVP9-hglc>`_, m’kay?


The following folks helped forming ``attrs`` into what it is now:

- `Glyph <https://github.com/glyph>`_

Of course ``characteristic``\ ’s `hall of fame <https://characteristic.readthedocs.org/en/stable/license.html>`_ applies as well since they share a lot of code.
38 changes: 38 additions & 0 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
How To Contribute
=================

Every open source project lives from the generous help by contributors that sacrifice their time and ``attrs`` is no different.

To make participation as pleasant as possible, this project adheres to the `Code of Conduct`_ by the Python Software Foundation.

Here are a few guidelines to get you started:

- Add yourself to the AUTHORS.rst_ file in an alphabetical fashion.
Every contribution is valuable and shall be credited.
- If your change is noteworthy, add an entry to the changelog_.
- No contribution is too small; please submit as many fixes for typos and grammar bloopers as you can!
- Don’t *ever* break backward compatibility.
If it ever *has* to happen for higher reasons, ``attrs`` will follow the proven procedures_ of the Twisted project.
- *Always* add tests and docs for your code.
This is a hard rule; patches with missing tests or documentation won’t be merged.
If a feature is not tested or documented, it doesn’t exist.
- Obey `PEP 8`_ and `PEP 257`_.
- Write `good commit messages`_.

.. note::
If you have something great but aren’t sure whether it adheres -- or even can adhere -- to the rules above: **please submit a pull request anyway**!

In the best case, we can mold it into something, in the worst case the pull request gets politely closed.
There’s absolutely nothing to fear.

Thank you for considering to contribute to ``attrs``!
If you have any question or concerns, feel free to reach out to me.


.. _`PEP 8`: http://legacy.python.org/dev/peps/pep-0008/
.. _`PEP 257`: http://legacy.python.org/dev/peps/pep-0257/
.. _`good commit messages`: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
.. _`Code of Conduct`: https://www.python.org/psf/codeofconduct/
.. _changelog: https://github.com/hynek/attrs/blob/master/docs/changelog.rst
.. _AUTHORS.rst: https://github.com/hynek/attrs/blob/master/AUTHORS.rst
.. _procedures: http://twistedmatrix.com/trac/wiki/CompatibilityPolicy
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2014 Hynek Schlawack

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
5 changes: 5 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include *.rst *.txt LICENSE tox.ini .travis.yml docs/Makefile .coveragerc
recursive-include tests *.py
recursive-include docs *.rst
recursive-include docs *.py
prune docs/_build
53 changes: 53 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
=============================================
attrs: Python attributes without boilerplate.
=============================================

.. image:: https://pypip.in/version/attrs/badge.svg
:target: https://pypi.python.org/pypi/attrs/
:alt: Latest Version

.. image:: https://travis-ci.org/hynek/attrs.svg
:target: https://travis-ci.org/hynek/attrs
:alt: CI status

.. image:: https://coveralls.io/repos/hynek/attrs/badge.png?branch=master
:target: https://coveralls.io/r/hynek/attrs?branch=master
:alt: Current coverage

.. begin
``attrs`` is an `MIT <http://choosealicense.com/licenses/mit/>`_-licensed Python package with class decorators that ease the chores of implementing the most common attribute-related object protocols:

.. code-block:: pycon
>>> import attr
>>> @attr.s
... class C(object):
... x = attr.a(default_value=42)
... y = attr.a(default_factory=list)
>>> i = C(x=1, y=2)
>>> i
<C(x=1, y=2)>
>>> i == C(1, 2)
True
>>> i != C(2, 1)
True
>>> attr.to_dict(i)
{'y': 2, 'x': 1}
>>> C()
<C(x=42, y=[])>
You just specify the attributes to work with and ``attrs`` gives you:

- a nice human-readable ``__repr__``,
- a complete set of comparison methods,
- and an initializer

*without* writing dull boilerplate code again and again.

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.

So put down that type-less data structures and welcome some class into your life!

``attrs``\ ’s documentation lives at `Read the Docs <https://attrs.readthedocs.org/>`_, the code on `GitHub <https://github.com/hynek/attrs>`_.
It’s rigorously tested on Python 2.6, 2.7, 3.3+, and PyPy.
22 changes: 22 additions & 0 deletions attr/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-

from __future__ import absolute_import, division, print_function

from ._funcs import (
ls,
to_dict,
)
from ._make import (
_make_attr as a,
s,
)

__version__ = "0.0.0.dev0"
__author__ = "Hynek Schlawack"
__license__ = "MIT"
__copyright__ = "Copyright 2015 Hynek Schlawack"


__all__ = [
"a", "s", "ls", "to_dict",
]
207 changes: 207 additions & 0 deletions attr/_dunders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
# -*- coding: utf-8 -*-

from __future__ import absolute_import, division, print_function

import hashlib
import linecache
import sys


def _attrs_to_tuple(obj, attrs):
"""
Create a tuple of all values of *obj*'s *attrs*.
"""
return tuple(getattr(obj, a) for a in attrs)


def _add_hash(cl, attrs=None):
if attrs is None:
attrs = [a.name for a in cl.__attrs_attrs__]

def hash_(self):
"""
Automatically created by attrs.
"""
return hash(_attrs_to_tuple(self, attrs))

cl.__hash__ = hash_
return cl


def _add_cmp(cl, attrs=None):
if attrs is None:
attrs = [a.name for a in cl.__attrs_attrs__]

def attrs_to_tuple(obj):
"""
Save us some typing.
"""
return _attrs_to_tuple(obj, attrs)

def eq(self, other):
"""
Automatically created by attrs.
"""
if isinstance(other, self.__class__):
return attrs_to_tuple(self) == attrs_to_tuple(other)
else:
return NotImplemented

def ne(self, other):
"""
Automatically created by attrs.
"""
result = eq(self, other)
if result is NotImplemented:
return NotImplemented
else:
return not result

def lt(self, other):
"""
Automatically created by attrs.
"""
if isinstance(other, self.__class__):
return attrs_to_tuple(self) < attrs_to_tuple(other)
else:
return NotImplemented

def le(self, other):
"""
Automatically created by attrs.
"""
if isinstance(other, self.__class__):
return attrs_to_tuple(self) <= attrs_to_tuple(other)
else:
return NotImplemented

def gt(self, other):
"""
Automatically created by attrs.
"""
if isinstance(other, self.__class__):
return attrs_to_tuple(self) > attrs_to_tuple(other)
else:
return NotImplemented

def ge(self, other):
"""
Automatically created by attrs.
"""
if isinstance(other, self.__class__):
return attrs_to_tuple(self) >= attrs_to_tuple(other)
else:
return NotImplemented

cl.__eq__ = eq
cl.__ne__ = ne
cl.__lt__ = lt
cl.__le__ = le
cl.__gt__ = gt
cl.__ge__ = ge

return cl


def _add_repr(cl, attrs=None):
if attrs is None:
attrs = [a.name for a in cl.__attrs_attrs__]

def repr_(self):
"""
Automatically created by attrs.
"""
return "<{0}({1})>".format(
self.__class__.__name__,
", ".join(a + "=" + repr(getattr(self, a)) for a in attrs)
)
cl.__repr__ = repr_
return cl


# I'm sorry. :(
if sys.version_info[0] == 2:
def exec_(code, locals_, globals_):
exec("exec code in locals_, globals_")
else: # pragma: no cover
def exec_(code, locals_, globals_):
exec(code, locals_, globals_)


class _Nothing(object):
"""
Sentinel class to indicate the lack of a value when ``None`` is ambiguous.
"""
def __repr__(self):
return "NOTHING"


NOTHING = _Nothing()
"""
Sentinel to indicate the lack of a value when ``None`` is ambiguous.
"""


def _add_init(cl):
attrs = cl.__attrs_attrs__

# We cache the generated init methods for the same kinds of attributes.
sha1 = hashlib.sha1()
sha1.update(repr(attrs).encode("utf-8"))
unique_filename = "<attrs generated init {0}>".format(
sha1.hexdigest()
)

script = _attrs_to_script(attrs)
locs = {}
bytecode = compile(script, unique_filename, "exec")
attr_dict = dict((a.name, a) for a in attrs)
exec_(bytecode, {"NOTHING": NOTHING, "attr_dict": attr_dict}, locs)
init = locs["__init__"]

# In order of debuggers like PDB being able to step through the code,
# we add a fake linecache entry.
linecache.cache[unique_filename] = (
len(script),
None,
script.splitlines(True),
unique_filename
)
cl.__init__ = init
return cl


def _attrs_to_script(attrs):
"""
Return a valid Python script of an initializer for *attrs*.
"""
lines = []
args = []
for a in attrs:
if a.default_value is not NOTHING:
args.append("{name}={default!r}".format(name=a.name,
default=a.default_value))
lines.append("self.{name} = {name}".format(name=a.name))
elif a.default_factory is not NOTHING:
args.append("{name}=NOTHING".format(name=a.name))
lines.extend("""\
if {name} is not NOTHING:
self.{name} = {name}
else:
self.{name} = attr_dict["{name}"].default_factory()"""
.format(name=a.name)
.split("\n"))
else:
args.append(a.name)
lines.append("self.{name} = {name}".format(name=a.name))

return """\
def __init__(self, {args}):
'''
Attribute initializer automatically created by attrs.
'''
{setters}
""".format(
args=", ".join(args),
setters="\n ".join(lines),
)
Loading

0 comments on commit 9560908

Please sign in to comment.