3
3
Generic odML validation framework.
4
4
"""
5
5
6
+ from . import dtypes
7
+
6
8
LABEL_ERROR = 'error'
7
9
LABEL_WARNING = 'warning'
8
10
@@ -74,12 +76,16 @@ def register_handler(klass, handler):
74
76
"""
75
77
Validation ._handlers .setdefault (klass , set ()).add (handler )
76
78
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
79
81
self .errors = []
80
- self .validate (doc )
81
82
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 ):
83
89
self .validate (sec )
84
90
for prop in sec .properties :
85
91
self .validate (prop )
@@ -116,6 +122,31 @@ def __getitem__(self, obj):
116
122
# ------------------------------------------------
117
123
# validation rules
118
124
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
+
119
150
def section_type_must_be_defined (sec ):
120
151
"""
121
152
Tests that no Section has an undefined type.
@@ -282,6 +313,9 @@ def property_terminology_check(prop):
282
313
2. warn, if there are multiple values with different units or the unit does
283
314
not match the one in the terminology.
284
315
"""
316
+ if not prop .parent :
317
+ return
318
+
285
319
tsec = prop .parent .get_terminology_equivalent ()
286
320
if tsec is None :
287
321
return
@@ -300,6 +334,9 @@ def property_dependency_check(prop):
300
334
Produces a warning if the dependency attribute refers to a non-existent attribute
301
335
or the dependency_value does not match.
302
336
"""
337
+ if not prop .parent :
338
+ return
339
+
303
340
dep = prop .dependency
304
341
if dep is None :
305
342
return
@@ -317,3 +354,35 @@ def property_dependency_check(prop):
317
354
318
355
319
356
Validation .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