Package gavo :: Package base :: Module metavalidation
[frames] | no frames]

Source Code for Module gavo.base.metavalidation

  1  """ 
  2  Meta information validation. 
  3   
  4  The idea is that you define certain assertions about the meta information 
  5  of a given object type.  Defined assertions are  
  6   
  7          - MetaExists -- a key is present 
  8          - MetaIsAtomic -- a key is present and a "leaf", i.e., has a single value 
  9          - MetaAtomicExistsOnSelf -- a key is present even without meta inheritance, 
 10                  and has a single value 
 11   
 12  Validators are usually built using model descriptions.  These are enumerations 
 13  of meta keys, separated by commata, with an optional code in parenteses. 
 14  Whitespace is ignored.  Codes allowed in parens are: 
 15   
 16          - empty (default): plain existence 
 17          - !: atomic existance on self 
 18          - 1: atomic existance 
 19   
 20  An example for a valid model description:  
 21  "publisher.name,creator.email(), identifier (!), dateUpdated(1)" 
 22   
 23  These model descriptions can come in metaModel attributes of structures. 
 24  If they are, you can use the validateStructure function below to validate 
 25  an entire structure tree. 
 26  """ 
 27   
 28  #c Copyright 2008-2019, the GAVO project 
 29  #c 
 30  #c This program is free software, covered by the GNU GPL.  See the 
 31  #c COPYING file in the source distribution. 
 32   
 33   
 34  from gavo import utils 
 35  from gavo.base import meta 
36 37 38 -class MetaValidationError(meta.MetaError):
39 - def __init__(self, carrier, failures):
40 self.failures = failures 41 if getattr(carrier, "id", None): 42 self.carrierRepr = carrier.id 43 else: 44 self.carrierRepr = repr(carrier) 45 meta.MetaError.__init__(self, "Meta structure on %s did not validate"% 46 self.carrierRepr, carrier)
47
48 - def __str__(self):
49 return "Meta structure on %s did not validate: %s"%( 50 self.carrierRepr, ", ".join(self.failures))
51
52 53 -class MetaAssertion(object):
54 """An assertion about the meta content of an object. 55 56 You must override the C{check} method. 57 """
58 - def __init__(self, key):
59 self.key = key
60
61 - def check(self, metaCarrier):
62 """returns None if the assertion is true, a user-displayable string of 63 what failed otherwise. 64 65 This must be overridden in derived classes. 66 @param metaCarrier: an object mixing in L{MetaMixin}. 67 """ 68 69 return "Null assertion on %s always fails"%self.key
70
71 72 -class MetaExists(MetaAssertion):
73 """An assertion that a meta item is present for key in whatever form. 74 """
75 - def check(self, metaCarrier):
76 if metaCarrier.getMeta(self.key) is None: 77 return "Meta key %s missing"%self.key
78
79 80 -class MetaIsAtomic(MetaAssertion):
81 """An assertion that a meta item is present and contains a single value 82 only. 83 """ 84 propagate = True
85 - def check(self, metaCarrier):
86 val = metaCarrier.getMeta(self.key, propagate=self.propagate) 87 if val is None: 88 return "Meta key %s missing"%self.key 89 if len(val.children)!=1: 90 return "Meta key %s is not atomic"%self.key
91
92 93 -class MetaAtomicExistsOnSelf(MetaIsAtomic):
94 """An assertion that a meta item is present and unique for key on 95 metaCarrier itself. 96 """ 97 propagate = False
98
99 100 -class MetaValidator(object):
101 """A metadata model that can verify objects of compliance. 102 103 The model is quite simple: it's a sequence of MetaAssertions. 104 The validate(metaCarrier) -> None method raises a MetaNotValid 105 exception with all failed assertions in its failedAssertions 106 attribute. 107 """
108 - def __init__(self, model):
109 self.model = model
110
111 - def validate(self, metaCarrier):
112 failures = [msg for msg in ( 113 ass.check(metaCarrier) for ass in self.model) if msg] 114 if failures: 115 raise MetaValidationError(metaCarrier, failures)
116 117 118 _assertionCodes = { 119 (): MetaExists, 120 ('!',): MetaAtomicExistsOnSelf, 121 ('1',): MetaIsAtomic, 122 }
123 124 125 @utils.memoized 126 -def _getModelGrammar():
127 from pyparsing import (Literal, Optional, StringEnd, Suppress, 128 Word, ZeroOrMore, alphas) 129 130 with utils.pyparsingWhitechars(" \t"): 131 metaKey = Word(alphas+".") 132 modChar = Literal('!') | '1' 133 modifier = Suppress('(') + Optional(modChar) + Suppress(')') 134 assertion = metaKey("key")+Optional(modifier)("mod") 135 model = assertion + ZeroOrMore( 136 Suppress(',') + assertion ) + StringEnd() 137 138 def _buildAssertion(s, p, toks): 139 key = str(toks["key"]) 140 mod = tuple(toks.get("mod", ())) 141 return _assertionCodes[mod](key)
142 143 assertion.addParseAction(_buildAssertion) 144 model.addParseAction(lambda s,p,toks: MetaValidator(toks)) 145 return model 146
147 148 -def parseModel(modelDescr):
149 """returns a MetaValidator for a model description. 150 151 model descriptions are covered in the module docstring. 152 """ 153 return utils.pyparseString(_getModelGrammar(), modelDescr)[0]
154
155 156 -def _validateStructNode(aStruct):
157 if hasattr(aStruct.__class__, "metaModel"): 158 metaModel = aStruct.__class__.metaModel 159 if metaModel is None: 160 return 161 if isinstance(metaModel, basestring): 162 aStruct.__class__.metaModel = parseModel(metaModel) 163 metaModel = aStruct.__class__.metaModel 164 metaModel.validate(aStruct)
165
166 167 -def validateStructure(aStruct):
168 """does a meta validation for a base.Structure. 169 170 This works by traversing the children of the structure, looking for 171 nodes with a metaModel attribute. For all these, a validation is 172 carried out. The first node failing the validation determines the 173 return value. 174 175 The function raises a MetaValidationError if aStruct is invalid. 176 """ 177 _validateStructNode(aStruct) 178 for s in aStruct.iterChildren(): 179 _validateStructNode(s)
180