Coverage for /usr/local/lib/python3.7/site-packages/attr/validators.py : 1%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Commonly useful validators.
3"""
5from __future__ import absolute_import, division, print_function
7import re
9from ._make import _AndValidator, and_, attrib, attrs
10from .exceptions import NotCallableError
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]
26@attrs(repr=False, slots=True, hash=True)
27class _InstanceOfValidator(object):
28 type = attrib()
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 )
48 def __repr__(self):
49 return "<instance_of validator for type {type!r}>".format(
50 type=self.type
51 )
54def 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).
60 :param type: The type to check for.
61 :type type: type or tuple of types
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)
70@attrs(repr=False, frozen=True)
71class _MatchesReValidator(object):
72 regex = attrib()
73 flags = attrib()
74 match_func = attrib()
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 )
91 def __repr__(self):
92 return "<matches_re validator for pattern {regex!r}>".format(
93 regex=self.regex
94 )
97def 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*.
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.
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 )
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
139 return _MatchesReValidator(pattern, flags, match_func)
142@attrs(repr=False, slots=True, hash=True)
143class _ProvidesValidator(object):
144 interface = attrib()
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 )
161 def __repr__(self):
162 return "<provides validator for interface {interface!r}>".format(
163 interface=self.interface
164 )
167def 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/>`_).
174 :param zope.interface.Interface interface: The interface to check for.
176 :raises TypeError: With a human readable error message, the attribute
177 (of type `attr.Attribute`), the expected interface, and the
178 value it got.
179 """
180 return _ProvidesValidator(interface)
183@attrs(repr=False, slots=True, hash=True)
184class _OptionalValidator(object):
185 validator = attrib()
187 def __call__(self, inst, attr, value):
188 if value is None:
189 return
191 self.validator(inst, attr, value)
193 def __repr__(self):
194 return "<optional validator for {what} or None>".format(
195 what=repr(self.validator)
196 )
199def optional(validator):
200 """
201 A validator that makes an attribute optional. An optional attribute is one
202 which can be set to ``None`` in addition to satisfying the requirements of
203 the sub-validator.
205 :param validator: A validator (or a list of validators) that is used for
206 non-``None`` values.
207 :type validator: callable or `list` of callables.
209 .. versionadded:: 15.1.0
210 .. versionchanged:: 17.1.0 *validator* can be a list of validators.
211 """
212 if isinstance(validator, list):
213 return _OptionalValidator(_AndValidator(validator))
214 return _OptionalValidator(validator)
217@attrs(repr=False, slots=True, hash=True)
218class _InValidator(object):
219 options = attrib()
221 def __call__(self, inst, attr, value):
222 try:
223 in_options = value in self.options
224 except TypeError: # e.g. `1 in "abc"`
225 in_options = False
227 if not in_options:
228 raise ValueError(
229 "'{name}' must be in {options!r} (got {value!r})".format(
230 name=attr.name, options=self.options, value=value
231 )
232 )
234 def __repr__(self):
235 return "<in_ validator with options {options!r}>".format(
236 options=self.options
237 )
240def in_(options):
241 """
242 A validator that raises a `ValueError` if the initializer is called
243 with a value that does not belong in the options provided. The check is
244 performed using ``value in options``.
246 :param options: Allowed options.
247 :type options: list, tuple, `enum.Enum`, ...
249 :raises ValueError: With a human readable error message, the attribute (of
250 type `attr.Attribute`), the expected options, and the value it
251 got.
253 .. versionadded:: 17.1.0
254 """
255 return _InValidator(options)
258@attrs(repr=False, slots=False, hash=True)
259class _IsCallableValidator(object):
260 def __call__(self, inst, attr, value):
261 """
262 We use a callable class to be able to change the ``__repr__``.
263 """
264 if not callable(value):
265 message = (
266 "'{name}' must be callable "
267 "(got {value!r} that is a {actual!r})."
268 )
269 raise NotCallableError(
270 msg=message.format(
271 name=attr.name, value=value, actual=value.__class__
272 ),
273 value=value,
274 )
276 def __repr__(self):
277 return "<is_callable validator>"
280def is_callable():
281 """
282 A validator that raises a `attr.exceptions.NotCallableError` if the
283 initializer is called with a value for this particular attribute
284 that is not callable.
286 .. versionadded:: 19.1.0
288 :raises `attr.exceptions.NotCallableError`: With a human readable error
289 message containing the attribute (`attr.Attribute`) name,
290 and the value it got.
291 """
292 return _IsCallableValidator()
295@attrs(repr=False, slots=True, hash=True)
296class _DeepIterable(object):
297 member_validator = attrib(validator=is_callable())
298 iterable_validator = attrib(
299 default=None, validator=optional(is_callable())
300 )
302 def __call__(self, inst, attr, value):
303 """
304 We use a callable class to be able to change the ``__repr__``.
305 """
306 if self.iterable_validator is not None:
307 self.iterable_validator(inst, attr, value)
309 for member in value:
310 self.member_validator(inst, attr, member)
312 def __repr__(self):
313 iterable_identifier = (
314 ""
315 if self.iterable_validator is None
316 else " {iterable!r}".format(iterable=self.iterable_validator)
317 )
318 return (
319 "<deep_iterable validator for{iterable_identifier}"
320 " iterables of {member!r}>"
321 ).format(
322 iterable_identifier=iterable_identifier,
323 member=self.member_validator,
324 )
327def deep_iterable(member_validator, iterable_validator=None):
328 """
329 A validator that performs deep validation of an iterable.
331 :param member_validator: Validator to apply to iterable members
332 :param iterable_validator: Validator to apply to iterable itself
333 (optional)
335 .. versionadded:: 19.1.0
337 :raises TypeError: if any sub-validators fail
338 """
339 return _DeepIterable(member_validator, iterable_validator)
342@attrs(repr=False, slots=True, hash=True)
343class _DeepMapping(object):
344 key_validator = attrib(validator=is_callable())
345 value_validator = attrib(validator=is_callable())
346 mapping_validator = attrib(default=None, validator=optional(is_callable()))
348 def __call__(self, inst, attr, value):
349 """
350 We use a callable class to be able to change the ``__repr__``.
351 """
352 if self.mapping_validator is not None:
353 self.mapping_validator(inst, attr, value)
355 for key in value:
356 self.key_validator(inst, attr, key)
357 self.value_validator(inst, attr, value[key])
359 def __repr__(self):
360 return (
361 "<deep_mapping validator for objects mapping {key!r} to {value!r}>"
362 ).format(key=self.key_validator, value=self.value_validator)
365def deep_mapping(key_validator, value_validator, mapping_validator=None):
366 """
367 A validator that performs deep validation of a dictionary.
369 :param key_validator: Validator to apply to dictionary keys
370 :param value_validator: Validator to apply to dictionary values
371 :param mapping_validator: Validator to apply to top-level mapping
372 attribute (optional)
374 .. versionadded:: 19.1.0
376 :raises TypeError: if any sub-validators fail
377 """
378 return _DeepMapping(key_validator, value_validator, mapping_validator)