comparison env/lib/python3.9/site-packages/attr/_funcs.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 from __future__ import absolute_import, division, print_function
2
3 import copy
4
5 from ._compat import iteritems
6 from ._make import NOTHING, _obj_setattr, fields
7 from .exceptions import AttrsAttributeNotFoundError
8
9
10 def asdict(
11 inst,
12 recurse=True,
13 filter=None,
14 dict_factory=dict,
15 retain_collection_types=False,
16 value_serializer=None,
17 ):
18 """
19 Return the ``attrs`` attribute values of *inst* as a dict.
20
21 Optionally recurse into other ``attrs``-decorated classes.
22
23 :param inst: Instance of an ``attrs``-decorated class.
24 :param bool recurse: Recurse into classes that are also
25 ``attrs``-decorated.
26 :param callable filter: A callable whose return code determines whether an
27 attribute or element is included (``True``) or dropped (``False``). Is
28 called with the `attr.Attribute` as the first argument and the
29 value as the second argument.
30 :param callable dict_factory: A callable to produce dictionaries from. For
31 example, to produce ordered dictionaries instead of normal Python
32 dictionaries, pass in ``collections.OrderedDict``.
33 :param bool retain_collection_types: Do not convert to ``list`` when
34 encountering an attribute whose type is ``tuple`` or ``set``. Only
35 meaningful if ``recurse`` is ``True``.
36 :param Optional[callable] value_serializer: A hook that is called for every
37 attribute or dict key/value. It receives the current instance, field
38 and value and must return the (updated) value. The hook is run *after*
39 the optional *filter* has been applied.
40
41 :rtype: return type of *dict_factory*
42
43 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
44 class.
45
46 .. versionadded:: 16.0.0 *dict_factory*
47 .. versionadded:: 16.1.0 *retain_collection_types*
48 .. versionadded:: 20.3.0 *value_serializer*
49 """
50 attrs = fields(inst.__class__)
51 rv = dict_factory()
52 for a in attrs:
53 v = getattr(inst, a.name)
54 if filter is not None and not filter(a, v):
55 continue
56
57 if value_serializer is not None:
58 v = value_serializer(inst, a, v)
59
60 if recurse is True:
61 if has(v.__class__):
62 rv[a.name] = asdict(
63 v,
64 True,
65 filter,
66 dict_factory,
67 retain_collection_types,
68 value_serializer,
69 )
70 elif isinstance(v, (tuple, list, set, frozenset)):
71 cf = v.__class__ if retain_collection_types is True else list
72 rv[a.name] = cf(
73 [
74 _asdict_anything(
75 i,
76 filter,
77 dict_factory,
78 retain_collection_types,
79 value_serializer,
80 )
81 for i in v
82 ]
83 )
84 elif isinstance(v, dict):
85 df = dict_factory
86 rv[a.name] = df(
87 (
88 _asdict_anything(
89 kk,
90 filter,
91 df,
92 retain_collection_types,
93 value_serializer,
94 ),
95 _asdict_anything(
96 vv,
97 filter,
98 df,
99 retain_collection_types,
100 value_serializer,
101 ),
102 )
103 for kk, vv in iteritems(v)
104 )
105 else:
106 rv[a.name] = v
107 else:
108 rv[a.name] = v
109 return rv
110
111
112 def _asdict_anything(
113 val,
114 filter,
115 dict_factory,
116 retain_collection_types,
117 value_serializer,
118 ):
119 """
120 ``asdict`` only works on attrs instances, this works on anything.
121 """
122 if getattr(val.__class__, "__attrs_attrs__", None) is not None:
123 # Attrs class.
124 rv = asdict(
125 val,
126 True,
127 filter,
128 dict_factory,
129 retain_collection_types,
130 value_serializer,
131 )
132 elif isinstance(val, (tuple, list, set, frozenset)):
133 cf = val.__class__ if retain_collection_types is True else list
134 rv = cf(
135 [
136 _asdict_anything(
137 i,
138 filter,
139 dict_factory,
140 retain_collection_types,
141 value_serializer,
142 )
143 for i in val
144 ]
145 )
146 elif isinstance(val, dict):
147 df = dict_factory
148 rv = df(
149 (
150 _asdict_anything(
151 kk, filter, df, retain_collection_types, value_serializer
152 ),
153 _asdict_anything(
154 vv, filter, df, retain_collection_types, value_serializer
155 ),
156 )
157 for kk, vv in iteritems(val)
158 )
159 else:
160 rv = val
161 if value_serializer is not None:
162 rv = value_serializer(None, None, rv)
163
164 return rv
165
166
167 def astuple(
168 inst,
169 recurse=True,
170 filter=None,
171 tuple_factory=tuple,
172 retain_collection_types=False,
173 ):
174 """
175 Return the ``attrs`` attribute values of *inst* as a tuple.
176
177 Optionally recurse into other ``attrs``-decorated classes.
178
179 :param inst: Instance of an ``attrs``-decorated class.
180 :param bool recurse: Recurse into classes that are also
181 ``attrs``-decorated.
182 :param callable filter: A callable whose return code determines whether an
183 attribute or element is included (``True``) or dropped (``False``). Is
184 called with the `attr.Attribute` as the first argument and the
185 value as the second argument.
186 :param callable tuple_factory: A callable to produce tuples from. For
187 example, to produce lists instead of tuples.
188 :param bool retain_collection_types: Do not convert to ``list``
189 or ``dict`` when encountering an attribute which type is
190 ``tuple``, ``dict`` or ``set``. Only meaningful if ``recurse`` is
191 ``True``.
192
193 :rtype: return type of *tuple_factory*
194
195 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
196 class.
197
198 .. versionadded:: 16.2.0
199 """
200 attrs = fields(inst.__class__)
201 rv = []
202 retain = retain_collection_types # Very long. :/
203 for a in attrs:
204 v = getattr(inst, a.name)
205 if filter is not None and not filter(a, v):
206 continue
207 if recurse is True:
208 if has(v.__class__):
209 rv.append(
210 astuple(
211 v,
212 recurse=True,
213 filter=filter,
214 tuple_factory=tuple_factory,
215 retain_collection_types=retain,
216 )
217 )
218 elif isinstance(v, (tuple, list, set, frozenset)):
219 cf = v.__class__ if retain is True else list
220 rv.append(
221 cf(
222 [
223 astuple(
224 j,
225 recurse=True,
226 filter=filter,
227 tuple_factory=tuple_factory,
228 retain_collection_types=retain,
229 )
230 if has(j.__class__)
231 else j
232 for j in v
233 ]
234 )
235 )
236 elif isinstance(v, dict):
237 df = v.__class__ if retain is True else dict
238 rv.append(
239 df(
240 (
241 astuple(
242 kk,
243 tuple_factory=tuple_factory,
244 retain_collection_types=retain,
245 )
246 if has(kk.__class__)
247 else kk,
248 astuple(
249 vv,
250 tuple_factory=tuple_factory,
251 retain_collection_types=retain,
252 )
253 if has(vv.__class__)
254 else vv,
255 )
256 for kk, vv in iteritems(v)
257 )
258 )
259 else:
260 rv.append(v)
261 else:
262 rv.append(v)
263
264 return rv if tuple_factory is list else tuple_factory(rv)
265
266
267 def has(cls):
268 """
269 Check whether *cls* is a class with ``attrs`` attributes.
270
271 :param type cls: Class to introspect.
272 :raise TypeError: If *cls* is not a class.
273
274 :rtype: bool
275 """
276 return getattr(cls, "__attrs_attrs__", None) is not None
277
278
279 def assoc(inst, **changes):
280 """
281 Copy *inst* and apply *changes*.
282
283 :param inst: Instance of a class with ``attrs`` attributes.
284 :param changes: Keyword changes in the new copy.
285
286 :return: A copy of inst with *changes* incorporated.
287
288 :raise attr.exceptions.AttrsAttributeNotFoundError: If *attr_name* couldn't
289 be found on *cls*.
290 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
291 class.
292
293 .. deprecated:: 17.1.0
294 Use `evolve` instead.
295 """
296 import warnings
297
298 warnings.warn(
299 "assoc is deprecated and will be removed after 2018/01.",
300 DeprecationWarning,
301 stacklevel=2,
302 )
303 new = copy.copy(inst)
304 attrs = fields(inst.__class__)
305 for k, v in iteritems(changes):
306 a = getattr(attrs, k, NOTHING)
307 if a is NOTHING:
308 raise AttrsAttributeNotFoundError(
309 "{k} is not an attrs attribute on {cl}.".format(
310 k=k, cl=new.__class__
311 )
312 )
313 _obj_setattr(new, k, v)
314 return new
315
316
317 def evolve(inst, **changes):
318 """
319 Create a new instance, based on *inst* with *changes* applied.
320
321 :param inst: Instance of a class with ``attrs`` attributes.
322 :param changes: Keyword changes in the new copy.
323
324 :return: A copy of inst with *changes* incorporated.
325
326 :raise TypeError: If *attr_name* couldn't be found in the class
327 ``__init__``.
328 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
329 class.
330
331 .. versionadded:: 17.1.0
332 """
333 cls = inst.__class__
334 attrs = fields(cls)
335 for a in attrs:
336 if not a.init:
337 continue
338 attr_name = a.name # To deal with private attributes.
339 init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
340 if init_name not in changes:
341 changes[init_name] = getattr(inst, attr_name)
342
343 return cls(**changes)
344
345
346 def resolve_types(cls, globalns=None, localns=None):
347 """
348 Resolve any strings and forward annotations in type annotations.
349
350 This is only required if you need concrete types in `Attribute`'s *type*
351 field. In other words, you don't need to resolve your types if you only
352 use them for static type checking.
353
354 With no arguments, names will be looked up in the module in which the class
355 was created. If this is not what you want, e.g. if the name only exists
356 inside a method, you may pass *globalns* or *localns* to specify other
357 dictionaries in which to look up these names. See the docs of
358 `typing.get_type_hints` for more details.
359
360 :param type cls: Class to resolve.
361 :param Optional[dict] globalns: Dictionary containing global variables.
362 :param Optional[dict] localns: Dictionary containing local variables.
363
364 :raise TypeError: If *cls* is not a class.
365 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
366 class.
367 :raise NameError: If types cannot be resolved because of missing variables.
368
369 :returns: *cls* so you can use this function also as a class decorator.
370 Please note that you have to apply it **after** `attr.s`. That means
371 the decorator has to come in the line **before** `attr.s`.
372
373 .. versionadded:: 20.1.0
374 """
375 try:
376 # Since calling get_type_hints is expensive we cache whether we've
377 # done it already.
378 cls.__attrs_types_resolved__
379 except AttributeError:
380 import typing
381
382 hints = typing.get_type_hints(cls, globalns=globalns, localns=localns)
383 for field in fields(cls):
384 if field.name in hints:
385 # Since fields have been frozen we must work around it.
386 _obj_setattr(field, "type", hints[field.name])
387 cls.__attrs_types_resolved__ = True
388
389 # Return the class so you can use it as a decorator too.
390 return cls