Mercurial > public > mercurial-scm > hg-stable
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 |