33Generic odML validation framework.
44"""
55
6+ from . import dtypes
7+
68LABEL_ERROR = 'error'
79LABEL_WARNING = 'warning'
810
@@ -74,12 +76,16 @@ def register_handler(klass, handler):
7476 """
7577 Validation ._handlers .setdefault (klass , set ()).add (handler )
7678
77- def __init__ (self , doc ):
78- self .doc = doc # may also be a section
79+ def __init__ (self , obj ):
80+ self .doc = obj # may also be a section
7981 self .errors = []
80- self .validate (doc )
8182
82- for sec in doc .itersections (recursive = True ):
83+ self .validate (obj )
84+
85+ if obj .format ().name == "property" :
86+ return
87+
88+ for sec in obj .itersections (recursive = True ):
8389 self .validate (sec )
8490 for prop in sec .properties :
8591 self .validate (prop )
@@ -116,6 +122,31 @@ def __getitem__(self, obj):
116122# ------------------------------------------------
117123# validation rules
118124
125+ def object_required_attributes (obj ):
126+ """
127+ Tests that no Object has undefined attributes, given in format.
128+
129+ :param obj: document, section or property.
130+ """
131+
132+ args = obj .format ().arguments
133+ for arg in args :
134+ if arg [1 ] == 1 :
135+ if not hasattr (obj , arg [0 ]):
136+ msg = "Missing attribute %s for %s" % (obj .format ().name .capitalize (), arg [0 ])
137+ yield ValidationError (obj , msg , LABEL_ERROR )
138+ continue
139+ obj_arg = getattr (obj , arg [0 ])
140+ if not obj_arg and not isinstance (obj_arg , bool ):
141+ msg = "%s %s undefined" % (obj .format ().name .capitalize (), arg [0 ])
142+ yield ValidationError (obj , msg , LABEL_ERROR )
143+
144+
145+ Validation .register_handler ('odML' , object_required_attributes )
146+ Validation .register_handler ('section' , object_required_attributes )
147+ Validation .register_handler ('property' , object_required_attributes )
148+
149+
119150def section_type_must_be_defined (sec ):
120151 """
121152 Tests that no Section has an undefined type.
@@ -282,6 +313,9 @@ def property_terminology_check(prop):
282313 2. warn, if there are multiple values with different units or the unit does
283314 not match the one in the terminology.
284315 """
316+ if not prop .parent :
317+ return
318+
285319 tsec = prop .parent .get_terminology_equivalent ()
286320 if tsec is None :
287321 return
@@ -300,6 +334,9 @@ def property_dependency_check(prop):
300334 Produces a warning if the dependency attribute refers to a non-existent attribute
301335 or the dependency_value does not match.
302336 """
337+ if not prop .parent :
338+ return
339+
303340 dep = prop .dependency
304341 if dep is None :
305342 return
@@ -317,3 +354,35 @@ def property_dependency_check(prop):
317354
318355
319356Validation .register_handler ('property' , property_dependency_check )
357+
358+
359+ def property_values_check (prop ):
360+ """
361+ Tests that the values are of consistent dtype.
362+ If dtype is not given, infer from first item in list.
363+
364+ :param prop: property the validation is applied on.
365+ """
366+
367+ if prop .dtype is not None and prop .dtype != "" :
368+ dtype = prop .dtype
369+ elif prop .values :
370+ dtype = dtypes .infer_dtype (prop .values [0 ])
371+ else :
372+ return
373+
374+ for val in prop .values :
375+ if dtype .endswith ("-tuple" ):
376+ tuple_len = int (dtype [:- 6 ])
377+ if len (val ) != tuple_len :
378+ msg = "Tuple of length %s not consistent with dtype %s!" % (len (val ), dtype )
379+ yield ValidationError (prop , msg , LABEL_WARNING )
380+ else :
381+ try :
382+ dtypes .get (val , dtype )
383+ except ValueError :
384+ msg = "Property values not of consistent dtype!"
385+ yield ValidationError (prop , msg , LABEL_WARNING )
386+
387+
388+ Validation .register_handler ('property' , property_values_check )
0 commit comments