comparison mercurial/thirdparty/attr/validators.py @ 49643: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
49642:7e6f3c69c0fb 49643:e1c586b9a43c
1 # SPDX-License-Identifier: MIT
2
1 """ 3 """
2 Commonly useful validators. 4 Commonly useful validators.
3 """ 5 """
4 6
5 from __future__ import absolute_import, division, print_function 7
6 8 import operator
7 from ._make import attr, attributes, and_, _AndValidator 9 import re
10
11 from contextlib import contextmanager
12
13 from ._config import get_run_validators, set_run_validators
14 from ._make import _AndValidator, and_, attrib, attrs
15 from .exceptions import NotCallableError
16
17
18 try:
19 Pattern = re.Pattern
20 except AttributeError: # Python <3.7 lacks a Pattern type.
21 Pattern = type(re.compile(""))
8 22
9 23
10 __all__ = [ 24 __all__ = [
11 "and_", 25 "and_",
26 "deep_iterable",
27 "deep_mapping",
28 "disabled",
29 "ge",
30 "get_disabled",
31 "gt",
12 "in_", 32 "in_",
13 "instance_of", 33 "instance_of",
34 "is_callable",
35 "le",
36 "lt",
37 "matches_re",
38 "max_len",
39 "min_len",
14 "optional", 40 "optional",
15 "provides", 41 "provides",
42 "set_disabled",
16 ] 43 ]
17 44
18 45
19 @attributes(repr=False, slots=True, hash=True) 46 def set_disabled(disabled):
20 class _InstanceOfValidator(object): 47 """
21 type = attr() 48 Globally disable or enable running validators.
49
50 By default, they are run.
51
52 :param disabled: If ``True``, disable running all validators.
53 :type disabled: bool
54
55 .. warning::
56
57 This function is not thread-safe!
58
59 .. versionadded:: 21.3.0
60 """
61 set_run_validators(not disabled)
62
63
64 def get_disabled():
65 """
66 Return a bool indicating whether validators are currently disabled or not.
67
68 :return: ``True`` if validators are currently disabled.
69 :rtype: bool
70
71 .. versionadded:: 21.3.0
72 """
73 return not get_run_validators()
74
75
76 @contextmanager
77 def disabled():
78 """
79 Context manager that disables running validators within its context.
80
81 .. warning::
82
83 This context manager is not thread-safe!
84
85 .. versionadded:: 21.3.0
86 """
87 set_run_validators(False)
88 try:
89 yield
90 finally:
91 set_run_validators(True)
92
93
94 @attrs(repr=False, slots=True, hash=True)
95 class _InstanceOfValidator:
96 type = attrib()
22 97
23 def __call__(self, inst, attr, value): 98 def __call__(self, inst, attr, value):
24 """ 99 """
25 We use a callable class to be able to change the ``__repr__``. 100 We use a callable class to be able to change the ``__repr__``.
26 """ 101 """
27 if not isinstance(value, self.type): 102 if not isinstance(value, self.type):
28 raise TypeError( 103 raise TypeError(
29 "'{name}' must be {type!r} (got {value!r} that is a " 104 "'{name}' must be {type!r} (got {value!r} that is a "
30 "{actual!r})." 105 "{actual!r}).".format(
31 .format(name=attr.name, type=self.type, 106 name=attr.name,
32 actual=value.__class__, value=value), 107 type=self.type,
33 attr, self.type, value, 108 actual=value.__class__,
34 ) 109 value=value,
35 110 ),
36 def __repr__(self): 111 attr,
37 return ( 112 self.type,
38 "<instance_of validator for type {type!r}>" 113 value,
39 .format(type=self.type) 114 )
115
116 def __repr__(self):
117 return "<instance_of validator for type {type!r}>".format(
118 type=self.type
40 ) 119 )
41 120
42 121
43 def instance_of(type): 122 def instance_of(type):
44 """ 123 """
45 A validator that raises a :exc:`TypeError` if the initializer is called 124 A validator that raises a `TypeError` if the initializer is called
46 with a wrong type for this particular attribute (checks are perfomed using 125 with a wrong type for this particular attribute (checks are performed using
47 :func:`isinstance` therefore it's also valid to pass a tuple of types). 126 `isinstance` therefore it's also valid to pass a tuple of types).
48 127
49 :param type: The type to check for. 128 :param type: The type to check for.
50 :type type: type or tuple of types 129 :type type: type or tuple of types
51 130
52 :raises TypeError: With a human readable error message, the attribute 131 :raises TypeError: With a human readable error message, the attribute
53 (of type :class:`attr.Attribute`), the expected type, and the value it 132 (of type `attrs.Attribute`), the expected type, and the value it
54 got. 133 got.
55 """ 134 """
56 return _InstanceOfValidator(type) 135 return _InstanceOfValidator(type)
57 136
58 137
59 @attributes(repr=False, slots=True, hash=True) 138 @attrs(repr=False, frozen=True, slots=True)
60 class _ProvidesValidator(object): 139 class _MatchesReValidator:
61 interface = attr() 140 pattern = attrib()
141 match_func = attrib()
142
143 def __call__(self, inst, attr, value):
144 """
145 We use a callable class to be able to change the ``__repr__``.
146 """
147 if not self.match_func(value):
148 raise ValueError(
149 "'{name}' must match regex {pattern!r}"
150 " ({value!r} doesn't)".format(
151 name=attr.name, pattern=self.pattern.pattern, value=value
152 ),
153 attr,
154 self.pattern,
155 value,
156 )
157
158 def __repr__(self):
159 return "<matches_re validator for pattern {pattern!r}>".format(
160 pattern=self.pattern
161 )
162
163
164 def matches_re(regex, flags=0, func=None):
165 r"""
166 A validator that raises `ValueError` if the initializer is called
167 with a string that doesn't match *regex*.
168
169 :param regex: a regex string or precompiled pattern to match against
170 :param int flags: flags that will be passed to the underlying re function
171 (default 0)
172 :param callable func: which underlying `re` function to call. Valid options
173 are `re.fullmatch`, `re.search`, and `re.match`; the default ``None``
174 means `re.fullmatch`. For performance reasons, the pattern is always
175 precompiled using `re.compile`.
176
177 .. versionadded:: 19.2.0
178 .. versionchanged:: 21.3.0 *regex* can be a pre-compiled pattern.
179 """
180 valid_funcs = (re.fullmatch, None, re.search, re.match)
181 if func not in valid_funcs:
182 raise ValueError(
183 "'func' must be one of {}.".format(
184 ", ".join(
185 sorted(
186 e and e.__name__ or "None" for e in set(valid_funcs)
187 )
188 )
189 )
190 )
191
192 if isinstance(regex, Pattern):
193 if flags:
194 raise TypeError(
195 "'flags' can only be used with a string pattern; "
196 "pass flags to re.compile() instead"
197 )
198 pattern = regex
199 else:
200 pattern = re.compile(regex, flags)
201
202 if func is re.match:
203 match_func = pattern.match
204 elif func is re.search:
205 match_func = pattern.search
206 else:
207 match_func = pattern.fullmatch
208
209 return _MatchesReValidator(pattern, match_func)
210
211
212 @attrs(repr=False, slots=True, hash=True)
213 class _ProvidesValidator:
214 interface = attrib()
62 215
63 def __call__(self, inst, attr, value): 216 def __call__(self, inst, attr, value):
64 """ 217 """
65 We use a callable class to be able to change the ``__repr__``. 218 We use a callable class to be able to change the ``__repr__``.
66 """ 219 """
67 if not self.interface.providedBy(value): 220 if not self.interface.providedBy(value):
68 raise TypeError( 221 raise TypeError(
69 "'{name}' must provide {interface!r} which {value!r} " 222 "'{name}' must provide {interface!r} which {value!r} "
70 "doesn't." 223 "doesn't.".format(
71 .format(name=attr.name, interface=self.interface, value=value), 224 name=attr.name, interface=self.interface, value=value
72 attr, self.interface, value, 225 ),
73 ) 226 attr,
74 227 self.interface,
75 def __repr__(self): 228 value,
76 return ( 229 )
77 "<provides validator for interface {interface!r}>" 230
78 .format(interface=self.interface) 231 def __repr__(self):
232 return "<provides validator for interface {interface!r}>".format(
233 interface=self.interface
79 ) 234 )
80 235
81 236
82 def provides(interface): 237 def provides(interface):
83 """ 238 """
84 A validator that raises a :exc:`TypeError` if the initializer is called 239 A validator that raises a `TypeError` if the initializer is called
85 with an object that does not provide the requested *interface* (checks are 240 with an object that does not provide the requested *interface* (checks are
86 performed using ``interface.providedBy(value)`` (see `zope.interface 241 performed using ``interface.providedBy(value)`` (see `zope.interface
87 <https://zopeinterface.readthedocs.io/en/latest/>`_). 242 <https://zopeinterface.readthedocs.io/en/latest/>`_).
88 243
89 :param zope.interface.Interface interface: The interface to check for. 244 :param interface: The interface to check for.
245 :type interface: ``zope.interface.Interface``
90 246
91 :raises TypeError: With a human readable error message, the attribute 247 :raises TypeError: With a human readable error message, the attribute
92 (of type :class:`attr.Attribute`), the expected interface, and the 248 (of type `attrs.Attribute`), the expected interface, and the
93 value it got. 249 value it got.
94 """ 250 """
95 return _ProvidesValidator(interface) 251 return _ProvidesValidator(interface)
96 252
97 253
98 @attributes(repr=False, slots=True, hash=True) 254 @attrs(repr=False, slots=True, hash=True)
99 class _OptionalValidator(object): 255 class _OptionalValidator:
100 validator = attr() 256 validator = attrib()
101 257
102 def __call__(self, inst, attr, value): 258 def __call__(self, inst, attr, value):
103 if value is None: 259 if value is None:
104 return 260 return
105 261
106 self.validator(inst, attr, value) 262 self.validator(inst, attr, value)
107 263
108 def __repr__(self): 264 def __repr__(self):
109 return ( 265 return "<optional validator for {what} or None>".format(
110 "<optional validator for {what} or None>" 266 what=repr(self.validator)
111 .format(what=repr(self.validator))
112 ) 267 )
113 268
114 269
115 def optional(validator): 270 def optional(validator):
116 """ 271 """
118 which can be set to ``None`` in addition to satisfying the requirements of 273 which can be set to ``None`` in addition to satisfying the requirements of
119 the sub-validator. 274 the sub-validator.
120 275
121 :param validator: A validator (or a list of validators) that is used for 276 :param validator: A validator (or a list of validators) that is used for
122 non-``None`` values. 277 non-``None`` values.
123 :type validator: callable or :class:`list` of callables. 278 :type validator: callable or `list` of callables.
124 279
125 .. versionadded:: 15.1.0 280 .. versionadded:: 15.1.0
126 .. versionchanged:: 17.1.0 *validator* can be a list of validators. 281 .. versionchanged:: 17.1.0 *validator* can be a list of validators.
127 """ 282 """
128 if isinstance(validator, list): 283 if isinstance(validator, list):
129 return _OptionalValidator(_AndValidator(validator)) 284 return _OptionalValidator(_AndValidator(validator))
130 return _OptionalValidator(validator) 285 return _OptionalValidator(validator)
131 286
132 287
133 @attributes(repr=False, slots=True, hash=True) 288 @attrs(repr=False, slots=True, hash=True)
134 class _InValidator(object): 289 class _InValidator:
135 options = attr() 290 options = attrib()
136 291
137 def __call__(self, inst, attr, value): 292 def __call__(self, inst, attr, value):
138 if value not in self.options: 293 try:
294 in_options = value in self.options
295 except TypeError: # e.g. `1 in "abc"`
296 in_options = False
297
298 if not in_options:
139 raise ValueError( 299 raise ValueError(
140 "'{name}' must be in {options!r} (got {value!r})" 300 "'{name}' must be in {options!r} (got {value!r})".format(
141 .format(name=attr.name, options=self.options, value=value) 301 name=attr.name, options=self.options, value=value
142 ) 302 ),
143 303 attr,
144 def __repr__(self): 304 self.options,
145 return ( 305 value,
146 "<in_ validator with options {options!r}>" 306 )
147 .format(options=self.options) 307
308 def __repr__(self):
309 return "<in_ validator with options {options!r}>".format(
310 options=self.options
148 ) 311 )
149 312
150 313
151 def in_(options): 314 def in_(options):
152 """ 315 """
153 A validator that raises a :exc:`ValueError` if the initializer is called 316 A validator that raises a `ValueError` if the initializer is called
154 with a value that does not belong in the options provided. The check is 317 with a value that does not belong in the options provided. The check is
155 performed using ``value in options``. 318 performed using ``value in options``.
156 319
157 :param options: Allowed options. 320 :param options: Allowed options.
158 :type options: list, tuple, :class:`enum.Enum`, ... 321 :type options: list, tuple, `enum.Enum`, ...
159 322
160 :raises ValueError: With a human readable error message, the attribute (of 323 :raises ValueError: With a human readable error message, the attribute (of
161 type :class:`attr.Attribute`), the expected options, and the value it 324 type `attrs.Attribute`), the expected options, and the value it
162 got. 325 got.
163 326
164 .. versionadded:: 17.1.0 327 .. versionadded:: 17.1.0
328 .. versionchanged:: 22.1.0
329 The ValueError was incomplete until now and only contained the human
330 readable error message. Now it contains all the information that has
331 been promised since 17.1.0.
165 """ 332 """
166 return _InValidator(options) 333 return _InValidator(options)
334
335
336 @attrs(repr=False, slots=False, hash=True)
337 class _IsCallableValidator:
338 def __call__(self, inst, attr, value):
339 """
340 We use a callable class to be able to change the ``__repr__``.
341 """
342 if not callable(value):
343 message = (
344 "'{name}' must be callable "
345 "(got {value!r} that is a {actual!r})."
346 )
347 raise NotCallableError(
348 msg=message.format(
349 name=attr.name, value=value, actual=value.__class__
350 ),
351 value=value,
352 )
353
354 def __repr__(self):
355 return "<is_callable validator>"
356
357
358 def is_callable():
359 """
360 A validator that raises a `attr.exceptions.NotCallableError` if the
361 initializer is called with a value for this particular attribute
362 that is not callable.
363
364 .. versionadded:: 19.1.0
365
366 :raises `attr.exceptions.NotCallableError`: With a human readable error
367 message containing the attribute (`attrs.Attribute`) name,
368 and the value it got.
369 """
370 return _IsCallableValidator()
371
372
373 @attrs(repr=False, slots=True, hash=True)
374 class _DeepIterable:
375 member_validator = attrib(validator=is_callable())
376 iterable_validator = attrib(
377 default=None, validator=optional(is_callable())
378 )
379
380 def __call__(self, inst, attr, value):
381 """
382 We use a callable class to be able to change the ``__repr__``.
383 """
384 if self.iterable_validator is not None:
385 self.iterable_validator(inst, attr, value)
386
387 for member in value:
388 self.member_validator(inst, attr, member)
389
390 def __repr__(self):
391 iterable_identifier = (
392 ""
393 if self.iterable_validator is None
394 else " {iterable!r}".format(iterable=self.iterable_validator)
395 )
396 return (
397 "<deep_iterable validator for{iterable_identifier}"
398 " iterables of {member!r}>"
399 ).format(
400 iterable_identifier=iterable_identifier,
401 member=self.member_validator,
402 )
403
404
405 def deep_iterable(member_validator, iterable_validator=None):
406 """
407 A validator that performs deep validation of an iterable.
408
409 :param member_validator: Validator(s) to apply to iterable members
410 :param iterable_validator: Validator to apply to iterable itself
411 (optional)
412
413 .. versionadded:: 19.1.0
414
415 :raises TypeError: if any sub-validators fail
416 """
417 if isinstance(member_validator, (list, tuple)):
418 member_validator = and_(*member_validator)
419 return _DeepIterable(member_validator, iterable_validator)
420
421
422 @attrs(repr=False, slots=True, hash=True)
423 class _DeepMapping:
424 key_validator = attrib(validator=is_callable())
425 value_validator = attrib(validator=is_callable())
426 mapping_validator = attrib(default=None, validator=optional(is_callable()))
427
428 def __call__(self, inst, attr, value):
429 """
430 We use a callable class to be able to change the ``__repr__``.
431 """
432 if self.mapping_validator is not None:
433 self.mapping_validator(inst, attr, value)
434
435 for key in value:
436 self.key_validator(inst, attr, key)
437 self.value_validator(inst, attr, value[key])
438
439 def __repr__(self):
440 return (
441 "<deep_mapping validator for objects mapping {key!r} to {value!r}>"
442 ).format(key=self.key_validator, value=self.value_validator)
443
444
445 def deep_mapping(key_validator, value_validator, mapping_validator=None):
446 """
447 A validator that performs deep validation of a dictionary.
448
449 :param key_validator: Validator to apply to dictionary keys
450 :param value_validator: Validator to apply to dictionary values
451 :param mapping_validator: Validator to apply to top-level mapping
452 attribute (optional)
453
454 .. versionadded:: 19.1.0
455
456 :raises TypeError: if any sub-validators fail
457 """
458 return _DeepMapping(key_validator, value_validator, mapping_validator)
459
460
461 @attrs(repr=False, frozen=True, slots=True)
462 class _NumberValidator:
463 bound = attrib()
464 compare_op = attrib()
465 compare_func = attrib()
466
467 def __call__(self, inst, attr, value):
468 """
469 We use a callable class to be able to change the ``__repr__``.
470 """
471 if not self.compare_func(value, self.bound):
472 raise ValueError(
473 "'{name}' must be {op} {bound}: {value}".format(
474 name=attr.name,
475 op=self.compare_op,
476 bound=self.bound,
477 value=value,
478 )
479 )
480
481 def __repr__(self):
482 return "<Validator for x {op} {bound}>".format(
483 op=self.compare_op, bound=self.bound
484 )
485
486
487 def lt(val):
488 """
489 A validator that raises `ValueError` if the initializer is called
490 with a number larger or equal to *val*.
491
492 :param val: Exclusive upper bound for values
493
494 .. versionadded:: 21.3.0
495 """
496 return _NumberValidator(val, "<", operator.lt)
497
498
499 def le(val):
500 """
501 A validator that raises `ValueError` if the initializer is called
502 with a number greater than *val*.
503
504 :param val: Inclusive upper bound for values
505
506 .. versionadded:: 21.3.0
507 """
508 return _NumberValidator(val, "<=", operator.le)
509
510
511 def ge(val):
512 """
513 A validator that raises `ValueError` if the initializer is called
514 with a number smaller than *val*.
515
516 :param val: Inclusive lower bound for values
517
518 .. versionadded:: 21.3.0
519 """
520 return _NumberValidator(val, ">=", operator.ge)
521
522
523 def gt(val):
524 """
525 A validator that raises `ValueError` if the initializer is called
526 with a number smaller or equal to *val*.
527
528 :param val: Exclusive lower bound for values
529
530 .. versionadded:: 21.3.0
531 """
532 return _NumberValidator(val, ">", operator.gt)
533
534
535 @attrs(repr=False, frozen=True, slots=True)
536 class _MaxLengthValidator:
537 max_length = attrib()
538
539 def __call__(self, inst, attr, value):
540 """
541 We use a callable class to be able to change the ``__repr__``.
542 """
543 if len(value) > self.max_length:
544 raise ValueError(
545 "Length of '{name}' must be <= {max}: {len}".format(
546 name=attr.name, max=self.max_length, len=len(value)
547 )
548 )
549
550 def __repr__(self):
551 return "<max_len validator for {max}>".format(max=self.max_length)
552
553
554 def max_len(length):
555 """
556 A validator that raises `ValueError` if the initializer is called
557 with a string or iterable that is longer than *length*.
558
559 :param int length: Maximum length of the string or iterable
560
561 .. versionadded:: 21.3.0
562 """
563 return _MaxLengthValidator(length)
564
565
566 @attrs(repr=False, frozen=True, slots=True)
567 class _MinLengthValidator:
568 min_length = attrib()
569
570 def __call__(self, inst, attr, value):
571 """
572 We use a callable class to be able to change the ``__repr__``.
573 """
574 if len(value) < self.min_length:
575 raise ValueError(
576 "Length of '{name}' must be => {min}: {len}".format(
577 name=attr.name, min=self.min_length, len=len(value)
578 )
579 )
580
581 def __repr__(self):
582 return "<min_len validator for {min}>".format(min=self.min_length)
583
584
585 def min_len(length):
586 """
587 A validator that raises `ValueError` if the initializer is called
588 with a string or iterable that is shorter than *length*.
589
590 :param int length: Minimum length of the string or iterable
591
592 .. versionadded:: 22.1.0
593 """
594 return _MinLengthValidator(length)