diff --git a/applications/admin/controllers/default.py b/applications/admin/controllers/default.py index a2840a51..9eaf90d1 100644 --- a/applications/admin/controllers/default.py +++ b/applications/admin/controllers/default.py @@ -32,15 +32,15 @@ from gluon.languages import (read_possible_languages, read_dict, write_dict, if DEMO_MODE and request.function in ['change_password', 'pack', -'pack_custom','pack_plugin', 'upgrade_web2py', 'uninstall', -'cleanup', 'compile_app', 'remove_compiled_app', 'delete', -'delete_plugin', 'create_file', 'upload_file', 'update_languages', -'reload_routes', 'git_push', 'git_pull', 'install_plugin']: + 'pack_custom', 'pack_plugin', 'upgrade_web2py', 'uninstall', + 'cleanup', 'compile_app', 'remove_compiled_app', 'delete', + 'delete_plugin', 'create_file', 'upload_file', 'update_languages', + 'reload_routes', 'git_push', 'git_pull', 'install_plugin']: session.flash = T('disabled in demo mode') redirect(URL('site')) if is_gae and request.function in ('edit', 'edit_language', -'edit_plurals', 'update_languages', 'create_file', 'install_plugin'): + 'edit_plurals', 'update_languages', 'create_file', 'install_plugin'): session.flash = T('disabled in GAE mode') redirect(URL('site')) @@ -74,8 +74,10 @@ def log_progress(app, mode='EDIT', filename=None, progress=0): def safe_open(a, b): if (DEMO_MODE or is_gae) and ('w' in b or 'a' in b): class tmp: + def write(self, data): pass + def close(self): pass return tmp() @@ -208,6 +210,7 @@ def site(): file_or_appurl = 'file' in request.vars or 'appurl' in request.vars class IS_VALID_APPNAME(object): + def __call__(self, value): if not re.compile('^\w+$').match(value): return (value, T('Invalid application name')) @@ -325,7 +328,7 @@ def report_progress(app): if not m: continue days = -(request.now - datetime.datetime.strptime(m[0], - '%Y-%m-%d %H:%M:%S')).days + '%Y-%m-%d %H:%M:%S')).days counter += int(m[1]) events.append([days, counter]) return events @@ -353,6 +356,7 @@ def pack(): session.flash = T('internal error: %s', e) redirect(URL('site')) + def pack_plugin(): app = get_app() if len(request.args) == 2: @@ -368,7 +372,6 @@ def pack_plugin(): redirect(URL('plugin', args=request.args)) - def pack_exe(app, base, filenames=None): import urllib import zipfile @@ -397,10 +400,20 @@ def pack_exe(app, base, filenames=None): def pack_custom(): app = get_app() base = apath(app, r=request) + + def ignore(fs): + return [f for f in fs if not ( + f[:1] in '#' or f.endswith('~') or f.endswith('.bak'))] + files = {} + for (r, d, f) in os.walk(base): + files[r] = {'folders': ignore(d), 'files': ignore(f)} + if request.post_vars.file: - + valid_set = set(os.path.relpath(os.path.join(r, f), base) for r in files for f in files[r]['files']) files = request.post_vars.file - files = [files] if not isinstance(files,list) else files + files = [files] if not isinstance(files, list) else files + files = [file for file in files if file in valid_set] + if request.post_vars.doexe is None: fname = 'web2py.app.%s.w2p' % app try: @@ -417,12 +430,7 @@ def pack_custom(): redirect(URL(args=request.args)) else: return pack_exe(app, base, files) - def ignore(fs): - return [f for f in fs if not ( - f[:1] in '#' or f.endswith('~') or f.endswith('.bak'))] - files = {} - for (r,d,f) in os.walk(base): - files[r] = {'folders':ignore(d),'files':ignore(f)} + return locals() @@ -485,14 +493,14 @@ def cleanup(): def compile_app(): app = get_app() c = app_compile(app, request, - skip_failed_views = (request.args(1) == 'skip_failed_views')) + skip_failed_views=(request.args(1) == 'skip_failed_views')) if not c: session.flash = T('application compiled') elif isinstance(c, list): session.flash = DIV(*[T('application compiled'), BR(), BR(), T('WARNING: The following views could not be compiled:'), BR()] + - [CAT(BR(), view) for view in c] + - [BR(), BR(), T('DO NOT use the "Pack compiled" feature.')]) + [CAT(BR(), view) for view in c] + + [BR(), BR(), T('DO NOT use the "Pack compiled" feature.')]) else: session.flash = DIV(T('Cannot compile: there are errors in your app:'), CODE(c)) @@ -546,6 +554,7 @@ def enable(): safe_open(filename, 'wb').write('disabled: True\ntime-disabled: %s' % request.now) return SPAN(T('Enable'), _style='color:red') + def peek(): """ Visualize object code """ app = get_app(request.vars.app) @@ -609,7 +618,7 @@ def edit(): # Load json only if it is ajax edited... app = get_app(request.vars.app) app_path = apath(app, r=request) - preferences={'theme':'web2py', 'editor': 'default', 'closetag': 'true', 'codefolding': 'false', 'tabwidth':'4', 'indentwithtabs':'false', 'linenumbers':'true', 'highlightline':'true'} + preferences = {'theme': 'web2py', 'editor': 'default', 'closetag': 'true', 'codefolding': 'false', 'tabwidth': '4', 'indentwithtabs': 'false', 'linenumbers': 'true', 'highlightline': 'true'} config = Config(os.path.join(request.folder, 'settings.cfg'), section='editor', default_values={}) preferences.update(config.read()) @@ -617,14 +626,14 @@ def edit(): if not(request.ajax) and not(is_mobile): # return the scaffolding, the rest will be through ajax requests response.title = T('Editing %s') % app - return response.render ('default/edit.html', dict(app=app, editor_settings=preferences)) + return response.render('default/edit.html', dict(app=app, editor_settings=preferences)) # show settings tab and save prefernces if 'settings' in request.vars: - if request.post_vars: #save new preferences + if request.post_vars: # save new preferences post_vars = request.post_vars.items() # Since unchecked checkbox are not serialized, we must set them as false by hand to store the correct preference in the settings - post_vars+= [(opt, 'false') for opt in preferences if opt not in request.post_vars ] + post_vars += [(opt, 'false') for opt in preferences if opt not in request.post_vars] if config.save(post_vars): response.headers["web2py-component-flash"] = T('Preferences saved correctly') else: @@ -632,8 +641,8 @@ def edit(): response.headers["web2py-component-command"] = "update_editor(%s);$('a[href=#editor_settings] button.close').click();" % response.json(config.read()) return else: - details = {'realfilename':'settings', 'filename':'settings', 'id':'editor_settings', 'force': False} - details['plain_html'] = response.render('default/editor_settings.html', {'editor_settings':preferences}) + details = {'realfilename': 'settings', 'filename': 'settings', 'id': 'editor_settings', 'force': False} + details['plain_html'] = response.render('default/editor_settings.html', {'editor_settings': preferences}) return response.json(details) """ File edit handler """ @@ -764,8 +773,8 @@ def edit(): view = request.args[3].replace('.html', '') view_link = URL(request.args[0], request.args[2], view) elif filetype == 'python' and request.args[1] == 'controllers': - ## it's a controller file. - ## Create links to all of the associated view files. + # it's a controller file. + # Create links to all of the associated view files. app = get_app() viewname = os.path.splitext(request.args[2])[0] viewpath = os.path.join(app, 'views', viewname) @@ -796,22 +805,22 @@ def edit(): return response.json({'file_hash': file_hash, 'saved_on': saved_on, 'functions': functions, 'controller': controller, 'application': request.args[0], 'highlight': highlight}) else: file_details = dict(app=request.args[0], - lineno=request.vars.lineno or 1, - editor_settings=preferences, - filename=filename, - realfilename=realfilename, - filetype=filetype, - data=data, - edit_controller=edit_controller, - file_hash=file_hash, - saved_on=saved_on, - controller=controller, - functions=functions, - view_link=view_link, - editviewlinks=editviewlinks, - id=IS_SLUG()(filename)[0], - force= True if (request.vars.restore or - request.vars.revert) else False) + lineno=request.vars.lineno or 1, + editor_settings=preferences, + filename=filename, + realfilename=realfilename, + filetype=filetype, + data=data, + edit_controller=edit_controller, + file_hash=file_hash, + saved_on=saved_on, + controller=controller, + functions=functions, + view_link=view_link, + editviewlinks=editviewlinks, + id=IS_SLUG()(filename)[0], + force=True if (request.vars.restore or + request.vars.revert) else False) plain_html = response.render('default/edit_js.html', file_details) file_details['plain_html'] = plain_html if is_mobile: @@ -820,14 +829,16 @@ def edit(): else: return response.json(file_details) + def todolist(): """ Returns all TODO of the requested app """ app = request.vars.app or '' - app_path = apath('%(app)s' % {'app':app}, r=request) - dirs=['models', 'controllers', 'modules', 'private' ] + app_path = apath('%(app)s' % {'app': app}, r=request) + dirs = ['models', 'controllers', 'modules', 'private'] + def listfiles(app, dir, regexp='.*\.py$'): - files = sorted( listdir(apath('%(app)s/%(dir)s/' % {'app':app, 'dir':dir}, r=request), regexp)) + files = sorted(listdir(apath('%(app)s/%(dir)s/' % {'app': app, 'dir': dir}, r=request), regexp)) files = [x.replace(os.path.sep, '/') for x in files if not x.endswith('.bak')] return files @@ -838,17 +849,18 @@ def todolist(): for d in dirs: for f in listfiles(app, d): matches = [] - filename= apath(os.path.join(app, d, f), r=request) + filename = apath(os.path.join(app, d, f), r=request) with open(filename, 'r') as f_s: src = f_s.read() for m in regex.finditer(src): start = m.start() lineno = src.count('\n', 0, start) + 1 - matches.append({'text':m.group(0), 'lineno':lineno}) + matches.append({'text': m.group(0), 'lineno': lineno}) if len(matches) != 0: - output.append({'filename':f,'matches':matches, 'dir':d}) + output.append({'filename': f, 'matches': matches, 'dir': d}) + + return {'todo': output, 'app': app} - return {'todo':output, 'app': app} def editor_sessions(): config = Config(os.path.join(request.folder, 'settings.cfg'), @@ -858,13 +870,14 @@ def editor_sessions(): if request.vars.session_name and request.vars.files: session_name = request.vars.session_name files = request.vars.files - preferences.update({session_name:','.join(files)}) + preferences.update({session_name: ','.join(files)}) if config.save(preferences.items()): response.headers["web2py-component-flash"] = T('Session saved correctly') else: response.headers["web2py-component-flash"] = T('Session saved on session only') - return response.render('default/editor_sessions.html', {'editor_sessions':preferences}) + return response.render('default/editor_sessions.html', {'editor_sessions': preferences}) + def resolve(): """ @@ -901,8 +914,8 @@ def resolve(): def getclass(item): """ Determine item class """ - operators = {' ':'normal', '+':'plus', '-':'minus'} - + operators = {' ': 'normal', '+': 'plus', '-': 'minus'} + return operators[item[0]] if request.vars: @@ -921,7 +934,7 @@ def resolve(): diff = TABLE(*[TR(TD(gen_data(i, item)), TD(item[0]), TD(leading(item[2:]), - TT(item[2:].rstrip())), + TT(item[2:].rstrip())), _class=getclass(item)) for (i, item) in enumerate(d) if item[0] != '?']) @@ -968,11 +981,11 @@ def edit_language(): new_row = DIV(LABEL(prefix, k, _style="font-weight:normal;"), CAT(elem, '\n', TAG.BUTTON( - T('delete'), - _onclick='return delkey("%s")' % name, - _class='btn')), _id=name, _class='span6 well well-small') + T('delete'), + _onclick='return delkey("%s")' % name, + _class='btn')), _id=name, _class='span6 well well-small') - rows.append(DIV(new_row,_class="row-fluid")) + rows.append(DIV(new_row, _class="row-fluid")) rows.append(DIV(INPUT(_type='submit', _value=T('update'), _class="btn btn-primary"), _class='controls')) form = FORM(*rows) if form.accepts(request.vars, keepvalues=True): @@ -1128,18 +1141,18 @@ def design(): # Get all static files statics = listdir(apath('%s/static/' % app, r=request), '[^\.#].*', - maxnum = MAXNFILES) + maxnum=MAXNFILES) statics = [x.replace(os.path.sep, '/') for x in statics] statics.sort() # Get all languages - langpath = os.path.join(apath(app, r=request),'languages') + langpath = os.path.join(apath(app, r=request), 'languages') languages = dict([(lang, info) for lang, info in read_possible_languages(langpath).iteritems() if info[2] != 0]) # info[2] is langfile_mtime: - # get only existed files + # get only existed files - #Get crontab + # Get crontab cronfolder = apath('%s/cron' % app, r=request) crontab = apath('%s/cron/crontab' % app, r=request) if not is_gae: @@ -1265,7 +1278,7 @@ def plugin(): # Get all static files statics = listdir(apath('%s/static/' % app, r=request), '[^\.#].*', - maxnum = MAXNFILES) + maxnum=MAXNFILES) statics = [x.replace(os.path.sep, '/') for x in statics] statics.sort() @@ -1273,9 +1286,9 @@ def plugin(): languages = sorted([lang + '.py' for lang, info in T.get_possible_languages_info().iteritems() if info[2] != 0]) # info[2] is langfile_mtime: - # get only existed files + # get only existed files - #Get crontab + # Get crontab crontab = apath('%s/cron/crontab' % app, r=request) if not os.path.exists(crontab): safe_write(crontab, '#crontab') @@ -1298,6 +1311,7 @@ def plugin(): languages=languages, crontab=crontab) + def create_file(): """ Create files handler """ if request.vars and not request.vars.token == session.token: @@ -1309,7 +1323,7 @@ def create_file(): path = abspath(request.vars.location) else: if request.vars.dir: - request.vars.location += request.vars.dir + '/' + request.vars.location += request.vars.dir + '/' app = get_app(name=request.vars.location.split('/')[0]) path = apath(request.vars.location, r=request) filename = re.sub('[^\w./-]+', '_', request.vars.filename) @@ -1419,7 +1433,7 @@ def create_file(): elif (path[-8:] == '/static/') or (path[-9:] == '/private/'): if (request.vars.plugin and - not filename.startswith('plugin_%s/' % request.vars.plugin)): + not filename.startswith('plugin_%s/' % request.vars.plugin)): filename = 'plugin_%s/%s' % (request.vars.plugin, filename) text = '' @@ -1439,17 +1453,17 @@ def create_file(): log_progress(app, 'CREATE', filename) if request.vars.dir: result = T('file "%(filename)s" created', - dict(filename=full_filename[len(path):])) + dict(filename=full_filename[len(path):])) else: session.flash = T('file "%(filename)s" created', - dict(filename=full_filename[len(path):])) + dict(filename=full_filename[len(path):])) vars = {} if request.vars.id: vars['id'] = request.vars.id if request.vars.app: vars['app'] = request.vars.app redirect(URL('edit', - args=[os.path.join(request.vars.location, filename)], vars=vars)) + args=[os.path.join(request.vars.location, filename)], vars=vars)) except Exception, e: if not isinstance(e, HTTP): @@ -1460,7 +1474,7 @@ def create_file(): response.headers['web2py-component-content'] = 'append' response.headers['web2py-component-command'] = "%s %s %s" % ( "$.web2py.invalidate('#files_menu');", - "load_file('%s');" % URL('edit', args=[app,request.vars.dir,filename]), + "load_file('%s');" % URL('edit', args=[app, request.vars.dir, filename]), "$.web2py.enableElement($('#form form').find($.web2py.formInputClickSelector));") return '' else: @@ -1468,32 +1482,35 @@ def create_file(): def listfiles(app, dir, regexp='.*\.py$'): - files = sorted( - listdir(apath('%(app)s/%(dir)s/' % {'app':app, 'dir':dir}, r=request), regexp)) - files = [x.replace('\\', '/') for x in files if not x.endswith('.bak')] - return files + files = sorted( + listdir(apath('%(app)s/%(dir)s/' % {'app': app, 'dir': dir}, r=request), regexp)) + files = [x.replace('\\', '/') for x in files if not x.endswith('.bak')] + return files + + +def editfile(path, file, vars={}, app=None): + args = (path, file) if 'app' in vars else (app, path, file) + url = URL('edit', args=args, vars=vars) + return A(file, _class='editor_filelink', _href=url, _style='word-wrap: nowrap;') -def editfile(path,file,vars={}, app = None): - args=(path,file) if 'app' in vars else (app,path,file) - url = URL('edit', args=args, vars=vars) - return A(file, _class='editor_filelink', _href=url, _style='word-wrap: nowrap;') def files_menu(): - app = request.vars.app or 'welcome' - dirs=[{'name':'models', 'reg':'.*\.py$'}, - {'name':'controllers', 'reg':'.*\.py$'}, - {'name':'views', 'reg':'[\w/\-]+(\.\w+)+$'}, - {'name':'modules', 'reg':'.*\.py$'}, - {'name':'static', 'reg': '[^\.#].*'}, - {'name':'private', 'reg':'.*\.py$'}] - result_files = [] - for dir in dirs: - result_files.append(TAG[''](LI(dir['name'], _class="nav-header component", _onclick="collapse('" + dir['name'] + "_files');"), - LI(UL(*[LI(editfile(dir['name'], f, dict(id=dir['name'] + f.replace('.','__')), app), _style="overflow:hidden", _id=dir['name']+"__"+f.replace('.','__')) - for f in listfiles(app, dir['name'], regexp=dir['reg'])], - _class="nav nav-list small-font"), - _id=dir['name'] + '_files', _style="display: none;"))) - return dict(result_files = result_files) + app = request.vars.app or 'welcome' + dirs = [{'name': 'models', 'reg': '.*\.py$'}, + {'name': 'controllers', 'reg': '.*\.py$'}, + {'name': 'views', 'reg': '[\w/\-]+(\.\w+)+$'}, + {'name': 'modules', 'reg': '.*\.py$'}, + {'name': 'static', 'reg': '[^\.#].*'}, + {'name': 'private', 'reg': '.*\.py$'}] + result_files = [] + for dir in dirs: + result_files.append(TAG[''](LI(dir['name'], _class="nav-header component", _onclick="collapse('" + dir['name'] + "_files');"), + LI(UL(*[LI(editfile(dir['name'], f, dict(id=dir['name'] + f.replace('.', '__')), app), _style="overflow:hidden", _id=dir['name'] + "__" + f.replace('.', '__')) + for f in listfiles(app, dir['name'], regexp=dir['reg'])], + _class="nav nav-list small-font"), + _id=dir['name'] + '_files', _style="display: none;"))) + return dict(result_files=result_files) + def upload_file(): """ File uploading handler """ @@ -1556,7 +1573,7 @@ def errors(): app = get_app() if is_gae: method = 'dbold' if ('old' in - (request.args(1) or '')) else 'dbnew' + (request.args(1) or '')) else 'dbnew' else: method = request.args(1) or 'new' db_ready = {} @@ -1599,7 +1616,7 @@ def errors(): hash2error[hash]['count'] += 1 except KeyError: error_lines = error['traceback'].split("\n") - last_line = error_lines[-2] if len(error_lines)>1 else 'unknown' + last_line = error_lines[-2] if len(error_lines) > 1 else 'unknown' error_causer = os.path.split(error['layer'])[1] hash2error[hash] = dict(count=1, pickel=error, causer=error_causer, @@ -1638,9 +1655,9 @@ def errors(): last_line = error_lines[-2] error_causer = os.path.split(error['layer'])[1] hash2error[hash] = dict(count=1, - pickel=error, causer=error_causer, - last_line=last_line, hash=hash, - ticket=fn.ticket_id) + pickel=error, causer=error_causer, + last_line=last_line, hash=hash, + ticket=fn.ticket_id) except AttributeError, e: tk_db(tk_table.id == fn.id).delete() tk_db.commit() @@ -1657,11 +1674,11 @@ def errors(): tk_db(tk_table.ticket_id == item[7:]).delete() tk_db.commit() tickets_ = tk_db(tk_table.id > 0).select(tk_table.ticket_id, - tk_table.created_datetime, - orderby=~tk_table.created_datetime) + tk_table.created_datetime, + orderby=~tk_table.created_datetime) tickets = [row.ticket_id for row in tickets_] times = dict([(row.ticket_id, row.created_datetime) for - row in tickets_]) + row in tickets_]) return dict(app=app, tickets=tickets, method=method, times=times, db_ready=db_ready) @@ -1721,7 +1738,7 @@ def make_link(path): if ext.lower() == editable[key] and check_extension: return A('"' + tryFile + '"', _href=URL(r=request, - f='edit/%s/%s/%s' % (app, key, filename))).xml() + f='edit/%s/%s/%s' % (app, key, filename))).xml() return '' @@ -1867,7 +1884,7 @@ def bulk_register(): redirect(URL('site')) return locals() -### Begin experimental stuff need fixes: +# Begin experimental stuff need fixes: # 1) should run in its own process - cannot os.chdir # 2) should not prompt user at console # 3) should give option to force commit and not reuqire manual merge @@ -1934,6 +1951,7 @@ def git_push(): redirect(URL('site')) return dict(app=app, form=form) + def plugins(): app = request.args(0) from serializers import loads_json @@ -1948,6 +1966,7 @@ def plugins(): session.plugins = [] return dict(plugins=session.plugins["results"], app=request.args(0)) + def install_plugin(): app = request.args(0) source = request.vars.source @@ -1972,5 +1991,5 @@ def install_plugin(): else: session.flash = \ T('unable to install plugin "%s"', filename) - redirect(URL(f="plugins", args=[app,])) + redirect(URL(f="plugins", args=[app, ])) return dict(form=form, app=app, plugin=plugin, source=source) diff --git a/gluon/tests/test_validators.py b/gluon/tests/test_validators.py index 96123ea8..57f63762 100644 --- a/gluon/tests/test_validators.py +++ b/gluon/tests/test_validators.py @@ -178,9 +178,9 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, (('max', 'john'), None)) rtn = IS_IN_SET(['max', 'john'], multiple=True)(('bill', 'john')) self.assertEqual(rtn, (('bill', 'john'), 'Value not allowed')) - rtn = IS_IN_SET(('id1','id2'), ['first label','second label'])('id1') # Traditional way + rtn = IS_IN_SET(('id1', 'id2'), ['first label', 'second label'])('id1') # Traditional way self.assertEqual(rtn, ('id1', None)) - rtn = IS_IN_SET({'id1':'first label', 'id2':'second label'})('id1') + rtn = IS_IN_SET({'id1': 'first label', 'id2': 'second label'})('id1') self.assertEqual(rtn, ('id1', None)) rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=True)(None) self.assertEqual(rtn, ([], None)) @@ -188,14 +188,14 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, ([], None)) rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=True)('id1') self.assertEqual(rtn, (['id1'], None)) - rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=(1,2))(None) + rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=(1, 2))(None) self.assertEqual(rtn, ([], 'oops')) import itertools - rtn = IS_IN_SET(itertools.chain(['1','3','5'],['2','4','6']))('1') + rtn = IS_IN_SET(itertools.chain(['1', '3', '5'], ['2', '4', '6']))('1') self.assertEqual(rtn, ('1', None)) - rtn = IS_IN_SET([('id1','first label'), ('id2','second label')])('id1') # Redundant way + rtn = IS_IN_SET([('id1', 'first label'), ('id2', 'second label')])('id1') # Redundant way self.assertEqual(rtn, ('id1', None)) - rtn = IS_IN_SET([('id1','first label'), ('id2','second label')]).options(zero=False) + rtn = IS_IN_SET([('id1', 'first label'), ('id2', 'second label')]).options(zero=False) self.assertEqual(rtn, [('id1', 'first label'), ('id2', 'second label')]) rtn = IS_IN_SET(['id1', 'id2']).options(zero=False) self.assertEqual(rtn, [('id1', 'id1'), ('id2', 'id2')]) @@ -210,47 +210,79 @@ class TestValidators(unittest.TestCase): costanza_id = db.person.insert(name='costanza') rtn = IS_IN_DB(db, 'person.id', '%(name)s')(george_id) self.assertEqual(rtn, (george_id, None)) + rtn = IS_IN_DB(db, 'person.name', '%(name)s')('george') + self.assertEqual(rtn, ('george', None)) rtn = IS_IN_DB(db, db.person, '%(name)s')(george_id) self.assertEqual(rtn, (george_id, None)) rtn = IS_IN_DB(db(db.person.id > 0), db.person, '%(name)s')(george_id) self.assertEqual(rtn, (george_id, None)) - rtn = IS_IN_DB(db, 'person.id', '%(name)s', error_message='oops')(george_id+costanza_id) - self.assertEqual(rtn, (george_id+costanza_id, 'oops')) + rtn = IS_IN_DB(db, 'person.id', '%(name)s', error_message='oops')(george_id + costanza_id) + self.assertEqual(rtn, (george_id + costanza_id, 'oops')) rtn = IS_IN_DB(db, db.person.id, '%(name)s')(george_id) self.assertEqual(rtn, (george_id, None)) - rtn = IS_IN_DB(db, db.person.id, '%(name)s', error_message='oops')(george_id+costanza_id) - self.assertEqual(rtn, (george_id+costanza_id, 'oops')) - rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=True)([george_id,costanza_id]) - self.assertEqual(rtn, ([george_id,costanza_id], None)) + rtn = IS_IN_DB(db, db.person.id, '%(name)s', error_message='oops')(george_id + costanza_id) + self.assertEqual(rtn, (george_id + costanza_id, 'oops')) + rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=True)([george_id, costanza_id]) + self.assertEqual(rtn, ([george_id, costanza_id], None)) + rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=True, error_message='oops')("I'm not even an id") + self.assertEqual(rtn, (["I'm not even an id"], 'oops')) rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=True, delimiter=',')('%d,%d' % (george_id, costanza_id)) - self.assertEqual(rtn, ( ('%d,%d' % (george_id, costanza_id)).split(','), None)) - rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=(1,3), delimiter=',')('%d,%d' % (george_id, costanza_id)) - self.assertEqual(rtn, ( ('%d,%d' % (george_id, costanza_id)).split(','), None)) - rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=(1,2), delimiter=',', error_message='oops')('%d,%d' % (george_id, costanza_id)) - self.assertEqual(rtn, ( ('%d,%d' % (george_id, costanza_id)).split(','), 'oops')) + self.assertEqual(rtn, (('%d,%d' % (george_id, costanza_id)).split(','), None)) + rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=(1, 3), delimiter=',')('%d,%d' % (george_id, costanza_id)) + self.assertEqual(rtn, (('%d,%d' % (george_id, costanza_id)).split(','), None)) + rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=(1, 2), delimiter=',', error_message='oops')('%d,%d' % (george_id, costanza_id)) + self.assertEqual(rtn, (('%d,%d' % (george_id, costanza_id)).split(','), 'oops')) rtn = IS_IN_DB(db, db.person.id, '%(name)s', error_message='oops').options(zero=False) self.assertEqual(sorted(rtn), [('%d' % george_id, 'george'), ('%d' % costanza_id, 'costanza')]) rtn = IS_IN_DB(db, db.person.id, db.person.name, error_message='oops', sort=True).options(zero=True) self.assertEqual(rtn, [('', ''), ('%d' % costanza_id, 'costanza'), ('%d' % george_id, 'george')]) + # Test using the set it made for options + vldtr = IS_IN_DB(db, 'person.name', '%(name)s', error_message='oops') + vldtr.options() + rtn = vldtr('george') + self.assertEqual(rtn, ('george', None)) + rtn = vldtr('jerry') + self.assertEqual(rtn, ('jerry', 'oops')) + vldtr = IS_IN_DB(db, 'person.name', '%(name)s', error_message='oops', multiple=True) + vldtr.options() + rtn = vldtr(['george', 'costanza']) + self.assertEqual(rtn, (['george', 'costanza'], None)) # Test it works with self reference db.define_table('category', - Field('parent_id', 'reference category', requires=IS_EMPTY_OR(IS_IN_DB(db, 'category.id', '%(name)s'))), - Field('name') - ) + Field('parent_id', 'reference category', requires=IS_EMPTY_OR(IS_IN_DB(db, 'category.id', '%(name)s'))), + Field('name') + ) ret = db.category.validate_and_insert(name='seinfeld') self.assertFalse(list(ret.errors)) ret = db.category.validate_and_insert(name='characters', parent_id=ret.id) self.assertFalse(list(ret.errors)) rtn = IS_IN_DB(db, 'category.id', '%(name)s')(ret.id) self.assertEqual(rtn, (ret.id, None)) + # Test _and + vldtr = IS_IN_DB(db, 'person.name', '%(name)s', error_message='oops', _and=IS_LENGTH(maxsize=7, error_message='bad')) + rtn = vldtr('george') + self.assertEqual(rtn, ('george', None)) + rtn = vldtr('costanza') + self.assertEqual(rtn, ('costanza', 'bad')) + rtn = vldtr('jerry') + self.assertEqual(rtn, ('jerry', 'oops')) + vldtr.options() # test theset with _and + rtn = vldtr('jerry') + self.assertEqual(rtn, ('jerry', 'oops')) + # Test auto_add + rtn = IS_IN_DB(db, 'person.id', '%(name)s', error_message='oops')('jerry') + self.assertEqual(rtn, ('jerry', 'oops')) + rtn = IS_IN_DB(db, 'person.id', '%(name)s', auto_add=True)('jerry') + self.assertEqual(rtn, (3, None)) db.person.drop() db.category.drop() def test_IS_NOT_IN_DB(self): from gluon.dal import DAL, Field db = DAL('sqlite:memory') - db.define_table('person', Field('name')) + db.define_table('person', Field('name'), Field('nickname')) db.person.insert(name='george') + db.person.insert(name='costanza', nickname='T Bone') rtn = IS_NOT_IN_DB(db, 'person.name', error_message='oops')('george') self.assertEqual(rtn, ('george', 'oops')) rtn = IS_NOT_IN_DB(db, 'person.name', error_message='oops', allowed_override=['george'])('george') @@ -261,6 +293,17 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, ('jerry', None)) rtn = IS_NOT_IN_DB(db, 'person.name')(u'jerry') self.assertEqual(rtn, ('jerry', None)) + rtn = IS_NOT_IN_DB(db(db.person.id > 0), 'person.name')(u'jerry') + self.assertEqual(rtn, ('jerry', None)) + rtn = IS_NOT_IN_DB(db, db.person, error_message='oops')(1) + self.assertEqual(rtn, ('1', 'oops')) + vldtr = IS_NOT_IN_DB(db, 'person.name', error_message='oops') + vldtr.set_self_id({'name': 'costanza', 'nickname': 'T Bone'}) + rtn = vldtr('george') + self.assertEqual(rtn, ('george', 'oops')) + rtn = vldtr('costanza') + self.assertEqual(rtn, ('costanza', None)) + db.person.drop() def test_IS_INT_IN_RANGE(self): @@ -413,7 +456,7 @@ class TestValidators(unittest.TestCase): def test_IS_ALPHANUMERIC(self): rtn = IS_ALPHANUMERIC()('1') self.assertEqual(rtn, ('1', None)) - rtn = IS_ALPHANUMERIC()('') + rtn = IS_ALPHANUMERIC()('') self.assertEqual(rtn, ('', None)) rtn = IS_ALPHANUMERIC()('A_a') self.assertEqual(rtn, ('A_a', None)) @@ -421,62 +464,62 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, ('!', 'Enter only letters, numbers, and underscore')) def test_IS_EMAIL(self): - rtn = IS_EMAIL()('a@b.com') + rtn = IS_EMAIL()('a@b.com') self.assertEqual(rtn, ('a@b.com', None)) - rtn = IS_EMAIL()('abc@def.com') + rtn = IS_EMAIL()('abc@def.com') self.assertEqual(rtn, ('abc@def.com', None)) - rtn = IS_EMAIL()('abc@3def.com') + rtn = IS_EMAIL()('abc@3def.com') self.assertEqual(rtn, ('abc@3def.com', None)) - rtn = IS_EMAIL()('abc@def.us') + rtn = IS_EMAIL()('abc@def.us') self.assertEqual(rtn, ('abc@def.us', None)) - rtn = IS_EMAIL()('abc@d_-f.us') + rtn = IS_EMAIL()('abc@d_-f.us') self.assertEqual(rtn, ('abc@d_-f.us', None)) - rtn = IS_EMAIL()('@def.com') # missing name + rtn = IS_EMAIL()('@def.com') # missing name self.assertEqual(rtn, ('@def.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('"abc@def".com') # quoted name + rtn = IS_EMAIL()('"abc@def".com') # quoted name self.assertEqual(rtn, ('"abc@def".com', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc+def.com') # no @ + rtn = IS_EMAIL()('abc+def.com') # no @ self.assertEqual(rtn, ('abc+def.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc@def.x') # one-char TLD + rtn = IS_EMAIL()('abc@def.x') # one-char TLD self.assertEqual(rtn, ('abc@def.x', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc@def.12') # numeric TLD + rtn = IS_EMAIL()('abc@def.12') # numeric TLD self.assertEqual(rtn, ('abc@def.12', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc@def..com') # double-dot in domain + rtn = IS_EMAIL()('abc@def..com') # double-dot in domain self.assertEqual(rtn, ('abc@def..com', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc@.def.com') # dot starts domain + rtn = IS_EMAIL()('abc@.def.com') # dot starts domain self.assertEqual(rtn, ('abc@.def.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc@def.c_m') # underscore in TLD + rtn = IS_EMAIL()('abc@def.c_m') # underscore in TLD self.assertEqual(rtn, ('abc@def.c_m', 'Enter a valid email address')) - rtn = IS_EMAIL()('NotAnEmail') # missing @ + rtn = IS_EMAIL()('NotAnEmail') # missing @ self.assertEqual(rtn, ('NotAnEmail', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc@NotAnEmail') # missing TLD + rtn = IS_EMAIL()('abc@NotAnEmail') # missing TLD self.assertEqual(rtn, ('abc@NotAnEmail', 'Enter a valid email address')) - rtn = IS_EMAIL()('customer/department@example.com') + rtn = IS_EMAIL()('customer/department@example.com') self.assertEqual(rtn, ('customer/department@example.com', None)) - rtn = IS_EMAIL()('$A12345@example.com') + rtn = IS_EMAIL()('$A12345@example.com') self.assertEqual(rtn, ('$A12345@example.com', None)) - rtn = IS_EMAIL()('!def!xyz%abc@example.com') + rtn = IS_EMAIL()('!def!xyz%abc@example.com') self.assertEqual(rtn, ('!def!xyz%abc@example.com', None)) - rtn = IS_EMAIL()('_Yosemite.Sam@example.com') + rtn = IS_EMAIL()('_Yosemite.Sam@example.com') self.assertEqual(rtn, ('_Yosemite.Sam@example.com', None)) - rtn = IS_EMAIL()('~@example.com') + rtn = IS_EMAIL()('~@example.com') self.assertEqual(rtn, ('~@example.com', None)) - rtn = IS_EMAIL()('.wooly@example.com') # dot starts name + rtn = IS_EMAIL()('.wooly@example.com') # dot starts name self.assertEqual(rtn, ('.wooly@example.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('wo..oly@example.com') # adjacent dots in name + rtn = IS_EMAIL()('wo..oly@example.com') # adjacent dots in name self.assertEqual(rtn, ('wo..oly@example.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('pootietang.@example.com') # dot ends name + rtn = IS_EMAIL()('pootietang.@example.com') # dot ends name self.assertEqual(rtn, ('pootietang.@example.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('.@example.com') # name is bare dot + rtn = IS_EMAIL()('.@example.com') # name is bare dot self.assertEqual(rtn, ('.@example.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('Ima.Fool@example.com') + rtn = IS_EMAIL()('Ima.Fool@example.com') self.assertEqual(rtn, ('Ima.Fool@example.com', None)) - rtn = IS_EMAIL()('Ima Fool@example.com') # space in name + rtn = IS_EMAIL()('Ima Fool@example.com') # space in name self.assertEqual(rtn, ('Ima Fool@example.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('localguy@localhost') # localhost as domain + rtn = IS_EMAIL()('localguy@localhost') # localhost as domain self.assertEqual(rtn, ('localguy@localhost', None)) # test for banned - rtn = IS_EMAIL(banned='^.*\.com(|\..*)$')('localguy@localhost') # localhost as domain + rtn = IS_EMAIL(banned='^.*\.com(|\..*)$')('localguy@localhost') # localhost as domain self.assertEqual(rtn, ('localguy@localhost', None)) rtn = IS_EMAIL(banned='^.*\.com(|\..*)$')('abc@example.com') self.assertEqual(rtn, ('abc@example.com', 'Enter a valid email address')) @@ -485,6 +528,9 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, ('localguy@localhost', 'Enter a valid email address')) rtn = IS_EMAIL(forced='^.*\.edu(|\..*)$')('localguy@example.edu') self.assertEqual(rtn, ('localguy@example.edu', None)) + # test for not a string at all + rtn = IS_EMAIL(error_message='oops')(42) + self.assertEqual(rtn, (42, 'oops')) def test_IS_LIST_OF_EMAILS(self): emails = ['localguy@localhost', '_Yosemite.Sam@example.com'] @@ -543,16 +589,16 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, ('', 'Enter time as hh:mm:ss (seconds, am, pm optional)')) def test_IS_DATE(self): - v = IS_DATE(format="%m/%d/%Y",error_message="oops") + v = IS_DATE(format="%m/%d/%Y", error_message="oops") rtn = v('03/03/2008') self.assertEqual(rtn, (datetime.date(2008, 3, 3), None)) rtn = v('31/03/2008') self.assertEqual(rtn, ('31/03/2008', 'oops')) - rtn = IS_DATE(format="%m/%d/%Y",error_message="oops").formatter(datetime.date(1834, 12, 14)) + rtn = IS_DATE(format="%m/%d/%Y", error_message="oops").formatter(datetime.date(1834, 12, 14)) self.assertEqual(rtn, '12/14/1834') def test_IS_DATETIME(self): - v = IS_DATETIME(format="%m/%d/%Y %H:%M",error_message="oops") + v = IS_DATETIME(format="%m/%d/%Y %H:%M", error_message="oops") rtn = v('03/03/2008 12:40') self.assertEqual(rtn, (datetime.datetime(2008, 3, 3, 12, 40), None)) rtn = v('31/03/2008 29:40') @@ -560,16 +606,20 @@ class TestValidators(unittest.TestCase): # Test timezone is removed and value is properly converted # # https://github.com/web2py/web2py/issues/1094 + class DummyTimezone(datetime.tzinfo): ONE = datetime.timedelta(hours=1) def utcoffset(self, dt): return DummyTimezone.ONE + def tzname(self, dt): return "UTC+1" + def dst(self, dt): return DummyTimezone.ONE + def localize(self, dt, is_dst=False): return dt.replace(tzinfo=self) v = IS_DATETIME(format="%Y-%m-%d %H:%M", error_message="oops", timezone=DummyTimezone()) @@ -577,65 +627,65 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, (datetime.datetime(1982, 12, 14, 7, 0), None)) def test_IS_DATE_IN_RANGE(self): - 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 = IS_DATE_IN_RANGE(minimum=datetime.date(2008, 1, 1), + maximum=datetime.date(2009, 12, 31), + format="%m/%d/%Y", error_message="oops") rtn = v('03/03/2008') self.assertEqual(rtn, (datetime.date(2008, 3, 3), None)) rtn = v('03/03/2010') self.assertEqual(rtn, ('03/03/2010', 'oops')) - rtn = v(datetime.date(2008,3,3)) + rtn = v(datetime.date(2008, 3, 3)) self.assertEqual(rtn, (datetime.date(2008, 3, 3), None)) - rtn = v(datetime.date(2010,3,3)) + rtn = v(datetime.date(2010, 3, 3)) self.assertEqual(rtn, (datetime.date(2010, 3, 3), 'oops')) - v = IS_DATE_IN_RANGE(maximum=datetime.date(2009,12,31), - format="%m/%d/%Y") + v = IS_DATE_IN_RANGE(maximum=datetime.date(2009, 12, 31), + format="%m/%d/%Y") rtn = v('03/03/2010') self.assertEqual(rtn, ('03/03/2010', 'Enter date on or before 12/31/2009')) - v = IS_DATE_IN_RANGE(minimum=datetime.date(2008,1,1), - format="%m/%d/%Y") + v = IS_DATE_IN_RANGE(minimum=datetime.date(2008, 1, 1), + format="%m/%d/%Y") rtn = v('03/03/2007') self.assertEqual(rtn, ('03/03/2007', 'Enter date on or after 01/01/2008')) - v = IS_DATE_IN_RANGE(minimum=datetime.date(2008,1,1), - maximum=datetime.date(2009,12,31), - format="%m/%d/%Y") + v = IS_DATE_IN_RANGE(minimum=datetime.date(2008, 1, 1), + maximum=datetime.date(2009, 12, 31), + format="%m/%d/%Y") rtn = v('03/03/2007') self.assertEqual(rtn, ('03/03/2007', 'Enter date in range 01/01/2008 12/31/2009')) def test_IS_DATETIME_IN_RANGE(self): 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") + 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") rtn = v('03/03/2008 12:40') self.assertEqual(rtn, (datetime.datetime(2008, 3, 3, 12, 40), None)) rtn = v('03/03/2010 10:34') self.assertEqual(rtn, ('03/03/2010 10:34', 'oops')) - rtn = v(datetime.datetime(2008,3,3,0,0)) + rtn = v(datetime.datetime(2008, 3, 3, 0, 0)) self.assertEqual(rtn, (datetime.datetime(2008, 3, 3, 0, 0), None)) - rtn = v(datetime.datetime(2010,3,3,0,0)) + rtn = v(datetime.datetime(2010, 3, 3, 0, 0)) self.assertEqual(rtn, (datetime.datetime(2010, 3, 3, 0, 0), 'oops')) - v = IS_DATETIME_IN_RANGE(maximum=datetime.datetime(2009,12,31,12,20), - format='%m/%d/%Y %H:%M:%S') + v = IS_DATETIME_IN_RANGE(maximum=datetime.datetime(2009, 12, 31, 12, 20), + format='%m/%d/%Y %H:%M:%S') rtn = v('03/03/2010 12:20:00') self.assertEqual(rtn, ('03/03/2010 12:20:00', 'Enter date and time on or before 12/31/2009 12:20:00')) - v = IS_DATETIME_IN_RANGE(minimum=datetime.datetime(2008,1,1,12,20), - format='%m/%d/%Y %H:%M:%S') + v = IS_DATETIME_IN_RANGE(minimum=datetime.datetime(2008, 1, 1, 12, 20), + format='%m/%d/%Y %H:%M:%S') rtn = v('03/03/2007 12:20:00') self.assertEqual(rtn, ('03/03/2007 12:20:00', 'Enter date and time on or after 01/01/2008 12:20:00')) - 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:%S') + 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:%S') rtn = v('03/03/2007 12:20:00') self.assertEqual(rtn, ('03/03/2007 12:20:00', 'Enter date and time in range 01/01/2008 12:20:00 12/31/2009 12:20:00')) - v = IS_DATETIME_IN_RANGE(maximum=datetime.datetime(2009,12,31,12,20), - format='%Y-%m-%d %H:%M:%S', error_message='oops') + v = IS_DATETIME_IN_RANGE(maximum=datetime.datetime(2009, 12, 31, 12, 20), + format='%Y-%m-%d %H:%M:%S', error_message='oops') rtn = v('clearly not a date') self.assertEqual(rtn, ('clearly not a date', 'oops')) def test_IS_LIST_OF(self): - values = [0,1,2,3,4] + values = [0, 1, 2, 3, 4] rtn = IS_LIST_OF(IS_INT_IN_RANGE(0, 10))(values) self.assertEqual(rtn, (values, None)) values.append(11) @@ -643,9 +693,9 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, (values, 'Enter an integer between 0 and 9')) rtn = IS_LIST_OF(IS_INT_IN_RANGE(0, 10))(1) self.assertEqual(rtn, ([1], None)) - rtn = IS_LIST_OF(IS_INT_IN_RANGE(0, 10), minimum=10)([1,2]) + rtn = IS_LIST_OF(IS_INT_IN_RANGE(0, 10), minimum=10)([1, 2]) self.assertEqual(rtn, ([1, 2], 'Enter between 10 and 100 values')) - rtn = IS_LIST_OF(IS_INT_IN_RANGE(0, 10), maximum=2)([1,2,3]) + rtn = IS_LIST_OF(IS_INT_IN_RANGE(0, 10), maximum=2)([1, 2, 3]) self.assertEqual(rtn, ([1, 2, 3], 'Enter between 0 and 2 values')) # regression test for issue 742 rtn = IS_LIST_OF(minimum=1)('') @@ -704,69 +754,69 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, ('a bc', 'Must be slug')) def test_ANY_OF(self): - rtn = ANY_OF([IS_EMAIL(),IS_ALPHANUMERIC()])('a@b.co') + rtn = ANY_OF([IS_EMAIL(), IS_ALPHANUMERIC()])('a@b.co') self.assertEqual(rtn, ('a@b.co', None)) - rtn = ANY_OF([IS_EMAIL(),IS_ALPHANUMERIC()])('abco') + rtn = ANY_OF([IS_EMAIL(), IS_ALPHANUMERIC()])('abco') self.assertEqual(rtn, ('abco', None)) - rtn = ANY_OF([IS_EMAIL(),IS_ALPHANUMERIC()])('@ab.co') + rtn = ANY_OF([IS_EMAIL(), IS_ALPHANUMERIC()])('@ab.co') self.assertEqual(rtn, ('@ab.co', 'Enter only letters, numbers, and underscore')) - rtn = ANY_OF([IS_ALPHANUMERIC(),IS_EMAIL()])('@ab.co') + rtn = ANY_OF([IS_ALPHANUMERIC(), IS_EMAIL()])('@ab.co') self.assertEqual(rtn, ('@ab.co', 'Enter a valid email address')) - rtn = ANY_OF([IS_DATE(),IS_EMAIL()])('a@b.co') + rtn = ANY_OF([IS_DATE(), IS_EMAIL()])('a@b.co') self.assertEqual(rtn, ('a@b.co', None)) - rtn = ANY_OF([IS_DATE(),IS_EMAIL()])('1982-12-14') + rtn = ANY_OF([IS_DATE(), IS_EMAIL()])('1982-12-14') self.assertEqual(rtn, (datetime.date(1982, 12, 14), None)) - rtn = ANY_OF([IS_DATE(format='%m/%d/%Y'),IS_EMAIL()]).formatter(datetime.date(1834, 12, 14)) + rtn = ANY_OF([IS_DATE(format='%m/%d/%Y'), IS_EMAIL()]).formatter(datetime.date(1834, 12, 14)) self.assertEqual(rtn, '12/14/1834') def test_IS_EMPTY_OR(self): rtn = IS_EMPTY_OR(IS_EMAIL())('abc@def.com') self.assertEqual(rtn, ('abc@def.com', None)) - rtn = IS_EMPTY_OR(IS_EMAIL())(' ') + rtn = IS_EMPTY_OR(IS_EMAIL())(' ') self.assertEqual(rtn, (None, None)) - rtn = IS_EMPTY_OR(IS_EMAIL(), null='abc')(' ') + rtn = IS_EMPTY_OR(IS_EMAIL(), null='abc')(' ') self.assertEqual(rtn, ('abc', None)) - rtn = IS_EMPTY_OR(IS_EMAIL(), null='abc', empty_regex='def')('def') + rtn = IS_EMPTY_OR(IS_EMAIL(), null='abc', empty_regex='def')('def') self.assertEqual(rtn, ('abc', None)) - rtn = IS_EMPTY_OR(IS_EMAIL())('abc') + rtn = IS_EMPTY_OR(IS_EMAIL())('abc') self.assertEqual(rtn, ('abc', 'Enter a valid email address')) - rtn = IS_EMPTY_OR(IS_EMAIL())(' abc ') + rtn = IS_EMPTY_OR(IS_EMAIL())(' abc ') self.assertEqual(rtn, ('abc', 'Enter a valid email address')) - rtn = IS_EMPTY_OR(IS_IN_SET([('id1','first label'), ('id2','second label')], zero='zero')).options(zero=False) - self.assertEqual(rtn, [('', ''),('id1', 'first label'), ('id2', 'second label')]) - rtn = IS_EMPTY_OR(IS_IN_SET([('id1','first label'), ('id2','second label')], zero='zero')).options() - self.assertEqual(rtn, [('', 'zero'),('id1', 'first label'), ('id2', 'second label')]) + rtn = IS_EMPTY_OR(IS_IN_SET([('id1', 'first label'), ('id2', 'second label')], zero='zero')).options(zero=False) + self.assertEqual(rtn, [('', ''), ('id1', 'first label'), ('id2', 'second label')]) + rtn = IS_EMPTY_OR(IS_IN_SET([('id1', 'first label'), ('id2', 'second label')], zero='zero')).options() + self.assertEqual(rtn, [('', 'zero'), ('id1', 'first label'), ('id2', 'second label')]) def test_CLEANUP(self): rtn = CLEANUP()('helloò') self.assertEqual(rtn, ('hello', None)) def test_CRYPT(self): - rtn = str(CRYPT(digest_alg='md5',salt=True)('test')[0]) + rtn = str(CRYPT(digest_alg='md5', salt=True)('test')[0]) self.assertRegexpMatches(rtn, r'^md5\$.{16}\$.{32}$') - rtn = str(CRYPT(digest_alg='sha1',salt=True)('test')[0]) + rtn = str(CRYPT(digest_alg='sha1', salt=True)('test')[0]) self.assertRegexpMatches(rtn, r'^sha1\$.{16}\$.{40}$') - rtn = str(CRYPT(digest_alg='sha256',salt=True)('test')[0]) + rtn = str(CRYPT(digest_alg='sha256', salt=True)('test')[0]) self.assertRegexpMatches(rtn, r'^sha256\$.{16}\$.{64}$') - rtn = str(CRYPT(digest_alg='sha384',salt=True)('test')[0]) + rtn = str(CRYPT(digest_alg='sha384', salt=True)('test')[0]) self.assertRegexpMatches(rtn, r'^sha384\$.{16}\$.{96}$') - rtn = str(CRYPT(digest_alg='sha512',salt=True)('test')[0]) + rtn = str(CRYPT(digest_alg='sha512', salt=True)('test')[0]) self.assertRegexpMatches(rtn, r'^sha512\$.{16}\$.{128}$') alg = 'pbkdf2(1000,20,sha512)' - rtn = str(CRYPT(digest_alg=alg,salt=True)('test')[0]) + rtn = str(CRYPT(digest_alg=alg, salt=True)('test')[0]) self.assertRegexpMatches(rtn, r'^pbkdf2\(1000,20,sha512\)\$.{16}\$.{40}$') - rtn = str(CRYPT(digest_alg='md5',key='mykey',salt=True)('test')[0]) + rtn = str(CRYPT(digest_alg='md5', key='mykey', salt=True)('test')[0]) self.assertRegexpMatches(rtn, r'^md5\$.{16}\$.{32}$') - a = str(CRYPT(digest_alg='sha1',salt=False)('test')[0]) - self.assertEqual(CRYPT(digest_alg='sha1',salt=False)('test')[0], a) - self.assertEqual(CRYPT(digest_alg='sha1',salt=False)('test')[0], a[6:]) - self.assertEqual(CRYPT(digest_alg='md5',salt=False)('test')[0], a) - self.assertEqual(CRYPT(digest_alg='md5',salt=False)('test')[0], a[6:]) + a = str(CRYPT(digest_alg='sha1', salt=False)('test')[0]) + self.assertEqual(CRYPT(digest_alg='sha1', salt=False)('test')[0], a) + self.assertEqual(CRYPT(digest_alg='sha1', salt=False)('test')[0], a[6:]) + self.assertEqual(CRYPT(digest_alg='md5', salt=False)('test')[0], a) + self.assertEqual(CRYPT(digest_alg='md5', salt=False)('test')[0], a[6:]) def test_IS_STRONG(self): rtn = IS_STRONG(es=True)('Abcd1234') self.assertEqual(rtn, ('Abcd1234', - 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|')) + 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|')) rtn = IS_STRONG(es=True)('Abcd1234!') self.assertEqual(rtn, ('Abcd1234!', None)) rtn = IS_STRONG(es=True, entropy=1)('a') @@ -787,33 +837,34 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, ('********', None)) rtn = IS_STRONG(es=True, max=4)('abcde') self.assertEqual(rtn, - ('abcde', - '|'.join(['Minimum length is 8', - 'Maximum length is 4', - 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|', - 'Must include at least 1 upper case', - 'Must include at least 1 number'])) - ) + ('abcde', + '|'.join(['Minimum length is 8', + 'Maximum length is 4', + 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|', + 'Must include at least 1 upper case', + 'Must include at least 1 number'])) + ) rtn = IS_STRONG(es=True)('abcde') self.assertEqual(rtn, - ('abcde', - '|'.join(['Minimum length is 8', - 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|', - 'Must include at least 1 upper case', - 'Must include at least 1 number'])) - ) + ('abcde', + '|'.join(['Minimum length is 8', + 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|', + 'Must include at least 1 upper case', + 'Must include at least 1 number'])) + ) rtn = IS_STRONG(upper=0, lower=0, number=0, es=True)('Abcde1') self.assertEqual(rtn, - ('Abcde1', - '|'.join(['Minimum length is 8', - 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|', - 'May not include any upper case letters', - 'May not include any lower case letters', - 'May not include any numbers'])) - ) + ('Abcde1', + '|'.join(['Minimum length is 8', + 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|', + 'May not include any upper case letters', + 'May not include any lower case letters', + 'May not include any numbers'])) + ) def test_IS_IMAGE(self): class DummyImageFile(object): + def __init__(self, filename, ext, width, height): from StringIO import StringIO import struct @@ -821,7 +872,7 @@ class TestValidators(unittest.TestCase): self.file = StringIO() if ext == 'bmp': self.file.write(b'BM') - self.file.write(b' '*16) + self.file.write(b' ' * 16) self.file.write(struct.pack('