@@ -176,14 +176,42 @@ class SchemaError(Exception):
176
176
"""
177
177
The provided schema is malformed.
178
178
179
+ The same attributes exist for ``SchemaError``s as for ``ValidationError``s.
180
+
179
181
"""
180
182
183
+ validator = None
184
+
185
+ def __init__ (self , message ):
186
+ super (SchemaError , self ).__init__ (message )
187
+ self .message = message
188
+ self .path = []
189
+
190
+
181
191
class ValidationError (Exception ):
182
192
"""
183
193
The instance didn't properly validate with the provided schema.
184
194
195
+ Relevant attributes are:
196
+ * ``message`` : a human readable message explaining the error
197
+ * ``path`` : a list containing the path to the offending element (or []
198
+ if the error happened globally) in *reverse* order (i.e.
199
+ deepest index first).
200
+
185
201
"""
186
202
203
+ # the failing validator will be set externally at whatever recursion level
204
+ # is immediately above the validation failure
205
+ validator = None
206
+
207
+ def __init__ (self , message ):
208
+ super (ValidationError , self ).__init__ (message )
209
+ self .message = message
210
+
211
+ # Any validator that recurses must append to the ValidationError's
212
+ # path (e.g., properties and items)
213
+ self .path = []
214
+
187
215
188
216
class Validator (object ):
189
217
"""
@@ -350,11 +378,23 @@ def iter_errors(self, instance, schema):
350
378
351
379
try :
352
380
if validator is None :
353
- self .unknown_property (k , instance , schema )
381
+ errors = self .unknown_property (k , instance , schema )
354
382
else :
355
- validator (v , instance , schema )
356
- except ValidationError as e :
357
- yield e
383
+ errors = validator (v , instance , schema )
384
+ except ValidationError as error :
385
+ warnings .warn (
386
+ "Raising errors from validators is deprecated. "
387
+ "Please yield them instead." ,
388
+ DeprecationWarning ,
389
+ stacklevel = 2
390
+ )
391
+ errors = [error ]
392
+
393
+ for error in errors or ():
394
+ # if the validator hasn't already been set (due to recursion)
395
+ # make sure to set it
396
+ error .validator = error .validator or k
397
+ yield error
358
398
359
399
def _validate (self , instance , schema ):
360
400
warnings .warn (
@@ -417,7 +457,7 @@ def validate_type(self, types, instance, schema):
417
457
)):
418
458
return
419
459
else :
420
- raise ValidationError (
460
+ yield ValidationError (
421
461
"%r is not of type %r" % (instance , _delist (types ))
422
462
)
423
463
@@ -429,26 +469,31 @@ def validate_properties(self, properties, instance, schema):
429
469
if property in instance :
430
470
dependencies = _list (subschema .get ("dependencies" , []))
431
471
if self .is_type (dependencies , "object" ):
432
- self .validate (instance , dependencies )
472
+ for error in self .iter_errors (instance , dependencies ):
473
+ yield error
433
474
else :
434
- missing = (d for d in dependencies if d not in instance )
435
- first = next (missing , None )
436
- if first is not None :
437
- raise ValidationError (
438
- "%r is a dependency of %r" % (first , property )
439
- )
440
-
441
- self .validate (instance [property ], subschema )
475
+ for dependency in dependencies :
476
+ if dependency not in instance :
477
+ yield ValidationError (
478
+ "%r is a dependency of %r" % (dependency , property )
479
+ )
480
+
481
+ for error in self .iter_errors (instance [property ], subschema ):
482
+ error .path .append (property )
483
+ yield error
442
484
elif subschema .get ("required" , False ):
443
- raise ValidationError (
485
+ error = ValidationError (
444
486
"%r is a required property" % (property ,)
445
487
)
488
+ error .validator = "required"
489
+ yield error
446
490
447
491
def validate_patternProperties (self , patternProperties , instance , schema ):
448
492
for pattern , subschema in iteritems (patternProperties ):
449
493
for k , v in iteritems (instance ):
450
494
if re .match (pattern , k ):
451
- self .validate (v , subschema )
495
+ for error in self .iter_errors (v , subschema ):
496
+ yield error
452
497
453
498
def validate_additionalProperties (self , aP , instance , schema ):
454
499
if not self .is_type (instance , "object" ):
@@ -459,32 +504,38 @@ def validate_additionalProperties(self, aP, instance, schema):
459
504
460
505
if self .is_type (aP , "object" ):
461
506
for extra in extras :
462
- self .validate (instance [extra ], aP )
507
+ for error in self .iter_errors (instance [extra ], aP ):
508
+ yield error
463
509
elif not aP and extras :
464
510
error = "Additional properties are not allowed (%s %s unexpected)"
465
- raise ValidationError (error % _extras_msg (extras ))
511
+ yield ValidationError (error % _extras_msg (extras ))
466
512
467
513
def validate_items (self , items , instance , schema ):
468
514
if not self .is_type (instance , "array" ):
469
515
return
470
516
471
517
if self .is_type (items , "object" ):
472
- for item in instance :
473
- self .validate (item , items )
518
+ for index , item in enumerate (instance ):
519
+ for error in self .iter_errors (item , items ):
520
+ error .path .append (index )
521
+ yield error
474
522
else :
475
- for item , subschema in zip (instance , items ):
476
- self .validate (item , subschema )
523
+ for (index , item ), subschema in zip (enumerate (instance ), items ):
524
+ for error in self .iter_errors (item , subschema ):
525
+ error .path .append (index )
526
+ yield error
477
527
478
528
def validate_additionalItems (self , aI , instance , schema ):
479
529
if not self .is_type (instance , "array" ):
480
530
return
481
531
482
532
if self .is_type (aI , "object" ):
483
533
for item in instance [len (schema ):]:
484
- self .validate (item , aI )
534
+ for error in self .iter_errors (item , aI ):
535
+ yield error
485
536
elif not aI and len (instance ) > len (schema .get ("items" , [])):
486
537
error = "Additional items are not allowed (%s %s unexpected)"
487
- raise ValidationError (
538
+ yield ValidationError (
488
539
error % _extras_msg (instance [len (schema ) - 1 :])
489
540
)
490
541
@@ -501,7 +552,7 @@ def validate_minimum(self, minimum, instance, schema):
501
552
cmp = "less than"
502
553
503
554
if failed :
504
- raise ValidationError (
555
+ yield ValidationError (
505
556
"%r is %s the minimum of %r" % (instance , cmp , minimum )
506
557
)
507
558
@@ -518,37 +569,37 @@ def validate_maximum(self, maximum, instance, schema):
518
569
cmp = "greater than"
519
570
520
571
if failed :
521
- raise ValidationError (
572
+ yield ValidationError (
522
573
"%r is %s the maximum of %r" % (instance , cmp , maximum )
523
574
)
524
575
525
576
def validate_minItems (self , mI , instance , schema ):
526
577
if self .is_type (instance , "array" ) and len (instance ) < mI :
527
- raise ValidationError ("%r is too short" % (instance ,))
578
+ yield ValidationError ("%r is too short" % (instance ,))
528
579
529
580
def validate_maxItems (self , mI , instance , schema ):
530
581
if self .is_type (instance , "array" ) and len (instance ) > mI :
531
- raise ValidationError ("%r is too long" % (instance ,))
582
+ yield ValidationError ("%r is too long" % (instance ,))
532
583
533
584
def validate_uniqueItems (self , uI , instance , schema ):
534
585
if uI and self .is_type (instance , "array" ) and not _uniq (instance ):
535
- raise ValidationError ("%r has non-unique elements" % instance )
586
+ yield ValidationError ("%r has non-unique elements" % instance )
536
587
537
588
def validate_pattern (self , patrn , instance , schema ):
538
589
if self .is_type (instance , "string" ) and not re .match (patrn , instance ):
539
- raise ValidationError ("%r does not match %r" % (instance , patrn ))
590
+ yield ValidationError ("%r does not match %r" % (instance , patrn ))
540
591
541
592
def validate_minLength (self , mL , instance , schema ):
542
593
if self .is_type (instance , "string" ) and len (instance ) < mL :
543
- raise ValidationError ("%r is too short" % (instance ,))
594
+ yield ValidationError ("%r is too short" % (instance ,))
544
595
545
596
def validate_maxLength (self , mL , instance , schema ):
546
597
if self .is_type (instance , "string" ) and len (instance ) > mL :
547
- raise ValidationError ("%r is too long" % (instance ,))
598
+ yield ValidationError ("%r is too long" % (instance ,))
548
599
549
600
def validate_enum (self , enums , instance , schema ):
550
601
if instance not in enums :
551
- raise ValidationError ("%r is not one of %r" % (instance , enums ))
602
+ yield ValidationError ("%r is not one of %r" % (instance , enums ))
552
603
553
604
def validate_divisibleBy (self , dB , instance , schema ):
554
605
if not self .is_type (instance , "number" ):
@@ -561,21 +612,21 @@ def validate_divisibleBy(self, dB, instance, schema):
561
612
failed = instance % dB
562
613
563
614
if failed :
564
- raise ValidationError ("%r is not divisible by %r" % (instance , dB ))
615
+ yield ValidationError ("%r is not divisible by %r" % (instance , dB ))
565
616
566
617
def validate_disallow (self , disallow , instance , schema ):
567
- disallow = _list (disallow )
568
-
569
- if any (self .is_valid (instance , {"type" : [d ]}) for d in disallow ):
570
- raise ValidationError (
571
- "%r is disallowed for %r" % (_delist (disallow ), instance )
572
- )
618
+ for disallowed in _list (disallow ):
619
+ if self .is_valid (instance , {"type" : [disallowed ]}):
620
+ yield ValidationError (
621
+ "%r is disallowed for %r" % (disallowed , instance )
622
+ )
573
623
574
624
def validate_extends (self , extends , instance , schema ):
575
625
if self .is_type (extends , "object" ):
576
626
extends = [extends ]
577
627
for subschema in extends :
578
- self .validate (instance , subschema )
628
+ for error in self .iter_errors (instance , subschema ):
629
+ yield error
579
630
580
631
581
632
for no_op in [ # handled in:
0 commit comments