comparison mercurial/thirdparty/attr/_funcs.py @ 49761:e1c586b9a43c

attr: vendor 22.1.0 The previous version was 5 years old, and pytype 2022.06.30 started complaining about various uses (e.g. seeing `mercurial.thirdparty.attr._make._CountingAttr` instead of `bytearray`). Hopefully this helps. Additionally, this has official python 3.11 support. The `attrs` package is left out, because it is simply a bunch of *.pyi stubs and `from attr.X import *`, and that's not how they've been used up to this point. We'd probably need to customize those anyway to `from mercurial.thirdparty.attr import *`.
author Matt Harbison <matt_harbison@yahoo.com>
date Mon, 21 Nov 2022 15:04:42 -0500
parents 765eb17a7eb8
children
comparison
equal deleted inserted replaced
49760:7e6f3c69c0fb 49761:e1c586b9a43c
1 from __future__ import absolute_import, division, print_function 1 # SPDX-License-Identifier: MIT
2
2 3
3 import copy 4 import copy
4 5
5 from ._compat import iteritems 6 from ._make import NOTHING, _obj_setattr, fields
6 from ._make import NOTHING, fields, _obj_setattr
7 from .exceptions import AttrsAttributeNotFoundError 7 from .exceptions import AttrsAttributeNotFoundError
8 8
9 9
10 def asdict(inst, recurse=True, filter=None, dict_factory=dict, 10 def asdict(
11 retain_collection_types=False): 11 inst,
12 recurse=True,
13 filter=None,
14 dict_factory=dict,
15 retain_collection_types=False,
16 value_serializer=None,
17 ):
12 """ 18 """
13 Return the ``attrs`` attribute values of *inst* as a dict. 19 Return the ``attrs`` attribute values of *inst* as a dict.
14 20
15 Optionally recurse into other ``attrs``-decorated classes. 21 Optionally recurse into other ``attrs``-decorated classes.
16 22
17 :param inst: Instance of an ``attrs``-decorated class. 23 :param inst: Instance of an ``attrs``-decorated class.
18 :param bool recurse: Recurse into classes that are also 24 :param bool recurse: Recurse into classes that are also
19 ``attrs``-decorated. 25 ``attrs``-decorated.
20 :param callable filter: A callable whose return code deteremines whether an 26 :param callable filter: A callable whose return code determines whether an
21 attribute or element is included (``True``) or dropped (``False``). Is 27 attribute or element is included (``True``) or dropped (``False``). Is
22 called with the :class:`attr.Attribute` as the first argument and the 28 called with the `attrs.Attribute` as the first argument and the
23 value as the second argument. 29 value as the second argument.
24 :param callable dict_factory: A callable to produce dictionaries from. For 30 :param callable dict_factory: A callable to produce dictionaries from. For
25 example, to produce ordered dictionaries instead of normal Python 31 example, to produce ordered dictionaries instead of normal Python
26 dictionaries, pass in ``collections.OrderedDict``. 32 dictionaries, pass in ``collections.OrderedDict``.
27 :param bool retain_collection_types: Do not convert to ``list`` when 33 :param bool retain_collection_types: Do not convert to ``list`` when
28 encountering an attribute whose type is ``tuple`` or ``set``. Only 34 encountering an attribute whose type is ``tuple`` or ``set``. Only
29 meaningful if ``recurse`` is ``True``. 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.
30 40
31 :rtype: return type of *dict_factory* 41 :rtype: return type of *dict_factory*
32 42
33 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` 43 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
34 class. 44 class.
35 45
36 .. versionadded:: 16.0.0 *dict_factory* 46 .. versionadded:: 16.0.0 *dict_factory*
37 .. versionadded:: 16.1.0 *retain_collection_types* 47 .. versionadded:: 16.1.0 *retain_collection_types*
48 .. versionadded:: 20.3.0 *value_serializer*
49 .. versionadded:: 21.3.0 If a dict has a collection for a key, it is
50 serialized as a tuple.
38 """ 51 """
39 attrs = fields(inst.__class__) 52 attrs = fields(inst.__class__)
40 rv = dict_factory() 53 rv = dict_factory()
41 for a in attrs: 54 for a in attrs:
42 v = getattr(inst, a.name) 55 v = getattr(inst, a.name)
43 if filter is not None and not filter(a, v): 56 if filter is not None and not filter(a, v):
44 continue 57 continue
58
59 if value_serializer is not None:
60 v = value_serializer(inst, a, v)
61
45 if recurse is True: 62 if recurse is True:
46 if has(v.__class__): 63 if has(v.__class__):
47 rv[a.name] = asdict(v, recurse=True, filter=filter, 64 rv[a.name] = asdict(
48 dict_factory=dict_factory) 65 v,
49 elif isinstance(v, (tuple, list, set)): 66 recurse=True,
67 filter=filter,
68 dict_factory=dict_factory,
69 retain_collection_types=retain_collection_types,
70 value_serializer=value_serializer,
71 )
72 elif isinstance(v, (tuple, list, set, frozenset)):
50 cf = v.__class__ if retain_collection_types is True else list 73 cf = v.__class__ if retain_collection_types is True else list
51 rv[a.name] = cf([ 74 rv[a.name] = cf(
52 asdict(i, recurse=True, filter=filter, 75 [
53 dict_factory=dict_factory) 76 _asdict_anything(
54 if has(i.__class__) else i 77 i,
55 for i in v 78 is_key=False,
56 ]) 79 filter=filter,
80 dict_factory=dict_factory,
81 retain_collection_types=retain_collection_types,
82 value_serializer=value_serializer,
83 )
84 for i in v
85 ]
86 )
57 elif isinstance(v, dict): 87 elif isinstance(v, dict):
58 df = dict_factory 88 df = dict_factory
59 rv[a.name] = df(( 89 rv[a.name] = df(
60 asdict(kk, dict_factory=df) if has(kk.__class__) else kk, 90 (
61 asdict(vv, dict_factory=df) if has(vv.__class__) else vv) 91 _asdict_anything(
62 for kk, vv in iteritems(v)) 92 kk,
93 is_key=True,
94 filter=filter,
95 dict_factory=df,
96 retain_collection_types=retain_collection_types,
97 value_serializer=value_serializer,
98 ),
99 _asdict_anything(
100 vv,
101 is_key=False,
102 filter=filter,
103 dict_factory=df,
104 retain_collection_types=retain_collection_types,
105 value_serializer=value_serializer,
106 ),
107 )
108 for kk, vv in v.items()
109 )
63 else: 110 else:
64 rv[a.name] = v 111 rv[a.name] = v
65 else: 112 else:
66 rv[a.name] = v 113 rv[a.name] = v
67 return rv 114 return rv
68 115
69 116
70 def astuple(inst, recurse=True, filter=None, tuple_factory=tuple, 117 def _asdict_anything(
71 retain_collection_types=False): 118 val,
119 is_key,
120 filter,
121 dict_factory,
122 retain_collection_types,
123 value_serializer,
124 ):
125 """
126 ``asdict`` only works on attrs instances, this works on anything.
127 """
128 if getattr(val.__class__, "__attrs_attrs__", None) is not None:
129 # Attrs class.
130 rv = asdict(
131 val,
132 recurse=True,
133 filter=filter,
134 dict_factory=dict_factory,
135 retain_collection_types=retain_collection_types,
136 value_serializer=value_serializer,
137 )
138 elif isinstance(val, (tuple, list, set, frozenset)):
139 if retain_collection_types is True:
140 cf = val.__class__
141 elif is_key:
142 cf = tuple
143 else:
144 cf = list
145
146 rv = cf(
147 [
148 _asdict_anything(
149 i,
150 is_key=False,
151 filter=filter,
152 dict_factory=dict_factory,
153 retain_collection_types=retain_collection_types,
154 value_serializer=value_serializer,
155 )
156 for i in val
157 ]
158 )
159 elif isinstance(val, dict):
160 df = dict_factory
161 rv = df(
162 (
163 _asdict_anything(
164 kk,
165 is_key=True,
166 filter=filter,
167 dict_factory=df,
168 retain_collection_types=retain_collection_types,
169 value_serializer=value_serializer,
170 ),
171 _asdict_anything(
172 vv,
173 is_key=False,
174 filter=filter,
175 dict_factory=df,
176 retain_collection_types=retain_collection_types,
177 value_serializer=value_serializer,
178 ),
179 )
180 for kk, vv in val.items()
181 )
182 else:
183 rv = val
184 if value_serializer is not None:
185 rv = value_serializer(None, None, rv)
186
187 return rv
188
189
190 def astuple(
191 inst,
192 recurse=True,
193 filter=None,
194 tuple_factory=tuple,
195 retain_collection_types=False,
196 ):
72 """ 197 """
73 Return the ``attrs`` attribute values of *inst* as a tuple. 198 Return the ``attrs`` attribute values of *inst* as a tuple.
74 199
75 Optionally recurse into other ``attrs``-decorated classes. 200 Optionally recurse into other ``attrs``-decorated classes.
76 201
77 :param inst: Instance of an ``attrs``-decorated class. 202 :param inst: Instance of an ``attrs``-decorated class.
78 :param bool recurse: Recurse into classes that are also 203 :param bool recurse: Recurse into classes that are also
79 ``attrs``-decorated. 204 ``attrs``-decorated.
80 :param callable filter: A callable whose return code determines whether an 205 :param callable filter: A callable whose return code determines whether an
81 attribute or element is included (``True``) or dropped (``False``). Is 206 attribute or element is included (``True``) or dropped (``False``). Is
82 called with the :class:`attr.Attribute` as the first argument and the 207 called with the `attrs.Attribute` as the first argument and the
83 value as the second argument. 208 value as the second argument.
84 :param callable tuple_factory: A callable to produce tuples from. For 209 :param callable tuple_factory: A callable to produce tuples from. For
85 example, to produce lists instead of tuples. 210 example, to produce lists instead of tuples.
86 :param bool retain_collection_types: Do not convert to ``list`` 211 :param bool retain_collection_types: Do not convert to ``list``
87 or ``dict`` when encountering an attribute which type is 212 or ``dict`` when encountering an attribute which type is
102 v = getattr(inst, a.name) 227 v = getattr(inst, a.name)
103 if filter is not None and not filter(a, v): 228 if filter is not None and not filter(a, v):
104 continue 229 continue
105 if recurse is True: 230 if recurse is True:
106 if has(v.__class__): 231 if has(v.__class__):
107 rv.append(astuple(v, recurse=True, filter=filter, 232 rv.append(
108 tuple_factory=tuple_factory, 233 astuple(
109 retain_collection_types=retain)) 234 v,
110 elif isinstance(v, (tuple, list, set)): 235 recurse=True,
236 filter=filter,
237 tuple_factory=tuple_factory,
238 retain_collection_types=retain,
239 )
240 )
241 elif isinstance(v, (tuple, list, set, frozenset)):
111 cf = v.__class__ if retain is True else list 242 cf = v.__class__ if retain is True else list
112 rv.append(cf([ 243 rv.append(
113 astuple(j, recurse=True, filter=filter, 244 cf(
114 tuple_factory=tuple_factory, 245 [
115 retain_collection_types=retain) 246 astuple(
116 if has(j.__class__) else j 247 j,
117 for j in v 248 recurse=True,
118 ])) 249 filter=filter,
250 tuple_factory=tuple_factory,
251 retain_collection_types=retain,
252 )
253 if has(j.__class__)
254 else j
255 for j in v
256 ]
257 )
258 )
119 elif isinstance(v, dict): 259 elif isinstance(v, dict):
120 df = v.__class__ if retain is True else dict 260 df = v.__class__ if retain is True else dict
121 rv.append(df( 261 rv.append(
262 df(
122 ( 263 (
123 astuple( 264 astuple(
124 kk, 265 kk,
125 tuple_factory=tuple_factory, 266 tuple_factory=tuple_factory,
126 retain_collection_types=retain 267 retain_collection_types=retain,
127 ) if has(kk.__class__) else kk, 268 )
269 if has(kk.__class__)
270 else kk,
128 astuple( 271 astuple(
129 vv, 272 vv,
130 tuple_factory=tuple_factory, 273 tuple_factory=tuple_factory,
131 retain_collection_types=retain 274 retain_collection_types=retain,
132 ) if has(vv.__class__) else vv 275 )
276 if has(vv.__class__)
277 else vv,
133 ) 278 )
134 for kk, vv in iteritems(v))) 279 for kk, vv in v.items()
280 )
281 )
135 else: 282 else:
136 rv.append(v) 283 rv.append(v)
137 else: 284 else:
138 rv.append(v) 285 rv.append(v)
286
139 return rv if tuple_factory is list else tuple_factory(rv) 287 return rv if tuple_factory is list else tuple_factory(rv)
140 288
141 289
142 def has(cls): 290 def has(cls):
143 """ 291 """
144 Check whether *cls* is a class with ``attrs`` attributes. 292 Check whether *cls* is a class with ``attrs`` attributes.
145 293
146 :param type cls: Class to introspect. 294 :param type cls: Class to introspect.
147 :raise TypeError: If *cls* is not a class. 295 :raise TypeError: If *cls* is not a class.
148 296
149 :rtype: :class:`bool` 297 :rtype: bool
150 """ 298 """
151 return getattr(cls, "__attrs_attrs__", None) is not None 299 return getattr(cls, "__attrs_attrs__", None) is not None
152 300
153 301
154 def assoc(inst, **changes): 302 def assoc(inst, **changes):
164 be found on *cls*. 312 be found on *cls*.
165 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` 313 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
166 class. 314 class.
167 315
168 .. deprecated:: 17.1.0 316 .. deprecated:: 17.1.0
169 Use :func:`evolve` instead. 317 Use `attrs.evolve` instead if you can.
318 This function will not be removed du to the slightly different approach
319 compared to `attrs.evolve`.
170 """ 320 """
171 import warnings 321 import warnings
172 warnings.warn("assoc is deprecated and will be removed after 2018/01.", 322
173 DeprecationWarning) 323 warnings.warn(
324 "assoc is deprecated and will be removed after 2018/01.",
325 DeprecationWarning,
326 stacklevel=2,
327 )
174 new = copy.copy(inst) 328 new = copy.copy(inst)
175 attrs = fields(inst.__class__) 329 attrs = fields(inst.__class__)
176 for k, v in iteritems(changes): 330 for k, v in changes.items():
177 a = getattr(attrs, k, NOTHING) 331 a = getattr(attrs, k, NOTHING)
178 if a is NOTHING: 332 if a is NOTHING:
179 raise AttrsAttributeNotFoundError( 333 raise AttrsAttributeNotFoundError(
180 "{k} is not an attrs attribute on {cl}." 334 "{k} is not an attrs attribute on {cl}.".format(
181 .format(k=k, cl=new.__class__) 335 k=k, cl=new.__class__
336 )
182 ) 337 )
183 _obj_setattr(new, k, v) 338 _obj_setattr(new, k, v)
184 return new 339 return new
185 340
186 341
207 continue 362 continue
208 attr_name = a.name # To deal with private attributes. 363 attr_name = a.name # To deal with private attributes.
209 init_name = attr_name if attr_name[0] != "_" else attr_name[1:] 364 init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
210 if init_name not in changes: 365 if init_name not in changes:
211 changes[init_name] = getattr(inst, attr_name) 366 changes[init_name] = getattr(inst, attr_name)
367
212 return cls(**changes) 368 return cls(**changes)
369
370
371 def resolve_types(cls, globalns=None, localns=None, attribs=None):
372 """
373 Resolve any strings and forward annotations in type annotations.
374
375 This is only required if you need concrete types in `Attribute`'s *type*
376 field. In other words, you don't need to resolve your types if you only
377 use them for static type checking.
378
379 With no arguments, names will be looked up in the module in which the class
380 was created. If this is not what you want, e.g. if the name only exists
381 inside a method, you may pass *globalns* or *localns* to specify other
382 dictionaries in which to look up these names. See the docs of
383 `typing.get_type_hints` for more details.
384
385 :param type cls: Class to resolve.
386 :param Optional[dict] globalns: Dictionary containing global variables.
387 :param Optional[dict] localns: Dictionary containing local variables.
388 :param Optional[list] attribs: List of attribs for the given class.
389 This is necessary when calling from inside a ``field_transformer``
390 since *cls* is not an ``attrs`` class yet.
391
392 :raise TypeError: If *cls* is not a class.
393 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
394 class and you didn't pass any attribs.
395 :raise NameError: If types cannot be resolved because of missing variables.
396
397 :returns: *cls* so you can use this function also as a class decorator.
398 Please note that you have to apply it **after** `attrs.define`. That
399 means the decorator has to come in the line **before** `attrs.define`.
400
401 .. versionadded:: 20.1.0
402 .. versionadded:: 21.1.0 *attribs*
403
404 """
405 # Since calling get_type_hints is expensive we cache whether we've
406 # done it already.
407 if getattr(cls, "__attrs_types_resolved__", None) != cls:
408 import typing
409
410 hints = typing.get_type_hints(cls, globalns=globalns, localns=localns)
411 for field in fields(cls) if attribs is None else attribs:
412 if field.name in hints:
413 # Since fields have been frozen we must work around it.
414 _obj_setattr(field, "type", hints[field.name])
415 # We store the class we resolved so that subclasses know they haven't
416 # been resolved.
417 cls.__attrs_types_resolved__ = cls
418
419 # Return the class so you can use it as a decorator too.
420 return cls