1#Copyright ReportLab Europe Ltd. 2000-2017 2#see license.txt for license details 3#history https://hg.reportlab.com/hg-public/reportlab/log/tip/src/reportlab/lib/validators.py 4__version__='3.5.33' 5__doc__="""Standard verifying functions used by attrmap.""" 6 7import sys, codecs 8from reportlab.lib.utils import isSeq, isBytes, isStr, isPy3 9from reportlab.lib import colors 10from reportlab import ascii 11 12class Percentage(float): 13 pass 14 15class Validator: 16 "base validator class" 17 def __call__(self,x): 18 return self.test(x) 19 20 def __str__(self): 21 return getattr(self,'_str',self.__class__.__name__) 22 23 def normalize(self,x): 24 return x 25 26 def normalizeTest(self,x): 27 try: 28 self.normalize(x) 29 return True 30 except: 31 return False 32 33class _isAnything(Validator): 34 def test(self,x): 35 return True 36 37class _isNothing(Validator): 38 def test(self,x): 39 return False 40 41class _isBoolean(Validator): 42 def test(self,x): 43 if isinstance(int,bool): return x in (0,1) 44 return self.normalizeTest(x) 45 46 def normalize(self,x): 47 if x in (0,1): return x 48 try: 49 S = x.upper() 50 except: 51 raise ValueError('Must be boolean not %s' % ascii(s)) 52 if S in ('YES','TRUE'): return True 53 if S in ('NO','FALSE',None): return False 54 raise ValueError('Must be boolean not %s' % ascii(s)) 55 56class _isString(Validator): 57 def test(self,x): 58 return isStr(x) 59 60class _isCodec(Validator): 61 def test(self,x): 62 if not isStr(x): 63 return False 64 try: 65 a,b,c,d = codecs.lookup(x) 66 return True 67 except LookupError: 68 return False 69 70class _isNumber(Validator): 71 def test(self,x): 72 if isinstance(x,(float,int)): return True 73 return self.normalizeTest(x) 74 75 def normalize(self,x): 76 try: 77 return float(x) 78 except: 79 return int(x) 80 81class _isInt(Validator): 82 def test(self,x): 83 if not isinstance(x,int) and not isStr(x): return False 84 return self.normalizeTest(x) 85 86 if not isPy3: 87 def normalize(self,x): 88 return int(x) 89 else: 90 def normalize(self,x): 91 return int(x.decode('utf8') if isBytes(x) else x) 92 93class _isNumberOrNone(_isNumber): 94 def test(self,x): 95 return x is None or isNumber(x) 96 97 def normalize(self,x): 98 if x is None: return x 99 return _isNumber.normalize(x) 100 101class _isListOfNumbersOrNone(Validator): 102 "ListOfNumbersOrNone validator class." 103 def test(self, x): 104 if x is None: return True 105 return isListOfNumbers(x) 106 107class isNumberInRange(_isNumber): 108 def __init__(self, min, max): 109 self.min = min 110 self.max = max 111 112 def test(self, x): 113 try: 114 n = self.normalize(x) 115 if self.min <= n <= self.max: 116 return True 117 except ValueError: 118 pass 119 return False 120 121 122class _isListOfShapes(Validator): 123 "ListOfShapes validator class." 124 def test(self, x): 125 from reportlab.graphics.shapes import Shape 126 if isSeq(x): 127 answer = 1 128 for e in x: 129 if not isinstance(e, Shape): 130 answer = 0 131 return answer 132 else: 133 return False 134 135class _isListOfStringsOrNone(Validator): 136 "ListOfStringsOrNone validator class." 137 138 def test(self, x): 139 if x is None: return True 140 return isListOfStrings(x) 141 142class _isTransform(Validator): 143 "Transform validator class." 144 def test(self, x): 145 if isSeq(x): 146 if len(x) == 6: 147 for element in x: 148 if not isNumber(element): 149 return False 150 return True 151 else: 152 return False 153 else: 154 return False 155 156class _isColor(Validator): 157 "Color validator class." 158 def test(self, x): 159 return isinstance(x, colors.Color) 160 161class _isColorOrNone(Validator): 162 "ColorOrNone validator class." 163 def test(self, x): 164 if x is None: return True 165 return isColor(x) 166 167from reportlab.lib.normalDate import NormalDate 168class _isNormalDate(Validator): 169 def test(self,x): 170 if isinstance(x,NormalDate): 171 return True 172 return x is not None and self.normalizeTest(x) 173 174 def normalize(self,x): 175 return NormalDate(x) 176 177class _isValidChild(Validator): 178 "ValidChild validator class." 179 def test(self, x): 180 """Is this child allowed in a drawing or group? 181 I.e. does it descend from Shape or UserNode? 182 """ 183 184 from reportlab.graphics.shapes import UserNode, Shape 185 return isinstance(x, UserNode) or isinstance(x, Shape) 186 187class _isValidChildOrNone(_isValidChild): 188 def test(self,x): 189 return _isValidChild.test(self,x) or x is None 190 191class _isCallable(Validator): 192 def test(self, x): 193 return hasattr(x,'__call__') 194 195class OneOf(Validator): 196 """Make validator functions for list of choices. 197 198 Usage: 199 f = reportlab.lib.validators.OneOf('happy','sad') 200 or 201 f = reportlab.lib.validators.OneOf(('happy','sad')) 202 f('sad'),f('happy'), f('grumpy') 203 (1,1,0) 204 """ 205 def __init__(self, enum,*args): 206 if isSeq(enum): 207 if args!=(): 208 raise ValueError("Either all singleton args or a single sequence argument") 209 self._enum = tuple(enum)+args 210 else: 211 self._enum = (enum,)+args 212 213 def test(self, x): 214 return x in self._enum 215 216class SequenceOf(Validator): 217 def __init__(self,elemTest,name=None,emptyOK=1, NoneOK=0, lo=0,hi=0x7fffffff): 218 self._elemTest = elemTest 219 self._emptyOK = emptyOK 220 self._NoneOK = NoneOK 221 self._lo, self._hi = lo, hi 222 if name: self._str = name 223 224 def test(self, x): 225 if not isSeq(x): 226 if x is None: return self._NoneOK 227 return False 228 if x==[] or x==(): 229 return self._emptyOK 230 elif not self._lo<=len(x)<=self._hi: return False 231 for e in x: 232 if not self._elemTest(e): return False 233 return True 234 235class EitherOr(Validator): 236 def __init__(self,tests,name=None): 237 if not isSeq(tests): tests = (tests,) 238 self._tests = tests 239 if name: self._str = name 240 241 def test(self, x): 242 for t in self._tests: 243 if t(x): return True 244 return False 245 246class NoneOr(Validator): 247 def __init__(self,elemTest,name=None): 248 self._elemTest = elemTest 249 if name: self._str = name 250 251 def test(self, x): 252 if x is None: return True 253 return self._elemTest(x) 254 255class NotSetOr(NoneOr): 256 _not_set = object() 257 def test(self, x): 258 if x is NotSetOr._not_set: return True 259 return self._elemTest(x) 260 261 @staticmethod 262 def conditionalValue(v,a): 263 return a if v is NotSetOr._not_set else v 264 265class _isNotSet(Validator): 266 def test(self,x): 267 return x is NotSetOr._not_set 268 269class Auto(Validator): 270 def __init__(self,**kw): 271 self.__dict__.update(kw) 272 273 def test(self,x): 274 return x is self.__class__ or isinstance(x,self.__class__) 275 276class AutoOr(NoneOr): 277 def test(self,x): 278 return isAuto(x) or self._elemTest(x) 279 280class isInstanceOf(Validator): 281 def __init__(self,klass=None): 282 self._klass = klass 283 def test(self,x): 284 return isinstance(x,self._klass) 285 286class isSubclassOf(Validator): 287 def __init__(self,klass=None): 288 self._klass = klass 289 def test(self,x): 290 return issubclass(x,self._klass) 291 292class matchesPattern(Validator): 293 """Matches value, or its string representation, against regex""" 294 def __init__(self, pattern): 295 self._pattern = re.compile(pattern) 296 297 def test(self,x): 298 x = str(x) 299 print('testing %s against %s' % (x, self._pattern)) 300 return (self._pattern.match(x) != None) 301 302class DerivedValue: 303 """This is used for magic values which work themselves out. 304 An example would be an "inherit" property, so that one can have 305 306 drawing.chart.categoryAxis.labels.fontName = inherit 307 308 and pick up the value from the top of the drawing. 309 Validators will permit this provided that a value can be pulled 310 in which satisfies it. And the renderer will have special 311 knowledge of these so they can evaluate themselves. 312 """ 313 def getValue(self, renderer, attr): 314 """Override this. The renderers will pass the renderer, 315 and the attribute name. Algorithms can then backtrack up 316 through all the stuff the renderer provides, including 317 a correct stack of parent nodes.""" 318 return None 319 320class Inherit(DerivedValue): 321 def __repr__(self): 322 return "inherit" 323 324 def getValue(self, renderer, attr): 325 return renderer.getStateValue(attr) 326inherit = Inherit() 327 328class NumericAlign(str): 329 '''for creating the numeric string value for anchors etc etc 330 dp is the character to align on (the last occurrence will be used) 331 dpLen is the length of characters after the dp 332 ''' 333 def __new__(cls,dp='.',dpLen=0): 334 self = str.__new__(cls,'numeric') 335 self._dp=dp 336 self._dpLen = dpLen 337 return self 338 339 340isAuto = Auto() 341isBoolean = _isBoolean() 342isString = _isString() 343isCodec = _isCodec() 344isNumber = _isNumber() 345isInt = _isInt() 346isNoneOrInt = NoneOr(isInt,'isNoneOrInt') 347isNumberOrNone = _isNumberOrNone() 348isTextAnchor = OneOf('start','middle','end','boxauto') 349isListOfNumbers = SequenceOf(isNumber,'isListOfNumbers') 350isListOfNoneOrNumber = SequenceOf(isNumberOrNone,'isListOfNoneOrNumber') 351isListOfListOfNoneOrNumber = SequenceOf(isListOfNoneOrNumber,'isListOfListOfNoneOrNumber') 352isListOfNumbersOrNone = _isListOfNumbersOrNone() 353isListOfShapes = _isListOfShapes() 354isListOfStrings = SequenceOf(isString,'isListOfStrings') 355isListOfStringsOrNone = _isListOfStringsOrNone() 356isTransform = _isTransform() 357isColor = _isColor() 358isListOfColors = SequenceOf(isColor,'isListOfColors') 359isColorOrNone = _isColorOrNone() 360isShape = isValidChild = _isValidChild() 361isNoneOrShape = isValidChildOrNone = _isValidChildOrNone() 362isAnything = _isAnything() 363isNothing = _isNothing() 364isXYCoord = SequenceOf(isNumber,lo=2,hi=2,emptyOK=0) 365isBoxAnchor = OneOf('nw','n','ne','w','c','e','sw','s','se', 'autox', 'autoy') 366isNoneOrString = NoneOr(isString,'NoneOrString') 367isNoneOrListOfNoneOrStrings=SequenceOf(isNoneOrString,'isNoneOrListOfNoneOrStrings',NoneOK=1) 368isListOfNoneOrString=SequenceOf(isNoneOrString,'isListOfNoneOrString',NoneOK=0) 369isNoneOrListOfNoneOrNumbers=SequenceOf(isNumberOrNone,'isNoneOrListOfNoneOrNumbers',NoneOK=1) 370isCallable = _isCallable() 371isNoneOrCallable = NoneOr(isCallable) 372isStringOrCallable=EitherOr((isString,isCallable),'isStringOrCallable') 373isStringOrCallableOrNone=NoneOr(isStringOrCallable,'isStringOrCallableNone') 374isStringOrNone=NoneOr(isString,'isStringOrNone') 375isNormalDate=_isNormalDate() 376isNotSet=_isNotSet() 377