9
9
10
10
from jsonschema .exceptions import FormatError
11
11
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
+
12
18
13
19
class FormatChecker (object ):
14
20
"""
@@ -35,13 +41,10 @@ class FormatChecker(object):
35
41
36
42
checkers : dict [
37
43
str ,
38
- tuple [
39
- typing .Callable [[typing .Any ], bool ],
40
- Exception | tuple [Exception , ...],
41
- ],
44
+ tuple [_FormatCheckCallable , _RaisesType ],
42
45
] = {}
43
46
44
- def __init__ (self , formats = None ):
47
+ def __init__ (self , formats : typing . Iterable [ str ] | None = None ):
45
48
if formats is None :
46
49
self .checkers = self .checkers .copy ()
47
50
else :
@@ -50,7 +53,9 @@ def __init__(self, formats=None):
50
53
def __repr__ (self ):
51
54
return "<FormatChecker checkers={}>" .format (sorted (self .checkers ))
52
55
53
- def checks (self , format , raises = ()):
56
+ def checks (
57
+ self , format : str , raises : _RaisesType = (),
58
+ ) -> typing .Callable [[_F ], _F ]:
54
59
"""
55
60
Register a decorated function as validating a new format.
56
61
@@ -70,14 +75,23 @@ def checks(self, format, raises=()):
70
75
resulting validation error.
71
76
"""
72
77
73
- def _checks (func ) :
78
+ def _checks (func : _F ) -> _F :
74
79
self .checkers [format ] = (func , raises )
75
80
return func
81
+
76
82
return _checks
77
83
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
79
93
80
- def check (self , instance , format ) :
94
+ def check (self , instance : object , format : str ) -> None :
81
95
"""
82
96
Check whether the instance conforms to the given format.
83
97
@@ -109,7 +123,7 @@ def check(self, instance, format):
109
123
if not result :
110
124
raise FormatError (f"{ instance !r} is not a { format !r} " , cause = cause )
111
125
112
- def conforms (self , instance , format ) :
126
+ def conforms (self , instance : object , format : str ) -> bool :
113
127
"""
114
128
Check whether the instance conforms to the given format.
115
129
@@ -143,7 +157,7 @@ def conforms(self, instance, format):
143
157
draft201909_format_checker = FormatChecker ()
144
158
draft202012_format_checker = FormatChecker ()
145
159
146
- _draft_checkers = dict (
160
+ _draft_checkers : dict [ str , FormatChecker ] = dict (
147
161
draft3 = draft3_format_checker ,
148
162
draft4 = draft4_format_checker ,
149
163
draft6 = draft6_format_checker ,
@@ -162,15 +176,15 @@ def _checks_drafts(
162
176
draft201909 = None ,
163
177
draft202012 = None ,
164
178
raises = (),
165
- ):
179
+ ) -> typing . Callable [[ _F ], _F ] :
166
180
draft3 = draft3 or name
167
181
draft4 = draft4 or name
168
182
draft6 = draft6 or name
169
183
draft7 = draft7 or name
170
184
draft201909 = draft201909 or name
171
185
draft202012 = draft202012 or name
172
186
173
- def wrap (func ) :
187
+ def wrap (func : _F ) -> _F :
174
188
if draft3 :
175
189
func = _draft_checkers ["draft3" ].checks (draft3 , raises )(func )
176
190
if draft4 :
@@ -195,12 +209,13 @@ def wrap(func):
195
209
raises ,
196
210
)(func )
197
211
return func
212
+
198
213
return wrap
199
214
200
215
201
216
@_checks_drafts (name = "idn-email" )
202
217
@_checks_drafts (name = "email" )
203
- def is_email (instance ) :
218
+ def is_email (instance : object ) -> bool :
204
219
if not isinstance (instance , str ):
205
220
return True
206
221
return "@" in instance
@@ -215,14 +230,14 @@ def is_email(instance):
215
230
draft202012 = "ipv4" ,
216
231
raises = ipaddress .AddressValueError ,
217
232
)
218
- def is_ipv4 (instance ) :
233
+ def is_ipv4 (instance : object ) -> bool :
219
234
if not isinstance (instance , str ):
220
235
return True
221
- return ipaddress .IPv4Address (instance )
236
+ return bool ( ipaddress .IPv4Address (instance ) )
222
237
223
238
224
239
@_checks_drafts (name = "ipv6" , raises = ipaddress .AddressValueError )
225
- def is_ipv6 (instance ) :
240
+ def is_ipv6 (instance : object ) -> bool :
226
241
if not isinstance (instance , str ):
227
242
return True
228
243
address = ipaddress .IPv6Address (instance )
@@ -240,7 +255,7 @@ def is_ipv6(instance):
240
255
draft201909 = "hostname" ,
241
256
draft202012 = "hostname" ,
242
257
)
243
- def is_host_name (instance ) :
258
+ def is_host_name (instance : object ) -> bool :
244
259
if not isinstance (instance , str ):
245
260
return True
246
261
return FQDN (instance ).is_valid
@@ -256,7 +271,7 @@ def is_host_name(instance):
256
271
draft202012 = "idn-hostname" ,
257
272
raises = (idna .IDNAError , UnicodeError ),
258
273
)
259
- def is_idn_host_name (instance ) :
274
+ def is_idn_host_name (instance : object ) -> bool :
260
275
if not isinstance (instance , str ):
261
276
return True
262
277
idna .encode (instance )
@@ -270,7 +285,7 @@ def is_idn_host_name(instance):
270
285
from rfc3986_validator import validate_rfc3986
271
286
272
287
@_checks_drafts (name = "uri" )
273
- def is_uri (instance ) :
288
+ def is_uri (instance : object ) -> bool :
274
289
if not isinstance (instance , str ):
275
290
return True
276
291
return validate_rfc3986 (instance , rule = "URI" )
@@ -282,19 +297,20 @@ def is_uri(instance):
282
297
draft202012 = "uri-reference" ,
283
298
raises = ValueError ,
284
299
)
285
- def is_uri_reference (instance ) :
300
+ def is_uri_reference (instance : object ) -> bool :
286
301
if not isinstance (instance , str ):
287
302
return True
288
303
return validate_rfc3986 (instance , rule = "URI_reference" )
289
304
290
305
else :
306
+
291
307
@_checks_drafts (
292
308
draft7 = "iri" ,
293
309
draft201909 = "iri" ,
294
310
draft202012 = "iri" ,
295
311
raises = ValueError ,
296
312
)
297
- def is_iri (instance ) :
313
+ def is_iri (instance : object ) -> bool :
298
314
if not isinstance (instance , str ):
299
315
return True
300
316
return rfc3987 .parse (instance , rule = "IRI" )
@@ -305,13 +321,13 @@ def is_iri(instance):
305
321
draft202012 = "iri-reference" ,
306
322
raises = ValueError ,
307
323
)
308
- def is_iri_reference (instance ) :
324
+ def is_iri_reference (instance : object ) -> bool :
309
325
if not isinstance (instance , str ):
310
326
return True
311
327
return rfc3987 .parse (instance , rule = "IRI_reference" )
312
328
313
329
@_checks_drafts (name = "uri" , raises = ValueError )
314
- def is_uri (instance ) :
330
+ def is_uri (instance : object ) -> bool :
315
331
if not isinstance (instance , str ):
316
332
return True
317
333
return rfc3987 .parse (instance , rule = "URI" )
@@ -323,16 +339,17 @@ def is_uri(instance):
323
339
draft202012 = "uri-reference" ,
324
340
raises = ValueError ,
325
341
)
326
- def is_uri_reference (instance ) :
342
+ def is_uri_reference (instance : object ) -> bool :
327
343
if not isinstance (instance , str ):
328
344
return True
329
345
return rfc3987 .parse (instance , rule = "URI_reference" )
330
346
347
+
331
348
with suppress (ImportError ):
332
349
from rfc3339_validator import validate_rfc3339
333
350
334
351
@_checks_drafts (name = "date-time" )
335
- def is_datetime (instance ) :
352
+ def is_datetime (instance : object ) -> bool :
336
353
if not isinstance (instance , str ):
337
354
return True
338
355
return validate_rfc3339 (instance .upper ())
@@ -342,17 +359,17 @@ def is_datetime(instance):
342
359
draft201909 = "time" ,
343
360
draft202012 = "time" ,
344
361
)
345
- def is_time (instance ) :
362
+ def is_time (instance : object ) -> bool :
346
363
if not isinstance (instance , str ):
347
364
return True
348
365
return is_datetime ("1970-01-01T" + instance )
349
366
350
367
351
368
@_checks_drafts (name = "regex" , raises = re .error )
352
- def is_regex (instance ) :
369
+ def is_regex (instance : object ) -> bool :
353
370
if not isinstance (instance , str ):
354
371
return True
355
- return re .compile (instance )
372
+ return bool ( re .compile (instance ) )
356
373
357
374
358
375
@_checks_drafts (
@@ -362,28 +379,28 @@ def is_regex(instance):
362
379
draft202012 = "date" ,
363
380
raises = ValueError ,
364
381
)
365
- def is_date (instance ) :
382
+ def is_date (instance : object ) -> bool :
366
383
if not isinstance (instance , str ):
367
384
return True
368
- return instance .isascii () and datetime .date .fromisoformat (instance )
385
+ return bool ( instance .isascii () and datetime .date .fromisoformat (instance ) )
369
386
370
387
371
388
@_checks_drafts (draft3 = "time" , raises = ValueError )
372
- def is_draft3_time (instance ) :
389
+ def is_draft3_time (instance : object ) -> bool :
373
390
if not isinstance (instance , str ):
374
391
return True
375
- return datetime .datetime .strptime (instance , "%H:%M:%S" )
392
+ return bool ( datetime .datetime .strptime (instance , "%H:%M:%S" ) )
376
393
377
394
378
395
with suppress (ImportError ):
379
396
from webcolors import CSS21_NAMES_TO_HEX
380
397
import webcolors
381
398
382
- def is_css_color_code (instance ) :
399
+ def is_css_color_code (instance : object ) -> bool :
383
400
return webcolors .normalize_hex (instance )
384
401
385
402
@_checks_drafts (draft3 = "color" , raises = (ValueError , TypeError ))
386
- def is_css21_color (instance ) :
403
+ def is_css21_color (instance : object ) -> bool :
387
404
if (
388
405
not isinstance (instance , str )
389
406
or instance .lower () in CSS21_NAMES_TO_HEX
@@ -402,10 +419,10 @@ def is_css21_color(instance):
402
419
draft202012 = "json-pointer" ,
403
420
raises = jsonpointer .JsonPointerException ,
404
421
)
405
- def is_json_pointer (instance ) :
422
+ def is_json_pointer (instance : object ) -> bool :
406
423
if not isinstance (instance , str ):
407
424
return True
408
- return jsonpointer .JsonPointer (instance )
425
+ return bool ( jsonpointer .JsonPointer (instance ) )
409
426
410
427
# TODO: I don't want to maintain this, so it
411
428
# needs to go either into jsonpointer (pending
@@ -417,7 +434,7 @@ def is_json_pointer(instance):
417
434
draft202012 = "relative-json-pointer" ,
418
435
raises = jsonpointer .JsonPointerException ,
419
436
)
420
- def is_relative_json_pointer (instance ) :
437
+ def is_relative_json_pointer (instance : object ) -> bool :
421
438
# Definition taken from:
422
439
# https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01#section-3
423
440
if not isinstance (instance , str ):
@@ -437,7 +454,7 @@ def is_relative_json_pointer(instance):
437
454
438
455
rest = instance [i :]
439
456
break
440
- return (rest == "#" ) or jsonpointer .JsonPointer (rest )
457
+ return (rest == "#" ) or bool ( jsonpointer .JsonPointer (rest ) )
441
458
442
459
443
460
with suppress (ImportError ):
@@ -449,7 +466,7 @@ def is_relative_json_pointer(instance):
449
466
draft201909 = "uri-template" ,
450
467
draft202012 = "uri-template" ,
451
468
)
452
- def is_uri_template (instance ) :
469
+ def is_uri_template (instance : object ) -> bool :
453
470
if not isinstance (instance , str ):
454
471
return True
455
472
return uri_template .validate (instance )
@@ -463,18 +480,18 @@ def is_uri_template(instance):
463
480
draft202012 = "duration" ,
464
481
raises = isoduration .DurationParsingException ,
465
482
)
466
- def is_duration (instance ) :
483
+ def is_duration (instance : object ) -> bool :
467
484
if not isinstance (instance , str ):
468
485
return True
469
- return isoduration .parse_duration (instance )
486
+ return bool ( isoduration .parse_duration (instance ) )
470
487
471
488
472
489
@_checks_drafts (
473
490
draft201909 = "uuid" ,
474
491
draft202012 = "uuid" ,
475
492
raises = ValueError ,
476
493
)
477
- def is_uuid (instance ) :
494
+ def is_uuid (instance : object ) -> bool :
478
495
if not isinstance (instance , str ):
479
496
return True
480
497
UUID (instance )
0 commit comments