@@ -53,9 +53,9 @@ def __repr__(self):
5353class Validation (object ):
5454 """
5555 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.
5757
58- :param doc : odml.Document that the validation will be applied to.
58+ :param obj : odml object the validation will be applied to.
5959 """
6060
6161 _handlers = {}
@@ -77,19 +77,18 @@ def register_handler(klass, handler):
7777 """
7878 Validation ._handlers .setdefault (klass , set ()).add (handler )
7979
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
8282 self .errors = []
8383
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 = {}
8788 return
8889
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 ()
9392
9493 def validate (self , obj ):
9594 """
@@ -109,6 +108,38 @@ def error(self, validation_error):
109108 """
110109 self .errors .append (validation_error )
111110
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+
112143 def __getitem__ (self , obj ):
113144 """
114145 Return a list of the errors for a certain object.
@@ -455,88 +486,89 @@ def property_values_string_check(prop):
455486Validation .register_handler ('property' , property_values_string_check )
456487
457488
458- def section_properties_cardinality (obj ):
489+ def _cardinality_validation (obj , cardinality , card_target_attr , validation_rank ):
459490 """
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.
461494
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.
464503 """
465- if obj .prop_cardinality and isinstance (obj .prop_cardinality , tuple ):
504+ err = None
505+ if cardinality and isinstance (cardinality , tuple ):
466506
467- val_min = obj . prop_cardinality [0 ]
468- val_max = obj . prop_cardinality [1 ]
507+ val_min = cardinality [0 ]
508+ val_max = cardinality [1 ]
469509
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
471512
472513 invalid_cause = ""
473514 if val_min and val_len < val_min :
474515 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 :
476517 invalid_cause = "maximum %s" % val_max
477518
478519 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 )
480522 msg += " (%s values, %s found)" % (invalid_cause , val_len )
481- yield ValidationError (obj , msg , LABEL_WARNING )
482523
524+ err = ValidationError (obj , msg , validation_rank )
483525
484- Validation . register_handler ( "section" , section_properties_cardinality )
526+ return err
485527
486528
487- def section_sections_cardinality (obj ):
529+ def section_properties_cardinality (obj ):
488530 """
489- Checks Section sub-sections against any set sub-section cardinality.
531+ Checks Section properties against any set property cardinality.
490532
491533 :param obj: odml.Section
534+
492535 :return: Yields a ValidationError warning, if a set cardinality is not met.
493536 """
494- if obj .sec_cardinality and isinstance (obj .sec_cardinality , tuple ):
537+ err = _cardinality_validation (obj , obj .prop_cardinality , 'properties' , LABEL_WARNING )
538+ if err :
539+ yield err
495540
496- val_min = obj .sec_cardinality [0 ]
497- val_max = obj .sec_cardinality [1 ]
498541
499- val_len = len ( obj . sections ) if obj . sections else 0
542+ Validation . register_handler ( "section" , section_properties_cardinality )
500543
501- invalid_cause = ""
502- if val_min and val_len < val_min :
503- invalid_cause = "minimum %s" % val_min
504- elif val_max and (obj .sections and len (obj .sections ) > val_max ):
505- invalid_cause = "maximum %s" % val_max
506544
507- if invalid_cause :
508- msg = "Section sub-section cardinality violated"
509- msg += " (%s values, %s found)" % (invalid_cause , val_len )
510- yield ValidationError (obj , msg , LABEL_WARNING )
545+ def section_sections_cardinality (obj ):
546+ """
547+ Checks Section sub-sections against any set sub-section cardinality.
548+
549+ :param obj: odml.Section
550+
551+ :return: Yields a ValidationError warning, if a set cardinality is not met.
552+ """
553+ err = _cardinality_validation (obj , obj .sec_cardinality , 'sections' , LABEL_WARNING )
554+ if err :
555+ yield err
511556
512557
513558Validation .register_handler ("section" , section_sections_cardinality )
514559
515560
516- def property_values_cardinality (prop ):
561+ def property_values_cardinality (obj ):
517562 """
518563 Checks Property values against any set value cardinality.
519564
520- :param prop: odml.Property
565+ :param obj: odml.Property
566+
521567 :return: Yields a ValidationError warning, if a set cardinality is not met.
522568 """
523- if prop .val_cardinality and isinstance (prop .val_cardinality , tuple ):
524-
525- val_min = prop .val_cardinality [0 ]
526- val_max = prop .val_cardinality [1 ]
527-
528- val_len = len (prop .values ) if prop .values else 0
529-
530- invalid_cause = ""
531- if val_min and val_len < val_min :
532- invalid_cause = "minimum %s" % val_min
533- elif val_max and (prop .values and len (prop .values ) > val_max ):
534- invalid_cause = "maximum %s" % val_max
535-
536- if invalid_cause :
537- msg = "Property values cardinality violated"
538- msg += " (%s values, %s found)" % (invalid_cause , val_len )
539- yield ValidationError (prop , msg , LABEL_WARNING )
569+ err = _cardinality_validation (obj , obj .val_cardinality , 'values' , LABEL_WARNING )
570+ if err :
571+ yield err
540572
541573
542574Validation .register_handler ("property" , property_values_cardinality )
0 commit comments