1"""
2These are Python 3.6+-only and keyword-only APIs that call `attr.s` and
3`attr.ib` with different default values.
4"""
5
6from functools import partial
7
8from attr.exceptions import UnannotatedAttributeError
9
10from . import setters
11from ._make import NOTHING, _frozen_setattrs, attrib, attrs
12
13
14def define(
15    maybe_cls=None,
16    *,
17    these=None,
18    repr=None,
19    hash=None,
20    init=None,
21    slots=True,
22    frozen=False,
23    weakref_slot=True,
24    str=False,
25    auto_attribs=None,
26    kw_only=False,
27    cache_hash=False,
28    auto_exc=True,
29    eq=None,
30    order=False,
31    auto_detect=True,
32    getstate_setstate=None,
33    on_setattr=None,
34    field_transformer=None,
35):
36    r"""
37    The only behavioral differences are the handling of the *auto_attribs*
38    option:
39
40    :param Optional[bool] auto_attribs: If set to `True` or `False`, it behaves
41       exactly like `attr.s`. If left `None`, `attr.s` will try to guess:
42
43       1. If any attributes are annotated and no unannotated `attr.ib`\ s
44          are found, it assumes *auto_attribs=True*.
45       2. Otherwise it assumes *auto_attribs=False* and tries to collect
46          `attr.ib`\ s.
47
48    and that mutable classes (``frozen=False``) validate on ``__setattr__``.
49
50    .. versionadded:: 20.1.0
51    """
52
53    def do_it(cls, auto_attribs):
54        return attrs(
55            maybe_cls=cls,
56            these=these,
57            repr=repr,
58            hash=hash,
59            init=init,
60            slots=slots,
61            frozen=frozen,
62            weakref_slot=weakref_slot,
63            str=str,
64            auto_attribs=auto_attribs,
65            kw_only=kw_only,
66            cache_hash=cache_hash,
67            auto_exc=auto_exc,
68            eq=eq,
69            order=order,
70            auto_detect=auto_detect,
71            collect_by_mro=True,
72            getstate_setstate=getstate_setstate,
73            on_setattr=on_setattr,
74            field_transformer=field_transformer,
75        )
76
77    def wrap(cls):
78        """
79        Making this a wrapper ensures this code runs during class creation.
80
81        We also ensure that frozen-ness of classes is inherited.
82        """
83        nonlocal frozen, on_setattr
84
85        had_on_setattr = on_setattr not in (None, setters.NO_OP)
86
87        # By default, mutable classes validate on setattr.
88        if frozen is False and on_setattr is None:
89            on_setattr = setters.validate
90
91        # However, if we subclass a frozen class, we inherit the immutability
92        # and disable on_setattr.
93        for base_cls in cls.__bases__:
94            if base_cls.__setattr__ is _frozen_setattrs:
95                if had_on_setattr:
96                    raise ValueError(
97                        "Frozen classes can't use on_setattr "
98                        "(frozen-ness was inherited)."
99                    )
100
101                on_setattr = setters.NO_OP
102                break
103
104        if auto_attribs is not None:
105            return do_it(cls, auto_attribs)
106
107        try:
108            return do_it(cls, True)
109        except UnannotatedAttributeError:
110            return do_it(cls, False)
111
112    # maybe_cls's type depends on the usage of the decorator.  It's a class
113    # if it's used as `@attrs` but ``None`` if used as `@attrs()`.
114    if maybe_cls is None:
115        return wrap
116    else:
117        return wrap(maybe_cls)
118
119
120mutable = define
121frozen = partial(define, frozen=True, on_setattr=None)
122
123
124def field(
125    *,
126    default=NOTHING,
127    validator=None,
128    repr=True,
129    hash=None,
130    init=True,
131    metadata=None,
132    converter=None,
133    factory=None,
134    kw_only=False,
135    eq=None,
136    order=None,
137    on_setattr=None,
138):
139    """
140    Identical to `attr.ib`, except keyword-only and with some arguments
141    removed.
142
143    .. versionadded:: 20.1.0
144    """
145    return attrib(
146        default=default,
147        validator=validator,
148        repr=repr,
149        hash=hash,
150        init=init,
151        metadata=metadata,
152        converter=converter,
153        factory=factory,
154        kw_only=kw_only,
155        eq=eq,
156        order=order,
157        on_setattr=on_setattr,
158    )
159