99
1010from jsonschema .exceptions import FormatError
1111
12+ _FormatCheckCallable = typing .Callable [[object ], bool ]
13+ _F = typing .TypeVar ("_F" , bound = _FormatCheckCallable )
14+ _RaisesType = typing .Union [
15+ typing .Type [Exception ], typing .Tuple [typing .Type [Exception ], ...],
16+ ]
17+
1218
1319class FormatChecker (object ):
1420 """
@@ -35,13 +41,10 @@ class FormatChecker(object):
3541
3642 checkers : dict [
3743 str ,
38- tuple [
39- typing .Callable [[typing .Any ], bool ],
40- Exception | tuple [Exception , ...],
41- ],
44+ tuple [_FormatCheckCallable , _RaisesType ],
4245 ] = {}
4346
44- def __init__ (self , formats = None ):
47+ def __init__ (self , formats : typing . Iterable [ str ] | None = None ):
4548 if formats is None :
4649 self .checkers = self .checkers .copy ()
4750 else :
@@ -50,7 +53,9 @@ def __init__(self, formats=None):
5053 def __repr__ (self ):
5154 return "<FormatChecker checkers={}>" .format (sorted (self .checkers ))
5255
53- def checks (self , format , raises = ()):
56+ def checks (
57+ self , format : str , raises : _RaisesType = (),
58+ ) -> typing .Callable [[_F ], _F ]:
5459 """
5560 Register a decorated function as validating a new format.
5661
@@ -70,14 +75,23 @@ def checks(self, format, raises=()):
7075 resulting validation error.
7176 """
7277
73- def _checks (func ) :
78+ def _checks (func : _F ) -> _F :
7479 self .checkers [format ] = (func , raises )
7580 return func
81+
7682 return _checks
7783
78- cls_checks = classmethod (checks )
84+ @classmethod
85+ def cls_checks (
86+ cls , format : str , raises : _RaisesType = (),
87+ ) -> typing .Callable [[_F ], _F ]:
88+ def _checks (func : _F ) -> _F :
89+ cls .checkers [format ] = (func , raises )
90+ return func
91+
92+ return _checks
7993
80- def check (self , instance , format ) :
94+ def check (self , instance : object , format : str ) -> None :
8195 """
8296 Check whether the instance conforms to the given format.
8397
@@ -109,7 +123,7 @@ def check(self, instance, format):
109123 if not result :
110124 raise FormatError (f"{ instance !r} is not a { format !r} " , cause = cause )
111125
112- def conforms (self , instance , format ) :
126+ def conforms (self , instance : object , format : str ) -> bool :
113127 """
114128 Check whether the instance conforms to the given format.
115129
@@ -143,7 +157,7 @@ def conforms(self, instance, format):
143157draft201909_format_checker = FormatChecker ()
144158draft202012_format_checker = FormatChecker ()
145159
146- _draft_checkers = dict (
160+ _draft_checkers : dict [ str , FormatChecker ] = dict (
147161 draft3 = draft3_format_checker ,
148162 draft4 = draft4_format_checker ,
149163 draft6 = draft6_format_checker ,
@@ -162,15 +176,15 @@ def _checks_drafts(
162176 draft201909 = None ,
163177 draft202012 = None ,
164178 raises = (),
165- ):
179+ ) -> typing . Callable [[ _F ], _F ] :
166180 draft3 = draft3 or name
167181 draft4 = draft4 or name
168182 draft6 = draft6 or name
169183 draft7 = draft7 or name
170184 draft201909 = draft201909 or name
171185 draft202012 = draft202012 or name
172186
173- def wrap (func ) :
187+ def wrap (func : _F ) -> _F :
174188 if draft3 :
175189 func = _draft_checkers ["draft3" ].checks (draft3 , raises )(func )
176190 if draft4 :
@@ -195,12 +209,13 @@ def wrap(func):
195209 raises ,
196210 )(func )
197211 return func
212+
198213 return wrap
199214
200215
201216@_checks_drafts (name = "idn-email" )
202217@_checks_drafts (name = "email" )
203- def is_email (instance ) :
218+ def is_email (instance : object ) -> bool :
204219 if not isinstance (instance , str ):
205220 return True
206221 return "@" in instance
@@ -215,14 +230,14 @@ def is_email(instance):
215230 draft202012 = "ipv4" ,
216231 raises = ipaddress .AddressValueError ,
217232)
218- def is_ipv4 (instance ) :
233+ def is_ipv4 (instance : object ) -> bool :
219234 if not isinstance (instance , str ):
220235 return True
221- return ipaddress .IPv4Address (instance )
236+ return bool ( ipaddress .IPv4Address (instance ) )
222237
223238
224239@_checks_drafts (name = "ipv6" , raises = ipaddress .AddressValueError )
225- def is_ipv6 (instance ) :
240+ def is_ipv6 (instance : object ) -> bool :
226241 if not isinstance (instance , str ):
227242 return True
228243 address = ipaddress .IPv6Address (instance )
@@ -240,7 +255,7 @@ def is_ipv6(instance):
240255 draft201909 = "hostname" ,
241256 draft202012 = "hostname" ,
242257 )
243- def is_host_name (instance ) :
258+ def is_host_name (instance : object ) -> bool :
244259 if not isinstance (instance , str ):
245260 return True
246261 return FQDN (instance ).is_valid
@@ -256,7 +271,7 @@ def is_host_name(instance):
256271 draft202012 = "idn-hostname" ,
257272 raises = (idna .IDNAError , UnicodeError ),
258273 )
259- def is_idn_host_name (instance ) :
274+ def is_idn_host_name (instance : object ) -> bool :
260275 if not isinstance (instance , str ):
261276 return True
262277 idna .encode (instance )
@@ -270,7 +285,7 @@ def is_idn_host_name(instance):
270285 from rfc3986_validator import validate_rfc3986
271286
272287 @_checks_drafts (name = "uri" )
273- def is_uri (instance ) :
288+ def is_uri (instance : object ) -> bool :
274289 if not isinstance (instance , str ):
275290 return True
276291 return validate_rfc3986 (instance , rule = "URI" )
@@ -282,19 +297,20 @@ def is_uri(instance):
282297 draft202012 = "uri-reference" ,
283298 raises = ValueError ,
284299 )
285- def is_uri_reference (instance ) :
300+ def is_uri_reference (instance : object ) -> bool :
286301 if not isinstance (instance , str ):
287302 return True
288303 return validate_rfc3986 (instance , rule = "URI_reference" )
289304
290305else :
306+
291307 @_checks_drafts (
292308 draft7 = "iri" ,
293309 draft201909 = "iri" ,
294310 draft202012 = "iri" ,
295311 raises = ValueError ,
296312 )
297- def is_iri (instance ) :
313+ def is_iri (instance : object ) -> bool :
298314 if not isinstance (instance , str ):
299315 return True
300316 return rfc3987 .parse (instance , rule = "IRI" )
@@ -305,13 +321,13 @@ def is_iri(instance):
305321 draft202012 = "iri-reference" ,
306322 raises = ValueError ,
307323 )
308- def is_iri_reference (instance ) :
324+ def is_iri_reference (instance : object ) -> bool :
309325 if not isinstance (instance , str ):
310326 return True
311327 return rfc3987 .parse (instance , rule = "IRI_reference" )
312328
313329 @_checks_drafts (name = "uri" , raises = ValueError )
314- def is_uri (instance ) :
330+ def is_uri (instance : object ) -> bool :
315331 if not isinstance (instance , str ):
316332 return True
317333 return rfc3987 .parse (instance , rule = "URI" )
@@ -323,16 +339,17 @@ def is_uri(instance):
323339 draft202012 = "uri-reference" ,
324340 raises = ValueError ,
325341 )
326- def is_uri_reference (instance ) :
342+ def is_uri_reference (instance : object ) -> bool :
327343 if not isinstance (instance , str ):
328344 return True
329345 return rfc3987 .parse (instance , rule = "URI_reference" )
330346
347+
331348with suppress (ImportError ):
332349 from rfc3339_validator import validate_rfc3339
333350
334351 @_checks_drafts (name = "date-time" )
335- def is_datetime (instance ) :
352+ def is_datetime (instance : object ) -> bool :
336353 if not isinstance (instance , str ):
337354 return True
338355 return validate_rfc3339 (instance .upper ())
@@ -342,17 +359,17 @@ def is_datetime(instance):
342359 draft201909 = "time" ,
343360 draft202012 = "time" ,
344361 )
345- def is_time (instance ) :
362+ def is_time (instance : object ) -> bool :
346363 if not isinstance (instance , str ):
347364 return True
348365 return is_datetime ("1970-01-01T" + instance )
349366
350367
351368@_checks_drafts (name = "regex" , raises = re .error )
352- def is_regex (instance ) :
369+ def is_regex (instance : object ) -> bool :
353370 if not isinstance (instance , str ):
354371 return True
355- return re .compile (instance )
372+ return bool ( re .compile (instance ) )
356373
357374
358375@_checks_drafts (
@@ -362,28 +379,28 @@ def is_regex(instance):
362379 draft202012 = "date" ,
363380 raises = ValueError ,
364381)
365- def is_date (instance ) :
382+ def is_date (instance : object ) -> bool :
366383 if not isinstance (instance , str ):
367384 return True
368- return instance .isascii () and datetime .date .fromisoformat (instance )
385+ return bool ( instance .isascii () and datetime .date .fromisoformat (instance ) )
369386
370387
371388@_checks_drafts (draft3 = "time" , raises = ValueError )
372- def is_draft3_time (instance ) :
389+ def is_draft3_time (instance : object ) -> bool :
373390 if not isinstance (instance , str ):
374391 return True
375- return datetime .datetime .strptime (instance , "%H:%M:%S" )
392+ return bool ( datetime .datetime .strptime (instance , "%H:%M:%S" ) )
376393
377394
378395with suppress (ImportError ):
379396 from webcolors import CSS21_NAMES_TO_HEX
380397 import webcolors
381398
382- def is_css_color_code (instance ) :
399+ def is_css_color_code (instance : object ) -> bool :
383400 return webcolors .normalize_hex (instance )
384401
385402 @_checks_drafts (draft3 = "color" , raises = (ValueError , TypeError ))
386- def is_css21_color (instance ) :
403+ def is_css21_color (instance : object ) -> bool :
387404 if (
388405 not isinstance (instance , str )
389406 or instance .lower () in CSS21_NAMES_TO_HEX
@@ -402,10 +419,10 @@ def is_css21_color(instance):
402419 draft202012 = "json-pointer" ,
403420 raises = jsonpointer .JsonPointerException ,
404421 )
405- def is_json_pointer (instance ) :
422+ def is_json_pointer (instance : object ) -> bool :
406423 if not isinstance (instance , str ):
407424 return True
408- return jsonpointer .JsonPointer (instance )
425+ return bool ( jsonpointer .JsonPointer (instance ) )
409426
410427 # TODO: I don't want to maintain this, so it
411428 # needs to go either into jsonpointer (pending
@@ -417,7 +434,7 @@ def is_json_pointer(instance):
417434 draft202012 = "relative-json-pointer" ,
418435 raises = jsonpointer .JsonPointerException ,
419436 )
420- def is_relative_json_pointer (instance ) :
437+ def is_relative_json_pointer (instance : object ) -> bool :
421438 # Definition taken from:
422439 # https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01#section-3
423440 if not isinstance (instance , str ):
@@ -437,7 +454,7 @@ def is_relative_json_pointer(instance):
437454
438455 rest = instance [i :]
439456 break
440- return (rest == "#" ) or jsonpointer .JsonPointer (rest )
457+ return (rest == "#" ) or bool ( jsonpointer .JsonPointer (rest ) )
441458
442459
443460with suppress (ImportError ):
@@ -449,7 +466,7 @@ def is_relative_json_pointer(instance):
449466 draft201909 = "uri-template" ,
450467 draft202012 = "uri-template" ,
451468 )
452- def is_uri_template (instance ) :
469+ def is_uri_template (instance : object ) -> bool :
453470 if not isinstance (instance , str ):
454471 return True
455472 return uri_template .validate (instance )
@@ -463,18 +480,18 @@ def is_uri_template(instance):
463480 draft202012 = "duration" ,
464481 raises = isoduration .DurationParsingException ,
465482 )
466- def is_duration (instance ) :
483+ def is_duration (instance : object ) -> bool :
467484 if not isinstance (instance , str ):
468485 return True
469- return isoduration .parse_duration (instance )
486+ return bool ( isoduration .parse_duration (instance ) )
470487
471488
472489@_checks_drafts (
473490 draft201909 = "uuid" ,
474491 draft202012 = "uuid" ,
475492 raises = ValueError ,
476493)
477- def is_uuid (instance ) :
494+ def is_uuid (instance : object ) -> bool :
478495 if not isinstance (instance , str ):
479496 return True
480497 UUID (instance )
0 commit comments