Coverage for pydantic/_internal/_validators.py: 99.15%

167 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-07-03 19:29 +0000

1"""Validator functions for standard library types. 

2 

3Import of this module is deferred since it contains imports of many standard library modules. 

4""" 

5 

6from __future__ import annotations as _annotations 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

7 

8import math 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

9import re 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

10import typing 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

11from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

12from typing import Any, Callable 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

13 

14from pydantic_core import PydanticCustomError, core_schema 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

15from pydantic_core._pydantic_core import PydanticKnownError 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

16 

17 

18def sequence_validator( 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

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) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

25 

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)): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

29 raise PydanticCustomError( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

30 'sequence_str', 

31 "'{type_name}' instances are not allowed as a Sequence value", 

32 {'type_name': value_type.__name__}, 

33 ) 

34 

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 is tuple: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

41 input_value = list(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

42 

43 v_list = validator(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

44 

45 # the rest of the logic is just re-creating the original type from `v_list` 

46 if value_type is list: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

47 return v_list 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

48 elif issubclass(value_type, range): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

49 # return the list as we probably can't re-create the range 

50 return v_list 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

51 elif value_type is tuple: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

52 return tuple(v_list) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

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] 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

56 

57 

58def import_string(value: Any) -> Any: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

59 if isinstance(value, str): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

60 try: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

61 return _import_string_logic(value) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

62 except ImportError as e: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

63 raise PydanticCustomError('import_error', 'Invalid python path: {error}', {'error': str(e)}) from e 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

64 else: 

65 # otherwise we just return the value and let the next validator do the rest of the work 

66 return value 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

67 

68 

69def _import_string_logic(dotted_path: str) -> Any: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

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.). 

72 

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. 

75 

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) 

81 

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 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

88 

89 components = dotted_path.strip().split(':') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

90 if len(components) > 2: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

91 raise ImportError(f"Import strings should have at most one ':'; received {dotted_path!r}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

92 

93 module_path = components[0] 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

94 if not module_path: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

95 raise ImportError(f'Import strings should have a nonempty module name; received {dotted_path!r}') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

96 

97 try: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

98 module = import_module(module_path) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

99 except ModuleNotFoundError as e: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

100 if '.' in module_path: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

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) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

103 try: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

104 return _import_string_logic(f'{maybe_module_path}:{maybe_attribute}') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

105 except ImportError: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

106 pass 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

107 raise ImportError(f'No module named {module_path!r}') from e 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

108 raise e 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

109 

110 if len(components) > 1: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

111 attribute = components[1] 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

112 try: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

113 return getattr(module, attribute) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

114 except AttributeError as e: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

115 raise ImportError(f'cannot import name {attribute!r} from {module_path!r}') from e 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

116 else: 

117 return module 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

118 

119 

120def pattern_either_validator(input_value: Any, /) -> typing.Pattern[Any]: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

121 if isinstance(input_value, typing.Pattern): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

122 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

123 elif isinstance(input_value, (str, bytes)): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

124 # todo strict mode 

125 return compile_pattern(input_value) # type: ignore 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

126 else: 

127 raise PydanticCustomError('pattern_type', 'Input should be a valid pattern') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

128 

129 

130def pattern_str_validator(input_value: Any, /) -> typing.Pattern[str]: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

131 if isinstance(input_value, typing.Pattern): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

132 if isinstance(input_value.pattern, str): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

133 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

134 else: 

135 raise PydanticCustomError('pattern_str_type', 'Input should be a string pattern') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

136 elif isinstance(input_value, str): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

137 return compile_pattern(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

138 elif isinstance(input_value, bytes): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

139 raise PydanticCustomError('pattern_str_type', 'Input should be a string pattern') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

140 else: 

141 raise PydanticCustomError('pattern_type', 'Input should be a valid pattern') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

142 

143 

144def pattern_bytes_validator(input_value: Any, /) -> typing.Pattern[bytes]: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

145 if isinstance(input_value, typing.Pattern): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

146 if isinstance(input_value.pattern, bytes): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

147 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

148 else: 

149 raise PydanticCustomError('pattern_bytes_type', 'Input should be a bytes pattern') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

150 elif isinstance(input_value, bytes): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

151 return compile_pattern(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

152 elif isinstance(input_value, str): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

153 raise PydanticCustomError('pattern_bytes_type', 'Input should be a bytes pattern') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

154 else: 

155 raise PydanticCustomError('pattern_type', 'Input should be a valid pattern') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

156 

157 

158PatternType = typing.TypeVar('PatternType', str, bytes) 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

159 

160 

161def compile_pattern(pattern: PatternType) -> typing.Pattern[PatternType]: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

162 try: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

163 return re.compile(pattern) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

164 except re.error: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

165 raise PydanticCustomError('pattern_regex', 'Input should be a valid regular expression') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

166 

167 

168def ip_v4_address_validator(input_value: Any, /) -> IPv4Address: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

169 if isinstance(input_value, IPv4Address): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

170 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

171 

172 try: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

173 return IPv4Address(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

174 except ValueError: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

175 raise PydanticCustomError('ip_v4_address', 'Input is not a valid IPv4 address') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

176 

177 

178def ip_v6_address_validator(input_value: Any, /) -> IPv6Address: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

179 if isinstance(input_value, IPv6Address): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

180 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

181 

182 try: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

183 return IPv6Address(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

184 except ValueError: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

185 raise PydanticCustomError('ip_v6_address', 'Input is not a valid IPv6 address') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

186 

187 

188def ip_v4_network_validator(input_value: Any, /) -> IPv4Network: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

189 """Assume IPv4Network initialised with a default `strict` argument. 

190 

191 See more: 

192 https://docs.python.org/library/ipaddress.html#ipaddress.IPv4Network 

193 """ 

194 if isinstance(input_value, IPv4Network): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

195 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

196 

197 try: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

198 return IPv4Network(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

199 except ValueError: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

200 raise PydanticCustomError('ip_v4_network', 'Input is not a valid IPv4 network') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

201 

202 

203def ip_v6_network_validator(input_value: Any, /) -> IPv6Network: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

204 """Assume IPv6Network initialised with a default `strict` argument. 

205 

206 See more: 

207 https://docs.python.org/library/ipaddress.html#ipaddress.IPv6Network 

208 """ 

209 if isinstance(input_value, IPv6Network): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

210 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

211 

212 try: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

213 return IPv6Network(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

214 except ValueError: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

215 raise PydanticCustomError('ip_v6_network', 'Input is not a valid IPv6 network') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

216 

217 

218def ip_v4_interface_validator(input_value: Any, /) -> IPv4Interface: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

219 if isinstance(input_value, IPv4Interface): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

220 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

221 

222 try: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

223 return IPv4Interface(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

224 except ValueError: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

225 raise PydanticCustomError('ip_v4_interface', 'Input is not a valid IPv4 interface') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

226 

227 

228def ip_v6_interface_validator(input_value: Any, /) -> IPv6Interface: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

229 if isinstance(input_value, IPv6Interface): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

230 return input_value 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

231 

232 try: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

233 return IPv6Interface(input_value) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

234 except ValueError: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

235 raise PydanticCustomError('ip_v6_interface', 'Input is not a valid IPv6 interface') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

236 

237 

238def greater_than_validator(x: Any, gt: Any) -> Any: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

239 if not (x > gt): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

240 raise PydanticKnownError('greater_than', {'gt': gt}) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

241 return x 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

242 

243 

244def greater_than_or_equal_validator(x: Any, ge: Any) -> Any: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

245 if not (x >= ge): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

246 raise PydanticKnownError('greater_than_equal', {'ge': ge}) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

247 return x 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

248 

249 

250def less_than_validator(x: Any, lt: Any) -> Any: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

251 if not (x < lt): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

252 raise PydanticKnownError('less_than', {'lt': lt}) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

253 return x 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

254 

255 

256def less_than_or_equal_validator(x: Any, le: Any) -> Any: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

257 if not (x <= le): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

258 raise PydanticKnownError('less_than_equal', {'le': le}) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

259 return x 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

260 

261 

262def multiple_of_validator(x: Any, multiple_of: Any) -> Any: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

263 if not (x % multiple_of == 0): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

264 raise PydanticKnownError('multiple_of', {'multiple_of': multiple_of}) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

265 return x 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

266 

267 

268def min_length_validator(x: Any, min_length: Any) -> Any: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

269 if not (len(x) >= min_length): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

270 raise PydanticKnownError( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

271 'too_short', 

272 {'field_type': 'Value', 'min_length': min_length, 'actual_length': len(x)}, 

273 ) 

274 return x 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

275 

276 

277def max_length_validator(x: Any, max_length: Any) -> Any: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

278 if len(x) > max_length: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

279 raise PydanticKnownError( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

280 'too_long', 

281 {'field_type': 'Value', 'max_length': max_length, 'actual_length': len(x)}, 

282 ) 

283 return x 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

284 

285 

286def forbid_inf_nan_check(x: Any) -> Any: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

287 if not math.isfinite(x): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

288 raise PydanticKnownError('finite_number') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

289 return x 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

290 

291 

292_CONSTRAINT_TO_VALIDATOR_MAP: dict[str, Callable] = { 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

293 'gt': greater_than_validator, 

294 'ge': greater_than_or_equal_validator, 

295 'lt': less_than_validator, 

296 'le': less_than_or_equal_validator, 

297 'multiple_of': multiple_of_validator, 

298 'min_length': min_length_validator, 

299 'max_length': max_length_validator, 

300} 

301 

302 

303def get_constraint_validator(constraint: str) -> Callable: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

304 """Fetch the validator function for the given constraint.""" 

305 try: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

306 return _CONSTRAINT_TO_VALIDATOR_MAP[constraint] 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

307 except KeyError: 

308 raise TypeError(f'Unknown constraint {constraint}')