114 lines
3.0 KiB
Python
114 lines
3.0 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
|
|
from ._dunders import (
|
|
NOTHING,
|
|
_add_cmp,
|
|
_add_hash,
|
|
_add_init,
|
|
_add_repr,
|
|
)
|
|
|
|
|
|
class Attribute(object):
|
|
def __init__(self, name, default_value, default_factory):
|
|
self.name = name
|
|
self.default_value = default_value
|
|
self.default_factory = default_factory
|
|
|
|
@classmethod
|
|
def from_counting_attr(cl, name, ca):
|
|
return cl(
|
|
name=name,
|
|
default_value=ca.default_value,
|
|
default_factory=ca.default_factory,
|
|
)
|
|
|
|
|
|
_a = ["name", "default_value", "default_factory"]
|
|
Attribute = _add_cmp(_add_repr(Attribute, attrs=_a), attrs=_a)
|
|
|
|
|
|
class _CountingAttr(object):
|
|
__attrs_attrs__ = [
|
|
Attribute(name=name, default_value=NOTHING, default_factory=NOTHING)
|
|
for name
|
|
in ("counter", "default_value", "default_factory",)
|
|
]
|
|
counter = 0
|
|
|
|
def __init__(self, default_value=NOTHING, default_factory=NOTHING):
|
|
_CountingAttr.counter += 1
|
|
self.counter = _CountingAttr.counter
|
|
self.default_value = default_value
|
|
self.default_factory = default_factory
|
|
|
|
|
|
_CountingAttr = _add_cmp(_add_repr(_CountingAttr))
|
|
|
|
|
|
def _make_attr(default_value=NOTHING, default_factory=NOTHING):
|
|
"""
|
|
Create a new attribute on a class.
|
|
|
|
Does nothing unless the class is also decorated with ``@attr.s``!
|
|
"""
|
|
if default_value is not NOTHING and default_factory is not NOTHING:
|
|
raise ValueError(
|
|
"Specifying both default_value and default_factory is "
|
|
"ambiguous."
|
|
)
|
|
|
|
return _CountingAttr(
|
|
default_value=default_value,
|
|
default_factory=default_factory,
|
|
)
|
|
|
|
|
|
def _get_attrs(cl):
|
|
"""
|
|
Return list of tuples of `(name, _Attr)`.
|
|
"""
|
|
attrs = []
|
|
|
|
for name, instance in sorted((
|
|
(n, i) for n, i in cl.__dict__.items()
|
|
if isinstance(i, _CountingAttr)
|
|
), key=lambda e: e[1].counter):
|
|
attrs.append((name, instance))
|
|
|
|
return attrs
|
|
|
|
|
|
def s(maybe_cl=None, add_repr=True, add_cmp=True, add_hash=True,
|
|
add_init=True):
|
|
# attrs_or class type depends on the usage of the decorator.
|
|
# It's a class if it's used as `@s` but ``None`` (or a value
|
|
# passed) if used as `@s()`.
|
|
if isinstance(maybe_cl, type):
|
|
cl = maybe_cl
|
|
cl.__attrs_attrs__ = [
|
|
Attribute.from_counting_attr(name=name, ca=ca)
|
|
for name, ca in _get_attrs(cl)
|
|
]
|
|
return _add_init(_add_hash(_add_cmp(_add_repr(cl))))
|
|
else:
|
|
def wrap(cl):
|
|
cl.__attrs_attrs__ = [
|
|
Attribute.from_counting_attr(name=name, ca=ca)
|
|
for name, ca in _get_attrs(cl)
|
|
]
|
|
if add_repr is True:
|
|
cl = _add_repr(cl)
|
|
if add_cmp is True:
|
|
cl = _add_cmp(cl)
|
|
if add_hash is True:
|
|
cl = _add_hash(cl)
|
|
if add_init is True:
|
|
cl = _add_init(cl)
|
|
return cl
|
|
|
|
return wrap
|