Mercurial > public > mercurial-scm > hg
comparison mercurial/thirdparty/attr/_funcs.py @ 34397:765eb17a7eb8
thirdparty: vendor attrs
The attrs package allows defining namedtuple-like classes with no weird
behavior and no runtime performance cost.
This patch vendors in attrs 17.2.0.
# no-check-commit
Differential Revision: https://phab.mercurial-scm.org/D867
author | Siddharth Agarwal <sid0@fb.com> |
---|---|
date | Sun, 01 Oct 2017 04:14:16 -0700 |
parents | |
children | e1c586b9a43c |
comparison
equal
deleted
inserted
replaced
34396:9fb9f8440b71 | 34397:765eb17a7eb8 |
---|---|
1 from __future__ import absolute_import, division, print_function | |
2 | |
3 import copy | |
4 | |
5 from ._compat import iteritems | |
6 from ._make import NOTHING, fields, _obj_setattr | |
7 from .exceptions import AttrsAttributeNotFoundError | |
8 | |
9 | |
10 def asdict(inst, recurse=True, filter=None, dict_factory=dict, | |
11 retain_collection_types=False): | |
12 """ | |
13 Return the ``attrs`` attribute values of *inst* as a dict. | |
14 | |
15 Optionally recurse into other ``attrs``-decorated classes. | |
16 | |
17 :param inst: Instance of an ``attrs``-decorated class. | |
18 :param bool recurse: Recurse into classes that are also | |
19 ``attrs``-decorated. | |
20 :param callable filter: A callable whose return code deteremines whether an | |
21 attribute or element is included (``True``) or dropped (``False``). Is | |
22 called with the :class:`attr.Attribute` as the first argument and the | |
23 value as the second argument. | |
24 :param callable dict_factory: A callable to produce dictionaries from. For | |
25 example, to produce ordered dictionaries instead of normal Python | |
26 dictionaries, pass in ``collections.OrderedDict``. | |
27 :param bool retain_collection_types: Do not convert to ``list`` when | |
28 encountering an attribute whose type is ``tuple`` or ``set``. Only | |
29 meaningful if ``recurse`` is ``True``. | |
30 | |
31 :rtype: return type of *dict_factory* | |
32 | |
33 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` | |
34 class. | |
35 | |
36 .. versionadded:: 16.0.0 *dict_factory* | |
37 .. versionadded:: 16.1.0 *retain_collection_types* | |
38 """ | |
39 attrs = fields(inst.__class__) | |
40 rv = dict_factory() | |
41 for a in attrs: | |
42 v = getattr(inst, a.name) | |
43 if filter is not None and not filter(a, v): | |
44 continue | |
45 if recurse is True: | |
46 if has(v.__class__): | |
47 rv[a.name] = asdict(v, recurse=True, filter=filter, | |
48 dict_factory=dict_factory) | |
49 elif isinstance(v, (tuple, list, set)): | |
50 cf = v.__class__ if retain_collection_types is True else list | |
51 rv[a.name] = cf([ | |
52 asdict(i, recurse=True, filter=filter, | |
53 dict_factory=dict_factory) | |
54 if has(i.__class__) else i | |
55 for i in v | |
56 ]) | |
57 elif isinstance(v, dict): | |
58 df = dict_factory | |
59 rv[a.name] = df(( | |
60 asdict(kk, dict_factory=df) if has(kk.__class__) else kk, | |
61 asdict(vv, dict_factory=df) if has(vv.__class__) else vv) | |
62 for kk, vv in iteritems(v)) | |
63 else: | |
64 rv[a.name] = v | |
65 else: | |
66 rv[a.name] = v | |
67 return rv | |
68 | |
69 | |
70 def astuple(inst, recurse=True, filter=None, tuple_factory=tuple, | |
71 retain_collection_types=False): | |
72 """ | |
73 Return the ``attrs`` attribute values of *inst* as a tuple. | |
74 | |
75 Optionally recurse into other ``attrs``-decorated classes. | |
76 | |
77 :param inst: Instance of an ``attrs``-decorated class. | |
78 :param bool recurse: Recurse into classes that are also | |
79 ``attrs``-decorated. | |
80 :param callable filter: A callable whose return code determines whether an | |
81 attribute or element is included (``True``) or dropped (``False``). Is | |
82 called with the :class:`attr.Attribute` as the first argument and the | |
83 value as the second argument. | |
84 :param callable tuple_factory: A callable to produce tuples from. For | |
85 example, to produce lists instead of tuples. | |
86 :param bool retain_collection_types: Do not convert to ``list`` | |
87 or ``dict`` when encountering an attribute which type is | |
88 ``tuple``, ``dict`` or ``set``. Only meaningful if ``recurse`` is | |
89 ``True``. | |
90 | |
91 :rtype: return type of *tuple_factory* | |
92 | |
93 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` | |
94 class. | |
95 | |
96 .. versionadded:: 16.2.0 | |
97 """ | |
98 attrs = fields(inst.__class__) | |
99 rv = [] | |
100 retain = retain_collection_types # Very long. :/ | |
101 for a in attrs: | |
102 v = getattr(inst, a.name) | |
103 if filter is not None and not filter(a, v): | |
104 continue | |
105 if recurse is True: | |
106 if has(v.__class__): | |
107 rv.append(astuple(v, recurse=True, filter=filter, | |
108 tuple_factory=tuple_factory, | |
109 retain_collection_types=retain)) | |
110 elif isinstance(v, (tuple, list, set)): | |
111 cf = v.__class__ if retain is True else list | |
112 rv.append(cf([ | |
113 astuple(j, recurse=True, filter=filter, | |
114 tuple_factory=tuple_factory, | |
115 retain_collection_types=retain) | |
116 if has(j.__class__) else j | |
117 for j in v | |
118 ])) | |
119 elif isinstance(v, dict): | |
120 df = v.__class__ if retain is True else dict | |
121 rv.append(df( | |
122 ( | |
123 astuple( | |
124 kk, | |
125 tuple_factory=tuple_factory, | |
126 retain_collection_types=retain | |
127 ) if has(kk.__class__) else kk, | |
128 astuple( | |
129 vv, | |
130 tuple_factory=tuple_factory, | |
131 retain_collection_types=retain | |
132 ) if has(vv.__class__) else vv | |
133 ) | |
134 for kk, vv in iteritems(v))) | |
135 else: | |
136 rv.append(v) | |
137 else: | |
138 rv.append(v) | |
139 return rv if tuple_factory is list else tuple_factory(rv) | |
140 | |
141 | |
142 def has(cls): | |
143 """ | |
144 Check whether *cls* is a class with ``attrs`` attributes. | |
145 | |
146 :param type cls: Class to introspect. | |
147 :raise TypeError: If *cls* is not a class. | |
148 | |
149 :rtype: :class:`bool` | |
150 """ | |
151 return getattr(cls, "__attrs_attrs__", None) is not None | |
152 | |
153 | |
154 def assoc(inst, **changes): | |
155 """ | |
156 Copy *inst* and apply *changes*. | |
157 | |
158 :param inst: Instance of a class with ``attrs`` attributes. | |
159 :param changes: Keyword changes in the new copy. | |
160 | |
161 :return: A copy of inst with *changes* incorporated. | |
162 | |
163 :raise attr.exceptions.AttrsAttributeNotFoundError: If *attr_name* couldn't | |
164 be found on *cls*. | |
165 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` | |
166 class. | |
167 | |
168 .. deprecated:: 17.1.0 | |
169 Use :func:`evolve` instead. | |
170 """ | |
171 import warnings | |
172 warnings.warn("assoc is deprecated and will be removed after 2018/01.", | |
173 DeprecationWarning) | |
174 new = copy.copy(inst) | |
175 attrs = fields(inst.__class__) | |
176 for k, v in iteritems(changes): | |
177 a = getattr(attrs, k, NOTHING) | |
178 if a is NOTHING: | |
179 raise AttrsAttributeNotFoundError( | |
180 "{k} is not an attrs attribute on {cl}." | |
181 .format(k=k, cl=new.__class__) | |
182 ) | |
183 _obj_setattr(new, k, v) | |
184 return new | |
185 | |
186 | |
187 def evolve(inst, **changes): | |
188 """ | |
189 Create a new instance, based on *inst* with *changes* applied. | |
190 | |
191 :param inst: Instance of a class with ``attrs`` attributes. | |
192 :param changes: Keyword changes in the new copy. | |
193 | |
194 :return: A copy of inst with *changes* incorporated. | |
195 | |
196 :raise TypeError: If *attr_name* couldn't be found in the class | |
197 ``__init__``. | |
198 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` | |
199 class. | |
200 | |
201 .. versionadded:: 17.1.0 | |
202 """ | |
203 cls = inst.__class__ | |
204 attrs = fields(cls) | |
205 for a in attrs: | |
206 if not a.init: | |
207 continue | |
208 attr_name = a.name # To deal with private attributes. | |
209 init_name = attr_name if attr_name[0] != "_" else attr_name[1:] | |
210 if init_name not in changes: | |
211 changes[init_name] = getattr(inst, attr_name) | |
212 return cls(**changes) |