Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/attr/validators.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 """ | |
2 Commonly useful validators. | |
3 """ | |
4 | |
5 from __future__ import absolute_import, division, print_function | |
6 | |
7 import re | |
8 | |
9 from ._make import _AndValidator, and_, attrib, attrs | |
10 from .exceptions import NotCallableError | |
11 | |
12 | |
13 __all__ = [ | |
14 "and_", | |
15 "deep_iterable", | |
16 "deep_mapping", | |
17 "in_", | |
18 "instance_of", | |
19 "is_callable", | |
20 "matches_re", | |
21 "optional", | |
22 "provides", | |
23 ] | |
24 | |
25 | |
26 @attrs(repr=False, slots=True, hash=True) | |
27 class _InstanceOfValidator(object): | |
28 type = attrib() | |
29 | |
30 def __call__(self, inst, attr, value): | |
31 """ | |
32 We use a callable class to be able to change the ``__repr__``. | |
33 """ | |
34 if not isinstance(value, self.type): | |
35 raise TypeError( | |
36 "'{name}' must be {type!r} (got {value!r} that is a " | |
37 "{actual!r}).".format( | |
38 name=attr.name, | |
39 type=self.type, | |
40 actual=value.__class__, | |
41 value=value, | |
42 ), | |
43 attr, | |
44 self.type, | |
45 value, | |
46 ) | |
47 | |
48 def __repr__(self): | |
49 return "<instance_of validator for type {type!r}>".format( | |
50 type=self.type | |
51 ) | |
52 | |
53 | |
54 def instance_of(type): | |
55 """ | |
56 A validator that raises a `TypeError` if the initializer is called | |
57 with a wrong type for this particular attribute (checks are performed using | |
58 `isinstance` therefore it's also valid to pass a tuple of types). | |
59 | |
60 :param type: The type to check for. | |
61 :type type: type or tuple of types | |
62 | |
63 :raises TypeError: With a human readable error message, the attribute | |
64 (of type `attr.Attribute`), the expected type, and the value it | |
65 got. | |
66 """ | |
67 return _InstanceOfValidator(type) | |
68 | |
69 | |
70 @attrs(repr=False, frozen=True, slots=True) | |
71 class _MatchesReValidator(object): | |
72 regex = attrib() | |
73 flags = attrib() | |
74 match_func = attrib() | |
75 | |
76 def __call__(self, inst, attr, value): | |
77 """ | |
78 We use a callable class to be able to change the ``__repr__``. | |
79 """ | |
80 if not self.match_func(value): | |
81 raise ValueError( | |
82 "'{name}' must match regex {regex!r}" | |
83 " ({value!r} doesn't)".format( | |
84 name=attr.name, regex=self.regex.pattern, value=value | |
85 ), | |
86 attr, | |
87 self.regex, | |
88 value, | |
89 ) | |
90 | |
91 def __repr__(self): | |
92 return "<matches_re validator for pattern {regex!r}>".format( | |
93 regex=self.regex | |
94 ) | |
95 | |
96 | |
97 def matches_re(regex, flags=0, func=None): | |
98 r""" | |
99 A validator that raises `ValueError` if the initializer is called | |
100 with a string that doesn't match *regex*. | |
101 | |
102 :param str regex: a regex string to match against | |
103 :param int flags: flags that will be passed to the underlying re function | |
104 (default 0) | |
105 :param callable func: which underlying `re` function to call (options | |
106 are `re.fullmatch`, `re.search`, `re.match`, default | |
107 is ``None`` which means either `re.fullmatch` or an emulation of | |
108 it on Python 2). For performance reasons, they won't be used directly | |
109 but on a pre-`re.compile`\ ed pattern. | |
110 | |
111 .. versionadded:: 19.2.0 | |
112 """ | |
113 fullmatch = getattr(re, "fullmatch", None) | |
114 valid_funcs = (fullmatch, None, re.search, re.match) | |
115 if func not in valid_funcs: | |
116 raise ValueError( | |
117 "'func' must be one of %s." | |
118 % ( | |
119 ", ".join( | |
120 sorted( | |
121 e and e.__name__ or "None" for e in set(valid_funcs) | |
122 ) | |
123 ), | |
124 ) | |
125 ) | |
126 | |
127 pattern = re.compile(regex, flags) | |
128 if func is re.match: | |
129 match_func = pattern.match | |
130 elif func is re.search: | |
131 match_func = pattern.search | |
132 else: | |
133 if fullmatch: | |
134 match_func = pattern.fullmatch | |
135 else: | |
136 pattern = re.compile(r"(?:{})\Z".format(regex), flags) | |
137 match_func = pattern.match | |
138 | |
139 return _MatchesReValidator(pattern, flags, match_func) | |
140 | |
141 | |
142 @attrs(repr=False, slots=True, hash=True) | |
143 class _ProvidesValidator(object): | |
144 interface = attrib() | |
145 | |
146 def __call__(self, inst, attr, value): | |
147 """ | |
148 We use a callable class to be able to change the ``__repr__``. | |
149 """ | |
150 if not self.interface.providedBy(value): | |
151 raise TypeError( | |
152 "'{name}' must provide {interface!r} which {value!r} " | |
153 "doesn't.".format( | |
154 name=attr.name, interface=self.interface, value=value | |
155 ), | |
156 attr, | |
157 self.interface, | |
158 value, | |
159 ) | |
160 | |
161 def __repr__(self): | |
162 return "<provides validator for interface {interface!r}>".format( | |
163 interface=self.interface | |
164 ) | |
165 | |
166 | |
167 def provides(interface): | |
168 """ | |
169 A validator that raises a `TypeError` if the initializer is called | |
170 with an object that does not provide the requested *interface* (checks are | |
171 performed using ``interface.providedBy(value)`` (see `zope.interface | |
172 <https://zopeinterface.readthedocs.io/en/latest/>`_). | |
173 | |
174 :param interface: The interface to check for. | |
175 :type interface: ``zope.interface.Interface`` | |
176 | |
177 :raises TypeError: With a human readable error message, the attribute | |
178 (of type `attr.Attribute`), the expected interface, and the | |
179 value it got. | |
180 """ | |
181 return _ProvidesValidator(interface) | |
182 | |
183 | |
184 @attrs(repr=False, slots=True, hash=True) | |
185 class _OptionalValidator(object): | |
186 validator = attrib() | |
187 | |
188 def __call__(self, inst, attr, value): | |
189 if value is None: | |
190 return | |
191 | |
192 self.validator(inst, attr, value) | |
193 | |
194 def __repr__(self): | |
195 return "<optional validator for {what} or None>".format( | |
196 what=repr(self.validator) | |
197 ) | |
198 | |
199 | |
200 def optional(validator): | |
201 """ | |
202 A validator that makes an attribute optional. An optional attribute is one | |
203 which can be set to ``None`` in addition to satisfying the requirements of | |
204 the sub-validator. | |
205 | |
206 :param validator: A validator (or a list of validators) that is used for | |
207 non-``None`` values. | |
208 :type validator: callable or `list` of callables. | |
209 | |
210 .. versionadded:: 15.1.0 | |
211 .. versionchanged:: 17.1.0 *validator* can be a list of validators. | |
212 """ | |
213 if isinstance(validator, list): | |
214 return _OptionalValidator(_AndValidator(validator)) | |
215 return _OptionalValidator(validator) | |
216 | |
217 | |
218 @attrs(repr=False, slots=True, hash=True) | |
219 class _InValidator(object): | |
220 options = attrib() | |
221 | |
222 def __call__(self, inst, attr, value): | |
223 try: | |
224 in_options = value in self.options | |
225 except TypeError: # e.g. `1 in "abc"` | |
226 in_options = False | |
227 | |
228 if not in_options: | |
229 raise ValueError( | |
230 "'{name}' must be in {options!r} (got {value!r})".format( | |
231 name=attr.name, options=self.options, value=value | |
232 ) | |
233 ) | |
234 | |
235 def __repr__(self): | |
236 return "<in_ validator with options {options!r}>".format( | |
237 options=self.options | |
238 ) | |
239 | |
240 | |
241 def in_(options): | |
242 """ | |
243 A validator that raises a `ValueError` if the initializer is called | |
244 with a value that does not belong in the options provided. The check is | |
245 performed using ``value in options``. | |
246 | |
247 :param options: Allowed options. | |
248 :type options: list, tuple, `enum.Enum`, ... | |
249 | |
250 :raises ValueError: With a human readable error message, the attribute (of | |
251 type `attr.Attribute`), the expected options, and the value it | |
252 got. | |
253 | |
254 .. versionadded:: 17.1.0 | |
255 """ | |
256 return _InValidator(options) | |
257 | |
258 | |
259 @attrs(repr=False, slots=False, hash=True) | |
260 class _IsCallableValidator(object): | |
261 def __call__(self, inst, attr, value): | |
262 """ | |
263 We use a callable class to be able to change the ``__repr__``. | |
264 """ | |
265 if not callable(value): | |
266 message = ( | |
267 "'{name}' must be callable " | |
268 "(got {value!r} that is a {actual!r})." | |
269 ) | |
270 raise NotCallableError( | |
271 msg=message.format( | |
272 name=attr.name, value=value, actual=value.__class__ | |
273 ), | |
274 value=value, | |
275 ) | |
276 | |
277 def __repr__(self): | |
278 return "<is_callable validator>" | |
279 | |
280 | |
281 def is_callable(): | |
282 """ | |
283 A validator that raises a `attr.exceptions.NotCallableError` if the | |
284 initializer is called with a value for this particular attribute | |
285 that is not callable. | |
286 | |
287 .. versionadded:: 19.1.0 | |
288 | |
289 :raises `attr.exceptions.NotCallableError`: With a human readable error | |
290 message containing the attribute (`attr.Attribute`) name, | |
291 and the value it got. | |
292 """ | |
293 return _IsCallableValidator() | |
294 | |
295 | |
296 @attrs(repr=False, slots=True, hash=True) | |
297 class _DeepIterable(object): | |
298 member_validator = attrib(validator=is_callable()) | |
299 iterable_validator = attrib( | |
300 default=None, validator=optional(is_callable()) | |
301 ) | |
302 | |
303 def __call__(self, inst, attr, value): | |
304 """ | |
305 We use a callable class to be able to change the ``__repr__``. | |
306 """ | |
307 if self.iterable_validator is not None: | |
308 self.iterable_validator(inst, attr, value) | |
309 | |
310 for member in value: | |
311 self.member_validator(inst, attr, member) | |
312 | |
313 def __repr__(self): | |
314 iterable_identifier = ( | |
315 "" | |
316 if self.iterable_validator is None | |
317 else " {iterable!r}".format(iterable=self.iterable_validator) | |
318 ) | |
319 return ( | |
320 "<deep_iterable validator for{iterable_identifier}" | |
321 " iterables of {member!r}>" | |
322 ).format( | |
323 iterable_identifier=iterable_identifier, | |
324 member=self.member_validator, | |
325 ) | |
326 | |
327 | |
328 def deep_iterable(member_validator, iterable_validator=None): | |
329 """ | |
330 A validator that performs deep validation of an iterable. | |
331 | |
332 :param member_validator: Validator to apply to iterable members | |
333 :param iterable_validator: Validator to apply to iterable itself | |
334 (optional) | |
335 | |
336 .. versionadded:: 19.1.0 | |
337 | |
338 :raises TypeError: if any sub-validators fail | |
339 """ | |
340 return _DeepIterable(member_validator, iterable_validator) | |
341 | |
342 | |
343 @attrs(repr=False, slots=True, hash=True) | |
344 class _DeepMapping(object): | |
345 key_validator = attrib(validator=is_callable()) | |
346 value_validator = attrib(validator=is_callable()) | |
347 mapping_validator = attrib(default=None, validator=optional(is_callable())) | |
348 | |
349 def __call__(self, inst, attr, value): | |
350 """ | |
351 We use a callable class to be able to change the ``__repr__``. | |
352 """ | |
353 if self.mapping_validator is not None: | |
354 self.mapping_validator(inst, attr, value) | |
355 | |
356 for key in value: | |
357 self.key_validator(inst, attr, key) | |
358 self.value_validator(inst, attr, value[key]) | |
359 | |
360 def __repr__(self): | |
361 return ( | |
362 "<deep_mapping validator for objects mapping {key!r} to {value!r}>" | |
363 ).format(key=self.key_validator, value=self.value_validator) | |
364 | |
365 | |
366 def deep_mapping(key_validator, value_validator, mapping_validator=None): | |
367 """ | |
368 A validator that performs deep validation of a dictionary. | |
369 | |
370 :param key_validator: Validator to apply to dictionary keys | |
371 :param value_validator: Validator to apply to dictionary values | |
372 :param mapping_validator: Validator to apply to top-level mapping | |
373 attribute (optional) | |
374 | |
375 .. versionadded:: 19.1.0 | |
376 | |
377 :raises TypeError: if any sub-validators fail | |
378 """ | |
379 return _DeepMapping(key_validator, value_validator, mapping_validator) |