m DMc @sl dZdkZdkZdkZdkZdkZdkZdkZdkZdk Z dk Z dk l Z dk lZlZdddddd d d d d ddddddddddddddddddd d!d"gZd#Zd$efd%YZdefd&YZdefd'YZdefd(YZdefd)YZdefd*YZeid+Zeid,Zdefd-YZdefd.YZdefd/YZdefd0YZd efd1YZ e!d2Z"defd3YZ#defd4YZ$d efd5YZ%d6d7d8d9d:d;d<d=d>d?d@dAdBdCdDdEdFdGdHdIdJdKdLdMdNdOdPdQdRdSdTdUdVdWdXdYdZd[d\d]d^d_d`dadbdcdddedfdgdhdidjdkdldmdndodpdqdrdsdsdtdudvgBZ&dwdxdydzd{d|d}d~ddddddddddddddddddddddddddddddddddddddddddddg4Z'e!ge&e'Z(e!dFdGgZ)eidZ*eidZ+dZ,dZ-dZ.defdYZ/ddddddddddddddddddd{dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd d dd d d ddddddddddddddddddd d!dId"dKd#d$d%d&d'd(d)d*d+d,d-d.d/d0d1d2d3d4d5d6d7d8d9d:d;d<d=d>d?d@dAdBdCdDdEdFdGdHdIdJdKdLdMdNdOdPdQdRdSdTdUdVdWdXdYdZd[d\d]d^d_d`dadbdcdddedfdgdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzd{d|d}d~dddddddddddddddddddddddddddddjdddddddddddddddodddddddddddddddddddddddddddddddddddg Z0defdYZ1d"efdYZ2eidZ3defdYZ4defdYZ5d efdYZ6de5fdYZ7d e6fdYZ8defdYZ9defdYZ:d!efdYZ;defdYZ<d efdYZ=e=Z>defdYZ?defdYZ@defdYZAdefdYZBdefdYZCd efdYZDdefdYZEeFdjodkGZGeGiHndS(s This file is part of the web2py Web Framework Copyrighted by Massimo Di Pierro License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) Thanks to ga2arch for help with IS_IN_DB and IS_NOT_IN_DB on GAE N(sStringIO(shashs get_digesttCLEANUPtCRYPTtIS_ALPHANUMERICtIS_DATE_IN_RANGEtIS_DATEtIS_DATETIME_IN_RANGEt IS_DATETIMEtIS_DECIMAL_IN_RANGEtIS_EMAILt IS_EMPTY_ORtIS_EXPRtIS_FLOAT_IN_RANGEtIS_IMAGEtIS_IN_DBt IS_IN_SETtIS_INT_IN_RANGEtIS_IPV4t IS_LENGTHt IS_LIST_OFtIS_LOWERtIS_MATCHt IS_EQUAL_TOt IS_NOT_EMPTYt IS_NOT_IN_DBt IS_NULL_ORtIS_SLUGt IS_STRONGtIS_TIMEtIS_UPLOAD_FILENAMEtIS_UPPERtIS_URLcCs8t|dit|dijodpdS(Nii(tstrtxtupperty(R R"((t:/home/camdpams_www/Products_Activation/gluon/validators.pytoptions_sorter;st ValidatorcBstZdZdZRS(sO Root for all validators, mainly for documentation purposes. Validators are classes used to validate input fields (including forms generated from database tables). Here is an example of using a validator with a FORM:: INPUT(_name='a', requires=IS_INT_IN_RANGE(0, 10)) Here is an example of how to require a validator for a table field:: db.define_table('person', SQLField('name')) db.person.name.requires=IS_NOT_EMPTY() Validators are always assigned using the requires attribute of a field. A field can have a single validator or multiple validators. Multiple validators are made part of a list:: db.person.name.requires=[IS_NOT_EMPTY(), IS_NOT_IN_DB(db, 'person.id')] Validators are called by the function accepts on a FORM or other HTML helper object that contains a form. They are always called in the order in which they are listed. Built-in validators have constructors that take the optional argument error message which allows you to change the default error message. Here is an example of a validator on a database table:: db.person.name.requires=IS_NOT_EMPTY(error_message=T('fill this')) where we have used the translation operator T to allow for internationalization. Notice that default error messages are not translated. cCs|S(s For some validators returns a formatted version (matching the validator) of value. Otherwise just returns the value. N(tvalue(tselfR&((R#t formatterds(t__name__t __module__t__doc__R((((R#R%>s $cBs&tZdZdedZdZRS(s example:: INPUT(_type='text', _name='name', requires=IS_MATCH('.+')) the argument of IS_MATCH is a regular expression:: >>> IS_MATCH('.+')('hello') ('hello', None) >>> IS_MATCH('.+')('') ('', 'invalid expression') sinvalid expressioncCsH|o"|idpd|}q)nti||_||_dS(Nt$s(%s)$(tstrictt expressiontendswithtretcompileR'tregext error_message(R'R.R3R-((R#t__init__{s cCs:|ii|}|o|idfSn||ifS(N(R'R2tmatchR&tgrouptNoneR3(R'R&R5((R#t__call__s(R)R*R+tTrueR4R8(((R#Rls cBs#tZdZddZdZRS(sY example:: INPUT(_type='text', _name='password') INPUT(_type='text', _name='password2', requires=IS_EQUAL_TO(request.vars.password)) the argument of IS_EQUAL_TO is a string >>> IS_EQUAL_TO('aaa')('aaa') ('aaa', None) >>> IS_EQUAL_TO('aaa')('aab') ('aab', 'no match') sno matchcCs||_||_dS(N(R.R'R3(R'R.R3((R#R4s cCs+||ijo|dfSn||ifS(N(R&R'R.R7R3(R'R&((R#R8s(R)R*R+R4R8(((R#Rs  cBs#tZdZddZdZRS(s; example:: INPUT(_type='text', _name='name', requires=IS_EXPR('5 < int(value) < 10')) the argument of IS_EXPR must be python condition:: >>> IS_EXPR('int(value) < 2')('1') ('1', None) >>> IS_EXPR('int(value) < 2')('2') ('2', 'invalid expression') sinvalid expressioncCs||_||_dS(N(R.R'R3(R'R.R3((R#R4s cBsDhd|<}d|i|U|do|dfSn||ifS(NR&s__ret__=t__ret__(R&t environmentR'R.R7R3(R'R&R;((R#R8s  (R)R*R+R4R8(((R#R s  cBs)tZdZddddZdZRS(s Checks if length of field's value fits between given boundaries. Works for both text and file inputs. Arguments: maxsize: maximum allowed length / size minsize: minimum allowed length / size Examples:: #Check if text string is shorter than 33 characters: INPUT(_type='text', _name='name', requires=IS_LENGTH(32)) #Check if password string is longer than 5 characters: INPUT(_type='password', _name='name', requires=IS_LENGTH(minsize=6)) #Check if uploaded file has size between 1KB and 1MB: INPUT(_type='file', _name='name', requires=IS_LENGTH(1048576, 1024)) >>> IS_LENGTH()('') ('', None) >>> IS_LENGTH()('1234567890') ('1234567890', None) >>> IS_LENGTH(maxsize=5, minsize=0)('1234567890') # too long ('1234567890', 'enter from 0 to 5 characters') >>> IS_LENGTH(maxsize=50, minsize=20)('1234567890') # too short ('1234567890', 'enter from 20 to 50 characters') iis(enter from %(min)g to %(max)g characterscCs2||_||_|td|d||_dS(Ntmintmax(tmaxsizeR'tminsizeR3tdict(R'R>R?R3((R#R4s  cCsst|tio|io?|iidti|ii}|iidti n'|i}|ot |}nd}|i|jo |ijno|dfSqfnt|tttfo<|it |jo |ijno|dfSqfnZ|it t|jo |ijno)y|id|dfSWqfqfXn||ifS(Nitutf8(t isinstanceR&tcgit FieldStoragetfiletseektostSEEK_ENDttelltlengthtSEEK_SETtvaltlenR'R?R>R7RtunicodetlisttdecodeR3(R'R&RLRJ((R#R8s*  $*0 (R)R*R+R4R8(((R#Rs cBs8tZdZedededZdZdZRS(s example:: INPUT(_type='text', _name='name', requires=IS_IN_SET(['max', 'john'],zero='')) the argument of IS_IN_SET must be a list or set >>> IS_IN_SET(['max', 'john'])('max') ('max', None) >>> IS_IN_SET(['max', 'john'])('massimo') ('massimo', 'value not allowed') >>> IS_IN_SET(['max', 'john'], multiple=True)(('max', 'john')) (('max', 'john'), None) >>> IS_IN_SET(['max', 'john'], multiple=True)(('bill', 'john')) (('bill', 'john'), 'value not allowed') >>> IS_IN_SET(('id1','id2'), ['first label','second label'])('id1') # Traditional way ('id1', None) >>> IS_IN_SET({'id1':'first label', 'id2':'second label'})('id1') ('id1', None) >>> import itertools >>> IS_IN_SET(itertools.chain(['1','3','5'],['2','4','6']))('1') ('1', None) >>> IS_IN_SET([('id1','first label'), ('id2','second label')])('id1') # Redundant way ('id1', None) svalue not allowedtc CsZ||_t|to=g}|D]} |t| q$~|_|i|_ n|ot|t t fot|dt t fo{t |ddjodg}|D]\} }|t| q~|_g}|D]\} }|t|q~|_ n4g}|D]} |t| q~|_||_ ||_||_||_dS(Nii(tmultipleR'RBtthesetR@t_[1]titemRtvaluestlabelsttupleRORMtlabelR3tzerotsort( R'RSRWR3RRRZR[RYRTRU((R#R4s *N04*   cCs|ip:g}t|iD]\}}|||fq~}n>g}t|iD] \}}|||i|fqX~}|io|it n|i djo(|i o|i dd|i fn|S(NiRQ(R'RWRTt enumerateRStitktitemsR[R$RZR7RRtinsert(R'R]R_RTR^((R#toptions2s := cCss|ioXt|ttfo |g}qkt|ttfo |}qk|p g}qkn |g}g}|D]!}||i jo ||qvqv~}|oM|i oC|io(|djp |djogdfSn||ifSn|iokt|ittfoD|idt|jo|idjn o||ifSn|dfSn|dfS(NRQii(R'RRRBR&RRNRVRXRORTR RStfailuresR7R3RM(R'R&RTRVR Rb((R#R8=s$    5$ L(R)R*R+R7tFalseR4RaR8(((R#Rs  s[\w_]+\.[\w_]+s%\((?P[^\)]+)\)sc BsVtZdZedeeeedeed ZdZdZdZdZ RS(s example:: INPUT(_type='text', _name='name', requires=IS_IN_DB(db, db.mytable.myfield, zero='')) used for reference fields, rendered as a dropbox svalue not in databaseRQc Csdkl}t||o |i}nt|do||_n ||_||_t|ii d\} } |pd| }nt|tont it|o!dt|i dd}nti|}| |jo|| g7}n|}n| g}d}||_||_ | |_ | |_ ||_||_d|_||_||_||_||_| |_| |_| |_dS(N(sTablet define_tablet.s%%(%s)sitall(tdaltTableRBtfieldt_idthasattrtdbsetR'RtsplittktabletkfieldRYtregex1R5tregex2tfindalltkstfieldsR3R7RStorderbytgroupbytcacheRRRZR[t_and(R'RlRiRYR3RuRvRwRRRZR[RxRnRoRtRsRh((R#R4csB    !                cCs|io||i_ndS(N(R'Rxtidt record_id(R'Ry((R#t set_self_ids c Cs|idjo2g}|ii|iD] }||q(~}n6g}|iD]} ||ii|i| qP~}|iiidjo\|i pt d|}|i }t d|d|d|i }|ii||}nd|i pt dd|D}t d|d|i }|ii|ii|ii|}g}|D]}|t||iqW~|_t|ito5g}|D]}||it |q~|_n.g}|D]}||i|q~|_dS( NRftgaecCs||BS(N(tatb(R}R~((R#tsRuRvRwcCs||BS(N(R}R~(R}R~((R#Rsccs,x%|]}|idjp|VqqWdS(RyN(t[outmost-iterable]tftname(RR((R#ts(R'RtRTRltdbRnRR^t_dbnameRutreduceRvR@RwtddtselecttrecordstALLtrRRoRSRBRYRW( R'RuRRtRvRTRRRR^((R#t build_sets25 &%15cCs|ig}t|iD] \}}|||i|fq~}|i o|i t n|i djo(|i o|idd|i fn|S(NiRQ(R'RRTR\RSR]R^RWR_R[R$RZR7RRR`(R'R]R_RTR^((R#Ras = cCs|iot|to |}n|o |g}ng}t|ittfoD|idt|jo|idjn o||ifSng}|D]!}||i jo ||qq~p|dfSqn|i o=||i jo)|i o|i |Sq&|dfSqnxt|iid\}}|ii||}|i||jio)|i o|i |Sq|dfSn||ifS(NiiRe(R'RRRBR&RORVRXRMR3RTR RSR7RxRRiRmRnRoRlRtcount(R'R&RnRTRiRVR Ro((R#R8s,   L6   ( R)R*R+R7RcR4R{RRaR8(((R#R Ys $2   cBs/tZdZdgdZdZdZRS(s example:: INPUT(_type='text', _name='name', requires=IS_NOT_IN_DB(db, db.table)) makes the field unique s"value already in database or emptycCs{dkl}t||o |i}nt|do||_n ||_||_||_d|_ ||_ dS(N(sTableRdi( RgRhRBRiRjRkRlR'R3Rztallowed_override(R'RlRiR3RRh((R#R4s      cCs ||_dS(N(RyR'Rz(R'Ry((R#R{scCsYt|}|ip||ifSn||ijo|dfSnt|iid\}}|i i ||}|i ||ji dd}t|djot|itoVx|iD]D}tt|d|t|i|jo||ifSqqWqOt|dit|ijo||ifSqOn|dfS(NRetlimitbyii(ii(RR&tstripR'R3RR7RiRmt tablenamet fieldnameRlRRtrowsRMRBRzR@RtgetattrRy(R'R&RRRRiR((R#R8s"  ! -#(R)R*R+R4R{R8(((R#Rs  cBs)tZdZeeedZdZRS(s Determine that the argument is (or can be represented as) an int, and that it falls within the specified range. The range is interpreted in the Pythonic way, so the test is: min <= value < max. The minimum and maximum limits can be None, meaning no lower or upper limit, respectively. example:: INPUT(_type='text', _name='name', requires=IS_INT_IN_RANGE(0, 10)) >>> IS_INT_IN_RANGE(1,5)('4') (4, None) >>> IS_INT_IN_RANGE(1,5)(4) (4, None) >>> IS_INT_IN_RANGE(1,5)(1) (1, None) >>> IS_INT_IN_RANGE(1,5)(5) (5, 'enter an integer between 1 and 4') >>> IS_INT_IN_RANGE(1,5)(5) (5, 'enter an integer between 1 and 4') >>> IS_INT_IN_RANGE(1,5)(3.5) (3, 'enter an integer between 1 and 4') >>> IS_INT_IN_RANGE(None,5)('4') (4, None) >>> IS_INT_IN_RANGE(None,5)('6') (6, 'enter an integer less than or equal to 4') >>> IS_INT_IN_RANGE(1,None)('4') (4, None) >>> IS_INT_IN_RANGE(1,None)('0') (0, 'enter an integer greater than or equal to 1') >>> IS_INT_IN_RANGE()(6) (6, None) >>> IS_INT_IN_RANGE()('abc') ('abc', 'enter an integer') cCs4d|_|_|djoh|djo|pd|_q0t||_|djo d}n|td|id|_n|djoCt||_|djo d}n|td|i|_n\t||_t||_|djo d}n|td|id|id|_dS(Nsenter an integers.enter an integer less than or equal to %(max)gR=is1enter an integer greater than or equal to %(min)gR<s,enter an integer between %(min)g and %(max)g(R7R'tminimumtmaximumR3tintR@(R'RRR3((R#R4.s$    !     cCsyt|}t|}||jo||ifSn|idjo2|idjp||ijo|dfSqne|idjo"||ijo|dfSqn3|i|jo |ijno|dfSnWnt j onX||ifS(N( tfloatR&tfvalueRR'R3RR7Rt ValueError(R'R&R((R#R8Is     $(R)R*R+R7R4R8(((R#Rs %cBs5tZdZeeeddZdZdZRS(s Determine that the argument is (or can be represented as) a float, and that it falls within the specified inclusive range. The comparison is made with native arithmetic. The minimum and maximum limits can be None, meaning no lower or upper limit, respectively. example:: INPUT(_type='text', _name='name', requires=IS_FLOAT_IN_RANGE(0, 10)) >>> IS_FLOAT_IN_RANGE(1,5)('4') (4.0, None) >>> IS_FLOAT_IN_RANGE(1,5)(4) (4.0, None) >>> IS_FLOAT_IN_RANGE(1,5)(1) (1.0, None) >>> IS_FLOAT_IN_RANGE(1,5)(5.25) (5.25, 'enter a number between 1 and 5') >>> IS_FLOAT_IN_RANGE(1,5)(6.0) (6.0, 'enter a number between 1 and 5') >>> IS_FLOAT_IN_RANGE(1,5)(3.5) (3.5, None) >>> IS_FLOAT_IN_RANGE(1,None)(3.5) (3.5, None) >>> IS_FLOAT_IN_RANGE(None,5)(3.5) (3.5, None) >>> IS_FLOAT_IN_RANGE(1,None)(0.5) (0.5, 'enter a number greater than or equal to 1') >>> IS_FLOAT_IN_RANGE(None,5)(6.5) (6.5, 'enter a number less than or equal to 5') >>> IS_FLOAT_IN_RANGE()(6.5) (6.5, None) >>> IS_FLOAT_IN_RANGE()('abc') ('abc', 'enter a number') RecCs d|_|_||_|djoR|djo|djo d}qtqt||_|djo d}qnm|djo*t||_|djo d}qn6t||_t||_|djo d}n|td|id|i|_dS(Nsenter a numbers,enter a number less than or equal to %(max)gs/enter a number greater than or equal to %(min)gs*enter a number between %(min)g and %(max)gR<R=(R7R'RRtdotR3RR@(R'RRR3R((R#R4s$         cCsy|idjot|}n"tt|i|id}|idjo2|i djp||i jo|dfSqne|i djo"||ijo|dfSqn3|i|jo |i jno|dfSnWnt t fj onX||i fS(NRe( R'RRR&RRtreplaceRR7RRt TypeErrorR3(R'R&R((R#R8s! $cCs;|idjot|Snt|id|iSdS(NRe(R'RRR&R(R'R&((R#R(s(R)R*R+R7R4R8R((((R#R \s % cBs5tZdZeeeddZdZdZRS(s Determine that the argument is (or can be represented as) a Python Decimal, and that it falls within the specified inclusive range. The comparison is made with Python Decimal arithmetic. The minimum and maximum limits can be None, meaning no lower or upper limit, respectively. example:: INPUT(_type='text', _name='name', requires=IS_DECIMAL_IN_RANGE(0, 10)) >>> IS_DECIMAL_IN_RANGE(1,5)('4') (Decimal('4'), None) >>> IS_DECIMAL_IN_RANGE(1,5)(4) (Decimal('4'), None) >>> IS_DECIMAL_IN_RANGE(1,5)(1) (Decimal('1'), None) >>> IS_DECIMAL_IN_RANGE(1,5)(5.25) (5.25, 'enter a number between 1 and 5') >>> IS_DECIMAL_IN_RANGE(5.25,6)(5.25) (Decimal('5.25'), None) >>> IS_DECIMAL_IN_RANGE(5.25,6)('5.25') (Decimal('5.25'), None) >>> IS_DECIMAL_IN_RANGE(1,5)(6.0) (6.0, 'enter a number between 1 and 5') >>> IS_DECIMAL_IN_RANGE(1,5)(3.5) (Decimal('3.5'), None) >>> IS_DECIMAL_IN_RANGE(1.5,5.5)(3.5) (Decimal('3.5'), None) >>> IS_DECIMAL_IN_RANGE(1.5,5.5)(6.5) (6.5, 'enter a number between 1.5 and 5.5') >>> IS_DECIMAL_IN_RANGE(1.5,None)(6.5) (Decimal('6.5'), None) >>> IS_DECIMAL_IN_RANGE(1.5,None)(0.5) (0.5, 'enter a number greater than or equal to 1.5') >>> IS_DECIMAL_IN_RANGE(None,5.5)(4.5) (Decimal('4.5'), None) >>> IS_DECIMAL_IN_RANGE(None,5.5)(6.5) (6.5, 'enter a number less than or equal to 5.5') >>> IS_DECIMAL_IN_RANGE()(6.5) (Decimal('6.5'), None) >>> IS_DECIMAL_IN_RANGE(0,99)(123.123) (123.123, 'enter a number between 0 and 99') >>> IS_DECIMAL_IN_RANGE(0,99)('123.123') ('123.123', 'enter a number between 0 and 99') >>> IS_DECIMAL_IN_RANGE(0,99)('12.34') (Decimal('12.34'), None) >>> IS_DECIMAL_IN_RANGE()('abc') ('abc', 'enter a decimal number') RecCs.d|_|_||_|djo[|djo|djo d}q}qtit||_|djo d}qn|djo3tit||_|djo d}qnHtit||_tit||_|djo d}n|t d|id|i|_dS(Nsenter a decimal numbers,enter a number less than or equal to %(max)gs/enter a number greater than or equal to %(min)gs*enter a number between %(min)g and %(max)gR<R=( R7R'RRRR3tdecimaltDecimalRR@(R'RRR3R((R#R4s$         cCs&y|idjotit|}n%tit|i|id}|idjo2|i djp||i jo|dfSqne|i djo"||ijo|dfSqn3|i|jo |i jno|dfSnWnt t ti fj onX||ifS(NRe(R'RRRRR&tvRRR7RRRtInvalidOperationR3(R'R&R((R#R8 s$ $cCst|id|iS(NRe(RR&RR'R(R'R&((R#R(s(R)R*R+R7R4R8R((((R#Rs 3 cCst|ttfo7|i}|dj o|i|o d}qMn|djp|djp |gjo|tfSn|t fS(stest empty fieldRQN( RBR&RRNRt empty_regexR7R5R9Rc(R&R((R#tis_emptys 'cBs&tZdZdedZdZRS(sL example:: INPUT(_type='text', _name='name', requires=IS_NOT_EMPTY()) >>> IS_NOT_EMPTY()(1) (1, None) >>> IS_NOT_EMPTY()(0) (0, None) >>> IS_NOT_EMPTY()('x') ('x', None) >>> IS_NOT_EMPTY()(' x ') ('x', None) >>> IS_NOT_EMPTY()(None) (None, 'enter a value') >>> IS_NOT_EMPTY()('') ('', 'enter a value') >>> IS_NOT_EMPTY()(' ') ('', 'enter a value') >>> IS_NOT_EMPTY()(' \n\t') ('', 'enter a value') >>> IS_NOT_EMPTY()([]) ([], 'enter a value') >>> IS_NOT_EMPTY(empty_regex='def')('def') ('', 'enter a value') >>> IS_NOT_EMPTY(empty_regex='de[fg]')('deg') ('', 'enter a value') >>> IS_NOT_EMPTY(empty_regex='def')('abc') ('abc', None) s enter a valuecCs9||_|dj oti||_n d|_dS(N(R3R'RR7R0R1(R'R3R((R#R4Hs  cCs=t|d|i\}}|o||ifSn|dfS(NR(RR&R'RtemptyR3R7(R'R&R((R#R8Os(R)R*R+R7R4R8(((R#R(s cBstZdZddZRS(sb example:: INPUT(_type='text', _name='name', requires=IS_ALPHANUMERIC()) >>> IS_ALPHANUMERIC()('1') ('1', None) >>> IS_ALPHANUMERIC()('') ('', None) >>> IS_ALPHANUMERIC()('A_a') ('A_a', None) >>> IS_ALPHANUMERIC()('!') ('!', 'enter only letters, numbers, and underscore') s+enter only letters, numbers, and underscorecCsti|d|dS(Ns^[\w]*$(RR4R'R3(R'R3((R#R4fs(R)R*R+R4(((R#RVs cBsatZdZeideieiBZeideieiBZe e ddZ dZ RS(s$ Checks if field's value is a valid email address. Can be set to disallow or force addresses from certain domain(s). Email regex adapted from http://haacked.com/archive/2007/08/21/i-knew-how-to-validate-an-email-address-until-i.aspx, generally following the RFCs, except that we disallow quoted strings and permit underscores and leading numerics in subdomain labels Arguments: - banned: regex text for disallowed address domains - forced: regex text for required address domains Both arguments can also be custom objects with a match(value) method. Examples:: #Check for valid email address: INPUT(_type='text', _name='name', requires=IS_EMAIL()) #Check for valid email address that can't be from a .com domain: INPUT(_type='text', _name='name', requires=IS_EMAIL(banned='^.*\.com(|\..*)$')) #Check for valid email address that must be from a .edu domain: INPUT(_type='text', _name='name', requires=IS_EMAIL(forced='^.*\.edu(|\..*)$')) >>> IS_EMAIL()('a@b.com') ('a@b.com', None) >>> IS_EMAIL()('abc@def.com') ('abc@def.com', None) >>> IS_EMAIL()('abc@3def.com') ('abc@3def.com', None) >>> IS_EMAIL()('abc@def.us') ('abc@def.us', None) >>> IS_EMAIL()('abc@d_-f.us') ('abc@d_-f.us', None) >>> IS_EMAIL()('@def.com') # missing name ('@def.com', 'enter a valid email address') >>> IS_EMAIL()('"abc@def".com') # quoted name ('"abc@def".com', 'enter a valid email address') >>> IS_EMAIL()('abc+def.com') # no @ ('abc+def.com', 'enter a valid email address') >>> IS_EMAIL()('abc@def.x') # one-char TLD ('abc@def.x', 'enter a valid email address') >>> IS_EMAIL()('abc@def.12') # numeric TLD ('abc@def.12', 'enter a valid email address') >>> IS_EMAIL()('abc@def..com') # double-dot in domain ('abc@def..com', 'enter a valid email address') >>> IS_EMAIL()('abc@.def.com') # dot starts domain ('abc@.def.com', 'enter a valid email address') >>> IS_EMAIL()('abc@def.c_m') # underscore in TLD ('abc@def.c_m', 'enter a valid email address') >>> IS_EMAIL()('NotAnEmail') # missing @ ('NotAnEmail', 'enter a valid email address') >>> IS_EMAIL()('abc@NotAnEmail') # missing TLD ('abc@NotAnEmail', 'enter a valid email address') >>> IS_EMAIL()('customer/department@example.com') ('customer/department@example.com', None) >>> IS_EMAIL()('$A12345@example.com') ('$A12345@example.com', None) >>> IS_EMAIL()('!def!xyz%abc@example.com') ('!def!xyz%abc@example.com', None) >>> IS_EMAIL()('_Yosemite.Sam@example.com') ('_Yosemite.Sam@example.com', None) >>> IS_EMAIL()('~@example.com') ('~@example.com', None) >>> IS_EMAIL()('.wooly@example.com') # dot starts name ('.wooly@example.com', 'enter a valid email address') >>> IS_EMAIL()('wo..oly@example.com') # adjacent dots in name ('wo..oly@example.com', 'enter a valid email address') >>> IS_EMAIL()('pootietang.@example.com') # dot ends name ('pootietang.@example.com', 'enter a valid email address') >>> IS_EMAIL()('.@example.com') # name is bare dot ('.@example.com', 'enter a valid email address') >>> IS_EMAIL()('Ima.Fool@example.com') ('Ima.Fool@example.com', None) >>> IS_EMAIL()('Ima Fool@example.com') # space in name ('Ima Fool@example.com', 'enter a valid email address') >>> IS_EMAIL()('localguy@localhost') # localhost as domain ('localguy@localhost', None) s ^(?!\.) # name may not begin with a dot ( [-a-z0-9!\#$%&'*+/=?^_`{|}~] # all legal characters except dot | (?>> IS_GENERIC_URL()('http://user@abc.com') ('http://user@abc.com', None) senter a valid URLcCsi||_|djo t|_n ||_||_|i|ijotd|i|ifndS(s :param error_message: a string, the error message to give the end user if the URL does not validate :param allowed_schemes: a list containing strings or None. Each element is a scheme the inputed URL is allowed to use :param prepend_scheme: a string, this scheme is prepended if it's necessary to make the URL valid s0prepend_scheme='%s' is not in allowed_schemes=%sN(R3R'tallowed_schemesR7tall_url_schemesR-t SyntaxError(R'R3R6R-((R#R4*s      cCs7y tidi|ptidi|oti|id}|djot i |i }n||i jo|dfSqtidi| old|i jo\|ipd}|i|d|}|ddjo |io|Sq|dfSqqqnWnnX||ifS(s :param value: a string, the URL to validate :returns: a tuple, where tuple[0] is the inputed value (possible prepended with prepend_scheme), and tuple[1] is either None (success!) or the string error_message sb%[^0-9A-Fa-f]{2}|%[^0-9A-Fa-f][0-9A-Fa-f]|%[0-9A-Fa-f][^0-9A-Fa-f]|%$|%[0-9A-Fa-f]$|%[^0-9A-Fa-f]$s&[A-Za-z0-9;/?:@&=+$,\-_\.!~*'\(\)%#]+$is://RiN(R0R1tsearchR&R5R*R6R0R7turllibtunquotetlowerR'R6R-t schemeToUseR8t prependTestR3(R'R&R>R0R=((R#R8Ds& * (R)R*R+R7R4R8(((R#R5 s tactadtaetaerotaftagtaitaltamtantaotaqtartarpatastasiatattautaxtaztbatbbtbdtbetbftbgtbhtbitbiztbjtbltbmtbntbotbrtbstbttbvtbwtbytbztcatcattcctcdtcftcgtchtcitcktcltcmtcntcotcomtcooptcrtcutcvtcxtcytcztdetdjtdktdmtdotdztecteduteetegtehtertestetteutexampletfitfjtfktfmtfotfrtgatgbtgdtgetgftghtgitgltgmtgntgovtgptgqtgrtgstgttgutgwtgythkthmthnthrthtthuRytietiltinRtinvalidtiotiqtirtistittjetjmtjotjobstjptketkgtkhtkitkmtkntkptkrtkwtkytkztlatlbtlctlitlkt localhosttlrtlstlttlutlvtlytmatmctmdtmetmftmgtmhtmiltmktmltmmtmntmotmobitmptmqtmrtmstmttmutmuseumtmvtmwtmxtmytmztnaRtnctnetnettnftngtnitnltnotnptnrtnutnztomtorgtpatpetpftpgtphtpktpltpmtpntprtprotpstpttpwtpytqaR0trotrstrutrwtsatsbtsctsdtsetsgtshtsitsjtsktsltsmtsntsotsrtsttsutsvtsytszttcttdttestttfttgtthttjttkttlttmttnttottpttrttraveltttttwttztuatugtuktumtustuytuztvatvctvetvgtvitvntvutwftwss xn--0zwm56dsxn--11b5bs3a9aj6gsxn--80akhbyknj4fsxn--9t4b11yi5as xn--deba0ads xn--g6w251dsxn--hgbk6aj7f53bbasxn--hlcj6aya9esc7as xn--jxalpdlps xn--kgbechtvs xn--zckzahtyetyttyutzatzmtzwt IS_HTTP_URLcBs)tZdZdeddZdZRS(s Rejects a URL string if any of the following is true: * The string is empty or None * The string uses characters that are not allowed in a URL * The string breaks any of the HTTP syntactic rules * The URL scheme specified (if one is specified) is not 'http' or 'https' * The top-level domain (if a host name is specified) does not exist Based on RFC 2616: http://www.faqs.org/rfcs/rfc2616.html This function only checks the URL's syntax. It does not check that the URL points to a real document, for example, or that it otherwise makes sense semantically. This function does automatically prepend 'http://' in front of a URL in the case of an abbreviated URL (e.g. 'google.ca'). The list of allowed schemes is customizable with the allowed_schemes parameter. If you exclude None from the list, then abbreviated URLs (lacking a scheme such as 'http') will be rejected. The default prepended scheme is customizable with the prepend_scheme parameter. If you set prepend_scheme to None then prepending will be disabled. URLs that require prepending to parse will still be accepted, but the return value will not be modified. @author: Jonathan Benn >>> IS_HTTP_URL()('http://1.2.3.4') ('http://1.2.3.4', None) >>> IS_HTTP_URL()('http://abc.com') ('http://abc.com', None) >>> IS_HTTP_URL()('https://abc.com') ('https://abc.com', None) >>> IS_HTTP_URL()('httpx://abc.com') ('httpx://abc.com', 'enter a valid URL') >>> IS_HTTP_URL()('http://abc.com:80') ('http://abc.com:80', None) >>> IS_HTTP_URL()('http://user@abc.com') ('http://user@abc.com', None) >>> IS_HTTP_URL()('http://user@1.2.3.4') ('http://user@1.2.3.4', None) senter a valid URLRcCs||_|djo t|_n ||_||_x5|iD]*}|tjotd|tfq?q?W|i|ijotd|i|ifndS(s :param error_message: a string, the error message to give the end user if the URL does not validate :param allowed_schemes: a list containing strings or None. Each element is a scheme the inputed URL is allowed to use :param prepend_scheme: a string, this scheme is prepended if it's necessary to make the URL valid s&allowed_scheme value '%s' is not in %ss0prepend_scheme='%s' is not in allowed_schemes=%sN(R3R'R6R7t http_schemesR-R]R8(R'R3R6R-R]((R#R4s        c Csytd|id|id|i}||dd joLti |}|i d}|oxt idi |o|d fSqt idi |}|o.|i ditjo|d fSqqq|i d}t id i |o|d fSqt id i|p\|ipd }|i|d |}|dd jo |io|Sq|d fSqqnWnnX||ifS( s :param value: a string, the URL to validate :returns: a tuple, where tuple[0] is the inputed value (possible prepended with prepend_scheme), and tuple[1] is either None (success!) or the string error_message R3R6R-iis1([\w.!~*'|;:&=+$,-]+@)?\d+\.\d+\.\d+\.\d+(:\d*)*$s([\w.!~*'|;:&=+$,-]+@)?(([A-Za-z0-9]+[A-Za-z0-9\-]*[A-Za-z0-9]+\.)*([A-Za-z0-9]+\.)*)*([A-Za-z]+[A-Za-z0-9\-]*[A-Za-z0-9]+)\.?(:\d*)*$it/s://RN(R5R'R3R6R-R R&R7R*R5tcomponentsMatchR6R"R0R1t domainMatchR<tofficial_top_level_domainsR1R9R=R8R>( R'R&R>RMR1R R=RNR"((R#R8s8   (R)R*R+R7R4R8(((R#RJs *!cBs,tZdZddeddZdZRS(s Rejects a URL string if any of the following is true: * The string is empty or None * The string uses characters that are not allowed in a URL * The string breaks any of the HTTP syntactic rules * The URL scheme specified (if one is specified) is not 'http' or 'https' * The top-level domain (if a host name is specified) does not exist (These rules are based on RFC 2616: http://www.faqs.org/rfcs/rfc2616.html) This function only checks the URL's syntax. It does not check that the URL points to a real document, for example, or that it otherwise makes sense semantically. This function does automatically prepend 'http://' in front of a URL in the case of an abbreviated URL (e.g. 'google.ca'). If the parameter mode='generic' is used, then this function's behavior changes. It then rejects a URL string if any of the following is true: * The string is empty or None * The string uses characters that are not allowed in a URL * The URL scheme specified (if one is specified) is not valid (These rules are based on RFC 2396: http://www.faqs.org/rfcs/rfc2396.html) The list of allowed schemes is customizable with the allowed_schemes parameter. If you exclude None from the list, then abbreviated URLs (lacking a scheme such as 'http') will be rejected. The default prepended scheme is customizable with the prepend_scheme parameter. If you set prepend_scheme to None then prepending will be disabled. URLs that require prepending to parse will still be accepted, but the return value will not be modified. IS_URL is compatible with the Internationalized Domain Name (IDN) standard specified in RFC 3490 (http://tools.ietf.org/html/rfc3490). As a result, URLs can be regular strings or unicode strings. If the URL's domain component (e.g. google.ca) contains non-US-ASCII letters, then the domain will be converted into Punycode (defined in RFC 3492, http://tools.ietf.org/html/rfc3492). IS_URL goes a bit beyond the standards, and allows non-US-ASCII characters to be present in the path and query components of the URL as well. These non-US-ASCII characters will be escaped using the standard '%20' type syntax. e.g. the unicode character with hex code 0x4e86 will become '%4e%86' Code Examples:: INPUT(_type='text', _name='name', requires=IS_URL()) >>> IS_URL()('abc.com') ('http://abc.com', None) INPUT(_type='text', _name='name', requires=IS_URL(mode='generic')) >>> IS_URL(mode='generic')('abc.com') ('abc.com', None) INPUT(_type='text', _name='name', requires=IS_URL(allowed_schemes=['https'], prepend_scheme='https')) >>> IS_URL(allowed_schemes=['https'], prepend_scheme='https')('https://abc.com') ('https://abc.com', None) INPUT(_type='text', _name='name', requires=IS_URL(prepend_scheme='https')) >>> IS_URL(prepend_scheme='https')('abc.com') ('https://abc.com', None) INPUT(_type='text', _name='name', requires=IS_URL(mode='generic', allowed_schemes=['ftps', 'https'], prepend_scheme='https')) >>> IS_URL(mode='generic', allowed_schemes=['ftps', 'https'], prepend_scheme='https')('https://abc.com') ('https://abc.com', None) >>> IS_URL(mode='generic', allowed_schemes=['ftps', 'https', None], prepend_scheme='https')('abc.com') ('abc.com', None) @author: Jonathan Benn senter a valid URLRcCs||_|i|_|iddgjotd|in||_|io.||ijotd||ifqn||_dS(s :param error_message: a string, the error message to give the end user if the URL does not validate :param allowed_schemes: a list containing strings or None. Each element is a scheme the inputed URL is allowed to use :param prepend_scheme: a string, this scheme is prepended if it's necessary to make the URL valid tgenericRsinvalid mode '%s' in IS_URLs0prepend_scheme='%s' is not in allowed_schemes=%sN(R3R'tmodeR<R8R6R-(R'R3RQR6R-((R#R4ws   cCs|idjo(td|id|id|i}nI|idjo(td|id|id|i}ntd|it |t jo||Snmyt ||i}Wn tj o||ifSnX||}|ddjo||dfSn|SdS( s :param value: a unicode or regular string, the URL to validate :returns: a (string, string) tuple, where tuple[0] is the modified input value and tuple[1] is either None (success!) or the string error_message. The input value will never be modified in the case of an error. However, if there is success then the input URL may be modified to (1) prepend a scheme, and/or (2) convert a non-compliant unicode URL into a compliant US-ASCII version. RPR3R6R-Rsinvalid mode '%s' in IS_URLiN(R'RQR5R3R6R-t subMethodRJR8ttypeR&RNR4t asciiValueR/t methodResultR7(R'R&RURTRR((R#R8s(      (R)R*R+R7R4R8(((R#R,s I!sQ((?P[0-9]+))([^0-9 ]+(?P[0-9 ]+))?([^0-9ap ]+(?P[0-9]*))?((?P[ap]m))?cBs#tZdZddZdZRS(s\ example:: INPUT(_type='text', _name='name', requires=IS_TIME()) understands the following formats hh:mm:ss [am/pm] hh:mm [am/pm] hh [am/pm] [am/pm] is optional, ':' can be replaced by any other non-space non-digit >>> IS_TIME()('21:30') (datetime.time(21, 30), None) >>> IS_TIME()('21-30') (datetime.time(21, 30), None) >>> IS_TIME()('21.30') (datetime.time(21, 30), None) >>> IS_TIME()('21:30:59') (datetime.time(21, 30, 59), None) >>> IS_TIME()('5:30') (datetime.time(5, 30), None) >>> IS_TIME()('5:30 am') (datetime.time(5, 30), None) >>> IS_TIME()('5:30 pm') (datetime.time(17, 30), None) >>> IS_TIME()('5:30 whatever') ('5:30 whatever', 'enter time as hh:mm:ss (seconds, am, pm optional)') >>> IS_TIME()('5:30 20') ('5:30 20', 'enter time as hh:mm:ss (seconds, am, pm optional)') >>> IS_TIME()('24:30') ('24:30', 'enter time as hh:mm:ss (seconds, am, pm optional)') >>> IS_TIME()('21:60') ('21:60', 'enter time as hh:mm:ss (seconds, am, pm optional)') >>> IS_TIME()('21:30::') ('21:30::', 'enter time as hh:mm:ss (seconds, am, pm optional)') >>> IS_TIME()('') ('', 'enter time as hh:mm:ss (seconds, am, pm optional)') s1enter time as hh:mm:ss (seconds, am, pm optional)cCs ||_dS(N(R3R'(R'R3((R#R4scCsyJ|}ti|i}t|iddd}}}|iddjot|id}n|iddjot|id}n|iddjo,d|jo djno|d}n|t djo#|t d jo|t d jpt d nt i|||}|dfSWn%tj ont j onX||ifS( NthitmtstdRi ii<s8Hours or minutes or seconds are outside of allowed range(R&tivaluet regex_timeR5R<RR6RVRWRXR7trangeRtdatetimettimetAttributeErrorR'R3(R'R&RWRZRXRV((R#R8s&#49(R)R*R+R4R8(((R#Rs ' cBs/tZdZdddZdZdZRS(s example:: INPUT(_type='text', _name='name', requires=IS_DATE()) date has to be in the ISO8960 format YYYY-MM-DD s%Y-%m-%dsenter date as %(format)scCs"t||_t||_dS(N(RtformatR'R3(R'R`R3((R#R4sc CsyYti|t|i\ } }}}}}} }}ti| ||}|dfSWn$||iti|ifSnXdS(N(R^tstrptimeR&RR'R`R"RWRYthhRtsstt0tt1tt2R]tdateR7R3Rtnice( R'R&RYRRfRWReRcRbRdR"((R#R8s 6cCs|i}|i}d|}|id|d}|id|}|djo d}nti||i|i }|i |S(Ns%.4is%yis%Yili( R'R`R&tyearR"RR]RgtmonthtdayRYtstrftime(R'R&RYR`RiR"((R#R( s     (R)R*R+R4R8R((((R#R s  cBsDtZdZdZedZdddZdZdZRS(s example:: INPUT(_type='text', _name='name', requires=IS_DATETIME()) datetime has to be in the ISO8960 format YYYY-MM-DD hh:mm:ss s%Y-%m-%d %H:%M:%Sc Cs]dddddddddd d!f }x&|D]\}}|i||}q.Wtd|S("Ns%Yt1963s%yt63s%dt28s%mt08s%btAugtAugusts%Ht14s%It02s%ptPMs%Mt30s%St59R`(s%YRm(s%yRn(s%dRo(s%mRp(s%bsAug(s%bRr(s%HRs(s%IRt(s%pRu(s%MRv(s%SRw(RR}R~R`RR@(R`R}RR~((R#Rh7s '  s!enter date and time as %(format)scCs"t||_t||_dS(N(RR`R'R3(R'R`R3((R#R4Hsc Csybti|t|i\ } }}}}}} }}ti| |||||}|dfSWn$||iti|ifSnXdS(N(R^RaR&RR'R`R"RWRYRbRRcRdReRfR]R7R3RRh( R'R&RYRRfRWReRcRbRdR"((R#R8Ms 6cCs|i}|i}d|}|id|d}|id|}|djo d}nti||i|i|i |i |i }|i |S(Ns%.4is%yis%Yili(R'R`R&RiR"RR]RjRkthourtminutetsecondRYRl(R'R&RYR`RiR"((R#R(Vs     -( R)R*R+t isodatetimet staticmethodRhR4R8R((((R#R,s  cBs,tZdZeededZdZRS(st example:: >>> v = IS_DATE_IN_RANGE(minimum=datetime.date(2008,1,1), maximum=datetime.date(2009,12,31), format="%m/%d/%Y",error_message="oops") >>> v('03/03/2008') (datetime.date(2008, 3, 3), None) >>> v('03/03/2010') (datetime.date(2010, 3, 3), 'oops') s%Y-%m-%dcCs||_||_|djo8|djo d}qW|djo d}qWd}ntd|d|}ti|d|d||dS(Nsenter date on or before %(max)ssenter date on or after %(min)ss#enter date in range %(min)s %(max)sR<R=R`R3( RR'RR3R7R@RYRR4R`(R'RRR`R3RY((R#R4ps         cCsti||\}}|dj o||fSn|io!|i|jo||ifSn|io!||ijo||ifSn|dfS(N( RR8R'R&tmsgR7RR3R(R'R&R}((R#R8s (R)R*R+R7R4R8(((R#Ras cBs,tZdZeededZdZRS(s example:: >>> v = IS_DATETIME_IN_RANGE( minimum=datetime.datetime(2008,1,1,12,20), maximum=datetime.datetime(2009,12,31,12,20), format="%m/%d/%Y %H:%M",error_message="oops") >>> v('03/03/2008 12:40') (datetime.datetime(2008, 3, 3, 12, 40), None) >>> v('03/03/2010 10:34') (datetime.datetime(2010, 3, 3, 10, 34), 'oops') s%Y-%m-%d %H:%M:%ScCs||_||_|djo8|djo d}qW|djo d}qWd}ntd|d|}ti|d|d||dS(Ns(enter date and time on or before %(max)ss'enter date and time on or after %(min)ss,enter date and time in range %(min)s %(max)sR<R=R`R3( RR'RR3R7R@RYRR4R`(R'RRR`R3RY((R#R4s         cCsti||\}}|dj o||fSn|io!|i|jo||ifSn|io!||ijo||ifSn|dfS(N( RR8R'R&R}R7RR3R(R'R&R}((R#R8s (R)R*R+R7R4R8(((R#Rs cBstZdZdZRS(NcCs ||_dS(N(totherR'(R'R~((R#R4scCs{|}t|tp |g}ng}xE|D]=}|i|\}}|o||fSq0|i |q0W|dfS(N( R&RZRBROt new_valueRUR'R~RteR&R7(R'R&RRZRURR((R#R8s (R)R*R4R8(((R#Rs cBstZdZdZRS(s| convert to lower case >>> IS_LOWER()('ABC') ('abc', None) >>> IS_LOWER()('Ñ') ('\xc3\xb1', None) cCs"|idiiddfS(NRA(R&RPR<tencodeR7(R'R&((R#R8s(R)R*R+R8(((R#Rs cBstZdZdZRS(s| convert to upper case >>> IS_UPPER()('abc') ('ABC', None) >>> IS_UPPER()('ñ') ('\xc3\x91', None) cCs"|idiiddfS(NRA(R&RPR!RR7(R'R&((R#R8s(R)R*R+R8(((R#Rs cBs;tZdZdeddZeddZdZRS(s1 convert arbitrary text string to a slug >>> IS_SLUG()('abc123') ('abc123', None) >>> IS_SLUG()('ABC123') ('abc123', None) >>> IS_SLUG()('abc-123') ('abc-123', None) >>> IS_SLUG()('abc--123') ('abc-123', None) >>> IS_SLUG()('abc 123') ('abc-123', None) >>> IS_SLUG()('-abc-') ('abc', None) >>> IS_SLUG()('abc&123') ('abc123', None) >>> IS_SLUG()('abc&123&def') ('abc123def', None) >>> IS_SLUG()('ñ') ('n', None) >>> IS_SLUG(maxlen=4)('abc123') ('abc1', None) iPs must be slugcCs||_||_||_dS(N(tmaxlenR'tcheckR3(R'RRR3((R#R4 s  cCs|idi}tid|}|idd}tidd|}tidd|}|i dd }tid d |}|i d }|| i d S( Nsutf-8tNFKDtASCIItignores&\w+?;RQs [^a-z0-9\-\s]t t-s--+( R&RPR<RXt unicodedatat normalizeRR0tsubRRR(R&RRX((R#turlify scCsP|io-|ti||ijo||ifSnti||idfS(N(R'RR&RRRR3R7(R'R&((R#R8 s&(R)R*R+RcR4R|RR8(((R#Rs  cBsAtZdZeedZdZdZdZdZRS(s dummy class for testing IS_EMPTY_OR >>> IS_EMPTY_OR(IS_EMAIL())('abc@def.com') ('abc@def.com', None) >>> IS_EMPTY_OR(IS_EMAIL())(' ') (None, None) >>> IS_EMPTY_OR(IS_EMAIL(), null='abc')(' ') ('abc', None) >>> IS_EMPTY_OR(IS_EMAIL(), null='abc', empty_regex='def')('def') ('abc', None) >>> IS_EMPTY_OR(IS_EMAIL())('abc') ('abc', 'enter a valid email address') >>> IS_EMPTY_OR(IS_EMAIL())(' abc ') ('abc', 'enter a valid email address') cCs|||_|_|dj oti||_n d|_t|do|i|_nt|do|i |_ ndS(NRRRa( R~tnullR'RR7R0R1RkRRt_optionsRa(R'R~RR((R#R4+ s  cCsO|ii}| p|dddjo|i o|iddn|S(NiRQ(RQRQ(R'R~RaRRR`(R'Ra((R#R6 s(cCs+t|ido|ii|ndS(NR{(RkR'R~R{Ry(R'Ry((R#R{< scCst|d|i\}}|o|idfSnt|it t fo@x/|iD]$}||\}}|oPqVqVW||fSn|i|SdS(NR( RR&R'RRRR7RBR~RORXRUterror(R'R&RURR((R#R8@ s  cCs+t|ido|ii|Sn|S(NR((RkR'R~R(R&(R'R&((R#R(L s( R)R*R+R7R4RR{R8R((((R#R  s    cBs#tZdZddZdZRS(s example:: INPUT(_type='text', _name='name', requires=CLEANUP()) removes special characters on validation s[^ \w]cCsti||_dS(N(R0R1R2R'(R'R2((R#R4] scCs+|iidt|i}|dfS(NRQ(R'R2RRR&RRR7(R'R&R((R#R8` s!(R)R*R+R4R8(((R#RT s  cBs&tZdZeedZdZRS(s example:: INPUT(_type='text', _name='name', requires=CRYPT()) encodes the value on validation with a digest. If no arguments are provided CRYPT uses the MD5 algorithm. If the key argument is provided the HMAC+MD5 algorithm is used. If the digest_alg is specified this is used to replace the MD5 with, for example, SHA512. The digest_alg can be the name of a hashlib algorithm as a string or the algorithm itself. cCsi|o;| o3|iddjo|id\}}qBn|p d}n||_||_dS(Nt:itmd5(tkeyt digest_algRRmR'(R'RR((R#R4t s  cCsY|io5t|i}ti|i||idfSnt ||idfSdS(N( R'Rt get_digestRtalgthmactnewR&t hexdigestR7thash(R'R&R((R#R8} s &(R)R*R+R7R4R8(((R#Re s  c Bs;tZdZdddddddded ZdZRS(s example:: INPUT(_type='password', _name='passwd', requires=IS_STRONG(min=10, special=2, upper=2)) enforces complexity requirements on a field iiis~!@#$%^&*()_+-=?<>,.:;{}[]|s "c CsU||_||_||_||_||_||_||_||_| |_ dS(N( R<R'R=R!R<tnumbertspecialtspecialsRR3( R'R<R=R!R<RRRRR3((R#R4 s        c Csg} t|itjoB|idjo2t||ijp| id|iq^nt|itjoB|idjo2t||ijp| id|iqnt|i tjo|g}|i D]}|||jq~} |i djo>| it|i jp!| id|i |i fqDqHn|io\g}|iD]}|||jq`~}|itdjo| id|iqnt|itjotid|}|idjo8t||ijp| idt|iqBqFt|djo| idqFnt|itjotid |}|idjo8t||ijp| id t|iqqt|djo| id qnt|itjotid |}|idjo^d } |idjo d} nt||ijp$| idt|i| fqqt|djo| idqnt| djo|dfSn|ip*dkl}||di | fSn||ifSdS(NisMinimum length is %ssMaximum length is %ss.Must include at least %s of the following : %ss(May not contain any of the following: %ss[A-Z]s#Must include at least %s upper cases&May not include any upper case letterss[a-z]s#Must include at least %s lower cases&May not include any lower case letterss[0-9]RitnumberssMust include at least %s %ssMay not include any numbers(sXMLs
(!RbRSR'R<RRMR&R&R=RRTRRnt all_specialRR9Rt all_invalidR!R0Rrt all_upperRR<t all_lowerRt all_numberRR7R3thtmltXMLtjoin( R'R&RRRnRRRTRRRbR((R#R8 s\&&*) *"" (  (R)R*R+R7R4R8(((R#R s $ t IS_IN_SUBSETcBstZdZdZRS(NcOsti|||dS(N(RR4R'R}R~(R'R}R~((R#R4 scCs|tidit|}g}|D](}ti ||do ||q)q)~}|o||i fSn|dfS(Ns\w+i(R0R1RrRR&RVRTR RR8R'RbR3R7(R'R&RTRVR Rb((R#R8 s <(R)R*R4R8(((R#R s cBsPtZdZdddddZd Zd Zd Zd Zd ZRS(s Checks if file uploaded through file input was saved in one of selected image formats and has dimensions (width and height) within given boundaries. Does *not* check for maximum file size (use IS_LENGTH for that). Returns validation failure if no data was uploaded. Supported file formats: BMP, GIF, JPEG, PNG. Code parts taken from http://mail.python.org/pipermail/python-list/2007-June/617126.html Arguments: extensions: iterable containing allowed *lowercase* image file extensions ('jpg' extension of uploaded file counts as 'jpeg') maxsize: iterable containing maximum width and height of the image minsize: iterable containing minimum width and height of the image Use (-1, -1) as minsize to pass image size check. Examples:: #Check if uploaded file is in any of supported image formats: INPUT(_type='file', _name='name', requires=IS_IMAGE()) #Check if uploaded file is either JPEG or PNG: INPUT(_type='file', _name='name', requires=IS_IMAGE(extensions=('jpeg', 'png'))) #Check if uploaded file is PNG with maximum size of 200x200 pixels: INPUT(_type='file', _name='name', requires=IS_IMAGE(extensions=('png'), maxsize=(200, 200))) tbmptgiftjpegtpngi'is invalid imagecCs(||_||_||_||_dS(N(t extensionsR'R>R?R3(R'RR>R?R3((R#R4 s   cCsy|iid}|djpt|i|di}|djo d}n||ijpt|djo|i|i \}}n|djo|i |i \}}n_|djo|i |i \}}n6|djo|i|i \}}n d }d }|id|jo|idjno,|id|jo|idjnpt|i id|dfSWn||ifSnXdS( NReiitjpgRRRRi(R&tfilenametrfindt extensiontAssertionErrorR<R'Rt_IS_IMAGE__bmpREtwidththeightt_IS_IMAGE__gift_IS_IMAGE__jpegt_IS_IMAGE__pngR?R>RFR7R3(R'R&RRR((R#R8 s,      _cCsD|iddjo*|idtid|idSndS(NitBMis file / png False means first dot, eg. file.tar.gz -> file / tar.gz case: 0 - keep the case, 1 - transform the string into lowercase (default), 2 - transform the string into uppercase If there is no dot present, extension checks will be done against empty string and filename checks against whole value. Examples:: #Check if file has a pdf extension (case insensitive): INPUT(_type='file', _name='name', requires=IS_UPLOAD_FILENAME(extension='pdf')) #Check if file has a tar.gz extension and name starting with backup: INPUT(_type='file', _name='name', requires=IS_UPLOAD_FILENAME(filename='backup.*', extension='tar.gz', lastdot=False)) #Check if file has no extension and name matching README #(case sensitive): INPUT(_type='file', _name='name', requires=IS_UPLOAD_FILENAME(filename='^README$', extension='^$', case=0)) isenter valid filenamecCswt|toti|}nt|toti|}n||_||_||_||_||_ dS(N( RBRRR0R1RR'tlastdottcaseR3(R'RRRRR3((R#R4t s    cCs%y |i}Wn||ifSnX|idjo|i}n!|idjo|i}n|io|i d}n|i d}|djot |}n|io)|ii ||  o||ifSnB|io-|ii ||d o||ifSn |dfSdS(NiiRei(R&RRR'R3RR<R!RRRtfindRMR5RR7(R'R&RR((R#R8 s$   "&(R)R*R+R7R9R4R8(((R#RM s % cBsbtZdZeidZdZdZddfZdZ d de e e e ddZ dZ RS(sf Checks if field's value is an IP version 4 address in decimal form. Can be set to force addresses from certain range. IPv4 regex taken from: http://regexlib.com/REDetails.aspx?regexp_id=1411 Arguments: minip: lowest allowed address; accepts: str, eg. 192.168.0.1 list or tuple of octets, eg. [192, 168, 0, 1] maxip: highest allowed address; same as above invert: True to allow addresses only from outside of given range; note that range boundaries are not matched this way is_localhost: localhost address treatment: None (default): indifferent True (enforce): query address must match localhost address (127.0.0.1) False (forbid): query address must not match localhost address is_private: same as above, except that query address is checked against two address ranges: 172.16.0.0 - 172.31.255.255 and 192.168.0.0 - 192.168.255.255 is_automatic: same as above, except that query address is checked against one address range: 169.254.0.0 - 169.254.255.255 Minip and maxip may also be lists or tuples of addresses in all above forms (str, int, list / tuple), allowing setup of multiple address ranges: minip = (minip1, minip2, ... minipN) | | | | | | maxip = (maxip1, maxip2, ... maxipN) Longer iterable will be truncated to match length of shorter one. Examples:: #Check for valid IPv4 address: INPUT(_type='text', _name='name', requires=IS_IPV4()) #Check for valid IPv4 address belonging to specific range: INPUT(_type='text', _name='name', requires=IS_IPV4(minip='100.200.0.0', maxip='100.200.255.255')) #Check for valid IPv4 address belonging to either 100.110.0.0 - #100.110.255.255 or 200.50.0.0 - 200.50.0.255 address range: INPUT(_type='text', _name='name', requires=IS_IPV4(minip=('100.110.0.0', '200.50.0.0'), maxip=('100.110.255.255', '200.50.0.255'))) #Check for valid IPv4 address belonging to private address space: INPUT(_type='text', _name='name', requires=IS_IPV4(is_private=True)) #Check for valid IPv4 address that is not a localhost address: INPUT(_type='text', _name='name', requires=IS_IPV4(is_localhost=False)) >>> IS_IPV4()('1.2.3.4') ('1.2.3.4', None) >>> IS_IPV4()('255.255.255.255') ('255.255.255.255', None) >>> IS_IPV4()('1.2.3.4 ') ('1.2.3.4 ', 'enter valid IPv4 address') >>> IS_IPV4()('1.2.3.4.5') ('1.2.3.4.5', 'enter valid IPv4 address') >>> IS_IPV4()('123.123') ('123.123', 'enter valid IPv4 address') >>> IS_IPV4()('1111.2.3.4') ('1111.2.3.4', 'enter valid IPv4 address') >>> IS_IPV4()('0111.2.3.4') ('0111.2.3.4', 'enter valid IPv4 address') >>> IS_IPV4()('256.2.3.4') ('256.2.3.4', 'enter valid IPv4 address') >>> IS_IPV4()('300.2.3.4') ('300.2.3.4', 'enter valid IPv4 address') >>> IS_IPV4(minip='1.2.3.4', maxip='1.2.3.4')('1.2.3.4') ('1.2.3.4', None) >>> IS_IPV4(minip='1.2.3.5', maxip='1.2.3.9', error_message='bad ip')('1.2.3.4') ('1.2.3.4', 'bad ip') >>> IS_IPV4(maxip='1.2.3.4', invert=True)('127.0.0.1') ('127.0.0.1', None) >>> IS_IPV4(maxip='1.2.3.4', invert=True)('1.2.3.4') ('1.2.3.4', 'enter valid IPv4 address') >>> IS_IPV4(is_localhost=True)('127.0.0.1') ('127.0.0.1', None) >>> IS_IPV4(is_localhost=True)('1.2.3.4') ('1.2.3.4', 'enter valid IPv4 address') >>> IS_IPV4(is_localhost=False)('127.0.0.1') ('127.0.0.1', 'enter valid IPv4 address') >>> IS_IPV4(maxip='100.0.0.0', is_localhost=True)('127.0.0.1') ('127.0.0.1', 'enter valid IPv4 address') sK^(([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.){3}([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])$iiiiil Xl!XlPlQlSlSs0.0.0.0s255.255.255.255senter valid IPv4 addresscCsxt||fD]\}} g} t| to| i| i dnt| t t fot | t t d| jo djno| i| qxc| D]W}t|to| i|i dqt|t t fo| i|qqWng} xX| D]P}d} x4t|i|D] \} }| | t|7} q>W| i| qW|djo | |_q| |_qW||_||_||_||_||_dS(NRecCs t|tS(N(RBRUR(RU((R#R sii(R\tminiptmaxiptnR&ttempRBRR&RmRORXRMtfilterRURRtzipR'R]tjRtinvertt is_localhostt is_privatet is_automaticR3(R'RRRRRRR3RRRRR]R&RRU((R#R4 s<  6        c Cs|ii|od}x=t|i|idD] \}}||t |7}q5Wt }xTt|i |iD]=\}}|i||jo |jnjo t}ququW|idjp|i||ijjp t }n|idjpV|itg}|iD],}||d|jo|djnq~djjp t }n|idjp2|i|id|jo|idjnjp t }n|o|dfSqn||ifS(NiRei(R'R2R5R&RRRRmR]RRRctokRRtbottomttopRR9RR7RRtsumRTtprivateRt automaticR3( R'R&RRR]RRRTR((R#R8" s&  ') i E (iiii(l Xl!X(lPlQ(lSlS(R)R*R+R0R1R2RRRRRcR7R4R8(((R#R s \ &t__main__(IR+RGR0R]R^RCRR:RRRt cStringIORtutilsRRt__all__R$tobjectR%RRR RRR1RpRqR RRR RR7RRRRtofficial_url_schemestunofficial_url_schemesR7RKR*R!RR)R4R5RORJRR[RRRRRRRRRR RRRRRR RRR)tdoctestttestmod(ARRRRRR[RR R]RRKRpRRqRRR$RRR:R R0RRR*R5RRRRRRRCR4RRRR R7RRRRRRR%RJRRRRRORR R R)RRR!RR^RRGRR((R#t? s           c" .>W{3U\f .D6   . <oi%D#5-,08 P kJ