@@ -53,9 +53,9 @@ def __repr__(self):
53
53
class Validation (object ):
54
54
"""
55
55
Validation provides a set of default validations that can used to validate
56
- an odml.Document . Custom validations can be added via the 'register_handler' method.
56
+ odml objects . Custom validations can be added via the 'register_handler' method.
57
57
58
- :param doc : odml.Document that the validation will be applied to.
58
+ :param obj : odml object the validation will be applied to.
59
59
"""
60
60
61
61
_handlers = {}
@@ -77,19 +77,18 @@ def register_handler(klass, handler):
77
77
"""
78
78
Validation ._handlers .setdefault (klass , set ()).add (handler )
79
79
80
- def __init__ (self , obj ):
81
- self .doc = obj # may also be a section
80
+ def __init__ (self , obj , validate = True , reset = False ):
81
+ self .obj = obj # may also be a section
82
82
self .errors = []
83
83
84
- self .validate (obj )
85
-
86
- if obj .format ().name == "property" :
84
+ # If initialized with reset=True, reset all handlers and
85
+ # do not run any validation yet to allow custom Validation objects.
86
+ if reset :
87
+ self ._handlers = {}
87
88
return
88
89
89
- for sec in obj .itersections (recursive = True ):
90
- self .validate (sec )
91
- for prop in sec .properties :
92
- self .validate (prop )
90
+ if validate :
91
+ self .run_validation ()
93
92
94
93
def validate (self , obj ):
95
94
"""
@@ -109,6 +108,38 @@ def error(self, validation_error):
109
108
"""
110
109
self .errors .append (validation_error )
111
110
111
+ def run_validation (self ):
112
+ """
113
+ Runs a clean new validation on the registered Validation object.
114
+ """
115
+ self .errors = []
116
+
117
+ self .validate (self .obj )
118
+
119
+ if self .obj .format ().name == "property" :
120
+ return
121
+
122
+ for sec in self .obj .itersections (recursive = True ):
123
+ self .validate (sec )
124
+ for prop in sec .properties :
125
+ self .validate (prop )
126
+
127
+ def register_custom_handler (self , klass , handler ):
128
+ """
129
+ Adds a validation handler for an odml class. The handler is called in the
130
+ validation process for each corresponding object.
131
+ The *handler* is assumed to be a generator function yielding
132
+ all ValidationErrors it finds.
133
+
134
+ Section handlers are only called for sections and not for the document node.
135
+ If both are required, the handler needs to be registered twice.
136
+
137
+ :param klass: string corresponding to an odml class. Valid strings are
138
+ 'odML', 'section' and 'property'.
139
+ :param handler: validation function applied to the odml class.
140
+ """
141
+ self ._handlers .setdefault (klass , set ()).add (handler )
142
+
112
143
def __getitem__ (self , obj ):
113
144
"""
114
145
Return a list of the errors for a certain object.
@@ -455,59 +486,89 @@ def property_values_string_check(prop):
455
486
Validation .register_handler ('property' , property_values_string_check )
456
487
457
488
458
- def section_properties_cardinality (obj ):
489
+ def _cardinality_validation (obj , cardinality , card_target_attr , validation_rank ):
459
490
"""
460
- Checks Section properties against any set property cardinality.
491
+ Helper function that validates the cardinality of an odml object attribute.
492
+ Valid object-attribute combinations are Section-sections, Section-properties and
493
+ Property-values.
461
494
462
- :param obj: odml.Section
463
- :return: Yields a ValidationError warning, if a set cardinality is not met.
495
+ :param obj: an odml.Section or an odml.Property
496
+ :param cardinality: 2-int tuple containing the cardinality value
497
+ :param card_target_attr: string containing the name of the attribute the cardinality is
498
+ applied against. Supported values are:
499
+ 'sections', 'properties' or 'values'
500
+ :param validation_rank: Rank of the yielded ValidationError.
501
+
502
+ :return: Returns a ValidationError, if a set cardinality is not met or None.
464
503
"""
465
- if obj .prop_cardinality and isinstance (obj .prop_cardinality , tuple ):
504
+ err = None
505
+ if cardinality and isinstance (cardinality , tuple ):
466
506
467
- val_min = obj . prop_cardinality [0 ]
468
- val_max = obj . prop_cardinality [1 ]
507
+ val_min = cardinality [0 ]
508
+ val_max = cardinality [1 ]
469
509
470
- val_len = len (obj .properties ) if obj .properties else 0
510
+ card_target = getattr (obj , card_target_attr )
511
+ val_len = len (card_target ) if card_target else 0
471
512
472
513
invalid_cause = ""
473
514
if val_min and val_len < val_min :
474
515
invalid_cause = "minimum %s" % val_min
475
- elif val_max and ( obj . properties and len ( obj . properties ) > val_max ) :
516
+ elif val_max and val_len > val_max :
476
517
invalid_cause = "maximum %s" % val_max
477
518
478
519
if invalid_cause :
479
- msg = "Section properties cardinality violated"
520
+ obj_name = obj .format ().name .capitalize ()
521
+ msg = "%s %s cardinality violated" % (obj_name , card_target_attr )
480
522
msg += " (%s values, %s found)" % (invalid_cause , val_len )
481
- yield ValidationError (obj , msg , LABEL_WARNING )
523
+
524
+ err = ValidationError (obj , msg , validation_rank )
525
+
526
+ return err
527
+
528
+
529
+ def section_properties_cardinality (obj ):
530
+ """
531
+ Checks Section properties against any set property cardinality.
532
+
533
+ :param obj: odml.Section
534
+
535
+ :return: Yields a ValidationError warning, if a set cardinality is not met.
536
+ """
537
+ err = _cardinality_validation (obj , obj .prop_cardinality , 'properties' , LABEL_WARNING )
538
+ if err :
539
+ yield err
482
540
483
541
484
542
Validation .register_handler ("section" , section_properties_cardinality )
485
543
486
544
487
- def property_values_cardinality ( prop ):
545
+ def section_sections_cardinality ( obj ):
488
546
"""
489
- Checks Property values against any set value cardinality.
547
+ Checks Section sub-sections against any set sub-section cardinality.
548
+
549
+ :param obj: odml.Section
490
550
491
- :param prop: odml.Property
492
551
:return: Yields a ValidationError warning, if a set cardinality is not met.
493
552
"""
494
- if prop .val_cardinality and isinstance (prop .val_cardinality , tuple ):
553
+ err = _cardinality_validation (obj , obj .sec_cardinality , 'sections' , LABEL_WARNING )
554
+ if err :
555
+ yield err
495
556
496
- val_min = prop .val_cardinality [0 ]
497
- val_max = prop .val_cardinality [1 ]
498
557
499
- val_len = len ( prop . values ) if prop . values else 0
558
+ Validation . register_handler ( "section" , section_sections_cardinality )
500
559
501
- invalid_cause = ""
502
- if val_min and val_len < val_min :
503
- invalid_cause = "minimum %s" % val_min
504
- elif val_max and (prop .values and len (prop .values ) > val_max ):
505
- invalid_cause = "maximum %s" % val_max
506
560
507
- if invalid_cause :
508
- msg = "Property values cardinality violated"
509
- msg += " (%s values, %s found)" % (invalid_cause , val_len )
510
- yield ValidationError (prop , msg , LABEL_WARNING )
561
+ def property_values_cardinality (obj ):
562
+ """
563
+ Checks Property values against any set value cardinality.
564
+
565
+ :param obj: odml.Property
566
+
567
+ :return: Yields a ValidationError warning, if a set cardinality is not met.
568
+ """
569
+ err = _cardinality_validation (obj , obj .val_cardinality , 'values' , LABEL_WARNING )
570
+ if err :
571
+ yield err
511
572
512
573
513
574
Validation .register_handler ("property" , property_values_cardinality )
0 commit comments