Package gluon :: Module validators
[hide private]
[frames] | no frames]

Source Code for Module gluon.validators

  1  """ 
  2  This file is part of web2py Web Framework (Copyrighted, 2007) 
  3  Developed by Massimo Di Pierro <mdipierro@cs.depaul.edu> 
  4  License: GPL v2 
  5  """ 
  6   
  7  import os, re, random, copy, sys, types, datetime, time, cgi, hmac 
  8  import gluon.sql  
  9  try:  
 10      import hashlib 
 11      have_hashlib=True 
 12  except: 
 13      import sha, md5 
 14      have_hashlib=False 
 15  from storage import Storage 
 16   
 17  __all__=['IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_EMAIL', 'IS_EXPR','IS_FLOAT_IN_RANGE', 'IS_INT_IN_RANGE', 'IS_IN_SET', 'IS_LENGTH', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_TIME', 'IS_URL', 'CLEANUP', 'CRYPT', 'IS_IN_DB', 'IS_NOT_IN_DB', 'IS_UPPER', 'IS_NULL_OR'] 
 18   
19 -class IS_MATCH(object):
20 """ 21 example: 22 23 INPUT(_type='text',_name='name',requires=IS_MATCH('.+')) 24 25 the argument of IS_MATCH is a regular expression. 26 27 IS_MATCH('.+')('hello') returns ('hello',None) 28 IS_MATCH('.+')('') returns ('','invalid!') 29 """
30 - def __init__(self,expression,error_message='invalid expression!'):
31 self.regex=re.compile(expression) 32 self.error_message=error_message
33 - def __call__(self,value):
34 match=self.regex.match(value) 35 if match: return (match.group(),None) 36 return (value,self.error_message)
37
38 -class IS_EXPR(object):
39 """ 40 example: 41 42 INPUT(_type='text',_name='name',requires=IS_EXPR('5<int(value)<10')) 43 44 the argument of IS_EXPR must be python condition 45 46 IS_EXPR('int(value)<2')('1') returns (1,None) 47 IS_EXPR('int(value)<2')('2') returns ('2','invalid expression!') 48 """
49 - def __init__(self,expression,error_message='invalid expression!'):
50 self.expression=expression 51 self.error_message=error_message
52 - def __call__(self,value):
53 environment={'value':value} 54 exec('__ret__='+self.expression) in environment 55 if environment['__ret__']: return (value,None) 56 return (value,self.error_message)
57
58 -class IS_LENGTH(object):
59 """ 60 example: 61 62 INPUT(_type='text',_name='name',requires=IS_LENGTH(32)) 63 64 the argument of IS_LENGTH is the man number of characters 65 """
66 - def __init__(self,size,error_message='too long!'):
67 self.size=size 68 self.error_message=error_message
69 - def __call__(self,value):
70 if isinstance(value,cgi.FieldStorage): 71 if value.file: 72 value.file.seek(0,os.SEEK_END) 73 length=value.file.tell() 74 value.file.seek(0,os.SEEK_SET) 75 else: 76 val=value.value 77 if val: length=len(val) 78 else: length=0 79 if length<=self.size: return (value,None) # for uploads 80 elif isinstance(value,(str,unicode)): 81 if len(value)<=self.size: return (value,None) 82 elif len(str(value))<=self.size: return (value,None) 83 return (value,self.error_message)
84
85 -class IS_IN_SET(object):
86 """ 87 example: 88 89 INPUT(_type='text',_name='name',requires=IS_IN_SET(['max','john'])) 90 91 the argument of IS_IN_SET must be a list or set 92 """
93 - def __init__(self,theset,labels=None,error_message='value not allowed!'):
94 self.theset=[str(item) for item in theset] 95 self.labels=labels 96 self.error_message=error_message
97 - def __call__(self,value):
98 if value in self.theset: return (value,None) 99 return (value,self.error_message)
100
101 -def IS_IN_DB(dbset,field,label=None,error_message='value not in database!'):
102 """ 103 example: 104 105 INPUT(_type='text',_name='name',requires=IS_IN_DB(db,db.table)) 106 107 used for reference fields, rendered as a dropbox 108 """ 109 try: dbset=dbset() # dbset is a db 110 except TypeError: pass # else dbset is a SQLSet as should be 111 ktable=str(field).split('.')[0] 112 kfield=str(field).split('.')[-1] 113 if not label: label='%%(%s)s' % kfield 114 elif str(label).find('%')<0: label='%%(%s)s' % str(label).split('.')[-1] 115 ks=re.compile('%\((?P<name>[^\)]+)\)s').findall(label) 116 if not kfield in ks: ks+=[kfield] 117 fields=['%s.%s'%(ktable,k) for k in ks] 118 records=dbset.select(*fields,**dict(orderby=', '.join(fields))) 119 theset=[r[kfield] for r in records] 120 labels=[label % dict(r) for r in records] 121 return IS_IN_SET(theset,labels,error_message)
122
123 -class IS_NOT_IN_DB(object):
124 """ 125 example: 126 127 INPUT(_type='text',_name='name',requires=IS_NOT_IN_DB(db,db.table)) 128 129 makes the field unique 130 """
131 - def __init__(self,dbset,field,error_message='value already in database!'):
132 self.dbset=dbset 133 self.field=field 134 self.error_message=error_message 135 self.record_id=0
136 - def __call__(self,value):
137 fieldname=str(self.field) 138 id_field='%s.id' % fieldname[:fieldname.find('.')] 139 value_field=gluon.sql.sql_represent(value,'string',None) 140 if not self.record_id: value_id='0' 141 else: value_id=gluon.sql.sql_represent(self.record_id,'integer',None) 142 fetched=self.dbset("%s=%s AND %s<>%s" % (fieldname,value_field,id_field,value_id)).select('count(*)') 143 if fetched[0]['count(*)']==0: return (value,None) 144 return (value,self.error_message)
145
146 -class IS_INT_IN_RANGE(object):
147 """ 148 example: 149 150 INPUT(_type='text',_name='name',requires=IS_INT_IN_RANGE(0,10)) 151 """
152 - def __init__(self,minimum,maximum,error_message='too small or too large!'):
153 self.minimum=minimum 154 self.maximum=maximum 155 self.error_message=error_message
156 - def __call__(self,value):
157 try: 158 fvalue=float(value) 159 value=int(value) 160 if value==fvalue and self.minimum<=value<self.maximum: 161 return (value,None) 162 except ValueError: pass 163 return (value,self.error_message)
164
165 -class IS_FLOAT_IN_RANGE(object):
166 """ 167 example: 168 169 INPUT(_type='text',_name='name',requires=IS_FLOAT_IN_RANGE(0,10)) 170 """
171 - def __init__(self,minimum,maximum,error_message='too small or too large!'):
172 self.minimum=minimum 173 self.maximum=maximum 174 self.error_message=error_message
175 - def __call__(self,value):
176 try: 177 value=float(value) 178 if self.minimum<=value<=self.maximum: return (value,None) 179 except ValueError: pass 180 return (value,self.error_message)
181
182 -class IS_NOT_EMPTY(object):
183 """ 184 example: 185 186 INPUT(_type='text',_name='name',requires=IS_NOT_EMPTY()) 187 """
188 - def __init__(self,error_message='cannot be empty!'):
190 - def __call__(self,value):
191 if value==None or value=='': return (value,self.error_message) 192 return (value,None)
193
194 -class IS_ALPHANUMERIC(IS_MATCH):
195 """ 196 example: 197 198 INPUT(_type='text',_name='name',requires=IS_ALPHANUMERIC()) 199 """
200 - def __init__(self,error_message='must be alphanumeric!'):
201 IS_MATCH.__init__(self,'^[\w]*$',error_message)
202
203 -class IS_EMAIL(IS_MATCH):
204 """ 205 example: 206 207 INPUT(_type='text',_name='name',requires=IS_EMAIL()) 208 """
209 - def __init__(self,error_message='invalid email!'):
210 IS_MATCH.__init__(self,'^\w+(.\w+)*@(\w+.)+(\w+)$',error_message)
211
212 -class IS_URL(IS_MATCH):
213 """ 214 example: 215 216 INPUT(_type='text',_name='name',requires=IS_URL()) 217 """
218 - def __init__(self,error_message='invalid url!'):
219 IS_MATCH.__init__(self,'^http\://(\w+.)*(\w+)$',error_message)
220
221 -class IS_TIME(object):
222 """ 223 example: 224 225 INPUT(_type='text',_name='name',requires=IS_TIME()) 226 227 understands the follwing formats 228 hh:mm:ss [am/pm] 229 hh:mm [am/pm] 230 hh [am/pm] 231 232 [am/pm] is options, ':' can be replaced by any other non-digit 233 """
234 - def __init__(self,error_message='must be HH:MM:SS!'):
236 - def __call__(self,value):
237 try: 238 ivalue=value 239 value=re.compile('((?P<h>[0-9]+))([^0-9 ]+(?P<m>[0-9 ]+))?([^0-9ap ]+(?P<s>[0-9]*))?((?P<d>[ap]m))?').match(value.lower()) 240 h,m,s=int(value.group('h')),0,0 241 if value.group('m')!=None: m=int(value.group('m')) 242 if value.group('s')!=None: s=int(value.group('s')) 243 if value.group('d')=='pm' and 0<h<12: h=h+12 244 if not (h in range(24) and m in range(60) and s in range(60)): 245 raise ValueError 246 value='%.2i:%.2i:%.2i' % (h,m,s) 247 return (value,None) 248 except AttributeError: pass 249 except ValueError: pass 250 return (ivalue,self.error_message)
251
252 -class IS_DATE(object):
253 """ 254 example: 255 256 INPUT(_type='text',_name='name',requires=IS_DATE()) 257 258 date has to be in the ISO8960 format YYYY-MM-DD 259 """
260 - def __init__(self,format='%Y-%m-%d',error_message='must be YYYY-MM-DD!'):
261 self.format=format 262 self.error_message=error_message
263 - def __call__(self,value):
264 try: 265 y, m, d, hh, mm, ss, t0, t1, t2=time.strptime(value,str(self.format)) 266 value=datetime.date(y,m,d).isoformat() 267 return (value,None) 268 except: 269 return (value,self.error_message)
270 - def formatter(self,value):
271 return value.strftime(str(self.format))
272
273 -class IS_DATETIME(object):
274 """ 275 example: 276 277 INPUT(_type='text',_name='name',requires=IS_DATETIME()) 278 279 datetime has to be in the ISO8960 format YYYY-MM-DD hh:mm:ss 280 """ 281 isodatetime='%Y-%m-%d %H:%M:%S'
282 - def __init__(self,format='%Y-%m-%d %H:%M:%S',error_message='must be YYYY-MM-DD HH:MM:SS!'):
283 self.format=format 284 self.error_message=error_message
285 - def __call__(self,value):
286 try: 287 y, m, d, hh, mm, ss, t0, t1, t2=time.strptime(value,str(self.format)) 288 value=datetime.datetime(y,m,d,hh,mm,ss).strftime(self.isodatetime) 289 return (value,None) 290 except: 291 return (value,self.error_message)
292 - def formatter(self,value):
293 return value.strftime(str(self.format))
294
295 -class IS_LOWER(object):
296 - def __call__(self,value): return (value.lower(),None)
297
298 -class IS_UPPER(object):
299 - def __call__(self,value): return (value.upper(),None)
300
301 -class IS_NULL_OR(object):
302 - def __init__(self,other,null=None):
303 self.other,self.null=other,null
304 - def __call__(self,value):
305 if not value: return (self.null,None) 306 return self.other(value)
307 - def formatter(self,value):
308 if hasattr(self.other,'formatter'): 309 return self.other.formatter(value) 310 return value
311
312 -class CLEANUP(object):
313 """ 314 example: 315 316 INPUT(_type='text',_name='name',requires=CLEANUP()) 317 318 removes special characters on validation 319 """
320 - def __init__(self): pass
321 - def __call__(self,value):
322 v='' 323 for c in str(value).strip(): 324 if ord(c) in [10,13]+range(32,127): v+=c 325 return (v,None)
326
327 -class CRYPT(object):
328 """ 329 example: 330 331 INPUT(_type='text',_name='name',requires=CRYPT()) 332 333 encodes the value on validation with md5 checkshum 334 """
335 - def __init__(self,key=None):
336 self.key=key
337 - def __call__(self,value):
338 if self.key: 339 if have_hashlib: return (hmac.new(self.key,value,hashlib.sha512).hexdigest(),None) 340 else: return (hmac.new(self.key,value,sha).hexdigest(),None) 341 if have_hashlib: return (hashlib.md5(value).hexdigest(),None) 342 else: return (md5.new(value).hexdigest(),None)
343