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