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