Coverage for pydantic/_internal/_validators.py: 100.00%
161 statements
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-21 17:00 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-21 17:00 +0000
1"""Validator functions for standard library types.
3Import of this module is deferred since it contains imports of many standard library modules.
4"""
6from __future__ import annotations as _annotations 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
8import math 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
9import re 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
10import typing 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
11from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
12from typing import Any 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
14from pydantic_core import PydanticCustomError, core_schema 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
15from pydantic_core._pydantic_core import PydanticKnownError 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
18def sequence_validator( 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
19 input_value: typing.Sequence[Any],
20 /,
21 validator: core_schema.ValidatorFunctionWrapHandler,
22) -> typing.Sequence[Any]:
23 """Validator for `Sequence` types, isinstance(v, Sequence) has already been called."""
24 value_type = type(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEF
26 # We don't accept any plain string as a sequence
27 # Relevant issue: https://github.com/pydantic/pydantic/issues/5595
28 if issubclass(value_type, (str, bytes)): 1abcdefghijklmnopqrstuvwxyzABCDEF
29 raise PydanticCustomError( 1abcdefghijklmnopqrstuvwxyzABCDEF
30 'sequence_str',
31 "'{type_name}' instances are not allowed as a Sequence value",
32 {'type_name': value_type.__name__},
33 )
35 # TODO: refactor sequence validation to validate with either a list or a tuple
36 # schema, depending on the type of the value.
37 # Additionally, we should be able to remove one of either this validator or the
38 # SequenceValidator in _std_types_schema.py (preferably this one, while porting over some logic).
39 # Effectively, a refactor for sequence validation is needed.
40 if value_type == tuple: 1abcdefghijklmnopqrstuvwxyzABCDEF
41 input_value = list(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEF
43 v_list = validator(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEF
45 # the rest of the logic is just re-creating the original type from `v_list`
46 if value_type == list: 1abcdefghijklmnopqrstuvwxyzABCDEF
47 return v_list 1abcdefghijklmnopqrstuvwxyzABCDEF
48 elif issubclass(value_type, range): 1abcdefghijklmnopqrstuvwxyzABCDEF
49 # return the list as we probably can't re-create the range
50 return v_list 1abcdefghijklmnopqrstuvwxyzABCDEF
51 elif value_type == tuple: 1abcdefghijklmnopqrstuvwxyzABCDEF
52 return tuple(v_list) 1abcdefghijklmnopqrstuvwxyzABCDEF
53 else:
54 # best guess at how to re-create the original type, more custom construction logic might be required
55 return value_type(v_list) # type: ignore[call-arg] 1abcdefghijklmnopqrstuvwxyzABCDEF
58def import_string(value: Any) -> Any: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
59 if isinstance(value, str): 1abcdefghijklmnopqrstuvwxyzABCDEF
60 try: 1abcdefghijklmnopqrstuvwxyzABCDEF
61 return _import_string_logic(value) 1abcdefghijklmnopqrstuvwxyzABCDEF
62 except ImportError as e: 1abcdefghijklmnopqrstuvwxyzABCDEF
63 raise PydanticCustomError('import_error', 'Invalid python path: {error}', {'error': str(e)}) from e 1abcdefghijklmnopqrstuvwxyzABCDEF
64 else:
65 # otherwise we just return the value and let the next validator do the rest of the work
66 return value 1abcdefghijklmnopqrstuvwxyzABCDEF
69def _import_string_logic(dotted_path: str) -> Any: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
70 """Inspired by uvicorn — dotted paths should include a colon before the final item if that item is not a module.
71 (This is necessary to distinguish between a submodule and an attribute when there is a conflict.).
73 If the dotted path does not include a colon and the final item is not a valid module, importing as an attribute
74 rather than a submodule will be attempted automatically.
76 So, for example, the following values of `dotted_path` result in the following returned values:
77 * 'collections': <module 'collections'>
78 * 'collections.abc': <module 'collections.abc'>
79 * 'collections.abc:Mapping': <class 'collections.abc.Mapping'>
80 * `collections.abc.Mapping`: <class 'collections.abc.Mapping'> (though this is a bit slower than the previous line)
82 An error will be raised under any of the following scenarios:
83 * `dotted_path` contains more than one colon (e.g., 'collections:abc:Mapping')
84 * the substring of `dotted_path` before the colon is not a valid module in the environment (e.g., '123:Mapping')
85 * the substring of `dotted_path` after the colon is not an attribute of the module (e.g., 'collections:abc123')
86 """
87 from importlib import import_module 1abcdefghijklmnopqrstuvwxyzABCDEF
89 components = dotted_path.strip().split(':') 1abcdefghijklmnopqrstuvwxyzABCDEF
90 if len(components) > 2: 1abcdefghijklmnopqrstuvwxyzABCDEF
91 raise ImportError(f"Import strings should have at most one ':'; received {dotted_path!r}") 1abcdefghijklmnopqrstuvwxyzABCDEF
93 module_path = components[0] 1abcdefghijklmnopqrstuvwxyzABCDEF
94 if not module_path: 1abcdefghijklmnopqrstuvwxyzABCDEF
95 raise ImportError(f'Import strings should have a nonempty module name; received {dotted_path!r}') 1abcdefghijklmnopqrstuvwxyzABCDEF
97 try: 1abcdefghijklmnopqrstuvwxyzABCDEF
98 module = import_module(module_path) 1abcdefghijklmnopqrstuvwxyzABCDEF
99 except ModuleNotFoundError as e: 1abcdefghijklmnopqrstuvwxyzABCDEF
100 if '.' in module_path: 1abcdefghijklmnopqrstuvwxyzABCDEF
101 # Check if it would be valid if the final item was separated from its module with a `:`
102 maybe_module_path, maybe_attribute = dotted_path.strip().rsplit('.', 1) 1abcdefghijklmnopqrstuvwxyzABCDEF
103 try: 1abcdefghijklmnopqrstuvwxyzABCDEF
104 return _import_string_logic(f'{maybe_module_path}:{maybe_attribute}') 1abcdefghijklmnopqrstuvwxyzABCDEF
105 except ImportError: 1abcdefghijklmnopqrstuvwxyzABCDEF
106 pass 1abcdefghijklmnopqrstuvwxyzABCDEF
107 raise ImportError(f'No module named {module_path!r}') from e 1abcdefghijklmnopqrstuvwxyzABCDEF
108 raise e 1abcdefghijklmnopqrstuvwxyzABCDEF
110 if len(components) > 1: 1abcdefghijklmnopqrstuvwxyzABCDEF
111 attribute = components[1] 1abcdefghijklmnopqrstuvwxyzABCDEF
112 try: 1abcdefghijklmnopqrstuvwxyzABCDEF
113 return getattr(module, attribute) 1abcdefghijklmnopqrstuvwxyzABCDEF
114 except AttributeError as e: 1abcdefghijklmnopqrstuvwxyzABCDEF
115 raise ImportError(f'cannot import name {attribute!r} from {module_path!r}') from e 1abcdefghijklmnopqrstuvwxyzABCDEF
116 else:
117 return module 1abcdefghijklmnopqrstuvwxyzABCDEF
120def pattern_either_validator(input_value: Any, /) -> typing.Pattern[Any]: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
121 if isinstance(input_value, typing.Pattern): 1abcdefghijklmnopqrstuvwxyzABCDEF
122 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEF
123 elif isinstance(input_value, (str, bytes)): 1abcdefghijklmnopqrstuvwxyzABCDEF
124 # todo strict mode
125 return compile_pattern(input_value) # type: ignore 1abcdefghijklmnopqrstuvwxyzABCDEF
126 else:
127 raise PydanticCustomError('pattern_type', 'Input should be a valid pattern') 1abcdefghijklmnopqrstuvwxyzABCDEF
130def pattern_str_validator(input_value: Any, /) -> typing.Pattern[str]: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
131 if isinstance(input_value, typing.Pattern): 1abcdefghijklmnopqrstuvwxyzABCDEF
132 if isinstance(input_value.pattern, str): 1abcdefghijklmnopqrstuvwxyzABCDEF
133 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEF
134 else:
135 raise PydanticCustomError('pattern_str_type', 'Input should be a string pattern') 1abcdefghijklmnopqrstuvwxyzABCDEF
136 elif isinstance(input_value, str): 1abcdefghijklmnopqrstuvwxyzABCDEF
137 return compile_pattern(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEF
138 elif isinstance(input_value, bytes): 1abcdefghijklmnopqrstuvwxyzABCDEF
139 raise PydanticCustomError('pattern_str_type', 'Input should be a string pattern') 1abcdefghijklmnopqrstuvwxyzABCDEF
140 else:
141 raise PydanticCustomError('pattern_type', 'Input should be a valid pattern') 1abcdefghijklmnopqrstuvwxyzABCDEF
144def pattern_bytes_validator(input_value: Any, /) -> typing.Pattern[bytes]: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
145 if isinstance(input_value, typing.Pattern): 1abcdefghijklmnopqrstuvwxyzABCDEF
146 if isinstance(input_value.pattern, bytes): 1abcdefghijklmnopqrstuvwxyzABCDEF
147 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEF
148 else:
149 raise PydanticCustomError('pattern_bytes_type', 'Input should be a bytes pattern') 1abcdefghijklmnopqrstuvwxyzABCDEF
150 elif isinstance(input_value, bytes): 1abcdefghijklmnopqrstuvwxyzABCDEF
151 return compile_pattern(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEF
152 elif isinstance(input_value, str): 1abcdefghijklmnopqrstuvwxyzABCDEF
153 raise PydanticCustomError('pattern_bytes_type', 'Input should be a bytes pattern') 1abcdefghijklmnopqrstuvwxyzABCDEF
154 else:
155 raise PydanticCustomError('pattern_type', 'Input should be a valid pattern') 1abcdefghijklmnopqrstuvwxyzABCDEF
158PatternType = typing.TypeVar('PatternType', str, bytes) 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
161def compile_pattern(pattern: PatternType) -> typing.Pattern[PatternType]: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
162 try: 1abcdefghijklmnopqrstuvwxyzABCDEF
163 return re.compile(pattern) 1abcdefghijklmnopqrstuvwxyzABCDEF
164 except re.error: 1abcdefghijklmnopqrstuvwxyzABCDEF
165 raise PydanticCustomError('pattern_regex', 'Input should be a valid regular expression') 1abcdefghijklmnopqrstuvwxyzABCDEF
168def ip_v4_address_validator(input_value: Any, /) -> IPv4Address: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
169 if isinstance(input_value, IPv4Address): 1abcdefghijklmnopqrstuvwxyzABCDEF
170 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEF
172 try: 1abcdefghijklmnopqrstuvwxyzABCDEF
173 return IPv4Address(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEF
174 except ValueError: 1abcdefghijklmnopqrstuvwxyzABCDEF
175 raise PydanticCustomError('ip_v4_address', 'Input is not a valid IPv4 address') 1abcdefghijklmnopqrstuvwxyzABCDEF
178def ip_v6_address_validator(input_value: Any, /) -> IPv6Address: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
179 if isinstance(input_value, IPv6Address): 1abcdefghijklmnopqrstuvwxyzABCDEF
180 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEF
182 try: 1abcdefghijklmnopqrstuvwxyzABCDEF
183 return IPv6Address(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEF
184 except ValueError: 1abcdefghijklmnopqrstuvwxyzABCDEF
185 raise PydanticCustomError('ip_v6_address', 'Input is not a valid IPv6 address') 1abcdefghijklmnopqrstuvwxyzABCDEF
188def ip_v4_network_validator(input_value: Any, /) -> IPv4Network: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
189 """Assume IPv4Network initialised with a default `strict` argument.
191 See more:
192 https://docs.python.org/library/ipaddress.html#ipaddress.IPv4Network
193 """
194 if isinstance(input_value, IPv4Network): 1abcdefghijklmnopqrstuvwxyzABCDEF
195 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEF
197 try: 1abcdefghijklmnopqrstuvwxyzABCDEF
198 return IPv4Network(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEF
199 except ValueError: 1abcdefghijklmnopqrstuvwxyzABCDEF
200 raise PydanticCustomError('ip_v4_network', 'Input is not a valid IPv4 network') 1abcdefghijklmnopqrstuvwxyzABCDEF
203def ip_v6_network_validator(input_value: Any, /) -> IPv6Network: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
204 """Assume IPv6Network initialised with a default `strict` argument.
206 See more:
207 https://docs.python.org/library/ipaddress.html#ipaddress.IPv6Network
208 """
209 if isinstance(input_value, IPv6Network): 1abcdefghijklmnopqrstuvwxyzABCDEF
210 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEF
212 try: 1abcdefghijklmnopqrstuvwxyzABCDEF
213 return IPv6Network(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEF
214 except ValueError: 1abcdefghijklmnopqrstuvwxyzABCDEF
215 raise PydanticCustomError('ip_v6_network', 'Input is not a valid IPv6 network') 1abcdefghijklmnopqrstuvwxyzABCDEF
218def ip_v4_interface_validator(input_value: Any, /) -> IPv4Interface: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
219 if isinstance(input_value, IPv4Interface): 1abcdefghijklmnopqrstuvwxyzABCDEF
220 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEF
222 try: 1abcdefghijklmnopqrstuvwxyzABCDEF
223 return IPv4Interface(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEF
224 except ValueError: 1abcdefghijklmnopqrstuvwxyzABCDEF
225 raise PydanticCustomError('ip_v4_interface', 'Input is not a valid IPv4 interface') 1abcdefghijklmnopqrstuvwxyzABCDEF
228def ip_v6_interface_validator(input_value: Any, /) -> IPv6Interface: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
229 if isinstance(input_value, IPv6Interface): 1abcdefghijklmnopqrstuvwxyzABCDEF
230 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEF
232 try: 1abcdefghijklmnopqrstuvwxyzABCDEF
233 return IPv6Interface(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEF
234 except ValueError: 1abcdefghijklmnopqrstuvwxyzABCDEF
235 raise PydanticCustomError('ip_v6_interface', 'Input is not a valid IPv6 interface') 1abcdefghijklmnopqrstuvwxyzABCDEF
238def greater_than_validator(x: Any, gt: Any) -> Any: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
239 if not (x > gt): 1abcdefghijklmnopqrstuvwxyzABCDEF
240 raise PydanticKnownError('greater_than', {'gt': gt}) 1abcdefghijklmnopqrstuvwxyzABCDEF
241 return x 1abcdefghijklmnopqrstuvwxyzABCDEF
244def greater_than_or_equal_validator(x: Any, ge: Any) -> Any: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
245 if not (x >= ge): 1abcdefghijklmnopqrstuvwxyzABCDEF
246 raise PydanticKnownError('greater_than_equal', {'ge': ge}) 1abcdefghijklmnopqrstuvwxyzABCDEF
247 return x 1abcdefghijklmnopqrstuvwxyzABCDEF
250def less_than_validator(x: Any, lt: Any) -> Any: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
251 if not (x < lt): 1abcdefghijklmnopqrstuvwxyzABCDEF
252 raise PydanticKnownError('less_than', {'lt': lt}) 1abcdefghijklmnopqrstuvwxyzABCDEF
253 return x 1abcdefghijklmnopqrstuvwxyzABCDEF
256def less_than_or_equal_validator(x: Any, le: Any) -> Any: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
257 if not (x <= le): 1abcdefghijklmnopqrstuvwxyzABCDEF
258 raise PydanticKnownError('less_than_equal', {'le': le}) 1abcdefghijklmnopqrstuvwxyzABCDEF
259 return x 1abcdefghijklmnopqrstuvwxyzABCDEF
262def multiple_of_validator(x: Any, multiple_of: Any) -> Any: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
263 if not (x % multiple_of == 0): 1abcdefghijklmnopqrstuvwxyzABCDEF
264 raise PydanticKnownError('multiple_of', {'multiple_of': multiple_of}) 1abcdefghijklmnopqrstuvwxyzABCDEF
265 return x 1abcdefghijklmnopqrstuvwxyzABCDEF
268def min_length_validator(x: Any, min_length: Any) -> Any: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
269 if not (len(x) >= min_length): 1abcdefghijklmnopqrstuvwxyzABCDEF
270 raise PydanticKnownError( 1abcdefghijklmnopqrstuvwxyzABCDEF
271 'too_short',
272 {'field_type': 'Value', 'min_length': min_length, 'actual_length': len(x)},
273 )
274 return x 1abcdefghijklmnopqrstuvwxyzABCDEF
277def max_length_validator(x: Any, max_length: Any) -> Any: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
278 if len(x) > max_length: 1abcdefghijklmnopqrstuvwxyzABCDEF
279 raise PydanticKnownError( 1abcdefghijklmnopqrstuvwxyzABCDEF
280 'too_long',
281 {'field_type': 'Value', 'max_length': max_length, 'actual_length': len(x)},
282 )
283 return x 1abcdefghijklmnopqrstuvwxyzABCDEF
286def forbid_inf_nan_check(x: Any) -> Any: 1abcdefghijklmnopqrstuvGHIJKLMNOwxyzABCDEF
287 if not math.isfinite(x): 1abcdefghijklmnopqrstuvwxyzABCDEF
288 raise PydanticKnownError('finite_number') 1abcdefghijklmnopqrstuvwxyzABCDEF
289 return x 1abcdefghijklmnopqrstuvwxyzABCDEF