diff --git a/VERSION b/VERSION index ba504ebd..a93bd69e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.1.1 (2012-10-19 10:37:01) dev +Version 2.1.1 (2012-10-19 12:33:48) dev diff --git a/__init__.py b/__init__.py index 8b137891..e69de29b 100644 --- a/__init__.py +++ b/__init__.py @@ -1 +0,0 @@ - diff --git a/anyserver.py b/anyserver.py index 46daf4ec..473cceaa 100644 --- a/anyserver.py +++ b/anyserver.py @@ -16,44 +16,47 @@ import urllib path = os.path.dirname(os.path.abspath(__file__)) os.chdir(path) -sys.path = [path]+[p for p in sys.path if not p==path] +sys.path = [path] + [p for p in sys.path if not p == path] import gluon.main from gluon.fileutils import read_file, write_file + class Servers: @staticmethod def cgi(app, address=None, **options): from wsgiref.handlers import CGIHandler - CGIHandler().run(app) # Just ignore host and port here + CGIHandler().run(app) # Just ignore host and port here @staticmethod - def flup(app,address, **options): + def flup(app, address, **options): import flup.server.fcgi flup.server.fcgi.WSGIServer(app, bindAddress=address).run() @staticmethod - def wsgiref(app,address,**options): # pragma: no cover + def wsgiref(app, address, **options): # pragma: no cover from wsgiref.simple_server import make_server, WSGIRequestHandler + class QuietHandler(WSGIRequestHandler): - def log_request(*args, **kw): pass + def log_request(*args, **kw): + pass options['handler_class'] = QuietHandler - srv = make_server(address[0],address[1],app,**options) + srv = make_server(address[0], address[1], app, **options) srv.serve_forever() @staticmethod - def cherrypy(app,address, **options): + def cherrypy(app, address, **options): from cherrypy import wsgiserver server = wsgiserver.CherryPyWSGIServer(address, app) server.start() @staticmethod - def rocket(app,address, **options): + def rocket(app, address, **options): from gluon.rocket import CherryPyWSGIServer server = CherryPyWSGIServer(address, app) server.start() @staticmethod - def rocket_with_repoze_profiler(app,address, **options): + def rocket_with_repoze_profiler(app, address, **options): from gluon.rocket import CherryPyWSGIServer from repoze.profile.profiler import AccumulatingProfileMiddleware from gluon.settings import global_settings @@ -63,44 +66,46 @@ class Servers: log_filename='wsgi.prof', discard_first_request=True, flush_at_shutdown=True, - path = '/__profile__' - ) + path='/__profile__' + ) server = CherryPyWSGIServer(address, wrapped) server.start() @staticmethod - def paste(app,address,**options): + def paste(app, address, **options): from paste import httpserver from paste.translogger import TransLogger httpserver.serve(app, host=address[0], port=address[1], **options) @staticmethod - def fapws(app,address, **options): + def fapws(app, address, **options): import fapws._evwsgi as evwsgi from fapws import base - evwsgi.start(address[0],str(address[1])) + evwsgi.start(address[0], str(address[1])) evwsgi.set_base_module(base) + def app(environ, start_response): environ['wsgi.multiprocess'] = False return app(environ, start_response) - evwsgi.wsgi_cb(('',app)) + evwsgi.wsgi_cb(('', app)) evwsgi.run() - @staticmethod - def gevent(app,address, **options): - from gevent import monkey; monkey.patch_all() + def gevent(app, address, **options): + from gevent import monkey + monkey.patch_all() from gevent import pywsgi from gevent.pool import Pool - pywsgi.WSGIServer(address, app, spawn = 'workers' in options and Pool(int(options.workers)) or 'default').serve_forever() + pywsgi.WSGIServer(address, app, spawn='workers' in options and Pool( + int(options.workers)) or 'default').serve_forever() @staticmethod - def bjoern(app,address, **options): + def bjoern(app, address, **options): import bjoern bjoern.run(app, *address) @staticmethod - def tornado(app,address, **options): + def tornado(app, address, **options): import tornado.wsgi import tornado.httpserver import tornado.ioloop @@ -110,7 +115,7 @@ class Servers: tornado.ioloop.IOLoop.instance().start() @staticmethod - def twisted(app,address, **options): + def twisted(app, address, **options): from twisted.web import server, wsgi from twisted.python.threadpool import ThreadPool from twisted.internet import reactor @@ -122,42 +127,44 @@ class Servers: reactor.run() @staticmethod - def diesel(app,address, **options): + def diesel(app, address, **options): from diesel.protocols.wsgi import WSGIApplication app = WSGIApplication(app, port=address[1]) app.run() @staticmethod - def gunicorn(app,address, **options): + def gunicorn(app, address, **options): from gunicorn.app.base import Application config = {'bind': "%s:%d" % address} config.update(options) sys.argv = ['anyserver.py'] + class GunicornApplication(Application): def init(self, parser, opts, args): return config + def load(self): return app g = GunicornApplication() g.run() @staticmethod - def eventlet(app,address, **options): + def eventlet(app, address, **options): from eventlet import wsgi, listen wsgi.server(listen(address), app) @staticmethod - def mongrel2(app,address,**options): + def mongrel2(app, address, **options): import uuid sys.path.append(os.path.abspath(os.path.dirname(__file__))) from mongrel2 import handler conn = handler.Connection(str(uuid.uuid4()), "tcp://127.0.0.1:9997", "tcp://127.0.0.1:9996") - mongrel2_handler(app,conn,debug=False) + mongrel2_handler(app, conn, debug=False) -def run(servername,ip,port,softcron=True,logging=False,profiler=None): +def run(servername, ip, port, softcron=True, logging=False, profiler=None): if logging: application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase, logfilename='httpserver.log', @@ -167,9 +174,10 @@ def run(servername,ip,port,softcron=True,logging=False,profiler=None): if softcron: from gluon.settings import global_settings global_settings.web2py_crontype = 'soft' - getattr(Servers,servername)(application,(ip,int(port))) + getattr(Servers, servername)(application, (ip, int(port))) -def mongrel2_handler(application,conn,debug=False): + +def mongrel2_handler(application, conn, debug=False): """ Based on : https://github.com/berry/Mongrel2-WSGI-Handler/blob/master/wsgi-handler.py @@ -194,20 +202,23 @@ def mongrel2_handler(application,conn,debug=False): # and responses. Unless I have missed something. while True: - if debug: print "WAITING FOR REQUEST" + if debug: + print "WAITING FOR REQUEST" # receive a request req = conn.recv() - if debug: print "REQUEST BODY: %r\n" % req.body + if debug: + print "REQUEST BODY: %r\n" % req.body if req.is_disconnect(): - if debug: print "DISCONNECT" - continue #effectively ignore the disconnect from the client + if debug: + print "DISCONNECT" + continue # effectively ignore the disconnect from the client # Set a couple of environment attributes a.k.a. header attributes # that are a must according to PEP 333 environ = req.headers - environ['SERVER_PROTOCOL'] = 'HTTP/1.1' # SimpleHandler expects a server_protocol, lets assume it is HTTP 1.1 + environ['SERVER_PROTOCOL'] = 'HTTP/1.1' # SimpleHandler expects a server_protocol, lets assume it is HTTP 1.1 environ['REQUEST_METHOD'] = environ['METHOD'] if ':' in environ['Host']: environ['SERVER_NAME'] = environ['Host'].split(':')[0] @@ -215,17 +226,19 @@ def mongrel2_handler(application,conn,debug=False): else: environ['SERVER_NAME'] = environ['Host'] environ['SERVER_PORT'] = '' - environ['SCRIPT_NAME'] = '' # empty for now + environ['SCRIPT_NAME'] = '' # empty for now environ['PATH_INFO'] = urllib.unquote(environ['PATH']) if '?' in environ['URI']: environ['QUERY_STRING'] = environ['URI'].split('?')[1] else: environ['QUERY_STRING'] = '' if 'Content-Length' in environ: - environ['CONTENT_LENGTH'] = environ['Content-Length'] # necessary for POST to work with Django + environ['CONTENT_LENGTH'] = environ[ + 'Content-Length'] # necessary for POST to work with Django environ['wsgi.input'] = req.body - if debug: print "ENVIRON: %r\n" % environ + if debug: + print "ENVIRON: %r\n" % environ # SimpleHandler needs file-like stream objects for # requests, errors and responses @@ -234,7 +247,8 @@ def mongrel2_handler(application,conn,debug=False): respIO = StringIO.StringIO() # execute the application - handler = SimpleHandler(reqIO, respIO, errIO, environ, multithread = False, multiprocess = False) + handler = SimpleHandler(reqIO, respIO, errIO, environ, + multithread=False, multiprocess=False) handler.run(application) # Get the response and filter out the response (=data) itself, @@ -258,11 +272,15 @@ def mongrel2_handler(application,conn,debug=False): errors = errIO.getvalue() # return the response - if debug: print "RESPONSE: %r\n" % response + if debug: + print "RESPONSE: %r\n" % response if errors: - if debug: print "ERRORS: %r" % errors + if debug: + print "ERRORS: %r" % errors data = "%s\r\n\r\n%s" % (data, errors) - conn.reply_http(req, data, code = code, status = status, headers = headers) + conn.reply_http( + req, data, code=code, status=status, headers=headers) + def main(): usage = "python anyserver.py -s tornado -i 127.0.0.1 -p 8000 -l -P" @@ -282,7 +300,7 @@ def main(): default=False, dest='profiler', help='profiler filename') - servers = ', '.join(x for x in dir(Servers) if not x[0]=='_') + servers = ', '.join(x for x in dir(Servers) if not x[0] == '_') parser.add_option('-s', '--server', default='rocket', @@ -304,8 +322,10 @@ def main(): dest='workers', help='number of workers number') (options, args) = parser.parse_args() - print 'starting %s on %s:%s...' % (options.server,options.ip,options.port) - run(options.server,options.ip,options.port,logging=options.logging,profiler=options.profiler) + print 'starting %s on %s:%s...' % ( + options.server, options.ip, options.port) + run(options.server, options.ip, options.port, + logging=options.logging, profiler=options.profiler) -if __name__=='__main__': +if __name__ == '__main__': main() diff --git a/cgihandler.py b/cgihandler.py index 3c919199..0929ed15 100755 --- a/cgihandler.py +++ b/cgihandler.py @@ -56,7 +56,7 @@ import wsgiref.handlers path = os.path.dirname(os.path.abspath(__file__)) os.chdir(path) -sys.path = [path]+[p for p in sys.path if not p==path] +sys.path = [path] + [p for p in sys.path if not p == path] import gluon.main diff --git a/fcgihandler.py b/fcgihandler.py index 76cda9b8..a3d4a5df 100755 --- a/fcgihandler.py +++ b/fcgihandler.py @@ -34,7 +34,7 @@ import os path = os.path.dirname(os.path.abspath(__file__)) os.chdir(path) -sys.path = [path]+[p for p in sys.path if not p==path] +sys.path = [path] + [p for p in sys.path if not p == path] import gluon.main import gluon.contrib.gateways.fcgi as fcgi diff --git a/gaehandler.py b/gaehandler.py index 0965fffe..b039d40b 100755 --- a/gaehandler.py +++ b/gaehandler.py @@ -33,7 +33,7 @@ import wsgiref.handlers import datetime path = os.path.dirname(os.path.abspath(__file__)) -sys.path = [path]+[p for p in sys.path if not p==path] +sys.path = [path] + [p for p in sys.path if not p == path] sys.modules['cPickle'] = sys.modules['pickle'] @@ -83,7 +83,7 @@ def wsgiapp(env, res): if global_settings.web2py_runtime == 'gae:development': gluon.admin.create_missing_folders() - web2py_path = global_settings.applications_parent # backward compatibility + web2py_path = global_settings.applications_parent # backward compatibility return gluon.main.wsgibase(env, res) @@ -91,6 +91,7 @@ def wsgiapp(env, res): if LOG_STATS or DEBUG: wsgiapp = log_stats(wsgiapp) + def main(): """Run the wsgi app""" run_wsgi_app(wsgiapp) diff --git a/gluon/__init__.py b/gluon/__init__.py index 7065a70a..3e9801f7 100644 --- a/gluon/__init__.py +++ b/gluon/__init__.py @@ -10,7 +10,7 @@ Web2Py framework modules ======================== """ -__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML','redirect','current','embed64'] +__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML', 'redirect', 'current', 'embed64'] from globals import current from html import * diff --git a/gluon/admin.py b/gluon/admin.py index ba392dbb..7f7888cb 100644 --- a/gluon/admin.py +++ b/gluon/admin.py @@ -23,6 +23,7 @@ from http import HTTP if not global_settings.web2py_runtime_gae: import site + def apath(path='', r=None): """ Builds a path inside an application folder @@ -95,6 +96,7 @@ def app_pack_compiled(app, request, raise_ex=False): raise return None + def app_cleanup(app, request): """ Removes session, cache and error files @@ -113,7 +115,7 @@ def app_cleanup(app, request): if os.path.exists(path): for f in os.listdir(path): try: - if f[:1]!='.': os.unlink(os.path.join(path,f)) + if f[:1] != '.': os.unlink(os.path.join(path, f)) except IOError: r = False @@ -122,7 +124,7 @@ def app_cleanup(app, request): if os.path.exists(path): for f in os.listdir(path): try: - if f[:1]!='.': recursive_unlink(os.path.join(path,f)) + if f[:1] != '.': recursive_unlink(os.path.join(path, f)) except IOError: r = False @@ -131,7 +133,7 @@ def app_cleanup(app, request): if os.path.exists(path): for f in os.listdir(path): try: - if f[:1]!='.': os.unlink(os.path.join(path,f)) + if f[:1] != '.': os.unlink(os.path.join(path, f)) except IOError: r = False return r @@ -158,7 +160,8 @@ def app_compile(app, request): remove_compiled_application(folder) return tb -def app_create(app, request,force=False,key=None,info=False): + +def app_create(app, request, force=False, key=None, info=False): """ Create a copy of welcome.w2p (scaffolding) app @@ -186,17 +189,17 @@ def app_create(app, request,force=False,key=None,info=False): return False try: w2p_unpack('welcome.w2p', path) - for subfolder in ['models','views','controllers', 'databases', - 'modules','cron','errors','sessions', - 'languages','static','private','uploads']: - subpath = os.path.join(path,subfolder) + for subfolder in ['models', 'views', 'controllers', 'databases', + 'modules', 'cron', 'errors', 'sessions', + 'languages', 'static', 'private', 'uploads']: + subpath = os.path.join(path, subfolder) if not os.path.exists(subpath): os.mkdir(subpath) db = os.path.join(path, 'models', 'db.py') if os.path.exists(db): data = read_file(db) data = data.replace('', - 'sha512:'+(key or web2py_uuid())) + 'sha512:' + (key or web2py_uuid())) write_file(db, data) if info: return True, None @@ -283,6 +286,7 @@ def app_uninstall(app, request): except Exception: return False + def plugin_pack(app, plugin_name, request): """ Builds a w2p package for the application @@ -302,12 +306,14 @@ def plugin_pack(app, plugin_name, request): filename of the w2p file or None on error """ try: - filename = apath('../deposit/web2py.plugin.%s.w2p' % plugin_name, request) + filename = apath( + '../deposit/web2py.plugin.%s.w2p' % plugin_name, request) w2p_pack_plugin(filename, apath(app, request), plugin_name) return filename except Exception: return False + def plugin_install(app, fobj, request, filename): """ Installs an application: @@ -345,6 +351,7 @@ def plugin_install(app, fobj, request, filename): os.unlink(upname) return False + def check_new_version(myversion, version_URL): """ Compares current web2py's version with the latest stable web2py version. @@ -375,6 +382,7 @@ def check_new_version(myversion, version_URL): else: return False, version + def unzip(filename, dir, subfolder=''): """ Unzips filename into dir (.zip only, no .gz etc) @@ -382,7 +390,7 @@ def unzip(filename, dir, subfolder=''): """ filename = abspath(filename) if not zipfile.is_zipfile(filename): - raise RuntimeError, 'Not a valid zipfile' + raise RuntimeError('Not a valid zipfile') zf = zipfile.ZipFile(filename) if not subfolder.endswith('/'): subfolder = subfolder + '/' @@ -392,7 +400,7 @@ def unzip(filename, dir, subfolder=''): continue #print name[n:] if name.endswith('/'): - folder = os.path.join(dir,name[n:]) + folder = os.path.join(dir, name[n:]) if not os.path.exists(folder): os.mkdir(folder) else: @@ -421,7 +429,7 @@ def upgrade(request, url='http://web2py.com'): if not gluon_parent.endswith('/'): gluon_parent = gluon_parent + '/' (check, version) = check_new_version(web2py_version, - url+'/examples/default/version') + url + '/examples/default/version') if not check: return (False, 'Already latest version') if os.path.exists(os.path.join(gluon_parent, 'web2py.exe')): @@ -442,35 +450,40 @@ def upgrade(request, url='http://web2py.com'): file = None try: write_file(filename, urllib.urlopen(full_url).read(), 'wb') - except Exception,e: + except Exception, e: return False, e try: unzip(filename, destination, subfolder) return True, None - except Exception,e: + except Exception, e: return False, e + def add_path_first(path): - sys.path = [path]+[p for p in sys.path if (not p==path and not p==(path+'/'))] + sys.path = [path] + [p for p in sys.path if ( + not p == path and not p == (path + '/'))] if not global_settings.web2py_runtime_gae: site.addsitedir(path) + def create_missing_folders(): if not global_settings.web2py_runtime_gae: for path in ('applications', 'deposit', 'site-packages', 'logs'): path = abspath(path, gluon=True) if not os.path.exists(path): os.mkdir(path) - paths = (global_settings.gluon_parent, abspath('site-packages', gluon=True), abspath('gluon', gluon=True), '') + paths = (global_settings.gluon_parent, abspath( + 'site-packages', gluon=True), abspath('gluon', gluon=True), '') [add_path_first(path) for path in paths] + def create_missing_app_folders(request): if not global_settings.web2py_runtime_gae: if request.folder not in global_settings.app_folders: for subfolder in ('models', 'views', 'controllers', 'databases', 'modules', 'cron', 'errors', 'sessions', 'languages', 'static', 'private', 'uploads'): - path = os.path.join(request.folder, subfolder) + path = os.path.join(request.folder, subfolder) if not os.path.exists(path): os.mkdir(path) global_settings.app_folders.add(request.folder) diff --git a/gluon/cache.py b/gluon/cache.py index ec26711f..6c52c418 100644 --- a/gluon/cache.py +++ b/gluon/cache.py @@ -40,6 +40,7 @@ __all__ = ['Cache', 'lazy_cache'] DEFAULT_TIME_EXPIRE = 300 + class CacheAbstract(object): """ Abstract class for cache implementations. @@ -73,7 +74,7 @@ class CacheAbstract(object): raise NotImplementedError def __call__(self, key, f, - time_expire = DEFAULT_TIME_EXPIRE): + time_expire=DEFAULT_TIME_EXPIRE): """ Tries retrieve the value corresponding to `key` from the cache of the object exists and if it did not expire, else it called the function `f` @@ -130,6 +131,7 @@ class CacheAbstract(object): if r.match(str(key)): del storage[key] + class CacheInRam(CacheAbstract): """ Ram based caching @@ -147,8 +149,10 @@ class CacheInRam(CacheAbstract): self.request = request def initialize(self): - if self.initialized: return - else: self.initialized = True + if self.initialized: + return + else: + self.initialized = True self.locker.acquire() request = self.request if request: @@ -172,13 +176,14 @@ class CacheInRam(CacheAbstract): self._clear(storage, regex) if not CacheAbstract.cache_stats_name in storage.keys(): - storage[CacheAbstract.cache_stats_name] = {'hit_total': 0,'misses': 0} + storage[CacheAbstract.cache_stats_name] = { + 'hit_total': 0, 'misses': 0} self.locker.release() def __call__(self, key, f, - time_expire = DEFAULT_TIME_EXPIRE, - destroyer = None): + time_expire=DEFAULT_TIME_EXPIRE, + destroyer=None): """ Attention! cache.ram does not copy the cached object. It just stores a reference to it. Turns out the deepcopying the object has some problems: @@ -269,14 +274,15 @@ class CacheOnDisk(CacheAbstract): try: storage = shelve.open(self.shelve_name) except: - logger.error('corrupted cache file %s, will try rebuild it' \ - % (self.shelve_name)) + logger.error('corrupted cache file %s, will try rebuild it' + % (self.shelve_name)) storage = None if not storage and os.path.exists(self.shelve_name): os.unlink(self.shelve_name) storage = shelve.open(self.shelve_name) if not CacheAbstract.cache_stats_name in storage.keys(): - storage[CacheAbstract.cache_stats_name] = {'hit_total':0, 'misses': 0} + storage[CacheAbstract.cache_stats_name] = { + 'hit_total': 0, 'misses': 0} storage.sync() except Exception, e: if storage: @@ -286,7 +292,8 @@ class CacheOnDisk(CacheAbstract): portalocker.unlock(locker) locker.close() locked = False - raise RuntimeError, 'unable to create/re-create cache file %s' % self.shelve_name + raise RuntimeError( + 'unable to create/re-create cache file %s' % self.shelve_name) self.locker = locker self.locked = locked self.storage = storage @@ -298,8 +305,10 @@ class CacheOnDisk(CacheAbstract): self.folder = folder def initialize(self): - if self.initialized: return - else: self.initialized = True + if self.initialized: + return + else: + self.initialized = True folder = self.folder request = self.request @@ -312,8 +321,8 @@ class CacheOnDisk(CacheAbstract): ### we need this because of a possible bug in shelve that may ### or may not lock - self.locker_name = os.path.join(folder,'cache.lock') - self.shelve_name = os.path.join(folder,'cache.shelve') + self.locker_name = os.path.join(folder, 'cache.lock') + self.shelve_name = os.path.join(folder, 'cache.shelve') def clear(self, regex=None): self.initialize() @@ -328,7 +337,7 @@ class CacheOnDisk(CacheAbstract): self._close_shelve_and_unlock() def __call__(self, key, f, - time_expire = DEFAULT_TIME_EXPIRE): + time_expire=DEFAULT_TIME_EXPIRE): self.initialize() dt = time_expire storage = self._open_shelve_and_lock() @@ -346,7 +355,7 @@ class CacheOnDisk(CacheAbstract): else: value = f() storage[key] = (now, value) - storage[CacheAbstract.cache_stats_name]['misses']+=1 + storage[CacheAbstract.cache_stats_name]['misses'] += 1 storage.sync() finally: self._close_shelve_and_unlock() @@ -365,8 +374,9 @@ class CacheOnDisk(CacheAbstract): self._close_shelve_and_unlock() return value + class CacheAction(object): - def __init__(self,func,key,time_expire,cache,cache_model): + def __init__(self, func, key, time_expire, cache, cache_model): self.__name__ = func.__name__ self.__doc__ = func.__doc__ self.func = func @@ -374,17 +384,18 @@ class CacheAction(object): self.time_expire = time_expire self.cache = cache self.cache_model = cache_model - def __call__(self,*a,**b): + + def __call__(self, *a, **b): if not self.key: - key2 = self.__name__+':'+repr(a)+':'+repr(b) + key2 = self.__name__ + ':' + repr(a) + ':' + repr(b) else: - key2 = self.key.replace('%(name)s',self.__name__)\ - .replace('%(args)s',str(a)).replace('%(vars)s',str(b)) + key2 = self.key.replace('%(name)s', self.__name__)\ + .replace('%(args)s', str(a)).replace('%(vars)s', str(b)) cache_model = self.cache_model - if not cache_model or isinstance(cache_model,str): - cache_model = getattr(self.cache,cache_model or 'ram') + if not cache_model or isinstance(cache_model, str): + cache_model = getattr(self.cache, cache_model or 'ram') return cache_model(key2, - lambda a=a,b=b:self.func(*a,**b), + lambda a=a, b=b: self.func(*a, **b), self.time_expire) @@ -424,9 +435,9 @@ class Cache(object): logger.warning('no cache.disk (AttributeError)') def __call__(self, - key = None, - time_expire = DEFAULT_TIME_EXPIRE, - cache_model = None): + key=None, + time_expire=DEFAULT_TIME_EXPIRE, + cache_model=None): """ Decorator function that can be used to cache any function/method. @@ -459,8 +470,8 @@ class Cache(object): `request.env.path_info` as key. """ - def tmp(func,cache=self,cache_model=cache_model): - return CacheAction(func,key,time_expire,self,cache_model) + def tmp(func, cache=self, cache_model=cache_model): + return CacheAction(func, key, time_expire, self, cache_model) return tmp @staticmethod @@ -473,7 +484,7 @@ class Cache(object): cache_model(prefix + key, f, time_expire) -def lazy_cache(key=None,time_expire=None,cache_model='ram'): +def lazy_cache(key=None, time_expire=None, cache_model='ram'): """ can be used to cache any function including in modules, as long as the cached function is only called within a web2py request @@ -481,11 +492,12 @@ def lazy_cache(key=None,time_expire=None,cache_model='ram'): the time_expire defaults to None (no cache expiration) if cache_model is "ram" then the model is current.cache.ram, etc. """ - def decorator(f,key=key,time_expire=time_expire,cache_model=cache_model): + def decorator(f, key=key, time_expire=time_expire, cache_model=cache_model): key = key or repr(f) - def g(*c,**d): + + def g(*c, **d): from gluon import current - return current.cache(key,time_expire,cache_model)(f)(*c,**d) + return current.cache(key, time_expire, cache_model)(f)(*c, **d) g.__name__ = f.__name__ return g return decorator diff --git a/gluon/compileapp.py b/gluon/compileapp.py index fc3eada8..36360d7a 100644 --- a/gluon/compileapp.py +++ b/gluon/compileapp.py @@ -48,7 +48,7 @@ except: logger.warning('unable to import py_compile') is_pypy = settings.global_settings.is_pypy -is_gae = settings.global_settings.web2py_runtime_gae +is_gae = settings.global_settings.web2py_runtime_gae is_jython = settings.global_settings.is_jython pjoin = os.path.join @@ -95,6 +95,7 @@ _TEST() CACHED_REGEXES = {} CACHED_REGEXES_MAX_SIZE = 1000 + def re_compile(regex): try: return CACHED_REGEXES[regex] @@ -104,6 +105,7 @@ def re_compile(regex): compiled_regex = CACHED_REGEXES[regex] = re.compile(regex) return compiled_regex + class mybuiltin(object): """ NOTE could simple use a dict and populate it, @@ -114,14 +116,16 @@ class mybuiltin(object): try: return getattr(__builtin__, key) except AttributeError: - raise KeyError, key + raise KeyError(key) + def __setitem__(self, key, value): setattr(self, key, value) + def LOAD(c=None, f='index', args=None, vars=None, - extension=None, target=None,ajax=False,ajax_trap=False, - url=None,user_signature=False, timeout=None, times=1, - content='loading...',**attr): + extension=None, target=None, ajax=False, ajax_trap=False, + url=None, user_signature=False, timeout=None, times=1, + content='loading...', **attr): """ LOAD a component into the action's document Timing options: @@ -134,13 +138,14 @@ def LOAD(c=None, f='index', args=None, vars=None, is added on page loading without delay. """ from html import TAG, DIV, URL, SCRIPT, XML - if args is None: args = [] + if args is None: + args = [] vars = Storage(vars or {}) - target = target or 'c'+str(random.random())[2:] - attr['_id']=target + target = target or 'c' + str(random.random())[2:] + attr['_id'] = target request = current.request if '.' in f: - f, extension = f.rsplit('.',1) + f, extension = f.rsplit('.', 1) if url or ajax: url = url or URL(request.application, c, f, r=request, args=args, vars=vars, extension=extension, @@ -160,19 +165,20 @@ def LOAD(c=None, f='index', args=None, vars=None, if not isinstance(timeout, (int, long)): raise ValueError("Timeout argument must be an integer or None") elif timeout <= 0: - raise ValueError("Timeout argument must be greater than zero or None") + raise ValueError( + "Timeout argument must be greater than zero or None") statement = "web2py_component('%s','%s', %s, %s);" \ - % (url, target, timeout, times) + % (url, target, timeout, times) else: statement = "web2py_component('%s','%s');" % (url, target) script = SCRIPT(statement, _type="text/javascript") if not content is None: - return TAG[''](script, DIV(content,**attr)) + return TAG[''](script, DIV(content, **attr)) else: return TAG[''](script) else: - if not isinstance(args,(list,tuple)): + if not isinstance(args, (list, tuple)): args = [args] c = c or request.controller other_request = Storage(request) @@ -186,17 +192,17 @@ def LOAD(c=None, f='index', args=None, vars=None, other_request.post_vars = Storage() other_response = Response() other_request.env.path_info = '/' + \ - '/'.join([request.application,c,f] + \ - map(str, other_request.args)) + '/'.join([request.application, c, f] + + map(str, other_request.args)) other_request.env.query_string = \ vars and URL(vars=vars).split('?')[1] or '' other_request.env.http_web2py_component_location = \ request.env.path_info other_request.cid = target other_request.env.http_web2py_component_element = target - other_response.view = '%s/%s.%s' % (c,f, other_request.extension) + other_response.view = '%s/%s.%s' % (c, f, other_request.extension) - other_environment = copy.copy(current.globalenv) ### NASTY + other_environment = copy.copy(current.globalenv) # NASTY other_response._view_environment = other_environment other_response.generic_patterns = \ @@ -218,40 +224,41 @@ def LOAD(c=None, f='index', args=None, vars=None, js = None if ajax_trap: link = URL(request.application, c, f, r=request, - args=args, vars=vars, extension=extension, - user_signature=user_signature) + args=args, vars=vars, extension=extension, + user_signature=user_signature) js = "web2py_trap_form('%s','%s');" % (link, target) - script = js and SCRIPT(js,_type="text/javascript") or '' - return TAG[''](DIV(XML(page),**attr),script) - + script = js and SCRIPT(js, _type="text/javascript") or '' + return TAG[''](DIV(XML(page), **attr), script) class LoadFactory(object): """ Attention: this helper is new and experimental """ - def __init__(self,environment): + def __init__(self, environment): self.environment = environment + def __call__(self, c=None, f='index', args=None, vars=None, - extension=None, target=None,ajax=False,ajax_trap=False, - url=None,user_signature=False, content='loading...',**attr): - if args is None: args = [] + extension=None, target=None, ajax=False, ajax_trap=False, + url=None, user_signature=False, content='loading...', **attr): + if args is None: + args = [] vars = Storage(vars or {}) import globals - target = target or 'c'+str(random.random())[2:] - attr['_id']=target + target = target or 'c' + str(random.random())[2:] + attr['_id'] = target request = self.environment['request'] if '.' in f: - f, extension = f.rsplit('.',1) + f, extension = f.rsplit('.', 1) if url or ajax: url = url or html.URL(request.application, c, f, r=request, args=args, vars=vars, extension=extension, user_signature=user_signature) script = html.SCRIPT('web2py_component("%s","%s")' % (url, target), _type="text/javascript") - return html.TAG[''](script, html.DIV(content,**attr)) + return html.TAG[''](script, html.DIV(content, **attr)) else: - if not isinstance(args,(list,tuple)): + if not isinstance(args, (list, tuple)): args = [args] c = c or request.controller @@ -266,15 +273,15 @@ class LoadFactory(object): other_request.post_vars = Storage() other_response = globals.Response() other_request.env.path_info = '/' + \ - '/'.join([request.application,c,f] + \ - map(str, other_request.args)) + '/'.join([request.application, c, f] + + map(str, other_request.args)) other_request.env.query_string = \ vars and html.URL(vars=vars).split('?')[1] or '' other_request.env.http_web2py_component_location = \ request.env.path_info other_request.cid = target other_request.env.http_web2py_component_element = target - other_response.view = '%s/%s.%s' % (c,f, other_request.extension) + other_response.view = '%s/%s.%s' % (c, f, other_request.extension) other_environment = copy.copy(self.environment) other_response._view_environment = other_environment other_response.generic_patterns = \ @@ -299,8 +306,8 @@ class LoadFactory(object): args=args, vars=vars, extension=extension, user_signature=user_signature) js = "web2py_trap_form('%s','%s');" % (link, target) - script = js and html.SCRIPT(js,_type="text/javascript") or '' - return html.TAG[''](html.DIV(html.XML(page),**attr),script) + script = js and html.SCRIPT(js, _type="text/javascript") or '' + return html.TAG[''](html.DIV(html.XML(page), **attr), script) def local_import_aux(name, reload_force=False, app='welcome'): @@ -321,7 +328,7 @@ def local_import_aux(name, reload_force=False, app='welcome'): This prevents conflict between applications and un-necessary execs. It can be used to import any module, including regular Python modules. """ - items = name.replace('/','.') + items = name.replace('/', '.') name = "applications.%s.modules.%s" % (app, items) module = __import__(name) for item in name.split(".")[1:]: @@ -355,12 +362,14 @@ OLD IMPLEMENTATION: file.close() imp.release_lock() if not module: - raise ImportError, "cannot find module %s in %s" % (filename, modulepath) + raise ImportError, "cannot find module %s in %s" % ( + filename, modulepath) return module """ -_base_environment_ = dict((k,getattr(html,k)) for k in html.__all__) -_base_environment_.update((k,getattr(validators,k)) for k in validators.__all__) +_base_environment_ = dict((k, getattr(html, k)) for k in html.__all__) +_base_environment_.update( + (k, getattr(validators, k)) for k in validators.__all__) _base_environment_['__builtins__'] = __builtins__ _base_environment_['HTTP'] = HTTP _base_environment_['redirect'] = redirect @@ -372,6 +381,7 @@ _base_environment_['SQLFORM'] = SQLFORM _base_environment_['SQLTABLE'] = SQLTABLE _base_environment_['LOAD'] = LOAD + def build_environment(request, response, session, store_current=True): """ Build the environment dictionary into which web2py files are executed. @@ -384,7 +394,7 @@ def build_environment(request, response, session, store_current=True): # Enable standard conditional models (i.e., /*.py, /[controller]/*.py, and # /[controller]/[function]/*.py) response.models_to_run = [r'^\w+\.py$', r'^%s/\w+\.py$' % request.controller, - r'^%s/%s/\w+\.py$' % (request.controller, request.function)] + r'^%s/%s/\w+\.py$' % (request.controller, request.function)] t = environment['T'] = translator(request) c = environment['cache'] = Cache(request) @@ -398,23 +408,24 @@ def build_environment(request, response, session, store_current=True): current.cache = c global __builtins__ - if is_jython: # jython hack + if is_jython: # jython hack __builtins__ = mybuiltin() - elif is_pypy: # apply the same hack to pypy too + elif is_pypy: # apply the same hack to pypy too __builtins__ = mybuiltin() else: - __builtins__['__import__'] = __builtin__.__import__ ### WHY? + __builtins__['__import__'] = __builtin__.__import__ # WHY? environment['request'] = request environment['response'] = response environment['session'] = session environment['local_import'] = \ - lambda name, reload=False, app=request.application:\ - local_import_aux(name,reload,app) + lambda name, reload=False, app=request.application:\ + local_import_aux(name, reload, app) BaseAdapter.set_folder(pjoin(request.folder, 'databases')) response._view_environment = copy.copy(environment) custom_import_install() return environment + def save_pyc(filename): """ Bytecode compiles the file `filename` @@ -431,7 +442,7 @@ def read_pyc(filename): """ data = read_file(filename, 'rb') if not is_gae and data[:4] != imp.get_magic(): - raise SystemError, 'compiled code is incompatible' + raise SystemError('compiled code is incompatible') return marshal.loads(data[8:]) @@ -458,7 +469,7 @@ def compile_models(folder): path = pjoin(folder, 'models') for file in listdir(path, '.+\.py$'): data = read_file(pjoin(path, file)) - filename = pjoin(folder, 'compiled','models',file) + filename = pjoin(folder, 'compiled', 'models', file) mktree(filename) write_file(filename, data) save_pyc(filename) @@ -473,14 +484,14 @@ def compile_controllers(folder): path = pjoin(folder, 'controllers') for file in listdir(path, '.+\.py$'): ### why is this here? save_pyc(pjoin(path, file)) - data = read_file(pjoin(path,file)) + data = read_file(pjoin(path, file)) exposed = regex_expose.findall(data) for function in exposed: command = data + "\nresponse._vars=response._caller(%s)\n" % \ function filename = pjoin(folder, 'compiled', ('controllers/' + file[:-3]).replace('/', '_') - + '_' + function + '.py') + + '_' + function + '.py') write_file(filename, command) save_pyc(filename) os.unlink(filename) @@ -500,19 +511,19 @@ def run_models_in(environment): for model in listdir(cpath, '^models_\w+\.pyc$', 0): restricted(read_pyc(model), environment, layer=model) path = pjoin(cpath, 'models') - models = listdir(path, '^\w+\.pyc$',0,sort=False) - compiled=True + models = listdir(path, '^\w+\.pyc$', 0, sort=False) + compiled = True else: path = pjoin(folder, 'models') - models = listdir(path, '^\w+\.py$',0,sort=False) - compiled=False + models = listdir(path, '^\w+\.py$', 0, sort=False) + compiled = False n = len(path) + 1 for model in models: regex = environment['response'].models_to_run if isinstance(regex, list): regex = re_compile('|'.join(regex)) file = model[n:].replace(os.path.sep, '/').replace('.pyc', '.py') - if not regex.search(file) and c!= 'appadmin': + if not regex.search(file) and c != 'appadmin': continue elif compiled: code = read_pyc(model) @@ -538,7 +549,7 @@ def run_controller_in(controller, function, environment): badf = 'invalid function (%s/%s)' % (controller, function) if os.path.exists(path): filename = pjoin(path, 'controllers_%s_%s.pyc' - % (controller, function)) + % (controller, function)) if not os.path.exists(filename): raise HTTP(404, rewrite.THREAD_LOCAL.routes.error_message % badf, @@ -548,7 +559,8 @@ def run_controller_in(controller, function, environment): # TESTING: adjust the path to include site packages from settings import global_settings from admin import abspath, add_path_first - paths = (global_settings.gluon_parent, abspath('site-packages', gluon=True), abspath('gluon', gluon=True), '') + paths = (global_settings.gluon_parent, abspath( + 'site-packages', gluon=True), abspath('gluon', gluon=True), '') [add_path_first(path) for path in paths] # TESTING END @@ -578,18 +590,19 @@ def run_controller_in(controller, function, environment): code = "%s\nresponse._vars=response._caller(%s)\n" % (code, function) if is_gae: layer = filename + ':' + function - code = getcfs(layer, filename, lambda: compile2(code,layer)) + code = getcfs(layer, filename, lambda: compile2(code, layer)) restricted(code, environment, filename) response = environment['response'] - vars=response._vars + vars = response._vars if response.postprocessing: vars = reduce(lambda vars, p: p(vars), response.postprocessing, vars) - if isinstance(vars,unicode): + if isinstance(vars, unicode): vars = vars.encode('utf8') - elif hasattr(vars,'xml') and callable(vars.xml): + elif hasattr(vars, 'xml') and callable(vars.xml): vars = vars.xml() return vars + def run_view_in(environment): """ Executes the view for the requested action. @@ -606,7 +619,7 @@ def run_view_in(environment): if response.generic_patterns: patterns = response.generic_patterns regex = re_compile('|'.join(map(fnmatch.translate, patterns))) - short_action = '%(controller)s/%(function)s.%(extension)s' % request + short_action = '%(controller)s/%(function)s.%(extension)s' % request allow_generic = regex.search(short_action) else: allow_generic = False @@ -626,7 +639,7 @@ def run_view_in(environment): files.append('views_generic.pyc') # end backward compatibility code for f in files: - filename = pjoin(path,f) + filename = pjoin(path, f) if os.path.exists(filename): code = read_pyc(filename) restricted(code, environment, layer=filename) @@ -648,13 +661,14 @@ def run_view_in(environment): ccode = getcfs(layer, filename, lambda: compile2(parse_template(view, pjoin(folder, 'views'), - context=environment),layer)) + context=environment), layer)) else: ccode = parse_template(view, pjoin(folder, 'views'), context=environment) restricted(ccode, environment, layer) + def remove_compiled_application(folder): """ Deletes the folder `compiled` containing the compiled application. @@ -662,7 +676,7 @@ def remove_compiled_application(folder): try: shutil.rmtree(pjoin(folder, 'compiled')) path = pjoin(folder, 'controllers') - for file in listdir(path,'.*\.pyc$',drop=False): + for file in listdir(path, '.*\.pyc$', drop=False): os.unlink(file) except OSError: pass diff --git a/gluon/contenttype.py b/gluon/contenttype.py index 4270bb6c..7d221330 100644 --- a/gluon/contenttype.py +++ b/gluon/contenttype.py @@ -700,7 +700,7 @@ CONTENT_TYPE = { '.zabw': 'application/x-abiword', '.zip': 'application/zip', '.zoo': 'application/x-zoo', - } +} def contenttype(filename, default='text/plain'): @@ -709,11 +709,11 @@ def contenttype(filename, default='text/plain'): """ i = filename.rfind('.') - if i>=0: - default = CONTENT_TYPE.get(filename[i:].lower(),default) + if i >= 0: + default = CONTENT_TYPE.get(filename[i:].lower(), default) j = filename.rfind('.', 0, i) - if j>=0: - default = CONTENT_TYPE.get(filename[j:].lower(),default) + if j >= 0: + default = CONTENT_TYPE.get(filename[j:].lower(), default) if default.startswith('text/'): default += '; charset=utf-8' return default diff --git a/gluon/contrib/AuthorizeNet.py b/gluon/contrib/AuthorizeNet.py index d54d7ee9..da882d08 100755 --- a/gluon/contrib/AuthorizeNet.py +++ b/gluon/contrib/AuthorizeNet.py @@ -21,6 +21,7 @@ import urllib _known_tuple_types = {} + class NamedTupleBase(tuple): """Base class for named tuples with the __new__ operator set, named tuples yielded by the namedtuple() function will subclass this and add @@ -29,7 +30,7 @@ class NamedTupleBase(tuple): """Create a new instance of this fielded tuple""" # May need to unpack named field values here if kws: - values = list(args) + [None]*(len(cls._fields) - len(args)) + values = list(args) + [None] * (len(cls._fields) - len(args)) fields = dict((val, idx) for idx, val in enumerate(cls._fields)) for kw, val in kws.iteritems(): assert kw in kws, "%r not in field list" % kw @@ -37,6 +38,7 @@ class NamedTupleBase(tuple): args = tuple(values) return tuple.__new__(cls, args) + def namedtuple(typename, fieldnames): """ >>> import namedtuples @@ -75,24 +77,26 @@ def namedtuple(typename, fieldnames): # Done return new_tuple_type + class AIM: class AIMError(Exception): def __init__(self, value): self.parameter = value + def __str__(self): return str(self.parameter) def __init__(self, login, transkey, testmode=False): - if str(login).strip() == '' or login == None: + if str(login).strip() == '' or login is None: raise AIM.AIMError('No login name provided') - if str(transkey).strip() == '' or transkey == None: + if str(transkey).strip() == '' or transkey is None: raise AIM.AIMError('No transaction key provided') if testmode != True and testmode != False: raise AIM.AIMError('Invalid value for testmode. Must be True or False. "{0}" given.'.format(testmode)) self.testmode = testmode - self.proxy = None; + self.proxy = None self.delimiter = '|' self.results = [] self.error = True @@ -117,8 +121,9 @@ class AIM: else: url = 'https://secure.authorize.net/gateway/transact.dll' - if self.proxy == None: - self.results += str(urllib.urlopen(url, encoded_args).read()).split(self.delimiter) + if self.proxy is None: + self.results += str(urllib.urlopen( + url, encoded_args).read()).split(self.delimiter) else: opener = urllib.FancyURLopener(self.proxy) opened = opener.open(url, encoded_args) @@ -147,36 +152,37 @@ class AIM: raise AIM.AIMError(self.response.ResponseText) def setTransaction(self, creditcard, expiration, total, cvv=None, tax=None, invoice=None): - if str(creditcard).strip() == '' or creditcard == None: + if str(creditcard).strip() == '' or creditcard is None: raise AIM.AIMError('No credit card number passed to setTransaction(): {0}'.format(creditcard)) - if str(expiration).strip() == '' or expiration == None: + if str(expiration).strip() == '' or expiration is None: raise AIM.AIMError('No expiration number to setTransaction(): {0}'.format(expiration)) - if str(total).strip() == '' or total == None: + if str(total).strip() == '' or total is None: raise AIM.AIMError('No total amount passed to setTransaction(): {0}'.format(total)) self.setParameter('x_card_num', creditcard) self.setParameter('x_exp_date', expiration) self.setParameter('x_amount', total) - if cvv != None: + if cvv is not None: self.setParameter('x_card_code', cvv) - if tax != None: + if tax is not None: self.setParameter('x_tax', tax) - if invoice != None: + if invoice is not None: self.setParameter('x_invoice_num', invoice) def setTransactionType(self, transtype=None): - types = ['AUTH_CAPTURE', 'AUTH_ONLY', 'PRIOR_AUTH_CAPTURE', 'CREDIT', 'CAPTURE_ONLY', 'VOID'] + types = ['AUTH_CAPTURE', 'AUTH_ONLY', 'PRIOR_AUTH_CAPTURE', + 'CREDIT', 'CAPTURE_ONLY', 'VOID'] if transtype.upper() not in types: raise AIM.AIMError('Incorrect Transaction Type passed to setTransactionType(): {0}'.format(transtype)) self.setParameter('x_type', transtype.upper()) def setProxy(self, proxy=None): - if str(proxy).strip() == '' or proxy == None: + if str(proxy).strip() == '' or proxy is None: raise AIM.AIMError('No proxy passed to setProxy()') self.proxy = {'http': str(proxy).strip()} def setParameter(self, key=None, value=None): - if key != None and value != None and str(key).strip() != '' and str(value).strip() != '': + if key is not None and value is not None and str(key).strip() != '' and str(value).strip() != '': self.parameters[key] = str(value).strip() else: raise AIM.AIMError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value)) @@ -194,10 +200,11 @@ class AIM: responses = ['', 'Approved', 'Declined', 'Error'] return responses[int(self.results[0])] -def process(creditcard,expiration,total,cvv=None,tax=None,invoice=None, - login='cnpdev4289', transkey='SR2P8g4jdEn7vFLQ',testmode=True): - payment = AIM(login,transkey,testmode) - expiration = expiration.replace('/','') + +def process(creditcard, expiration, total, cvv=None, tax=None, invoice=None, + login='cnpdev4289', transkey='SR2P8g4jdEn7vFLQ', testmode=True): + payment = AIM(login, transkey, testmode) + expiration = expiration.replace('/', '') payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice) try: payment.process() @@ -205,6 +212,7 @@ def process(creditcard,expiration,total,cvv=None,tax=None,invoice=None, except AIM.AIMError: return False + def test(): import socket import sys @@ -215,12 +223,14 @@ def test(): total = '1.00' cvv = '123' tax = '0.00' - invoice = str(time())[4:10] # get a random invoice number + invoice = str(time())[4:10] # get a random invoice number try: payment = AIM('cnpdev4289', 'SR2P8g4jdEn7vFLQ', True) - payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice) - payment.setParameter('x_duplicate_window', 180) # three minutes duplicate windows + payment.setTransaction( + creditcard, expiration, total, cvv, tax, invoice) + payment.setParameter( + 'x_duplicate_window', 180) # three minutes duplicate windows payment.setParameter('x_cust_id', '1324') # customer ID payment.setParameter('x_first_name', 'John') payment.setParameter('x_last_name', 'Conde') @@ -232,7 +242,8 @@ def test(): payment.setParameter('x_country', 'US') payment.setParameter('x_phone', '800-555-1234') payment.setParameter('x_description', 'Test Transaction') - payment.setParameter('x_customer_ip', socket.gethostbyname(socket.gethostname())) + payment.setParameter( + 'x_customer_ip', socket.gethostbyname(socket.gethostname())) payment.setParameter('x_email', 'john@example.com') payment.setParameter('x_email_customer', False) payment.process() @@ -251,9 +262,9 @@ def test(): except AIM.AIMError, e: print "Exception thrown:", e print 'An error occured' - print 'approved',payment.isApproved() - print 'declined',payment.isDeclined() - print 'error',payment.isError() + print 'approved', payment.isApproved() + print 'declined', payment.isDeclined() + print 'error', payment.isError() -if __name__=='__main__': +if __name__ == '__main__': test() diff --git a/gluon/contrib/DowCommerce.py b/gluon/contrib/DowCommerce.py index 03d19fd1..d06b088f 100644 --- a/gluon/contrib/DowCommerce.py +++ b/gluon/contrib/DowCommerce.py @@ -15,25 +15,27 @@ __all__ = ['DowCommerce'] from operator import itemgetter import urllib + class DowCommerce: class DowCommerceError(Exception): def __init__(self, value): self.parameter = value + def __str__(self): return str(self.parameter) def __init__(self, username=None, password=None, demomode=False): if not demomode: - if str(username).strip() == '' or username == None: + if str(username).strip() == '' or username is None: raise DowCommerce.DowCommerceError('No username provided') - if str(password).strip() == '' or password == None: + if str(password).strip() == '' or password is None: raise DowCommerce.DowCommerceError('No password provided') else: username = 'demo' password = 'password' - self.proxy = None; + self.proxy = None self.delimiter = '&' self.results = {} self.error = True @@ -45,11 +47,11 @@ class DowCommerce: self.setParameter('username', username) self.setParameter('password', password) - def process(self): encoded_args = urllib.urlencode(self.parameters) - if self.proxy == None: - results = str(urllib.urlopen(self.url, encoded_args).read()).split(self.delimiter) + if self.proxy is None: + results = str(urllib.urlopen( + self.url, encoded_args).read()).split(self.delimiter) else: opener = urllib.FancyURLopener(self.proxy) opened = opener.open(self.url, encoded_args) @@ -59,7 +61,7 @@ class DowCommerce: opened.close() for result in results: - (key,val) = result.split('=') + (key, val) = result.split('=') self.results[key] = val if self.results['response'] == '1': @@ -80,17 +82,18 @@ class DowCommerce: self.declined = False raise DowCommerce.DowCommerceError(self.results) - def setTransaction(self, creditcard, expiration, total, cvv=None, orderid=None, orderdescription=None, - ipaddress=None, tax=None, shipping=None, - firstname=None, lastname=None, company=None, address1=None, address2=None, city=None, state=None, zipcode=None, - country=None, phone=None, fax=None, emailaddress=None, website=None, - shipping_firstname=None, shipping_lastname=None, shipping_company=None, shipping_address1=None, shipping_address2=None, - shipping_city=None, shipping_state=None, shipping_zipcode = None, shipping_country=None, shipping_emailaddress=None): - if str(creditcard).strip() == '' or creditcard == None: + def setTransaction( + self, creditcard, expiration, total, cvv=None, orderid=None, orderdescription=None, + ipaddress=None, tax=None, shipping=None, + firstname=None, lastname=None, company=None, address1=None, address2=None, city=None, state=None, zipcode=None, + country=None, phone=None, fax=None, emailaddress=None, website=None, + shipping_firstname=None, shipping_lastname=None, shipping_company=None, shipping_address1=None, shipping_address2=None, + shipping_city=None, shipping_state=None, shipping_zipcode=None, shipping_country=None, shipping_emailaddress=None): + if str(creditcard).strip() == '' or creditcard is None: raise DowCommerce.DowCommerceError('No credit card number passed to setTransaction(): {0}'.format(creditcard)) - if str(expiration).strip() == '' or expiration == None: + if str(expiration).strip() == '' or expiration is None: raise DowCommerce.DowCommerceError('No expiration number passed to setTransaction(): {0}'.format(expiration)) - if str(total).strip() == '' or total == None: + if str(total).strip() == '' or total is None: raise DowCommerce.DowCommerceError('No total amount passed to setTransaction(): {0}'.format(total)) self.setParameter('ccnumber', creditcard) @@ -165,12 +168,12 @@ class DowCommerce: self.setParameter('type', transtype.lower()) def setProxy(self, proxy=None): - if str(proxy).strip() == '' or proxy == None: + if str(proxy).strip() == '' or proxy is None: raise DowCommerce.DowCommerceError('No proxy passed to setProxy()') self.proxy = {'http': str(proxy).strip()} def setParameter(self, key=None, value=None): - if key != None and value != None and str(key).strip() != '' and str(value).strip() != '': + if key is not None and value is not None and str(key).strip() != '' and str(value).strip() != '': self.parameters[key] = str(value).strip() else: raise DowCommerce.DowCommerceError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value)) @@ -194,6 +197,7 @@ class DowCommerce: def getResponseText(self): return self.results['responsetext'] + def test(): import socket import sys @@ -212,13 +216,14 @@ def test(): total = '1.00' cvv = '999' tax = '0.00' - orderid = str(time())[4:10] # get a random invoice number + orderid = str(time())[4:10] # get a random invoice number try: payment = DowCommerce(demomode=True) - payment.setTransaction(creditcard, expiration, total, cvv=cvv, tax=tax, orderid=orderid, orderdescription='Test Transaction', - firstname='John', lastname='Doe', company='Acme', address1='123 Min Street', city='Hometown', state='VA', - zipcode='12345', country='US', phone='888-555-1212', emailaddress='john@noemail.local', ipaddress='192.168.1.1') + payment.setTransaction( + creditcard, expiration, total, cvv=cvv, tax=tax, orderid=orderid, orderdescription='Test Transaction', + firstname='John', lastname='Doe', company='Acme', address1='123 Min Street', city='Hometown', state='VA', + zipcode='12345', country='US', phone='888-555-1212', emailaddress='john@noemail.local', ipaddress='192.168.1.1') payment.process() if payment.isApproved(): @@ -231,9 +236,9 @@ def test(): except DowCommerce.DowCommerceError, e: print "Exception thrown:", e print 'An error occured' - print 'approved',payment.isApproved() - print 'declined',payment.isDeclined() - print 'error',payment.isError() + print 'approved', payment.isApproved() + print 'declined', payment.isDeclined() + print 'error', payment.isError() -if __name__=='__main__': +if __name__ == '__main__': test() diff --git a/gluon/contrib/__init__.py b/gluon/contrib/__init__.py index 8b137891..e69de29b 100644 --- a/gluon/contrib/__init__.py +++ b/gluon/contrib/__init__.py @@ -1 +0,0 @@ - diff --git a/gluon/contrib/autolinks.py b/gluon/contrib/autolinks.py index 05f02b64..48f555cb 100644 --- a/gluon/contrib/autolinks.py +++ b/gluon/contrib/autolinks.py @@ -42,7 +42,9 @@ revision3.com viddler.com """ -import re, cgi, sys +import re +import cgi +import sys from simplejson import loads import urllib import uuid @@ -75,23 +77,28 @@ EMBED_MAPS = [ 'http://revision3.com/api/oembed/'), (re.compile('http://\S+.viddler.com/\S+'), 'http://lab.viddler.com/services/oembed/'), - ] +] + def image(url): return '' % url + def audio(url): return '' % url + def video(url): return '' % url + def googledoc_viewer(url): return '' % urllib.quote(url) + def web2py_component(url): code = str(uuid.uuid4()) - return '
' % (code,url,code) + return '
' % (code, url, code) EXTENSION_MAPS = { 'png': image, @@ -126,33 +133,37 @@ EXTENSION_MAPS = { 'xps': googledoc_viewer, } + class VimeoURLOpener(urllib.FancyURLopener): "Vimeo blocks the urllib user agent for some reason" version = "Mozilla/4.0" urllib._urlopener = VimeoURLOpener() + def oembed(url): - for k,v in EMBED_MAPS: + for k, v in EMBED_MAPS: if k.match(url): - oembed = v+'?format=json&url='+cgi.escape(url) + oembed = v + '?format=json&url=' + cgi.escape(url) try: data = urllib.urlopen(oembed).read() print data - return loads(data) # json! + return loads(data) # json! except: pass return {} + def extension(url): return url.split('?')[0].split('.')[-1].lower() -def expand_one(url,cdict): + +def expand_one(url, cdict): # try ombed but first check in cache if cdict and url in cdict: r = cdict[url] else: r = oembed(url) - if isinstance(cdict,dict): + if isinstance(cdict, dict): cdict[url] = r # if oembed service if 'html' in r: @@ -170,21 +181,23 @@ def expand_one(url,cdict): # else regular link return '%(u)s' % dict(u=url) -def expand_html(html,cdict=None): + +def expand_html(html, cdict=None): if not have_soup: - raise RuntimeError, "Missing BeautifulSoup" + raise RuntimeError("Missing BeautifulSoup") soup = BeautifulSoup(html) - comments = soup.findAll(text=lambda text:isinstance(text, Comment)) + comments = soup.findAll(text=lambda text: isinstance(text, Comment)) [comment.extract() for comment in comments] for txt in soup.findAll(text=True): - if not txt.parent.name in ('a','script','pre','code','embed','object','audio','video'): + if not txt.parent.name in ('a', 'script', 'pre', 'code', 'embed', 'object', 'audio', 'video'): ntxt = regex_link.sub( - lambda match: expand_one(match.group(0),cdict), txt) + lambda match: expand_one(match.group(0), cdict), txt) txt.replaceWith(BeautifulSoup(ntxt)) return str(soup) + def test(): - example=""" + example = """

Fringilla nisi parturient nullam

http://www.youtube.com/watch?v=IWBFiI5RrA0

http://www.web2py.com/examples/static/images/logo_bw.png

@@ -198,8 +211,8 @@ laoreet tortor.

""" return expand_html(example) -if __name__=="__main__": - if len(sys.argv)>1: +if __name__ == "__main__": + if len(sys.argv) > 1: print expand_html(open(sys.argv[1]).read()) else: print test() diff --git a/gluon/contrib/gae_memcache.py b/gluon/contrib/gae_memcache.py index ec08fd6a..67efd499 100644 --- a/gluon/contrib/gae_memcache.py +++ b/gluon/contrib/gae_memcache.py @@ -12,6 +12,7 @@ cache.ram=cache.disk=MemcacheClient(request) import time from google.appengine.api.memcache import Client + class MemcacheClient(object): client = Client() @@ -24,12 +25,12 @@ class MemcacheClient(object): key, f, time_expire=300, - ): + ): key = '%s/%s' % (self.request.application, key) dt = time_expire value = None obj = self.client.get(key) - if obj and (dt == None or obj[0] > time.time() - dt): + if obj and (dt is None or obj[0] > time.time() - dt): value = obj[1] elif f is None: if obj: @@ -47,21 +48,21 @@ class MemcacheClient(object): self.client.set(key, (time.time(), value)) return value - def clear(self, key = None): + def clear(self, key=None): if key: key = '%s/%s' % (self.request.application, key) self.client.delete(key) else: self.client.flush_all() - def delete(self,*a,**b): - return self.client.delete(*a,**b) + def delete(self, *a, **b): + return self.client.delete(*a, **b) - def get(self,*a,**b): - return self.client.delete(*a,**b) + def get(self, *a, **b): + return self.client.delete(*a, **b) - def set(self,*a,**b): - return self.client.delete(*a,**b) + def set(self, *a, **b): + return self.client.delete(*a, **b) - def flush_all(self,*a,**b): - return self.client.delete(*a,**b) + def flush_all(self, *a, **b): + return self.client.delete(*a, **b) diff --git a/gluon/contrib/gae_retry.py b/gluon/contrib/gae_retry.py index 9b0fe426..59844521 100644 --- a/gluon/contrib/gae_retry.py +++ b/gluon/contrib/gae_retry.py @@ -51,7 +51,8 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0): :param exponent: rate of exponential back-off. """ - import time, logging + import time + import logging from google.appengine.api import apiproxy_stub_map from google.appengine.runtime import apiproxy_errors from google.appengine.datastore import datastore_pb @@ -60,8 +61,8 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0): interval = float(interval) exponent = float(exponent) wrapped = apiproxy_stub_map.MakeSyncCall - errors = {datastore_pb.Error.TIMEOUT:'Timeout', - datastore_pb.Error.CONCURRENT_TRANSACTION:'TransactionFailedError'} + errors = {datastore_pb.Error.TIMEOUT: 'Timeout', + datastore_pb.Error.CONCURRENT_TRANSACTION: 'TransactionFailedError'} def wrapper(*args, **kwargs): count = 0.0 @@ -70,10 +71,12 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0): return wrapped(*args, **kwargs) except apiproxy_errors.ApplicationError, err: errno = err.application_error - if errno not in errors: raise + if errno not in errors: + raise sleep = (exponent ** count) * interval count += 1.0 - if count > attempts: raise + if count > attempts: + raise msg = "Datastore %s: retry #%d in %s seconds.\n%s" vals = '' if count == 1.0: diff --git a/gluon/contrib/generics.py b/gluon/contrib/generics.py index fa074416..f1922975 100644 --- a/gluon/contrib/generics.py +++ b/gluon/contrib/generics.py @@ -12,6 +12,7 @@ from gluon.sanitizer import sanitize from gluon.contrib.markmin.markmin2latex import markmin2latex from gluon.contrib.markmin.markmin2pdf import markmin2pdf + def wrapper(f): def g(data): try: @@ -25,39 +26,47 @@ def wrapper(f): raise HTTP(405, '%s error' % e) return g + def latex_from_html(html): - markmin=TAG(html).element('body').flatten(markmin_serializer) + markmin = TAG(html).element('body').flatten(markmin_serializer) return XML(markmin2latex(markmin)) + def pdflatex_from_html(html): - if os.system('which pdflatex > /dev/null')==0: - markmin=TAG(html).element('body').flatten(markmin_serializer) - out,warnings,errors=markmin2pdf(markmin) + if os.system('which pdflatex > /dev/null') == 0: + markmin = TAG(html).element('body').flatten(markmin_serializer) + out, warnings, errors = markmin2pdf(markmin) if errors: - current.response.headers['Content-Type']='text/html' - raise HTTP(405,HTML(BODY(H1('errors'), - UL(*errors), - H1('warnings'), - UL(*warnings))).xml()) + current.response.headers['Content-Type'] = 'text/html' + raise HTTP(405, HTML(BODY(H1('errors'), + UL(*errors), + H1('warnings'), + UL(*warnings))).xml()) else: return XML(out) + def pyfpdf_from_html(html): request = current.request + def image_map(path): if path.startswith('/%s/static/' % request.application): - return os.path.join(request.folder,path.split('/',2)[2]) - return 'http%s://%s%s' % (request.is_https and 's' or '',request.env.http_host, path) - class MyFPDF(FPDF, HTMLMixin): pass - pdf=MyFPDF() + return os.path.join(request.folder, path.split('/', 2)[2]) + return 'http%s://%s%s' % (request.is_https and 's' or '', request.env.http_host, path) + + class MyFPDF(FPDF, HTMLMixin): + pass + pdf = MyFPDF() pdf.add_page() - html = sanitize(html, escape=False) #### should have better list of allowed tags - pdf.write_html(html,image_map=image_map) + html = sanitize( + html, escape=False) # should have better list of allowed tags + pdf.write_html(html, image_map=image_map) return XML(pdf.output(dest='S')) + def pdf_from_html(html): # try use latex and pdflatex - if os.system('which pdflatex > /dev/null')==0: + if os.system('which pdflatex > /dev/null') == 0: return pdflatex_from_html(html) else: return pyfpdf_from_html(html) diff --git a/gluon/contrib/google_wallet.py b/gluon/contrib/google_wallet.py index ffcbd9f8..f24c9074 100644 --- a/gluon/contrib/google_wallet.py +++ b/gluon/contrib/google_wallet.py @@ -1,5 +1,6 @@ from gluon import XML + def button(merchant_id="123456789012345", products=[dict(name="shoes", quantity=1, @@ -8,8 +9,8 @@ def button(merchant_id="123456789012345", description="running shoes black")]): t = '' list_products = '' - for k,product in enumerate(products): - for key,value in product.items(): - list_products += t % dict(k=k+1,key=key,value=value) + for k, product in enumerate(products): + for key, value in product.items(): + list_products += t % dict(k=k + 1, key=key, value=value) button = '
%s
' % (merchant_id, list_products, merchant_id) return XML(button) diff --git a/gluon/contrib/gql.py b/gluon/contrib/gql.py index d1023ab1..5438888b 100644 --- a/gluon/contrib/gql.py +++ b/gluon/contrib/gql.py @@ -1,5 +1,5 @@ # this file exists for backward compatibility -__all__ = ['DAL','Field','drivers','gae'] +__all__ = ['DAL', 'Field', 'drivers', 'gae'] from gluon.dal import DAL, Field, Table, Query, Set, Expression, Row, Rows, drivers, BaseAdapter, SQLField, SQLTable, SQLXorable, SQLQuery, SQLSet, SQLRows, SQLStorage, SQLDB, GQLDB, SQLALL, SQLCustomType, gae diff --git a/gluon/contrib/imageutils.py b/gluon/contrib/imageutils.py index 96facc08..594fe521 100644 --- a/gluon/contrib/imageutils.py +++ b/gluon/contrib/imageutils.py @@ -22,6 +22,7 @@ ######################################################################### from gluon import current + class RESIZE(object): def __init__(self, nx=160, ny=80, error_message=' image resize'): (self.nx, self.ny, self.error_message) = (nx, ny, error_message) @@ -43,6 +44,7 @@ class RESIZE(object): else: return (value, None) + def THUMB(image, nx=120, ny=120, gae=False, name='thumb'): if image: if not gae: diff --git a/gluon/contrib/login_methods/__init__.py b/gluon/contrib/login_methods/__init__.py index b28b04f6..e69de29b 100644 --- a/gluon/contrib/login_methods/__init__.py +++ b/gluon/contrib/login_methods/__init__.py @@ -1,3 +0,0 @@ - - - diff --git a/gluon/contrib/login_methods/basic_auth.py b/gluon/contrib/login_methods/basic_auth.py index fee1c35b..88341d05 100644 --- a/gluon/contrib/login_methods/basic_auth.py +++ b/gluon/contrib/login_methods/basic_auth.py @@ -11,9 +11,9 @@ def basic_auth(server="http://127.0.0.1"): """ def basic_login_aux(username, - password, - server=server): - key = base64.b64encode(username+':'+password) + password, + server=server): + key = base64.b64encode(username + ':' + password) headers = {'Authorization': 'Basic ' + key} request = urllib2.Request(server, None, headers) try: @@ -22,5 +22,3 @@ def basic_auth(server="http://127.0.0.1"): except (urllib2.URLError, urllib2.HTTPError): return False return basic_login_aux - - diff --git a/gluon/contrib/login_methods/browserid_account.py b/gluon/contrib/login_methods/browserid_account.py index 83375c8e..c3ccb38c 100644 --- a/gluon/contrib/login_methods/browserid_account.py +++ b/gluon/contrib/login_methods/browserid_account.py @@ -25,6 +25,7 @@ from gluon.storage import Storage from gluon.tools import fetch import gluon.contrib.simplejson as json + class BrowserID(object): """ from gluon.contrib.login_methods.browserid_account import BrowserID @@ -34,17 +35,17 @@ class BrowserID(object): """ def __init__(self, - request, - audience = "", - assertion_post_url = "", - prompt = "BrowserID Login", - issuer = "browserid.org", - verify_url = "https://browserid.org/verify", - browserid_js = "https://browserid.org/include.js", - browserid_button = "https://browserid.org/i/sign_in_red.png", - crypto_js = "https://crypto-js.googlecode.com/files/2.2.0-crypto-md5.js", - on_login_failure = None, - ): + request, + audience="", + assertion_post_url="", + prompt="BrowserID Login", + issuer="browserid.org", + verify_url="https://browserid.org/verify", + browserid_js="https://browserid.org/include.js", + browserid_button="https://browserid.org/i/sign_in_red.png", + crypto_js="https://crypto-js.googlecode.com/files/2.2.0-crypto-md5.js", + on_login_failure=None, + ): self.request = request self.audience = audience @@ -67,13 +68,13 @@ class BrowserID(object): if request.vars.assertion: audience = self.audience issuer = self.issuer - assertion = XML(request.vars.assertion,sanitize=True) - verify_data = {'assertion':assertion,'audience':audience} - auth_info_json = fetch(self.verify_url,data=verify_data) + assertion = XML(request.vars.assertion, sanitize=True) + verify_data = {'assertion': assertion, 'audience': audience} + auth_info_json = fetch(self.verify_url, data=verify_data) j = json.loads(auth_info_json) - epoch_time = int(time.time()*1000) # we need 13 digit epoch time + epoch_time = int(time.time() * 1000) # we need 13 digit epoch time if j["status"] == "okay" and j["audience"] == audience and j['issuer'] == issuer and j['expires'] >= epoch_time: - return dict(email = j['email']) + return dict(email=j['email']) elif self.on_login_failure: redirect('http://google.com') else: @@ -83,9 +84,8 @@ class BrowserID(object): def login_form(self): request = self.request onclick = "javascript:navigator.id.getVerifiedEmail(gotVerifiedEmail) ; return false" - form = DIV(SCRIPT(_src=self.browserid_js,_type="text/javascript"), - SCRIPT(_src=self.crypto_js,_type="text/javascript"), - A(IMG(_src=self.browserid_button,_alt=self.prompt),_href="#",_onclick=onclick,_class="browserid",_title="Login With BrowserID"), - SCRIPT(self.asertion_js)) + form = DIV(SCRIPT(_src=self.browserid_js, _type="text/javascript"), + SCRIPT(_src=self.crypto_js, _type="text/javascript"), + A(IMG(_src=self.browserid_button, _alt=self.prompt), _href="#", _onclick=onclick, _class="browserid", _title="Login With BrowserID"), + SCRIPT(self.asertion_js)) return form - diff --git a/gluon/contrib/login_methods/cas_auth.py b/gluon/contrib/login_methods/cas_auth.py index 0c922a56..54abf92e 100644 --- a/gluon/contrib/login_methods/cas_auth.py +++ b/gluon/contrib/login_methods/cas_auth.py @@ -11,7 +11,8 @@ Tinkered by Szabolcs Gyuris < szimszo n @ o regpreshaz dot eu> from gluon import current, redirect -class CasAuth( object ): + +class CasAuth(object): """ Login will be done via Web2py's CAS application, instead of web2py's login form. @@ -39,101 +40,105 @@ class CasAuth( object ): user's username. """ - def __init__(self, g=None, ### g for backward compatibility ### - urlbase = "https://web2py.com/cas/cas", - actions=['login','validate','logout'], - maps=dict(username=lambda v:v.get('username',v['user']), - email=lambda v:v.get('email',None), - user_id=lambda v:v['user']), - casversion = 1, - casusername = 'cas:user' + def __init__(self, g=None, # g for backward compatibility ### + urlbase="https://web2py.com/cas/cas", + actions=['login', 'validate', 'logout'], + maps=dict(username=lambda v: v.get('username', v['user']), + email=lambda v: v.get('email', None), + user_id=lambda v: v['user']), + casversion=1, + casusername='cas:user' ): - self.urlbase=urlbase - self.cas_login_url="%s/%s"%(self.urlbase,actions[0]) - self.cas_check_url="%s/%s"%(self.urlbase,actions[1]) - self.cas_logout_url="%s/%s"%(self.urlbase,actions[2]) - self.maps=maps + self.urlbase = urlbase + self.cas_login_url = "%s/%s" % (self.urlbase, actions[0]) + self.cas_check_url = "%s/%s" % (self.urlbase, actions[1]) + self.cas_logout_url = "%s/%s" % (self.urlbase, actions[2]) + self.maps = maps self.casversion = casversion self.casusername = casusername - http_host=current.request.env.http_x_forwarded_host - if not http_host: http_host=current.request.env.http_host - if current.request.env.wsgi_url_scheme in [ 'https', 'HTTPS' ]: + http_host = current.request.env.http_x_forwarded_host + if not http_host: + http_host = current.request.env.http_host + if current.request.env.wsgi_url_scheme in ['https', 'HTTPS']: scheme = 'https' else: scheme = 'http' - self.cas_my_url='%s://%s%s'%( scheme, http_host, current.request.env.path_info ) + self.cas_my_url = '%s://%s%s' % ( + scheme, http_host, current.request.env.path_info) - def login_url( self, next = "/" ): - current.session.token=self._CAS_login() + def login_url(self, next="/"): + current.session.token = self._CAS_login() return next - def logout_url( self, next = "/" ): - current.session.token=None - current.session.auth=None + + def logout_url(self, next="/"): + current.session.token = None + current.session.auth = None self._CAS_logout() return next - def get_user( self ): - user=current.session.token + + def get_user(self): + user = current.session.token if user: - d = {'source':'web2py cas'} + d = {'source': 'web2py cas'} for key in self.maps: - d[key]=self.maps[key](user) + d[key] = self.maps[key](user) return d return None - def _CAS_login( self ): + + def _CAS_login(self): """ exposed as CAS.login(request) returns a token on success, None on failed authentication """ import urllib - self.ticket=current.request.vars.ticket + self.ticket = current.request.vars.ticket if not current.request.vars.ticket: - redirect( "%s?service=%s"% (self.cas_login_url, + redirect("%s?service=%s" % (self.cas_login_url, self.cas_my_url)) else: - url="%s?service=%s&ticket=%s" % (self.cas_check_url, - self.cas_my_url, - self.ticket ) - data=urllib.urlopen( url ).read() + url = "%s?service=%s&ticket=%s" % (self.cas_check_url, + self.cas_my_url, + self.ticket) + data = urllib.urlopen(url).read() if data.startswith('yes') or data.startswith('no'): data = data.split('\n') - if data[0]=='yes': - if ':' in data[1]: # for Compatibility with Custom CAS + if data[0] == 'yes': + if ':' in data[1]: # for Compatibility with Custom CAS items = data[1].split(':') a = items[0] - b = len(items)>1 and items[1] or a - c = len(items)>2 and items[2] or b + b = len(items) > 1 and items[1] or a + c = len(items) > 2 and items[2] or b else: a = b = c = data[1] - return dict(user=a,email=b,username=c) + return dict(user=a, email=b, username=c) return None import xml.dom.minidom as dom import xml.parsers.expat as expat try: - dxml=dom.parseString(data) - envelop = dxml.getElementsByTagName("cas:authenticationSuccess") - if len(envelop)>0: + dxml = dom.parseString(data) + envelop = dxml.getElementsByTagName( + "cas:authenticationSuccess") + if len(envelop) > 0: res = dict() for x in envelop[0].childNodes: if x.nodeName.startswith('cas:') and len(x.childNodes): key = x.nodeName[4:].encode('utf8') value = x.childNodes[0].nodeValue.encode('utf8') if not key in res: - res[key]=value + res[key] = value else: - if not isinstance(res[key],list): - res[key]=[res[key]] + if not isinstance(res[key], list): + res[key] = [res[key]] res[key].append(value) return res - except expat.ExpatError: pass - return None # fallback + except expat.ExpatError: + pass + return None # fallback - - def _CAS_logout( self ): + def _CAS_logout(self): """ exposed CAS.logout() redirects to the CAS logout page """ import urllib - redirect("%s?service=%s" % (self.cas_logout_url,self.cas_my_url)) - - + redirect("%s?service=%s" % (self.cas_logout_url, self.cas_my_url)) diff --git a/gluon/contrib/login_methods/dropbox_account.py b/gluon/contrib/login_methods/dropbox_account.py index 42877773..8bfc7079 100644 --- a/gluon/contrib/login_methods/dropbox_account.py +++ b/gluon/contrib/login_methods/dropbox_account.py @@ -18,11 +18,13 @@ from gluon.tools import fetch from gluon.storage import Storage import gluon.contrib.simplejson as json + class DropboxAccount(object): """ from gluon.contrib.login_methods.dropbox_account import DropboxAccount - auth.settings.actions_disabled=['register','change_password','request_reset_password'] + auth.settings.actions_disabled=['register','change_password', + 'request_reset_password'] auth.settings.login_form = DropboxAccount(request, key="...", secret="...", @@ -34,47 +36,45 @@ class DropboxAccount(object): def __init__(self, request, - key = "", - secret = "", + key="", + secret="", access_type="app_folder", - login_url = "", + login_url="", on_login_failure=None, ): - - self.request=request - self.key=key - self.secret=secret - self.access_type=access_type + + self.request = request + self.key = key + self.secret = secret + self.access_type = access_type self.login_url = login_url self.on_login_failure = on_login_failure self.sess = session.DropboxSession( - self.key,self.secret,self.access_type) - + self.key, self.secret, self.access_type) def get_user(self): request = self.request if not current.session.dropbox_request_token: return None elif not current.session.dropbox_access_token: - - request_token = current.session.dropbox_request_token - self.sess.set_request_token(request_token[0],request_token[1]) + + request_token = current.session.dropbox_request_token + self.sess.set_request_token(request_token[0], request_token[1]) access_token = self.sess.obtain_access_token(self.sess.token) current.session.dropbox_access_token = \ - (access_token.key,access_token.secret) + (access_token.key, access_token.secret) else: access_token = current.session.dropbox_access_token - self.sess.set_token(access_token[0],access_token[1]) + self.sess.set_token(access_token[0], access_token[1]) - user = Storage() self.client = client.DropboxClient(self.sess) data = self.client.account_info() - display_name = data.get('display_name','').split(' ',1) - user = dict(email = data.get('email',None), - first_name = display_name[0], - last_name = display_name[-1], - registration_id = data.get('uid',None)) + display_name = data.get('display_name', '').split(' ', 1) + user = dict(email=data.get('email', None), + first_name=display_name[0], + last_name=display_name[-1], + registration_id=data.get('uid', None)) if not user['registration_id'] and self.on_login_failure: redirect(self.on_login_failure) return user @@ -83,7 +83,7 @@ class DropboxAccount(object): request_token = self.sess.obtain_request_token() current.session.dropbox_request_token = \ - (request_token.key,request_token.secret) + (request_token.key, request_token.secret) dropbox_url = self.sess.build_authorize_url(request_token, self.login_url) redirect(dropbox_url) @@ -93,29 +93,32 @@ class DropboxAccount(object): _style="width:400px;height:240px;") return form - def logout_url(self, next = "/"): - current.session.dropbox_request_token=None - current.session.auth=None + def logout_url(self, next="/"): + current.session.dropbox_request_token = None + current.session.auth = None redirect('https://www.dropbox.com/logout') return next - def put(self,filename,file): - return json.loads(self.client.put_file(filename,file))['bytes'] - def get(self,filename,file): + + def put(self, filename, file): + return json.loads(self.client.put_file(filename, file))['bytes'] + + def get(self, filename, file): return self.client.get_file(filename) - def dir(self,path): + + def dir(self, path): return json.loads(self.client.metadata(path)) -def use_dropbox(auth,filename='private/dropbox.key',**kwargs): - path = os.path.join(current.request.folder,filename) + +def use_dropbox(auth, filename='private/dropbox.key', **kwargs): + path = os.path.join(current.request.folder, filename) if os.path.exists(path): request = current.request - key,secret,access_type = open(path,'r').read().strip().split(':') + key, secret, access_type = open(path, 'r').read().strip().split(':') host = current.request.env.http_host login_url = "http://%s/%s/default/user/login" % \ - (host,request.application) + (host, request.application) auth.settings.actions_disabled = \ - ['register','change_password','request_reset_password'] + ['register', 'change_password', 'request_reset_password'] auth.settings.login_form = DropboxAccount( - request,key=key,secret=secret,access_type=access_type, - login_url = login_url,**kwargs) - + request, key=key, secret=secret, access_type=access_type, + login_url=login_url, **kwargs) diff --git a/gluon/contrib/login_methods/email_auth.py b/gluon/contrib/login_methods/email_auth.py index a53011a2..840fc0c3 100644 --- a/gluon/contrib/login_methods/email_auth.py +++ b/gluon/contrib/login_methods/email_auth.py @@ -1,6 +1,7 @@ import smtplib import logging + def email_auth(server="smtp.gmail.com:587", domain="@gmail.com", tls_mode=None): @@ -17,9 +18,9 @@ def email_auth(server="smtp.gmail.com:587", domain=domain, tls_mode=tls_mode): if domain: - if not isinstance(domain,(list,tuple)): - domain=[str(domain)] - if not [d for d in domain if email[-len(d):]==d]: + if not isinstance(domain, (list, tuple)): + domain = [str(domain)] + if not [d for d in domain if email[-len(d):] == d]: return False (host, port) = server.split(':') if tls_mode is None: # then auto detect @@ -43,4 +44,3 @@ def email_auth(server="smtp.gmail.com:587", pass return False return email_auth_aux - diff --git a/gluon/contrib/login_methods/extended_login_form.py b/gluon/contrib/login_methods/extended_login_form.py index 059d4816..6946531d 100644 --- a/gluon/contrib/login_methods/extended_login_form.py +++ b/gluon/contrib/login_methods/extended_login_form.py @@ -8,6 +8,7 @@ So user can choose the built-in login or extended login methods. from gluon import current, DIV + class ExtendedLoginForm(object): """ Put extended_login_form under web2py/gluon/contrib/login_methods folder. @@ -22,7 +23,8 @@ class ExtendedLoginForm(object): api_key="...", domain="...", url = "http://localhost:8000/%s/default/user/login" % request.application) - extended_login_form = ExtendedLoginForm(auth, alt_login_form, signals=['token']) + extended_login_form = ExtendedLoginForm( + auth, alt_login_form, signals=['token']) auth.settings.login_form = extended_login_form @@ -37,7 +39,7 @@ class ExtendedLoginForm(object): auth, alt_login_form, signals=[], - login_arg = 'login' + login_arg='login' ): self.auth = auth self.alt_login_form = alt_login_form @@ -50,7 +52,7 @@ class ExtendedLoginForm(object): """ if hasattr(self.alt_login_form, 'get_user'): return self.alt_login_form.get_user() - return None # let gluon.tools.Auth.get_or_create_user do the rest + return None # let gluon.tools.Auth.get_or_create_user do the rest def login_url(self, next): """ @@ -91,8 +93,8 @@ class ExtendedLoginForm(object): args = request.args if (self.signals and - any([True for signal in self.signals if request.vars.has_key(signal)]) - ): + any([True for signal in self.signals if signal in request.vars]) + ): return self.alt_login_form.login_form() self.auth.settings.login_form = self.auth @@ -101,5 +103,3 @@ class ExtendedLoginForm(object): form.components.append(self.alt_login_form.login_form()) return form - - diff --git a/gluon/contrib/login_methods/gae_google_account.py b/gluon/contrib/login_methods/gae_google_account.py index 9559b42a..49b435b0 100644 --- a/gluon/contrib/login_methods/gae_google_account.py +++ b/gluon/contrib/login_methods/gae_google_account.py @@ -11,6 +11,7 @@ Thanks to Hans Donner for GaeGoogleAccount. from google.appengine.api import users + class GaeGoogleAccount(object): """ Login will be done via Google's Appengine login object, instead of web2py's @@ -35,5 +36,3 @@ class GaeGoogleAccount(object): if user: return dict(nickname=user.nickname(), email=user.email(), user_id=user.user_id(), source="google account") - - diff --git a/gluon/contrib/login_methods/ldap_auth.py b/gluon/contrib/login_methods/ldap_auth.py index 2eb76bcf..a0574318 100644 --- a/gluon/contrib/login_methods/ldap_auth.py +++ b/gluon/contrib/login_methods/ldap_auth.py @@ -188,18 +188,23 @@ def ldap_auth(server='ldap', port=None, str(custom_scope), str(manage_groups))) if manage_user: if user_firstname_attrib.count(':') > 0: - (user_firstname_attrib, user_firstname_part) = user_firstname_attrib.split(':', 1) + (user_firstname_attrib, + user_firstname_part) = user_firstname_attrib.split(':', 1) user_firstname_part = (int(user_firstname_part) - 1) else: user_firstname_part = None if user_lastname_attrib.count(':') > 0: - (user_lastname_attrib, user_lastname_part) = user_lastname_attrib.split(':', 1) + (user_lastname_attrib, + user_lastname_part) = user_lastname_attrib.split(':', 1) user_lastname_part = (int(user_lastname_part) - 1) else: user_lastname_part = None - user_firstname_attrib = ldap.filter.escape_filter_chars(user_firstname_attrib) - user_lastname_attrib = ldap.filter.escape_filter_chars(user_lastname_attrib) - user_mail_attrib = ldap.filter.escape_filter_chars(user_mail_attrib) + user_firstname_attrib = ldap.filter.escape_filter_chars( + user_firstname_attrib) + user_lastname_attrib = ldap.filter.escape_filter_chars( + user_lastname_attrib) + user_mail_attrib = ldap.filter.escape_filter_chars( + user_mail_attrib) try: if allowed_groups: if not is_user_in_allowed_groups(username, password): @@ -310,7 +315,8 @@ def ldap_auth(server='ldap', port=None, basedns = ldap_basedn else: basedns = [ldap_basedn] - filter = '(&(uid=%s)(%s))' % (ldap.filter.escape_filter_chars(username), filterstr) + filter = '(&(uid=%s)(%s))' % ( + ldap.filter.escape_filter_chars(username), filterstr) found = False for basedn in basedns: try: @@ -338,7 +344,8 @@ def ldap_auth(server='ldap', port=None, else: basedns = [ldap_basedn] filter = '(&(%s=%s)(%s))' % (username_attrib, - ldap.filter.escape_filter_chars(username), + ldap.filter.escape_filter_chars( + username), filterstr) if custom_scope == 'subtree': ldap_scope = ldap.SCOPE_SUBTREE @@ -368,14 +375,16 @@ def ldap_auth(server='ldap', port=None, logger.info('[%s] Manage user data' % str(username)) try: if user_firstname_part is not None: - store_user_firstname = result[user_firstname_attrib][0].split(' ', 1)[user_firstname_part] + store_user_firstname = result[user_firstname_attrib][ + 0].split(' ', 1)[user_firstname_part] else: store_user_firstname = result[user_firstname_attrib][0] except KeyError, e: store_user_firstname = None try: if user_lastname_part is not None: - store_user_lastname = result[user_lastname_attrib][0].split(' ', 1)[user_lastname_part] + store_user_lastname = result[user_lastname_attrib][ + 0].split(' ', 1)[user_lastname_part] else: store_user_lastname = result[user_lastname_attrib][0] except KeyError, e: @@ -464,16 +473,19 @@ def ldap_auth(server='ldap', port=None, # # Get all group name where the user is in actually in ldap # ######################################################### - ldap_groups_of_the_user = get_user_groups_from_ldap(username, password) + ldap_groups_of_the_user = get_user_groups_from_ldap( + username, password) # # Get all group name where the user is in actually in local db # ############################################################# try: - db_user_id = db(db.auth_user.username == username).select(db.auth_user.id).first().id + db_user_id = db(db.auth_user.username == username).select( + db.auth_user.id).first().id except: try: - db_user_id = db(db.auth_user.email == username).select(db.auth_user.id).first().id + db_user_id = db(db.auth_user.email == username).select( + db.auth_user.id).first().id except AttributeError, e: # # There is no user in local db @@ -486,7 +498,8 @@ def ldap_auth(server='ldap', port=None, db_user_id = db.auth_user.insert(email=username, first_name=username) if not db_user_id: - logging.error('There is no username or email for %s!' % username) + logging.error( + 'There is no username or email for %s!' % username) raise db_group_search = db((db.auth_membership.user_id == db_user_id) & (db.auth_user.id == db.auth_membership.user_id) & @@ -520,7 +533,8 @@ def ldap_auth(server='ldap', port=None, gid = db.auth_group.insert(role=group_to_add, description='Generated from LDAP') else: - gid = db(db.auth_group.role == group_to_add).select(db.auth_group.id).first().id + gid = db(db.auth_group.role == group_to_add).select( + db.auth_group.id).first().id db.auth_membership.insert(user_id=db_user_id, group_id=gid) except: @@ -634,4 +648,3 @@ def ldap_auth(server='ldap', port=None, if filterstr[0] == '(' and filterstr[-1] == ')': # rfc4515 syntax filterstr = filterstr[1:-1] # parens added again where used return ldap_auth_aux - diff --git a/gluon/contrib/login_methods/linkedin_account.py b/gluon/contrib/login_methods/linkedin_account.py index f16deadd..4376a628 100644 --- a/gluon/contrib/login_methods/linkedin_account.py +++ b/gluon/contrib/login_methods/linkedin_account.py @@ -14,7 +14,8 @@ from gluon.http import HTTP try: import linkedin except ImportError: - raise HTTP(400,"linkedin module not found") + raise HTTP(400, "linkedin module not found") + class LinkedInAccount(object): """ @@ -28,9 +29,9 @@ class LinkedInAccount(object): """ - def __init__(self,request,key,secret,return_url): + def __init__(self, request, key, secret, return_url): self.request = request - self.api = linkedin.LinkedIn(key,secret,return_url) + self.api = linkedin.LinkedIn(key, secret, return_url) self.token = result = self.api.requestToken() def login_url(self, next="/"): @@ -40,13 +41,12 @@ class LinkedInAccount(object): return '' def get_user(self): - result = self.request.vars.verifier and self.api.accessToken(verifier = self.request.vars.verifier ) + result = self.request.vars.verifier and self.api.accessToken( + verifier=self.request.vars.verifier) if result: profile = self.api.GetProfile() - profile = self.api.GetProfile(profile).public_url = "http://www.linkedin.com/in/ozgurv" - return dict(first_name = profile.first_name, - last_name = profile.last_name, - username = profile.id) - - - + profile = self.api.GetProfile( + profile).public_url = "http://www.linkedin.com/in/ozgurv" + return dict(first_name=profile.first_name, + last_name=profile.last_name, + username=profile.id) diff --git a/gluon/contrib/login_methods/loginza.py b/gluon/contrib/login_methods/loginza.py index a54e3ca9..9c688f77 100644 --- a/gluon/contrib/login_methods/loginza.py +++ b/gluon/contrib/login_methods/loginza.py @@ -13,6 +13,7 @@ from gluon.tools import fetch from gluon.storage import Storage import gluon.contrib.simplejson as json + class Loginza(object): """ @@ -23,13 +24,13 @@ class Loginza(object): def __init__(self, request, - url = "", - embed = True, - auth_url = "http://loginza.ru/api/authinfo", - language = "en", - prompt = "loginza", - on_login_failure = None, - ): + url="", + embed=True, + auth_url="http://loginza.ru/api/authinfo", + language="en", + prompt="loginza", + on_login_failure=None, + ): self.request = request self.token_url = url @@ -46,49 +47,50 @@ class Loginza(object): # FIXME: what if email is unique=True self.mappings["http://twitter.com/"] = lambda profile:\ - dict(registration_id = profile.get("identity",""), - username = profile.get("nickname",""), - email = profile.get("email",""), - last_name = profile.get("name","").get("full_name",""), + dict(registration_id=profile.get("identity", ""), + username=profile.get("nickname", ""), + email=profile.get("email", ""), + last_name=profile.get("name", "").get("full_name", ""), #avatar = profile.get("photo",""), - ) + ) self.mappings["https://www.google.com/accounts/o8/ud"] = lambda profile:\ - dict(registration_id = profile.get("identity",""), - username = profile.get("name","").get("full_name",""), - email = profile.get("email",""), - first_name = profile.get("name","").get("first_name",""), - last_name = profile.get("name","").get("last_name",""), + dict(registration_id=profile.get("identity", ""), + username=profile.get("name", "").get("full_name", ""), + email=profile.get("email", ""), + first_name=profile.get("name", "").get("first_name", ""), + last_name=profile.get("name", "").get("last_name", ""), #avatar = profile.get("photo",""), - ) + ) self.mappings["http://vkontakte.ru/"] = lambda profile:\ - dict(registration_id=profile.get("identity",""), - username = profile.get("name","").get("full_name",""), - email = profile.get("email",""), - first_name = profile.get("name","").get("first_name",""), - last_name = profile.get("name","").get("last_name",""), + dict(registration_id=profile.get("identity", ""), + username=profile.get("name", "").get("full_name", ""), + email=profile.get("email", ""), + first_name=profile.get("name", "").get("first_name", ""), + last_name=profile.get("name", "").get("last_name", ""), #avatar = profile.get("photo",""), - ) + ) self.mappings.default = lambda profile:\ - dict(registration_id = profile.get("identity",""), - username = profile.get("name","").get("full_name"), - email = profile.get("email",""), - first_name = profile.get("name","").get("first_name",""), - last_name = profile.get("name","").get("last_name",""), + dict(registration_id=profile.get("identity", ""), + username=profile.get("name", "").get("full_name"), + email=profile.get("email", ""), + first_name=profile.get("name", "").get("first_name", ""), + last_name=profile.get("name", "").get("last_name", ""), #avatar = profile.get("photo",""), - ) + ) def get_user(self): request = self.request if request.vars.token: user = Storage() - data = urllib.urlencode(dict(token = request.vars.token)) - auth_info_json = fetch(self.auth_url+'?'+data) + data = urllib.urlencode(dict(token=request.vars.token)) + auth_info_json = fetch(self.auth_url + '?' + data) #print auth_info_json auth_info = json.loads(auth_info_json) - if auth_info["identity"] != None: + if auth_info["identity"] is not None: self.profile = auth_info provider = self.profile["provider"] - user = self.mappings.get(provider, self.mappings.default)(self.profile) + user = self.mappings.get( + provider, self.mappings.default)(self.profile) #user["password"] = ??? #user["avatar"] = ??? return user @@ -106,8 +108,8 @@ class Loginza(object): _frameborder="no", _style="width:359px;height:300px;") else: - form = DIV(A(self.prompt, _href=LOGINZA_URL % (self.language, self.token_url), _class="loginza"), - SCRIPT(_src="https://s3-eu-west-1.amazonaws.com/s1.loginza.ru/js/widget.js", _type="text/javascript")) + form = DIV( + A(self.prompt, _href=LOGINZA_URL % ( + self.language, self.token_url), _class="loginza"), + SCRIPT(_src="https://s3-eu-west-1.amazonaws.com/s1.loginza.ru/js/widget.js", _type="text/javascript")) return form - - diff --git a/gluon/contrib/login_methods/motp_auth.py b/gluon/contrib/login_methods/motp_auth.py index 8d2bfa70..aad9d27b 100644 --- a/gluon/contrib/login_methods/motp_auth.py +++ b/gluon/contrib/login_methods/motp_auth.py @@ -4,6 +4,7 @@ import time from hashlib import md5 from gluon.dal import DAL + def motp_auth(db=DAL('sqlite://storage.sqlite'), time_offset=60): @@ -44,7 +45,8 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'), writable=False, readable=False, default='')) ##validators - custom_auth_table = db[auth.settings.table_user_name] # get the custom_auth_table + custom_auth_table = db[auth.settings.table_user_name] + # get the custom_auth_table custom_auth_table.first_name.requires = \ IS_NOT_EMPTY(error_message=auth.messages.is_empty) custom_auth_table.last_name.requires = \ @@ -76,14 +78,15 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'), - as of now user field is hardcoded to email. Some way of selecting user table and user field. """ - def verify_otp(otp,pin,secret,offset=60): + def verify_otp(otp, pin, secret, offset=60): epoch_time = int(time.time()) time_start = int(str(epoch_time - offset)[:-1]) time_end = int(str(epoch_time + offset)[:-1]) - for t in range(time_start-1,time_end+1): - to_hash = str(t)+secret+pin + for t in range(time_start - 1, time_end + 1): + to_hash = str(t) + secret + pin hash = md5(to_hash).hexdigest()[:6] - if otp == hash: return True + if otp == hash: + return True return False def motp_auth_aux(email, @@ -91,15 +94,18 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'), db=db, offset=time_offset): if db: - user_data = db(db.auth_user.email == email ).select().first() + user_data = db(db.auth_user.email == email).select().first() if user_data: if user_data['motp_secret'] and user_data['motp_pin']: motp_secret = user_data['motp_secret'] motp_pin = user_data['motp_pin'] - otp_check = verify_otp(password,motp_pin,motp_secret,offset=offset) - if otp_check: return True - else: return False - else: return False + otp_check = verify_otp( + password, motp_pin, motp_secret, offset=offset) + if otp_check: + return True + else: + return False + else: + return False return False return motp_auth_aux - diff --git a/gluon/contrib/login_methods/oauth10a_account.py b/gluon/contrib/login_methods/oauth10a_account.py index 534a7582..1f464f8c 100644 --- a/gluon/contrib/login_methods/oauth10a_account.py +++ b/gluon/contrib/login_methods/oauth10a_account.py @@ -19,6 +19,7 @@ from urllib2 import urlopen import urllib2 from urllib import urlencode + class OAuthAccount(object): """ Login will be done via OAuth Framework, instead of web2py's @@ -51,7 +52,8 @@ class OAuthAccount(object): TOKEN_URL="..." ACCESS_TOKEN_URL="..." from gluon.contrib.login_methods.oauth10a_account import OAuthAccount - auth.settings.login_form=OAuthAccount(globals(),CLIENT_ID,CLIENT_SECRET, AUTH_URL, TOKEN_URL, ACCESS_TOKEN_URL) + auth.settings.login_form=OAuthAccount(globals( + ),CLIENT_ID,CLIENT_SECRET, AUTH_URL, TOKEN_URL, ACCESS_TOKEN_URL) """ @@ -61,20 +63,20 @@ class OAuthAccount(object): Appends the _next action to the generated url so the flows continues. """ r = self.request - http_host=r.env.http_x_forwarded_for - if not http_host: http_host=r.env.http_host + http_host = r.env.http_x_forwarded_for + if not http_host: + http_host = r.env.http_host url_scheme = r.env.wsgi_url_scheme if next: path_info = next else: path_info = r.env.path_info - uri = '%s://%s%s' %(url_scheme, http_host, path_info) + uri = '%s://%s%s' % (url_scheme, http_host, path_info) if r.get_vars and not next: uri += '?' + urlencode(r.get_vars) return uri - def accessToken(self): """Return the access token generated by the authenticating server. @@ -97,12 +99,11 @@ class OAuthAccount(object): token.set_verifier(self.request.vars.oauth_verifier) client = oauth.Client(self.consumer, token) - resp, content = client.request(self.access_token_url, "POST") if str(resp['status']) != '200': self.session.request_token = None - self.globals['redirect'](self.globals['URL'](f='user',args='logout')) - + self.globals['redirect'](self.globals[ + 'URL'](f='user', args='logout')) self.session.access_token = oauth.Token.from_string(content) @@ -111,7 +112,7 @@ class OAuthAccount(object): self.session.access_token = None return None - def __init__(self, g, client_id, client_secret, auth_url, token_url, access_token_url): + def __init__(self, g, client_id, client_secret, auth_url, token_url, access_token_url): self.globals = g self.client_id = client_id self.client_secret = client_secret @@ -125,7 +126,6 @@ class OAuthAccount(object): # consumer init self.consumer = oauth.Consumer(self.client_id, self.client_secret) - def login_url(self, next="/"): self.__oauth_login(next) return next @@ -142,7 +142,7 @@ class OAuthAccount(object): is, this function must be implemented for the specific provider. ''' - raise NotImplementedError, "Must override get_user()" + raise NotImplementedError("Must override get_user()") def __oauth_login(self, next): '''This method redirects the user to the authenticating form @@ -163,10 +163,11 @@ class OAuthAccount(object): # putting it in the body seems to work. callback_url = self.__redirect_uri(next) data = urlencode(dict(oauth_callback=callback_url)) - resp, content = client.request(self.token_url, "POST", body=data) + resp, content = client.request(self.token_url, "POST", body=data) if resp['status'] != '200': self.session.request_token = None - self.globals['redirect'](self.globals['URL'](f='user',args='logout')) + self.globals['redirect'](self.globals[ + 'URL'](f='user', args='logout')) # Store the request token in session. request_token = self.session.request_token = oauth.Token.from_string(content) @@ -174,18 +175,12 @@ class OAuthAccount(object): # Redirect the user to the authentication URL and pass the callback url. data = urlencode(dict(oauth_token=request_token.key, oauth_callback=callback_url)) - auth_request_url = self.auth_url + '?' +data - + auth_request_url = self.auth_url + '?' + data HTTP = self.globals['HTTP'] - raise HTTP(307, "You are not authenticated: you are being redirected to the authentication server", Location=auth_request_url) return None - - - - diff --git a/gluon/contrib/login_methods/oauth20_account.py b/gluon/contrib/login_methods/oauth20_account.py index e3d38321..0ed3adb2 100644 --- a/gluon/contrib/login_methods/oauth20_account.py +++ b/gluon/contrib/login_methods/oauth20_account.py @@ -17,6 +17,7 @@ import urllib2 from urllib import urlencode from gluon import current, redirect, HTTP + class OAuthAccount(object): """ Login will be done via OAuth Framework, instead of web2py's @@ -84,7 +85,8 @@ class OAuthAccount(object): username = user['id']) - auth.settings.actions_disabled=['register','change_password','request_reset_password','profile'] + auth.settings.actions_disabled=['register', + 'change_password','request_reset_password','profile'] auth.settings.login_form=FaceBookAccount() Any optional arg in the constructor will be passed asis to remote @@ -99,8 +101,9 @@ server for requests. It can be used for the optional"scope" parameters for Face """ r = current.request - http_host=r.env.http_x_forwarded_for - if not http_host: http_host=r.env.http_host + http_host = r.env.http_x_forwarded_for + if not http_host: + http_host = r.env.http_host url_scheme = r.env.wsgi_url_scheme if next: @@ -112,7 +115,6 @@ server for requests. It can be used for the optional"scope" parameters for Face uri += '?' + urlencode(r.get_vars) return uri - def __build_url_opener(self, uri): """ Build the url opener for managing HTTP Basic Athentication @@ -128,7 +130,6 @@ server for requests. It can be used for the optional"scope" parameters for Face opener = urllib2.build_opener(auth_handler) return opener - def accessToken(self): """ Return the access token generated by the authenticating server. @@ -137,7 +138,7 @@ server for requests. It can be used for the optional"scope" parameters for Face Otherwise the token is fetched from the auth server. """ - if current.session.token and current.session.token.has_key('expires'): + if current.session.token and 'expires' in current.session.token: expires = current.session.token['expires'] # reuse token until expiration if expires == 0 or expires > time.time(): @@ -159,19 +160,19 @@ server for requests. It can be used for the optional"scope" parameters for Face print tmp raise Exception(tmp) finally: - del current.session.code # throw it away + del current.session.code # throw it away if open_url: try: data = open_url.read() tokendata = cgi.parse_qs(data) current.session.token = \ - dict([(k,v[-1]) for k,v in tokendata.items()]) + dict([(k, v[-1]) for k, v in tokendata.items()]) # set expiration absolute time try to avoid broken # implementations where "expires_in" becomes "expires" - if current.session.token.has_key('expires_in'): + if 'expires_in' in current.session.token: exps = 'expires_in' - elif current.session.token.has_key('expires'): + elif 'expires' in current.session.token: exps = 'expires' else: exps = None @@ -217,11 +218,12 @@ server for requests. It can be used for the optional"scope" parameters for Face Override this method by sublcassing the class. """ - if not current.session.token: return None - return dict(first_name = 'Pinco', - last_name = 'Pallino', - username = 'pincopallino') - raise NotImplementedError, "Must override get_user()" + if not current.session.token: + return None + return dict(first_name='Pinco', + last_name='Pallino', + username='pincopallino') + raise NotImplementedError("Must override get_user()") # Following code is never executed. It can be used as example # for overriding in subclasses. @@ -239,10 +241,9 @@ server for requests. It can be used for the optional"scope" parameters for Face self.graph = None if user: - return dict(first_name = user['first_name'], - last_name = user['last_name'], - username = user['id']) - + return dict(first_name=user['first_name'], + last_name=user['last_name'], + username=user['id']) def __oauth_login(self, next): """ @@ -258,13 +259,13 @@ server for requests. It can be used for the optional"scope" parameters for Face if not self.accessToken(): if not current.request.vars.code: - current.session.redirect_uri=self.__redirect_uri(next) + current.session.redirect_uri = self.__redirect_uri(next) data = dict(redirect_uri=current.session.redirect_uri, - response_type='code', - client_id=self.client_id) + response_type='code', + client_id=self.client_id) if self.args: data.update(self.args) - auth_request_url = self.auth_url + "?" +urlencode(data) + auth_request_url = self.auth_url + "?" + urlencode(data) raise HTTP(307, "You are not authenticated: you are being redirected to the authentication server", Location=auth_request_url) @@ -273,5 +274,3 @@ server for requests. It can be used for the optional"scope" parameters for Face self.accessToken() return current.session.code return None - - diff --git a/gluon/contrib/login_methods/openid_auth.py b/gluon/contrib/login_methods/openid_auth.py index a2c22633..4e977030 100644 --- a/gluon/contrib/login_methods/openid_auth.py +++ b/gluon/contrib/login_methods/openid_auth.py @@ -49,6 +49,7 @@ except ImportError, err: DEFAULT = lambda: None + class OpenIDAuth(object): """ OpenIDAuth @@ -94,7 +95,7 @@ class OpenIDAuth(object): if not auth.settings.table_user: raise self.table_user = self.auth.settings.table_user - self.openid_expiration = 15 #minutes + self.openid_expiration = 15 # minutes self.messages = self._define_messages() @@ -116,7 +117,7 @@ class OpenIDAuth(object): messages.flash_openid_associated = 'OpenID associated' messages.flash_associate_openid = 'Please login or register an account for this OpenID.' messages.p_openid_not_registered = "This Open ID haven't be registered. " \ - + "Please login to associate with it or register an account for it." + + "Please login to associate with it or register an account for it." messages.flash_openid_authenticated = 'OpenID authenticated successfully.' messages.flash_openid_fail_authentication = 'OpenID authentication failed. (Error message: %s)' messages.flash_openid_canceled = 'OpenID authentication canceled by user.' @@ -158,7 +159,7 @@ class OpenIDAuth(object): and not processed yet. Else return the OpenID form for login. """ request = current.request - if request.vars.has_key('janrain_nonce') and not self._processed(): + if 'janrain_nonce' in request.vars and not self._processed(): self._process_response() return self.auth() return self._form() @@ -172,12 +173,12 @@ class OpenIDAuth(object): args = request.args if args[0] == 'logout': - return True # Let logout_url got called + return True # Let logout_url got called if current.session.w2popenid: w2popenid = current.session.w2popenid db = self.db - if (w2popenid.ok is True and w2popenid.oid): # OpenID authenticated + if (w2popenid.ok is True and w2popenid.oid): # OpenID authenticated if self._w2popenid_expired(w2popenid): del(current.session.w2popenid) flash = self.messages.flash_openid_expired @@ -196,22 +197,23 @@ class OpenIDAuth(object): if current.session.w2popenid: del(current.session.w2popenid) current.session.flash = self.messages.flash_openid_associated - if request.vars.has_key(nextvar): + if nextvar in request.vars: redirect(request.vars[nextvar]) redirect(self.auth.settings.login_next) - if not request.vars.has_key(nextvar): + if nextvar not in request.vars: # no next var, add it and do login again # so if user login or register can go back here to associate the OpenID redirect(URL(r=request, - args=['login'], - vars={nextvar:self.login_url})) + args=['login'], + vars={nextvar: self.login_url})) self.login_form = self._form_with_notification() current.session.flash = self.messages.flash_associate_openid - return None # need to login or register to associate this openid + return None # need to login or register to associate this openid # Get existed OpenID user - user = db(self.table_user.id==alt_login.user).select().first() + user = db( + self.table_user.id == alt_login.user).select().first() if user: if current.session.w2popenid: del(current.session.w2popenid) @@ -219,16 +221,17 @@ class OpenIDAuth(object): username = 'username' elif 'email' in self.table_user.fields(): username = 'email' - return {username: user[username]} if user else None # login success (almost) + return {username: user[username]} if user else None # login success (almost) - return None # just start to login + return None # just start to login def _find_matched_openid(self, db, oid, type_='openid'): """ Get the matched OpenID for given """ - query = ((db.alt_logins.username == oid) & (db.alt_logins.type == type_)) - alt_login = db(query).select().first() # Get the OpenID record + query = ( + (db.alt_logins.username == oid) & (db.alt_logins.type == type_)) + alt_login = db(query).select().first() # Get the OpenID record return alt_login def _associate_user_openid(self, user, oid): @@ -275,7 +278,6 @@ class OpenIDAuth(object): self.db) return self.consumerhelper - def _form(self, style=None): form = DIV(H3(self.messages.h_openid_login), self._login_form(style)) return form @@ -300,7 +302,7 @@ background-color: transparent; padding-left: 18px; width: 400px; """ - style = style.replace("\n","") + style = style.replace("\n", "") request = current.request session = current.session @@ -308,21 +310,25 @@ width: 400px; hidden_next_input = "" if _next == 'profile': profile_url = URL(r=request, f='user', args=['profile']) - hidden_next_input = INPUT(_type="hidden", _name="_next", _value=profile_url) - form = FORM(openid_field_label or self.messages.label_alt_login_username, - INPUT(_type="input", _name="oid", - requires=IS_NOT_EMPTY(error_message=messages.openid_fail_discover), - _style=style), - hidden_next_input, - INPUT(_type="submit", _value=submit_button or messages.submit_button), - " ", - A(messages.comment_openid_signin, - _href=messages.comment_openid_help_url, - _title=messages.comment_openid_help_title, - _class='openid-identifier', - _target="_blank"), - _action=self.login_url - ) + hidden_next_input = INPUT( + _type="hidden", _name="_next", _value=profile_url) + form = FORM( + openid_field_label or self.messages.label_alt_login_username, + INPUT(_type="input", _name="oid", + requires=IS_NOT_EMPTY( + error_message=messages.openid_fail_discover), + _style=style), + hidden_next_input, + INPUT(_type="submit", + _value=submit_button or messages.submit_button), + " ", + A(messages.comment_openid_signin, + _href=messages.comment_openid_help_url, + _title=messages.comment_openid_help_title, + _class='openid-identifier', + _target="_blank"), + _action=self.login_url + ) if form.accepts(request.vars, session): oid = request.vars.oid consumerhelper = self._init_consumerhelper() @@ -332,8 +338,9 @@ width: 400px; warning_openid_fail(session) redirect(url) try: - if request.vars.has_key('_next'): - return_to_url = self.return_to_url + '?_next=' + request.vars._next + if '_next' in request.vars: + return_to_url = self.return_to_url + \ + '?_next=' + request.vars._next url = consumerhelper.begin(oid, self.realm, return_to_url) except DiscoveryFailure: warning_openid_fail(session) @@ -353,7 +360,8 @@ width: 400px; """ Set expiration for OpenID authentication. """ - w2popenid.expiration = datetime.now() + timedelta(minutes=self.openid_expiration) + w2popenid.expiration = datetime.now( + ) + timedelta(minutes=self.openid_expiration) def _w2popenid_expired(self, w2popenid): """ @@ -369,7 +377,8 @@ width: 400px; request = current.request request_vars = request.vars consumerhelper = self._init_consumerhelper() - process_status = consumerhelper.process_response(request_vars, self.return_to_url) + process_status = consumerhelper.process_response( + request_vars, self.return_to_url) if process_status == "success": w2popenid = current.session.w2popenid user_data = self.consumerhelper.sreg() @@ -388,7 +397,7 @@ width: 400px; def list_user_openids(self): messages = self.messages request = current.request - if request.vars.has_key('delete_openid'): + if 'delete_openid' in request.vars: self.remove_openid(request.vars.delete_openid) query = self.db.alt_logins.user == self.auth.user.id @@ -397,8 +406,8 @@ width: 400px; for alt_login in alt_logins: username = alt_login.username delete_href = URL(r=request, f='user', - args=['profile'], - vars={'delete_openid': username}) + args=['profile'], + vars={'delete_openid': username}) delete_link = A(messages.a_delete, _href=delete_href) l.append(LI(username, " ", delete_link)) @@ -409,23 +418,23 @@ width: 400px; _next='profile', submit_button=messages.submit_button_add, openid_field_label=messages.label_add_alt_login_username) - ) + ) return openid_list - def remove_openid(self, openid): query = self.db.alt_logins.username == openid self.db(query).delete() + class ConsumerHelper(object): """ ConsumerHelper knows the python-openid and """ def __init__(self, session, db): - self.session = session - store = self._init_store(db) - self.consumer = openid.consumer.consumer.Consumer(session, store) + self.session = session + store = self._init_store(db) + self.consumer = openid.consumer.consumer.Consumer(session, store) def _init_store(self, db): """ @@ -434,7 +443,7 @@ class ConsumerHelper(object): if not hasattr(self, "store"): store = Web2pyStore(db) session = self.session - if not session.has_key('w2popenid'): + if 'w2popenid' not in session: session.w2popenid = Storage() self.store = store return self.store @@ -446,7 +455,7 @@ class ConsumerHelper(object): w2popenid = self.session.w2popenid w2popenid.oid = oid auth_req = self.consumer.begin(oid) - auth_req.addExtension(SRegRequest(required=['email','nickname'])) + auth_req.addExtension(SRegRequest(required=['email', 'nickname'])) url = auth_req.redirectURL(return_to=return_to_url, realm=realm) return url @@ -504,19 +513,27 @@ class Web2pyStore(OpenIDStore): if self.table_oid_associations_name not in self.database: self.database.define_table(self.table_oid_associations_name, - Field('server_url', 'string', length=2047, required=True), - Field('handle', 'string', length=255, required=True), - Field('secret', 'blob', required=True), - Field('issued', 'integer', required=True), - Field('lifetime', 'integer', required=True), - Field('assoc_type', 'string', length=64, required=True) - ) + Field('server_url', + 'string', length=2047, required=True), + Field('handle', + 'string', length=255, required=True), + Field('secret', 'blob', required=True), + Field('issued', + 'integer', required=True), + Field('lifetime', + 'integer', required=True), + Field('assoc_type', + 'string', length=64, required=True) + ) if self.table_oid_nonces_name not in self.database: self.database.define_table(self.table_oid_nonces_name, - Field('server_url', 'string', length=2047, required=True), - Field('timestamp', 'integer', required=True), - Field('salt', 'string', length=40, required=True) - ) + Field('server_url', + 'string', length=2047, required=True), + Field('timestamp', + 'integer', required=True), + Field('salt', 'string', + length=40, required=True) + ) def storeAssociation(self, server_url, association): """ @@ -525,14 +542,15 @@ class Web2pyStore(OpenIDStore): """ db = self.database - query = (db.oid_associations.server_url == server_url) & (db.oid_associations.handle == association.handle) + query = (db.oid_associations.server_url == server_url) & ( + db.oid_associations.handle == association.handle) db(query).delete() - db.oid_associations.insert(server_url = server_url, - handle = association.handle, - secret = association.secret, - issued = association.issued, - lifetime = association.lifetime, - assoc_type = association.assoc_type), 'insert '*10 + db.oid_associations.insert(server_url=server_url, + handle=association.handle, + secret=association.secret, + issued=association.issued, + lifetime=association.lifetime, + assoc_type=association.assoc_type), 'insert ' * 10 def getAssociation(self, server_url, handle=None): """ @@ -550,7 +568,8 @@ class Web2pyStore(OpenIDStore): if len(keep_assoc) == 0: return None else: - assoc = keep_assoc.pop() # pop the last one as it should be the latest one + assoc = keep_assoc.pop( + ) # pop the last one as it should be the latest one return Association(assoc['handle'], assoc['secret'], assoc['issued'], @@ -559,8 +578,9 @@ class Web2pyStore(OpenIDStore): def removeAssociation(self, server_url, handle): db = self.database - query = (db.oid_associations.server_url == server_url) & (db.oid_associations.handle == handle) - return db(query).delete() != None + query = (db.oid_associations.server_url == server_url) & ( + db.oid_associations.handle == handle) + return db(query).delete() is not None def useNonce(self, server_url, timestamp, salt): """ @@ -575,10 +595,10 @@ class Web2pyStore(OpenIDStore): if db(query).count() > 0: return False else: - db.oid_nonces.insert(server_url = server_url, - timestamp = timestamp, - salt = salt) - return True + db.oid_nonces.insert(server_url=server_url, + timestamp=timestamp, + salt=salt) + return True def _removeExpiredAssocations(self, rows): """ @@ -599,7 +619,7 @@ class Web2pyStore(OpenIDStore): keep_assoc.append(r) for r in remove_assoc: del db.oid_associations[r['id']] - return (keep_assoc, len(remove_assoc)) # return tuple (list of valid associations, number of deleted associations) + return (keep_assoc, len(remove_assoc)) # return tuple (list of valid associations, number of deleted associations) def cleanupNonces(self): """ @@ -619,7 +639,7 @@ class Web2pyStore(OpenIDStore): db = self.database query = (db.oid_associations.id > 0) - return self._removeExpiredAssocations(db(query).select())[1] #return number of assoc removed + return self._removeExpiredAssocations(db(query).select())[1] # return number of assoc removed def cleanup(self): """ @@ -628,6 +648,3 @@ class Web2pyStore(OpenIDStore): """ return self.cleanupNonces(), self.cleanupAssociations() - - - diff --git a/gluon/contrib/login_methods/pam_auth.py b/gluon/contrib/login_methods/pam_auth.py index 31c343e0..03564bf2 100644 --- a/gluon/contrib/login_methods/pam_auth.py +++ b/gluon/contrib/login_methods/pam_auth.py @@ -1,5 +1,6 @@ from gluon.contrib.pam import authenticate + def pam_auth(): """ to use pam_login: @@ -19,5 +20,3 @@ def pam_auth(): return authenticate(username, password) return pam_auth_aux - - diff --git a/gluon/contrib/login_methods/rpx_account.py b/gluon/contrib/login_methods/rpx_account.py index 34688a69..0cd1c56a 100644 --- a/gluon/contrib/login_methods/rpx_account.py +++ b/gluon/contrib/login_methods/rpx_account.py @@ -19,11 +19,13 @@ from gluon.tools import fetch from gluon.storage import Storage import gluon.contrib.simplejson as json + class RPXAccount(object): """ from gluon.contrib.login_methods.rpx_account import RPXAccount - auth.settings.actions_disabled=['register','change_password','request_reset_password'] + auth.settings.actions_disabled=['register','change_password', + 'request_reset_password'] auth.settings.login_form = RPXAccount(request, api_key="...", domain="...", @@ -32,18 +34,18 @@ class RPXAccount(object): def __init__(self, request, - api_key = "", - domain = "", - url = "", - embed = True, - auth_url = "https://rpxnow.com/api/v2/auth_info", - language= "en", + api_key="", + domain="", + url="", + embed=True, + auth_url="https://rpxnow.com/api/v2/auth_info", + language="en", prompt='rpx', - on_login_failure = None, + on_login_failure=None, ): - self.request=request - self.api_key=api_key + self.request = request + self.api_key = api_key self.embed = embed self.auth_url = auth_url self.domain = domain @@ -54,38 +56,40 @@ class RPXAccount(object): self.on_login_failure = on_login_failure self.mappings = Storage() - dn = {'givenName':'','familyName':''} + dn = {'givenName': '', 'familyName': ''} self.mappings.Facebook = lambda profile, dn=dn:\ - dict(registration_id = profile.get("identifier",""), - username = profile.get("preferredUsername",""), - email = profile.get("email",""), - first_name = profile.get("name",dn).get("givenName",""), - last_name = profile.get("name",dn).get("familyName","")) + dict(registration_id=profile.get("identifier", ""), + username=profile.get("preferredUsername", ""), + email=profile.get("email", ""), + first_name=profile.get("name", dn).get("givenName", ""), + last_name=profile.get("name", dn).get("familyName", "")) self.mappings.Google = lambda profile, dn=dn:\ - dict(registration_id=profile.get("identifier",""), - username=profile.get("preferredUsername",""), - email=profile.get("email",""), - first_name=profile.get("name",dn).get("givenName",""), - last_name=profile.get("name",dn).get("familyName","")) + dict(registration_id=profile.get("identifier", ""), + username=profile.get("preferredUsername", ""), + email=profile.get("email", ""), + first_name=profile.get("name", dn).get("givenName", ""), + last_name=profile.get("name", dn).get("familyName", "")) self.mappings.default = lambda profile:\ - dict(registration_id=profile.get("identifier",""), - username=profile.get("preferredUsername",""), - email=profile.get("email",""), - first_name=profile.get("preferredUsername",""), + dict(registration_id=profile.get("identifier", ""), + username=profile.get("preferredUsername", ""), + email=profile.get("email", ""), + first_name=profile.get("preferredUsername", ""), last_name='') def get_user(self): request = self.request if request.vars.token: user = Storage() - data = urllib.urlencode(dict(apiKey = self.api_key, token=request.vars.token)) - auth_info_json = fetch(self.auth_url+'?'+data) + data = urllib.urlencode( + dict(apiKey=self.api_key, token=request.vars.token)) + auth_info_json = fetch(self.auth_url + '?' + data) auth_info = json.loads(auth_info_json) if auth_info['stat'] == 'ok': self.profile = auth_info['profile'] - provider = re.sub('[^\w\-]','',self.profile['providerName']) - user = self.mappings.get(provider,self.mappings.default)(self.profile) + provider = re.sub('[^\w\-]', '', self.profile['providerName']) + user = self.mappings.get( + provider, self.mappings.default)(self.profile) return user elif self.on_login_failure: redirect(self.on_login_failure) @@ -95,12 +99,14 @@ class RPXAccount(object): request = self.request args = request.args if self.embed: - JANRAIN_URL = \ - "https://%s.rpxnow.com/openid/embed?token_url=%s&language_preference=%s" - rpxform = IFRAME(_src=JANRAIN_URL % (self.domain,self.token_url,self.language), - _scrolling="no", - _frameborder="no", - _style="width:400px;height:240px;") + JANRAIN_URL = \ + "https://%s.rpxnow.com/openid/embed?token_url=%s&language_preference=%s" + rpxform = IFRAME( + _src=JANRAIN_URL % ( + self.domain, self.token_url, self.language), + _scrolling="no", + _frameborder="no", + _style="width:400px;height:240px;") else: JANRAIN_URL = \ "https://%s.rpxnow.com/openid/v2/signin?token_url=%s" @@ -114,15 +120,15 @@ class RPXAccount(object): _type="text/javascript")) return rpxform -def use_janrain(auth,filename='private/janrain.key',**kwargs): - path = os.path.join(current.request.folder,filename) + +def use_janrain(auth, filename='private/janrain.key', **kwargs): + path = os.path.join(current.request.folder, filename) if os.path.exists(path): request = current.request - domain,key = open(path,'r').read().strip().split(':') + domain, key = open(path, 'r').read().strip().split(':') host = current.request.env.http_host url = URL('default', 'user', args='login', scheme=True) auth.settings.actions_disabled = \ - ['register','change_password','request_reset_password'] + ['register', 'change_password', 'request_reset_password'] auth.settings.login_form = RPXAccount( - request, api_key=key,domain=domain, url = url,**kwargs) - + request, api_key=key, domain=domain, url=url, **kwargs) diff --git a/gluon/contrib/login_methods/x509_auth.py b/gluon/contrib/login_methods/x509_auth.py index 36db643d..7dbb9a64 100644 --- a/gluon/contrib/login_methods/x509_auth.py +++ b/gluon/contrib/login_methods/x509_auth.py @@ -11,13 +11,12 @@ Adds support for x509 authentication. from gluon.globals import current from gluon.storage import Storage -from gluon.http import HTTP,redirect +from gluon.http import HTTP, redirect #requires M2Crypto from M2Crypto import X509 - class X509Auth(object): """ Login using x509 cert from client. @@ -29,8 +28,6 @@ class X509Auth(object): """ - - def __init__(self): self.request = current.request self.ssl_client_raw_cert = self.request.env.ssl_client_raw_cert @@ -41,10 +38,11 @@ class X509Auth(object): if self.ssl_client_raw_cert: - x509=X509.load_cert_string(self.ssl_client_raw_cert, X509.FORMAT_PEM) + x509 = X509.load_cert_string( + self.ssl_client_raw_cert, X509.FORMAT_PEM) # extract it from the cert - self.serial = self.request.env.ssl_client_serial or ('%x' % x509.get_serial_number()).upper() - + self.serial = self.request.env.ssl_client_serial or ( + '%x' % x509.get_serial_number()).upper() subject = x509.get_subject() @@ -53,23 +51,17 @@ class X509Auth(object): # cn = self.subject.cn self.subject = Storage(filter(None, map(lambda x: - (x,map(lambda y: - y.get_data().as_text(), - subject.get_entries_by_nid(subject.nid[x]))), + (x, map(lambda y: + y.get_data( + ).as_text(), + subject.get_entries_by_nid(subject.nid[x]))), subject.nid.keys()))) - - def login_form(self, **args): - raise HTTP(403,'Login not allowed. No valid x509 crentials') - - + raise HTTP(403, 'Login not allowed. No valid x509 crentials') def login_url(self, next="/"): - raise HTTP(403,'Login not allowed. No valid x509 crentials') - - - + raise HTTP(403, 'Login not allowed. No valid x509 crentials') def logout_url(self, next="/"): return next @@ -86,10 +78,14 @@ class X509Auth(object): p = profile = dict() - username = p['username'] = reduce(lambda a,b: '%s | %s' % (a,b), self.subject.CN or self.subject.commonName) - p['first_name'] = reduce(lambda a,b: '%s | %s' % (a,b),self.subject.givenName or username) - p['last_name'] = reduce(lambda a,b: '%s | %s' % (a,b),self.subject.surname) - p['email'] = reduce(lambda a,b: '%s | %s' % (a,b),self.subject.Email or self.subject.emailAddress) + username = p['username'] = reduce(lambda a, b: '%s | %s' % ( + a, b), self.subject.CN or self.subject.commonName) + p['first_name'] = reduce(lambda a, b: '%s | %s' % (a, b), + self.subject.givenName or username) + p['last_name'] = reduce( + lambda a, b: '%s | %s' % (a, b), self.subject.surname) + p['email'] = reduce(lambda a, b: '%s | %s' % ( + a, b), self.subject.Email or self.subject.emailAddress) # IMPORTANT WE USE THE CERT SERIAL AS UNIQUE KEY FOR THE USER p['registration_id'] = self.serial @@ -100,6 +96,3 @@ class X509Auth(object): p['certificate'] = self.ssl_client_raw_cert return profile - - - diff --git a/gluon/contrib/memdb.py b/gluon/contrib/memdb.py index 527b051a..382739e8 100644 --- a/gluon/contrib/memdb.py +++ b/gluon/contrib/memdb.py @@ -44,22 +44,21 @@ SQL_DIALECTS = {'memcache': { 'is not null': 'IS NOT NULL', 'extract': None, 'left join': None, - }} +}} def cleanup(text): if re.compile('[^0-9a-zA-Z_]').findall(text): - raise SyntaxError, \ - 'Can\'t cleanup \'%s\': only [0-9a-zA-Z_] allowed in table and field names' % text + raise SyntaxError('Can\'t cleanup \'%s\': only [0-9a-zA-Z_] allowed in table and field names' % text) return text def assert_filter_fields(*fields): for field in fields: if isinstance(field, (Field, Expression)) and field.type\ - in ['text', 'blob']: - raise SyntaxError, 'AppEngine does not index by: %s'\ - % field.type + in ['text', 'blob']: + raise SyntaxError('AppEngine does not index by: %s' + % field.type) def dateobj_to_datetime(object): @@ -78,7 +77,7 @@ def dateobj_to_datetime(object): object.minute, object.second, object.microsecond, - ) + ) return object @@ -96,7 +95,7 @@ def sqlhtml_validators(field_type, length): 'time': validators.IS_TIME(), 'datetime': validators.IS_DATETIME(), 'reference': validators.IS_INT_IN_RANGE(0, 1e100), - } + } try: return v[field_type[:9]] except KeyError: @@ -114,7 +113,8 @@ class DALStorage(dict): def __setattr__(self, key, value): if key in self: - raise SyntaxError, 'Object \'%s\'exists and cannot be redefined' % key + raise SyntaxError( + 'Object \'%s\'exists and cannot be redefined' % key) self[key] = value def __repr__(self): @@ -151,14 +151,14 @@ class MEMDB(DALStorage): tablename, *fields, **args - ): + ): tablename = cleanup(tablename) if tablename in dir(self) or tablename[0] == '_': - raise SyntaxError, 'invalid table name: %s' % tablename + raise SyntaxError('invalid table name: %s' % tablename) if not tablename in self.tables: self.tables.append(tablename) else: - raise SyntaxError, 'table already defined: %s' % tablename + raise SyntaxError('table already defined: %s' % tablename) t = self[tablename] = Table(self, tablename, *fields) t._create() return t @@ -190,7 +190,7 @@ class Table(DALStorage): db, tablename, *fields - ): + ): self._db = db self._tablename = tablename self.fields = SQLCallableList() @@ -219,24 +219,24 @@ class Table(DALStorage): if field.type[:9] == 'reference': referenced = field.type[10:].strip() if not referenced: - raise SyntaxError, \ - 'Table %s: reference \'%s\' to nothing!' % (self._tablename, k) + raise SyntaxError('Table %s: reference \'%s\' to nothing!' % ( + self._tablename, k)) if not referenced in self._db: - raise SyntaxError, \ - 'Table: table %s does not exist' % referenced + raise SyntaxError( + 'Table: table %s does not exist' % referenced) referee = self._db[referenced] ftype = \ self._db._translator[field.type[:9]]( self._db[referenced]._tableobj) if self._tablename in referee.fields: # ## THIS IS OK - raise SyntaxError, \ - 'Field: table \'%s\' has same name as a field ' \ - 'in referenced table \'%s\'' % (self._tablename, referenced) + raise SyntaxError('Field: table \'%s\' has same name as a field ' + 'in referenced table \'%s\'' % ( + self._tablename, referenced)) self._db[referenced]._referenced_by.append((self._tablename, - field.name)) + field.name)) elif not field.type in self._db._translator\ - or not self._db._translator[field.type]: - raise SyntaxError, 'Field: unkown field type %s' % field.type + or not self._db._translator[field.type]: + raise SyntaxError('Field: unkown field type %s' % field.type) self._tableobj = self._db.client return None @@ -269,11 +269,11 @@ class Table(DALStorage): def update(self, id, **fields): for field in fields: if not field in fields and self[field].default\ - != None: + is not None: fields[field] = self[field].default if field in fields: fields[field] = obj_represent(fields[field], - self[field].type, self._db) + self[field].type, self._db) return self._tableobj.set(self._id_to_key(id), fields) def delete(self, id): @@ -293,7 +293,7 @@ class Table(DALStorage): if self._tableobj.set(shard_id, '0'): id = 0 else: - raise Exception, 'cannot set memcache' + raise Exception('cannot set memcache') return long(str(shard) + str(id)) def __str__(self): @@ -307,7 +307,7 @@ class Expression(object): name, type='string', db=None, - ): + ): (self.name, self.type, self._db) = (name, type, db) def __str__(self): @@ -393,11 +393,11 @@ class Field(Expression): notnull=False, unique=False, uploadfield=True, - ): + ): self.name = cleanup(fieldname) if fieldname in dir(Table) or fieldname[0] == '_': - raise SyntaxError, 'Field: invalid field name: %s' % fieldname + raise SyntaxError('Field: invalid field name: %s' % fieldname) if isinstance(type, Table): type = 'reference ' + type._tablename if not length: @@ -437,9 +437,9 @@ MEMDB.Field = Field # ## required by gluon/globals.py session.connect def obj_represent(object, fieldtype, db): - if object != None: + if object is not None: if fieldtype == 'date' and not isinstance(object, - datetime.date): + datetime.date): (y, m, d) = [int(x) for x in str(object).strip().split('-')] object = datetime.date(y, m, d) elif fieldtype == 'time' and not isinstance(object, datetime.time): @@ -450,7 +450,7 @@ def obj_represent(object, fieldtype, db): (h, mi, s) = time_items + [0] object = datetime.time(h, mi, s) elif fieldtype == 'datetime' and not isinstance(object, - datetime.datetime): + datetime.datetime): (y, m, d) = [int(x) for x in str(object)[:10].strip().split('-')] time_items = [int(x) for x in @@ -466,7 +466,7 @@ def obj_represent(object, fieldtype, db): h, mi, s, - ) + ) elif fieldtype == 'integer' and not isinstance(object, long): object = long(object) @@ -496,10 +496,10 @@ class Query(object): left, op=None, right=None, - ): + ): if isinstance(right, (Field, Expression)): - raise SyntaxError, \ - 'Query: right side of filter must be a value or entity' + raise SyntaxError( + 'Query: right side of filter must be a value or entity') if isinstance(left, Field) and left.name == 'id': if op == '=': self.get_one = \ @@ -507,8 +507,8 @@ class Query(object): id=long(right)) return else: - raise SyntaxError, 'only equality by id is supported' - raise SyntaxError, 'not supported' + raise SyntaxError('only equality by id is supported') + raise SyntaxError('not supported') def __str__(self): return str(self.left) @@ -539,7 +539,7 @@ class Set(object): self.where = where self._tables.insert(0, where.get_all) elif hasattr(where, 'get_one') and isinstance(where.get_one, - QueryException): + QueryException): self.where = where.get_one else: @@ -553,9 +553,8 @@ class Set(object): def __call__(self, where): if isinstance(self.where, QueryException) or isinstance(where, - QueryException): - raise SyntaxError, \ - 'neither self.where nor where can be a QueryException instance' + QueryException): + raise SyntaxError('neither self.where nor where can be a QueryException instance') if self.where: return Set(self._db, self.where & where) else: @@ -564,9 +563,9 @@ class Set(object): def _get_table_or_raise(self): tablenames = list(set(self._tables)) # unique if len(tablenames) < 1: - raise SyntaxError, 'Set: no tables selected' + raise SyntaxError('Set: no tables selected') if len(tablenames) > 1: - raise SyntaxError, 'Set: no join in appengine' + raise SyntaxError('Set: no join in appengine') return self._db[tablenames[0]]._tableobj def _getitem_exception(self): @@ -597,7 +596,7 @@ class Set(object): if isinstance(self.where, QueryException): return self._select_except() else: - raise SyntaxError, 'select arguments not supported' + raise SyntaxError('select arguments not supported') def count(self): return len(self.select()) @@ -609,7 +608,7 @@ class Set(object): return self._db[tablename].delete(id) else: - raise Exception, 'deletion not implemented' + raise Exception('deletion not implemented') def update(self, **update_fields): if isinstance(self.where, QueryException): @@ -620,7 +619,7 @@ class Set(object): setattr(item, key, value) self._db[tablename].update(id, **item) else: - raise Exception, 'update not implemented' + raise Exception('update not implemented') def update_record( @@ -628,7 +627,7 @@ def update_record( s, id, a, - ): +): item = s.get(id) for (key, value) in a.items(): t[key] = value @@ -650,7 +649,7 @@ class Rows(object): db, response, *colnames - ): + ): self._db = db self.colnames = colnames self.response = response @@ -660,9 +659,9 @@ class Rows(object): def __getitem__(self, i): if i >= len(self.response) or i < 0: - raise SyntaxError, 'Rows: no such row: %i' % i + raise SyntaxError('Rows: no such row: %i' % i) if len(self.response[0]) != len(self.colnames): - raise SyntaxError, 'Rows: internal error' + raise SyntaxError('Rows: internal error') row = DALStorage() for j in xrange(len(self.colnames)): value = self.response[i][j] @@ -684,7 +683,7 @@ class Rows(object): referee = field.type[10:].strip() rid = value row[tablename][fieldname] = rid - elif field.type == 'boolean' and value != None: + elif field.type == 'boolean' and value is not None: # row[tablename][fieldname]=Set(self._db[referee].id==rid) @@ -692,13 +691,13 @@ class Rows(object): row[tablename][fieldname] = True else: row[tablename][fieldname] = False - elif field.type == 'date' and value != None\ - and not isinstance(value, datetime.date): + elif field.type == 'date' and value is not None\ + and not isinstance(value, datetime.date): (y, m, d) = [int(x) for x in str(value).strip().split('-')] row[tablename][fieldname] = datetime.date(y, m, d) - elif field.type == 'time' and value != None\ - and not isinstance(value, datetime.time): + elif field.type == 'time' and value is not None\ + and not isinstance(value, datetime.time): time_items = [int(x) for x in str(value).strip().split(':')[:3]] if len(time_items) == 3: @@ -706,8 +705,8 @@ class Rows(object): else: (h, mi, s) = time_items + [0] row[tablename][fieldname] = datetime.time(h, mi, s) - elif field.type == 'datetime' and value != None\ - and not isinstance(value, datetime.datetime): + elif field.type == 'datetime' and value is not None\ + and not isinstance(value, datetime.datetime): (y, m, d) = [int(x) for x in str(value)[:10].strip().split('-')] time_items = [int(x) for x in @@ -723,19 +722,19 @@ class Rows(object): h, mi, s, - ) + ) else: row[tablename][fieldname] = value if fieldname == 'id': id = row[tablename].id row[tablename].update_record = lambda t = row[tablename], \ s = self._db[tablename], id = id, **a: update_record(t, - s, id, a) + s, id, a) for (referee_table, referee_name) in \ - table._referenced_by: + table._referenced_by: s = self._db[referee_table][referee_name] row[tablename][referee_table] = Set(self._db, s - == id) + == id) if len(row.keys()) == 1: return row[row.keys()[0]] return row diff --git a/gluon/contrib/minify/cssmin.py b/gluon/contrib/minify/cssmin.py index 0a8853cf..f9ed89f9 100644 --- a/gluon/contrib/minify/cssmin.py +++ b/gluon/contrib/minify/cssmin.py @@ -11,7 +11,7 @@ Modified for inclusion into web2py by: Ross Peoples """ -from StringIO import StringIO # The pure-Python StringIO supports unicode. +from StringIO import StringIO # The pure-Python StringIO supports unicode. import re @@ -20,7 +20,7 @@ __version__ = '0.1.4' def remove_comments(css): """Remove all CSS comment blocks.""" - + iemac = False preserve = False comment_start = css.find("/*") @@ -28,7 +28,7 @@ def remove_comments(css): # Preserve comments that look like `/*!...*/`. # Slicing is used to make sure we don"t get an IndexError. preserve = css[comment_start + 2:comment_start + 3] == "!" - + comment_end = css.find("*/", comment_start + 2) if comment_end < 0: if not preserve: @@ -48,22 +48,22 @@ def remove_comments(css): else: comment_start = comment_end + 2 comment_start = css.find("/*", comment_start) - + return css def remove_unnecessary_whitespace(css): """Remove unnecessary whitespace characters.""" - + def pseudoclasscolon(css): - + """ Prevents 'p :link' from becoming 'p:link'. - + Translates 'p :link' into 'p ___PSEUDOCLASSCOLON___link'; this is translated back again later. """ - + regex = re.compile(r"(^|\})(([^\{\:])+\:)+([^\{]*\{)") match = regex.search(css) while match: @@ -73,43 +73,43 @@ def remove_unnecessary_whitespace(css): css[match.end():]]) match = regex.search(css) return css - + css = pseudoclasscolon(css) # Remove spaces from before things. css = re.sub(r"\s+([!{};:>+\(\)\],])", r"\1", css) - + # If there is a `@charset`, then only allow one, and move to the beginning. css = re.sub(r"^(.*)(@charset \"[^\"]*\";)", r"\2\1", css) css = re.sub(r"^(\s*@charset [^;]+;\s*)+", r"\1", css) - + # Put the space back in for a few cases, such as `@media screen` and # `(-webkit-min-device-pixel-ratio:0)`. css = re.sub(r"\band\(", "and (", css) - + # Put the colons back. css = css.replace('___PSEUDOCLASSCOLON___', ':') - + # Remove spaces from after things. css = re.sub(r"([!{}:;>+\(\[,])\s+", r"\1", css) - + return css def remove_unnecessary_semicolons(css): """Remove unnecessary semicolons.""" - + return re.sub(r";+\}", "}", css) def remove_empty_rules(css): """Remove empty rules.""" - + return re.sub(r"[^\}\{]+\{\}", "", css) def normalize_rgb_colors_to_hex(css): """Convert `rgb(51,102,153)` to `#336699`.""" - + regex = re.compile(r"rgb\s*\(\s*([0-9,\s]+)\s*\)") match = regex.search(css) while match: @@ -122,39 +122,40 @@ def normalize_rgb_colors_to_hex(css): def condense_zero_units(css): """Replace `0(px, em, %, etc)` with `0`.""" - + return re.sub(r"([\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", r"\1\2", css) def condense_multidimensional_zeros(css): """Replace `:0 0 0 0;`, `:0 0 0;` etc. with `:0;`.""" - + css = css.replace(":0 0 0 0;", ":0;") css = css.replace(":0 0 0;", ":0;") css = css.replace(":0 0;", ":0;") - + # Revert `background-position:0;` to the valid `background-position:0 0;`. css = css.replace("background-position:0;", "background-position:0 0;") - + return css def condense_floating_points(css): """Replace `0.6` with `.6` where possible.""" - + return re.sub(r"(:|\s)0+\.(\d+)", r"\1.\2", css) def condense_hex_colors(css): """Shorten colors from #AABBCC to #ABC where possible.""" - + regex = re.compile(r"([^\"'=\s])(\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])") match = regex.search(css) while match: first = match.group(3) + match.group(5) + match.group(7) second = match.group(4) + match.group(6) + match.group(8) if first.lower() == second.lower(): - css = css.replace(match.group(), match.group(1) + match.group(2) + '#' + first) + css = css.replace( + match.group(), match.group(1) + match.group(2) + '#' + first) match = regex.search(css, match.end() - 3) else: match = regex.search(css, match.end()) @@ -163,19 +164,19 @@ def condense_hex_colors(css): def condense_whitespace(css): """Condense multiple adjacent whitespace characters into one.""" - + return re.sub(r"\s+", " ", css) def condense_semicolons(css): """Condense multiple adjacent semicolon characters into one.""" - + return re.sub(r";;+", ";", css) def wrap_css_lines(css, line_length): """Wrap the lines of the given CSS to an approximate length.""" - + lines = [] line_start = 0 for i, char in enumerate(css): @@ -183,7 +184,7 @@ def wrap_css_lines(css, line_length): if char == '}' and (i - line_start >= line_length): lines.append(css[line_start:i + 1]) line_start = i + 1 - + if line_start < len(css): lines.append(css[line_start:]) return '\n'.join(lines) @@ -212,19 +213,19 @@ def cssmin(css, wrap=None): def main(): import optparse import sys - + p = optparse.OptionParser( prog="cssmin", version=__version__, usage="%prog [--wrap N]", description="""Reads raw CSS from stdin, and writes compressed CSS to stdout.""") - + p.add_option( '-w', '--wrap', type='int', default=None, metavar='N', help="Wrap output to approximately N chars per line.") - + options, args = p.parse_args() sys.stdout.write(cssmin(sys.stdin.read(), wrap=options.wrap)) if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/gluon/contrib/minify/htmlmin.py b/gluon/contrib/minify/htmlmin.py index fbb3896b..ea4971ea 100644 --- a/gluon/contrib/minify/htmlmin.py +++ b/gluon/contrib/minify/htmlmin.py @@ -2,12 +2,14 @@ import re + def minify(response): - def _replace(match): - match = match.group() - # save whole
, |' % ('x'*512) ### trick IE
+                    body += '' % ('x' * 512)  # trick IE
                 headers['Content-Length'] = len(body)
         rheaders = []
         for k, v in headers.iteritems():
@@ -107,9 +108,9 @@ class HTTP(BaseException):
             elif not v is None:
                 rheaders.append((k, str(v)))
         responder(status, rheaders)
-        if env.get('request_method','')=='HEAD':
+        if env.get('request_method', '') == 'HEAD':
             return ['']
-        elif isinstance(body,str):
+        elif isinstance(body, str):
             return [body]
         elif hasattr(body, '__iter__'):
             return body
diff --git a/gluon/import_all.py b/gluon/import_all.py
index 35d09744..4f2ab8e1 100644
--- a/gluon/import_all.py
+++ b/gluon/import_all.py
@@ -69,10 +69,10 @@ base_modules = ['aifc', 'anydbm', 'array', 'asynchat', 'asyncore', 'atexit',
 contributed_modules = []
 for root, dirs, files in os.walk('gluon'):
     for candidate in ['.'.join(
-      os.path.join(root, os.path.splitext(name)[0]).split(os.sep))
-      for name in files if name.endswith('.py')
-        and root.split(os.sep) != ['gluon', 'tests']
-      ]:
+                      os.path.join(root, os.path.splitext(name)[0]).split(os.sep))
+                      for name in files if name.endswith('.py')
+                      and root.split(os.sep) != ['gluon', 'tests']
+                      ]:
         contributed_modules.append(candidate)
 
 # Python base version
@@ -86,7 +86,7 @@ alert_dependency = ['hashlib', 'uuid']
 #
 # List of modules deprecated in Python 2.6 or 2.7 that are in the above set
 py26_deprecated = ['mhlib', 'multifile', 'mimify', 'sets', 'MimeWriter']
-py27_deprecated = [] # ['optparse'] but we need it for now
+py27_deprecated = []  # ['optparse'] but we need it for now
 
 if python_version >= '2.6':
     base_modules += ['json', 'multiprocessing']
@@ -99,11 +99,11 @@ if python_version >= '2.7':
 # Now iterate in the base_modules, trying to do the import
 for module in base_modules + contributed_modules:
     try:
-         __import__(module, globals(), locals(), [])
+        __import__(module, globals(), locals(), [])
     except:
         # Raise an exception if the current module is a dependency
         if module in alert_dependency:
             msg = "Missing dependency: %(module)s\n" % locals()
             msg += "Try the following command: "
             msg += "easy_install-%(python_version)s -U %(module)s" % locals()
-            raise ImportError, msg
+            raise ImportError(msg)
diff --git a/gluon/languages.py b/gluon/languages.py
index 9a7617a7..7f25c5d8 100644
--- a/gluon/languages.py
+++ b/gluon/languages.py
@@ -48,16 +48,16 @@ DEFAULT_GET_PLURAL_ID = lambda n: 0
 # word is unchangeable
 DEFAULT_CONSTRUCT_PLURAL_FORM = lambda word, plural_id: word
 
-NUMBERS = (int,long,float)
+NUMBERS = (int, long, float)
 
 # pattern to find T(blah blah blah) expressions
 PY_STRING_LITERAL_RE = r'(?<=[^\w]T\()(?P'\
-     + r"[uU]?[rR]?(?:'''(?:[^']|'{1,2}(?!'))*''')|"\
-     + r"(?:'(?:[^'\\]|\\.)*')|" + r'(?:"""(?:[^"]|"{1,2}(?!"))*""")|'\
-     + r'(?:"(?:[^"\\]|\\.)*"))'
+    + r"[uU]?[rR]?(?:'''(?:[^']|'{1,2}(?!'))*''')|"\
+    + r"(?:'(?:[^'\\]|\\.)*')|" + r'(?:"""(?:[^"]|"{1,2}(?!"))*""")|'\
+    + r'(?:"(?:[^"\\]|\\.)*"))'
 
 regex_translate = re.compile(PY_STRING_LITERAL_RE, re.DOTALL)
-regex_param=re.compile(r'{(?P.+?)}')
+regex_param = re.compile(r'{(?P.+?)}')
 
 # pattern for a valid accept_language
 regex_language = \
@@ -66,33 +66,43 @@ regex_langfile = re.compile('^[a-z]{2}(-[a-z]{2})?\.py$')
 regex_backslash = re.compile(r"\\([\\{}%])")
 regex_plural = re.compile('%({.+?})')
 regex_plural_dict = re.compile('^{(?P[^()[\]][^()[\]]*?)\((?P[^()\[\]]+)\)}$')  # %%{word(varname or number)}
-regex_plural_tuple = re.compile('^{(?P[^[\]()]+)(?:\[(?P\d+)\])?}$') # %%{word[index]} or %%{word}
+regex_plural_tuple = re.compile(
+    '^{(?P[^[\]()]+)(?:\[(?P\d+)\])?}$')  # %%{word[index]} or %%{word}
 regex_plural_file = re.compile('^plural-[a-zA-Z]{2}(-[a-zA-Z]{2})?\.py$')
 
+
 def safe_eval(text):
     if text.strip():
         try:
             import ast
             return ast.literal_eval(text)
         except ImportError:
-            return eval(text,{},{})
+            return eval(text, {}, {})
     return None
 
 # used as default filter in translator.M()
+
+
 def markmin(s):
     def markmin_aux(m):
         return '{%s}' % markmin_escape(m.group('s'))
-    return render(regex_param.sub(markmin_aux,s),
+    return render(regex_param.sub(markmin_aux, s),
                   sep='br', autolinks=None, id_prefix='')
 
 # UTF8 helper functions
+
+
 def upper_fun(s):
-    return unicode(s,'utf-8').upper().encode('utf-8')
+    return unicode(s, 'utf-8').upper().encode('utf-8')
+
+
 def title_fun(s):
-    return unicode(s,'utf-8').title().encode('utf-8')
+    return unicode(s, 'utf-8').title().encode('utf-8')
+
+
 def cap_fun(s):
-    return unicode(s,'utf-8').capitalize().encode('utf-8')
-ttab_in  = maketrans("\\%{}", '\x1c\x1d\x1e\x1f')
+    return unicode(s, 'utf-8').capitalize().encode('utf-8')
+ttab_in = maketrans("\\%{}", '\x1c\x1d\x1e\x1f')
 ttab_out = maketrans('\x1c\x1d\x1e\x1f', "\\%{}")
 
 # cache of translated messages:
@@ -105,13 +115,14 @@ ttab_out = maketrans('\x1c\x1d\x1e\x1f', "\\%{}")
 #  ...
 # }
 
-global_language_cache={}
+global_language_cache = {}
+
 
 def get_from_cache(cache, val, fun):
     lang_dict, lock = cache
     lock.acquire()
     try:
-        result = lang_dict.get(val);
+        result = lang_dict.get(val)
     finally:
         lock.release()
     if result:
@@ -123,16 +134,18 @@ def get_from_cache(cache, val, fun):
         lock.release()
     return result
 
+
 def clear_cache(filename):
     cache = global_language_cache.setdefault(
         filename, ({}, allocate_lock()))
     lang_dict, lock = cache
     lock.acquire()
     try:
-        lang_dict.clear();
+        lang_dict.clear()
     finally:
         lock.release()
 
+
 def read_dict_aux(filename):
     lang_text = portalocker.read_locked(filename).replace('\r\n', '\n')
     clear_cache(filename)
@@ -141,13 +154,14 @@ def read_dict_aux(filename):
     except Exception, e:
         status = 'Syntax error in %s (%s)' % (filename, e)
         logging.error(status)
-        return {'__corrupted__':status}
+        return {'__corrupted__': status}
+
 
 def read_dict(filename):
     """ return dictionary with translation messages
     """
-    return getcfs('lang:'+filename, filename,
-                lambda: read_dict_aux(filename))
+    return getcfs('lang:' + filename, filename,
+                  lambda: read_dict_aux(filename))
 
 
 def read_possible_plural_rules():
@@ -159,17 +173,17 @@ def read_possible_plural_rules():
     try:
         import contrib.plural_rules as package
         for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
-            if len(modname)==2:
-                module = __import__(package.__name__+'.'+modname,
+            if len(modname) == 2:
+                module = __import__(package.__name__ + '.' + modname,
                                     fromlist=[modname])
                 lang = modname
-                pname = modname+'.py'
-                nplurals = getattr(module,'nplurals', DEFAULT_NPLURALS)
+                pname = modname + '.py'
+                nplurals = getattr(module, 'nplurals', DEFAULT_NPLURALS)
                 get_plural_id = getattr(
-                    module,'get_plural_id',
+                    module, 'get_plural_id',
                     DEFAULT_GET_PLURAL_ID)
                 construct_plural_form = getattr(
-                    module,'construct_plural_form',
+                    module, 'construct_plural_form',
                     DEFAULT_CONSTRUCT_PLURAL_FORM)
                 plurals[lang] = (lang, nplurals, get_plural_id,
                                  construct_plural_form)
@@ -179,6 +193,7 @@ def read_possible_plural_rules():
 
 PLURAL_RULES = read_possible_plural_rules()
 
+
 def read_possible_languages_aux(langdir):
     def get_lang_struct(lang, langcode, langname, langfile_mtime):
         if lang == 'default':
@@ -189,27 +204,28 @@ def read_possible_languages_aux(langdir):
          nplurals,
          get_plural_id,
          construct_plural_form
-        ) = PLURAL_RULES.get(real_lang[:2],('default',
-                                            DEFAULT_NPLURALS,
-                                            DEFAULT_GET_PLURAL_ID,
-                                            DEFAULT_CONSTRUCT_PLURAL_FORM))
+         ) = PLURAL_RULES.get(real_lang[:2], ('default',
+                                              DEFAULT_NPLURALS,
+                                              DEFAULT_GET_PLURAL_ID,
+                                              DEFAULT_CONSTRUCT_PLURAL_FORM))
         if prules_langcode != 'default':
             (pluraldict_fname,
              pluraldict_mtime) = plurals.get(real_lang,
-                                    plurals.get(real_lang[:2],
-                                        ('plural-%s.py'%real_lang,0)))
+                                             plurals.get(real_lang[:2],
+                                                         ('plural-%s.py' % real_lang, 0)))
         else:
             pluraldict_fname = None
             pluraldict_mtime = 0
         return (langcode,        # language code from !langcode!
-                langname,        # language name in national spelling from !langname!
+                langname,
+                # language name in national spelling from !langname!
                 langfile_mtime,  # m_time of language file
-                pluraldict_fname,# name of plural dictionary file or None (when default.py is not exist)
-                pluraldict_mtime,# m_time of plural dictionary file or 0 if file is not exist
-                prules_langcode, # code of plural rules language or 'default'
+                pluraldict_fname,  # name of plural dictionary file or None (when default.py is not exist)
+                pluraldict_mtime,  # m_time of plural dictionary file or 0 if file is not exist
+                prules_langcode,  # code of plural rules language or 'default'
                 nplurals,        # nplurals for current language
                 get_plural_id,   # get_plural_id() for current language
-                construct_plural_form) # construct_plural_form() for current language
+                construct_plural_form)  # construct_plural_form() for current language
 
     plurals = {}
     flist = oslistdir(langdir)
@@ -217,17 +233,17 @@ def read_possible_languages_aux(langdir):
     for pname in flist:
         if regex_plural_file.match(pname):
             plurals[pname[7:-3]] = (pname,
-                ostat(pjoin(langdir,pname)).st_mtime)
+                                    ostat(pjoin(langdir, pname)).st_mtime)
     langs = {}
     # scan languages directory for langfiles:
     for fname in flist:
         if regex_langfile.match(fname) or fname == 'default.py':
-            fname_with_path = pjoin(langdir,fname)
+            fname_with_path = pjoin(langdir, fname)
             d = read_dict(fname_with_path)
             lang = fname[:-3]
             langcode = d.get('!langcode!', lang if lang != 'default'
-                                              else DEFAULT_LANGUAGE)
-            langname = d.get('!langname!',langcode)
+                             else DEFAULT_LANGUAGE)
+            langname = d.get('!langname!', langcode)
             langfile_mtime = ostat(fname_with_path).st_mtime
             langs[lang] = get_lang_struct(lang, langcode,
                                           langname, langfile_mtime)
@@ -240,27 +256,31 @@ def read_possible_languages_aux(langdir):
     deflangcode = deflang[0]
     if deflangcode not in langs:
         # create language from default.py:
-        langs[deflangcode] = deflang[:2]+(0,)+deflang[3:]
+        langs[deflangcode] = deflang[:2] + (0,) + deflang[3:]
 
     return langs
 
+
 def read_possible_languages(appdir):
-    langdir = pjoin(appdir,'languages')
-    return getcfs('langs:'+langdir, langdir,
-                   lambda: read_possible_languages_aux(langdir))
+    langdir = pjoin(appdir, 'languages')
+    return getcfs('langs:' + langdir, langdir,
+                  lambda: read_possible_languages_aux(langdir))
+
 
 def read_plural_dict_aux(filename):
     lang_text = portalocker.read_locked(filename).replace('\r\n', '\n')
     try:
         return eval(lang_text) or {}
     except Exception, e:
-        status='Syntax error in %s (%s)' % (filename, e)
+        status = 'Syntax error in %s (%s)' % (filename, e)
         logging.error(status)
-        return {'__corrupted__':status}
+        return {'__corrupted__': status}
+
 
 def read_plural_dict(filename):
-    return getcfs('plurals:'+filename, filename,
-                      lambda: read_plural_dict_aux(filename))
+    return getcfs('plurals:' + filename, filename,
+                  lambda: read_plural_dict_aux(filename))
+
 
 def write_plural_dict(filename, contents):
     if '__corrupted__' in contents:
@@ -269,8 +289,9 @@ def write_plural_dict(filename, contents):
         fp = portalocker.LockedFile(filename, 'w')
         fp.write('#!/usr/bin/env python\n{\n# "singular form (0)": ["first plural form (1)", "second plural form (2)", ...],\n')
         # coding: utf8\n{\n')
-        for key in sorted(contents,lambda x,y: cmp(unicode(x,'utf-8').lower(), unicode(y,'utf-8').lower())):
-            forms = '['+','.join([repr(Utf8(form)) for form in contents[key]])+']'
+        for key in sorted(contents, lambda x, y: cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())):
+            forms = '[' + ','.join([repr(Utf8(form))
+                                   for form in contents[key]]) + ']'
             fp.write('%s: %s,\n' % (repr(Utf8(key)), forms))
         fp.write('}\n')
     except (IOError, OSError):
@@ -291,13 +312,12 @@ def write_dict(filename, contents):
             logging.warning('Unable to write to file %s' % filename)
         return
     fp.write('# coding: utf8\n{\n')
-    for key in sorted(contents,lambda x,y: cmp(unicode(x,'utf-8').lower(), unicode(y,'utf-8').lower())):
+    for key in sorted(contents, lambda x, y: cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())):
         fp.write('%s: %s,\n' % (repr(Utf8(key)), repr(Utf8(contents[key]))))
     fp.write('}\n')
     fp.close()
 
 
-
 class lazyT(object):
     """
     never to be called explicitly, returned by
@@ -309,12 +329,12 @@ class lazyT(object):
     def __init__(
         self,
         message,
-        symbols = {},
-        T = None,
-        filter = None,
-        ftag = None,
-        M = False
-        ):
+        symbols={},
+        T=None,
+        filter=None,
+        ftag=None,
+        M=False
+    ):
         if isinstance(message, lazyT):
             self.m = message.m
             self.s = message.s
@@ -354,7 +374,7 @@ class lazyT(object):
     def __mul__(self, other):
         return str(self) * other
 
-    def __cmp__(self,other):
+    def __cmp__(self, other):
         return cmp(str(self), str(other))
 
     def __hash__(self):
@@ -370,7 +390,8 @@ class lazyT(object):
         return str(self)[i:j]
 
     def __iter__(self):
-        for c in str(self): yield c
+        for c in str(self):
+            yield c
 
     def __len__(self):
         return len(str(self))
@@ -388,9 +409,11 @@ class lazyT(object):
         return str(self)
 
     def __mod__(self, symbols):
-        if self.is_copy: return lazyT(self)
+        if self.is_copy:
+            return lazyT(self)
         return lazyT(self.m, symbols, self.T, self.f, self.t, self.M)
 
+
 class translator(object):
     """
     this class is instantiated by gluon.compileapp.build_environment
@@ -459,7 +482,8 @@ class translator(object):
                returns dictionary with all possible languages:
             { langcode(from filename):
                 ( langcode,        # language code from !langcode!
-                  langname,        # language name in national spelling from !langname!
+                  langname,
+                      # language name in national spelling from !langname!
                   langfile_mtime,  # m_time of language file
                   pluraldict_fname,# name of plural dictionary file or None (when default.py is not exist)
                   pluraldict_mtime,# m_time of plural dictionary file or 0 if file is not exist
@@ -470,14 +494,15 @@ class translator(object):
             }
         """
         info = read_possible_languages(self.folder)
-        if lang: info = info.get(lang)
+        if lang:
+            info = info.get(lang)
         return info
 
     def get_possible_languages(self):
         """ get list of all possible languages for current applications """
         return list(set(self.current_languages +
-            [lang for lang in read_possible_languages(self.folder).iterkeys()
-                 if lang != 'default']))
+                        [lang for lang in read_possible_languages(self.folder).iterkeys()
+                         if lang != 'default']))
 
     def set_current_languages(self, *languages):
         """
@@ -486,12 +511,12 @@ class translator(object):
         turn translation off to use default language
         """
         if len(languages) == 1 and isinstance(
-            languages[0], (tuple, list)):
+                languages[0], (tuple, list)):
             languages = languages[0]
         if not languages or languages[0] is None:
             # set default language from default.py/DEFAULT_LANGUAGE
             pl_info = self.get_possible_languages_info('default')
-            if pl_info[2]==0: # langfile_mtime
+            if pl_info[2] == 0:  # langfile_mtime
                 # if languages/default.py is not found
                 self.default_language_file = self.langpath
                 self.default_t = {}
@@ -500,12 +525,11 @@ class translator(object):
                 self.default_language_file = pjoin(self.langpath,
                                                    'default.py')
                 self.default_t = read_dict(self.default_language_file)
-                self.current_languages = [pl_info[0]] # !langcode!
+                self.current_languages = [pl_info[0]]  # !langcode!
         else:
             self.current_languages = list(languages)
         self.force(self.http_accept_language)
 
-
     def plural(self, word, n):
         """ get plural form of word for number *n*
             NOTE: *word" MUST be defined in current language
@@ -529,14 +553,14 @@ class translator(object):
             # etc.
             if id != 0:
                 forms = self.plural_dict.get(word, [])
-                if len(forms)>=id:
+                if len(forms) >= id:
                     # have this plural form:
-                    return forms[id-1]
+                    return forms[id - 1]
                 else:
                     # guessing this plural form
-                    forms += ['']*(self.nplurals-len(forms)-1)
+                    forms += [''] * (self.nplurals - len(forms) - 1)
                     form = self.construct_plural_form(word, id)
-                    forms[id-1] = form
+                    forms[id - 1] = form
                     self.plural_dict[word] = forms
                     if self.is_writable and self.plural_file:
                         write_plural_dict(self.plural_file,
@@ -558,6 +582,7 @@ class translator(object):
         of them matches possible_languages.
         """
         pl_info = read_possible_languages(self.folder)
+
         def set_plural(language):
             """
             initialize plural forms subsystem
@@ -570,7 +595,7 @@ class translator(object):
                  self.nplurals,
                  self.get_plural_id,
                  self.construct_plural_form
-                ) = lang_info[3:]
+                 ) = lang_info[3:]
                 pdict = {}
                 if pname:
                     pname = pjoin(self.langpath, pname)
@@ -586,7 +611,7 @@ class translator(object):
                 self.plural_file = None
                 self.plural_dict = {}
         language = ''
-        if len(languages)==1 and isinstance(languages[0],str):
+        if len(languages) == 1 and isinstance(languages[0], str):
             languages = regex_language.findall(languages[0].lower())
         elif not languages or languages[0] is None:
             languages = []
@@ -594,7 +619,7 @@ class translator(object):
         if languages:
             all_languages = set(lang for lang in pl_info.iterkeys()
                                 if lang != 'default') \
-                                | set(self.current_languages)
+                | set(self.current_languages)
             for lang in languages:
                 # compare "aa-bb" | "aa" from *language* parameter
                 # with strings from langlist using such alghorythm:
@@ -604,20 +629,20 @@ class translator(object):
                     language = lang5
                 else:
                     lang2 = lang[:2]
-                    if len(lang5)>2 and lang2 in all_languages:
+                    if len(lang5) > 2 and lang2 in all_languages:
                         language = lang2
                     else:
                         for l in all_languages:
-                            if l[:2]==lang2:
+                            if l[:2] == lang2:
                                 language = l
                 if language:
                     if language in self.current_languages:
                         break
-                    self.language_file = pjoin(self.langpath, language+'.py')
+                    self.language_file = pjoin(self.langpath, language + '.py')
                     self.t = read_dict(self.language_file)
                     self.cache = global_language_cache.setdefault(
-                                         self.language_file,
-                                         ({},allocate_lock()))
+                        self.language_file,
+                        ({}, allocate_lock()))
                     set_plural(language)
                     self.accepted_language = language
                     return languages
@@ -637,7 +662,7 @@ class translator(object):
         if lazy is None:
             lazy = self.lazy
         if not language:
-            if lazy :
+            if lazy:
                 return lazyT(message, symbols, self)
             else:
                 return self.translate(message, symbols)
@@ -654,18 +679,18 @@ class translator(object):
             s = self.get_t(message, prefix)
             return filter(s) if filter else self.filter(s)
         if filter:
-            prefix = '@'+(ftag or 'userdef')+'\x01'
+            prefix = '@' + (ftag or 'userdef') + '\x01'
         else:
-            prefix = '@'+self.ftag+'\x01'
+            prefix = '@' + self.ftag + '\x01'
         message = get_from_cache(
-            self.cache, prefix+message,
+            self.cache, prefix + message,
             lambda: get_tr(message, prefix, filter))
         if symbols or symbols == 0 or symbols == "":
             if isinstance(symbols, dict):
                 symbols.update(
                     (key, xmlescape(value).translate(ttab_in))
                     for key, value in symbols.iteritems()
-                    if not isinstance(value, NUMBERS) )
+                    if not isinstance(value, NUMBERS))
             else:
                 if not isinstance(symbols, tuple):
                     symbols = (symbols,)
@@ -686,7 +711,7 @@ class translator(object):
             lazy = self.lazy
         if not language:
             if lazy:
-                return lazyT(message, symbols, self, filter, ftag,  True)
+                return lazyT(message, symbols, self, filter, ftag, True)
             else:
                 return self.apply_filter(message, symbols, filter, ftag)
         else:
@@ -714,11 +739,12 @@ class translator(object):
             message = message.encode('utf8')
         if isinstance(prefix, unicode):
             prefix = prefix.encode('utf8')
-        key = prefix+message
+        key = prefix + message
         mt = self.t.get(key, None)
-        if mt is not None: return mt
+        if mt is not None:
+            return mt
         # we did not find a translation
-        if message.find('##')>0 and not '\n' in message:
+        if message.find('##') > 0 and not '\n' in message:
             # remove comments
             message = message.rsplit('##', 1)[0]
         # guess translation same as original
@@ -756,7 +782,7 @@ class translator(object):
                     word, !word, !!word, !!!word, ?word?number, ??number, ?number
                     ?word?word[number], ?word?[number], ??word[number]
                 """
-                w,i = m.group('w','i')
+                w, i = m.group('w', 'i')
                 c = w[0]
                 if c not in '!?':
                     return self.plural(w, symbols[int(i or 0)])
@@ -764,15 +790,17 @@ class translator(object):
                     (p1, sep, p2) = w[1:].partition("?")
                     part1 = p1 if sep else ""
                     (part2, sep, part3) = (p2 if sep else p1).partition("?")
-                    if not sep: part3 = part2
+                    if not sep:
+                        part3 = part2
                     if i is None:
-                       # ?[word]?number[?number] or ?number
-                       if not part2: return m.group(0)
-                       num = int(part2)
+                        # ?[word]?number[?number] or ?number
+                        if not part2:
+                            return m.group(0)
+                        num = int(part2)
                     else:
-                       # ?[word]?word2[?word3][number]
-                       num = int(symbols[int(i or 0)])
-                    return part1 if num==1 else part3 if num==0 else part2
+                        # ?[word]?word2[?word3][number]
+                        num = int(symbols[int(i or 0)])
+                    return part1 if num == 1 else part3 if num == 0 else part2
                 elif w.startswith('!!!'):
                     word = w[3:]
                     fun = upper_fun
@@ -783,7 +811,7 @@ class translator(object):
                     word = w[1:]
                     fun = cap_fun
                 if i is not None:
-                   return fun(self.plural(word, symbols[int(i)]))
+                    return fun(self.plural(word, symbols[int(i)]))
                 return fun(word)
 
             def sub_dict(m):
@@ -792,7 +820,7 @@ class translator(object):
                     ?word2(var), ?word1?word2(var), ?word1?word2?word0(var)
                     ?word2(num), ?word1?word2(num), ?word1?word2?word0(num)
                 """
-                w,n = m.group('w','n')
+                w, n = m.group('w', 'n')
                 c = w[0]
                 n = int(n) if n.isdigit() else symbols[n]
                 if c not in '!?':
@@ -802,9 +830,10 @@ class translator(object):
                     (p1, sep, p2) = w[1:].partition("?")
                     part1 = p1 if sep else ""
                     (part2, sep, part3) = (p2 if sep else p1).partition("?")
-                    if not sep: part3 = part2
+                    if not sep:
+                        part3 = part2
                     num = int(n)
-                    return part1 if num==1 else part3 if num==0 else part2
+                    return part1 if num == 1 else part3 if num == 0 else part2
                 elif w.startswith('!!!'):
                     word = w[3:]
                     fun = upper_fun
@@ -824,7 +853,7 @@ class translator(object):
                     return m.group(0)
             return part
         message = message % symbols
-        message = regex_plural.sub(sub_plural, message )
+        message = regex_plural.sub(sub_plural, message)
         return message
 
     def translate(self, message, symbols):
@@ -838,7 +867,7 @@ class translator(object):
                 symbols.update(
                     (key, str(value).translate(ttab_in))
                     for key, value in symbols.iteritems()
-                    if not isinstance(value, NUMBERS) )
+                    if not isinstance(value, NUMBERS))
             else:
                 if not isinstance(symbols, tuple):
                     symbols = (symbols,)
@@ -849,6 +878,7 @@ class translator(object):
             message = self.params_substitution(message, symbols)
         return message.translate(ttab_out)
 
+
 def findT(path, language=DEFAULT_LANGUAGE):
     """
     must be run by the admin app
@@ -860,22 +890,22 @@ def findT(path, language=DEFAULT_LANGUAGE):
     vp = pjoin(path, 'views')
     mop = pjoin(path, 'modules')
     for filename in \
-            listdir(mp, '^.+\.py$', 0)+listdir(cp, '^.+\.py$', 0)\
-            +listdir(vp, '^.+\.html$', 0)+listdir(mop, '^.+\.py$', 0):
+            listdir(mp, '^.+\.py$', 0) + listdir(cp, '^.+\.py$', 0)\
+            + listdir(vp, '^.+\.html$', 0) + listdir(mop, '^.+\.py$', 0):
         data = portalocker.read_locked(filename)
         items = regex_translate.findall(data)
         for item in items:
             try:
                 message = safe_eval(item)
             except:
-                continue # silently ignore inproperly formatted strings
+                continue  # silently ignore inproperly formatted strings
             if not message.startswith('#') and not '\n' in message:
                 tokens = message.rsplit('##', 1)
             else:
                 # this allows markmin syntax in translations
                 tokens = [message]
             if len(tokens) == 2:
-                message = tokens[0].strip()+'##'+tokens[1].strip()
+                message = tokens[0].strip() + '##' + tokens[1].strip()
             if message and not message in sentences:
                 sentences[message] = message
     if not '!langcode!' in sentences:
@@ -888,8 +918,12 @@ def findT(path, language=DEFAULT_LANGUAGE):
     write_dict(lang_file, sentences)
 
 ### important to allow safe session.flash=T(....)
+
+
 def lazyT_unpickle(data):
     return marshal.loads(data)
+
+
 def lazyT_pickle(data):
     return lazyT_unpickle, (marshal.dumps(str(data)),)
 copy_reg.pickle(lazyT, lazyT_pickle, lazyT_unpickle)
diff --git a/gluon/main.py b/gluon/main.py
index 2aaaa5e3..4cc0f84f 100644
--- a/gluon/main.py
+++ b/gluon/main.py
@@ -51,7 +51,7 @@ from globals import current
 #  The two are identical unless web2py_path is changed via the web2py.py -f folder option
 #  main.web2py_path is the same as applications_parent (for backward compatibility)
 
-web2py_path = global_settings.applications_parent # backward compatibility
+web2py_path = global_settings.applications_parent  # backward compatibility
 
 create_missing_folders()
 
@@ -107,7 +107,7 @@ try:
     version_info.close()
     global_settings.web2py_version = parse_version(raw_version_string)
 except:
-    raise RuntimeError, "Cannot determine web2py version"
+    raise RuntimeError("Cannot determine web2py version")
 
 web2py_version = global_settings.web2py_version
 
@@ -119,7 +119,8 @@ except:
 
 load()
 
-HTTPS_SCHEMES = set(('https','HTTPS'))
+HTTPS_SCHEMES = set(('https', 'HTTPS'))
+
 
 def get_client(env):
     """
@@ -138,10 +139,11 @@ def get_client(env):
         else:
             client = '127.0.0.1'
     if not is_valid_ip_address(client):
-        raise HTTP(400,"Bad Request (request.client=%s)" % client)
+        raise HTTP(400, "Bad Request (request.client=%s)" % client)
     return client
 
-def copystream_progress(request, chunk_size= 10**5):
+
+def copystream_progress(request, chunk_size=10 ** 5):
     """
     copies request.env.wsgi_input into request.body
     and stores progress upload status in cache_ram
@@ -154,22 +156,22 @@ def copystream_progress(request, chunk_size= 10**5):
     try:
         size = int(env.content_length)
     except ValueError:
-        raise HTTP(400,"Invalid Content-Length header")
+        raise HTTP(400, "Invalid Content-Length header")
     dest = tempfile.TemporaryFile()
     if not 'X-Progress-ID' in request.vars:
         copystream(source, dest, size, chunk_size)
         return dest
-    cache_key = 'X-Progress-ID:'+request.vars['X-Progress-ID']
+    cache_key = 'X-Progress-ID:' + request.vars['X-Progress-ID']
     cache_ram = CacheInRam(request)  # same as cache.ram because meta_storage
-    cache_ram(cache_key+':length', lambda: size, 0)
-    cache_ram(cache_key+':uploaded', lambda: 0, 0)
+    cache_ram(cache_key + ':length', lambda: size, 0)
+    cache_ram(cache_key + ':uploaded', lambda: 0, 0)
     while size > 0:
         if size < chunk_size:
             data = source.read(size)
-            cache_ram.increment(cache_key+':uploaded', size)
+            cache_ram.increment(cache_key + ':uploaded', size)
         else:
             data = source.read(chunk_size)
-            cache_ram.increment(cache_key+':uploaded', chunk_size)
+            cache_ram.increment(cache_key + ':uploaded', chunk_size)
         length = len(data)
         if length > size:
             (data, length) = (data[:size], size)
@@ -180,8 +182,8 @@ def copystream_progress(request, chunk_size= 10**5):
         if length < chunk_size:
             break
     dest.seek(0)
-    cache_ram(cache_key+':length', None)
-    cache_ram(cache_key+':uploaded', None)
+    cache_ram(cache_key + ':length', None)
+    cache_ram(cache_key + ':uploaded', None)
     return dest
 
 
@@ -224,8 +226,9 @@ def serve_controller(request, response, session):
         page = response.body.getvalue()
     # logic to garbage collect after exec, not always, once every 100 requests
     global requests
-    requests = ('requests' in globals()) and (requests+1) % 100 or 0
-    if not requests: gc.collect()
+    requests = ('requests' in globals()) and (requests + 1) % 100 or 0
+    if not requests:
+        gc.collect()
     # end garbage collection logic
 
     # ##################################################
@@ -233,13 +236,14 @@ def serve_controller(request, response, session):
     # ##################################################
 
     default_headers = [
-        ('Content-Type', contenttype('.'+request.extension)),
-        ('Cache-Control','no-store, no-cache, must-revalidate, post-check=0, pre-check=0'),
+        ('Content-Type', contenttype('.' + request.extension)),
+        ('Cache-Control',
+         'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'),
         ('Expires', time.strftime('%a, %d %b %Y %H:%M:%S GMT',
                                   time.gmtime())),
         ('Pragma', 'no-cache')]
-    for key,value in default_headers:
-        response.headers.setdefault(key,value)
+    for key, value in default_headers:
+        response.headers.setdefault(key, value)
 
     raise HTTP(response.status, page, **response.headers)
 
@@ -253,9 +257,9 @@ def start_response_aux(status, headers, exc_info, response=None):
 
     to call third party WSGI applications
     """
-    response.status = str(status).split(' ',1)[0]
+    response.status = str(status).split(' ', 1)[0]
     response.headers = dict(headers)
-    return lambda *args, **kargs: response.write(escape=False,*args,**kargs)
+    return lambda *args, **kargs: response.write(escape=False, *args, **kargs)
 
 
 def middleware_aux(request, response, *middleware_apps):
@@ -270,24 +274,27 @@ def middleware_aux(request, response, *middleware_apps):
     def middleware(f):
         def app(environ, start_response):
             data = f()
-            start_response(response.status,response.headers.items())
-            if isinstance(data,list):
+            start_response(response.status, response.headers.items())
+            if isinstance(data, list):
                 return data
             return [data]
         for item in middleware_apps:
-            app=item(app)
+            app = item(app)
+
         def caller(app):
             wsgi = request.wsgi
             return app(wsgi.environ, wsgi.start_response)
         return lambda caller=caller, app=app: caller(app)
     return middleware
 
-def environ_aux(environ,request):
+
+def environ_aux(environ, request):
     new_environ = copy.copy(environ)
     new_environ['wsgi.input'] = request.body
     new_environ['wsgi.version'] = 1
     return new_environ
 
+
 def parse_get_post_vars(request, environ):
 
     # always parse variables in URL for GET, POST, PUT, DELETE, etc. in get_vars
@@ -307,22 +314,23 @@ def parse_get_post_vars(request, environ):
     try:
         request.body = body = copystream_progress(request)
     except IOError:
-        raise HTTP(400,"Bad Request - HTTP body is incomplete")
+        raise HTTP(400, "Bad Request - HTTP body is incomplete")
     if (body and env.request_method in ('POST', 'PUT', 'BOTH')):
-        dpost = cgi.FieldStorage(fp=body,environ=environ,keep_blank_values=1)
+        dpost = cgi.FieldStorage(fp=body, environ=environ, keep_blank_values=1)
         # The same detection used by FieldStorage to detect multipart POSTs
         is_multipart = dpost.type[:10] == 'multipart/'
         body.seek(0)
         isle25 = sys.version_info[1] <= 5
 
         def listify(a):
-            return (not isinstance(a,list) and [a]) or a
+            return (not isinstance(a, list) and [a]) or a
         try:
             keys = sorted(dpost)
         except TypeError:
             keys = []
         for key in keys:
-            if key is None: continue # not sure why cgi.FieldStorage returns None key
+            if key is None:
+                continue  # not sure why cgi.FieldStorage returns None key
             dpk = dpost[key]
             # if en element is not a file replace it with its value else leave it alone
             if isinstance(dpk, list):
@@ -347,7 +355,8 @@ def parse_get_post_vars(request, environ):
                     pvalue = pvalue[:-len(gvalue)]
             request.vars[key] = value
             if len(pvalue):
-                request.post_vars[key] = (len(pvalue)>1 and pvalue) or pvalue[0]
+                request.post_vars[key] = (len(pvalue) >
+                                          1 and pvalue) or pvalue[0]
 
 
 def wsgibase(environ, responder):
@@ -407,22 +416,23 @@ def wsgibase(environ, responder):
                 response.status = env.web2py_status_code or response.status
 
                 if static_file:
-                    if environ.get('QUERY_STRING','').startswith(
-                        'attachment'):
+                    if environ.get('QUERY_STRING', '').startswith(
+                            'attachment'):
                         response.headers['Content-Disposition'] \
                             = 'attachment'
                     if version:
                         response.headers['Cache-Control'] = 'max-age=315360000'
-                        response.headers['Expires'] = 'Thu, 31 Dec 2037 23:59:59 GMT'
+                        response.headers[
+                            'Expires'] = 'Thu, 31 Dec 2037 23:59:59 GMT'
                     response.stream(static_file, request=request)
 
                 # ##################################################
                 # fill in request items
                 # ##################################################
-                app = request.application ## must go after url_in!
+                app = request.application  # must go after url_in!
 
                 if not global_settings.local_hosts:
-                    local_hosts = ['127.0.0.1','::ffff:127.0.0.1']
+                    local_hosts = ['127.0.0.1', '::ffff:127.0.0.1']
                     if not global_settings.web2py_runtime_gae:
                         try:
                             local_hosts.append(socket.gethostname())
@@ -433,7 +443,7 @@ def wsgibase(environ, responder):
                                 local_hosts += [
                                     env.server_name,
                                     socket.gethostbyname(env.server_name)]
-                        except (socket.gaierror,TypeError):
+                        except (socket.gaierror, TypeError):
                             pass
                     global_settings.local_hosts = local_hosts
                 else:
@@ -442,15 +452,15 @@ def wsgibase(environ, responder):
                 x_req_with = str(env.http_x_requested_with).lower()
 
                 request.update(
-                    client = client,
-                    folder = abspath('applications',app) + os.sep,
-                    ajax = x_req_with == 'xmlhttprequest',
-                    cid = env.http_web2py_component_element,
-                    is_local = env.remote_addr in local_hosts,
-                    is_https = env.wsgi_url_scheme in HTTPS_SCHEMES \
-                        or request.env.http_x_forwarded_proto in HTTPS_SCHEMES \
-                        or env.https=='on')
-                request.uuid = request.compute_uuid() # requires client
+                    client=client,
+                    folder=abspath('applications', app) + os.sep,
+                    ajax=x_req_with == 'xmlhttprequest',
+                    cid=env.http_web2py_component_element,
+                    is_local=env.remote_addr in local_hosts,
+                    is_https=env.wsgi_url_scheme in HTTPS_SCHEMES
+                    or request.env.http_x_forwarded_proto in HTTPS_SCHEMES
+                    or env.https == 'on')
+                request.uuid = request.compute_uuid()  # requires client
                 request.url = environ['PATH_INFO']
 
                 # ##################################################
@@ -460,7 +470,7 @@ def wsgibase(environ, responder):
                 if not exists(request.folder):
                     if app == rwthread.routes.default_application \
                             and app != 'welcome':
-                        redirect(URL('welcome','default','index'))
+                        redirect(URL('welcome', 'default', 'index'))
                     elif rwthread.routes.error_handler:
                         _handler = rwthread.routes.error_handler
                         redirect(URL(_handler['application'],
@@ -468,11 +478,11 @@ def wsgibase(environ, responder):
                                      _handler['function'],
                                      args=app))
                     else:
-                        raise HTTP(404, rwthread.routes.error_message \
-                                       % 'invalid request',
+                        raise HTTP(404, rwthread.routes.error_message
+                                   % 'invalid request',
                                    web2py_error='invalid application')
                 elif not request.is_local and \
-                        exists(pjoin(request.folder,'DISABLED')):
+                        exists(pjoin(request.folder, 'DISABLED')):
                     raise HTTP(503, "

Temporarily down for maintenance

") # ################################################## @@ -491,13 +501,13 @@ def wsgibase(environ, responder): # expose wsgi hooks for convenience # ################################################## - request.wsgi.environ = environ_aux(environ,request) + request.wsgi.environ = environ_aux(environ, request) request.wsgi.start_response = \ lambda status='200', headers=[], \ exec_info=None, response=response: \ start_response_aux(status, headers, exec_info, response) request.wsgi.middleware = \ - lambda *a: middleware_aux(request,response,*a) + lambda *a: middleware_aux(request, response, *a) # ################################################## # load cookies @@ -507,7 +517,7 @@ def wsgibase(environ, responder): try: request.cookies.load(env.http_cookie) except Cookie.CookieError, e: - pass # invalid cookies + pass # invalid cookies # ################################################## # try load session or create new session file @@ -530,7 +540,7 @@ def wsgibase(environ, responder): except HTTP, http_response: if static_file: - return http_response.to(responder,env=env) + return http_response.to(responder, env=env) if request.body: request.body.close() @@ -562,9 +572,9 @@ def wsgibase(environ, responder): if request.cid: if response.flash: - http_response.headers['web2py-component-flash'] = urllib2.quote(xmlescape(response.flash).replace('\n','')) + http_response.headers['web2py-component-flash'] = urllib2.quote(xmlescape(response.flash).replace('\n', '')) if response.js: - http_response.headers['web2py-component-command'] = response.js.replace('\n','') + http_response.headers['web2py-component-command'] = response.js.replace('\n', '') # ################################################## # store cookies in headers @@ -576,7 +586,7 @@ def wsgibase(environ, responder): elif session._secure: rcookies[response.session_id_name]['secure'] = True http_response.cookies2headers(rcookies) - ticket=None + ticket = None except RestrictedError, e: @@ -594,8 +604,8 @@ def wsgibase(environ, responder): BaseAdapter.close_all_instances('rollback') http_response = \ - HTTP(500, rwthread.routes.error_message_ticket % \ - dict(ticket=ticket), + HTTP(500, rwthread.routes.error_message_ticket % + dict(ticket=ticket), web2py_error='ticket %s' % ticket) except: @@ -617,8 +627,8 @@ def wsgibase(environ, responder): e = RestrictedError('Framework', '', '', locals()) ticket = e.log(request) or 'unrecoverable' http_response = \ - HTTP(500, rwthread.routes.error_message_ticket \ - % dict(ticket=ticket), + HTTP(500, rwthread.routes.error_message_ticket + % dict(ticket=ticket), web2py_error='ticket %s' % ticket) finally: @@ -630,10 +640,10 @@ def wsgibase(environ, responder): http_response, new_environ = try_rewrite_on_error( http_response, request, environ, ticket) if not http_response: - return wsgibase(new_environ,responder) + return wsgibase(new_environ, responder) if global_settings.web2py_crontype == 'soft': newcron.softcron(global_settings.applications_parent).start() - return http_response.to(responder,env=env) + return http_response.to(responder, env=env) def save_password(password, port): @@ -712,14 +722,14 @@ def appfactory(wsgiapp=wsgibase, locker.acquire() cProfile.runctx('ret[0] = wsgiapp(environ, responder2)', - globals(), locals(), profilerfilename+'.tmp') - stat = pstats.Stats(profilerfilename+'.tmp') + globals(), locals(), profilerfilename + '.tmp') + stat = pstats.Stats(profilerfilename + '.tmp') stat.stream = cStringIO.StringIO() stat.strip_dirs().sort_stats("time").print_stats(80) profile_out = stat.stream.getvalue() profile_file = open(profilerfilename, 'a') - profile_file.write('%s\n%s\n%s\n%s\n\n' % \ - ('='*60, environ['PATH_INFO'], '='*60, profile_out)) + profile_file.write('%s\n%s\n%s\n%s\n\n' % + ('=' * 60, environ['PATH_INFO'], '=' * 60, profile_out)) profile_file.close() locker.release() try: @@ -731,7 +741,7 @@ def appfactory(wsgiapp=wsgibase, environ['SERVER_PROTOCOL'], (status_headers[0])[:3], time.time() - time_in, - ) + ) if not logfilename: sys.stdout.write(line) elif isinstance(logfilename, str): @@ -766,11 +776,11 @@ class HttpServer(object): server_name=None, request_queue_size=5, timeout=10, - socket_timeout = 1, - shutdown_timeout=None, # Rocket does not use a shutdown timeout + socket_timeout=1, + shutdown_timeout=None, # Rocket does not use a shutdown timeout path=None, - interfaces=None # Rocket is able to use several interfaces - must be list of socket-tuples as string - ): + interfaces=None # Rocket is able to use several interfaces - must be list of socket-tuples as string + ): """ starts the web server. """ @@ -779,9 +789,9 @@ class HttpServer(object): # if interfaces is specified, it must be tested for rocket parameter correctness # not necessarily completely tested (e.g. content of tuples or ip-format) import types - if isinstance(interfaces,types.ListType): + if isinstance(interfaces, types.ListType): for i in interfaces: - if not isinstance(i,types.TupleType): + if not isinstance(i, types.TupleType): raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/" else: raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/" @@ -822,7 +832,7 @@ class HttpServer(object): logger.info('SSL is ON') app_info = {'wsgi_app': appfactory(wsgibase, log_filename, - profiler_filename) } + profiler_filename)} self.server = rocket.Rocket(interfaces or tuple(sock_list), method='wsgi', @@ -834,7 +844,6 @@ class HttpServer(object): handle_signals=False, ) - def start(self): """ start the web server diff --git a/gluon/messageboxhandler.py b/gluon/messageboxhandler.py index 33cd59b4..3b5a916c 100644 --- a/gluon/messageboxhandler.py +++ b/gluon/messageboxhandler.py @@ -5,6 +5,7 @@ try: except: tkMessageBox = None + class MessageBoxHandler(logging.Handler): def __init__(self): logging.Handler.__init__(self) diff --git a/gluon/myregex.py b/gluon/myregex.py index 3661abfa..f53943ca 100644 --- a/gluon/myregex.py +++ b/gluon/myregex.py @@ -11,18 +11,18 @@ import re # pattern to find defined tables -regex_tables = re.compile(\ +regex_tables = re.compile( """^[\w]+\.define_table\(\s*[\'\"](?P\w+)[\'\"]""", flags=re.M) # pattern to find exposed functions in controller -regex_expose = re.compile(\ +regex_expose = re.compile( '^def\s+(?P(?:[a-zA-Z0-9]\w*)|(?:_[a-zA-Z0-9]\w*))\(\)\s*:', flags=re.M) -regex_include = re.compile(\ +regex_include = re.compile( '(?P\{\{\s*include\s+[\'"](?P[^\'"]*)[\'"]\s*\}\})') -regex_extend = re.compile(\ - '^\s*(?P\{\{\s*extend\s+[\'"](?P[^\'"]+)[\'"]\s*\}\})',re.MULTILINE) +regex_extend = re.compile( + '^\s*(?P\{\{\s*extend\s+[\'"](?P[^\'"]+)[\'"]\s*\}\})', re.MULTILINE) diff --git a/gluon/newcron.py b/gluon/newcron.py index c6b3026b..aa3bcd9f 100644 --- a/gluon/newcron.py +++ b/gluon/newcron.py @@ -23,6 +23,7 @@ from settings import global_settings logger = logging.getLogger("web2py.cron") _cron_stopping = False + def absolute_path_link(path): """ Return an absolute path for the destination of a symlink @@ -36,11 +37,13 @@ def absolute_path_link(path): link = os.path.abspath(path) return link + def stopcron(): "graceful shutdown of cron" global _cron_stopping _cron_stopping = True + class extcron(threading.Thread): def __init__(self, applications_parent, apps=None): @@ -55,6 +58,7 @@ class extcron(threading.Thread): logger.debug('external cron invocation') crondance(self.path, 'external', startup=False, apps=self.apps) + class hardcron(threading.Thread): def __init__(self, applications_parent): @@ -66,7 +70,7 @@ class hardcron(threading.Thread): def launch(self): if not _cron_stopping: logger.debug('hard cron invocation') - crondance(self.path, 'hard', startup = False) + crondance(self.path, 'hard', startup=False) def run(self): s = sched.scheduler(time.time, time.sleep) @@ -76,6 +80,7 @@ class hardcron(threading.Thread): s.enter(60 - now % 60, 1, self.launch, ()) s.run() + class softcron(threading.Thread): def __init__(self, applications_parent): @@ -88,16 +93,17 @@ class softcron(threading.Thread): logger.debug('soft cron invocation') crondance(self.path, 'soft', startup=False) + class Token(object): - def __init__(self,path): + def __init__(self, path): self.path = os.path.join(path, 'cron.master') if not os.path.exists(self.path): fileutils.write_file(self.path, '', 'wb') self.master = None self.now = time.time() - def acquire(self,startup=False): + def acquire(self, startup=False): """ returns the time when the lock is acquired or None if cron already running @@ -112,12 +118,12 @@ class Token(object): if portalocker.LOCK_EX is None: logger.warning('WEB2PY CRON: Disabled because no file locking') return None - self.master = open(self.path,'rb+') + self.master = open(self.path, 'rb+') try: ret = None - portalocker.lock(self.master,portalocker.LOCK_EX) + portalocker.lock(self.master, portalocker.LOCK_EX) try: - (start, stop) = cPickle.load(self.master) + (start, stop) = cPickle.load(self.master) except: (start, stop) = (0, 1) if startup or self.now - start > 59.99: @@ -127,7 +133,7 @@ class Token(object): logger.warning('WEB2PY CRON: Stale cron.master detected') logger.debug('WEB2PY CRON: Acquiring lock') self.master.seek(0) - cPickle.dump((self.now,0),self.master) + cPickle.dump((self.now, 0), self.master) finally: portalocker.unlock(self.master) if not ret: @@ -141,13 +147,13 @@ class Token(object): was completed """ if not self.master.closed: - portalocker.lock(self.master,portalocker.LOCK_EX) + portalocker.lock(self.master, portalocker.LOCK_EX) logger.debug('WEB2PY CRON: Releasing cron lock') self.master.seek(0) - (start, stop) = cPickle.load(self.master) - if start == self.now: # if this is my lock + (start, stop) = cPickle.load(self.master) + if start == self.now: # if this is my lock self.master.seek(0) - cPickle.dump((self.now,time.time()),self.master) + cPickle.dump((self.now, time.time()), self.master) portalocker.unlock(self.master) self.master.close() @@ -177,25 +183,26 @@ def rangetolist(s, period='min'): def parsecronline(line): task = {} if line.startswith('@reboot'): - line=line.replace('@reboot', '-1 * * * *') + line = line.replace('@reboot', '-1 * * * *') elif line.startswith('@yearly'): - line=line.replace('@yearly', '0 0 1 1 *') + line = line.replace('@yearly', '0 0 1 1 *') elif line.startswith('@annually'): - line=line.replace('@annually', '0 0 1 1 *') + line = line.replace('@annually', '0 0 1 1 *') elif line.startswith('@monthly'): - line=line.replace('@monthly', '0 0 1 * *') + line = line.replace('@monthly', '0 0 1 * *') elif line.startswith('@weekly'): - line=line.replace('@weekly', '0 0 * * 0') + line = line.replace('@weekly', '0 0 * * 0') elif line.startswith('@daily'): - line=line.replace('@daily', '0 0 * * *') + line = line.replace('@daily', '0 0 * * *') elif line.startswith('@midnight'): - line=line.replace('@midnight', '0 0 * * *') + line = line.replace('@midnight', '0 0 * * *') elif line.startswith('@hourly'): - line=line.replace('@hourly', '0 * * * *') + line = line.replace('@hourly', '0 * * * *') params = line.strip().split(None, 6) if len(params) < 7: return None - daysofweek={'sun':0,'mon':1,'tue':2,'wed':3,'thu':4,'fri':5,'sat':6} + daysofweek = {'sun': 0, 'mon': 1, 'tue': 2, 'wed': 3, 'thu': 4, + 'fri': 5, 'sat': 6} for (s, id) in zip(params[:5], ['min', 'hr', 'dom', 'mon', 'dow']): if not s in [None, '*']: task[id] = [] @@ -205,9 +212,9 @@ def parsecronline(line): val = '%s/1' % val if '/' in val: task[id] += rangetolist(val, id) - elif val.isdigit() or val=='-1': + elif val.isdigit() or val == '-1': task[id].append(int(val)) - elif id=='dow' and val[:3].lower() in daysofweek: + elif id == 'dow' and val[:3].lower() in daysofweek: task[id].append(daysofweek(val[:3].lower())) task['user'] = params[5] task['cmd'] = params[6] @@ -225,7 +232,7 @@ class cronlauncher(threading.Thread): def run(self): import subprocess - if isinstance(self.cmd, (list,tuple)): + if isinstance(self.cmd, (list, tuple)): cmd = self.cmd else: cmd = self.cmd.split() @@ -234,28 +241,29 @@ class cronlauncher(threading.Thread): stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=self.shell) - (stdoutdata,stderrdata) = proc.communicate() + (stdoutdata, stderrdata) = proc.communicate() if proc.returncode != 0: logger.warning( - 'WEB2PY CRON Call returned code %s:\n%s' % \ - (proc.returncode, stdoutdata+stderrdata)) + 'WEB2PY CRON Call returned code %s:\n%s' % + (proc.returncode, stdoutdata + stderrdata)) else: - logger.debug('WEB2PY CRON Call returned success:\n%s' \ - % stdoutdata) + logger.debug('WEB2PY CRON Call returned success:\n%s' + % stdoutdata) + def crondance(applications_parent, ctype='soft', startup=False, apps=None): - apppath = os.path.join(applications_parent,'applications') + apppath = os.path.join(applications_parent, 'applications') cron_path = os.path.join(applications_parent) token = Token(cron_path) cronmaster = token.acquire(startup=startup) if not cronmaster: return now_s = time.localtime() - checks=(('min',now_s.tm_min), - ('hr',now_s.tm_hour), - ('mon',now_s.tm_mon), - ('dom',now_s.tm_mday), - ('dow',(now_s.tm_wday+1)%7)) + checks = (('min', now_s.tm_min), + ('hr', now_s.tm_hour), + ('mon', now_s.tm_mon), + ('dom', now_s.tm_mday), + ('dow', (now_s.tm_wday + 1) % 7)) if apps is None: apps = [x for x in os.listdir(apppath) @@ -265,8 +273,8 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None): for app in apps: if _cron_stopping: - break; - apath = os.path.join(apppath,app) + break + apath = os.path.join(apppath, app) # if app is a symbolic link to other app, skip it full_apath_link = absolute_path_link(apath) @@ -281,7 +289,8 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None): continue try: cronlines = fileutils.readlines_file(crontab, 'rt') - lines = [x.strip() for x in cronlines if x.strip() and not x.strip().startswith('#')] + lines = [x.strip() for x in cronlines if x.strip( + ) and not x.strip().startswith('#')] tasks = [parsecronline(cline) for cline in lines] except Exception, e: logger.error('WEB2PY CRON: crontab read error %s' % e) @@ -289,42 +298,42 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None): for task in tasks: if _cron_stopping: - break; + break commands = [sys.executable] w2p_path = fileutils.abspath('web2py.py', gluon=True) if os.path.exists(w2p_path): commands.append(w2p_path) if global_settings.applications_parent != global_settings.gluon_parent: commands.extend(('-f', global_settings.applications_parent)) - citems = [(k in task and not v in task[k]) for k,v in checks] - task_min= task.get('min',[]) + citems = [(k in task and not v in task[k]) for k, v in checks] + task_min = task.get('min', []) if not task: continue elif not startup and task_min == [-1]: continue - elif task_min != [-1] and reduce(lambda a,b: a or b, citems): + elif task_min != [-1] and reduce(lambda a, b: a or b, citems): continue - logger.info('WEB2PY CRON (%s): %s executing %s in %s at %s' \ - % (ctype, app, task.get('cmd'), - os.getcwd(), datetime.datetime.now())) + logger.info('WEB2PY CRON (%s): %s executing %s in %s at %s' + % (ctype, app, task.get('cmd'), + os.getcwd(), datetime.datetime.now())) action, command, models = False, task['cmd'], '' if command.startswith('**'): - (action,models,command) = (True,'',command[2:]) + (action, models, command) = (True, '', command[2:]) elif command.startswith('*'): - (action,models,command) = (True,'-M',command[1:]) + (action, models, command) = (True, '-M', command[1:]) else: - action=False + action = False if action and command.endswith('.py'): commands.extend(('-J', # cron job models, # import models? '-S', app, # app name - '-a', '""', # password + '-a', '""', # password '-R', command)) # command elif action: commands.extend(('-J', # cron job models, # import models? - '-S', app+'/'+command, # app name + '-S', app + '/' + command, # app name '-a', '""')) # password else: commands = command @@ -338,6 +347,6 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None): cronlauncher(commands, shell=shell).start() except Exception, e: logger.warning( - 'WEB2PY CRON: Execution error for %s: %s' \ - % (task.get('cmd'), e)) + 'WEB2PY CRON: Execution error for %s: %s' + % (task.get('cmd'), e)) token.release() diff --git a/gluon/portalocker.py b/gluon/portalocker.py index 2766032d..37e5ffe3 100644 --- a/gluon/portalocker.py +++ b/gluon/portalocker.py @@ -110,54 +110,62 @@ else: class LockedFile(object): - def __init__(self,filename, mode='rb'): + def __init__(self, filename, mode='rb'): self.filename = filename self.mode = mode self.file = None if 'r' in mode: - self.file = open(filename,mode) - lock(self.file,LOCK_SH) + self.file = open(filename, mode) + lock(self.file, LOCK_SH) elif 'w' in mode or 'a' in mode: - self.file = open(filename,mode.replace('w','a')) - lock(self.file,LOCK_EX) + self.file = open(filename, mode.replace('w', 'a')) + lock(self.file, LOCK_EX) if not 'a' in mode: self.file.seek(0) self.file.truncate() else: - raise RuntimeError, "invalid LockedFile(...,mode)" - def read(self,size=None): + raise RuntimeError("invalid LockedFile(...,mode)") + + def read(self, size=None): return self.file.read() if size is None else self.file.read(size) + def readline(self): return self.file.readline() + def readlines(self): return self.file.readlines() - def write(self,data): + + def write(self, data): self.file.write(data) self.file.flush() + def close(self): if not self.file is None: unlock(self.file) self.file.close() self.file = None + def __del__(self): if not self.file is None: self.close() + def read_locked(filename): fp = LockedFile(filename, 'r') data = fp.read() fp.close() return data -def write_locked(filename,data): + +def write_locked(filename, data): fp = LockedFile(filename, 'w') data = fp.write(data) fp.close() -if __name__=='__main__': - f = LockedFile('test.txt',mode='wb') +if __name__ == '__main__': + f = LockedFile('test.txt', mode='wb') f.write('test ok') f.close() - f = LockedFile('test.txt',mode='rb') + f = LockedFile('test.txt', mode='rb') print f.read() f.close() diff --git a/gluon/reserved_sql_keywords.py b/gluon/reserved_sql_keywords.py index cd3c4b55..e5b0fd14 100644 --- a/gluon/reserved_sql_keywords.py +++ b/gluon/reserved_sql_keywords.py @@ -419,7 +419,7 @@ POSTGRESQL_NONRESERVED = set(( 'HOLD', 'HOST', 'HOUR', -# 'ID', + # 'ID', 'IDENTITY', 'IF', 'IGNORE', @@ -501,7 +501,7 @@ POSTGRESQL_NONRESERVED = set(( 'MOVE', 'MULTISET', 'MUMPS', -# 'NAME', + # 'NAME', 'NAMES', 'NAMESPACE', 'NCLOB', @@ -563,7 +563,7 @@ POSTGRESQL_NONRESERVED = set(( 'PARTITION', 'PASCAL', 'PASSING', -# 'PASSWORD', + # 'PASSWORD', 'PATH', 'PERCENT_RANK', 'PERCENTILE_CONT', @@ -622,7 +622,7 @@ POSTGRESQL_NONRESERVED = set(( 'RETURNED_SQLSTATE', 'RETURNS', 'REVOKE', -# 'ROLE', + # 'ROLE', 'ROLLBACK', 'ROLLUP', 'ROUTINE', @@ -697,7 +697,7 @@ POSTGRESQL_NONRESERVED = set(( 'SYSTEM', 'SYSTEM_USER', 'T', -# 'TABLE_NAME', + # 'TABLE_NAME', 'TABLESAMPLE', 'TABLESPACE', 'TEMP', @@ -785,7 +785,7 @@ POSTGRESQL_NONRESERVED = set(( 'YEAR', 'YES', 'ZONE', -)) + )) #Thanks villas FIREBIRD = set(( @@ -887,7 +887,7 @@ FIREBIRD = set(( 'PAGES', 'PAGE_SIZE', 'PARAMETER', -# 'PASSWORD', + # 'PASSWORD', 'PLAN', 'POST_EVENT', 'QUIT', @@ -903,7 +903,7 @@ FIREBIRD = set(( 'RETURN', 'RETURNING_VALUES', 'RETURNS', -# 'ROLE', + # 'ROLE', 'ROW_COUNT', 'ROWS', 'RUNTIME', @@ -941,7 +941,7 @@ FIREBIRD = set(( 'WEEKDAY', 'WHILE', 'YEARDAY', -)) + )) FIREBIRD_NONRESERVED = set(( 'BACKUP', 'BLOCK', @@ -1710,4 +1710,5 @@ ADAPTERS = { 'common': COMMON, } -ADAPTERS['all'] = reduce(lambda a,b:a.union(b),(x for x in ADAPTERS.values())) +ADAPTERS['all'] = reduce(lambda a, b: a.union(b), ( + x for x in ADAPTERS.values())) diff --git a/gluon/restricted.py b/gluon/restricted.py index 6533af47..0416dea2 100644 --- a/gluon/restricted.py +++ b/gluon/restricted.py @@ -22,6 +22,7 @@ logger = logging.getLogger("web2py") __all__ = ['RestrictedError', 'restricted', 'TicketStorage', 'compile2'] + class TicketStorage(Storage): """ @@ -32,7 +33,7 @@ class TicketStorage(Storage): self, db=None, tablename='web2py_ticket' - ): + ): Storage.__init__(self) self.db = db self.tablename = tablename @@ -64,7 +65,8 @@ class TicketStorage(Storage): root = request.folder if app: root = os.path.join(os.path.join(root, '..'), app) - errors_folder = os.path.abspath(os.path.join(root, 'errors'))#.replace('\\', '/') + errors_folder = os.path.abspath( + os.path.join(root, 'errors')) # .replace('\\', '/') return open(os.path.join(errors_folder, ticket_id), mode) def _get_table(self, db, tablename, app): @@ -78,7 +80,7 @@ class TicketStorage(Storage): db.Field('ticket_id', length=100), db.Field('ticket_data', 'text'), db.Field('created_datetime', 'datetime'), - ) + ) return table def load( @@ -86,7 +88,7 @@ class TicketStorage(Storage): request, app, ticket_id, - ): + ): if not self.db: try: ef = self._error_file(request, ticket_id, 'rb', app) @@ -102,7 +104,6 @@ class TicketStorage(Storage): return cPickle.loads(rows[0].ticket_data) if rows else {} - class RestrictedError(Exception): """ class used to wrap an exception that occurs in the restricted environment @@ -115,12 +116,13 @@ class RestrictedError(Exception): code='', output='', environment=None, - ): + ): """ layer here is some description of where in the system the exception occurred. """ - if environment is None: environment = {} + if environment is None: + environment = {} self.layer = layer self.code = code self.output = output @@ -131,7 +133,7 @@ class RestrictedError(Exception): except: self.traceback = 'no traceback because template parting error' try: - self.snapshot = snapshot(context=10,code=code, + self.snapshot = snapshot(context=10, code=code, environment=self.environment) except: self.snapshot = {} @@ -151,15 +153,14 @@ class RestrictedError(Exception): 'output': str(self.output), 'traceback': str(self.traceback), 'snapshot': self.snapshot, - } + } ticket_storage = TicketStorage(db=request.tickets_db) - ticket_storage.store(request, request.uuid.split('/',1)[1], d) + ticket_storage.store(request, request.uuid.split('/', 1)[1], d) return request.uuid except: logger.error(self.traceback) return None - def load(self, request, app, ticket_id): """ loads a logged exception. @@ -186,11 +187,12 @@ class RestrictedError(Exception): return output -def compile2(code,layer): +def compile2(code, layer): """ The +'\n' is necessary else compile fails when code ends in a comment. """ - return compile(code.rstrip().replace('\r\n','\n')+'\n', layer, 'exec') + return compile(code.rstrip().replace('\r\n', '\n') + '\n', layer, 'exec') + def restricted(code, environment=None, layer='Unknown'): """ @@ -198,14 +200,15 @@ def restricted(code, environment=None, layer='Unknown'): in code it raises a RestrictedError containing the traceback. layer is passed to RestrictedError to identify where the error occurred. """ - if environment is None: environment = {} + if environment is None: + environment = {} environment['__file__'] = layer environment['__name__'] = '__restricted__' try: - if type(code) == types.CodeType: + if isinstance(code, types.CodeType): ccode = code else: - ccode = compile2(code,layer) + ccode = compile2(code, layer) exec ccode in environment except HTTP: raise @@ -221,14 +224,21 @@ def restricted(code, environment=None, layer='Unknown'): output = "%s %s" % (etype, evalue) raise RestrictedError(layer, code, output, environment) + def snapshot(info=None, context=5, code=None, environment=None): """Return a dict describing a given traceback (based on cgitb.text).""" - import os, types, time, linecache, inspect, pydoc, cgitb + import os + import types + import time + import linecache + import inspect + import pydoc + import cgitb # if no exception info given, get current: etype, evalue, etb = info or sys.exc_info() - if type(etype) is types.ClassType: + if isinstance(etype, types.ClassType): etype = etype.__name__ # create a snapshot dict with some basic information @@ -245,22 +255,26 @@ def snapshot(info=None, context=5, code=None, environment=None): call = '' if func != '?': call = inspect.formatargvalues(args, varargs, varkw, locals, - formatvalue=lambda value: '=' + pydoc.text.repr(value)) + formatvalue=lambda value: '=' + pydoc.text.repr(value)) # basic frame information - f = {'file': file, 'func': func, 'call': call, 'lines': {}, 'lnum': lnum} + f = {'file': file, 'func': func, 'call': call, 'lines': {}, + 'lnum': lnum} highlight = {} + def reader(lnum=[lnum]): highlight[lnum[0]] = 1 - try: return linecache.getline(file, lnum[0]) - finally: lnum[0] += 1 + try: + return linecache.getline(file, lnum[0]) + finally: + lnum[0] += 1 vars = cgitb.scanvars(reader, frame, locals) # if it is a view, replace with generated code if file.endswith('html'): - lmin = lnum>context and (lnum-context) or 0 - lmax = lnum+context + lmin = lnum > context and (lnum - context) or 0 + lmax = lnum + context lines = code.split("\n")[lmin:lmax] index = min(context, lnum) - 1 @@ -273,10 +287,13 @@ def snapshot(info=None, context=5, code=None, environment=None): # dump local variables (referenced in current line only) f['dump'] = {} for name, where, value in vars: - if name in f['dump']: continue + if name in f['dump']: + continue if value is not cgitb.__UNDEF__: - if where == 'global': name = 'global ' + name - elif where != 'local': name = where + name.split('.')[-1] + if where == 'global': + name = 'global ' + name + elif where != 'local': + name = where + name.split('.')[-1] f['dump'][name] = pydoc.text.repr(value) else: f['dump'][name] = 'undefined' @@ -290,7 +307,7 @@ def snapshot(info=None, context=5, code=None, environment=None): if isinstance(evalue, BaseException): for name in dir(evalue): # prevent py26 DeprecatedWarning: - if name!='message' or sys.version_info<(2.6): + if name != 'message' or sys.version_info < (2.6): value = pydoc.text.repr(getattr(evalue, name)) s['exception'][name] = value @@ -300,7 +317,7 @@ def snapshot(info=None, context=5, code=None, environment=None): s['locals'][name] = pydoc.text.repr(value) # add web2py environment variables - for k,v in environment.items(): + for k, v in environment.items(): if k in ('request', 'response', 'session'): s[k] = BEAUTIFY(v) diff --git a/gluon/rewrite.py b/gluon/rewrite.py index 10227cde..c18035d9 100644 --- a/gluon/rewrite.py +++ b/gluon/rewrite.py @@ -38,7 +38,8 @@ THREAD_LOCAL = threading.local() # thread-local storage for routing params regex_at = re.compile(r'(?(.*)') -regex_full_url = re.compile(r'^(?Phttp|https|HTTP|HTTPS)\://(?P[^/]*)(?P.*)') +regex_full_url = re.compile( + r'^(?Phttp|https|HTTP|HTTPS)\://(?P[^/]*)(?P.*)') regex_version = re.compile(r'^(_[\d]+\.[\d]+\.[\d]+)$') # pattern to replace spaces with underscore in URL # also the html escaped variants '+' and '%20' are covered @@ -60,25 +61,26 @@ regex_url = re.compile('^/((?P\w+)(/(?P\w+)(/(?P(?P\w+)(\.(?P[\w. def _router_default(): "return new copy of default base router" router = Storage( - default_application = 'init', - applications = 'ALL', - default_controller = 'default', - controllers = 'DEFAULT', - default_function = 'index', - functions = dict(), - default_language = None, - languages = None, - root_static = ['favicon.ico', 'robots.txt'], - map_static = None, - domains = None, - exclusive_domain = False, - map_hyphen = False, - acfe_match = r'\w+$', # legal app/ctlr/fcn/ext - file_match = r'([-+=@$%\w]+[./]?)+$', # legal static subpath - args_match = r'([\w@ -]+[=.]?)*$', # legal arg in args + default_application='init', + applications='ALL', + default_controller='default', + controllers='DEFAULT', + default_function='index', + functions=dict(), + default_language=None, + languages=None, + root_static=['favicon.ico', 'robots.txt'], + map_static=None, + domains=None, + exclusive_domain=False, + map_hyphen=False, + acfe_match=r'\w+$', # legal app/ctlr/fcn/ext + file_match=r'([-+=@$%\w]+[./]?)+$', # legal static subpath + args_match=r'([\w@ -]+[=.]?)*$', # legal arg in args ) return router + def _params_default(app=None): "return new copy of default parameters" p = Storage() @@ -94,7 +96,7 @@ def _params_default(app=None): p.error_handler = None p.error_message = '

%s

' p.error_message_ticket = \ - '

Internal error

Ticket issued:
%(ticket)s' + '

Internal error

Ticket issued: %(ticket)s' p.routers = None p.logging = 'off' return p @@ -104,6 +106,7 @@ params = _params_default(app=None) # regex rewrite parameters THREAD_LOCAL.routes = params # default to base regex rewrite parameters routers = None + def log_rewrite(string): "Log rewrite activity under control of routes.py" if params.logging == 'debug': # catch common cases first @@ -152,6 +155,7 @@ ROUTER_BASE_KEYS = set( # filter_err: helper for doctest & unittest # regex_filter_out: doctest + def fixup_missing_path_info(environ): eget = environ.get path_info = eget('PATH_INFO') @@ -165,12 +169,12 @@ def fixup_missing_path_info(environ): elif not request_uri: query_string = eget('QUERY_STRING') if query_string: - environ['REQUEST_URI'] = '%s?%s' % (path_info,query_string) + environ['REQUEST_URI'] = '%s?%s' % (path_info, query_string) else: environ['REQUEST_URI'] = path_info if not eget('HTTP_HOST'): environ['HTTP_HOST'] = \ - '%s:%s' % (eget('SERVER_NAME'),eget('SERVER_PORT')) + '%s:%s' % (eget('SERVER_NAME'), eget('SERVER_PORT')) def url_in(request, environ): @@ -179,6 +183,7 @@ def url_in(request, environ): return map_url_in(request, environ) return regex_url_in(request, environ) + def url_out(request, environ, application, controller, function, args, other, scheme, host, port): "assemble and rewrite outgoing URL" @@ -193,48 +198,51 @@ def url_out(request, environ, application, controller, function, # fill in scheme and host if absolute URL is requested # scheme can be a string, eg 'http', 'https', 'ws', 'wss' # - if host is True or (host is None and (scheme or port!=None)): + if host is True or (host is None and (scheme or port is not None)): host = request.env.http_host if not scheme or scheme is True: scheme = request.env.get('wsgi_url_scheme', 'http').lower() \ if request else 'http' if host: - host_port = host if not port else host.split(':',1)[0]+':%s'%port + host_port = host if not port else host.split(':', 1)[0] + ':%s' % port url = '%s://%s%s' % (scheme, host_port, url) return url + def try_rewrite_on_error(http_response, request, environ, ticket=None): """ called from main.wsgibase to rewrite the http response. """ status = int(str(http_response.status).split()[0]) - if status>=399 and THREAD_LOCAL.routes.routes_onerror: - keys=set(('%s/%s' % (request.application, status), - '%s/*' % (request.application), - '*/%s' % (status), - '*/*')) - for (key,uri) in THREAD_LOCAL.routes.routes_onerror: + if status >= 399 and THREAD_LOCAL.routes.routes_onerror: + keys = set(('%s/%s' % (request.application, status), + '%s/*' % (request.application), + '*/%s' % (status), + '*/*')) + for (key, uri) in THREAD_LOCAL.routes.routes_onerror: if key in keys: if uri == '!': # do nothing! return http_response, environ elif '?' in uri: - path_info, query_string = uri.split('?',1) + path_info, query_string = uri.split('?', 1) query_string += '&' else: path_info, query_string = uri, '' query_string += \ 'code=%s&ticket=%s&requested_uri=%s&request_url=%s' % \ - (status,ticket,urllib.quote_plus(request.env.request_uri),request.url) + (status, ticket, urllib.quote_plus( + request.env.request_uri), request.url) if uri.startswith('http://') or uri.startswith('https://'): # make up a response - url = path_info+'?'+query_string + url = path_info + '?' + query_string message = 'You are being redirected here' return HTTP(303, message % url, Location=url), environ else: error_raising_path = environ['PATH_INFO'] # Rewrite routes_onerror path. - path_info = '/' + path_info.lstrip('/') # add leading '/' if missing + path_info = '/' + path_info.lstrip( + '/') # add leading '/' if missing environ['PATH_INFO'] = path_info error_handling_path = \ url_in(request, environ)[2]['PATH_INFO'] @@ -248,29 +256,30 @@ def try_rewrite_on_error(http_response, request, environ, ticket=None): # do nothing! return http_response, environ + def try_redirect_on_error(http_object, request, ticket=None): "called from main.wsgibase to rewrite the http response" status = int(str(http_object.status).split()[0]) - if status>399 and THREAD_LOCAL.routes.routes_onerror: - keys=set(('%s/%s' % (request.application, status), - '%s/*' % (request.application), - '*/%s' % (status), - '*/*')) - for (key,redir) in THREAD_LOCAL.routes.routes_onerror: + if status > 399 and THREAD_LOCAL.routes.routes_onerror: + keys = set(('%s/%s' % (request.application, status), + '%s/*' % (request.application), + '*/%s' % (status), + '*/*')) + for (key, redir) in THREAD_LOCAL.routes.routes_onerror: if key in keys: if redir == '!': break elif '?' in redir: url = '%s&code=%s&ticket=%s&requested_uri=%s&request_url=%s' % \ - (redir,status,ticket, + (redir, status, ticket, urllib.quote_plus(request.env.request_uri), request.url) else: url = '%s?code=%s&ticket=%s&requested_uri=%s&request_url=%s' % \ - (redir,status,ticket, + (redir, status, ticket, urllib.quote_plus(request.env.request_uri), request.url) - return HTTP(303,'You are being redirected here' % url,Location=url) + return HTTP(303, 'You are being redirected here' % url, Location=url) return http_object @@ -305,7 +314,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None): path = abspath('applications', app, routes) if not exists(path): return - data = read_file(path).replace('\r\n','\n') + data = read_file(path).replace('\r\n', '\n') symbols = dict(app=app) try: @@ -323,8 +332,8 @@ def load(routes='routes.py', app=None, data=None, rdict=None): for items in symbols[sym]: p[sym].append(compile_regex(*items)) for sym in ('routes_onerror', 'routes_apps_raw', - 'error_handler','error_message', 'error_message_ticket', - 'default_application','default_controller', 'default_function', + 'error_handler', 'error_message', 'error_message_ticket', + 'default_application', 'default_controller', 'default_function', 'logging'): if sym in symbols: p[sym] = symbols[sym] @@ -357,15 +366,15 @@ def load(routes='routes.py', app=None, data=None, rdict=None): apppath = abspath('applications') for appname in os.listdir(apppath): if not appname.startswith('.') and \ - isdir(abspath(apppath,appname)) and \ - isdir(abspath(apppath,appname,'controllers')): + isdir(abspath(apppath, appname)) and \ + isdir(abspath(apppath, appname, 'controllers')): all_apps.append(appname) if routers: router = Storage(routers.BASE) # new copy if appname in routers: for key in routers[appname].keys(): if key in ROUTER_BASE_KEYS: - raise SyntaxError, "BASE-only key '%s' in router '%s'" % (key, appname) + raise SyntaxError("BASE-only key '%s' in router '%s'" % (key, appname)) router.update(routers[appname]) routers[appname] = router if exists(abspath('applications', appname, routes)): @@ -374,7 +383,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None): if routers: load_routers(all_apps) - else: # app + else: # app params_apps[app] = p if routers and p.routers: if app in p.routers: @@ -406,8 +415,8 @@ def compile_regex(k, v, env=None): if k.find('://') < 0: i = k.find(':/') if i < 0: - raise SyntaxError, "routes pattern syntax error: path needs leading '/' [%s]" % k0 - k = r'%s:https?://[^:/]+:[a-z]+ %s' % (k[:i], k[i+1:]) + raise SyntaxError("routes pattern syntax error: path needs leading '/' [%s]" % k0) + k = r'%s:https?://[^:/]+:[a-z]+ %s' % (k[:i], k[i + 1:]) # $anything -> ?P.* for item in regex_anything.findall(k): k = k.replace(item, '(?P.*)') @@ -419,6 +428,7 @@ def compile_regex(k, v, env=None): v = v.replace(item, r'\g<%s>' % item[1:]) return (re.compile(k, re.DOTALL), v, env or {}) + def load_routers(all_apps): "load-time post-processing of routers" @@ -431,13 +441,15 @@ def load_routers(all_apps): if app != 'BASE': keys = set(routers[app]).intersection(ROUTER_BASE_KEYS) if keys: - raise SyntaxError, "BASE-only key(s) %s in router '%s'" % (tuple(keys), app) + raise SyntaxError("BASE-only key(s) %s in router '%s'" % ( + tuple(keys), app)) router.update(routers[app]) routers[app] = router router = routers[app] keys = set(router).difference(ROUTER_KEYS) if keys: - raise SyntaxError, "unknown key(s) %s in router '%s'" % (tuple(keys), app) + raise SyntaxError("unknown key(s) %s in router '%s'" % ( + tuple(keys), app)) if not router.controllers: router.controllers = set() elif not isinstance(router.controllers, str): @@ -450,10 +462,12 @@ def load_routers(all_apps): if isinstance(router.functions, (set, tuple, list)): functions = set(router.functions) if isinstance(router.default_function, str): - functions.add(router.default_function) # legacy compatibility - router.functions = { router.default_controller: functions } + functions.add( + router.default_function) # legacy compatibility + router.functions = {router.default_controller: functions} for controller in router.functions: - router.functions[controller] = set(router.functions[controller]) + router.functions[controller] = set( + router.functions[controller]) else: router.functions = dict() if app != 'BASE': @@ -516,19 +530,20 @@ def load_routers(all_apps): else: fcn = None if app not in all_apps and app not in routers: - raise SyntaxError, "unknown app '%s' in domains" % app + raise SyntaxError("unknown app '%s' in domains" % app) domains[(domain, port)] = (app, ctlr, fcn) routers.BASE.domains = domains + def regex_uri(e, regexes, tag, default=None): "filter incoming URI against a list of regexes" path = e['PATH_INFO'] - host = e.get('http_host', e.get('SERVER_NAME','localhost')).lower() + host = e.get('http_host', e.get('SERVER_NAME', 'localhost')).lower() i = host.find(':') if i > 0: host = host[:i] key = '%s:%s://%s:%s %s' % \ - (e.get('REMOTE_ADDR','localhost'), + (e.get('REMOTE_ADDR', 'localhost'), e.get('wsgi.url_scheme', 'http').lower(), host, e.get('REQUEST_METHOD', 'get').lower(), path) for (regex, value, custom_env) in regexes: @@ -540,6 +555,7 @@ def regex_uri(e, regexes, tag, default=None): log_rewrite('%s: [%s] -> %s (not rewritten)' % (tag, key, default)) return default + def regex_select(env=None, app=None, request=None): """ select a set of regex rewrite params for the current request @@ -553,10 +569,11 @@ def regex_select(env=None, app=None, request=None): app = regex_uri(env, params.routes_app, "routes_app") THREAD_LOCAL.routes = params_apps.get(app, params) else: - THREAD_LOCAL.routes = params # default to base rewrite parameters + THREAD_LOCAL.routes = params # default to base rewrite parameters log_rewrite("select routing parameters: %s" % THREAD_LOCAL.routes.name) return app # for doctest + def regex_filter_in(e): "regex rewrite incoming URL" routes = THREAD_LOCAL.routes @@ -567,7 +584,7 @@ def regex_filter_in(e): "routes_in", e['PATH_INFO']) rmatch = regex_redirect.match(path) if rmatch: - raise HTTP(int(rmatch.group(1)),location=rmatch.group(2)) + raise HTTP(int(rmatch.group(1)), location=rmatch.group(2)) items = path.split('?', 1) e['PATH_INFO'] = items[0] if len(items) > 1: @@ -581,7 +598,8 @@ def regex_filter_in(e): def sluggify(key): - return key.lower().replace('.','_') + return key.lower().replace('.', '_') + def regex_url_in(request, environ): "rewrite and parse incoming URL" @@ -596,26 +614,28 @@ def regex_url_in(request, environ): routes = THREAD_LOCAL.routes if routes.routes_in: environ = regex_filter_in(environ) - request.env.update((k.lower().replace('.','_'),v) for k,v in environ.iteritems()) + request.env.update( + (k.lower().replace('.', '_'), v) for k, v in environ.iteritems()) # ################################################## # serve if a static file # ################################################## path = request.env.path_info.replace('\\', '/') or '/' - path = regex_space.sub('_',path) - if path.endswith('/') and len(path)>1: path = path[:-1] + path = regex_space.sub('_', path) + if path.endswith('/') and len(path) > 1: + path = path[:-1] match = regex_url.match(path) if not match: raise HTTP(400, routes.error_message % 'invalid request', web2py_error='invalid path') - elif match.group('c')=='static': + elif match.group('c') == 'static': application = match.group('a') version, filename = None, match.group('z') - items = filename.split('/',1) + items = filename.split('/', 1) if regex_version.match(items[0]): - version,filename = items + version, filename = items static_file = pjoin(request.env.applications_parent, 'applications', application, 'static', filename) @@ -655,9 +675,9 @@ def regex_filter_out(url, e=None): if i > 0: host = host[:i] items[0] = '%s:%s://%s:%s %s' % \ - (e.get('remote_addr', ''), - e.get('wsgi_url_scheme', 'http').lower(), host, - e.get('request_method', 'get').lower(), items[0]) + (e.get('remote_addr', ''), + e.get('wsgi_url_scheme', 'http').lower(), host, + e.get('request_method', 'get').lower(), items[0]) else: items[0] = ':http://localhost:get %s' % items[0] for (regex, value, tmp) in routes.routes_out: @@ -671,7 +691,7 @@ def regex_filter_out(url, e=None): def filter_url(url, method='get', remote='0.0.0.0', out=False, app=False, lang=None, - domain=(None,None), env=False, scheme=None, + domain=(None, None), env=False, scheme=None, host=None, port=None): """ doctest/unittest interface to regex_filter_in() and regex_filter_out() @@ -685,21 +705,21 @@ def filter_url(url, method='get', remote='0.0.0.0', k = len(uri) if isinstance(domain, str): domain = (domain, None) - (path_info, query_string) = (uri[:k], uri[k+1:]) + (path_info, query_string) = (uri[:k], uri[k + 1:]) path_info = urllib.unquote(path_info) # simulate server e = { - 'REMOTE_ADDR': remote, - 'REQUEST_METHOD': method, - 'wsgi.url_scheme': urlscheme, - 'HTTP_HOST': urlhost, - 'REQUEST_URI': uri, - 'PATH_INFO': path_info, - 'QUERY_STRING': query_string, - #for filter_out request.env use lowercase - 'remote_addr': remote, - 'request_method': method, - 'wsgi_url_scheme': urlscheme, - 'http_host': urlhost + 'REMOTE_ADDR': remote, + 'REQUEST_METHOD': method, + 'wsgi.url_scheme': urlscheme, + 'HTTP_HOST': urlhost, + 'REQUEST_URI': uri, + 'PATH_INFO': path_info, + 'QUERY_STRING': query_string, + #for filter_out request.env use lowercase + 'remote_addr': remote, + 'request_method': method, + 'wsgi_url_scheme': urlscheme, + 'http_host': urlhost } request = Storage() @@ -717,17 +737,19 @@ def filter_url(url, method='get', remote='0.0.0.0', # rewrite outbound URL # if out: - (request.env.domain_application, request.env.domain_controller) = domain + (request.env.domain_application, + request.env.domain_controller) = domain items = path_info.lstrip('/').split('/') if items[-1] == '': - items.pop() # adjust trailing empty args + items.pop() # adjust trailing empty args assert len(items) >= 3, "at least /a/c/f is required" a = items.pop(0) c = items.pop(0) f = items.pop(0) if not routers: return regex_filter_out(uri, e) - acf = map_url_out(request, None, a, c, f, items, None, scheme, host, port) + acf = map_url_out( + request, None, a, c, f, items, None, scheme, host, port) if items: url = '%s/%s' % (acf, '/'.join(items)) if items[-1] == '': @@ -743,7 +765,8 @@ def filter_url(url, method='get', remote='0.0.0.0', (static, version, e) = url_in(request, e) if static: return static - result = "/%s/%s/%s" % (request.application, request.controller, request.function) + result = "/%s/%s/%s" % ( + request.application, request.controller, request.function) if request.extension and request.extension != 'html': result += ".%s" % request.extension if request.args: @@ -762,22 +785,24 @@ def filter_err(status, application='app', ticket='tkt'): routes = THREAD_LOCAL.routes if status > 399 and routes.routes_onerror: keys = set(('%s/%s' % (application, status), - '%s/*' % (application), - '*/%s' % (status), - '*/*')) - for (key,redir) in routes.routes_onerror: + '%s/*' % (application), + '*/%s' % (status), + '*/*')) + for (key, redir) in routes.routes_onerror: if key in keys: if redir == '!': break elif '?' in redir: - url = redir + '&' + 'code=%s&ticket=%s' % (status,ticket) + url = redir + '&' + 'code=%s&ticket=%s' % (status, ticket) else: - url = redir + '?' + 'code=%s&ticket=%s' % (status,ticket) - return url # redirection - return status # no action + url = redir + '?' + 'code=%s&ticket=%s' % (status, ticket) + return url # redirection + return status # no action # router support # + + class MapUrlIn(object): "logic for mapping incoming URLs" @@ -804,7 +829,8 @@ class MapUrlIn(object): self.query = self.env.get('QUERY_STRING', None) path = path.lstrip('/') self.env['PATH_INFO'] = '/' + path - self.env['WEB2PY_ORIGINAL_URI'] = self.env['PATH_INFO'] + (self.query and ('?' + self.query) or '') + self.env['WEB2PY_ORIGINAL_URI'] = self.env['PATH_INFO'] + ( + self.query and ('?' + self.query) or '') # to handle empty args, strip exactly one trailing slash, if present # .../arg1// represents one trailing empty arg @@ -814,12 +840,13 @@ class MapUrlIn(object): self.args = List(path and path.split('/') or []) # see http://www.python.org/dev/peps/pep-3333/#url-reconstruction for URL composition - self.remote_addr = self.env.get('REMOTE_ADDR','localhost') + self.remote_addr = self.env.get('REMOTE_ADDR', 'localhost') self.scheme = self.env.get('wsgi.url_scheme', 'http').lower() self.method = self.env.get('REQUEST_METHOD', 'get').lower() (self.host, self.port) = (self.env.get('HTTP_HOST'), None) if not self.host: - (self.host, self.port) = (self.env.get('SERVER_NAME'), self.env.get('SERVER_PORT')) + (self.host, self.port) = ( + self.env.get('SERVER_NAME'), self.env.get('SERVER_PORT')) if not self.host: (self.host, self.port) = ('localhost', '80') if ':' in self.host: @@ -837,7 +864,7 @@ class MapUrlIn(object): for i in xrange(prefixlen): if prefix[i] != self.args[i]: return # prefix didn't match - self.args = List(self.args[prefixlen:]) # strip the prefix + self.args = List(self.args[prefixlen:]) # strip the prefix def map_app(self): "determine application name" @@ -851,12 +878,14 @@ class MapUrlIn(object): elif not base.exclusive_domain and arg0 and not base.applications: self.application = arg0 elif (self.host, self.port) in base.domains: - (self.application, self.domain_controller, self.domain_function) = base.domains[(self.host, self.port)] + (self.application, self.domain_controller, + self.domain_function) = base.domains[(self.host, self.port)] self.env['domain_application'] = self.application self.env['domain_controller'] = self.domain_controller self.env['domain_function'] = self.domain_function elif (self.host, None) in base.domains: - (self.application, self.domain_controller, self.domain_function) = base.domains[(self.host, None)] + (self.application, self.domain_controller, + self.domain_function) = base.domains[(self.host, None)] self.env['domain_application'] = self.application self.env['domain_controller'] = self.domain_controller self.env['domain_function'] = self.domain_function @@ -869,12 +898,14 @@ class MapUrlIn(object): self.pop_arg_if(self.application == arg0) if not base._acfe_match.match(self.application): - raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request', - web2py_error="invalid application: '%s'" % self.application) + raise HTTP( + 400, THREAD_LOCAL.routes.error_message % 'invalid request', + web2py_error="invalid application: '%s'" % self.application) if self.application not in routers and \ - (self.application != THREAD_LOCAL.routes.default_application or self.application == 'welcome'): - raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request', + (self.application != THREAD_LOCAL.routes.default_application or self.application == 'welcome'): + raise HTTP( + 400, THREAD_LOCAL.routes.error_message % 'invalid request', web2py_error="unknown application: '%s'" % self.application) # set the application router @@ -908,8 +939,8 @@ class MapUrlIn(object): if len(self.args) == 1 and self.arg0 in self.router.root_static: self.controller = self.request.controller = 'static' root_static_file = pjoin(self.request.env.applications_parent, - 'applications', self.application, - self.controller, self.arg0) + 'applications', self.application, + self.controller, self.arg0) log_rewrite("route: root static=%s" % root_static_file) return root_static_file, None return None, None @@ -938,8 +969,9 @@ class MapUrlIn(object): self.pop_arg_if(arg0 == self.controller) log_rewrite("route: controller=%s" % self.controller) if not self.router._acfe_match.match(self.controller): - raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request', - web2py_error='invalid controller') + raise HTTP( + 400, THREAD_LOCAL.routes.error_message % 'invalid request', + web2py_error='invalid controller') def map_static(self): ''' @@ -962,7 +994,8 @@ class MapUrlIn(object): # match path elements bad_static = False for name in self.args: - bad_static = bad_static or name in ('', '.', '..') or not self.router._file_match.match(name) + bad_static = bad_static or name in ( + '', '.', '..') or not self.router._file_match.match(name) if bad_static: log_rewrite('bad static path=%s' % file) raise HTTP(400, @@ -990,9 +1023,10 @@ class MapUrlIn(object): arg0 = self.harg0 # map hyphens functions = self.functions.get(self.controller, set()) if isinstance(self.router.default_function, dict): - default_function = self.router.default_function.get(self.controller, None) + default_function = self.router.default_function.get( + self.controller, None) else: - default_function = self.router.default_function # str or None + default_function = self.router.default_function # str or None default_function = self.domain_function or default_function if not arg0 or functions and arg0 not in functions: self.function = default_function or "" @@ -1005,14 +1039,17 @@ class MapUrlIn(object): else: self.function = arg0 self.pop_arg_if(True) - log_rewrite("route: function.ext=%s.%s" % (self.function, self.extension)) + log_rewrite( + "route: function.ext=%s.%s" % (self.function, self.extension)) if not self.router._acfe_match.match(self.function): - raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request', - web2py_error='invalid function') + raise HTTP( + 400, THREAD_LOCAL.routes.error_message % 'invalid request', + web2py_error='invalid function') if self.extension and not self.router._acfe_match.match(self.extension): - raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request', - web2py_error='invalid extension') + raise HTTP( + 400, THREAD_LOCAL.routes.error_message % 'invalid request', + web2py_error='invalid extension') def validate_args(self): ''' @@ -1020,13 +1057,14 @@ class MapUrlIn(object): ''' for arg in self.args: if not self.router._args_match.match(arg): - raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request', - web2py_error='invalid arg <%s>' % arg) + raise HTTP( + 400, THREAD_LOCAL.routes.error_message % 'invalid request', + web2py_error='invalid arg <%s>' % arg) def sluggify(self): "" self.request.env.update( - (k.lower().replace('.','_'),v) for k,v in self.env.iteritems()) + (k.lower().replace('.', '_'), v) for k, v in self.env.iteritems()) def update_request(self): ''' @@ -1053,7 +1091,8 @@ class MapUrlIn(object): uri = '/%s%s%s%s' % ( app, uri, - urllib.quote('/'+'/'.join(str(x) for x in self.args)) if self.args else '', + urllib.quote('/' + '/'.join( + str(x) for x in self.args)) if self.args else '', ('?' + self.query) if self.query else '') self.env['REQUEST_URI'] = uri self.sluggify() @@ -1075,6 +1114,7 @@ class MapUrlIn(object): if dopop: self.args.pop(0) + class MapUrlOut(object): "logic for mapping outgoing URLs" @@ -1090,7 +1130,8 @@ class MapUrlOut(object): self.env = env self.application = application self.controller = controller - self.is_static = (controller == 'static' or controller.startswith('static/')) + self.is_static = ( + controller == 'static' or controller.startswith('static/')) self.function = function self.args = args self.other = other @@ -1111,12 +1152,13 @@ class MapUrlOut(object): self.domain_application = request and self.request.env.domain_application self.domain_controller = request and self.request.env.domain_controller if isinstance(self.router.default_function, dict): - self.default_function = self.router.default_function.get(self.controller, None) + self.default_function = self.router.default_function.get( + self.controller, None) else: self.default_function = self.router.default_function if (self.router.exclusive_domain and self.domain_application and self.domain_application != self.application and not self.host): - raise SyntaxError, 'cross-domain conflict: must specify host' + raise SyntaxError('cross-domain conflict: must specify host') lang = request and request.uri_language if lang and self.languages and lang in self.languages: @@ -1237,7 +1279,7 @@ class MapUrlOut(object): return None # use regex filter self.omit_lang() # try to omit language self.omit_acf() # try to omit a/c/f - return self.build_acf() # build and return the /a/lang/c/f string + return self.build_acf() # build and return the /a/lang/c/f string def map_url_in(request, env, app=False): @@ -1259,7 +1301,8 @@ def map_url_in(request, env, app=False): if app: return map.application - root_static_file, version = map.map_root_static() # handle root-static files + root_static_file, version = map.map_root_static( + ) # handle root-static files if root_static_file: map.update_request() return (root_static_file, version, map.env) @@ -1280,6 +1323,7 @@ def map_url_in(request, env, app=False): map.update_request() return (None, None, map.env) + def map_url_out(request, env, application, controller, function, args, other, scheme, host, port): ''' @@ -1311,6 +1355,7 @@ def map_url_out(request, env, application, controller, function, args, other, scheme, host, port) return map.acf() + def get_effective_router(appname): "return a private copy of the effective router for the specified application" if not routers or appname not in routers: diff --git a/gluon/rocket.py b/gluon/rocket.py index d8e832ae..af5e7133 100644 --- a/gluon/rocket.py +++ b/gluon/rocket.py @@ -16,21 +16,23 @@ import traceback VERSION = '1.2.5' SERVER_NAME = socket.gethostname() SERVER_SOFTWARE = 'Rocket %s' % VERSION -HTTP_SERVER_SOFTWARE = '%s Python/%s' % (SERVER_SOFTWARE, sys.version.split(' ')[0]) +HTTP_SERVER_SOFTWARE = '%s Python/%s' % ( + SERVER_SOFTWARE, sys.version.split(' ')[0]) BUF_SIZE = 16384 -SOCKET_TIMEOUT = 10 # in secs -THREAD_STOP_CHECK_INTERVAL = 1 # in secs, How often should threads check for a server stop message? -IS_JYTHON = platform.system() == 'Java' # Handle special cases for Jython +SOCKET_TIMEOUT = 10 # in secs +THREAD_STOP_CHECK_INTERVAL = 1 # in secs, How often should threads check for a server stop message? +IS_JYTHON = platform.system() == 'Java' # Handle special cases for Jython IGNORE_ERRORS_ON_CLOSE = set([errno.ECONNABORTED, errno.ECONNRESET]) DEFAULT_LISTEN_QUEUE_SIZE = 5 DEFAULT_MIN_THREADS = 10 DEFAULT_MAX_THREADS = 0 -DEFAULTS = dict(LISTEN_QUEUE_SIZE = DEFAULT_LISTEN_QUEUE_SIZE, - MIN_THREADS = DEFAULT_MIN_THREADS, - MAX_THREADS = DEFAULT_MAX_THREADS) +DEFAULTS = dict(LISTEN_QUEUE_SIZE=DEFAULT_LISTEN_QUEUE_SIZE, + MIN_THREADS=DEFAULT_MIN_THREADS, + MAX_THREADS=DEFAULT_MAX_THREADS) PY3K = sys.version_info[0] > 2 + class NullHandler(logging.Handler): "A Logging handler to prevent library errors." def emit(self, record): @@ -94,6 +96,7 @@ except ImportError: # TODO - This part is still very experimental. #from .filelike import FileLikeSocket + class Connection(object): __slots__ = [ 'setblocking', @@ -170,6 +173,7 @@ except ImportError: # Import Package Modules # package imports removed in monolithic build + class FileLikeSocket(object): def __init__(self, conn, buf_size=BUF_SIZE): self.conn = conn @@ -317,13 +321,12 @@ class WSGIFuture(Future): else: return super(WSGIFuture, self).set_running_or_notify_cancel() - def remember(self, name, lifespan=None): self._lifespan = lifespan or self._lifespan if name in self._mem_dict: - raise NameError('Cannot remember future by name "%s". ' % name + \ - 'A future already exists with that name.' ) + raise NameError('Cannot remember future by name "%s". ' % name + + 'A future already exists with that name.') self._name = name self._mem_dict[name] = self @@ -334,6 +337,7 @@ class WSGIFuture(Future): del self._mem_dict[self._name] self._name = None + class _WorkItem(object): def __init__(self, future, fn, args, kwargs): self.future = future @@ -353,6 +357,7 @@ class _WorkItem(object): else: self.future.set_result(result) + class WSGIExecutor(ThreadPoolExecutor): multithread = True multiprocess = False @@ -366,7 +371,8 @@ class WSGIExecutor(ThreadPoolExecutor): if self._shutdown_lock.acquire(): if self._shutdown: self._shutdown_lock.release() - raise RuntimeError('Cannot schedule new futures after shutdown') + raise RuntimeError( + 'Cannot schedule new futures after shutdown') f = WSGIFuture(self.futures) w = _WorkItem(f, fn, args, kwargs) @@ -378,6 +384,7 @@ class WSGIExecutor(ThreadPoolExecutor): else: return False + class FuturesMiddleware(object): "Futures middleware that adds a Futures Executor to the environment" def __init__(self, app, threads=5): @@ -405,11 +412,13 @@ try: has_ssl = True except ImportError: has_ssl = False + class SSLError(socket.error): pass # Import Package Modules # package imports removed in monolithic build + class Listener(Thread): """The Listener class is a class responsible for accepting connections and queuing them to be processed by a worker thread.""" @@ -446,18 +455,18 @@ class Listener(Thread): elif not os.path.exists(interface[2]): data = (interface[2], interface[0], interface[1]) self.err_log.error("Cannot find key file " - "'%s'. Cannot bind to %s:%s" % data) + "'%s'. Cannot bind to %s:%s" % data) return elif not os.path.exists(interface[3]): data = (interface[3], interface[0], interface[1]) self.err_log.error("Cannot find certificate file " - "'%s'. Cannot bind to %s:%s" % data) + "'%s'. Cannot bind to %s:%s" % data) return if self.clientcert_req and not os.path.exists(interface[4]): data = (interface[4], interface[0], interface[1]) self.err_log.error("Cannot find root ca certificate file " - "'%s'. Cannot bind to %s:%s" % data) + "'%s'. Cannot bind to %s:%s" % data) return # Set socket options @@ -499,18 +508,18 @@ class Listener(Thread): ca_certs = self.interface[4] cert_reqs = ssl.CERT_OPTIONAL sock = ssl.wrap_socket(sock, - keyfile = self.interface[2], - certfile = self.interface[3], - server_side = True, - cert_reqs = cert_reqs, - ca_certs = ca_certs, - ssl_version = ssl.PROTOCOL_SSLv23) + keyfile=self.interface[2], + certfile=self.interface[3], + server_side=True, + cert_reqs=cert_reqs, + ca_certs=ca_certs, + ssl_version=ssl.PROTOCOL_SSLv23) else: sock = ssl.wrap_socket(sock, - keyfile = self.interface[2], - certfile = self.interface[3], - server_side = True, - ssl_version = ssl.PROTOCOL_SSLv23) + keyfile=self.interface[2], + certfile=self.interface[3], + server_side=True, + ssl_version=ssl.PROTOCOL_SSLv23) except SSLError: # Generally this happens when an HTTP request is received on a @@ -598,19 +607,17 @@ except ImportError: # package imports removed in monolithic build - - - # Setup Logging log = logging.getLogger('Rocket') log.addHandler(NullHandler()) + class Rocket(object): """The Rocket class is responsible for handling threads and accepting and dispatching connections.""" def __init__(self, - interfaces = ('127.0.0.1', 8000), + interfaces=('127.0.0.1', 8000), method = 'wsgi', app_info = None, min_threads = None, @@ -650,15 +657,16 @@ class Rocket(object): self.active_queue = Queue() self._threadpool = ThreadPool(get_method(method), - app_info = app_info, - active_queue = self.active_queue, - monitor_queue = self.monitor_queue, - min_threads = min_threads, - max_threads = max_threads) + app_info=app_info, + active_queue=self.active_queue, + monitor_queue=self.monitor_queue, + min_threads=min_threads, + max_threads=max_threads) # Build our socket listeners - self.listeners = [Listener(i, queue_size, self.active_queue) for i in self.interfaces] - for ndx in range(len(self.listeners)-1, 0, -1): + self.listeners = [Listener( + i, queue_size, self.active_queue) for i in self.interfaces] + for ndx in range(len(self.listeners) - 1, 0, -1): if not self.listeners[ndx].ready: del self.listeners[ndx] @@ -705,7 +713,8 @@ class Rocket(object): str_extract = lambda l: (l.addr, l.port, l.secure and '*' or '') msg = 'Listening on sockets: ' - msg += ', '.join(['%s:%i%s' % str_extract(l) for l in self.listeners]) + msg += ', '.join( + ['%s:%i%s' % str_extract(l) for l in self.listeners]) log.info(msg) for l in self.listeners: @@ -730,7 +739,7 @@ class Rocket(object): return self.stop() - def stop(self, stoplogging = False): + def stop(self, stoplogging=False): log.info('Stopping %s' % SERVER_SOFTWARE) self.startstop_lock.acquire() @@ -773,23 +782,24 @@ class Rocket(object): self.stop() self.start() + def CherryPyWSGIServer(bind_addr, wsgi_app, - numthreads = 10, - server_name = None, - max = -1, - request_queue_size = 5, - timeout = 10, - shutdown_timeout = 5): + numthreads=10, + server_name=None, + max=-1, + request_queue_size=5, + timeout=10, + shutdown_timeout=5): """ A Cherrypy wsgiserver-compatible wrapper. """ max_threads = max if max_threads < 0: max_threads = 0 return Rocket(bind_addr, 'wsgi', {'wsgi_app': wsgi_app}, - min_threads = numthreads, - max_threads = max_threads, - queue_size = request_queue_size, - timeout = timeout) + min_threads=numthreads, + max_threads=max_threads, + queue_size=request_queue_size, + timeout=timeout) # Monolithic build...end of module: rocket\main.py # Monolithic build...start of module: rocket\monitor.py @@ -803,6 +813,7 @@ from threading import Thread # Import Package Modules # package imports removed in monolithic build + class Monitor(Thread): # Monitor worker class. @@ -926,8 +937,10 @@ class Monitor(Thread): for c in stale: if __debug__: # "EXPR and A or B" kept for Py2.4 compatibility - data = (c.client_addr, c.server_port, c.ssl and '*' or '') - self.log.debug('Flushing stale connection: %s:%i%s' % data) + data = ( + c.client_addr, c.server_port, c.ssl and '*' or '') + self.log.debug( + 'Flushing stale connection: %s:%i%s' % data) self.connections.remove(c) list_changed = True @@ -940,7 +953,6 @@ class Monitor(Thread): # Dynamically resize the threadpool to adapt to our changing needs. self._threadpool.dynamic_resize() - def stop(self): self.active = False @@ -984,6 +996,7 @@ import logging log = logging.getLogger('Rocket.Errors.ThreadPool') log.addHandler(NullHandler()) + class ThreadPool: """The ThreadPool class is a container class for all the worker threads. It manages the number of actively running threads.""" @@ -1011,7 +1024,7 @@ class ThreadPool: self.alive = False # TODO - Optimize this based on some real-world usage data - self.grow_threshold = int(max_threads/10) + 2 + self.grow_threshold = int(max_threads / 10) + 2 if not isinstance(app_info, dict): app_info = dict() @@ -1193,7 +1206,9 @@ Content-Type: %s %s ''' if IS_JYTHON: - HTTP_METHODS = set(['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT']) + HTTP_METHODS = set(['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', + 'DELETE', 'TRACE', 'CONNECT']) + class Worker(Thread): """The Worker class is a base class responsible for receiving connections @@ -1223,7 +1238,7 @@ class Worker(Thread): self.req_log.addHandler(NullHandler()) # Error Log - self.err_log = logging.getLogger('Rocket.Errors.'+self.getName()) + self.err_log = logging.getLogger('Rocket.Errors.' + self.getName()) self.err_log.addHandler(NullHandler()) def _handleError(self, typ, val, tb): @@ -1301,11 +1316,11 @@ class Worker(Thread): self.err_log.debug('Serving a request') try: self.run_app(conn) - log_info = dict(client_ip = conn.client_addr, - time = datetime.now().strftime('%c'), - status = self.status.split(' ')[0], - size = self.size, - request_line = self.request_line) + log_info = dict(client_ip=conn.client_addr, + time=datetime.now().strftime('%c'), + status=self.status.split(' ')[0], + size=self.size, + request_line=self.request_line) self.req_log.info(LOG_LINE % log_info) except: exc = sys.exc_info() @@ -1314,11 +1329,12 @@ class Worker(Thread): break else: if self.request_line: - log_info = dict(client_ip = conn.client_addr, - time = datetime.now().strftime('%c'), - status = self.status.split(' ')[0], - size = self.size, - request_line = self.request_line + ' - not stopping') + log_info = dict(client_ip=conn.client_addr, + time=datetime.now( + ).strftime('%c'), + status=self.status.split(' ')[0], + size=self.size, + request_line=self.request_line + ' - not stopping') self.req_log.info(LOG_LINE % log_info) if self.closeConnection: @@ -1382,13 +1398,15 @@ class Worker(Thread): except socket.timeout: raise SocketTimeout("Socket timed out before request.") except TypeError: - raise SocketClosed("ssl bug caused closer of socket, upgrade to python 2.7") + raise SocketClosed( + "ssl bug caused closer of socket, upgrade to python 2.7") d = d.strip() if not d: if __debug__: - self.err_log.debug('Client did not send a recognizable request.') + self.err_log.debug( + 'Client did not send a recognizable request.') raise SocketClosed('Client closed socket.') self.request_line = d @@ -1408,11 +1426,12 @@ class Worker(Thread): raise BadRequest req = match.groupdict() - for k,v in req.iteritems(): + for k, v in req.iteritems(): if not v: req[k] = "" if k == 'path': - req['path'] = r'%2F'.join([unquote(x) for x in re_SLASH.split(v)]) + req['path'] = r'%2F'.join( + [unquote(x) for x in re_SLASH.split(v)]) return req @@ -1421,15 +1440,15 @@ class Worker(Thread): try: method, uri, proto = d.split(' ') if not proto.startswith('HTTP') or \ - proto[-3:] not in ('1.0', '1.1') or \ - method not in HTTP_METHODS: + proto[-3:] not in ('1.0', '1.1') or \ + method not in HTTP_METHODS: self.send_response('400 Bad Request') raise BadRequest except ValueError: self.send_response('400 Bad Request') raise BadRequest - req = dict(method=method, protocol = proto) + req = dict(method=method, protocol=proto) scheme = '' host = '' if uri == '*' or uri.startswith('/'): @@ -1454,7 +1473,6 @@ class Worker(Thread): host=host) return req - def read_headers(self, sock_file, environ): try: lname = None @@ -1464,9 +1482,10 @@ class Worker(Thread): try: l = str(l, 'ISO-8859-1') except UnicodeDecodeError: - self.err_log.warning('Invalid request header: '+repr(l)) + self.err_log.warning( + 'Invalid request header: ' + repr(l)) - if l.strip().replace('\0','') == '': + if l.strip().replace('\0', '') == '': break elif l[0] in ' \t' and lname: # Some headers take more than one line @@ -1476,25 +1495,30 @@ class Worker(Thread): l = l.split(':', 1) # HTTP header names are us-ascii encoded - lname = str('HTTP_'+l[0].strip().upper().replace('-', '_')) + lname = str( + 'HTTP_' + l[0].strip().upper().replace('-', '_')) lval = str(l[-1].strip()) environ[lname] = lval except socket.timeout: raise SocketTimeout("Socket timed out before request.") + class SocketTimeout(Exception): "Exception for when a socket times out between requests." pass + class BadRequest(Exception): "Exception for when a client sends an incomprehensible request." pass + class SocketClosed(Exception): "Exception for when a socket is closed by the client." pass + class ChunkedReader(object): def __init__(self, sock_file): self.stream = sock_file @@ -1542,6 +1566,7 @@ class ChunkedReader(object): def readlines(self): yield self.readline() + def get_method(method): methods = dict(wsgi=WSGIWorker) return methods[method.lower()] @@ -1559,7 +1584,6 @@ from wsgiref.util import FileWrapper # package imports removed in monolithic build - if PY3K: from email.utils import formatdate else: @@ -1578,6 +1602,7 @@ BASE_ENV = {'SERVER_NAME': SERVER_NAME, 'wsgi.file_wrapper': FileWrapper } + class WSGIWorker(Worker): def __init__(self, *args, **kwargs): """Builds some instance variables that will last the life of the @@ -1588,9 +1613,10 @@ class WSGIWorker(Worker): multithreaded = self.app_info.get('max_threads') != 1 else: multithreaded = False - self.base_environ = dict({'SERVER_SOFTWARE': self.app_info['server_software'], - 'wsgi.multithread': multithreaded, - }) + self.base_environ = dict( + {'SERVER_SOFTWARE': self.app_info['server_software'], + 'wsgi.multithread': multithreaded, + }) self.base_environ.update(BASE_ENV) # Grab our application @@ -1614,7 +1640,7 @@ class WSGIWorker(Worker): environ = self.base_environ.copy() # Grab the headers - self.read_headers(sock_file,environ) + self.read_headers(sock_file, environ) # Add CGI Variables environ['SERVER_PORT'] = str(conn.server_port) @@ -1644,7 +1670,7 @@ class WSGIWorker(Worker): peercert = conn.socket.getpeercert(binary_form=True) environ['SSL_CLIENT_RAW_CERT'] = \ peercert and ssl.DER_cert_to_PEM_cert(peercert) - except Exception,e: + except Exception, e: print e if environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked': @@ -1822,7 +1848,7 @@ class WSGIWorker(Worker): if __debug__: self.err_log.debug('Finally closing output and sock_file') - if hasattr(output,'close'): + if hasattr(output, 'close'): output.close() sock_file.close() @@ -1833,19 +1859,21 @@ class WSGIWorker(Worker): # the following code is not part of Rocket but was added in web2py for testing purposes # + def demo_app(environ, start_response): global static_folder import os - types = {'htm': 'text/html','html': 'text/html','gif': 'image/gif', - 'jpg': 'image/jpeg','png': 'image/png','pdf': 'applications/pdf'} + types = {'htm': 'text/html', 'html': 'text/html', 'gif': 'image/gif', + 'jpg': 'image/jpeg', 'png': 'image/png', 'pdf': 'applications/pdf'} if static_folder: if not static_folder.startswith('/'): - static_folder = os.path.join(os.getcwd(),static_folder) - path = os.path.join(static_folder, environ['PATH_INFO'][1:] or 'index.html') - type = types.get(path.split('.')[-1],'text') + static_folder = os.path.join(os.getcwd(), static_folder) + path = os.path.join( + static_folder, environ['PATH_INFO'][1:] or 'index.html') + type = types.get(path.split('.')[-1], 'text') if os.path.exists(path): try: - data = open(path,'rb').read() + data = open(path, 'rb').read() start_response('200 OK', [('Content-Type', type)]) except IOError: start_response('404 NOT FOUND', []) @@ -1858,21 +1886,22 @@ def demo_app(environ, start_response): data = '

Hello from Rocket Web Server

' return [data] + def demo(): from optparse import OptionParser parser = OptionParser() - parser.add_option("-i", "--ip", dest="ip",default="127.0.0.1", + parser.add_option("-i", "--ip", dest="ip", default="127.0.0.1", help="ip address of the network interface") - parser.add_option("-p", "--port", dest="port",default="8000", + parser.add_option("-p", "--port", dest="port", default="8000", help="post where to run web server") - parser.add_option("-s", "--static", dest="static",default=None, + parser.add_option("-s", "--static", dest="static", default=None, help="folder containing static files") (options, args) = parser.parse_args() global static_folder static_folder = options.static print 'Rocket running on %s:%s' % (options.ip, options.port) - r=Rocket((options.ip,int(options.port)),'wsgi', {'wsgi_app':demo_app}) + r = Rocket((options.ip, int(options.port)), 'wsgi', {'wsgi_app': demo_app}) r.start() -if __name__=='__main__': +if __name__ == '__main__': demo() diff --git a/gluon/sanitizer.py b/gluon/sanitizer.py index 6708a983..e9ded858 100644 --- a/gluon/sanitizer.py +++ b/gluon/sanitizer.py @@ -47,12 +47,12 @@ class XssCleaner(HTMLParser): 'code', 'pre', 'img/', - ], + ], allowed_attributes={'a': ['href', 'title'], 'img': ['src', 'alt' - ], 'blockquote': ['type']}, + ], 'blockquote': ['type']}, fmt=AbstractFormatter, - strip_disallowed = False - ): + strip_disallowed=False + ): HTMLParser.__init__(self, fmt) self.result = '' @@ -103,7 +103,7 @@ class XssCleaner(HTMLParser): tag, method, attrs, - ): + ): if tag not in self.permitted_tags: if self.strip_disallowed: self.in_disallowed = True @@ -114,16 +114,16 @@ class XssCleaner(HTMLParser): if tag in self.allowed_attributes: attrs = dict(attrs) self.allowed_attributes_here = [x for x in - self.allowed_attributes[tag] if x in attrs - and len(attrs[x]) > 0] + self.allowed_attributes[tag] if x in attrs + and len(attrs[x]) > 0] for attribute in self.allowed_attributes_here: if attribute in ['href', 'src', 'background']: if self.url_is_acceptable(attrs[attribute]): bt += ' %s="%s"' % (attribute, - attrs[attribute]) + attrs[attribute]) else: bt += ' %s=%s' % (xssescape(attribute), - quoteattr(attrs[attribute])) + quoteattr(attrs[attribute])) if bt == '" % tag, "<%s />" % tag) if not escape: @@ -209,16 +210,17 @@ def sanitize(text, permitted_tags=[ 'code', 'pre', 'img/', - 'h1','h2','h3','h4','h5','h6', - 'table','tr','td','div', - ], - allowed_attributes = { + 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'table', 'tr', 'td', 'div', +], + allowed_attributes={ 'a': ['href', 'title'], 'img': ['src', 'alt'], 'blockquote': ['type'], 'td': ['colspan'], - }, - escape=True): - if not isinstance(text, str): return str(text) + }, + escape=True): + if not isinstance(text, str): + return str(text) return XssCleaner(permitted_tags=permitted_tags, allowed_attributes=allowed_attributes).strip(text, escape) diff --git a/gluon/scheduler.py b/gluon/scheduler.py index 9f4a5a4a..fde13d9a 100644 --- a/gluon/scheduler.py +++ b/gluon/scheduler.py @@ -106,7 +106,7 @@ DISABLED = 'DISABLED' KILL = 'KILL' EXPIRED = 'EXPIRED' SECONDS = 1 -HEARTBEAT = 3*SECONDS +HEARTBEAT = 3 * SECONDS MAXHIBERNATION = 10 CLEAROUT = '!clear!' @@ -114,20 +114,23 @@ CALLABLETYPES = (types.LambdaType, types.FunctionType, types.BuiltinFunctionType, types.MethodType, types.BuiltinMethodType) + class Task(object): - def __init__(self,app,function,timeout,args='[]',vars='{}',**kwargs): - logger.debug(' new task allocated: %s.%s' % (app,function)) + def __init__(self, app, function, timeout, args='[]', vars='{}', **kwargs): + logger.debug(' new task allocated: %s.%s' % (app, function)) self.app = app self.function = function self.timeout = timeout - self.args = args # json - self.vars = vars # json + self.args = args # json + self.vars = vars # json self.__dict__.update(kwargs) + def __str__(self): return '' % self.function + class TaskReport(object): - def __init__(self,status,result=None,output=None,tb=None): + def __init__(self, status, result=None, output=None, tb=None): logger.debug(' new task report: %s' % status) if tb: logger.debug(' traceback: %s' % tb) @@ -137,19 +140,23 @@ class TaskReport(object): self.result = result self.output = output self.tb = tb + def __str__(self): return '' % self.status -def demo_function(*argv,**kwargs): + +def demo_function(*argv, **kwargs): """ test function """ for i in range(argv[0]): - print 'click',i + print 'click', i time.sleep(1) return 'done' #the two functions below deal with simplejson decoding as unicode, esp for the dict decode #and subsequent usage as function Keyword arguments unicode variable names won't work! #borrowed from http://stackoverflow.com/questions/956867/how-to-get-string-objects-instead-unicode-ones-from-json-in-python + + def _decode_list(lst): newlist = [] for i in lst: @@ -160,19 +167,21 @@ def _decode_list(lst): newlist.append(i) return newlist + def _decode_dict(dct): newdict = {} for k, v in dct.iteritems(): if isinstance(k, unicode): k = k.encode('utf-8') if isinstance(v, unicode): - v = v.encode('utf-8') + v = v.encode('utf-8') elif isinstance(v, list): v = _decode_list(v) newdict[k] = v return newdict -def executor(queue,task, out): + +def executor(queue, task, out): """ the background process """ logger.debug(' task started') @@ -182,11 +191,14 @@ def executor(queue,task, out): self.out_queue = out_queue self.stdout = sys.stdout sys.stdout = self + def __del__(self): sys.stdout = self.stdout + def flush(self): pass - def write(self,data): + + def write(self, data): self.out_queue.put(data) stdout = LogOutput(out) @@ -200,8 +212,8 @@ def executor(queue,task, out): logging.getLogger().setLevel(logging.WARN) # Get controller-specific subdirectory if task.app is of # form 'app/controller' - (a,c,f) = parse_path_info(task.app) - _env = env(a=a,c=c,import_models=True) + (a, c, f) = parse_path_info(task.app) + _env = env(a=a, c=c, import_models=True) logging.getLogger().setLevel(level) scheduler = current._scheduler f = task.function @@ -212,22 +224,24 @@ def executor(queue,task, out): else: _function = functions.get(f) if not isinstance(_function, CALLABLETYPES): - raise NameError("name '%s' not found in scheduler's environment" % f) + raise NameError( + "name '%s' not found in scheduler's environment" % f) globals().update(_env) args = loads(task.args) vars = loads(task.vars, object_hook=_decode_dict) - result = dumps(_function(*args,**vars)) + result = dumps(_function(*args, **vars)) else: ### for testing purpose only result = eval(task.function)( *loads(task.args, object_hook=_decode_dict), - **loads(task.vars, object_hook=_decode_dict)) + **loads(task.vars, object_hook=_decode_dict)) queue.put(TaskReport(COMPLETED, result=result)) - except BaseException,e: + except BaseException, e: tb = traceback.format_exc() - queue.put(TaskReport(FAILED,tb=tb)) + queue.put(TaskReport(FAILED, tb=tb)) del stdout + class MetaScheduler(threading.Thread): def __init__(self): threading.Thread.__init__(self) @@ -235,7 +249,7 @@ class MetaScheduler(threading.Thread): self.have_heartbeat = True # set to False to kill self.empty_runs = 0 - def async(self,task): + def async(self, task): """ starts the background process and returns: ('ok',result,output) @@ -247,7 +261,7 @@ class MetaScheduler(threading.Thread): sr = db.scheduler_run out = multiprocessing.Queue() queue = multiprocessing.Queue(maxsize=1) - p = multiprocessing.Process(target=executor,args=(queue,task,out)) + p = multiprocessing.Process(target=executor, args=(queue, task, out)) self.process = p logger.debug(' task starting') p.start() @@ -264,11 +278,11 @@ class MetaScheduler(threading.Thread): start = time.time() while p.is_alive() and ( - not task.timeout or time.time()-start < task.timeout): + not task.timeout or time.time() - start < task.timeout): if tout: try: logger.debug(' partial output saved') - db(sr.id==task.run_id).update(output = task_output) + db(sr.id == task.run_id).update(output=task_output) db.commit() except: pass @@ -279,7 +293,8 @@ class MetaScheduler(threading.Thread): if tout: logger.debug(' partial output: "%s"' % str(tout)) if CLEAROUT in tout: - task_output = tout[tout.rfind(CLEAROUT)+len(CLEAROUT):] + task_output = tout[ + tout.rfind(CLEAROUT) + len(CLEAROUT):] else: task_output += tout except: @@ -322,7 +337,7 @@ class MetaScheduler(threading.Thread): try: self.process.terminate() except: - pass # no process to terminate + pass # no process to terminate def run(self): """ the thread that sends heartbeat """ @@ -334,19 +349,19 @@ class MetaScheduler(threading.Thread): def start_heartbeats(self): self.start() - def send_heartbeat(self,counter): + def send_heartbeat(self, counter): print 'thum' time.sleep(1) def pop_task(self): return Task( - app = None, - function = 'demo_function', - timeout = 7, - args = '[2]', - vars = '{}') + app=None, + function='demo_function', + timeout=7, + args='[2]', + vars='{}') - def report_task(self,task,task_report): + def report_task(self, task, task_report): print 'reporting task' pass @@ -361,14 +376,16 @@ class MetaScheduler(threading.Thread): task = self.pop_task() if task: self.empty_runs = 0 - self.report_task(task,self.async(task)) + self.report_task(task, self.async(task)) else: self.empty_runs += 1 logger.debug('sleeping...') if self.max_empty_runs != 0: - logger.debug('empty runs %s/%s', self.empty_runs, self.max_empty_runs) + logger.debug('empty runs %s/%s', + self.empty_runs, self.max_empty_runs) if self.empty_runs >= self.max_empty_runs: - logger.info('empty runs limit reached, killing myself') + logger.info( + 'empty runs limit reached, killing myself') self.die() self.sleep() except KeyboardInterrupt: @@ -379,33 +396,35 @@ TASK_STATUS = (QUEUED, RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED, EXPIRED) RUN_STATUS = (RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED) WORKER_STATUS = (ACTIVE, DISABLED, TERMINATE, KILL) + class TYPE(object): """ validator that check whether field is valid json and validate its type """ - def __init__(self,myclass=list,parse=False): + def __init__(self, myclass=list, parse=False): self.myclass = myclass self.parse = parse - def __call__(self,value): + def __call__(self, value): from gluon import current try: obj = loads(value) except: - return (value,current.T('invalid json')) + return (value, current.T('invalid json')) else: - if isinstance(obj,self.myclass): + if isinstance(obj, self.myclass): if self.parse: - return (obj,None) + return (obj, None) else: - return (value,None) + return (value, None) else: - return (value,current.T('Not of type: %s') % self.myclass) + return (value, current.T('Not of type: %s') % self.myclass) + class Scheduler(MetaScheduler): - def __init__(self,db,tasks=None,migrate=True, - worker_name=None,group_names=['main'],heartbeat=HEARTBEAT, + def __init__(self, db, tasks=None, migrate=True, + worker_name=None, group_names=['main'], heartbeat=HEARTBEAT, max_empty_runs=0, discard_results=False, utc_time=False): MetaScheduler.__init__(self) @@ -415,8 +434,9 @@ class Scheduler(MetaScheduler): self.tasks = tasks self.group_names = group_names self.heartbeat = heartbeat - self.worker_name = worker_name or socket.gethostname()+'#'+str(os.getpid()) - self.worker_status = RUNNING, 1 #tuple containing status as recorded in + self.worker_name = worker_name or socket.gethostname( + ) + '#' + str(os.getpid()) + self.worker_status = RUNNING, 1 # tuple containing status as recorded in #the table, plus a boost parameter for #hibernation (i.e. when someone stop the #worker acting on the scheduler_worker table) @@ -429,86 +449,85 @@ class Scheduler(MetaScheduler): from gluon import current current._scheduler = self - self.define_tables(db,migrate=migrate) + self.define_tables(db, migrate=migrate) def now(self): return self.utc_time and datetime.datetime.utcnow() or datetime.datetime.now() def set_requirements(self, scheduler_task): from gluon import current - if hasattr(current,'request'): - scheduler_task.application_name.default= '%s/%s' % ( - current.request.application, current.request.controller + if hasattr(current, 'request'): + scheduler_task.application_name.default = '%s/%s' % ( + current.request.application, current.request.controller ) - def define_tables(self,db,migrate): + def define_tables(self, db, migrate): from gluon.dal import DEFAULT logger.debug('defining tables (migrate=%s)' % migrate) now = self.now db.define_table( 'scheduler_task', - Field('application_name',requires=IS_NOT_EMPTY(), - default=None,writable=False), - Field('task_name',default=None), - Field('group_name',default='main'), - Field('status',requires=IS_IN_SET(TASK_STATUS), - default=QUEUED,writable=False), + Field('application_name', requires=IS_NOT_EMPTY(), + default=None, writable=False), + Field('task_name', default=None), + Field('group_name', default='main'), + Field('status', requires=IS_IN_SET(TASK_STATUS), + default=QUEUED, writable=False), Field('function_name', requires=IS_IN_SET(sorted(self.tasks.keys())) if self.tasks else DEFAULT), Field('uuid', requires=IS_NOT_IN_DB(db, 'scheduler_task.uuid'), unique=True, default=web2py_uuid), - Field('args','text',default='[]',requires=TYPE(list)), - Field('vars','text',default='{}',requires=TYPE(dict)), - Field('enabled','boolean',default=True), - Field('start_time','datetime',default=now, - requires = IS_DATETIME()), - Field('next_run_time','datetime',default=now), - Field('stop_time','datetime'), - Field('repeats','integer',default=1,comment="0=unlimited", + Field('args', 'text', default='[]', requires=TYPE(list)), + Field('vars', 'text', default='{}', requires=TYPE(dict)), + Field('enabled', 'boolean', default=True), + Field('start_time', 'datetime', default=now, + requires=IS_DATETIME()), + Field('next_run_time', 'datetime', default=now), + Field('stop_time', 'datetime'), + Field('repeats', 'integer', default=1, comment="0=unlimited", requires=IS_INT_IN_RANGE(0, None)), Field('retry_failed', 'integer', default=0, comment="-1=unlimited", requires=IS_INT_IN_RANGE(-1, None)), - Field('period','integer',default=60,comment='seconds', + Field('period', 'integer', default=60, comment='seconds', requires=IS_INT_IN_RANGE(0, None)), - Field('timeout','integer',default=60,comment='seconds', + Field('timeout', 'integer', default=60, comment='seconds', requires=IS_INT_IN_RANGE(0, None)), Field('sync_output', 'integer', default=0, comment="update output every n sec: 0=never", requires=IS_INT_IN_RANGE(0, None)), - Field('times_run','integer',default=0,writable=False), - Field('times_failed','integer',default=0,writable=False), - Field('last_run_time','datetime',writable=False,readable=False), - Field('assigned_worker_name',default='',writable=False), + Field('times_run', 'integer', default=0, writable=False), + Field('times_failed', 'integer', default=0, writable=False), + Field('last_run_time', 'datetime', writable=False, readable=False), + Field('assigned_worker_name', default='', writable=False), on_define=self.set_requirements, - migrate=migrate,format='%(task_name)s') - + migrate=migrate, format='%(task_name)s') db.define_table( 'scheduler_run', - Field('scheduler_task','reference scheduler_task'), - Field('status',requires=IS_IN_SET(RUN_STATUS)), - Field('start_time','datetime'), - Field('stop_time','datetime'), - Field('output','text'), - Field('result','text'), - Field('traceback','text'), - Field('worker_name',default=self.worker_name), + Field('scheduler_task', 'reference scheduler_task'), + Field('status', requires=IS_IN_SET(RUN_STATUS)), + Field('start_time', 'datetime'), + Field('stop_time', 'datetime'), + Field('output', 'text'), + Field('result', 'text'), + Field('traceback', 'text'), + Field('worker_name', default=self.worker_name), migrate=migrate) db.define_table( 'scheduler_worker', Field('worker_name', unique=True), - Field('first_heartbeat','datetime'), - Field('last_heartbeat','datetime'), - Field('status',requires=IS_IN_SET(WORKER_STATUS)), + Field('first_heartbeat', 'datetime'), + Field('last_heartbeat', 'datetime'), + Field('status', requires=IS_IN_SET(WORKER_STATUS)), Field('is_ticker', 'boolean', default=False, writable=False), Field('group_names', 'list:string', default=self.group_names), migrate=migrate) if migrate: db.commit() - def loop(self,worker_name=None): + def loop(self, worker_name=None): signal.signal(signal.SIGTERM, lambda signum, stack_frame: sys.exit(1)) try: self.start_heartbeats() @@ -521,14 +540,16 @@ class Scheduler(MetaScheduler): task = self.pop_task() if task: self.empty_runs = 0 - self.report_task(task,self.async(task)) + self.report_task(task, self.async(task)) else: self.empty_runs += 1 logger.debug('sleeping...') if self.max_empty_runs != 0: - logger.debug('empty runs %s/%s', self.empty_runs, self.max_empty_runs) + logger.debug('empty runs %s/%s', + self.empty_runs, self.max_empty_runs) if self.empty_runs >= self.max_empty_runs: - logger.info('empty runs limit reached, killing myself') + logger.info( + 'empty runs limit reached, killing myself') self.die() self.sleep() except (KeyboardInterrupt, SystemExit): @@ -541,7 +562,7 @@ class Scheduler(MetaScheduler): if self.is_a_ticker and self.do_assign_tasks: #I'm a ticker, and 5 loops passed without reassigning tasks, let's do #that and loop again - db.commit() #?don't know if it's useful, let's be completely sure + db.commit() # ?don't know if it's useful, let's be completely sure while True: try: self.assign_tasks() @@ -552,21 +573,22 @@ class Scheduler(MetaScheduler): logger.error('TICKER: error assigning tasks') return None db.commit() - grabbed = db(ts.assigned_worker_name==self.worker_name)\ - (ts.status==ASSIGNED) + grabbed = db(ts.assigned_worker_name == self.worker_name)( + ts.status == ASSIGNED) - task = grabbed.select(limitby=(0,1), orderby=ts.next_run_time).first() + task = grabbed.select(limitby=(0, 1), orderby=ts.next_run_time).first() if task: - task.update_record(status=RUNNING,last_run_time=now) + task.update_record(status=RUNNING, last_run_time=now) #noone will touch my task! db.commit() logger.debug(' work to do %s' % task.id) else: logger.debug('nothing to do') return None - next_run_time = task.last_run_time + datetime.timedelta(seconds=task.period) + next_run_time = task.last_run_time + datetime.timedelta( + seconds=task.period) times_run = task.times_run + 1 - if times_run < task.repeats or task.repeats==0: + if times_run < task.repeats or task.repeats == 0: run_again = True else: run_again = False @@ -575,7 +597,7 @@ class Scheduler(MetaScheduler): logger.debug(' new scheduler_run record') try: run_id = db.scheduler_run.insert( - scheduler_task = task.id, + scheduler_task=task.id, status=RUNNING, start_time=now, worker_name=self.worker_name) @@ -585,22 +607,22 @@ class Scheduler(MetaScheduler): db.rollback() logger.info('new task %(id)s "%(task_name)s" %(application_name)s.%(function_name)s' % task) return Task( - app = task.application_name, - function = task.function_name, - timeout = task.timeout, - args = task.args, #in json - vars = task.vars, #in json - task_id = task.id, - run_id = run_id, - run_again = run_again, + app=task.application_name, + function=task.function_name, + timeout=task.timeout, + args=task.args, # in json + vars=task.vars, # in json + task_id=task.id, + run_id=run_id, + run_again=run_again, next_run_time=next_run_time, - times_run = times_run, - stop_time = task.stop_time, - retry_failed = task.retry_failed, - times_failed = task.times_failed, - sync_output = task.sync_output) + times_run=times_run, + stop_time=task.stop_time, + retry_failed=task.retry_failed, + times_failed=task.times_failed, + sync_output=task.sync_output) - def report_task(self,task,task_report): + def report_task(self, task, task_report): db = self.db now = self.now() while True: @@ -610,16 +632,17 @@ class Scheduler(MetaScheduler): #result is 'null' as a string if task completed #if it's stopped it's None as NoneType, so we record #the STOPPED "run" anyway - logger.debug(' recording task report in db (%s)' % task_report.status) - db(db.scheduler_run.id==task.run_id).update( - status = task_report.status, - stop_time = now, - result = task_report.result, - output = task_report.output, - traceback = task_report.tb) + logger.debug(' recording task report in db (%s)' % + task_report.status) + db(db.scheduler_run.id == task.run_id).update( + status=task_report.status, + stop_time=now, + result=task_report.result, + output=task_report.output, + traceback=task_report.tb) else: logger.debug(' deleting task report in db because of no result') - db(db.scheduler_run.id==task.run_id).delete() + db(db.scheduler_run.id == task.run_id).delete() is_expired = (task.stop_time and task.next_run_time > task.stop_time and True or False) @@ -627,26 +650,25 @@ class Scheduler(MetaScheduler): or task.run_again and not is_expired and QUEUED or COMPLETED) if task_report.status == COMPLETED: - d = dict(status = status, - next_run_time = task.next_run_time, - times_run = task.times_run, - times_failed = 0 + d = dict(status=status, + next_run_time=task.next_run_time, + times_run=task.times_run, + times_failed=0 ) - db(db.scheduler_task.id==task.task_id)\ - (db.scheduler_task.status==RUNNING).update(**d) + db(db.scheduler_task.id == task.task_id)( + db.scheduler_task.status == RUNNING).update(**d) else: - st_mapping = {'FAILED':'FAILED', - 'TIMEOUT':'TIMEOUT', - 'STOPPED':'QUEUED'}[task_report.status] + st_mapping = {'FAILED': 'FAILED', + 'TIMEOUT': 'TIMEOUT', + 'STOPPED': 'QUEUED'}[task_report.status] status = (task.retry_failed and task.times_failed < task.retry_failed - and QUEUED or task.retry_failed==-1 + and QUEUED or task.retry_failed == -1 and QUEUED or st_mapping) - db(db.scheduler_task.id==task.task_id)\ - (db.scheduler_task.status==RUNNING).update( - times_failed=db.scheduler_task.times_failed+1, - next_run_time = task.next_run_time, - status=status) + db(db.scheduler_task.id == task.task_id)(db.scheduler_task.status == RUNNING).update( + times_failed=db.scheduler_task.times_failed + 1, + next_run_time=task.next_run_time, + status=status) db.commit() logger.info('task completed (%s)' % task_report.status) break @@ -655,34 +677,39 @@ class Scheduler(MetaScheduler): def adj_hibernation(self): if self.worker_status[0] == DISABLED: - hibernation = self.worker_status[1] + 1 if self.worker_status[1] < MAXHIBERNATION else MAXHIBERNATION + hibernation = self.worker_status[1] + 1 if self.worker_status[ + 1] < MAXHIBERNATION else MAXHIBERNATION self.worker_status = DISABLED, hibernation - def send_heartbeat(self,counter): + def send_heartbeat(self, counter): if not self.db_thread: logger.debug('thread building own DAL object') - self.db_thread = DAL(self.db._uri,folder = self.db._adapter.folder) - self.define_tables(self.db_thread,migrate=False) + self.db_thread = DAL( + self.db._uri, folder=self.db._adapter.folder) + self.define_tables(self.db_thread, migrate=False) try: db = self.db_thread sw, st = db.scheduler_worker, db.scheduler_task now = self.now() - expiration = now-datetime.timedelta(seconds=self.heartbeat*3) - departure = now-datetime.timedelta(seconds=self.heartbeat*3*MAXHIBERNATION) + expiration = now - datetime.timedelta(seconds=self.heartbeat * 3) + departure = now - datetime.timedelta( + seconds=self.heartbeat * 3 * MAXHIBERNATION) # record heartbeat - mybackedstatus = db(sw.worker_name==self.worker_name).select().first() + mybackedstatus = db( + sw.worker_name == self.worker_name).select().first() if not mybackedstatus: - sw.insert(status = ACTIVE,worker_name = self.worker_name, - first_heartbeat = now,last_heartbeat = now, - group_names = self.group_names) - self.worker_status = ACTIVE, 1 #activating the process + sw.insert(status=ACTIVE, worker_name=self.worker_name, + first_heartbeat=now, last_heartbeat=now, + group_names=self.group_names) + self.worker_status = ACTIVE, 1 # activating the process else: if mybackedstatus.status == DISABLED: - self.worker_status = DISABLED, self.worker_status[1]#keep sleeping + self.worker_status = DISABLED, self.worker_status[ + 1] # keep sleeping if self.worker_status[1] == MAXHIBERNATION: logger.debug('........recording heartbeat') - db(sw.worker_name==self.worker_name).update( - last_heartbeat = now) + db(sw.worker_name == self.worker_name).update( + last_heartbeat=now) elif mybackedstatus.status == TERMINATE: self.worker_status = TERMINATE, self.worker_status[1] @@ -695,23 +722,24 @@ class Scheduler(MetaScheduler): else: logger.debug('........recording heartbeat') - db(sw.worker_name==self.worker_name).update( - last_heartbeat = now, status = ACTIVE) - self.worker_status = ACTIVE, 1 #re-activating the process + db(sw.worker_name == self.worker_name).update( + last_heartbeat=now, status=ACTIVE) + self.worker_status = ACTIVE, 1 # re-activating the process self.do_assign_tasks = False if counter % 5 == 0: try: # delete inactive workers - logger.debug(' freeing workers that have not sent heartbeat') + logger.debug( + ' freeing workers that have not sent heartbeat') inactive_workers = db( - ((sw.last_heartbeatnow))\ - (ts.next_run_time<=now)\ - (ts.enabled==True) + all_available = db(ts.status.belongs((QUEUED, ASSIGNED)))((ts.times_run < ts.repeats) | (ts.repeats == 0))(ts.start_time <= now)((ts.stop_time is None) | (ts.stop_time > now))(ts.next_run_time <= now)(ts.enabled == True) limit = len(all_workers) * 50 #if there are a moltitude of tasks, let's assign a maximum of 50 tasks per worker. @@ -770,7 +798,8 @@ class Scheduler(MetaScheduler): #50 is quite a sweet spot also for fast tasks, with sane heartbeat values #NB: ticker reassign tasks every 5 cycles, so if a worker completes his 50 tasks in less #than heartbeat*5 seconds, it won't pick new tasks until heartbeat*5 seconds pass. - tasks = all_available.select(limitby=(0,limit), orderby=ts.next_run_time) + tasks = all_available.select( + limitby=(0, limit), orderby=ts.next_run_time) #everything until now is going fine. If a worker is currently elaborating a long task, #all other tasks assigned to him needs to be reassigned "freely" to other workers, that may be free. #this shuffles up things a bit, in order to maintain the idea of a semi-linear scalability @@ -803,7 +832,8 @@ class Scheduler(MetaScheduler): logger.info('TICKER: tasks are %s' % len(tasks)) def sleep(self): - time.sleep(self.heartbeat*self.worker_status[1]) # should only sleep until next available task + time.sleep(self.heartbeat * self.worker_status[1]) + # should only sleep until next available task def queue_task(self, function, pargs=[], pvars={}, **kwargs): """ @@ -829,12 +859,12 @@ class Scheduler(MetaScheduler): tname = 'task_name' in kwargs and kwargs.pop('task_name') or function print 'a', targs rtn = self.db.scheduler_task.validate_and_insert( - function_name=function, - task_name=tname, - args=targs, - vars=tvars, - uuid=tuuid, - **kwargs) + function_name=function, + task_name=tname, + args=targs, + vars=tvars, + uuid=tuuid, + **kwargs) if not rtn.errors: rtn.uuid = tuuid else: @@ -866,25 +896,28 @@ class Scheduler(MetaScheduler): elif isinstance(ref, Query): q = ref else: - raise SyntaxError, "You can retrieve results only by id, uuid or Query" + raise SyntaxError( + "You can retrieve results only by id, uuid or Query") fields = st.ALL left = False orderby = ~st.id if output: fields = st.ALL, sr.ALL left = sr.on(sr.scheduler_task == st.id) - orderby = ~st.id|~sr.id + orderby = ~st.id | ~sr.id row = self.db(q).select( *fields, orderby=orderby, left=left, - limitby=(0,1) - ).first() + limitby=(0, 1) + ).first() if output: row.result = row.scheduler_run.result and \ - loads(row.scheduler_run.result, object_hook=_decode_dict) or None + loads(row.scheduler_run.result, + object_hook=_decode_dict) or None return row + def main(): """ allows to run worker without python web2py.py .... by simply python this.py @@ -894,34 +927,34 @@ def main(): "-w", "--worker_name", dest="worker_name", default=None, help="start a worker with name") parser.add_option( - "-b", "--heartbeat",dest="heartbeat", default = 10, + "-b", "--heartbeat", dest="heartbeat", default=10, type='int', help="heartbeat time in seconds (default 10)") parser.add_option( - "-L", "--logger_level",dest="logger_level", + "-L", "--logger_level", dest="logger_level", default=30, type='int', help="set debug output level (0-100, 0 means all, 100 means none;default is 30)") parser.add_option("-E", "--empty-runs", - dest="max_empty_runs", - type='int', - default = 0, - help="max loops with no grabbed tasks permitted (0 for never check)") + dest="max_empty_runs", + type='int', + default=0, + help="max loops with no grabbed tasks permitted (0 for never check)") parser.add_option( - "-g", "--group_names",dest="group_names", - default = 'main', + "-g", "--group_names", dest="group_names", + default='main', help="comma separated list of groups to be picked by the worker") parser.add_option( - "-f", "--db_folder",dest="db_folder", - default = '/Users/mdipierro/web2py/applications/scheduler/databases', + "-f", "--db_folder", dest="db_folder", + default='/Users/mdipierro/web2py/applications/scheduler/databases', help="location of the dal database folder") parser.add_option( - "-u", "--db_uri",dest="db_uri", - default = 'sqlite://storage.sqlite', + "-u", "--db_uri", dest="db_uri", + default='sqlite://storage.sqlite', help="database URI string (web2py DAL syntax)") parser.add_option( - "-t", "--tasks",dest="tasks",default=None, - help="file containing task files, must define" + \ - "tasks = {'task_name':(lambda: 'output')} or similar set of tasks") + "-t", "--tasks", dest="tasks", default=None, + help="file containing task files, must define" + + "tasks = {'task_name':(lambda: 'output')} or similar set of tasks") parser.add_option( "-U", "--utc-time", dest="utc_time", default=False, help="work with UTC timestamps" @@ -930,35 +963,35 @@ def main(): if not options.tasks or not options.db_uri: print USAGE if options.tasks: - path,filename = os.path.split(options.tasks) + path, filename = os.path.split(options.tasks) if filename.endswith('.py'): filename = filename[:-3] sys.path.append(path) print 'importing tasks...' tasks = __import__(filename, globals(), locals(), [], -1).tasks - print 'tasks found: '+', '.join(tasks.keys()) + print 'tasks found: ' + ', '.join(tasks.keys()) else: tasks = {} group_names = [x.strip() for x in options.group_names.split(',')] logging.getLogger().setLevel(options.logger_level) - print 'groups for this worker: '+', '.join(group_names) + print 'groups for this worker: ' + ', '.join(group_names) print 'connecting to database in folder: ' + options.db_folder or './' - print 'using URI: '+options.db_uri - db = DAL(options.db_uri,folder=options.db_folder) + print 'using URI: ' + options.db_uri + db = DAL(options.db_uri, folder=options.db_folder) print 'instantiating scheduler...' - scheduler=Scheduler(db = db, - worker_name = options.worker_name, - tasks = tasks, - migrate = True, - group_names = group_names, - heartbeat = options.heartbeat, - max_empty_runs = options.max_empty_runs, - utc_time = options.utc_time) + scheduler = Scheduler(db=db, + worker_name=options.worker_name, + tasks=tasks, + migrate=True, + group_names=group_names, + heartbeat=options.heartbeat, + max_empty_runs=options.max_empty_runs, + utc_time=options.utc_time) signal.signal(signal.SIGTERM, lambda signum, stack_frame: sys.exit(1)) print 'starting main worker loop...' scheduler.loop() -if __name__=='__main__': +if __name__ == '__main__': main() diff --git a/gluon/serializers.py b/gluon/serializers.py index b9f2b04c..b50ab994 100644 --- a/gluon/serializers.py +++ b/gluon/serializers.py @@ -19,61 +19,64 @@ except ImportError: except: import contrib.simplejson as json_parser # fallback to pure-Python module + def custom_json(o): - if hasattr(o,'custom_json') and callable(o.custom_json): + if hasattr(o, 'custom_json') and callable(o.custom_json): return o.custom_json() if isinstance(o, (datetime.date, datetime.datetime, datetime.time)): - return o.isoformat()[:19].replace('T',' ') + return o.isoformat()[:19].replace('T', ' ') elif isinstance(o, (int, long)): return int(o) elif isinstance(o, decimal.Decimal): return str(o) elif isinstance(o, lazyT): return str(o) - elif isinstance(o,XmlComponent): + elif isinstance(o, XmlComponent): return str(o) - elif hasattr(o,'as_list') and callable(o.as_list): + elif hasattr(o, 'as_list') and callable(o.as_list): return o.as_list() - elif hasattr(o,'as_dict') and callable(o.as_dict): + elif hasattr(o, 'as_dict') and callable(o.as_dict): return o.as_dict() else: raise TypeError(repr(o) + " is not JSON serializable") def xml_rec(value, key, quote=True): - if hasattr(value,'custom_xml') and callable(value.custom_xml): + if hasattr(value, 'custom_xml') and callable(value.custom_xml): return value.custom_xml() elif isinstance(value, (dict, Storage)): - return TAG[key](*[TAG[k](xml_rec(v, '',quote)) \ - for k, v in value.items()]) + return TAG[key](*[TAG[k](xml_rec(v, '', quote)) + for k, v in value.items()]) elif isinstance(value, list): - return TAG[key](*[TAG.item(xml_rec(item, '',quote)) for item in value]) - elif hasattr(value,'as_list') and callable(value.as_list): - return str(xml_rec(value.as_list(),'',quote)) - elif hasattr(value,'as_dict') and callable(value.as_dict): - return str(xml_rec(value.as_dict(),'',quote)) + return TAG[key](*[TAG.item(xml_rec(item, '', quote)) for item in value]) + elif hasattr(value, 'as_list') and callable(value.as_list): + return str(xml_rec(value.as_list(), '', quote)) + elif hasattr(value, 'as_dict') and callable(value.as_dict): + return str(xml_rec(value.as_dict(), '', quote)) else: - return xmlescape(value,quote) + return xmlescape(value, quote) def xml(value, encoding='UTF-8', key='document', quote=True): - return ('' % encoding) + str(xml_rec(value,key,quote)) + return ('' % encoding) + str(xml_rec(value, key, quote)) -def json(value,default=custom_json): - return json_parser.dumps(value,default=default) +def json(value, default=custom_json): + return json_parser.dumps(value, default=default) def csv(value): return '' + def ics(events, title=None, link=None, timeshift=0, **ignored): import datetime title = title or '(unkown)' if link and not callable(link): - link = lambda item,prefix=link: prefix.replace('[id]',str(item['id'])) + link = lambda item, prefix=link: prefix.replace( + '[id]', str(item['id'])) s = 'BEGIN:VCALENDAR' s += '\nVERSION:2.0' s += '\nX-WR-CALNAME:%s' % title @@ -86,9 +89,9 @@ def ics(events, title=None, link=None, timeshift=0, **ignored): s += '\nUID:%s' % item['id'] if link: s += '\nURL:%s' % link(item) - shift = datetime.timedelta(seconds=3600*timeshift) - start = item['start_datetime']+shift - stop = item['stop_datetime']+shift + shift = datetime.timedelta(seconds=3600 * timeshift) + start = item['start_datetime'] + shift + stop = item['stop_datetime'] + shift s += '\nDTSTART:%s' % start.strftime('%Y%m%dT%H%M%S') s += '\nDTEND:%s' % stop.strftime('%Y%m%dT%H%M%S') s += '\nSUMMARY:%s' % item['title'] @@ -96,18 +99,19 @@ def ics(events, title=None, link=None, timeshift=0, **ignored): s += '\nEND:VCALENDAR' return s + def rss(feed): if not 'entries' in feed and 'items' in feed: feed['entries'] = feed['items'] - now=datetime.datetime.now() - rss = rss2.RSS2(title = str(feed.get('title','(notitle)')), - link = str(feed.get('link',None)), - description = str(feed.get('description','')), - lastBuildDate = feed.get('created_on', now), - items = [rss2.RSSItem( - title=str(entry.get('title','(notitle)')), - link=str(entry.get('link',None)), - description=str(entry.get('description','')), - pubDate=entry.get('created_on', now) - ) for entry in feed.get('entries',[])]) + now = datetime.datetime.now() + rss = rss2.RSS2(title=str(feed.get('title', '(notitle)')), + link=str(feed.get('link', None)), + description=str(feed.get('description', '')), + lastBuildDate=feed.get('created_on', now), + items=[rss2.RSSItem( + title=str(entry.get('title', '(notitle)')), + link=str(entry.get('link', None)), + description=str(entry.get('description', '')), + pubDate=entry.get('created_on', now) + ) for entry in feed.get('entries', [])]) return rss2.dumps(rss) diff --git a/gluon/settings.py b/gluon/settings.py index 655e6467..76058408 100644 --- a/gluon/settings.py +++ b/gluon/settings.py @@ -29,7 +29,7 @@ global_settings.app_folders = set() global_settings.debugging = False global_settings.is_pypy = \ - hasattr(platform,'python_implementation') and \ + hasattr(platform, 'python_implementation') and \ platform.python_implementation() == 'PyPy' global_settings.is_jython = \ diff --git a/gluon/shell.py b/gluon/shell.py index 10469a1c..b3930329 100644 --- a/gluon/shell.py +++ b/gluon/shell.py @@ -30,12 +30,13 @@ from dal import BaseAdapter logger = logging.getLogger("web2py") + def exec_environment( pyfile='', request=None, response=None, session=None, - ): +): """ .. function:: gluon.shell.exec_environment([pyfile=''[, request=Request() [, response=Response[, session=Session()]]]]) @@ -50,9 +51,12 @@ def exec_environment( """ - if request is None: request = Request() - if response is None: response = Response() - if session is None: session = Session() + if request is None: + request = Request() + if response is None: + response = Response() + if session is None: + session = Session() if request.folder is None: mo = re.match(r'(|.*/)applications/(?P[^/]+)', pyfile) @@ -78,7 +82,7 @@ def env( f=None, dir='', extra_request={}, - ): +): """ Return web2py execution environment for application (a), controller (c), function (f). @@ -113,7 +117,7 @@ def env( request.env.remote_addr = '127.0.0.1' request.env.web2py_runtime_gae = global_settings.web2py_runtime_gae - for k,v in extra_request.items(): + for k, v in extra_request.items(): request[k] = v # Monkey patch so credentials checks pass. @@ -129,7 +133,7 @@ def env( try: run_models_in(environment) except RestrictedError, e: - sys.stderr.write(e.traceback+'\n') + sys.stderr.write(e.traceback + '\n') sys.exit(1) environment['__name__'] = '__main__' @@ -156,7 +160,7 @@ def run( startfile=None, bpython=False, python_code=False - ): +): """ Start interactive shell or run Python script (startfile) in web2py controller environment. appname is formatted like: @@ -172,7 +176,8 @@ def run( adir = os.path.join('applications', a) if not os.path.exists(adir): if sys.stdin and not sys.stdin.name == '/dev/null': - confirm = raw_input('application %s does not exist, create (y/n)?' % a) + confirm = raw_input( + 'application %s does not exist, create (y/n)?' % a) else: logging.warn('application does not exist and will not be created') return @@ -180,16 +185,17 @@ def run( os.mkdir(adir) w2p_unpack('welcome.w2p', adir) - for subfolder in ['models','views','controllers', 'databases', - 'modules','cron','errors','sessions', - 'languages','static','private','uploads']: - subpath = os.path.join(adir,subfolder) + for subfolder in ['models', 'views', 'controllers', 'databases', + 'modules', 'cron', 'errors', 'sessions', + 'languages', 'static', 'private', 'uploads']: + subpath = os.path.join(adir, subfolder) if not os.path.exists(subpath): os.mkdir(subpath) - db = os.path.join(adir,'models/db.py') + db = os.path.join(adir, 'models/db.py') if os.path.exists(db): data = fileutils.read_file(db) - data = data.replace('','sha512:'+web2py_uuid()) + data = data.replace( + '', 'sha512:' + web2py_uuid()) fileutils.write_file(db, data) if c: @@ -198,7 +204,8 @@ def run( if c: cfile = os.path.join('applications', a, 'controllers', c + '.py') if not os.path.isfile(cfile): - cfile = os.path.join('applications', a, 'compiled', "controllers_%s_%s.pyc" % (c,f)) + cfile = os.path.join('applications', a, 'compiled', + "controllers_%s_%s.pyc" % (c, f)) if not os.path.isfile(cfile): die(errmsg) else: @@ -214,17 +221,21 @@ def run( if startfile: try: execfile(startfile, _env) - if import_models: BaseAdapter.close_all_instances('commit') + if import_models: + BaseAdapter.close_all_instances('commit') except Exception, e: print traceback.format_exc() - if import_models: BaseAdapter.close_all_instances('rollback') + if import_models: + BaseAdapter.close_all_instances('rollback') elif python_code: try: exec(python_code, _env) - if import_models: BaseAdapter.close_all_instances('commit') + if import_models: + BaseAdapter.close_all_instances('commit') except Exception, e: print traceback.format_exc() - if import_models: BaseAdapter.close_all_instances('rollback') + if import_models: + BaseAdapter.close_all_instances('rollback') else: if not plain: if bpython: @@ -248,7 +259,7 @@ def run( # IPython; thanks Michael Toomim if '__builtins__' in _env: del _env['__builtins__'] - shell = IPython.Shell.IPShell(argv=[],user_ns=_env) + shell = IPython.Shell.IPShell(argv=[], user_ns=_env) shell.mainloop() return except: @@ -304,7 +315,7 @@ def test(testpath, import_models=True, verbose=False): mo = re.match(r'(|.*/)applications/(?P[^/]+)', testpath) if not mo: die('test file is not in application directory: %s' - % testpath) + % testpath) a = mo.group('a') c = f = None files = [testpath] @@ -340,8 +351,8 @@ def test(testpath, import_models=True, verbose=False): globs = env(a, c=c, f=f, import_models=import_models) execfile(testfile, globs) doctest.run_docstring_examples(obj, globs=globs, - name='%s: %s' % (os.path.basename(testfile), - name), verbose=verbose) + name='%s: %s' % (os.path.basename(testfile), + name), verbose=verbose) if type(obj) in (types.TypeType, types.ClassType): for attr_name in dir(obj): @@ -369,8 +380,8 @@ def execute_from_command_line(argv=None): parser = optparse.OptionParser(usage=get_usage()) parser.add_option('-S', '--shell', dest='shell', metavar='APPNAME', - help='run web2py in interactive shell or IPython(if installed) ' + \ - 'with specified appname') + help='run web2py in interactive shell or IPython(if installed) ' + + 'with specified appname') msg = 'run web2py in interactive shell or bpython (if installed) with' msg += ' specified appname (if app does not exist it will be created).' msg += '\n Use combined with --shell' @@ -381,7 +392,7 @@ def execute_from_command_line(argv=None): default=False, dest='bpython', help=msg, - ) + ) parser.add_option( '-P', '--plain', @@ -389,25 +400,25 @@ def execute_from_command_line(argv=None): default=False, dest='plain', help='only use plain python shell, should be used with --shell option', - ) + ) parser.add_option( '-M', '--import_models', action='store_true', default=False, dest='import_models', - help='auto import model files, default is False, ' + \ - ' should be used with --shell option', - ) + help='auto import model files, default is False, ' + + ' should be used with --shell option', + ) parser.add_option( '-R', '--run', dest='run', metavar='PYTHON_FILE', default='', - help='run PYTHON_FILE in web2py environment, ' + \ - 'should be used with --shell option', - ) + help='run PYTHON_FILE in web2py environment, ' + + 'should be used with --shell option', + ) (options, args) = parser.parse_args(argv[1:]) @@ -419,7 +430,8 @@ def execute_from_command_line(argv=None): startfile = args[0] else: startfile = '' - run(options.shell, options.plain, startfile=startfile, bpython=options.bpython) + run(options.shell, options.plain, startfile=startfile, + bpython=options.bpython) if __name__ == '__main__': diff --git a/gluon/sql.py b/gluon/sql.py index 41dadbea..144e6c27 100644 --- a/gluon/sql.py +++ b/gluon/sql.py @@ -1,5 +1,5 @@ # this file exists for backward compatibility -__all__ = ['DAL','Field','DRIVERS'] +__all__ = ['DAL', 'Field', 'DRIVERS'] from dal import DAL, Field, Table, Query, Set, Expression, Row, Rows, DRIVERS, BaseAdapter, SQLField, SQLTable, SQLXorable, SQLQuery, SQLSet, SQLRows, SQLStorage, SQLDB, GQLDB, SQLALL, SQLCustomType diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 8ffae918..ef574aee 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -42,21 +42,25 @@ is_gae = settings.global_settings.web2py_runtime_gae table_field = re.compile('[\w_]+\.[\w_]+') widget_class = re.compile('^\w*') -def trap_class(_class=None,trap=True): - return (trap and 'w2p_trap' or '')+(_class and ' '+_class or '') -def represent(field,value,record): +def trap_class(_class=None, trap=True): + return (trap and 'w2p_trap' or '') + (_class and ' ' + _class or '') + + +def represent(field, value, record): f = field.represent if not callable(f): return str(value) - n = f.func_code.co_argcount-len(f.func_defaults or []) - if getattr(f, 'im_self', None): n -= 1 - if n==1: + n = f.func_code.co_argcount - len(f.func_defaults or []) + if getattr(f, 'im_self', None): + n -= 1 + if n == 1: return f(value) - elif n==2: - return f(value,record) + elif n == 2: + return f(value, record) else: - raise RuntimeError, "field representation must take 1 or 2 args" + raise RuntimeError("field representation must take 1 or 2 args") + def safe_int(x): try: @@ -64,12 +68,14 @@ def safe_int(x): except ValueError: return 0 + def safe_float(x): try: return float(x) except ValueError: return 0 + class FormWidget(object): """ helper for SQLFORM to generate form input fields @@ -90,12 +96,12 @@ class FormWidget(object): :param attributes: any other supplied attributes """ attr = dict( - _id = '%s_%s' % (field._tablename, field.name), - _class = cls._class or \ + _id='%s_%s' % (field._tablename, field.name), + _class=cls._class or widget_class.match(str(field.type)).group(), - _name = field.name, - requires = field.requires, - ) + _name=field.name, + requires=field.requires, + ) attr.update(widget_attributes) attr.update(attributes) return attr @@ -118,6 +124,7 @@ class FormWidget(object): raise NotImplementedError + class StringWidget(FormWidget): _class = 'string' @@ -130,9 +137,9 @@ class StringWidget(FormWidget): """ default = dict( - _type = 'text', - value = (not value is None and str(value)) or '', - ) + _type='text', + value=(not value is None and str(value)) or '', + ) attr = cls._attributes(field, default, **attributes) return INPUT(**attr) @@ -173,8 +180,8 @@ class TextWidget(FormWidget): see also: :meth:`FormWidget.widget` """ - default = dict(value = value) - attr = cls._attributes(field, default,**attributes) + default = dict(value=value) + attr = cls._attributes(field, default, **attributes) return TEXTAREA(**attr) @@ -189,7 +196,7 @@ class BooleanWidget(FormWidget): see also: :meth:`FormWidget.widget` """ - default=dict(_type='checkbox', value=value) + default = dict(_type='checkbox', value=value) attr = cls._attributes(field, default, **attributes) return INPUT(**attr) @@ -225,28 +232,32 @@ class OptionsWidget(FormWidget): if hasattr(requires[0], 'options'): options = requires[0].options() else: - raise SyntaxError, 'widget cannot determine options of %s' % field + raise SyntaxError( + 'widget cannot determine options of %s' % field) opts = [OPTION(v, _value=k) for (k, v) in options] return SELECT(*opts, **attr) + class ListWidget(StringWidget): @classmethod def widget(cls, field, value, **attributes): _id = '%s_%s' % (field._tablename, field.name) _name = field.name - if field.type=='list:integer': _class = 'integer' - else: _class = 'string' + if field.type == 'list:integer': + _class = 'integer' + else: + _class = 'string' requires = field.requires if isinstance( field.requires, (IS_NOT_EMPTY, IS_LIST_OF)) else None attributes['_style'] = 'list-style:none' nvalue = value or [''] items = [LI(INPUT(_id=_id, _class=_class, _name=_name, - value=v, hideerror=k @@ -1088,17 +1116,18 @@ class SQLFORM(FORM): self.custom.deletable = '' if record and deletable: widget = INPUT(_type='checkbox', - _class='delete', - _id=self.FIELDKEY_DELETE_RECORD, - _name=self.FIELDNAME_REQUEST_DELETE, - ) - xfields.append((self.FIELDKEY_DELETE_RECORD+SQLFORM.ID_ROW_SUFFIX, - LABEL( - delete_label,separator, - _for=self.FIELDKEY_DELETE_RECORD, - _id=self.FIELDKEY_DELETE_RECORD+SQLFORM.ID_LABEL_SUFFIX), - widget, - col3.get(self.FIELDKEY_DELETE_RECORD, ''))) + _class='delete', + _id=self.FIELDKEY_DELETE_RECORD, + _name=self.FIELDNAME_REQUEST_DELETE, + ) + xfields.append( + (self.FIELDKEY_DELETE_RECORD + SQLFORM.ID_ROW_SUFFIX, + LABEL( + delete_label, separator, + _for=self.FIELDKEY_DELETE_RECORD, + _id=self.FIELDKEY_DELETE_RECORD + SQLFORM.ID_LABEL_SUFFIX), + widget, + col3.get(self.FIELDKEY_DELETE_RECORD, ''))) self.custom.deletable = widget # when writable, add submit button @@ -1106,7 +1135,7 @@ class SQLFORM(FORM): if not readonly: if 'submit' in buttons: widget = self.custom.submit = INPUT(_type='submit', - _value=T(submit_button)) + _value=T(submit_button)) elif buttons: widget = self.custom.submit = DIV(*buttons) if self.custom.submit: @@ -1133,26 +1162,26 @@ class SQLFORM(FORM): if formstyle in SQLFORM.formstyles: formstyle = SQLFORM.formstyles[formstyle] else: - raise RuntimeError, 'formstyle not found' + raise RuntimeError('formstyle not found') if callable(formstyle): # backward compatibility, 4 argument function is the old style args, varargs, keywords, defaults = inspect.getargspec(formstyle) if defaults and len(args) - len(defaults) == 4 or len(args) == 4: table = TABLE() - for id,a,b,c in xfields: - newrows = formstyle(id,a,b,c) - self.field_parent[id] = getattr(b,'parent',None) + for id, a, b, c in xfields: + newrows = formstyle(id, a, b, c) + self.field_parent[id] = getattr(b, 'parent', None) if type(newrows).__name__ != "tuple": newrows = [newrows] for newrow in newrows: table.append(newrow) else: table = formstyle(self, xfields) - for id,a,b,c in xfields: - self.field_parent[id] = getattr(b,'parent',None) + for id, a, b, c in xfields: + self.field_parent[id] = getattr(b, 'parent', None) else: - raise RuntimeError, 'formstyle not supported' + raise RuntimeError('formstyle not supported') return table def accepts( @@ -1166,7 +1195,7 @@ class SQLFORM(FORM): hideerror=False, detect_record_change=False, **kwargs - ): + ): """ similar FORM.accepts but also does insert, update or delete in DAL. @@ -1180,12 +1209,13 @@ class SQLFORM(FORM): if keepvalues is None: keepvalues = True if self.record else False - if self.readonly: return False + if self.readonly: + return False if request_vars.__class__.__name__ == 'Request': request_vars = request_vars.post_vars - keyed = hasattr(self.table,'_primarykey') + keyed = hasattr(self.table, '_primarykey') # implement logic to detect whether record exist but has been modified # server side @@ -1194,7 +1224,8 @@ class SQLFORM(FORM): if self.detect_record_change: if self.record: self.record_changed = False - serialized = '|'.join(str(self.record[k]) for k in self.table.fields()) + serialized = '|'.join( + str(self.record[k]) for k in self.table.fields()) self.record_hash = md5_hash(serialized) # logic to deal with record_id for keyed tables @@ -1202,9 +1233,9 @@ class SQLFORM(FORM): if keyed: formname_id = '.'.join(str(self.record[k]) for k in self.table._primarykey - if hasattr(self.record,k)) - record_id = dict((k, request_vars.get(k,None)) \ - for k in self.table._primarykey) + if hasattr(self.record, k)) + record_id = dict((k, request_vars.get(k, None)) + for k in self.table._primarykey) else: (formname_id, record_id) = (self.record[self.id_field_name], request_vars.get('id', None)) @@ -1220,8 +1251,8 @@ class SQLFORM(FORM): record_id = record_id[0] if formname: - formname = formname % dict(tablename = self.table._tablename, - record_id = formname_id) + formname = formname % dict(tablename=self.table._tablename, + record_id=formname_id) # ## THIS IS FOR UNIQUE RECORDS, read IS_NOT_IN_DB @@ -1231,7 +1262,7 @@ class SQLFORM(FORM): if not isinstance(requires, (list, tuple)): requires = [requires] [item.set_self_id(self.record_id) for item in requires - if hasattr(item, 'set_self_id') and self.record_id] + if hasattr(item, 'set_self_id') and self.record_id] # ## END @@ -1248,7 +1279,7 @@ class SQLFORM(FORM): onvalidation, hideerror=hideerror, **kwargs - ) + ) self.deleted = \ request_vars.get(self.FIELDNAME_REQUEST_DELETE, False) @@ -1260,10 +1291,10 @@ class SQLFORM(FORM): if self.record_changed and self.detect_record_change: message_onchange = \ kwargs.setdefault("message_onchange", - current.T("A record change was detected. " + - "Consecutive update self-submissions " + - "are not allowed. Try re-submitting or " + - "refreshing the form page.")) + current.T("A record change was detected. " + + "Consecutive update self-submissions " + + "are not allowed. Try re-submitting or " + + "refreshing the form page.")) if message_onchange is not None: current.response.flash = message_onchange return ret @@ -1285,19 +1316,20 @@ class SQLFORM(FORM): value = self.table[fieldname].default if field.type.startswith('list:') and isinstance(value, str): value = [value] - row_id = '%s_%s%s' % (self.table, fieldname, SQLFORM.ID_ROW_SUFFIX) + row_id = '%s_%s%s' % ( + self.table, fieldname, SQLFORM.ID_ROW_SUFFIX) widget = field.widget(field, value) parent = self.field_parent[row_id] if parent: - parent.components = [ widget ] + parent.components = [widget] parent._traverse(False, hideerror) self.custom.widget[fieldname] = widget self.accepted = ret return ret if record_id and str(record_id) != str(self.record_id): - raise SyntaxError, 'user is tampering with form\'s record_id: ' \ - '%s != %s' % (record_id, self.record_id) + raise SyntaxError('user is tampering with form\'s record_id: ' + '%s != %s' % (record_id, self.record_id)) if record_id and dbio and not keyed: self.vars.id = self.record[self.id_field_name] @@ -1306,8 +1338,8 @@ class SQLFORM(FORM): if dbio: if keyed: qry = reduce(lambda x, y: x & y, - [self.table[k] == record_id[k] \ - for k in self.table._primarykey]) + [self.table[k] == record_id[k] + for k in self.table._primarykey]) else: qry = self.table._id == self.record[self.id_field_name] self.table._db(qry).delete() @@ -1325,7 +1357,7 @@ class SQLFORM(FORM): ### this happens because FORM has no knowledge of writable ### and thinks that a missing boolean field is a None if self.table[fieldname].type == 'boolean' and \ - self.vars.get(fieldname, True) is None: + self.vars.get(fieldname, True) is None: del self.vars[fieldname] continue @@ -1363,7 +1395,7 @@ class SQLFORM(FORM): f = os.path.join(current.request.folder, os.path.normpath(f)) source_file = open(f, 'rb') - original_filename = os.path.split(f)[1] + original_filename = os.path.split(f)[1] elif hasattr(f, 'file'): (source_file, original_filename) = (f.file, f.filename) elif isinstance(f, (str, unicode)): @@ -1386,13 +1418,14 @@ class SQLFORM(FORM): self.errors[fieldname] = 'no data' self.accepted = False return False - value = fields.get(fieldname,None) + value = fields.get(fieldname, None) if field.type == 'list:string': if not isinstance(value, (tuple, list)): fields[fieldname] = value and [value] or [] - elif isinstance(field.type,str) and field.type.startswith('list:'): + elif isinstance(field.type, str) and field.type.startswith('list:'): if not isinstance(value, list): - fields[fieldname] = [safe_int(x) for x in (value and [value] or [])] + fields[fieldname] = [safe_int( + x) for x in (value and [value] or [])] elif field.type == 'integer': if not value is None: fields[fieldname] = safe_int(value) @@ -1405,8 +1438,8 @@ class SQLFORM(FORM): for fieldname in self.vars: if fieldname != 'id' and fieldname in self.table.fields\ - and not fieldname in fields and not fieldname\ - in request_vars: + and not fieldname in fields and not fieldname\ + in request_vars: fields[fieldname] = self.vars[fieldname] if dbio: @@ -1421,10 +1454,10 @@ class SQLFORM(FORM): elif not self.table[field.name].default is None: fields[field.name] = self.table[field.name].default if keyed: - if reduce(lambda x, y: x and y, record_id.values()): # if record_id + if reduce(lambda x, y: x and y, record_id.values()): # if record_id if fields: qry = reduce(lambda x, y: x & y, - [self.table[k] == self.record[k] for k in self.table._primarykey]) + [self.table[k] == self.record[k] for k in self.table._primarykey]) self.table._db(qry).update(**fields) else: pk = self.table.insert(**fields) @@ -1436,7 +1469,8 @@ class SQLFORM(FORM): if record_id: self.vars.id = self.record[self.id_field_name] if fields: - self.table._db(self.table._id == self.record[self.id_field_name]).update(**fields) + self.table._db(self.table._id == self.record[ + self.id_field_name]).update(**fields) else: self.vars.id = self.table.insert(**fields) self.accepted = ret @@ -1445,38 +1479,38 @@ class SQLFORM(FORM): AUTOTYPES = { type(''): ('string', None), type(True): ('boolean', None), - type(1): ('integer', IS_INT_IN_RANGE(-1e12,+1e12)), + type(1): ('integer', IS_INT_IN_RANGE(-1e12, +1e12)), type(1.0): ('double', IS_FLOAT_IN_RANGE()), type([]): ('list:string', None), type(datetime.date.today()): ('date', IS_DATE()), type(datetime.datetime.today()): ('datetime', IS_DATETIME()) - } + } @staticmethod - def dictform(dictionary,**kwargs): + def dictform(dictionary, **kwargs): fields = [] - for key,value in sorted(dictionary.items()): - t, requires = SQLFORM.AUTOTYPES.get(type(value),(None,None)) + for key, value in sorted(dictionary.items()): + t, requires = SQLFORM.AUTOTYPES.get(type(value), (None, None)) if t: - fields.append(Field(key,t,requires=requires, + fields.append(Field(key, t, requires=requires, default=value)) - return SQLFORM.factory(*fields,**kwargs) + return SQLFORM.factory(*fields, **kwargs) @staticmethod - def smartdictform(session,name,filename=None,query=None,**kwargs): + def smartdictform(session, name, filename=None, query=None, **kwargs): import os if query: session[name] = query.db(query).select().first().as_dict() elif os.path.exists(filename): - env = {'datetime':datetime} - session[name] = eval(open(filename).read(),{},env) + env = {'datetime': datetime} + session[name] = eval(open(filename).read(), {}, env) form = SQLFORM.dictform(session[name]) if form.process().accepted: session[name].update(form.vars) if query: query.db(query).update(**form.vars) else: - open(filename,'w').write(repr(session[name])) + open(filename, 'w').write(repr(session[name])) return form @staticmethod @@ -1501,88 +1535,91 @@ class SQLFORM(FORM): **attributes) @staticmethod - def build_query(fields,keywords): + def build_query(fields, keywords): request = current.request - if isinstance(keywords,(tuple,list)): - keywords = keywords[0] - request.vars.keywords = keywords + if isinstance(keywords, (tuple, list)): + keywords = keywords[0] + request.vars.keywords = keywords key = keywords.strip() if key and not ' ' in key and not '"' in key and not "'" in key: - SEARCHABLE_TYPES = ('string','text','list:string') - parts = [field.contains(key) for field in fields if field.type in SEARCHABLE_TYPES] + SEARCHABLE_TYPES = ('string', 'text', 'list:string') + parts = [field.contains( + key) for field in fields if field.type in SEARCHABLE_TYPES] else: parts = None if parts: - return reduce(lambda a,b: a|b,parts) + return reduce(lambda a, b: a | b, parts) else: - return smart_query(fields,key) + return smart_query(fields, key) @staticmethod - def search_menu(fields,search_options=None): + def search_menu(fields, search_options=None): T = current.T search_options = search_options or { - 'string':['=','!=','<','>','<=','>=','starts with','contains'], - 'text':['=','!=','<','>','<=','>=','starts with','contains'], - 'date':['=','!=','<','>','<=','>='], - 'time':['=','!=','<','>','<=','>='], - 'datetime':['=','!=','<','>','<=','>='], - 'integer':['=','!=','<','>','<=','>='], - 'double':['=','!=','<','>','<=','>='], - 'id':['=','!=','<','>','<=','>='], - 'reference':['=','!=','<','>','<=','>='], - 'boolean':['=','!=']} - if fields[0]._db._adapter.dbengine=='google:datastore': - search_options['string'] = ['=','!=','<','>','<=','>='] - search_options['text'] = ['=','!=','<','>','<=','>='] + 'string': ['=', '!=', '<', '>', '<=', '>=', 'starts with', 'contains'], + 'text': ['=', '!=', '<', '>', '<=', '>=', 'starts with', 'contains'], + 'date': ['=', '!=', '<', '>', '<=', '>='], + 'time': ['=', '!=', '<', '>', '<=', '>='], + 'datetime': ['=', '!=', '<', '>', '<=', '>='], + 'integer': ['=', '!=', '<', '>', '<=', '>='], + 'double': ['=', '!=', '<', '>', '<=', '>='], + 'id': ['=', '!=', '<', '>', '<=', '>='], + 'reference': ['=', '!=', '<', '>', '<=', '>='], + 'boolean': ['=', '!=']} + if fields[0]._db._adapter.dbengine == 'google:datastore': + search_options['string'] = ['=', '!=', '<', '>', '<=', '>='] + search_options['text'] = ['=', '!=', '<', '>', '<=', '>='] search_options['list:string'] = ['contains'] search_options['list:integer'] = ['contains'] search_options['list:reference'] = ['contains'] criteria = [] selectfields = [] for field in fields: - name = str(field).replace('.','-') - options = search_options.get(field.type.split(' ')[0],None) + name = str(field).replace('.', '-') + options = search_options.get(field.type.split(' ')[0], None) if options: - label = isinstance(field.label,str) and T(field.label) or field.label + label = isinstance( + field.label, str) and T(field.label) or field.label selectfields.append(OPTION(label, _value=str(field))) operators = SELECT(*[T(option) for option in options]) - if field.type=='boolean': + if field.type == 'boolean': value_input = SELECT( - OPTION(T("True"),_value="T"), - OPTION(T("False"),_value="F"), - _id="w2p_value_"+name) + OPTION(T("True"), _value="T"), + OPTION(T("False"), _value="F"), + _id="w2p_value_" + name) else: value_input = INPUT(_type='text', - _id="w2p_value_"+name, + _id="w2p_value_" + name, _class=field.type) new_button = INPUT( - _type="button", _value=T('New'),_class="btn", + _type="button", _value=T('New'), _class="btn", _onclick="w2p_build_query('new','%s')" % field) and_button = INPUT( - _type="button", _value=T('And'),_class="btn", + _type="button", _value=T('And'), _class="btn", _onclick="w2p_build_query('and','%s')" % field) or_button = INPUT( - _type="button", _value=T('Or'),_class="btn", + _type="button", _value=T('Or'), _class="btn", _onclick="w2p_build_query('or','%s')" % field) close_button = INPUT( - _type="button", _value=T('Close'),_class="btn", + _type="button", _value=T('Close'), _class="btn", _onclick="jQuery('#w2p_query_panel').slideUp()") criteria.append(DIV( - operators,value_input,new_button, - and_button,or_button,close_button, - _id='w2p_field_%s' % name, + operators, value_input, new_button, + and_button, or_button, close_button, + _id='w2p_field_%s' % name, _class='w2p_query_row hidden', _style='display:inline')) - criteria.insert(0,SELECT( - _id="w2p_query_fields", + criteria.insert(0, SELECT( + _id="w2p_query_fields", _onchange="jQuery('.w2p_query_row').hide();jQuery('#w2p_field_'+jQuery('#w2p_query_fields').val().replace('.','-')).show();", _style='float:left', *selectfields)) fadd = SCRIPT(""" - jQuery('#w2p_query_panel input,#w2p_query_panel select').css('width','auto'); + jQuery('#w2p_query_panel input,#w2p_query_panel select').css( + 'width','auto'); jQuery(function(){web2py_ajax_fields('#w2p_query_panel');}); function w2p_build_query(aggregator,a) { var b=a.replace('.','-'); @@ -1595,9 +1632,7 @@ class SQLFORM(FORM): } """) return CAT( - DIV(_id="w2p_query_panel",_class='hidden',*criteria),fadd) - - + DIV(_id="w2p_query_panel", _class='hidden', *criteria), fadd) @staticmethod def grid(query, @@ -1618,16 +1653,16 @@ class SQLFORM(FORM): csv=True, links=None, links_in_grid=True, - upload = '', + upload='', args=[], - user_signature = True, + user_signature=True, maxtextlengths={}, maxtextlength=20, onvalidation=None, oncreate=None, onupdate=None, ondelete=None, - sorter_icons=(XML('↑'),XML('↓')), + sorter_icons=(XML('↑'), XML('↓')), ui = 'web2py', showbuttontext=True, _class="web2py_grid", @@ -1640,7 +1675,7 @@ class SQLFORM(FORM): createargs={}, editargs={}, viewargs={}, - ): + ): # jQuery UI ThemeRoller classes (empty if ui is disabled) if ui == 'jquery-ui': @@ -1679,8 +1714,8 @@ class SQLFORM(FORM): buttontable='icon rightarrow icon-arrow-right', buttonview='icon magnifier icon-zoom-in', ) - elif not isinstance(ui,dict): - raise RuntimeError,'SQLFORM.grid ui argument must be a dictionary' + elif not isinstance(ui, dict): + raise RuntimeError('SQLFORM.grid ui argument must be a dictionary') db = query._db T = current.T @@ -1693,18 +1728,18 @@ class SQLFORM(FORM): deletable = wenabled and deletable def url(**b): - b['args'] = args+b.get('args',[]) - b['hash_vars']=False + b['args'] = args + b.get('args', []) + b['hash_vars'] = False b['user_signature'] = user_signature return URL(**b) def url2(**b): - b['args'] = request.args+b.get('args',[]) - b['hash_vars']=False + b['args'] = request.args + b.get('args', []) + b['hash_vars'] = False b['user_signature'] = user_signature return URL(**b) - referrer = session.get('_web2py_grid_referrer_'+formname, url()) + referrer = session.get('_web2py_grid_referrer_' + formname, url()) # if not user_signature every action is accessible # else forbid access unless # - url is based url @@ -1713,7 +1748,7 @@ class SQLFORM(FORM): if user_signature: if not( '/'.join(str(a) for a in args) == '/'.join(request.args) or - URL.verify(request,user_signature=user_signature, + URL.verify(request, user_signature=user_signature, hash_vars=False) or not ( 'create' in request.args or 'delete' in request.args or @@ -1727,65 +1762,66 @@ class SQLFORM(FORM): if showbuttontext: if callback: return A(SPAN(_class=ui.get(buttonclass)), - SPAN(T(buttontext),_title=buttontext, + SPAN(T(buttontext), _title=buttontext, _class=ui.get('buttontext')), - callback=callback,delete=delete, - _class=trap_class(ui.get('button'),trap)) + callback=callback, delete=delete, + _class=trap_class(ui.get('button'), trap)) else: return A(SPAN(_class=ui.get(buttonclass)), - SPAN(T(buttontext),_title=buttontext, + SPAN(T(buttontext), _title=buttontext, _class=ui.get('buttontext')), _href=buttonurl, - _class=trap_class(ui.get('button'),trap)) + _class=trap_class(ui.get('button'), trap)) else: if callback: return A(SPAN(_class=ui.get(buttonclass)), - callback=callback,delete=delete, + callback=callback, delete=delete, _title=buttontext, - _class=trap_class(ui.get('buttontext'),trap)) + _class=trap_class(ui.get('buttontext'), trap)) else: return A(SPAN(_class=ui.get(buttonclass)), - _href=buttonurl,_title=buttontext, - _class=trap_class(ui.get('buttontext'),trap)) + _href=buttonurl, _title=buttontext, + _class=trap_class(ui.get('buttontext'), trap)) dbset = db(query) tablenames = db._adapter.tables(dbset.query) - if left!=None: tablenames+=db._adapter.tables(left) + if left is not None: + tablenames += db._adapter.tables(left) tables = [db[tablename] for tablename in tablenames] if not fields: - fields = reduce(lambda a,b:a+b, + fields = reduce(lambda a, b: a + b, [[field for field in table] for table in tables]) if not field_id: field_id = tables[0]._id - columns = [str(field) for field in fields \ - if field._tablename in tablenames] + columns = [str(field) for field in fields + if field._tablename in tablenames] if not str(field_id) in [str(f) for f in fields]: fields.append(field_id) table = field_id.table tablename = table._tablename - if upload=='': - upload = lambda filename: url(args=['download',filename]) - if len(request.args)>1 and request.args[-2]=='download': - stream = response.download(request,db) - raise HTTP(200,stream,**response.headers) + if upload == '': + upload = lambda filename: url(args=['download', filename]) + if len(request.args) > 1 and request.args[-2] == 'download': + stream = response.download(request, db) + raise HTTP(200, stream, **response.headers) - def buttons(edit=False,view=False,record=None): + def buttons(edit=False, view=False, record=None): buttons = DIV(gridbutton('buttonback', 'Back', referrer), _class='form_header row_buttons %(header)s %(cornertop)s' % ui) if edit and (not callable(edit) or edit(record)): - args = ['edit',table._tablename,request.args[-1]] + args = ['edit', table._tablename, request.args[-1]] buttons.append(gridbutton('buttonedit', 'Edit', url(args=args))) if view: - args = ['view',table._tablename,request.args[-1]] + args = ['view', table._tablename, request.args[-1]] buttons.append(gridbutton('buttonview', 'View', url(args=args))) if record and links: for link in links: - if isinstance(link,dict): - buttons.append(link['body'](record)) + if isinstance(link, dict): + buttons.append(link['body'](record)) elif link(record): - buttons.append(link(record)) + buttons.append(link(record)) return buttons formfooter = DIV( @@ -1794,7 +1830,7 @@ class SQLFORM(FORM): create_form = update_form = view_form = search_form = None sqlformargs = dict(formargs) - if create and len(request.args)>1 and request.args[-2] == 'new': + if create and len(request.args) > 1 and request.args[-2] == 'new': table = db[request.args[-1]] sqlformargs.update(createargs) create_form = SQLFORM( @@ -1802,9 +1838,9 @@ class SQLFORM(FORM): _class='web2py_form', **sqlformargs) create_form.process(formname=formname, - next=referrer, - onvalidation=onvalidation, - onsuccess=oncreate) + next=referrer, + onvalidation=onvalidation, + onsuccess=oncreate) res = DIV(buttons(), create_form, formfooter, _class=_class) res.create_form = create_form res.update_form = update_form @@ -1812,13 +1848,14 @@ class SQLFORM(FORM): res.search_form = search_form return res - elif details and len(request.args)>2 and request.args[-3]=='view': + elif details and len(request.args) > 2 and request.args[-3] == 'view': table = db[request.args[-2]] record = table(request.args[-1]) or redirect(URL('error')) sqlformargs.update(viewargs) - view_form = SQLFORM(table, record, upload=upload, ignore_rw=ignore_rw, - formstyle=formstyle, readonly=True, _class='web2py_form', - **sqlformargs) + view_form = SQLFORM( + table, record, upload=upload, ignore_rw=ignore_rw, + formstyle=formstyle, readonly=True, _class='web2py_form', + **sqlformargs) res = DIV(buttons(edit=editable, record=record), view_form, formfooter, _class=_class) res.create_form = create_form @@ -1826,7 +1863,7 @@ class SQLFORM(FORM): res.view_form = view_form res.search_form = search_form return res - elif editable and len(request.args)>2 and request.args[-3]=='edit': + elif editable and len(request.args) > 2 and request.args[-3] == 'edit': table = db[request.args[-2]] record = table(request.args[-1]) or redirect(URL('error')) sqlformargs.update(editargs) @@ -1850,20 +1887,20 @@ class SQLFORM(FORM): res.view_form = view_form res.search_form = search_form return res - elif deletable and len(request.args)>2 and request.args[-3]=='delete': + elif deletable and len(request.args) > 2 and request.args[-3] == 'delete': table = db[request.args[-2]] if ondelete: - ondelete(table,request.args[-1]) - ret = db(table[table._id.name]==request.args[-1]).delete() + ondelete(table, request.args[-1]) + ret = db(table[table._id.name] == request.args[-1]).delete() return ret exportManager = dict( - csv_with_hidden_cols=(ExporterCSV,'CSV (hidden cols)'), - csv=(ExporterCSV,'CSV'), + csv_with_hidden_cols=(ExporterCSV, 'CSV (hidden cols)'), + csv=(ExporterCSV, 'CSV'), xml=(ExporterXML, 'XML'), html=(ExporterHTML, 'HTML'), - tsv_with_hidden_cols=\ - (ExporterTSV,'TSV (Excel compatible, hidden cols)'), + tsv_with_hidden_cols= + (ExporterTSV, 'TSV (Excel compatible, hidden cols)'), tsv=(ExporterTSV, 'TSV (Excel compatible)')) if not exportclasses is None: exportManager.update(exportclasses) @@ -1872,22 +1909,22 @@ class SQLFORM(FORM): if export_type: order = request.vars.order or '' if sortable: - if order and not order=='None': - if order[:1]=='~': + if order and not order == 'None': + if order[:1] == '~': sign, rorder = '~', order[1:] else: sign, rorder = '', order - tablename,fieldname = rorder.split('.',1) - orderby=db[tablename][fieldname] - if sign=='~': - orderby=~orderby + tablename, fieldname = rorder.split('.', 1) + orderby = db[tablename][fieldname] + if sign == '~': + orderby = ~orderby table_fields = [f for f in fields if f._tablename in tablenames] - if export_type in ('csv_with_hidden_cols','tsv_with_hidden_cols'): + if export_type in ('csv_with_hidden_cols', 'tsv_with_hidden_cols'): if request.vars.keywords: try: dbset = dbset(SQLFORM.build_query( - fields,request.vars.get('keywords',''))) + fields, request.vars.get('keywords', ''))) rows = dbset.select(cacheable=True) except Exception, e: response.flash = T('Internal Error') @@ -1895,8 +1932,8 @@ class SQLFORM(FORM): else: rows = dbset.select(cacheable=True) else: - rows = dbset.select(left=left,orderby=orderby, - cacheable=True,*columns) + rows = dbset.select(left=left, orderby=orderby, + cacheable=True, *columns) if export_type in exportManager: value = exportManager[export_type] @@ -1905,46 +1942,46 @@ class SQLFORM(FORM): filename = '.'.join(('rows', oExp.file_ext)) response.headers['Content-Type'] = oExp.content_type response.headers['Content-Disposition'] = \ - 'attachment;filename='+filename+';' - raise HTTP(200, oExp.export(),**response.headers) + 'attachment;filename=' + filename + ';' + raise HTTP(200, oExp.export(), **response.headers) elif request.vars.records and not isinstance( - request.vars.records,list): - request.vars.records=[request.vars.records] + request.vars.records, list): + request.vars.records = [request.vars.records] elif not request.vars.records: - request.vars.records=[] + request.vars.records = [] - session['_web2py_grid_referrer_'+formname] = url2(vars=request.vars) + session['_web2py_grid_referrer_' + formname] = url2(vars=request.vars) console = DIV(_class='web2py_console %(header)s %(cornertop)s' % ui) error = None if create: add = gridbutton( - buttonclass='buttonadd', - buttontext='Add', - buttonurl=url(args=['new',tablename])) + buttonclass='buttonadd', + buttontext='Add', + buttonurl=url(args=['new', tablename])) if not searchable: console.append(add) else: add = '' if searchable: - sfields = reduce(lambda a,b:a+b, + sfields = reduce(lambda a, b: a + b, [[f for f in t if f.readable] for t in tables]) - if isinstance(search_widget,dict): + if isinstance(search_widget, dict): search_widget = search_widget[tablename] - if search_widget=='default': + if search_widget == 'default': search_menu = SQLFORM.search_menu(sfields) search_widget = lambda sfield, url: CAT(FORM( - INPUT(_name='keywords',_value=request.vars.keywords, - _id='web2py_keywords',_onfocus="jQuery('#w2p_query_fields').change();jQuery('#w2p_query_panel').slideDown();"), - INPUT(_type='submit',_value=T('Search'),_class="btn"), - INPUT(_type='submit',_value=T('Clear'),_class="btn", + INPUT(_name='keywords', _value=request.vars.keywords, + _id='web2py_keywords', _onfocus="jQuery('#w2p_query_fields').change();jQuery('#w2p_query_panel').slideDown();"), + INPUT(_type='submit', _value=T('Search'), _class="btn"), + INPUT(_type='submit', _value=T('Clear'), _class="btn", _onclick="jQuery('#web2py_keywords').val('');"), - _method="GET",_action=url),search_menu) - form = search_widget and search_widget(sfields,url()) or '' + _method="GET", _action=url), search_menu) + form = search_widget and search_widget(sfields, url()) or '' console.append(add) console.append(form) - keywords = request.vars.get('keywords','') + keywords = request.vars.get('keywords', '') try: if callable(searchable): subquery = searchable(sfields, keywords) @@ -1961,7 +1998,7 @@ class SQLFORM(FORM): try: if left or groupby: c = 'count(*)' - nrows = dbset.select(c,left=left,cacheable=True, + nrows = dbset.select(c, left=left, cacheable=True, groupby=groupby).first()[c] else: nrows = dbset.count() @@ -1971,140 +2008,154 @@ class SQLFORM(FORM): order = request.vars.order or '' if sortable: - if order and not order=='None': - tablename,fieldname = order.split('~')[-1].split('.',1) + if order and not order == 'None': + tablename, fieldname = order.split('~')[-1].split('.', 1) sort_field = db[tablename][fieldname] - exception = sort_field.type in ('date','datetime','time') + exception = sort_field.type in ('date', 'datetime', 'time') if exception: - orderby = (order[:1]=='~' and sort_field) or ~sort_field + orderby = (order[:1] == '~' and sort_field) or ~sort_field else: - orderby = (order[:1]=='~' and ~sort_field) or sort_field + orderby = (order[:1] == '~' and ~sort_field) or sort_field head = TR(_class=ui.get('header')) if selectable: head.append(TH(_class=ui.get('default'))) for field in fields: - if columns and not str(field) in columns: continue - if not field.readable: continue + if columns and not str(field) in columns: + continue + if not field.readable: + continue key = str(field) header = headers.get(str(field), field.label or key) if sortable: if key == order: - key, marker = '~'+order, sorter_icons[0] + key, marker = '~' + order, sorter_icons[0] elif key == order[1:]: marker = sorter_icons[1] else: marker = '' - header = A(header,marker,_href=url(vars=dict( - keywords=request.vars.keywords or '', - order=key)),_class=trap_class()) + header = A(header, marker, _href=url(vars=dict( + keywords=request.vars.keywords or '', + order=key)), _class=trap_class()) head.append(TH(header, _class=ui.get('default'))) if links and links_in_grid: for link in links: - if isinstance(link,dict): + if isinstance(link, dict): head.append(TH(link['header'], _class=ui.get('default'))) # Include extra column for buttons if needed. include_buttons_column = (details or editable or deletable or - (links and links_in_grid and - not all([isinstance(link, dict) for link in links]))) + (links and links_in_grid and + not all([isinstance(link, dict) for link in links]))) if include_buttons_column: head.append(TH(_class=ui.get('default'))) paginator = UL() - if paginate and paginateNPAGES+1: - paginator.append(LI(self_link('<<',0))) - if page>NPAGES: - paginator.append(LI(self_link('<',page-1))) - pages = range(max(0,page-NPAGES),min(page+NPAGES,npages)) + if paginate and paginate < nrows: + npages, reminder = divmod(nrows, paginate) + if reminder: + npages += 1 + try: + page = int(request.vars.page or 1) - 1 + except ValueError: + page = 0 + limitby = (paginate * page, paginate * (page + 1)) + + def self_link(name, p): + d = dict(page=p + 1) + if order: + d['order'] = order + if request.vars.keywords: + d['keywords'] = request.vars.keywords + return A(name, _href=url(vars=d), _class=trap_class()) + NPAGES = 5 # window is 2*NPAGES + if page > NPAGES + 1: + paginator.append(LI(self_link('<<', 0))) + if page > NPAGES: + paginator.append(LI(self_link('<', page - 1))) + pages = range(max(0, page - NPAGES), min(page + NPAGES, npages)) for p in pages: if p == page: - paginator.append(LI(A(p+1,_onclick='return false'), + paginator.append(LI(A(p + 1, _onclick='return false'), _class=trap_class('current'))) else: - paginator.append(LI(self_link(p+1,p))) - if page',page+1))) - if page>',npages-1))) + paginator.append(LI(self_link(p + 1, p))) + if page < npages - NPAGES: + paginator.append(LI(self_link('>', page + 1))) + if page < npages - NPAGES - 1: + paginator.append(LI(self_link('>>', npages - 1))) else: limitby = None try: table_fields = [f for f in fields if f._tablename in tablenames] - rows = dbset.select(left=left,orderby=orderby, - groupby=groupby,limitby=limitby, + rows = dbset.select(left=left, orderby=orderby, + groupby=groupby, limitby=limitby, *table_fields) except SyntaxError: rows = None error = T("Query Not Supported") if nrows: message = error or T('%(nrows)s records found') % dict(nrows=nrows) - console.append(DIV(message,_class='web2py_counter')) + console.append(DIV(message, _class='web2py_counter')) if rows: htmltable = TABLE(THEAD(head)) tbody = TBODY() - numrec=0 + numrec = 0 for row in rows: if numrec % 2 == 0: classtr = 'even' else: classtr = 'odd' - numrec+=1 + numrec += 1 id = row[field_id] if id: rid = id - if callable(rid): ### can this ever be callable? + if callable(rid): # can this ever be callable? rid = rid(row) tr = TR(_id=rid, _class='%s %s' % (classtr, 'with_id')) else: tr = TR(_class=classtr) if selectable: - tr.append(INPUT(_type="checkbox",_name="records",_value=id, + tr.append( + INPUT(_type="checkbox", _name="records", _value=id, value=request.vars.records)) for field in fields: - if not str(field) in columns: continue - if not field.readable: continue - if field.type=='blob': continue + if not str(field) in columns: + continue + if not field.readable: + continue + if field.type == 'blob': + continue value = row[field] - maxlength = maxtextlengths.get(str(field),maxtextlength) + maxlength = maxtextlengths.get(str(field), maxtextlength) if field.represent: try: - value=field.represent(value,row) + value = field.represent(value, row) except KeyError: try: - value=field.represent(value,row[field._tablename]) + value = field.represent( + value, row[field._tablename]) except KeyError: pass - elif field.type=='boolean': - value = INPUT(_type="checkbox",_checked = value, + elif field.type == 'boolean': + value = INPUT(_type="checkbox", _checked=value, _disabled=True) - elif field.type=='upload': + elif field.type == 'upload': if value: if callable(upload): - value = A(current.T('file'), _href=upload(value)) + value = A( + current.T('file'), _href=upload(value)) elif upload: value = A(current.T('file'), _href='%s/%s' % (upload, value)) else: value = '' - if isinstance(value,str): - value = truncate_string(value,maxlength) - elif not isinstance(value,DIV): + if isinstance(value, str): + value = truncate_string(value, maxlength) + elif not isinstance(value, DIV): value = field.formatter(value) tr.append(TD(value)) row_buttons = TD(_class='row_buttons') @@ -2118,24 +2169,24 @@ class SQLFORM(FORM): if include_buttons_column: if details and (not callable(details) or details(row)): row_buttons.append(gridbutton( - 'buttonview', 'View', - url(args=['view',tablename,id]))) + 'buttonview', 'View', + url(args=['view', tablename, id]))) if editable and (not callable(editable) or editable(row)): row_buttons.append(gridbutton( - 'buttonedit', 'Edit', - url(args=['edit',tablename,id]))) + 'buttonedit', 'Edit', + url(args=['edit', tablename, id]))) if deletable and (not callable(deletable) or deletable(row)): row_buttons.append(gridbutton( - 'buttondelete', 'Delete', - callback=url(args=['delete',tablename,id]), - delete='tr')) + 'buttondelete', 'Delete', + callback=url(args=['delete', tablename, id]), + delete='tr')) tr.append(row_buttons) tbody.append(tr) htmltable.append(tbody) - htmltable = DIV(htmltable,_style='width:100%;overflow-x:auto') + htmltable = DIV(htmltable, _style='width:100%;overflow-x:auto') if selectable: - htmltable = FORM(htmltable,INPUT(_type="submit")) - if htmltable.process(formname=formname).accepted:# + htmltable = FORM(htmltable, INPUT(_type="submit")) + if htmltable.process(formname=formname).accepted: htmltable.vars.records = htmltable.vars.records or [] htmltable.vars.records = htmltable.vars.records if type(htmltable.vars.records) == list else [htmltable.vars.records] records = [int(r) for r in htmltable.vars.records] @@ -2145,26 +2196,27 @@ class SQLFORM(FORM): htmltable = DIV(current.T('No records found')) if csv and nrows: - export_links =[] - for k,v in sorted(exportManager.items()): + export_links = [] + for k, v in sorted(exportManager.items()): label = v[1] if hasattr(v, "__getitem__") else k link = url2(vars=dict( - order=request.vars.order or '', - _export_type=k, - keywords=request.vars.keywords or '')) - export_links.append(A(T(label),_href=link)) + order=request.vars.order or '', + _export_type=k, + keywords=request.vars.keywords or '')) + export_links.append(A(T(label), _href=link)) export_menu = \ - DIV(T('Export:'),_class="w2p_export_menu",*export_links) + DIV(T('Export:'), _class="w2p_export_menu", *export_links) else: export_menu = None - res = DIV(console,DIV(htmltable,_class="web2py_table"), + res = DIV(console, DIV(htmltable, _class="web2py_table"), _class='%s %s' % (_class, ui.get('widget'))) if paginator.components: res.append( DIV(paginator, - _class="web2py_paginator %(header)s %(cornerbottom)s"%ui)) - if export_menu: res.append(export_menu) + _class="web2py_paginator %(header)s %(cornerbottom)s" % ui)) + if export_menu: + res.append(export_menu) res.create_form = create_form res.update_form = update_form res.view_form = view_form @@ -2207,50 +2259,56 @@ class SQLFORM(FORM): to be linked """ request, T = current.request, current.T - if args is None: args = [] + if args is None: + args = [] def url(**b): - b['args'] = request.args[:nargs]+b.get('args',[]) - b['hash_vars']=False + b['args'] = request.args[:nargs] + b.get('args', []) + b['hash_vars'] = False b['user_signature'] = user_signature return URL(**b) db = table._db breadcrumbs = [] if request.args(len(args)) != table._tablename: - request.args[:]=args+[table._tablename] - if links is None: links = {} - if constraints is None: constraints = {} + request.args[:] = args + [table._tablename] + if links is None: + links = {} + if constraints is None: + constraints = {} field = None try: - nargs = len(args)+1 - previous_tablename,previous_fieldname,previous_id = \ - table._tablename,None,None - while len(request.args)>nargs: + nargs = len(args) + 1 + previous_tablename, previous_fieldname, previous_id = \ + table._tablename, None, None + while len(request.args) > nargs: key = request.args(nargs) if '.' in key: - id = request.args(nargs+1) - tablename,fieldname = key.split('.',1) + id = request.args(nargs + 1) + tablename, fieldname = key.split('.', 1) table = db[tablename] field = table[fieldname] field.default = id referee = field.type[10:] - if referee!=previous_tablename: + if referee != previous_tablename: raise HTTP(400) - cond = constraints.get(referee,None) + cond = constraints.get(referee, None) if cond: - record = db(db[referee].id==id)(cond).select().first() + record = db( + db[referee].id == id)(cond).select().first() else: record = db[referee](id) if previous_id: if record[previous_fieldname] != int(previous_id): raise HTTP(400) - previous_tablename,previous_fieldname,previous_id = \ - tablename,fieldname,id + previous_tablename, previous_fieldname, previous_id = \ + tablename, fieldname, id try: format = db[referee]._format - if callable(format): name = format(record) - else: name = format % record + if callable(format): + name = format(record) + else: + name = format % record except TypeError: name = id nameLink = 'view' @@ -2258,35 +2316,35 @@ class SQLFORM(FORM): LI(A(T(db[referee]._plural), _class=trap_class(), _href=url()), - SPAN(divider,_class='divider'), + SPAN(divider, _class='divider'), _class='w2p_grid_breadcrumb_elem')) - if kwargs.get('details',True): + if kwargs.get('details', True): breadcrumbs.append( - LI(A(name,_class=trap_class(), - _href=url(args=['view',referee,id])), - SPAN(divider,_class='divider'), + LI(A(name, _class=trap_class(), + _href=url(args=['view', referee, id])), + SPAN(divider, _class='divider'), _class='w2p_grid_breadcrumb_elem')) - nargs+=2 + nargs += 2 else: break - if nargs>len(args)+1: + if nargs > len(args) + 1: query = (field == id) - if isinstance(linked_tables,dict): - linked_tables = linked_tables.get(table._tablename,[]) + if isinstance(linked_tables, dict): + linked_tables = linked_tables.get(table._tablename, []) if linked_tables is None or referee in linked_tables: - field.represent = lambda id,r=None,referee=referee,rep=field.represent: A(callable(rep) and rep(id) or id,_class=trap_class(),_href=url(args=['view',referee,id])) - except (KeyError,ValueError,TypeError): + field.represent = lambda id, r=None, referee=referee, rep=field.represent: A(callable(rep) and rep(id) or id, _class=trap_class(), _href=url(args=['view', referee, id])) + except (KeyError, ValueError, TypeError): redirect(URL(args=table._tablename)) - if nargs==len(args)+1: - query = table.id>0 + if nargs == len(args) + 1: + query = table.id > 0 # filter out data info for displayed table if table._tablename in constraints: query = query & constraints[table._tablename] - if isinstance(links,dict): - links = links.get(table._tablename,[]) + if isinstance(links, dict): + links = links.get(table._tablename, []) for key in 'columns,orderby,searchable,sortable,paginate,deletable,editable,details,selectable,create,fields'.split(','): - if isinstance(kwargs.get(key,None),dict): + if isinstance(kwargs.get(key, None), dict): if table._tablename in kwargs[key]: kwargs[key] = kwargs[key][table._tablename] else: @@ -2296,31 +2354,32 @@ class SQLFORM(FORM): for rfield in table._referenced_by: if rfield.readable: check[rfield.tablename] = \ - check.get(rfield.tablename,[])+[rfield.name] - if isinstance(linked_tables,dict): - linked_tables = linked_tables.get(table._tablename,[]) + check.get(rfield.tablename, []) + [rfield.name] + if isinstance(linked_tables, dict): + linked_tables = linked_tables.get(table._tablename, []) for tablename in sorted(check): linked_fieldnames = check[tablename] tb = db[tablename] - multiple_links = len(linked_fieldnames)>1 + multiple_links = len(linked_fieldnames) > 1 for fieldname in linked_fieldnames: if linked_tables is None or tablename in linked_tables: t = T(tb._plural) if not multiple_links else \ - T(tb._plural+'('+fieldname+')') - args0 = tablename+'.'+fieldname + T(tb._plural + '(' + fieldname + ')') + args0 = tablename + '.' + fieldname links.append( - lambda row,t=t,nargs=nargs,args0=args0:\ - A(SPAN(t),_class=trap_class(),_href=url( - args=[args0,row[id_field_name]]))) + lambda row, t=t, nargs=nargs, args0=args0: + A(SPAN(t), _class=trap_class(), _href=url( + args=[args0, row[id_field_name]]))) - grid=SQLFORM.grid(query,args=request.args[:nargs],links=links, - links_in_grid=links_in_grid, - user_signature=user_signature,**kwargs) - if isinstance(grid,DIV): - header = table._plural + (field and ' for '+field.name or '') - breadcrumbs.append(LI(A(T(header),_class=trap_class(), - _href=url()),_class='active w2p_grid_breadcrumb_elem')) - grid.insert(0,DIV(UL(*breadcrumbs, **{'_class':breadcrumbs_class}), + grid = SQLFORM.grid(query, args=request.args[:nargs], links=links, + links_in_grid=links_in_grid, + user_signature=user_signature, **kwargs) + if isinstance(grid, DIV): + header = table._plural + (field and ' for ' + field.name or '') + breadcrumbs.append(LI(A(T(header), _class=trap_class(), + _href=url()), _class='active w2p_grid_breadcrumb_elem')) + grid.insert( + 0, DIV(UL(*breadcrumbs, **{'_class': breadcrumbs_class}), _class='web2py_breadcrumbs')) return grid @@ -2428,46 +2487,45 @@ class SQLTABLE(TABLE): return if not columns: columns = sqlrows.colnames - if headers=='fieldname:capitalize': + if headers == 'fieldname:capitalize': headers = {} for c in columns: - headers[c] = c.split('.')[-1].replace('_',' ').title() - elif headers=='labels': + headers[c] = c.split('.')[-1].replace('_', ' ').title() + elif headers == 'labels': headers = {} for c in columns: - (t,f) = c.split('.') + (t, f) = c.split('.') field = sqlrows.db[t][f] headers[c] = field.label if headers is None: headers = {} else: - for c in columns:#new implement dict + for c in columns: # new implement dict if isinstance(headers.get(c, c), dict): coldict = headers.get(c, c) attrcol = dict() - if coldict['width']!="": + if coldict['width'] != "": attrcol.update(_width=coldict['width']) - if coldict['class']!="": + if coldict['class'] != "": attrcol.update(_class=coldict['class']) - row.append(TH(coldict['label'],**attrcol)) + row.append(TH(coldict['label'], **attrcol)) elif orderby: row.append(TH(A(headers.get(c, c), - _href=th_link+'?orderby=' + c, cid=cid))) + _href=th_link + '?orderby=' + c, cid=cid))) else: row.append(TH(headers.get(c, c))) - if extracolumns:#new implement dict + if extracolumns: # new implement dict for c in extracolumns: attrcol = dict() - if c['width']!="": + if c['width'] != "": attrcol.update(_width=c['width']) - if c['class']!="": + if c['class'] != "": attrcol.update(_class=c['class']) - row.append(TH(c['label'],**attrcol)) + row.append(TH(c['label'], **attrcol)) components.append(THEAD(TR(*row))) - tbody = [] for (rc, record) in enumerate(sqlrows): row = [] @@ -2476,7 +2534,7 @@ class SQLTABLE(TABLE): else: _class = 'odd' - if not selectid is None: #new implement + if not selectid is None: # new implement if record.get('id') == selectid: _class += ' rowselected' @@ -2487,20 +2545,21 @@ class SQLTABLE(TABLE): row.append(TD(r)) continue else: - raise KeyError("Column %s not found (SQLTABLE)" % colname) + raise KeyError( + "Column %s not found (SQLTABLE)" % colname) (tablename, fieldname) = colname.split('.') try: field = sqlrows.db[tablename][fieldname] except KeyError: field = None if tablename in record \ - and isinstance(record,Row) \ - and isinstance(record[tablename],Row): + and isinstance(record, Row) \ + and isinstance(record[tablename], Row): r = record[tablename][fieldname] elif fieldname in record: r = record[fieldname] else: - raise SyntaxError, 'something wrong in Rows object' + raise SyntaxError('something wrong in Rows object') r_old = r if not field: pass @@ -2518,26 +2577,26 @@ class SQLTABLE(TABLE): except TypeError: href = '%s/%s/%s' % (linkto, ref, r_old) if ref.find('.') >= 0: - tref,fref = ref.split('.') - if hasattr(sqlrows.db[tref],'_primarykey'): - href = '%s/%s?%s' % (linkto, tref, urllib.urlencode({fref:r})) - r = A(represent(field,r,record), _href=str(href)) + tref, fref = ref.split('.') + if hasattr(sqlrows.db[tref], '_primarykey'): + href = '%s/%s?%s' % (linkto, tref, urllib.urlencode({fref: r})) + r = A(represent(field, r, record), _href=str(href)) elif field.represent: - r = represent(field,r,record) - elif linkto and hasattr(field._table,'_primarykey')\ + r = represent(field, r, record) + elif linkto and hasattr(field._table, '_primarykey')\ and fieldname in field._table._primarykey: # have to test this with multi-key tables - key = urllib.urlencode(dict( [ \ - ((tablename in record \ - and isinstance(record, Row) \ + key = urllib.urlencode(dict([ + ((tablename in record + and isinstance(record, Row) and isinstance(record[tablename], Row)) and - (k, record[tablename][k])) or (k, record[k]) \ - for k in field._table._primarykey ] )) + (k, record[tablename][k])) or (k, record[k]) + for k in field._table._primarykey])) r = A(r, _href='%s/%s?%s' % (linkto, tablename, key)) elif isinstance(field.type, str) and field.type.startswith('list:'): - r = represent(field,r or [],record) + r = represent(field, r or [], record) elif field.represent: - r = represent(field,r,record) + r = represent(field, r, record) elif field.type == 'blob' and r: r = 'DATA' elif field.type == 'upload': @@ -2547,35 +2606,37 @@ class SQLTABLE(TABLE): r = current.T('file') else: r = '' - elif field.type in ['string','text']: + elif field.type in ['string', 'text']: r = str(field.formatter(r)) - if headers!={}: #new implement dict - if isinstance(headers[colname],dict): + if headers != {}: # new implement dict + if isinstance(headers[colname], dict): if isinstance(headers[colname]['truncate'], int): - r = truncate_string(r, headers[colname]['truncate']) + r = truncate_string( + r, headers[colname]['truncate']) elif not truncate is None: r = truncate_string(r, truncate) - attrcol = dict()#new implement dict - if headers!={}: - if isinstance(headers[colname],dict): - colclass=headers[colname]['class'] + attrcol = dict() # new implement dict + if headers != {}: + if isinstance(headers[colname], dict): + colclass = headers[colname]['class'] if headers[colname]['selected']: - colclass= str(headers[colname]['class'] + " colselected").strip() - if colclass!="": + colclass = str(headers[colname] + ['class'] + " colselected").strip() + if colclass != "": attrcol.update(_class=colclass) - row.append(TD(r,**attrcol)) + row.append(TD(r, **attrcol)) - if extracolumns:#new implement dict + if extracolumns: # new implement dict for c in extracolumns: attrcol = dict() - colclass=c['class'] + colclass = c['class'] if c['selected']: - colclass= str(c['class'] + " colselected").strip() - if colclass!="": + colclass = str(c['class'] + " colselected").strip() + if colclass != "": attrcol.update(_class=colclass) contentfunc = c['content'] - row.append(TD(contentfunc(record, rc),**attrcol)) + row.append(TD(contentfunc(record, rc), **attrcol)) tbody.append(TR(_class=_class, *row)) @@ -2584,7 +2645,6 @@ class SQLTABLE(TABLE): components.append(TBODY(*tbody)) - def style(self): css = ''' @@ -2607,7 +2667,7 @@ class SQLTABLE(TABLE): return css -form_factory = SQLFORM.factory # for backward compatibility, deprecated +form_factory = SQLFORM.factory # for backward compatibility, deprecated class ExportClass(object): @@ -2629,11 +2689,11 @@ class ExportClass(object): return '' elif isinstance(value, unicode): return value.encode('utf8') - elif isinstance(value,Reference): + elif isinstance(value, Reference): return int(value) elif hasattr(value, 'isoformat'): return value.isoformat()[:19].replace('T', ' ') - elif isinstance(value, (list,tuple)): # for type='list:..' + elif isinstance(value, (list, tuple)): # for type='list:..' return bar_encode(value) return value @@ -2646,11 +2706,11 @@ class ExportClass(object): else: (t, f) = col.split('.') field = self.rows.db[t][f] - if isinstance(record.get(t, None), (Row,dict)): + if isinstance(record.get(t, None), (Row, dict)): value = record[t][f] else: value = record[f] - if field.type=='blob' and not value is None: + if field.type == 'blob' and not value is None: value = '' elif field.represent: value = field.represent(value, record) @@ -2662,6 +2722,7 @@ class ExportClass(object): def export(self): raise NotImplementedError + class ExporterTSV(ExportClass): label = 'TSV' @@ -2680,7 +2741,8 @@ class ExporterTSV(ExportClass): import codecs final.write(codecs.BOM_UTF16) colnames = [a.split('.') for a in self.rows.colnames] - writer.writerow([unicode(col).encode("utf8") for col in self.rows.colnames]) + writer.writerow( + [unicode(col).encode("utf8") for col in self.rows.colnames]) data = out.getvalue().decode("utf8") data = data.encode("utf-16") data = data[2:] @@ -2688,7 +2750,8 @@ class ExporterTSV(ExportClass): out.truncate(0) records = self.represented() for row in records: - writer.writerow([str(col).decode('utf8').encode("utf-8") for col in row]) + writer.writerow( + [str(col).decode('utf8').encode("utf-8") for col in row]) data = out.getvalue().decode("utf8") data = data.encode("utf-16") data = data[2:] @@ -2696,6 +2759,7 @@ class ExporterTSV(ExportClass): out.truncate(0) return str(final.getvalue()) + class ExporterCSV(ExportClass): label = 'CSV' file_ext = "csv" @@ -2707,6 +2771,7 @@ class ExporterCSV(ExportClass): def export(self): return str(self.rows) + class ExporterHTML(ExportClass): label = 'HTML' file_ext = "html" @@ -2722,7 +2787,7 @@ class ExporterHTML(ExportClass): for row in self.rows.records: out.write('\n') for col in colnames: - out.write(''+str(row[col[0]][col[1]])+'\n') + out.write('' + str(row[col[0]][col[1]]) + '\n') out.write('\n') out.write('\n\n') return str(out.getvalue()) @@ -2743,7 +2808,8 @@ class ExporterXML(ExportClass): for row in self.rows.records: out.write('\n') for col in colnames: - out.write('<%s>'%col+str(row[col[0]][col[1]])+'\n'%col) + out.write( + '<%s>' % col + str(row[col[0]][col[1]]) + '\n' % col) out.write('\n') out.write('') return str(out.getvalue()) diff --git a/gluon/storage.py b/gluon/storage.py index a90ccd3d..bb63b408 100644 --- a/gluon/storage.py +++ b/gluon/storage.py @@ -18,6 +18,7 @@ import portalocker __all__ = ['List', 'Storage', 'Settings', 'Messages', 'StorageList', 'load_storage', 'save_storage'] + class Storage(dict): """ A Storage object is like a dictionary except `obj.foo` can be used @@ -38,7 +39,7 @@ class Storage(dict): >>> print o.a None """ - __slots__=() + __slots__ = () __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__ __getitem__ = dict.get @@ -48,7 +49,7 @@ class Storage(dict): __getstate__ = lambda self: None __copy__ = lambda self: Storage(self) - def getlist(self,key): + def getlist(self, key): """ Return a Storage value as a list. @@ -68,11 +69,11 @@ class Storage(dict): >>> request.vars.getlist('z') [] """ - value = self.get(key,[]) + value = self.get(key, []) return value if not value else \ - value if isinstance(value,(list,tuple)) else [value] + value if isinstance(value, (list, tuple)) else [value] - def getfirst(self,key,default=None): + def getfirst(self, key, default=None): """ Return the first or only value when given a request.vars-style key. @@ -93,7 +94,7 @@ class Storage(dict): values = self.getlist(key) return values[0] if values else default - def getlast(self,key,default=None): + def getlast(self, key, default=None): """ Returns the last or only single value when given a request.vars-style key. @@ -115,56 +116,66 @@ class Storage(dict): values = self.getlist(key) return values[-1] if values else default -PICKABLE = (str,int,long,float,bool,list,dict,tuple,set) +PICKABLE = (str, int, long, float, bool, list, dict, tuple, set) + class StorageList(Storage): """ like Storage but missing elements default to [] instead of None """ - def __getitem__(self,key): + def __getitem__(self, key): return self.__getattr__(key) + def __getattr__(self, key): if key in self: - return getattr(self,key) + return getattr(self, key) else: r = [] - setattr(self,key,r) + setattr(self, key, r) return r + def load_storage(filename): fp = None try: fp = portalocker.LockedFile(filename, 'rb') storage = cPickle.load(fp) finally: - if fp: fp.close() + if fp: + fp.close() return Storage(storage) + def save_storage(storage, filename): fp = None try: fp = portalocker.LockedFile(filename, 'wb') cPickle.dump(dict(storage), fp) finally: - if fp: fp.close() + if fp: + fp.close() + class Settings(Storage): def __setattr__(self, key, value): if key != 'lock_keys' and self['lock_keys'] and key not in self: - raise SyntaxError, 'setting key \'%s\' does not exist' % key + raise SyntaxError('setting key \'%s\' does not exist' % key) if key != 'lock_values' and self['lock_values']: - raise SyntaxError, 'setting value cannot be changed: %s' % key + raise SyntaxError('setting value cannot be changed: %s' % key) self[key] = value + class Messages(Settings): def __init__(self, T): - Storage.__init__(self,T=T) + Storage.__init__(self, T=T) + def __getattr__(self, key): value = self[key] if isinstance(value, str): return str(self.T(value)) return value + class FastStorage(dict): """ Eventually this should replace class Storage but causes memory leak @@ -203,25 +214,33 @@ class FastStorage(dict): def __init__(self, *args, **kwargs): dict.__init__(self, *args, **kwargs) self.__dict__ = self - def __getattr__(self,key): - return getattr(self,key) if key in self else None - def __getitem__(self,key): - return dict.get(self,key,None) + + def __getattr__(self, key): + return getattr(self, key) if key in self else None + + def __getitem__(self, key): + return dict.get(self, key, None) + def copy(self): self.__dict__ = {} s = FastStorage(self) self.__dict__ = self return s + def __repr__(self): return '' % dict.__repr__(self) + def __getstate__(self): return dict(self) + def __setstate__(self, sdict): dict.__init__(self, sdict) - self.__dict__=self + self.__dict__ = self + def update(self, *args, **kwargs): dict.__init__(self, *args, **kwargs) - self.__dict__=self + self.__dict__ = self + class List(list): """ @@ -235,7 +254,7 @@ class List(list): request.args(0,default=0,cast=int,otherwise=lambda:...) """ n = len(self) - if 0<=imodified: + if os.path.isfile(gzipped) and os.path.getmtime(gzipped) > modified: static_file = gzipped fsize = os.path.getsize(gzipped) headers['Content-Encoding'] = 'gzip' diff --git a/gluon/template.py b/gluon/template.py index e531860c..0c1cb913 100644 --- a/gluon/template.py +++ b/gluon/template.py @@ -26,23 +26,26 @@ try: except ImportError: # do not have web2py current = None - def RestrictedError(a,b,c): - logging.error(str(a)+':'+str(b)+':'+str(c)) + + def RestrictedError(a, b, c): + logging.error(str(a) + ':' + str(b) + ':' + str(c)) return RuntimeError + class Node(object): """ Basic Container Object """ - def __init__(self, value = None, pre_extend = False): + def __init__(self, value=None, pre_extend=False): self.value = value self.pre_extend = pre_extend def __str__(self): return str(self.value) + class SuperNode(Node): - def __init__(self, name = '', pre_extend = False): + def __init__(self, name='', pre_extend=False): self.name = name self.value = None self.pre_extend = pre_extend @@ -57,7 +60,8 @@ class SuperNode(Node): def __repr__(self): return "%s->%s" % (self.name, self.value) -def output_aux(node,blocks): + +def output_aux(node, blocks): # If we have a block level # If we can override this block. # Override block from vars. @@ -66,8 +70,9 @@ def output_aux(node,blocks): return (blocks[node.name].output(blocks) if node.name in blocks else node.output(blocks)) \ - if isinstance(node, BlockNode) \ - else str(node) + if isinstance(node, BlockNode) \ + else str(node) + class BlockNode(Node): """ @@ -82,7 +87,7 @@ class BlockNode(Node): This is default block test {{ end }} """ - def __init__(self, name = '', pre_extend = False, delimiters = ('{{','}}')): + def __init__(self, name='', pre_extend=False, delimiters=('{{', '}}')): """ name - Name of this Node. """ @@ -92,7 +97,7 @@ class BlockNode(Node): self.left, self.right = delimiters def __repr__(self): - lines = ['%sblock %s%s' % (self.left,self.name,self.right)] + lines = ['%sblock %s%s' % (self.left, self.name, self.right)] lines += [str(node) for node in self.nodes] lines.append('%send%s' % (self.left, self.right)) return ''.join(lines) @@ -101,8 +106,8 @@ class BlockNode(Node): """ Get this BlockNodes content, not including child Nodes """ - return ''.join(str(node) for node in self.nodes \ - if not isinstance(node, BlockNode)) + return ''.join(str(node) for node in self.nodes + if not isinstance(node, BlockNode)) def append(self, node): """ @@ -128,8 +133,8 @@ class BlockNode(Node): if isinstance(other, BlockNode): self.nodes.extend(other.nodes) else: - raise TypeError("Invalid type; must be instance of ``BlockNode``. %s" % other) - + raise TypeError( + "Invalid type; must be instance of ``BlockNode``. %s" % other) def output(self, blocks): """ @@ -137,7 +142,8 @@ class BlockNode(Node): blocks -- Dictionary of blocks that are extending from this template. """ - return ''.join(output_aux(node,blocks) for node in self.nodes) + return ''.join(output_aux(node, blocks) for node in self.nodes) + class Content(BlockNode): """ @@ -145,7 +151,7 @@ class Content(BlockNode): Contains functions that operate as such. """ - def __init__(self, name = "ContentBlock", pre_extend = False): + def __init__(self, name="ContentBlock", pre_extend=False): """ Keyword Arguments @@ -157,18 +163,19 @@ class Content(BlockNode): self.pre_extend = pre_extend def __str__(self): - return ''.join(output_aux(node,self.blocks) for node in self.nodes) + return ''.join(output_aux(node, self.blocks) for node in self.nodes) - def _insert(self, other, index = 0): + def _insert(self, other, index=0): """ Inserts object at index. """ if isinstance(other, (str, Node)): self.nodes.insert(index, other) else: - raise TypeError("Invalid type, must be instance of ``str`` or ``Node``.") + raise TypeError( + "Invalid type, must be instance of ``str`` or ``Node``.") - def insert(self, other, index = 0): + def insert(self, other, index=0): """ Inserts object at index. @@ -201,21 +208,23 @@ class Content(BlockNode): self.nodes.extend(other.nodes) self.blocks.update(other.blocks) else: - raise TypeError("Invalid type; must be instance of ``BlockNode``. %s" % other) + raise TypeError( + "Invalid type; must be instance of ``BlockNode``. %s" % other) def clear_content(self): self.nodes = [] + class TemplateParser(object): - default_delimiters = ('{{','}}') + default_delimiters = ('{{', '}}') r_tag = compile(r'(\{\{.*?\}\})', DOTALL) r_multiline = compile(r'(""".*?""")|(\'\'\'.*?\'\'\')', DOTALL) # These are used for re-indentation. # Indent + 1 - re_block = compile('^(elif |else:|except:|except |finally:).*$',DOTALL) + re_block = compile('^(elif |else:|except:|except |finally:).*$', DOTALL) # Indent - 1 re_unblock = compile('^(return|continue|break|raise)( .*)?$', DOTALL) @@ -223,12 +232,12 @@ class TemplateParser(object): re_pass = compile('^pass( .*)?$', DOTALL) def __init__(self, text, - name = "ParserContainer", - context = dict(), - path = 'views/', - writer = 'response.write', - lexers = {}, - delimiters = ('{{','}}'), + name="ParserContainer", + context=dict(), + path='views/', + writer='response.write', + lexers={}, + delimiters=('{{', '}}'), _super_nodes = [], ): """ @@ -270,7 +279,7 @@ class TemplateParser(object): escaped_delimiters = (escape(delimiters[0]), escape(delimiters[1])) self.r_tag = compile(r'(%s.*?%s)' % escaped_delimiters, DOTALL) - elif hasattr(context.get('response',None),'delimiters'): + elif hasattr(context.get('response', None), 'delimiters'): if context['response'].delimiters != self.default_delimiters: escaped_delimiters = ( escape(context['response'].delimiters[0]), @@ -359,10 +368,10 @@ class TemplateParser(object): k = k + credit - 1 # We obviously can't have a negative indentation - k = max(k,0) + k = max(k, 0) # Add the indentation! - new_lines.append(' '*(4*k)+line) + new_lines.append(' ' * (4 * k) + line) # Bank account back to 0 again :( credit = 0 @@ -416,7 +425,7 @@ class TemplateParser(object): # Allow Views to include other views dynamically context = self.context if current and not "response" in context: - context["response"] = getattr(current,'response',None) + context["response"] = getattr(current, 'response', None) # Get the filename; filename looks like ``"template.html"``. # We need to eval to remove the quotes and get the string type. @@ -442,11 +451,11 @@ class TemplateParser(object): text = self._get_file_text(filename) t = TemplateParser(text, - name = filename, - context = self.context, - path = self.path, - writer = self.writer, - delimiters = self.delimiters) + name=filename, + context=self.context, + path=self.path, + writer=self.writer, + delimiters=self.delimiters) content.append(t.content) @@ -465,16 +474,17 @@ class TemplateParser(object): super_nodes.extend(self.super_nodes) t = TemplateParser(text, - name = filename, - context = self.context, - path = self.path, - writer = self.writer, - delimiters = self.delimiters, - _super_nodes = super_nodes) + name=filename, + context=self.context, + path=self.path, + writer=self.writer, + delimiters=self.delimiters, + _super_nodes=super_nodes) # Make a temporary buffer that is unique for parent # template. - buf = BlockNode(name='__include__' + filename, delimiters=self.delimiters) + buf = BlockNode( + name='__include__' + filename, delimiters=self.delimiters) pre = [] # Iterate through each of our nodes @@ -601,22 +611,22 @@ class TemplateParser(object): # You can define custom names such as # '{{<>> render() @@ -856,7 +877,7 @@ def render(content = "hello world", # If we don't have anything to render, why bother? if not content and not stream and not filename: - raise SyntaxError, "Must specify a stream or filename or content" + raise SyntaxError("Must specify a stream or filename or content") # Here for legacy purposes, probably can be reduced to # something more simple. @@ -869,7 +890,8 @@ def render(content = "hello world", stream = cStringIO.StringIO(content) # Execute the template. - code = str(TemplateParser(stream.read(), context=context, path=path, lexers=lexers, delimiters=delimiters)) + code = str(TemplateParser(stream.read( + ), context=context, path=path, lexers=lexers, delimiters=delimiters)) try: exec(code) in context except Exception: diff --git a/gluon/tests/test_cache.py b/gluon/tests/test_cache.py index ec9b94d4..2ef79103 100644 --- a/gluon/tests/test_cache.py +++ b/gluon/tests/test_cache.py @@ -18,6 +18,7 @@ from cache import CacheInRam, CacheOnDisk oldcwd = None + def setUpModule(): global oldcwd if oldcwd is None: @@ -25,12 +26,14 @@ def setUpModule(): if not os.path.isdir('gluon'): os.chdir(os.path.realpath('../../')) + def tearDownModule(): global oldcwd if oldcwd: os.chdir(oldcwd) oldcwd = None + class TestCache(unittest.TestCase): def testCacheInRam(self): @@ -70,5 +73,3 @@ if __name__ == '__main__': setUpModule() # pre-python-2.7 unittest.main() tearDownModule() - - diff --git a/gluon/tests/test_contribs.py b/gluon/tests/test_contribs.py index fbb4595f..534c04dd 100644 --- a/gluon/tests/test_contribs.py +++ b/gluon/tests/test_contribs.py @@ -22,13 +22,14 @@ class TestContribs(unittest.TestCase): def test_fpdf(self): """ Basic PDF test and sanity checks """ - self.assertEqual(fpdf.FPDF_VERSION, pyfpdf.FPDF_VERSION, 'version mistmatch') + self.assertEqual( + fpdf.FPDF_VERSION, pyfpdf.FPDF_VERSION, 'version mistmatch') self.assertEqual(fpdf.FPDF, pyfpdf.FPDF, 'class mistmatch') pdf = fpdf.FPDF() pdf.add_page() pdf.compress = False - pdf.set_font('Arial', '',14) + pdf.set_font('Arial', '', 14) pdf.ln(10) pdf.write(5, 'hello world') pdf_out = pdf.output('', 'S') @@ -39,4 +40,3 @@ class TestContribs(unittest.TestCase): if __name__ == '__main__': unittest.main() - diff --git a/gluon/tests/test_html.py b/gluon/tests/test_html.py index ff906b56..336eff9b 100644 --- a/gluon/tests/test_html.py +++ b/gluon/tests/test_html.py @@ -130,7 +130,7 @@ class TestBareHelpers(unittest.TestCase): def testOPTION(self): self.assertEqual(OPTION('<>', _a='1', _b='2').xml(), - '') def testP(self): @@ -149,7 +149,7 @@ class TestBareHelpers(unittest.TestCase): def testSELECT(self): self.assertEqual(SELECT('<>', _a='1', _b='2').xml(), - '' + '') def testSPAN(self): @@ -162,7 +162,7 @@ class TestBareHelpers(unittest.TestCase): def testTABLE(self): self.assertEqual(TABLE('<>', _a='1', _b='2').xml(), - '' + \ + '
<>
' + '
<>
') def testTBODY(self): @@ -175,8 +175,8 @@ class TestBareHelpers(unittest.TestCase): def testTEXTAREA(self): self.assertEqual(TEXTAREA('<>', _a='1', _b='2').xml(), - '') + '') def testTFOOT(self): self.assertEqual(TFOOT('<>', _a='1', _b='2').xml(), @@ -209,4 +209,3 @@ class TestBareHelpers(unittest.TestCase): if __name__ == '__main__': unittest.main() - diff --git a/gluon/tests/test_is_url.py b/gluon/tests/test_is_url.py index 7a17496c..39f89610 100644 --- a/gluon/tests/test_is_url.py +++ b/gluon/tests/test_is_url.py @@ -31,7 +31,8 @@ class TestIsUrl(unittest.TestCase): self.assertEqual(x('unreal.blargg'), ('unreal.blargg', 'enter a valid URL')) self.assertEqual(x('google..ca'), ('google..ca', 'enter a valid URL')) - self.assertEqual(x('google.ca..'), ('google.ca..', 'enter a valid URL')) + self.assertEqual( + x('google.ca..'), ('google.ca..', 'enter a valid URL')) # explicit use of 'http' mode @@ -116,9 +117,9 @@ class TestIsUrl(unittest.TestCase): # 'generic' mode x = IS_URL(mode='generic') - self.assertEqual(x('http://google.ca'), ('http://google.ca',None)) + self.assertEqual(x('http://google.ca'), ('http://google.ca', None)) self.assertEqual(x('google.ca'), ('google.ca', None)) - self.assertEqual(x('google.ca:80'), ('http://google.ca:80',None)) + self.assertEqual(x('google.ca:80'), ('http://google.ca:80', None)) self.assertEqual(x('blargg://unreal'), ('blargg://unreal', 'enter a valid URL')) @@ -152,7 +153,7 @@ class TestIsUrl(unittest.TestCase): # 'generic' mode with overriden allowed_schemes and prepend_scheme x = IS_URL(mode='generic', allowed_schemes=[None, 'ftp', 'ftps' - ], prepend_scheme='ftp') + ], prepend_scheme='ftp') self.assertEqual(x('http://google.ca'), ('http://google.ca', 'enter a valid URL')) self.assertEqual(x('google.ca'), ('google.ca', None)) @@ -191,10 +192,11 @@ class TestIsUrl(unittest.TestCase): # error at calling time except Exception, e: if str(e)\ - != "allowed_scheme value 'ftp' is not in [None, 'http', 'https']": + != "allowed_scheme value 'ftp' is not in [None, 'http', 'https']": self.fail('Wrong exception: ' + str(e)) else: - self.fail("Accepted invalid allowed_schemes: [None, 'ftp', 'ftps']") + self.fail( + "Accepted invalid allowed_schemes: [None, 'ftp', 'ftps']") # prepend_scheme's value must be in allowed_schemes (default for 'http' # mode is [None, 'http', 'https']) @@ -205,7 +207,7 @@ class TestIsUrl(unittest.TestCase): # error at calling time except Exception, e: if str(e)\ - != "prepend_scheme='ftp' is not in allowed_schemes=[None, 'http', 'https']": + != "prepend_scheme='ftp' is not in allowed_schemes=[None, 'http', 'https']": self.fail('Wrong exception: ' + str(e)) else: self.fail("Accepted invalid prepend_scheme: 'ftp'") @@ -217,7 +219,7 @@ class TestIsUrl(unittest.TestCase): x = IS_URL(allowed_schemes=[None, 'https']) except Exception, e: if str(e)\ - != "prepend_scheme='http' is not in allowed_schemes=[None, 'https']": + != "prepend_scheme='http' is not in allowed_schemes=[None, 'https']": self.fail('Wrong exception: ' + str(e)) else: self.fail("Accepted invalid prepend_scheme: 'http'") @@ -229,7 +231,7 @@ class TestIsUrl(unittest.TestCase): prepend_scheme='https') except Exception, e: if str(e)\ - != "prepend_scheme='https' is not in allowed_schemes=[None, 'http']": + != "prepend_scheme='https' is not in allowed_schemes=[None, 'http']": self.fail('Wrong exception: ' + str(e)) else: self.fail("Accepted invalid prepend_scheme: 'https'") @@ -241,7 +243,7 @@ class TestIsUrl(unittest.TestCase): 'ftps']) except Exception, e: if str(e)\ - != "prepend_scheme='http' is not in allowed_schemes=[None, 'ftp', 'ftps']": + != "prepend_scheme='http' is not in allowed_schemes=[None, 'ftp', 'ftps']": self.fail('Wrong exception: ' + str(e)) else: self.fail("Accepted invalid prepend_scheme: 'http'") @@ -251,7 +253,8 @@ class TestIsUrl(unittest.TestCase): try: x = IS_URL(mode='generic', prepend_scheme='blargg') - x('http://www.google.ca') # we can only reasonably know about the error at calling time + x('http://www.google.ca') + # we can only reasonably know about the error at calling time except Exception, e: if not str(e).startswith( "prepend_scheme='blargg' is not in allowed_schemes="): @@ -266,7 +269,7 @@ class TestIsUrl(unittest.TestCase): prepend_scheme='blargg') except Exception, e: if str(e)\ - != "prepend_scheme='blargg' is not in allowed_schemes=[None, 'http']": + != "prepend_scheme='blargg' is not in allowed_schemes=[None, 'http']": self.fail('Wrong exception: ' + str(e)) else: self.fail("Accepted invalid prepend_scheme: 'blargg'") @@ -282,7 +285,7 @@ class TestIsUrl(unittest.TestCase): # prepend_scheme has the invalid value 'http', we don't care! x = IS_URL(mode='generic', allowed_schemes=['https'], - prepend_scheme='https') + prepend_scheme='https') self.assertEqual(x('google.ca'), ('google.ca', 'enter a valid URL')) @@ -335,12 +338,12 @@ class TestIsGenericUrl(unittest.TestCase): 'ht,tp://www.benn.ca', 'ht:tp://www.benn.ca', 'htp://invalid_scheme.com', - ] + ] failures = [] for url in urlsToCheckA + urlsToCheckB: - if self.x(url)[1] == None: + if self.x(url)[1] is None: failures.append('Incorrectly accepted: ' + str(url)) if len(failures) > 0: @@ -377,14 +380,13 @@ class TestIsGenericUrl(unittest.TestCase): 'http://localhost:8080/', 'http://localhost:8080/hello', 'http://localhost:8080/hello/', - 'file:///C:/Documents%20and%20Settings/Jonathan/Desktop/view.py' - , - ] + 'file:///C:/Documents%20and%20Settings/Jonathan/Desktop/view.py', + ] failures = [] for url in urlsToCheck: - if self.x(url)[1] != None: + if self.x(url)[1] is not None: failures.append('Incorrectly rejected: ' + str(url)) if len(failures) > 0: @@ -405,7 +407,7 @@ class TestIsGenericUrl(unittest.TestCase): # because a scheme is required y = IS_GENERIC_URL(allowed_schemes=['http', 'blargg'], - prepend_scheme='http') + prepend_scheme='http') self.assertEqual(y('google.ca'), ('google.ca', 'enter a valid URL')) @@ -459,12 +461,12 @@ class TestIsHttpUrl(unittest.TestCase): 'path/segment/without/starting/slash', 'http://www.math.uio.no;param=3', '://ABC.com:/%7esmith/home.html', - ] + ] failures = [] for url in urlsToCheck: - if self.x(url)[1] == None: + if self.x(url)[1] is None: failures.append('Incorrectly accepted: ' + str(url)) if len(failures) > 0: @@ -523,8 +525,7 @@ class TestIsHttpUrl(unittest.TestCase): 'HTTPS://localhost.', 'http://localhost#fragment', 'http://localhost/hello;param=value', - 'http://localhost/hello;param=value/hi;param2=value2;param3=value3' - , + 'http://localhost/hello;param=value/hi;param2=value2;param3=value3', 'http://localhost/hello?query=True', 'http://www.benn.ca/hello;param=value/hi;param2=value2;param3=value3/index.html?query=3', 'http://localhost/hello/?query=1500&five=6', @@ -534,12 +535,12 @@ class TestIsHttpUrl(unittest.TestCase): 'http://localhost:8080/hello%20world/', 'http://www.a.3.be-nn.5.ca', 'http://www.amazon.COM', - ] + ] failures = [] for url in urlsToCheck: - if self.x(url)[1] != None: + if self.x(url)[1] is not None: failures.append('Incorrectly rejected: ' + str(url)) if len(failures) > 0: @@ -557,17 +558,20 @@ class TestIsHttpUrl(unittest.TestCase): self.assertEqual(self.x('https://google.ca'), ('https://google.ca', None)) - y = IS_HTTP_URL(prepend_scheme='https', allowed_schemes=[None, 'https']) - self.assertEqual(y('google.ca'), ('https://google.ca', None)) # prepends https if asked + y = IS_HTTP_URL( + prepend_scheme='https', allowed_schemes=[None, 'https']) + self.assertEqual(y('google.ca'), ( + 'https://google.ca', None)) # prepends https if asked z = IS_HTTP_URL(prepend_scheme=None) - self.assertEqual(z('google.ca:8080'), ('google.ca:8080', None)) # prepending disabled + self.assertEqual(z('google.ca:8080'), ('google.ca:8080', + None)) # prepending disabled try: IS_HTTP_URL(prepend_scheme='mailto') except Exception, e: if str(e)\ - != "prepend_scheme='mailto' is not in allowed_schemes=[None, 'http', 'https']": + != "prepend_scheme='mailto' is not in allowed_schemes=[None, 'http', 'https']": self.fail('Wrong exception: ' + str(e)) else: self.fail("Got invalid prepend_scheme: 'mailto'") @@ -579,67 +583,93 @@ class TestIsHttpUrl(unittest.TestCase): self.assertEqual(a('google.ca:80'), ('google.ca:80', 'enter a valid URL')) + class TestUnicode(unittest.TestCase): x = IS_URL() - y = IS_URL(allowed_schemes=['https'], prepend_scheme='https') #excludes the option for abbreviated URLs with no scheme - z = IS_URL(prepend_scheme=None) # disables prepending the scheme in the return value - + y = IS_URL(allowed_schemes=['https'], prepend_scheme='https') + #excludes the option for abbreviated URLs with no scheme + z = IS_URL(prepend_scheme=None) + # disables prepending the scheme in the return value def testUnicodeToAsciiUrl(self): self.assertEquals(unicode_to_ascii_authority(u'www.Alliancefran\xe7aise.nu'), 'www.xn--alliancefranaise-npb.nu') - self.assertEquals(unicode_to_ascii_authority(u'www.benn.ca'), 'www.benn.ca') - self.assertRaises(UnicodeError, unicode_to_ascii_authority, u'\u4e2d'*1000) #label is too long - + self.assertEquals( + unicode_to_ascii_authority(u'www.benn.ca'), 'www.benn.ca') + self.assertRaises(UnicodeError, unicode_to_ascii_authority, + u'\u4e2d' * 1000) # label is too long def testValidUrls(self): - self.assertEquals(self.x(u'www.Alliancefrancaise.nu'), ('http://www.Alliancefrancaise.nu', None)) - self.assertEquals(self.x(u'www.Alliancefran\xe7aise.nu'), ('http://www.xn--alliancefranaise-npb.nu', None)) - self.assertEquals(self.x(u'www.Alliancefran\xe7aise.nu:8080'), ('http://www.xn--alliancefranaise-npb.nu:8080', None)) - self.assertEquals(self.x(u'http://www.Alliancefran\xe7aise.nu'), ('http://www.xn--alliancefranaise-npb.nu', None)) + self.assertEquals(self.x(u'www.Alliancefrancaise.nu'), ( + 'http://www.Alliancefrancaise.nu', None)) + self.assertEquals(self.x(u'www.Alliancefran\xe7aise.nu'), ( + 'http://www.xn--alliancefranaise-npb.nu', None)) + self.assertEquals(self.x(u'www.Alliancefran\xe7aise.nu:8080'), ( + 'http://www.xn--alliancefranaise-npb.nu:8080', None)) + self.assertEquals(self.x(u'http://www.Alliancefran\xe7aise.nu'), + ('http://www.xn--alliancefranaise-npb.nu', None)) self.assertEquals(self.x(u'http://www.Alliancefran\xe7aise.nu/parnaise/blue'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue', None)) self.assertEquals(self.x(u'http://www.Alliancefran\xe7aise.nu/parnaise/blue#fragment'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue#fragment', None)) self.assertEquals(self.x(u'http://www.Alliancefran\xe7aise.nu/parnaise/blue?query=value#fragment'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue?query=value#fragment', None)) self.assertEquals(self.x(u'http://www.Alliancefran\xe7aise.nu:8080/parnaise/blue?query=value#fragment'), ('http://www.xn--alliancefranaise-npb.nu:8080/parnaise/blue?query=value#fragment', None)) self.assertEquals(self.x(u'www.Alliancefran\xe7aise.nu/parnaise/blue?query=value#fragment'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue?query=value#fragment', None)) - self.assertEquals(self.x(u'http://\u4e2d\u4fd4.com'), ('http://xn--fiq13b.com', None)) - self.assertEquals(self.x(u'http://\u4e2d\u4fd4.com/\u4e86'), ('http://xn--fiq13b.com/%4e%86', None)) + self.assertEquals(self.x( + u'http://\u4e2d\u4fd4.com'), ('http://xn--fiq13b.com', None)) + self.assertEquals(self.x(u'http://\u4e2d\u4fd4.com/\u4e86'), + ('http://xn--fiq13b.com/%4e%86', None)) self.assertEquals(self.x(u'http://\u4e2d\u4fd4.com/\u4e86?query=\u4e86'), ('http://xn--fiq13b.com/%4e%86?query=%4e%86', None)) self.assertEquals(self.x(u'http://\u4e2d\u4fd4.com/\u4e86?query=\u4e86#fragment'), ('http://xn--fiq13b.com/%4e%86?query=%4e%86#fragment', None)) self.assertEquals(self.x(u'http://\u4e2d\u4fd4.com?query=\u4e86#fragment'), ('http://xn--fiq13b.com?query=%4e%86#fragment', None)) - self.assertEquals(self.x(u'http://B\xfccher.ch'), ('http://xn--bcher-kva.ch', None)) - self.assertEquals(self.x(u'http://\xe4\xf6\xfc\xdf.com'), ('http://xn--ss-uia6e4a.com', None)) - self.assertEquals(self.x(u'http://visegr\xe1d.com'), ('http://xn--visegrd-mwa.com', None)) - self.assertEquals(self.x(u'http://h\xe1zipatika.com'), ('http://xn--hzipatika-01a.com', None)) - self.assertEquals(self.x(u'http://www.\xe7ukurova.com'), ('http://www.xn--ukurova-txa.com', None)) + self.assertEquals( + self.x(u'http://B\xfccher.ch'), ('http://xn--bcher-kva.ch', None)) + self.assertEquals(self.x(u'http://\xe4\xf6\xfc\xdf.com'), ( + 'http://xn--ss-uia6e4a.com', None)) + self.assertEquals(self.x( + u'http://visegr\xe1d.com'), ('http://xn--visegrd-mwa.com', None)) + self.assertEquals(self.x(u'http://h\xe1zipatika.com'), ( + 'http://xn--hzipatika-01a.com', None)) + self.assertEquals(self.x(u'http://www.\xe7ukurova.com'), ( + 'http://www.xn--ukurova-txa.com', None)) self.assertEquals(self.x(u'http://nixier\xf6hre.nixieclock-tube.com'), ('http://xn--nixierhre-57a.nixieclock-tube.com', None)) self.assertEquals(self.x(u'google.ca.'), ('http://google.ca.', None)) - self.assertEquals(self.y(u'https://google.ca'), ('https://google.ca', None)) - self.assertEquals(self.y(u'https://\u4e2d\u4fd4.com'), ('https://xn--fiq13b.com', None)) + self.assertEquals( + self.y(u'https://google.ca'), ('https://google.ca', None)) + self.assertEquals(self.y( + u'https://\u4e2d\u4fd4.com'), ('https://xn--fiq13b.com', None)) self.assertEquals(self.z(u'google.ca'), ('google.ca', None)) - def testInvalidUrls(self): - self.assertEquals(self.x(u'://ABC.com'), (u'://ABC.com', 'enter a valid URL')) - self.assertEquals(self.x(u'http://\u4e2d\u4fd4.dne'), (u'http://\u4e2d\u4fd4.dne', 'enter a valid URL')) - self.assertEquals(self.x(u'https://google.dne'), (u'https://google.dne', 'enter a valid URL')) - self.assertEquals(self.x(u'https://google..ca'), (u'https://google..ca', 'enter a valid URL')) - self.assertEquals(self.x(u'google..ca'), (u'google..ca', 'enter a valid URL')) - self.assertEquals(self.x(u'http://' + u'\u4e2d'*1000 + u'.com'), (u'http://' + u'\u4e2d'*1000 + u'.com', 'enter a valid URL')) + self.assertEquals( + self.x(u'://ABC.com'), (u'://ABC.com', 'enter a valid URL')) + self.assertEquals(self.x(u'http://\u4e2d\u4fd4.dne'), ( + u'http://\u4e2d\u4fd4.dne', 'enter a valid URL')) + self.assertEquals(self.x(u'https://google.dne'), ( + u'https://google.dne', 'enter a valid URL')) + self.assertEquals(self.x(u'https://google..ca'), ( + u'https://google..ca', 'enter a valid URL')) + self.assertEquals( + self.x(u'google..ca'), (u'google..ca', 'enter a valid URL')) + self.assertEquals(self.x(u'http://' + u'\u4e2d' * 1000 + u'.com'), ( + u'http://' + u'\u4e2d' * 1000 + u'.com', 'enter a valid URL')) - self.assertEquals(self.x(u'http://google.com#fragment_\u4e86'), (u'http://google.com#fragment_\u4e86', 'enter a valid URL')) - self.assertEquals(self.x(u'http\u4e86://google.com'), (u'http\u4e86://google.com', 'enter a valid URL')) - self.assertEquals(self.x(u'http\u4e86://google.com#fragment_\u4e86'), (u'http\u4e86://google.com#fragment_\u4e86', 'enter a valid URL')) + self.assertEquals(self.x(u'http://google.com#fragment_\u4e86'), ( + u'http://google.com#fragment_\u4e86', 'enter a valid URL')) + self.assertEquals(self.x(u'http\u4e86://google.com'), ( + u'http\u4e86://google.com', 'enter a valid URL')) + self.assertEquals(self.x(u'http\u4e86://google.com#fragment_\u4e86'), ( + u'http\u4e86://google.com#fragment_\u4e86', 'enter a valid URL')) - self.assertEquals(self.y(u'http://\u4e2d\u4fd4.com/\u4e86'), (u'http://\u4e2d\u4fd4.com/\u4e86', 'enter a valid URL')) + self.assertEquals(self.y(u'http://\u4e2d\u4fd4.com/\u4e86'), ( + u'http://\u4e2d\u4fd4.com/\u4e86', 'enter a valid URL')) #self.assertEquals(self.y(u'google.ca'), (u'google.ca', 'enter a valid URL')) - self.assertEquals(self.z(u'invalid.domain..com'), (u'invalid.domain..com', 'enter a valid URL')) - self.assertEquals(self.z(u'invalid.\u4e2d\u4fd4.blargg'), (u'invalid.\u4e2d\u4fd4.blargg', 'enter a valid URL')) + self.assertEquals(self.z(u'invalid.domain..com'), ( + u'invalid.domain..com', 'enter a valid URL')) + self.assertEquals(self.z(u'invalid.\u4e2d\u4fd4.blargg'), ( + u'invalid.\u4e2d\u4fd4.blargg', 'enter a valid URL')) # ############################################################################## if __name__ == '__main__': unittest.main() - diff --git a/gluon/tests/test_languages.py b/gluon/tests/test_languages.py index 5fda85e6..911ddcd2 100644 --- a/gluon/tests/test_languages.py +++ b/gluon/tests/test_languages.py @@ -49,12 +49,11 @@ try: def test_reads_and_writes(self): readwriters = 10 - pool = multiprocessing.Pool(processes = readwriters) + pool = multiprocessing.Pool(processes=readwriters) results = pool.map(read_write, [[self.filename, 10]] * readwriters) for result in results: self.assertTrue(result) - class TestTranslations(unittest.TestCase): def setUp(self): @@ -62,11 +61,11 @@ try: if os.path.isdir('gluon'): self.request.folder = 'applications/welcome' else: - self.request.folder = os.path.realpath('../../applications/welcome') + self.request.folder = os.path.realpath( + '../../applications/welcome') self.request.env = Storage() self.request.env.http_accept_language = 'en' - def tearDown(self): pass @@ -99,4 +98,3 @@ except ImportError: if __name__ == '__main__': unittest.main() - diff --git a/gluon/tests/test_markmin.py b/gluon/tests/test_markmin.py index 643a100a..f53d3aa0 100644 --- a/gluon/tests/test_markmin.py +++ b/gluon/tests/test_markmin.py @@ -13,10 +13,10 @@ else: import unittest from contrib.markmin.markmin2html import run_doctests + class TestMarkmin(unittest.TestCase): def testMarkmin(self): run_doctests() if __name__ == '__main__': unittest.main() - diff --git a/gluon/tests/test_router.py b/gluon/tests/test_router.py index 3cd75db3..8b9709b4 100644 --- a/gluon/tests/test_router.py +++ b/gluon/tests/test_router.py @@ -10,9 +10,9 @@ import tempfile import logging if os.path.isdir('gluon'): - sys.path.append(os.path.realpath('gluon')) # running from web2py base + sys.path.append(os.path.realpath('gluon')) # running from web2py base else: - sys.path.append(os.path.realpath('../')) # running from gluon/tests/ + sys.path.append(os.path.realpath('../')) # running from gluon/tests/ os.environ['web2py_path'] = os.path.realpath('../../') # for settings from rewrite import load, filter_url, filter_err, get_effective_router, map_url_out @@ -26,6 +26,7 @@ logger = None oldcwd = None root = None + def setUpModule(): def make_apptree(): "build a temporary applications tree" @@ -41,14 +42,17 @@ def setUpModule(): # applications/admin/controllers/*.py for ctr in ('appadmin', 'default', 'gae', 'mercurial', 'shell', 'wizard'): - open(abspath('applications', 'admin', 'controllers', '%s.py' % ctr), 'w').close() + open(abspath('applications', 'admin', + 'controllers', '%s.py' % ctr), 'w').close() # applications/examples/controllers/*.py for ctr in ('ajax_examples', 'appadmin', 'default', 'global', 'spreadsheet'): - open(abspath('applications', 'examples', 'controllers', '%s.py' % ctr), 'w').close() + open(abspath('applications', 'examples', + 'controllers', '%s.py' % ctr), 'w').close() # applications/welcome/controllers/*.py # (include controller that collides with another app) for ctr in ('appadmin', 'default', 'other', 'admin'): - open(abspath('applications', 'welcome', 'controllers', '%s.py' % ctr), 'w').close() + open(abspath('applications', 'welcome', + 'controllers', '%s.py' % ctr), 'w').close() # create an app-specific routes.py for examples app routes = open(abspath('applications', 'examples', 'routes.py'), 'w') @@ -58,13 +62,15 @@ def setUpModule(): # create language files for examples app for lang in ('en', 'it'): os.mkdir(abspath('applications', 'examples', 'static', lang)) - open(abspath('applications', 'examples', 'static', lang, 'file'), 'w').close() + open(abspath('applications', 'examples', 'static', + lang, 'file'), 'w').close() global oldcwd if oldcwd is None: # do this only once oldcwd = os.getcwd() if not os.path.isdir('gluon'): - os.chdir(os.path.realpath('../../')) # run from web2py base directory + os.chdir(os.path.realpath( + '../../')) # run from web2py base directory import main # for initialization after chdir global logger logger = logging.getLogger('web2py.rewrite') @@ -73,6 +79,7 @@ def setUpModule(): root = global_settings.applications_parent make_apptree() + def tearDownModule(): global oldcwd if oldcwd is not None: @@ -88,16 +95,18 @@ class TestRouter(unittest.TestCase): level = logger.getEffectiveLevel() logger.setLevel(logging.CRITICAL) # disable logging temporarily self.assertRaises(SyntaxError, load, data='x:y') - self.assertRaises(SyntaxError, load, rdict=dict(BASE=dict(badkey="value"))) - self.assertRaises(SyntaxError, load, rdict=dict(BASE=dict(), app=dict(default_application="name"))) + self.assertRaises( + SyntaxError, load, rdict=dict(BASE=dict(badkey="value"))) + self.assertRaises(SyntaxError, load, rdict=dict( + BASE=dict(), app=dict(default_application="name"))) try: # 2.7+ only self.assertRaisesRegexp(SyntaxError, "invalid syntax", - load, data='x:y') + load, data='x:y') self.assertRaisesRegexp(SyntaxError, "unknown key", - load, rdict=dict(BASE=dict(badkey="value"))) + load, rdict=dict(BASE=dict(badkey="value"))) self.assertRaisesRegexp(SyntaxError, "BASE-only key", - load, rdict=dict(BASE=dict(), app=dict(default_application="name"))) + load, rdict=dict(BASE=dict(), app=dict(default_application="name"))) except AttributeError: pass logger.setLevel(level) @@ -106,14 +115,20 @@ class TestRouter(unittest.TestCase): """ Tests the null router """ load(rdict=dict()) # app resolution - self.assertEqual(filter_url('http://domain.com/welcome', app=True), 'welcome') + self.assertEqual( + filter_url('http://domain.com/welcome', app=True), 'welcome') self.assertEqual(filter_url('http://domain.com/', app=True), 'init') # incoming - self.assertEqual(filter_url('http://domain.com/favicon.ico'), '%s/applications/init/static/favicon.ico' % root) - self.assertEqual(filter_url('http://domain.com/abc'), '/init/default/abc') - self.assertEqual(filter_url('http://domain.com/index/abc'), "/init/default/index ['abc']") - self.assertEqual(filter_url('http://domain.com/abc/def'), "/init/default/abc ['def']") - self.assertEqual(filter_url('http://domain.com/index/a%20bc'), "/init/default/index ['a bc']") + self.assertEqual(filter_url('http://domain.com/favicon.ico'), + '%s/applications/init/static/favicon.ico' % root) + self.assertEqual( + filter_url('http://domain.com/abc'), '/init/default/abc') + self.assertEqual(filter_url( + 'http://domain.com/index/abc'), "/init/default/index ['abc']") + self.assertEqual(filter_url( + 'http://domain.com/abc/def'), "/init/default/abc ['def']") + self.assertEqual(filter_url( + 'http://domain.com/index/a%20bc'), "/init/default/index ['a bc']") self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static'), "%s/applications/welcome/static/path/to/static" % root) self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/st~tic') try: @@ -122,15 +137,23 @@ class TestRouter(unittest.TestCase): except AttributeError: pass # outgoing - self.assertEqual(filter_url('http://domain.com/init/default/index', out=True), '/') + self.assertEqual( + filter_url('http://domain.com/init/default/index', out=True), '/') self.assertEqual(filter_url('http://domain.com/init/default/index/arg1', out=True), '/index/arg1') - self.assertEqual(filter_url('http://domain.com/init/default/abc', out=True), '/abc') - self.assertEqual(filter_url('http://domain.com/init/static/abc', out=True), '/init/static/abc') - self.assertEqual(filter_url('http://domain.com/init/appadmin/index', out=True), '/appadmin') - self.assertEqual(filter_url('http://domain.com/init/appadmin/abc', out=True), '/appadmin/abc') - self.assertEqual(filter_url('http://domain.com/init/admin/index', out=True), '/init/admin') - self.assertEqual(filter_url('http://domain.com/init/admin/abc', out=True), '/init/admin/abc') - self.assertEqual(filter_url('http://domain.com/admin/default/abc', out=True), '/admin/abc') + self.assertEqual(filter_url( + 'http://domain.com/init/default/abc', out=True), '/abc') + self.assertEqual(filter_url('http://domain.com/init/static/abc', + out=True), '/init/static/abc') + self.assertEqual(filter_url( + 'http://domain.com/init/appadmin/index', out=True), '/appadmin') + self.assertEqual(filter_url( + 'http://domain.com/init/appadmin/abc', out=True), '/appadmin/abc') + self.assertEqual(filter_url( + 'http://domain.com/init/admin/index', out=True), '/init/admin') + self.assertEqual(filter_url( + 'http://domain.com/init/admin/abc', out=True), '/init/admin/abc') + self.assertEqual(filter_url( + 'http://domain.com/admin/default/abc', out=True), '/admin/abc') def test_router_specific(self): """ @@ -139,38 +162,58 @@ class TestRouter(unittest.TestCase): Note that make_apptree above created applications/examples/routes.py with a default_function. """ load(rdict=dict()) - self.assertEqual(filter_url('http://domain.com/welcome'), '/welcome/default/index') - self.assertEqual(filter_url('http://domain.com/examples'), '/examples/default/exdef') + self.assertEqual( + filter_url('http://domain.com/welcome'), '/welcome/default/index') + self.assertEqual(filter_url( + 'http://domain.com/examples'), '/examples/default/exdef') def test_router_defapp(self): """ Test the default-application function """ routers = dict(BASE=dict(default_application='welcome')) load(rdict=routers) # app resolution - self.assertEqual(filter_url('http://domain.com/welcome', app=True), 'welcome') + self.assertEqual( + filter_url('http://domain.com/welcome', app=True), 'welcome') self.assertEqual(filter_url('http://domain.com/', app=True), 'welcome') # incoming - self.assertEqual(filter_url('http://domain.com'), '/welcome/default/index') - self.assertEqual(filter_url('http://domain.com/'), '/welcome/default/index') - self.assertEqual(filter_url('http://domain.com/appadmin'), '/welcome/appadmin/index') - self.assertEqual(filter_url('http://domain.com/abc'), '/welcome/default/abc') - self.assertEqual(filter_url('http://domain.com/index/abc'), "/welcome/default/index ['abc']") - self.assertEqual(filter_url('http://domain.com/abc/def'), "/welcome/default/abc ['def']") - self.assertEqual(filter_url('http://domain.com/favicon.ico'), '%s/applications/welcome/static/favicon.ico' % root) - self.assertEqual(filter_url('http://domain.com/static/abc'), '%s/applications/welcome/static/abc' % root) + self.assertEqual( + filter_url('http://domain.com'), '/welcome/default/index') + self.assertEqual( + filter_url('http://domain.com/'), '/welcome/default/index') + self.assertEqual(filter_url( + 'http://domain.com/appadmin'), '/welcome/appadmin/index') + self.assertEqual( + filter_url('http://domain.com/abc'), '/welcome/default/abc') + self.assertEqual(filter_url( + 'http://domain.com/index/abc'), "/welcome/default/index ['abc']") + self.assertEqual(filter_url( + 'http://domain.com/abc/def'), "/welcome/default/abc ['def']") + self.assertEqual(filter_url('http://domain.com/favicon.ico'), + '%s/applications/welcome/static/favicon.ico' % root) + self.assertEqual(filter_url('http://domain.com/static/abc'), + '%s/applications/welcome/static/abc' % root) self.assertEqual(filter_url('http://domain.com/static/path/to/static'), "%s/applications/welcome/static/path/to/static" % root) # outgoing - self.assertEqual(filter_url('http://domain.com/welcome/default/index', out=True), '/') + self.assertEqual(filter_url( + 'http://domain.com/welcome/default/index', out=True), '/') self.assertEqual(filter_url('http://domain.com/welcome/default/index/arg1', out=True), '/index/arg1') - self.assertEqual(filter_url('http://domain.com/welcome/default/abc', out=True), '/abc') - self.assertEqual(filter_url('http://domain.com/welcome/default/admin', out=True), '/default/admin') - self.assertEqual(filter_url('http://domain.com/welcome/static/abc', out=True), + self.assertEqual(filter_url( + 'http://domain.com/welcome/default/abc', out=True), '/abc') + self.assertEqual(filter_url('http://domain.com/welcome/default/admin', + out=True), '/default/admin') + self.assertEqual( + filter_url('http://domain.com/welcome/static/abc', out=True), '/welcome/static/abc') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', out=True), '/appadmin') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', out=True), '/appadmin/abc') - self.assertEqual(filter_url('http://domain.com/welcome/admin/index', out=True), '/welcome/admin') - self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', out=True), '/welcome/admin/abc') - self.assertEqual(filter_url('http://domain.com/admin/default/abc', out=True), '/admin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', + out=True), '/appadmin') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', + out=True), '/appadmin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/admin/index', + out=True), '/welcome/admin') + self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', + out=True), '/welcome/admin/abc') + self.assertEqual(filter_url( + 'http://domain.com/admin/default/abc', out=True), '/admin/abc') def test_router_nodef(self): """ Test no-default functions """ @@ -180,46 +223,71 @@ class TestRouter(unittest.TestCase): ) load(rdict=routers) # outgoing - self.assertEqual(filter_url('http://domain.com/welcome/default/index', out=True), '/default') + self.assertEqual(filter_url( + 'http://domain.com/welcome/default/index', out=True), '/default') self.assertEqual(filter_url('http://domain.com/welcome/default/index/arg1', out=True), '/default/index/arg1') - self.assertEqual(filter_url('http://domain.com/welcome/default/abc', out=True), '/default/abc') - self.assertEqual(filter_url('http://domain.com/welcome/static/abc', out=True), + self.assertEqual(filter_url('http://domain.com/welcome/default/abc', + out=True), '/default/abc') + self.assertEqual( + filter_url('http://domain.com/welcome/static/abc', out=True), '/welcome/static/abc') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', out=True), '/appadmin') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', out=True), '/appadmin/abc') - self.assertEqual(filter_url('http://domain.com/welcome/admin/index', out=True), '/welcome/admin') - self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', out=True), '/welcome/admin/abc') - self.assertEqual(filter_url('http://domain.com/admin/default/abc', out=True), '/admin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', + out=True), '/appadmin') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', + out=True), '/appadmin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/admin/index', + out=True), '/welcome/admin') + self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', + out=True), '/welcome/admin/abc') + self.assertEqual(filter_url( + 'http://domain.com/admin/default/abc', out=True), '/admin/abc') # incoming - self.assertEqual(filter_url('http://domain.com'), '/welcome/default/index') - self.assertEqual(filter_url('http://domain.com/'), '/welcome/default/index') - self.assertEqual(filter_url('http://domain.com/appadmin'), '/welcome/appadmin/index') - self.assertEqual(filter_url('http://domain.com/abc'), '/welcome/abc/index') - self.assertEqual(filter_url('http://domain.com/index/abc'), "/welcome/index/abc") - self.assertEqual(filter_url('http://domain.com/abc/def'), "/welcome/abc/def") - self.assertEqual(filter_url('http://domain.com/abc/def/ghi'), "/welcome/abc/def ['ghi']") + self.assertEqual( + filter_url('http://domain.com'), '/welcome/default/index') + self.assertEqual( + filter_url('http://domain.com/'), '/welcome/default/index') + self.assertEqual(filter_url( + 'http://domain.com/appadmin'), '/welcome/appadmin/index') + self.assertEqual( + filter_url('http://domain.com/abc'), '/welcome/abc/index') + self.assertEqual( + filter_url('http://domain.com/index/abc'), "/welcome/index/abc") + self.assertEqual( + filter_url('http://domain.com/abc/def'), "/welcome/abc/def") + self.assertEqual(filter_url( + 'http://domain.com/abc/def/ghi'), "/welcome/abc/def ['ghi']") routers = dict( BASE=dict(default_application=None), ) load(rdict=routers) # outgoing - self.assertEqual(filter_url('http://domain.com/welcome/default/index', out=True), '/welcome') + self.assertEqual(filter_url( + 'http://domain.com/welcome/default/index', out=True), '/welcome') self.assertEqual(filter_url('http://domain.com/welcome/default/index/arg1', out=True), '/welcome/index/arg1') - self.assertEqual(filter_url('http://domain.com/welcome/default/abc', out=True), '/welcome/abc') - self.assertEqual(filter_url('http://domain.com/welcome/static/abc', out=True), '/welcome/static/abc') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', out=True), '/welcome/appadmin') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', out=True), '/welcome/appadmin/abc') - self.assertEqual(filter_url('http://domain.com/welcome/admin/index', out=True), '/welcome/admin') - self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', out=True), '/welcome/admin/abc') - self.assertEqual(filter_url('http://domain.com/admin/default/abc', out=True), '/admin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/default/abc', + out=True), '/welcome/abc') + self.assertEqual(filter_url('http://domain.com/welcome/static/abc', + out=True), '/welcome/static/abc') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', + out=True), '/welcome/appadmin') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', + out=True), '/welcome/appadmin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/admin/index', + out=True), '/welcome/admin') + self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', + out=True), '/welcome/admin/abc') + self.assertEqual(filter_url( + 'http://domain.com/admin/default/abc', out=True), '/admin/abc') # incoming self.assertRaises(HTTP, filter_url, 'http://domain.com') self.assertRaises(HTTP, filter_url, 'http://domain.com/appadmin') try: # 2.7+ only - self.assertRaisesRegexp(HTTP, "400.*invalid application", filter_url, 'http://domain.com') - self.assertRaisesRegexp(HTTP, "400.*invalid application", filter_url, 'http://domain.com/appadmin') + self.assertRaisesRegexp(HTTP, "400.*invalid application", + filter_url, 'http://domain.com') + self.assertRaisesRegexp(HTTP, "400.*invalid application", + filter_url, 'http://domain.com/appadmin') except AttributeError: pass @@ -228,18 +296,28 @@ class TestRouter(unittest.TestCase): ) load(rdict=routers) # outgoing - self.assertEqual(filter_url('http://domain.com/welcome/default/index', out=True), '/welcome') + self.assertEqual(filter_url( + 'http://domain.com/welcome/default/index', out=True), '/welcome') self.assertEqual(filter_url('http://domain.com/welcome/default/index/arg1', out=True), '/welcome/index/arg1') - self.assertEqual(filter_url('http://domain.com/welcome/default/abc', out=True), '/welcome/abc') - self.assertEqual(filter_url('http://domain.com/welcome/static/abc', out=True), '/welcome/static/abc') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', out=True), '/welcome/appadmin') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', out=True), '/welcome/appadmin/abc') - self.assertEqual(filter_url('http://domain.com/welcome/admin/index', out=True), '/welcome/admin') - self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', out=True), '/welcome/admin/abc') - self.assertEqual(filter_url('http://domain.com/admin/default/abc', out=True), '/admin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/default/abc', + out=True), '/welcome/abc') + self.assertEqual(filter_url('http://domain.com/welcome/static/abc', + out=True), '/welcome/static/abc') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', + out=True), '/welcome/appadmin') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', + out=True), '/welcome/appadmin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/admin/index', + out=True), '/welcome/admin') + self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', + out=True), '/welcome/admin/abc') + self.assertEqual(filter_url( + 'http://domain.com/admin/default/abc', out=True), '/admin/abc') # incoming - self.assertEqual(filter_url('http://domain.com'), '/welcome/default/index') - self.assertEqual(filter_url('http://domain.com/'), '/welcome/default/index') + self.assertEqual( + filter_url('http://domain.com'), '/welcome/default/index') + self.assertEqual( + filter_url('http://domain.com/'), '/welcome/default/index') self.assertRaises(HTTP, filter_url, 'http://domain.com/appadmin') try: # 2.7+ only @@ -253,18 +331,28 @@ class TestRouter(unittest.TestCase): ) load(rdict=routers) # outgoing - self.assertEqual(filter_url('http://domain.com/welcome/default/index', out=True), '/welcome/default') + self.assertEqual(filter_url('http://domain.com/welcome/default/index', + out=True), '/welcome/default') self.assertEqual(filter_url('http://domain.com/welcome/default/index/arg1', out=True), '/welcome/default/index/arg1') - self.assertEqual(filter_url('http://domain.com/welcome/default/abc', out=True), '/welcome/default/abc') - self.assertEqual(filter_url('http://domain.com/welcome/static/abc', out=True), '/welcome/static/abc') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', out=True), '/welcome/appadmin') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', out=True), '/welcome/appadmin/abc') - self.assertEqual(filter_url('http://domain.com/welcome/admin/index', out=True), '/welcome/admin') - self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', out=True), '/welcome/admin/abc') - self.assertEqual(filter_url('http://domain.com/admin/default/abc', out=True), '/admin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/default/abc', + out=True), '/welcome/default/abc') + self.assertEqual(filter_url('http://domain.com/welcome/static/abc', + out=True), '/welcome/static/abc') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', + out=True), '/welcome/appadmin') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', + out=True), '/welcome/appadmin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/admin/index', + out=True), '/welcome/admin') + self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', + out=True), '/welcome/admin/abc') + self.assertEqual(filter_url( + 'http://domain.com/admin/default/abc', out=True), '/admin/abc') # incoming - self.assertEqual(filter_url('http://domain.com'), '/welcome/default/index') - self.assertEqual(filter_url('http://domain.com/'), '/welcome/default/index') + self.assertEqual( + filter_url('http://domain.com'), '/welcome/default/index') + self.assertEqual( + filter_url('http://domain.com/'), '/welcome/default/index') self.assertRaises(HTTP, filter_url, 'http://domain.com/appadmin') try: # 2.7+ only @@ -278,21 +366,30 @@ class TestRouter(unittest.TestCase): ) load(rdict=routers) # outgoing - self.assertEqual(filter_url('http://domain.com/welcome/default/index', out=True), '/welcome/default') + self.assertEqual(filter_url('http://domain.com/welcome/default/index', + out=True), '/welcome/default') self.assertEqual(filter_url('http://domain.com/welcome/default/index/arg1', out=True), '/welcome/default/index/arg1') - self.assertEqual(filter_url('http://domain.com/welcome/default/abc', out=True), '/welcome/default/abc') - self.assertEqual(filter_url('http://domain.com/welcome/static/abc', out=True), '/welcome/static/abc') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', out=True), '/welcome/appadmin') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', out=True), '/welcome/appadmin/abc') - self.assertEqual(filter_url('http://domain.com/welcome/admin/index', out=True), '/welcome/admin') - self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', out=True), '/welcome/admin/abc') - self.assertEqual(filter_url('http://domain.com/admin/default/abc', out=True), '/admin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/default/abc', + out=True), '/welcome/default/abc') + self.assertEqual(filter_url('http://domain.com/welcome/static/abc', + out=True), '/welcome/static/abc') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', + out=True), '/welcome/appadmin') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', + out=True), '/welcome/appadmin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/admin/index', + out=True), '/welcome/admin') + self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', + out=True), '/welcome/admin/abc') + self.assertEqual(filter_url( + 'http://domain.com/admin/default/abc', out=True), '/admin/abc') # incoming self.assertRaises(HTTP, filter_url, 'http://domain.com') self.assertRaises(HTTP, filter_url, 'http://domain.com/appadmin') try: # 2.7+ only - self.assertRaisesRegexp(HTTP, "400.*invalid controller", filter_url, 'http://domain.com') + self.assertRaisesRegexp(HTTP, "400.*invalid controller", + filter_url, 'http://domain.com') self.assertRaisesRegexp(HTTP, "400.*unknown application: 'appadmin'", filter_url, 'http://domain.com/appadmin') except AttributeError: pass @@ -303,21 +400,30 @@ class TestRouter(unittest.TestCase): ) load(rdict=routers) # outgoing - self.assertEqual(filter_url('http://domain.com/welcome/default/index', out=True), '/welcome/default/index') + self.assertEqual(filter_url('http://domain.com/welcome/default/index', + out=True), '/welcome/default/index') self.assertEqual(filter_url('http://domain.com/welcome/default/index/arg1', out=True), '/welcome/default/index/arg1') - self.assertEqual(filter_url('http://domain.com/welcome/default/abc', out=True), '/welcome/default/abc') - self.assertEqual(filter_url('http://domain.com/welcome/static/abc', out=True), '/welcome/static/abc') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', out=True), '/welcome/appadmin/index') - self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', out=True), '/welcome/appadmin/abc') - self.assertEqual(filter_url('http://domain.com/welcome/admin/index', out=True), '/welcome/admin/index') - self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', out=True), '/welcome/admin/abc') - self.assertEqual(filter_url('http://domain.com/admin/default/abc', out=True), '/admin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/default/abc', + out=True), '/welcome/default/abc') + self.assertEqual(filter_url('http://domain.com/welcome/static/abc', + out=True), '/welcome/static/abc') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/index', + out=True), '/welcome/appadmin/index') + self.assertEqual(filter_url('http://domain.com/welcome/appadmin/abc', + out=True), '/welcome/appadmin/abc') + self.assertEqual(filter_url('http://domain.com/welcome/admin/index', + out=True), '/welcome/admin/index') + self.assertEqual(filter_url('http://domain.com/welcome/admin/abc', + out=True), '/welcome/admin/abc') + self.assertEqual(filter_url( + 'http://domain.com/admin/default/abc', out=True), '/admin/abc') # incoming self.assertRaises(HTTP, filter_url, 'http://domain.com') self.assertRaises(HTTP, filter_url, 'http://domain.com/appadmin') try: # 2.7+ only - self.assertRaisesRegexp(HTTP, "400.*invalid function", filter_url, 'http://domain.com') + self.assertRaisesRegexp(HTTP, "400.*invalid function", + filter_url, 'http://domain.com') self.assertRaisesRegexp(HTTP, "400.*unknown application: 'appadmin'", filter_url, 'http://domain.com/appadmin') except AttributeError: pass @@ -325,39 +431,48 @@ class TestRouter(unittest.TestCase): def test_router_app(self): """ Tests the doctest router app resolution""" routers = dict( - BASE = dict( - domains = { - "domain1.com" : "app1", - "www.domain1.com" : "app1", - "domain2.com" : "app2", + BASE=dict( + domains={ + "domain1.com": "app1", + "www.domain1.com": "app1", + "domain2.com": "app2", }, ), - app1 = dict(), - app2 = dict(), - goodapp = dict(), + app1=dict(), + app2=dict(), + goodapp=dict(), ) routers['bad!app'] = dict() load(rdict=routers) - self.assertEqual(filter_url('http://domain.com/welcome', app=True), 'welcome') - self.assertEqual(filter_url('http://domain.com/welcome/', app=True), 'welcome') + self.assertEqual( + filter_url('http://domain.com/welcome', app=True), 'welcome') + self.assertEqual( + filter_url('http://domain.com/welcome/', app=True), 'welcome') self.assertEqual(filter_url('http://domain.com', app=True), 'init') self.assertEqual(filter_url('http://domain.com/', app=True), 'init') self.assertEqual(filter_url('http://domain.com/abc', app=True), 'init') - self.assertEqual(filter_url('http://domain1.com/abc', app=True), 'app1') - self.assertEqual(filter_url('http://www.domain1.com/abc', app=True), 'app1') - self.assertEqual(filter_url('http://domain2.com/abc', app=True), 'app2') - self.assertEqual(filter_url('http://domain2.com/admin', app=True), 'admin') + self.assertEqual( + filter_url('http://domain1.com/abc', app=True), 'app1') + self.assertEqual( + filter_url('http://www.domain1.com/abc', app=True), 'app1') + self.assertEqual( + filter_url('http://domain2.com/abc', app=True), 'app2') + self.assertEqual( + filter_url('http://domain2.com/admin', app=True), 'admin') routers['BASE']['exclusive_domain'] = True load(rdict=routers) - self.assertEqual(filter_url('http://domain2.com/admin', app=True), 'app2') + self.assertEqual( + filter_url('http://domain2.com/admin', app=True), 'app2') - - self.assertEqual(filter_url('http://domain.com/goodapp', app=True), 'goodapp') - self.assertRaises(HTTP, filter_url, 'http://domain.com/bad!app', app=True) + self.assertEqual( + filter_url('http://domain.com/goodapp', app=True), 'goodapp') + self.assertRaises( + HTTP, filter_url, 'http://domain.com/bad!app', app=True) try: # 2.7+ only - self.assertRaisesRegexp(HTTP, '400.*invalid application', filter_url, 'http://domain.com/bad!app') + self.assertRaisesRegexp(HTTP, '400.*invalid application', + filter_url, 'http://domain.com/bad!app') except AttributeError: pass @@ -365,7 +480,8 @@ class TestRouter(unittest.TestCase): self.assertRaises(SyntaxError, load, rdict=routers) try: # 2.7+ only - self.assertRaisesRegexp(SyntaxError, "unknown.*app3", load, rdict=routers) + self.assertRaisesRegexp( + SyntaxError, "unknown.*app3", load, rdict=routers) except AttributeError: pass @@ -374,203 +490,270 @@ class TestRouter(unittest.TestCase): Test URLs that map domains using test filesystem layout ''' routers = dict( - BASE = dict( - domains = { - "domain1.com" : "admin", - "domain2.com" : "welcome", + BASE=dict( + domains={ + "domain1.com": "admin", + "domain2.com": "welcome", }, ), ) load(rdict=routers) - self.assertEqual(filter_url('http://domain1.com'), '/admin/default/index') - self.assertEqual(filter_url('http://domain2.com'), '/welcome/default/index') - self.assertEqual(filter_url('http://domain1.com/gae'), '/admin/gae/index') - self.assertEqual(filter_url('http://domain2.com/other'), '/welcome/other/index') - self.assertEqual(filter_url('http://domain1.com/gae/f1'), '/admin/gae/f1') - self.assertEqual(filter_url('http://domain2.com/f2'), '/welcome/default/f2') - self.assertEqual(filter_url('http://domain2.com/other/f3'), '/welcome/other/f3') - + self.assertEqual( + filter_url('http://domain1.com'), '/admin/default/index') + self.assertEqual( + filter_url('http://domain2.com'), '/welcome/default/index') + self.assertEqual( + filter_url('http://domain1.com/gae'), '/admin/gae/index') + self.assertEqual( + filter_url('http://domain2.com/other'), '/welcome/other/index') + self.assertEqual( + filter_url('http://domain1.com/gae/f1'), '/admin/gae/f1') + self.assertEqual( + filter_url('http://domain2.com/f2'), '/welcome/default/f2') + self.assertEqual( + filter_url('http://domain2.com/other/f3'), '/welcome/other/f3') def test_router_domains(self): ''' Test URLs that map domains ''' routers = dict( - BASE = dict( - applications = ['app1', 'app2', 'app2A', 'app3', 'app4', 'app5', 'app6'], - domains = { + BASE=dict( + applications=['app1', 'app2', 'app2A', + 'app3', 'app4', 'app5', 'app6'], + domains={ # two domains to the same app - "domain1.com" : "app1", - "www.domain1.com" : "app1", + "domain1.com": "app1", + "www.domain1.com": "app1", # same domain, two ports, to two apps - "domain2.com" : "app2a", - "domain2.com:8080" : "app2b", + "domain2.com": "app2a", + "domain2.com:8080": "app2b", # two domains, same app, two controllers - "domain3a.com" : "app3/c3a", - "domain3b.com" : "app3/c3b", + "domain3a.com": "app3/c3a", + "domain3b.com": "app3/c3b", # two domains, same app & controller, two functions - "domain4a.com" : "app4/c4/f4a", - "domain4b.com" : "app4/c4/f4b", + "domain4a.com": "app4/c4/f4a", + "domain4b.com": "app4/c4/f4b", # http vs https - "domain6.com:80" : "app6", - "domain6.com:443" : "app6s", + "domain6.com:80": "app6", + "domain6.com:443": "app6s", }, ), - app1 = dict( default_controller = 'c1', default_function = 'f1', controllers = ['c1'], exclusive_domain=True, ), - app2a = dict( default_controller = 'c2a', default_function = 'f2a', controllers = ['c2a'], ), - app2b = dict( default_controller = 'c2b', default_function = 'f2b', controllers = ['c2b'], ), - app3 = dict( controllers = ['c3a', 'c3b'], ), - app4 = dict( default_controller = 'c4', controllers = ['c4']), - app5 = dict( default_controller = 'c5', controllers = ['c5'], domain = 'localhost' ), - app6 = dict( default_controller = 'c6', default_function = 'f6', controllers = ['c6'], ), - app6s = dict( default_controller = 'c6s', default_function = 'f6s', controllers = ['c6s'], ), + app1=dict(default_controller='c1', default_function='f1', + controllers=['c1'], exclusive_domain=True, ), + app2a=dict(default_controller='c2a', + default_function='f2a', controllers=['c2a'], ), + app2b=dict(default_controller='c2b', + default_function='f2b', controllers=['c2b'], ), + app3=dict(controllers=['c3a', 'c3b'], ), + app4=dict(default_controller='c4', controllers=['c4']), + app5=dict(default_controller='c5', + controllers=['c5'], domain='localhost'), + app6=dict(default_controller='c6', + default_function='f6', controllers=['c6'], ), + app6s=dict(default_controller='c6s', + default_function='f6s', controllers=['c6s'], ), ) load(rdict=routers) self.assertEqual(filter_url('http://domain1.com/abc'), '/app1/c1/abc') - self.assertEqual(filter_url('http://domain1.com/c1/abc'), '/app1/c1/abc') - self.assertEqual(filter_url('http://domain1.com/abc.html'), '/app1/c1/abc') - self.assertEqual(filter_url('http://domain1.com/abc.css'), '/app1/c1/abc.css') - self.assertEqual(filter_url('http://domain1.com/index/abc'), "/app1/c1/index ['abc']") + self.assertEqual( + filter_url('http://domain1.com/c1/abc'), '/app1/c1/abc') + self.assertEqual( + filter_url('http://domain1.com/abc.html'), '/app1/c1/abc') + self.assertEqual( + filter_url('http://domain1.com/abc.css'), '/app1/c1/abc.css') + self.assertEqual(filter_url( + 'http://domain1.com/index/abc'), "/app1/c1/index ['abc']") self.assertEqual(filter_url('http://domain2.com/app1'), "/app1/c1/f1") - self.assertEqual(filter_url('https://domain1.com/app1/ctr/fcn', domain=('app1',None), out=True), "/ctr/fcn") - self.assertEqual(filter_url('https://www.domain1.com/app1/ctr/fcn', domain=('app1',None), out=True), "/ctr/fcn") + self.assertEqual(filter_url('https://domain1.com/app1/ctr/fcn', + domain=('app1', None), out=True), "/ctr/fcn") + self.assertEqual(filter_url('https://www.domain1.com/app1/ctr/fcn', + domain=('app1', None), out=True), "/ctr/fcn") - self.assertEqual(filter_url('http://domain2.com/abc'), '/app2a/c2a/abc') - self.assertEqual(filter_url('http://domain2.com:8080/abc'), '/app2b/c2b/abc') + self.assertEqual( + filter_url('http://domain2.com/abc'), '/app2a/c2a/abc') + self.assertEqual( + filter_url('http://domain2.com:8080/abc'), '/app2b/c2b/abc') - self.assertEqual(filter_url('http://domain2.com/app2a/ctr/fcn', domain=('app2a',None), out=True), "/ctr/fcn") - self.assertEqual(filter_url('http://domain2.com/app2a/ctr/f2a', domain=('app2a',None), out=True), "/ctr") - self.assertEqual(filter_url('http://domain2.com/app2a/c2a/f2a', domain=('app2a',None), out=True), "/") - self.assertEqual(filter_url('http://domain2.com/app2a/c2a/fcn', domain=('app2a',None), out=True), "/fcn") - self.assertEqual(filter_url('http://domain2.com/app2a/ctr/fcn', domain=('app2b',None), out=True), "/app2a/ctr/fcn") - self.assertEqual(filter_url('http://domain2.com/app2a/ctr/f2a', domain=('app2b',None), out=True), "/app2a/ctr") - self.assertEqual(filter_url('http://domain2.com/app2a/c2a/f2a', domain=('app2b',None), out=True), "/app2a") + self.assertEqual(filter_url('http://domain2.com/app2a/ctr/fcn', + domain=('app2a', None), out=True), "/ctr/fcn") + self.assertEqual(filter_url('http://domain2.com/app2a/ctr/f2a', + domain=('app2a', None), out=True), "/ctr") + self.assertEqual(filter_url('http://domain2.com/app2a/c2a/f2a', + domain=('app2a', None), out=True), "/") + self.assertEqual(filter_url('http://domain2.com/app2a/c2a/fcn', + domain=('app2a', None), out=True), "/fcn") + self.assertEqual(filter_url('http://domain2.com/app2a/ctr/fcn', + domain=('app2b', None), out=True), "/app2a/ctr/fcn") + self.assertEqual(filter_url('http://domain2.com/app2a/ctr/f2a', + domain=('app2b', None), out=True), "/app2a/ctr") + self.assertEqual(filter_url('http://domain2.com/app2a/c2a/f2a', + domain=('app2b', None), out=True), "/app2a") self.assertEqual(filter_url('http://domain3a.com/'), '/app3/c3a/index') - self.assertEqual(filter_url('http://domain3a.com/abc'), '/app3/c3a/abc') - self.assertEqual(filter_url('http://domain3a.com/c3b'), '/app3/c3b/index') - self.assertEqual(filter_url('http://domain3b.com/abc'), '/app3/c3b/abc') + self.assertEqual( + filter_url('http://domain3a.com/abc'), '/app3/c3a/abc') + self.assertEqual( + filter_url('http://domain3a.com/c3b'), '/app3/c3b/index') + self.assertEqual( + filter_url('http://domain3b.com/abc'), '/app3/c3b/abc') - self.assertEqual(filter_url('http://domain3a.com/app3/c3a/fcn', domain=('app3','c3a'), out=True), "/fcn") - self.assertEqual(filter_url('http://domain3a.com/app3/c3a/fcn', domain=('app3','c3b'), out=True), "/c3a/fcn") - self.assertEqual(filter_url('http://domain3a.com/app3/c3a/fcn', domain=('app1',None), out=True), "/app3/c3a/fcn") + self.assertEqual(filter_url('http://domain3a.com/app3/c3a/fcn', + domain=('app3', 'c3a'), out=True), "/fcn") + self.assertEqual(filter_url('http://domain3a.com/app3/c3a/fcn', + domain=('app3', 'c3b'), out=True), "/c3a/fcn") + self.assertEqual(filter_url('http://domain3a.com/app3/c3a/fcn', + domain=('app1', None), out=True), "/app3/c3a/fcn") self.assertEqual(filter_url('http://domain4a.com/abc'), '/app4/c4/abc') - self.assertEqual(filter_url('https://domain4a.com/app4/c4/fcn', domain=('app4',None), out=True), "/fcn") + self.assertEqual(filter_url('https://domain4a.com/app4/c4/fcn', + domain=('app4', None), out=True), "/fcn") self.assertEqual(filter_url('http://domain4a.com'), '/app4/c4/f4a') self.assertEqual(filter_url('http://domain4b.com'), '/app4/c4/f4b') self.assertEqual(filter_url('http://localhost/abc'), '/app5/c5/abc') - self.assertEqual(filter_url('http:///abc'), '/app5/c5/abc') # test null host => localhost - self.assertEqual(filter_url('https://localhost/app5/c5/fcn', domain=('app5',None), out=True), "/fcn") + self.assertEqual(filter_url( + 'http:///abc'), '/app5/c5/abc') # test null host => localhost + self.assertEqual(filter_url('https://localhost/app5/c5/fcn', + domain=('app5', None), out=True), "/fcn") self.assertEqual(filter_url('http://domain6.com'), '/app6/c6/f6') self.assertEqual(filter_url('https://domain6.com'), '/app6s/c6s/f6s') - self.assertEqual(filter_url('http://domain2.com/app3/c3a/f3', domain=('app2b',None), out=True), "/app3/c3a/f3") - self.assertRaises(SyntaxError, filter_url, 'http://domain1.com/app1/c1/f1', domain=('app2b',None), out=True) + self.assertEqual(filter_url('http://domain2.com/app3/c3a/f3', + domain=('app2b', None), out=True), "/app3/c3a/f3") + self.assertRaises(SyntaxError, filter_url, 'http://domain1.com/app1/c1/f1', domain=('app2b', None), out=True) try: # 2.7+ only - self.assertRaisesRegexp(SyntaxError, 'cross-domain conflict', filter_url, 'http://domain1.com/app1/c1/f1', domain=('app2b',None), out=True) + self.assertRaisesRegexp(SyntaxError, 'cross-domain conflict', filter_url, 'http://domain1.com/app1/c1/f1', domain=('app2b', None), out=True) except AttributeError: pass - self.assertEqual(filter_url('http://domain1.com/app1/c1/f1', domain=('app2b',None), host='domain2.com', out=True), "/app1") - + self.assertEqual(filter_url('http://domain1.com/app1/c1/f1', domain=( + 'app2b', None), host='domain2.com', out=True), "/app1") def test_router_domains_ed(self): ''' Test URLs that map domains with exclusive_domain set ''' routers = dict( - BASE = dict( - applications = ['app1', 'app2', 'app2A', 'app3', 'app4', 'app5', 'app6'], - exclusive_domain = True, - domains = { + BASE=dict( + applications=['app1', 'app2', 'app2A', + 'app3', 'app4', 'app5', 'app6'], + exclusive_domain=True, + domains={ # two domains to the same app - "domain1.com" : "app1", - "www.domain1.com" : "app1", + "domain1.com": "app1", + "www.domain1.com": "app1", # same domain, two ports, to two apps - "domain2.com" : "app2a", - "domain2.com:8080" : "app2b", + "domain2.com": "app2a", + "domain2.com:8080": "app2b", # two domains, same app, two controllers - "domain3a.com" : "app3/c3a", - "domain3b.com" : "app3/c3b", + "domain3a.com": "app3/c3a", + "domain3b.com": "app3/c3b", # two domains, same app & controller, two functions - "domain4a.com" : "app4/c4/f4a", - "domain4b.com" : "app4/c4/f4b", + "domain4a.com": "app4/c4/f4a", + "domain4b.com": "app4/c4/f4b", # http vs https - "domain6.com:80" : "app6", - "domain6.com:443" : "app6s", + "domain6.com:80": "app6", + "domain6.com:443": "app6s", }, ), - app1 = dict( default_controller = 'c1', default_function = 'f1', controllers = ['c1'], exclusive_domain=True, ), - app2a = dict( default_controller = 'c2a', default_function = 'f2a', controllers = ['c2a'], ), - app2b = dict( default_controller = 'c2b', default_function = 'f2b', controllers = ['c2b'], ), - app3 = dict( controllers = ['c3a', 'c3b'], ), - app4 = dict( default_controller = 'c4', controllers = ['c4']), - app5 = dict( default_controller = 'c5', controllers = ['c5'], domain = 'localhost' ), - app6 = dict( default_controller = 'c6', default_function = 'f6', controllers = ['c6'], ), - app6s = dict( default_controller = 'c6s', default_function = 'f6s', controllers = ['c6s'], ), + app1=dict(default_controller='c1', default_function='f1', + controllers=['c1'], exclusive_domain=True, ), + app2a=dict(default_controller='c2a', + default_function='f2a', controllers=['c2a'], ), + app2b=dict(default_controller='c2b', + default_function='f2b', controllers=['c2b'], ), + app3=dict(controllers=['c3a', 'c3b'], ), + app4=dict(default_controller='c4', controllers=['c4']), + app5=dict(default_controller='c5', + controllers=['c5'], domain='localhost'), + app6=dict(default_controller='c6', + default_function='f6', controllers=['c6'], ), + app6s=dict(default_controller='c6s', + default_function='f6s', controllers=['c6s'], ), ) load(rdict=routers) self.assertEqual(filter_url('http://domain1.com/abc'), '/app1/c1/abc') - self.assertEqual(filter_url('http://domain1.com/c1/abc'), '/app1/c1/abc') - self.assertEqual(filter_url('http://domain1.com/abc.html'), '/app1/c1/abc') - self.assertEqual(filter_url('http://domain1.com/abc.css'), '/app1/c1/abc.css') - self.assertEqual(filter_url('http://domain1.com/index/abc'), "/app1/c1/index ['abc']") - self.assertEqual(filter_url('http://domain2.com/app1'), "/app2a/c2a/app1") + self.assertEqual( + filter_url('http://domain1.com/c1/abc'), '/app1/c1/abc') + self.assertEqual( + filter_url('http://domain1.com/abc.html'), '/app1/c1/abc') + self.assertEqual( + filter_url('http://domain1.com/abc.css'), '/app1/c1/abc.css') + self.assertEqual(filter_url( + 'http://domain1.com/index/abc'), "/app1/c1/index ['abc']") + self.assertEqual( + filter_url('http://domain2.com/app1'), "/app2a/c2a/app1") - self.assertEqual(filter_url('https://domain1.com/app1/ctr/fcn', domain=('app1',None), out=True), "/ctr/fcn") - self.assertEqual(filter_url('https://www.domain1.com/app1/ctr/fcn', domain=('app1',None), out=True), "/ctr/fcn") + self.assertEqual(filter_url('https://domain1.com/app1/ctr/fcn', + domain=('app1', None), out=True), "/ctr/fcn") + self.assertEqual(filter_url('https://www.domain1.com/app1/ctr/fcn', + domain=('app1', None), out=True), "/ctr/fcn") - self.assertEqual(filter_url('http://domain2.com/abc'), '/app2a/c2a/abc') - self.assertEqual(filter_url('http://domain2.com:8080/abc'), '/app2b/c2b/abc') + self.assertEqual( + filter_url('http://domain2.com/abc'), '/app2a/c2a/abc') + self.assertEqual( + filter_url('http://domain2.com:8080/abc'), '/app2b/c2b/abc') - self.assertEqual(filter_url('http://domain2.com/app2a/ctr/fcn', domain=('app2a',None), out=True), "/ctr/fcn") - self.assertEqual(filter_url('http://domain2.com/app2a/ctr/f2a', domain=('app2a',None), out=True), "/ctr") - self.assertEqual(filter_url('http://domain2.com/app2a/c2a/f2a', domain=('app2a',None), out=True), "/") - self.assertEqual(filter_url('http://domain2.com/app2a/c2a/fcn', domain=('app2a',None), out=True), "/fcn") - - self.assertRaises(SyntaxError, filter_url, 'http://domain2.com/app2a/ctr/fcn', domain=('app2b',None), out=True) - self.assertRaises(SyntaxError, filter_url, 'http://domain2.com/app2a/ctr/f2a', domain=('app2b',None), out=True) - self.assertRaises(SyntaxError, filter_url, 'http://domain2.com/app2a/c2a/f2a', domain=('app2b',None), out=True) + self.assertEqual(filter_url('http://domain2.com/app2a/ctr/fcn', + domain=('app2a', None), out=True), "/ctr/fcn") + self.assertEqual(filter_url('http://domain2.com/app2a/ctr/f2a', + domain=('app2a', None), out=True), "/ctr") + self.assertEqual(filter_url('http://domain2.com/app2a/c2a/f2a', + domain=('app2a', None), out=True), "/") + self.assertEqual(filter_url('http://domain2.com/app2a/c2a/fcn', + domain=('app2a', None), out=True), "/fcn") + + self.assertRaises(SyntaxError, filter_url, 'http://domain2.com/app2a/ctr/fcn', domain=('app2b', None), out=True) + self.assertRaises(SyntaxError, filter_url, 'http://domain2.com/app2a/ctr/f2a', domain=('app2b', None), out=True) + self.assertRaises(SyntaxError, filter_url, 'http://domain2.com/app2a/c2a/f2a', domain=('app2b', None), out=True) self.assertEqual(filter_url('http://domain3a.com/'), '/app3/c3a/index') - self.assertEqual(filter_url('http://domain3a.com/abc'), '/app3/c3a/abc') - self.assertEqual(filter_url('http://domain3a.com/c3b'), '/app3/c3b/index') - self.assertEqual(filter_url('http://domain3b.com/abc'), '/app3/c3b/abc') + self.assertEqual( + filter_url('http://domain3a.com/abc'), '/app3/c3a/abc') + self.assertEqual( + filter_url('http://domain3a.com/c3b'), '/app3/c3b/index') + self.assertEqual( + filter_url('http://domain3b.com/abc'), '/app3/c3b/abc') - self.assertEqual(filter_url('http://domain3a.com/app3/c3a/fcn', domain=('app3','c3a'), out=True), "/fcn") - self.assertEqual(filter_url('http://domain3a.com/app3/c3a/fcn', domain=('app3','c3b'), out=True), "/c3a/fcn") - - self.assertRaises(SyntaxError, filter_url, 'http://domain3a.com/app3/c3a/fcn', domain=('app1',None), out=True) + self.assertEqual(filter_url('http://domain3a.com/app3/c3a/fcn', + domain=('app3', 'c3a'), out=True), "/fcn") + self.assertEqual(filter_url('http://domain3a.com/app3/c3a/fcn', + domain=('app3', 'c3b'), out=True), "/c3a/fcn") + + self.assertRaises(SyntaxError, filter_url, 'http://domain3a.com/app3/c3a/fcn', domain=('app1', None), out=True) self.assertEqual(filter_url('http://domain4a.com/abc'), '/app4/c4/abc') - self.assertEqual(filter_url('https://domain4a.com/app4/c4/fcn', domain=('app4',None), out=True), "/fcn") + self.assertEqual(filter_url('https://domain4a.com/app4/c4/fcn', + domain=('app4', None), out=True), "/fcn") self.assertEqual(filter_url('http://domain4a.com'), '/app4/c4/f4a') self.assertEqual(filter_url('http://domain4b.com'), '/app4/c4/f4b') self.assertEqual(filter_url('http://localhost/abc'), '/app5/c5/abc') - self.assertEqual(filter_url('http:///abc'), '/app5/c5/abc') # test null host => localhost - self.assertEqual(filter_url('https://localhost/app5/c5/fcn', domain=('app5',None), out=True), "/fcn") + self.assertEqual(filter_url( + 'http:///abc'), '/app5/c5/abc') # test null host => localhost + self.assertEqual(filter_url('https://localhost/app5/c5/fcn', + domain=('app5', None), out=True), "/fcn") self.assertEqual(filter_url('http://domain6.com'), '/app6/c6/f6') self.assertEqual(filter_url('https://domain6.com'), '/app6s/c6s/f6s') - self.assertRaises(SyntaxError, filter_url, 'http://domain2.com/app3/c3a/f3', domain=('app2b',None), out=True) - self.assertRaises(SyntaxError, filter_url, 'http://domain1.com/app1/c1/f1', domain=('app2b',None), out=True) + self.assertRaises(SyntaxError, filter_url, 'http://domain2.com/app3/c3a/f3', domain=('app2b', None), out=True) + self.assertRaises(SyntaxError, filter_url, 'http://domain1.com/app1/c1/f1', domain=('app2b', None), out=True) try: # 2.7+ only - self.assertRaisesRegexp(SyntaxError, 'cross-domain conflict', filter_url, 'http://domain1.com/app1/c1/f1', domain=('app2b',None), out=True) + self.assertRaisesRegexp(SyntaxError, 'cross-domain conflict', filter_url, 'http://domain1.com/app1/c1/f1', domain=('app2b', None), out=True) except AttributeError: pass - self.assertEqual(filter_url('http://domain1.com/app1/c1/f1', domain=('app2b',None), host='domain2.com', out=True), "/app1") + self.assertEqual(filter_url('http://domain1.com/app1/c1/f1', domain=( + 'app2b', None), host='domain2.com', out=True), "/app1") def test_router_raise(self): ''' @@ -578,37 +761,49 @@ class TestRouter(unittest.TestCase): ''' # test non-exception variants router_raise = dict( - init = dict( - controllers = [], + init=dict( + controllers=[], ), - welcome = dict( - map_hyphen = False, + welcome=dict( + map_hyphen=False, ), ) load(rdict=router_raise) - self.assertEqual(filter_url('http://domain.com/ctl'), "/init/ctl/index") - self.assertEqual(filter_url('http://domain.com/default/fcn'), "/init/default/fcn") - self.assertEqual(filter_url('http://domain.com/default/fcn.ext'), "/init/default/fcn.ext") - self.assertEqual(filter_url('http://domain.com/default/fcn/arg'), "/init/default/fcn ['arg']") + self.assertEqual( + filter_url('http://domain.com/ctl'), "/init/ctl/index") + self.assertEqual( + filter_url('http://domain.com/default/fcn'), "/init/default/fcn") + self.assertEqual(filter_url( + 'http://domain.com/default/fcn.ext'), "/init/default/fcn.ext") + self.assertEqual(filter_url('http://domain.com/default/fcn/arg'), + "/init/default/fcn ['arg']") # now raise-HTTP variants self.assertRaises(HTTP, filter_url, 'http://domain.com/bad!ctl') self.assertRaises(HTTP, filter_url, 'http://domain.com/ctl/bad!fcn') - self.assertRaises(HTTP, filter_url, 'http://domain.com/ctl/fcn.bad!ext') - self.assertRaises(HTTP, filter_url, 'http://domain.com/ctl/fcn/bad!arg') + self.assertRaises( + HTTP, filter_url, 'http://domain.com/ctl/fcn.bad!ext') + self.assertRaises( + HTTP, filter_url, 'http://domain.com/ctl/fcn/bad!arg') try: # 2.7+ only self.assertRaisesRegexp(HTTP, '400.*invalid controller', filter_url, 'http://domain.com/init/bad!ctl') - self.assertRaisesRegexp(HTTP, '400.*invalid function', filter_url, 'http://domain.com/init/ctlr/bad!fcn') - self.assertRaisesRegexp(HTTP, '400.*invalid extension', filter_url, 'http://domain.com/init/ctlr/fcn.bad!ext') - self.assertRaisesRegexp(HTTP, '400.*invalid arg', filter_url, 'http://domain.com/appc/init/fcn/bad!arg') + self.assertRaisesRegexp(HTTP, '400.*invalid function', filter_url, + 'http://domain.com/init/ctlr/bad!fcn') + self.assertRaisesRegexp(HTTP, '400.*invalid extension', filter_url, + 'http://domain.com/init/ctlr/fcn.bad!ext') + self.assertRaisesRegexp(HTTP, '400.*invalid arg', filter_url, + 'http://domain.com/appc/init/fcn/bad!arg') except AttributeError: pass - self.assertEqual(filter_url('http://domain.com/welcome/default/fcn_1'), "/welcome/default/fcn_1") - self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/default/fcn-1') + self.assertEqual(filter_url('http://domain.com/welcome/default/fcn_1'), + "/welcome/default/fcn_1") + self.assertRaises( + HTTP, filter_url, 'http://domain.com/welcome/default/fcn-1') try: # 2.7+ only - self.assertRaisesRegexp(HTTP, '400.*invalid function', filter_url, 'http://domain.com/welcome/default/fcn-1') + self.assertRaisesRegexp(HTTP, '400.*invalid function', filter_url, + 'http://domain.com/welcome/default/fcn-1') except AttributeError: pass @@ -617,245 +812,344 @@ class TestRouter(unittest.TestCase): Test basic outgoing routing ''' router_out = dict( - BASE = dict(), - init = dict( controllers = ['default', 'ctr'], ), - app = dict(), + BASE=dict(), + init=dict(controllers=['default', 'ctr'], ), + app=dict(), ) load(rdict=router_out) - self.assertEqual(filter_url('https://domain.com/app/ctr/fcn', out=True), "/app/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/init/ctr/fcn', out=True), "/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/init/ctr/fcn', out=True), "/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/init/static/file', out=True), "/init/static/file") - self.assertEqual(filter_url('https://domain.com/init/static/index', out=True), "/init/static/index") - self.assertEqual(filter_url('https://domain.com/init/default/index', out=True), "/") - self.assertEqual(filter_url('https://domain.com/init/ctr/index', out=True), "/ctr") - self.assertEqual(filter_url('http://domain.com/init/default/fcn?query', out=True), "/fcn?query") + self.assertEqual(filter_url( + 'https://domain.com/app/ctr/fcn', out=True), "/app/ctr/fcn") + self.assertEqual(filter_url( + 'https://domain.com/init/ctr/fcn', out=True), "/ctr/fcn") + self.assertEqual(filter_url( + 'https://domain.com/init/ctr/fcn', out=True), "/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/init/static/file', + out=True), "/init/static/file") + self.assertEqual(filter_url('https://domain.com/init/static/index', + out=True), "/init/static/index") + self.assertEqual(filter_url( + 'https://domain.com/init/default/index', out=True), "/") + self.assertEqual( + filter_url('https://domain.com/init/ctr/index', out=True), "/ctr") + self.assertEqual(filter_url('http://domain.com/init/default/fcn?query', + out=True), "/fcn?query") self.assertEqual(filter_url('http://domain.com/init/default/fcn#anchor', out=True), "/fcn#anchor") - self.assertEqual(filter_url('http://domain.com/init/default/fcn?query#anchor', out=True), + self.assertEqual( + filter_url( + 'http://domain.com/init/default/fcn?query#anchor', out=True), "/fcn?query#anchor") router_out['BASE']['map_static'] = True load(rdict=router_out) - self.assertEqual(filter_url('https://domain.com/init/static/file', out=True), "/static/file") - self.assertEqual(filter_url('https://domain.com/init/static/index', out=True), "/static/index") + self.assertEqual(filter_url( + 'https://domain.com/init/static/file', out=True), "/static/file") + self.assertEqual(filter_url('https://domain.com/init/static/index', + out=True), "/static/index") router_out['init']['map_static'] = None load(rdict=router_out) - self.assertEqual(filter_url('https://domain.com/init/static/file', out=True), "/init/static/file") - self.assertEqual(filter_url('https://domain.com/init/static/index', out=True), "/init/static/index") + self.assertEqual(filter_url('https://domain.com/init/static/file', + out=True), "/init/static/file") + self.assertEqual(filter_url('https://domain.com/init/static/index', + out=True), "/init/static/index") def test_router_functions(self): ''' Test function-omission with functions=[something] ''' router_functions = dict( - BASE = dict( - applications = ['init', 'app', 'app2'], - default_application = 'app', + BASE=dict( + applications=['init', 'app', 'app2'], + default_application='app', ), - init = dict( - controllers = ['default'], + init=dict( + controllers=['default'], ), - app = dict( - controllers = ['default', 'ctr'], - functions = dict( + app=dict( + controllers=['default', 'ctr'], + functions=dict( default=['index', 'user', 'help'], ctr=['ctrf1', 'ctrf2', 'ctrf3'], ), - default_function = dict( + default_function=dict( default='index', ctr='ctrf1', ), ), - app2 = dict( - controllers = ['default', 'ctr'], - functions = ['index', 'user', 'help'], + app2=dict( + controllers=['default', 'ctr'], + functions=['index', 'user', 'help'], ), ) load(rdict=router_functions) # outbound - self.assertEqual(str(URL(a='init', c='default', f='f', args=['arg1'])), "/init/f/arg1") - self.assertEqual(str(URL(a='init', c='default', f='index', args=['arg1'])), "/init/index/arg1") + self.assertEqual(str( + URL(a='init', c='default', f='f', args=['arg1'])), "/init/f/arg1") + self.assertEqual(str(URL(a='init', c='default', f='index', + args=['arg1'])), "/init/index/arg1") - self.assertEqual(str(URL(a='app', c='default', f='index', args=['arg1'])), "/arg1") - self.assertEqual(str(URL(a='app', c='default', f='user', args=['arg1'])), "/user/arg1") - self.assertEqual(str(URL(a='app', c='default', f='user', args=['index'])), "/user/index") - self.assertEqual(str(URL(a='app', c='default', f='index', args=['index'])), "/index/index") - self.assertEqual(str(URL(a='app', c='default', f='index', args=['init'])), "/index/init") - self.assertEqual(str(URL(a='app', c='default', f='index', args=['ctr'])), "/index/ctr") - self.assertEqual(str(URL(a='app', c='ctr', f='index', args=['arg'])), "/ctr/index/arg") - self.assertEqual(str(URL(a='app', c='ctr', f='ctrf1', args=['arg'])), "/ctr/arg") - self.assertEqual(str(URL(a='app', c='ctr', f='ctrf1', args=['ctrf2'])), "/ctr/ctrf1/ctrf2") + self.assertEqual( + str(URL(a='app', c='default', f='index', args=['arg1'])), "/arg1") + self.assertEqual(str( + URL(a='app', c='default', f='user', args=['arg1'])), "/user/arg1") + self.assertEqual(str(URL( + a='app', c='default', f='user', args=['index'])), "/user/index") + self.assertEqual(str(URL( + a='app', c='default', f='index', args=['index'])), "/index/index") + self.assertEqual(str(URL( + a='app', c='default', f='index', args=['init'])), "/index/init") + self.assertEqual(str( + URL(a='app', c='default', f='index', args=['ctr'])), "/index/ctr") + self.assertEqual(str( + URL(a='app', c='ctr', f='index', args=['arg'])), "/ctr/index/arg") + self.assertEqual( + str(URL(a='app', c='ctr', f='ctrf1', args=['arg'])), "/ctr/arg") + self.assertEqual(str(URL( + a='app', c='ctr', f='ctrf1', args=['ctrf2'])), "/ctr/ctrf1/ctrf2") - self.assertEqual(str(URL(a='app2', c='default', f='index', args=['arg1'])), "/app2/arg1") - self.assertEqual(str(URL(a='app2', c='default', f='user', args=['arg1'])), "/app2/user/arg1") - self.assertEqual(str(URL(a='app2', c='default', f='user', args=['index'])), "/app2/user/index") - self.assertEqual(str(URL(a='app2', c='default', f='index', args=['index'])), "/app2/index/index") - self.assertEqual(str(URL(a='app2', c='default', f='index', args=['init'])), "/app2/index/init") - self.assertEqual(str(URL(a='app2', c='default', f='index', args=['ctr'])), "/app2/index/ctr") + self.assertEqual(str(URL( + a='app2', c='default', f='index', args=['arg1'])), "/app2/arg1") + self.assertEqual(str(URL(a='app2', c='default', f='user', + args=['arg1'])), "/app2/user/arg1") + self.assertEqual(str(URL(a='app2', c='default', f='user', + args=['index'])), "/app2/user/index") + self.assertEqual(str(URL(a='app2', c='default', f='index', + args=['index'])), "/app2/index/index") + self.assertEqual(str(URL(a='app2', c='default', f='index', + args=['init'])), "/app2/index/init") + self.assertEqual(str(URL(a='app2', c='default', f='index', + args=['ctr'])), "/app2/index/ctr") # inbound - self.assertEqual(filter_url('http://d.com/arg'), "/app/default/index ['arg']") + self.assertEqual( + filter_url('http://d.com/arg'), "/app/default/index ['arg']") self.assertEqual(filter_url('http://d.com/user'), "/app/default/user") - self.assertEqual(filter_url('http://d.com/user/arg'), "/app/default/user ['arg']") + self.assertEqual( + filter_url('http://d.com/user/arg'), "/app/default/user ['arg']") self.assertEqual(filter_url('http://d.com/ctr'), "/app/ctr/ctrf1") - self.assertEqual(filter_url('http://d.com/ctr/arg'), "/app/ctr/ctrf1 ['arg']") + self.assertEqual( + filter_url('http://d.com/ctr/arg'), "/app/ctr/ctrf1 ['arg']") - self.assertEqual(filter_url('http://d.com/app2/arg'), "/app2/default/index ['arg']") - self.assertEqual(filter_url('http://d.com/app2/user'), "/app2/default/user") - self.assertEqual(filter_url('http://d.com/app2/user/arg'), "/app2/default/user ['arg']") - self.assertEqual(filter_url('http://d.com/app2/ctr'), "/app2/ctr/index") - self.assertEqual(filter_url('http://d.com/app2/ctr/index/arg'), "/app2/ctr/index ['arg']") - self.assertEqual(filter_url('http://d.com/app2/ctr/arg'), "/app2/ctr/arg") + self.assertEqual(filter_url( + 'http://d.com/app2/arg'), "/app2/default/index ['arg']") + self.assertEqual( + filter_url('http://d.com/app2/user'), "/app2/default/user") + self.assertEqual(filter_url( + 'http://d.com/app2/user/arg'), "/app2/default/user ['arg']") + self.assertEqual( + filter_url('http://d.com/app2/ctr'), "/app2/ctr/index") + self.assertEqual(filter_url( + 'http://d.com/app2/ctr/index/arg'), "/app2/ctr/index ['arg']") + self.assertEqual( + filter_url('http://d.com/app2/ctr/arg'), "/app2/ctr/arg") def test_router_functions2(self): ''' Test more functions=[something] ''' router_functions = dict( - BASE = dict( - default_application = 'init', - applications = 'INIT', - ), - init = dict( - #default_controller = 'default', - controllers = ['default', 'ctr'], - #default_function = 'index', - functions = ['index','user','register','basicRegister', - 'download','call','data','error'] - ), + BASE=dict( + default_application='init', + applications='INIT', + ), + init=dict( + #default_controller = 'default', + controllers=['default', 'ctr'], + #default_function = 'index', + functions=['index', 'user', 'register', 'basicRegister', + 'download', 'call', 'data', 'error'] + ), ) load(rdict=router_functions) # outbound - self.assertEqual(str(URL(a='init', c='default', f='index', args=['arg1'])), "/arg1") - self.assertEqual(str(URL(a='init', c='default', f='user', args=['arg1'])), "/user/arg1") - self.assertEqual(str(URL(a='init', c='default', f='user', args=['index'])), "/user/index") - self.assertEqual(str(URL(a='init', c='default', f='index', args=['index'])), "/index/index") - self.assertEqual(str(URL(a='init', c='default', f='index', args=['init'])), "/init") - self.assertEqual(str(URL(a='init', c='default', f='index', args=['ctr'])), "/index/ctr") - self.assertEqual(str(URL(a='init', c='ctr', f='index', args=['arg'])), "/ctr/index/arg") - self.assertEqual(str(URL(a='init', c='ctr', f='ctrf1', args=['arg'])), "/ctr/ctrf1/arg") - self.assertEqual(str(URL(a='init', c='ctr', f='ctrf1', args=['ctrf2'])), "/ctr/ctrf1/ctrf2") - self.assertEqual(str(URL(a='init', c='default', f='register')), "/register") + self.assertEqual(str( + URL(a='init', c='default', f='index', args=['arg1'])), "/arg1") + self.assertEqual(str(URL( + a='init', c='default', f='user', args=['arg1'])), "/user/arg1") + self.assertEqual(str(URL( + a='init', c='default', f='user', args=['index'])), "/user/index") + self.assertEqual(str(URL(a='init', c='default', f='index', + args=['index'])), "/index/index") + self.assertEqual(str( + URL(a='init', c='default', f='index', args=['init'])), "/init") + self.assertEqual(str(URL( + a='init', c='default', f='index', args=['ctr'])), "/index/ctr") + self.assertEqual(str(URL( + a='init', c='ctr', f='index', args=['arg'])), "/ctr/index/arg") + self.assertEqual(str(URL( + a='init', c='ctr', f='ctrf1', args=['arg'])), "/ctr/ctrf1/arg") + self.assertEqual(str(URL(a='init', c='ctr', f='ctrf1', + args=['ctrf2'])), "/ctr/ctrf1/ctrf2") + self.assertEqual( + str(URL(a='init', c='default', f='register')), "/register") # inbound - self.assertEqual(filter_url('http://d.com/arg'), "/init/default/index ['arg']") + self.assertEqual( + filter_url('http://d.com/arg'), "/init/default/index ['arg']") self.assertEqual(filter_url('http://d.com/user'), "/init/default/user") - self.assertEqual(filter_url('http://d.com/user/arg'), "/init/default/user ['arg']") + self.assertEqual( + filter_url('http://d.com/user/arg'), "/init/default/user ['arg']") self.assertEqual(filter_url('http://d.com/ctr'), "/init/ctr/index") - self.assertEqual(filter_url('http://d.com/ctr/ctrf1/arg'), "/init/ctr/ctrf1 ['arg']") + self.assertEqual(filter_url( + 'http://d.com/ctr/ctrf1/arg'), "/init/ctr/ctrf1 ['arg']") def test_router_hyphen(self): ''' Test hyphen conversion ''' router_hyphen = dict( - BASE = dict( - applications = ['init', 'app1', 'app2'], + BASE=dict( + applications=['init', 'app1', 'app2'], ), - init = dict( - controllers = ['default'], + init=dict( + controllers=['default'], ), - app1 = dict( - controllers = ['default'], - map_hyphen = True, + app1=dict( + controllers=['default'], + map_hyphen=True, ), - app2 = dict( - controllers = ['default'], - map_hyphen = False, + app2=dict( + controllers=['default'], + map_hyphen=False, ), ) load(rdict=router_hyphen) - self.assertEqual(filter_url('http://domain.com/init/default/fcn_1', out=True), "/fcn_1") - self.assertEqual(filter_url('http://domain.com/static/filename-with_underscore'), + self.assertEqual(filter_url( + 'http://domain.com/init/default/fcn_1', out=True), "/fcn_1") + self.assertEqual( + filter_url('http://domain.com/static/filename-with_underscore'), "%s/applications/init/static/filename-with_underscore" % root) - self.assertEqual(filter_url('http://domain.com/init/static/filename-with_underscore', out=True), + self.assertEqual( + filter_url('http://domain.com/init/static/filename-with_underscore', out=True), "/init/static/filename-with_underscore") self.assertEqual(filter_url('http://domain.com/app2/fcn_1'), - "/app2/default/fcn_1") - self.assertEqual(filter_url('http://domain.com/app2/ctr/fcn_1', domain=('app2',None), out=True), + "/app2/default/fcn_1") + self.assertEqual( + filter_url('http://domain.com/app2/ctr/fcn_1', + domain=('app2', None), out=True), "/ctr/fcn_1") - self.assertEqual(filter_url('http://domain.com/app2/static/filename-with_underscore', domain=('app2',None), out=True), + self.assertEqual( + filter_url('http://domain.com/app2/static/filename-with_underscore', domain=('app2', None), out=True), "/app2/static/filename-with_underscore") - self.assertEqual(filter_url('http://domain.com/app2/static/filename-with_underscore'), + self.assertEqual( + filter_url( + 'http://domain.com/app2/static/filename-with_underscore'), "%s/applications/app2/static/filename-with_underscore" % root) self.assertEqual(str(URL(a='init', c='default', f='a_b')), "/a_b") self.assertEqual(str(URL(a='app1', c='default', f='a_b')), "/app1/a-b") self.assertEqual(str(URL(a='app2', c='default', f='a_b')), "/app2/a_b") - self.assertEqual(str(URL(a='app1', c='static', f='a/b_c')), "/app1/static/a/b_c") - self.assertEqual(str(URL(a='app1', c='static/a', f='b_c')), "/app1/static/a/b_c") - self.assertEqual(str(URL(a='app2', c='static', f='a/b_c')), "/app2/static/a/b_c") - self.assertEqual(str(URL(a='app2', c='static/a', f='b_c')), "/app2/static/a/b_c") - + self.assertEqual( + str(URL(a='app1', c='static', f='a/b_c')), "/app1/static/a/b_c") + self.assertEqual( + str(URL(a='app1', c='static/a', f='b_c')), "/app1/static/a/b_c") + self.assertEqual( + str(URL(a='app2', c='static', f='a/b_c')), "/app2/static/a/b_c") + self.assertEqual( + str(URL(a='app2', c='static/a', f='b_c')), "/app2/static/a/b_c") def test_router_lang(self): ''' Test language specifications ''' router_lang = dict( - BASE = dict(default_application = 'admin'), - welcome = dict(), - admin = dict( - controllers = ['default', 'ctr'], - languages = ['en', 'it', 'it-it'], default_language = 'en', + BASE=dict(default_application='admin'), + welcome=dict(), + admin=dict( + controllers=['default', 'ctr'], + languages=['en', 'it', 'it-it'], default_language='en', ), - examples = dict( - languages = ['en', 'it', 'it-it'], default_language = 'en', + examples=dict( + languages=['en', 'it', 'it-it'], default_language='en', ), ) load(rdict=router_lang) - self.assertEqual(filter_url('http://domain.com/index/abc'), "/admin/default/index ['abc'] (en)") - self.assertEqual(filter_url('http://domain.com/en/abc/def'), "/admin/default/abc ['def'] (en)") - self.assertEqual(filter_url('http://domain.com/it/abc/def'), "/admin/default/abc ['def'] (it)") - self.assertEqual(filter_url('http://domain.com/it-it/abc/def'), "/admin/default/abc ['def'] (it-it)") - self.assertEqual(filter_url('http://domain.com/index/a%20bc'), "/admin/default/index ['a bc'] (en)") - self.assertEqual(filter_url('http://domain.com/static/file'), "%s/applications/admin/static/file" % root) - self.assertEqual(filter_url('http://domain.com/en/static/file'), "%s/applications/admin/static/file" % root) + self.assertEqual(filter_url('http://domain.com/index/abc'), + "/admin/default/index ['abc'] (en)") + self.assertEqual(filter_url('http://domain.com/en/abc/def'), + "/admin/default/abc ['def'] (en)") + self.assertEqual(filter_url('http://domain.com/it/abc/def'), + "/admin/default/abc ['def'] (it)") + self.assertEqual(filter_url('http://domain.com/it-it/abc/def'), + "/admin/default/abc ['def'] (it-it)") + self.assertEqual(filter_url('http://domain.com/index/a%20bc'), + "/admin/default/index ['a bc'] (en)") + self.assertEqual(filter_url('http://domain.com/static/file'), + "%s/applications/admin/static/file" % root) + self.assertEqual(filter_url('http://domain.com/en/static/file'), + "%s/applications/admin/static/file" % root) self.assertEqual(filter_url('http://domain.com/examples/en/static/file'), "%s/applications/examples/static/en/file" % root) - self.assertEqual(filter_url('http://domain.com/examples/static/file'), "%s/applications/examples/static/en/file" % root) + self.assertEqual(filter_url('http://domain.com/examples/static/file'), + "%s/applications/examples/static/en/file" % root) self.assertEqual(filter_url('http://domain.com/examples/it/static/file'), "%s/applications/examples/static/it/file" % root) self.assertEqual(filter_url('http://domain.com/examples/it-it/static/file'), "%s/applications/examples/static/file" % root) - self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', lang='en', out=True), "/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', lang='it', out=True), "/it/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', lang='it-it', out=True), "/it-it/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/admin/static/file', lang='en', out=True), "/admin/en/static/file") - self.assertEqual(filter_url('https://domain.com/admin/static/file', lang='it', out=True), "/admin/it/static/file") - self.assertEqual(filter_url('https://domain.com/admin/static/file', lang='it-it', out=True), "/admin/it-it/static/file") - self.assertEqual(filter_url('https://domain.com/welcome/ctr/fcn', lang='it', out=True), "/welcome/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/welcome/ctr/fcn', lang='es', out=True), "/welcome/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', + lang='en', out=True), "/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', + lang='it', out=True), "/it/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', + lang='it-it', out=True), "/it-it/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/admin/static/file', + lang='en', out=True), "/admin/en/static/file") + self.assertEqual(filter_url('https://domain.com/admin/static/file', + lang='it', out=True), "/admin/it/static/file") + self.assertEqual(filter_url('https://domain.com/admin/static/file', + lang='it-it', out=True), "/admin/it-it/static/file") + self.assertEqual(filter_url('https://domain.com/welcome/ctr/fcn', + lang='it', out=True), "/welcome/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/welcome/ctr/fcn', + lang='es', out=True), "/welcome/ctr/fcn") router_lang['admin']['map_static'] = True load(rdict=router_lang) - self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', lang='en', out=True), "/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', lang='it', out=True), "/it/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', lang='it-it', out=True), "/it-it/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/admin/static/file', lang='en', out=True), "/static/file") - self.assertEqual(filter_url('https://domain.com/admin/static/file', lang='it', out=True), "/it/static/file") - self.assertEqual(filter_url('https://domain.com/admin/static/file', lang='it-it', out=True), "/it-it/static/file") - self.assertEqual(filter_url('https://domain.com/welcome/ctr/fcn', lang='it', out=True), "/welcome/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/welcome/ctr/fcn', lang='es', out=True), "/welcome/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', + lang='en', out=True), "/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', + lang='it', out=True), "/it/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', + lang='it-it', out=True), "/it-it/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/admin/static/file', + lang='en', out=True), "/static/file") + self.assertEqual(filter_url('https://domain.com/admin/static/file', + lang='it', out=True), "/it/static/file") + self.assertEqual(filter_url('https://domain.com/admin/static/file', + lang='it-it', out=True), "/it-it/static/file") + self.assertEqual(filter_url('https://domain.com/welcome/ctr/fcn', + lang='it', out=True), "/welcome/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/welcome/ctr/fcn', + lang='es', out=True), "/welcome/ctr/fcn") router_lang['admin']['map_static'] = False router_lang['examples']['map_static'] = False load(rdict=router_lang) - self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', lang='en', out=True), "/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', lang='it', out=True), "/it/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', lang='it-it', out=True), "/it-it/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/admin/static/file', lang='en', out=True), "/admin/static/en/file") - self.assertEqual(filter_url('https://domain.com/admin/static/file', lang='it', out=True), "/admin/static/it/file") - self.assertEqual(filter_url('https://domain.com/admin/static/file', lang='it-it', out=True), "/admin/static/it-it/file") - self.assertEqual(filter_url('https://domain.com/welcome/ctr/fcn', lang='it', out=True), "/welcome/ctr/fcn") - self.assertEqual(filter_url('https://domain.com/welcome/ctr/fcn', lang='es', out=True), "/welcome/ctr/fcn") - self.assertEqual(filter_url('http://domain.com/static/file'), "%s/applications/admin/static/file" % root) - self.assertEqual(filter_url('http://domain.com/en/static/file'), "%s/applications/admin/static/file" % root) + self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', + lang='en', out=True), "/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', + lang='it', out=True), "/it/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn', + lang='it-it', out=True), "/it-it/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/admin/static/file', + lang='en', out=True), "/admin/static/en/file") + self.assertEqual(filter_url('https://domain.com/admin/static/file', + lang='it', out=True), "/admin/static/it/file") + self.assertEqual(filter_url('https://domain.com/admin/static/file', + lang='it-it', out=True), "/admin/static/it-it/file") + self.assertEqual(filter_url('https://domain.com/welcome/ctr/fcn', + lang='it', out=True), "/welcome/ctr/fcn") + self.assertEqual(filter_url('https://domain.com/welcome/ctr/fcn', + lang='es', out=True), "/welcome/ctr/fcn") + self.assertEqual(filter_url('http://domain.com/static/file'), + "%s/applications/admin/static/file" % root) + self.assertEqual(filter_url('http://domain.com/en/static/file'), + "%s/applications/admin/static/file" % root) self.assertEqual(filter_url('http://domain.com/examples/en/static/file'), "%s/applications/examples/static/en/file" % root) - self.assertEqual(filter_url('http://domain.com/examples/static/file'), "%s/applications/examples/static/en/file" % root) + self.assertEqual(filter_url('http://domain.com/examples/static/file'), + "%s/applications/examples/static/en/file" % root) self.assertEqual(filter_url('http://domain.com/examples/it/static/file'), "%s/applications/examples/static/it/file" % root) self.assertEqual(filter_url('http://domain.com/examples/it-it/static/file'), "%s/applications/examples/static/file" % root) @@ -868,40 +1162,46 @@ class TestRouter(unittest.TestCase): Test get_effective_router ''' router_get_effective = dict( - BASE = dict( - default_application = 'a1', - applications = ['a1', 'a2'], + BASE=dict( + default_application='a1', + applications=['a1', 'a2'], ), - a1 = dict( - controllers = ['c1a', 'c1b', 'default'], + a1=dict( + controllers=['c1a', 'c1b', 'default'], ), - a2 = dict( - default_controller = 'c2', - controllers = [], + a2=dict( + default_controller='c2', + controllers=[], ), - a3 = dict( - default_controller = 'c2', - controllers = ['c1'], + a3=dict( + default_controller='c2', + controllers=['c1'], ), - a4 = dict( - default_function = 'f1', - functions = ['f2'], + a4=dict( + default_function='f1', + functions=['f2'], ), ) load(rdict=router_get_effective) - self.assertEqual(get_effective_router('BASE').applications, set(['a1','a2'])) - self.assertEqual(get_effective_router('BASE').default_application, 'a1') + self.assertEqual( + get_effective_router('BASE').applications, set(['a1', 'a2'])) + self.assertEqual( + get_effective_router('BASE').default_application, 'a1') self.assertEqual(get_effective_router('BASE').domains, {}) self.assertEqual(get_effective_router('a1').applications, None) self.assertEqual(get_effective_router('a1').default_application, None) self.assertEqual(get_effective_router('a1').domains, None) - self.assertEqual(get_effective_router('a1').default_controller, "default") + self.assertEqual( + get_effective_router('a1').default_controller, "default") self.assertEqual(get_effective_router('a2').default_application, None) self.assertEqual(get_effective_router('a2').default_controller, "c2") - self.assertEqual(get_effective_router('a1').controllers, set(['c1a', 'c1b', 'default', 'static'])) + self.assertEqual(get_effective_router( + 'a1').controllers, set(['c1a', 'c1b', 'default', 'static'])) self.assertEqual(get_effective_router('a2').controllers, set()) - self.assertEqual(get_effective_router('a3').controllers, set(['c1', 'c2', 'static'])) - self.assertEqual(get_effective_router('a4').functions, dict(default=set(['f1', 'f2']))) + self.assertEqual(get_effective_router( + 'a3').controllers, set(['c1', 'c2', 'static'])) + self.assertEqual(get_effective_router( + 'a4').functions, dict(default=set(['f1', 'f2']))) self.assertEqual(get_effective_router('xx'), None) def test_router_error(self): @@ -932,16 +1232,16 @@ class TestRouter(unittest.TestCase): self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/#static') router_static = dict( - BASE = dict( - file_match = r'([-+=@$%#\w]+[./]?)+$', # legal static path + BASE=dict( + file_match=r'([-+=@$%#\w]+[./]?)+$', # legal static path ), ) load(rdict=router_static) self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/#static'), "%s/applications/welcome/static/path/to/#static" % root) router_static = dict( - BASE = dict( - file_match = r'[-+=@$%#.\w]+$', # legal static path element + BASE=dict( + file_match=r'[-+=@$%#.\w]+$', # legal static path element ), ) load(rdict=router_static) @@ -963,57 +1263,81 @@ class TestRouter(unittest.TestCase): ''' load(rdict=dict()) self.assertEqual(filter_url('http://domain.com/init/default/f/arg1'), - "/init/default/f ['arg1']") + "/init/default/f ['arg1']") self.assertEqual(filter_url('http://domain.com/init/default/f/arg1/'), - "/init/default/f ['arg1']") + "/init/default/f ['arg1']") self.assertEqual(filter_url('http://domain.com/init/default/f/arg1//'), - "/init/default/f ['arg1', '']") + "/init/default/f ['arg1', '']") self.assertEqual(filter_url('http://domain.com/init/default/f//arg1'), - "/init/default/f ['', 'arg1']") - self.assertEqual(filter_url('http://domain.com/init/default/f/arg1/arg2'), + "/init/default/f ['', 'arg1']") + self.assertEqual( + filter_url('http://domain.com/init/default/f/arg1/arg2'), "/init/default/f ['arg1', 'arg2']") - self.assertEqual(filter_url('http://domain.com/init/default/f/arg1//arg2'), + self.assertEqual( + filter_url('http://domain.com/init/default/f/arg1//arg2'), "/init/default/f ['arg1', '', 'arg2']") - self.assertEqual(filter_url('http://domain.com/init/default/f/arg1//arg3/'), + self.assertEqual( + filter_url('http://domain.com/init/default/f/arg1//arg3/'), "/init/default/f ['arg1', '', 'arg3']") - self.assertEqual(filter_url('http://domain.com/init/default/f/arg1//arg3//'), + self.assertEqual( + filter_url('http://domain.com/init/default/f/arg1//arg3//'), "/init/default/f ['arg1', '', 'arg3', '']") - self.assertEqual(filter_url('http://domain.com/init/default/f', out=True), "/f") - self.assertEqual(map_url_out(None, None, 'init', 'default', 'f', None, None, None, None, None), "/f") - self.assertEqual(map_url_out(None, None, 'init', 'default', 'f', [], None, None, None, None), "/f") - self.assertEqual(map_url_out(None, None, 'init', 'default', 'f', ['arg1'], None, None, None, None), "/f") - self.assertEqual(map_url_out(None, None, 'init', 'default', 'f', ['arg1', ''], None, None, None, None), "/f") - self.assertEqual(str(URL(a='init', c='default', f='f', args=None)), "/f") - self.assertEqual(str(URL(a='init', c='default', f='f', args=['arg1'])), "/f/arg1") - self.assertEqual(str(URL(a='init', c='default', f='f', args=['arg1', ''])), "/f/arg1//") - self.assertEqual(str(URL(a='init', c='default', f='f', args=['arg1', '', 'arg3'])), "/f/arg1//arg3") - self.assertEqual(str(URL(a='init', c='default', f='f', args=['ar g'])), "/f/ar%20g") - self.assertEqual(str(URL(a='init', c='default', f='f', args=['årg'])), "/f/%C3%A5rg") - self.assertEqual(str(URL(a='init', c='default', f='fünc')), "/f\xc3\xbcnc") + self.assertEqual( + filter_url('http://domain.com/init/default/f', out=True), "/f") + self.assertEqual(map_url_out(None, None, 'init', 'default', + 'f', None, None, None, None, None), "/f") + self.assertEqual(map_url_out(None, None, 'init', 'default', + 'f', [], None, None, None, None), "/f") + self.assertEqual(map_url_out(None, None, 'init', 'default', + 'f', ['arg1'], None, None, None, None), "/f") + self.assertEqual(map_url_out(None, None, 'init', 'default', + 'f', ['arg1', ''], None, None, None, None), "/f") + self.assertEqual( + str(URL(a='init', c='default', f='f', args=None)), "/f") + self.assertEqual( + str(URL(a='init', c='default', f='f', args=['arg1'])), "/f/arg1") + self.assertEqual(str(URL( + a='init', c='default', f='f', args=['arg1', ''])), "/f/arg1//") + self.assertEqual(str(URL(a='init', c='default', f='f', + args=['arg1', '', 'arg3'])), "/f/arg1//arg3") + self.assertEqual(str( + URL(a='init', c='default', f='f', args=['ar g'])), "/f/ar%20g") + self.assertEqual(str( + URL(a='init', c='default', f='f', args=['årg'])), "/f/%C3%A5rg") + self.assertEqual( + str(URL(a='init', c='default', f='fünc')), "/f\xc3\xbcnc") def test_routes_anchor(self): ''' Test URL with anchor ''' - self.assertEqual(str(URL(a='a', c='c', f='f', anchor='anchor')), "/a/c/f#anchor") + self.assertEqual( + str(URL(a='a', c='c', f='f', anchor='anchor')), "/a/c/f#anchor") load(rdict=dict()) - self.assertEqual(str(URL(a='a', c='c', f='f', anchor='anchor')), "/a/c/f#anchor") + self.assertEqual( + str(URL(a='a', c='c', f='f', anchor='anchor')), "/a/c/f#anchor") args = ['a1', 'a2'] - self.assertEqual(str(URL(a='a', c='c', f='f', args=args, anchor='anchor')), + self.assertEqual( + str(URL(a='a', c='c', f='f', args=args, anchor='anchor')), "/a/c/f/a1/a2#anchor") vars = dict(v1=1, v2=2) - self.assertEqual(str(URL(a='a', c='c', f='f', vars=vars, anchor='anchor')), + self.assertEqual( + str(URL(a='a', c='c', f='f', vars=vars, anchor='anchor')), "/a/c/f?v1=1&v2=2#anchor") - self.assertEqual(str(URL(a='a', c='c', f='f', args=args, vars=vars, anchor='anchor')), + self.assertEqual( + str(URL( + a='a', c='c', f='f', args=args, vars=vars, anchor='anchor')), "/a/c/f/a1/a2?v1=1&v2=2#anchor") self.assertEqual(str(URL(a='init', c='default', f='index')), - "/") + "/") self.assertEqual(str(URL(a='init', c='default', f='f')), - "/f") - self.assertEqual(str(URL(a='init', c='default', f='index', anchor='anchor')), + "/f") + self.assertEqual( + str(URL(a='init', c='default', f='index', anchor='anchor')), "/#anchor") - self.assertEqual(str(URL(a='init', c='default', f='f', anchor='anchor')), + self.assertEqual( + str(URL(a='init', c='default', f='f', anchor='anchor')), "/f#anchor") def test_router_prefix(self): @@ -1021,29 +1345,32 @@ class TestRouter(unittest.TestCase): Test path_prefix ''' router_path_prefix = dict( - BASE = dict( - default_application = 'a1', - applications = ['a1', 'a2'], - path_prefix = '/path/to/apps', + BASE=dict( + default_application='a1', + applications=['a1', 'a2'], + path_prefix='/path/to/apps', ), - a1 = dict( - controllers = ['c1a', 'c1b', 'default'], + a1=dict( + controllers=['c1a', 'c1b', 'default'], ), - a2 = dict( - default_controller = 'c2', - controllers = [], + a2=dict( + default_controller='c2', + controllers=[], ), ) load(rdict=router_path_prefix) self.assertEqual(str(URL(a='a1', c='c1a', f='f')), - "/path/to/apps/c1a/f") + "/path/to/apps/c1a/f") self.assertEqual(str(URL(a='a2', c='c', f='f')), - "/path/to/apps/a2/c/f") + "/path/to/apps/a2/c/f") self.assertEqual(str(URL(a='a2', c='c2', f='f')), - "/path/to/apps/a2/c2/f") - self.assertEqual(filter_url('http://domain.com/a1/'), "/a1/default/index") - self.assertEqual(filter_url('http://domain.com/path/to/apps/a1/'), "/a1/default/index") - self.assertEqual(filter_url('http://domain.com/path/to/a1/'), "/a1/default/path ['to', 'a1']") + "/path/to/apps/a2/c2/f") + self.assertEqual( + filter_url('http://domain.com/a1/'), "/a1/default/index") + self.assertEqual(filter_url( + 'http://domain.com/path/to/apps/a1/'), "/a1/default/index") + self.assertEqual(filter_url( + 'http://domain.com/path/to/a1/'), "/a1/default/path ['to', 'a1']") def test_router_absolute(self): ''' @@ -1053,37 +1380,46 @@ class TestRouter(unittest.TestCase): r = Storage() r.env = Storage() r.env.http_host = 'domain.com' - r.env.wsgi_url_scheme = 'httpx' # distinguish incoming scheme + r.env.wsgi_url_scheme = 'httpx' # distinguish incoming scheme self.assertEqual(str(URL(r=r, a='a', c='c', f='f')), "/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', host=True)), - "httpx://domain.com/a/c/f") + "httpx://domain.com/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', host='host.com')), - "httpx://host.com/a/c/f") + "httpx://host.com/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=True)), - "httpx://domain.com/a/c/f") + "httpx://domain.com/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=False)), - "/a/c/f") + "/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme='https')), - "https://domain.com/a/c/f") + "https://domain.com/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme='wss')), - "wss://domain.com/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=True, host=True)), + "wss://domain.com/a/c/f") + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme=True, host=True)), "httpx://domain.com/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme='https', host=True)), + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme='https', host=True)), "https://domain.com/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=False, host=True)), + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme=False, host=True)), "httpx://domain.com/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=True, host='host.com')), + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme=True, host='host.com')), "httpx://host.com/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=False, host='host.com')), + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme=False, host='host.com')), "httpx://host.com/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', port=1234)), + "httpx://domain.com:1234/a/c/f") + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme=True, port=1234)), "httpx://domain.com:1234/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=True, port=1234)), - "httpx://domain.com:1234/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', host='host.com', port=1234)), + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', host='host.com', port=1234)), "httpx://host.com:1234/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme='wss', host='host.com', port=1234)), + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme='wss', + host='host.com', port=1234)), "wss://host.com:1234/a/c/f") def test_request_uri(self): @@ -1092,15 +1428,20 @@ class TestRouter(unittest.TestCase): ''' load(rdict=dict()) - self.assertEqual(filter_url('http://domain.com/abc', env=True).request_uri, + self.assertEqual( + filter_url('http://domain.com/abc', env=True).request_uri, '/init/default/abc') - self.assertEqual(filter_url('http://domain.com/abc?def', env=True).request_uri, + self.assertEqual( + filter_url('http://domain.com/abc?def', env=True).request_uri, '/init/default/abc?def') - self.assertEqual(filter_url('http://domain.com/index/abc', env=True).request_uri, + self.assertEqual( + filter_url('http://domain.com/index/abc', env=True).request_uri, "/init/default/index/abc") - self.assertEqual(filter_url('http://domain.com/abc/def', env=True).request_uri, + self.assertEqual( + filter_url('http://domain.com/abc/def', env=True).request_uri, "/init/default/abc/def") - self.assertEqual(filter_url('http://domain.com/index/a%20bc', env=True).request_uri, + self.assertEqual( + filter_url('http://domain.com/index/a%20bc', env=True).request_uri, "/init/default/index/a%20bc") def test_request_collide(self): @@ -1108,12 +1449,12 @@ class TestRouter(unittest.TestCase): Test controller-app name collision: admin vs welcome/admin ''' router_collide = dict( - BASE = dict( - domains = { - 'ex.domain.com' : 'examples', - 'ad.domain.com' : 'admin', - 'welcome.com' : 'welcome', - 'www.welcome.com' : 'welcome', + BASE=dict( + domains={ + 'ex.domain.com': 'examples', + 'ad.domain.com': 'admin', + 'welcome.com': 'welcome', + 'www.welcome.com': 'welcome', }, exclusive_domain=True, ), @@ -1121,33 +1462,47 @@ class TestRouter(unittest.TestCase): load(rdict=router_collide) # basic inbound - self.assertEqual(filter_url('http://ex.domain.com'), '/examples/default/exdef') - self.assertEqual(filter_url('http://ad.domain.com'), '/admin/default/index') - self.assertEqual(filter_url('http://welcome.com'), '/welcome/default/index') - self.assertEqual(filter_url('http://www.welcome.com'), '/welcome/default/index') + self.assertEqual( + filter_url('http://ex.domain.com'), '/examples/default/exdef') + self.assertEqual( + filter_url('http://ad.domain.com'), '/admin/default/index') + self.assertEqual( + filter_url('http://welcome.com'), '/welcome/default/index') + self.assertEqual( + filter_url('http://www.welcome.com'), '/welcome/default/index') # basic outbound self.assertEqual(filter_url('http://ex.domain.com/examples/default/exdef', domain='examples', out=True), "/") - self.assertEqual(filter_url('http://ad.domain.com/admin/default/index', domain='admin', out=True), "/") - self.assertEqual(filter_url('http://welcome.com/welcome/default/index', domain='welcome', out=True), "/") + self.assertEqual(filter_url('http://ad.domain.com/admin/default/index', + domain='admin', out=True), "/") + self.assertEqual(filter_url('http://welcome.com/welcome/default/index', + domain='welcome', out=True), "/") self.assertEqual(filter_url('http://www.welcome.com/welcome/default/index', domain='welcome', out=True), "/") # inbound - self.assertEqual(filter_url('http://welcome.com/admin'), '/welcome/admin/index') - self.assertEqual(filter_url('http://welcome.com/f1'), '/welcome/default/f1') - self.assertEqual(filter_url('http://ad.domain.com/shell'), '/admin/shell/index') - self.assertEqual(filter_url('http://ad.domain.com/f1'), '/admin/default/f1') + self.assertEqual( + filter_url('http://welcome.com/admin'), '/welcome/admin/index') + self.assertEqual( + filter_url('http://welcome.com/f1'), '/welcome/default/f1') + self.assertEqual( + filter_url('http://ad.domain.com/shell'), '/admin/shell/index') + self.assertEqual( + filter_url('http://ad.domain.com/f1'), '/admin/default/f1') # outbound - self.assertEqual(filter_url('http://welcome.com/welcome/other/index', domain='welcome', out=True), "/other") - self.assertEqual(filter_url('http://welcome.com/welcome/admin/index', domain='welcome', out=True), "/admin") - self.assertEqual(filter_url('http://ad.domain.com/admin/shell/index', domain='admin', out=True), "/shell") - self.assertEqual(filter_url('http://ad.domain.com/admin/default/f1', domain='admin', out=True), "/f1") + self.assertEqual(filter_url('http://welcome.com/welcome/other/index', + domain='welcome', out=True), "/other") + self.assertEqual(filter_url('http://welcome.com/welcome/admin/index', + domain='welcome', out=True), "/admin") + self.assertEqual(filter_url('http://ad.domain.com/admin/shell/index', + domain='admin', out=True), "/shell") + self.assertEqual(filter_url('http://ad.domain.com/admin/default/f1', + domain='admin', out=True), "/f1") router_collide['BASE']['exclusive_domain'] = False load(rdict=router_collide) - self.assertEqual(filter_url('http://welcome.com/welcome/admin/index', domain='welcome', out=True), "/welcome/admin") + self.assertEqual(filter_url('http://welcome.com/welcome/admin/index', + domain='welcome', out=True), "/welcome/admin") if __name__ == '__main__': setUpModule() # pre-2.7 unittest.main() tearDownModule() - diff --git a/gluon/tests/test_routes.py b/gluon/tests/test_routes.py index 6bca6fda..675371ab 100644 --- a/gluon/tests/test_routes.py +++ b/gluon/tests/test_routes.py @@ -10,9 +10,9 @@ import tempfile import logging if os.path.isdir('gluon'): - sys.path.append(os.path.realpath('gluon')) # running from web2py base + sys.path.append(os.path.realpath('gluon')) # running from web2py base else: - sys.path.append(os.path.realpath('../')) # running from gluon/tests/ + sys.path.append(os.path.realpath('../')) # running from gluon/tests/ os.environ['web2py_path'] = os.path.realpath('../../') # for settings from rewrite import load, filter_url, filter_err, get_effective_router, regex_filter_out, regex_select @@ -26,6 +26,7 @@ logger = None oldcwd = None root = None + def setUpModule(): def make_apptree(): "build a temporary applications tree" @@ -39,13 +40,16 @@ def setUpModule(): os.mkdir(abspath('applications', app, subdir)) # applications/admin/controllers/*.py for ctr in ('appadmin', 'default', 'gae', 'mercurial', 'shell', 'wizard'): - open(abspath('applications', 'admin', 'controllers', '%s.py' % ctr), 'w').close() + open(abspath('applications', 'admin', + 'controllers', '%s.py' % ctr), 'w').close() # applications/examples/controllers/*.py for ctr in ('ajax_examples', 'appadmin', 'default', 'global', 'spreadsheet'): - open(abspath('applications', 'examples', 'controllers', '%s.py' % ctr), 'w').close() + open(abspath('applications', 'examples', + 'controllers', '%s.py' % ctr), 'w').close() # applications/welcome/controllers/*.py for ctr in ('appadmin', 'default'): - open(abspath('applications', 'welcome', 'controllers', '%s.py' % ctr), 'w').close() + open(abspath('applications', 'welcome', + 'controllers', '%s.py' % ctr), 'w').close() # create an app-specific routes.py for examples app routes = open(abspath('applications', 'examples', 'routes.py'), 'w') routes.write("default_function='exdef'\n") @@ -55,7 +59,8 @@ def setUpModule(): if oldcwd is None: # do this only once oldcwd = os.getcwd() if not os.path.isdir('gluon'): - os.chdir(os.path.realpath('../../')) # run from web2py base directory + os.chdir(os.path.realpath( + '../../')) # run from web2py base directory import main # for initialization after chdir global logger logger = logging.getLogger('web2py.rewrite') @@ -64,6 +69,7 @@ def setUpModule(): root = global_settings.applications_parent make_apptree() + def tearDownModule(): global oldcwd if oldcwd is not None: @@ -78,16 +84,26 @@ class TestRoutes(unittest.TestCase): """ Tests a null routes table """ load(data='') # incoming - self.assertEqual(filter_url('http://domain.com'), '/init/default/index') - self.assertEqual(filter_url('http://domain.com/'), '/init/default/index') - self.assertEqual(filter_url('http://domain.com/abc'), '/abc/default/index') - self.assertEqual(filter_url('http://domain.com/abc/'), '/abc/default/index') - self.assertEqual(filter_url('http://domain.com/abc/def'), "/abc/def/index") - self.assertEqual(filter_url('http://domain.com/abc/def/'), "/abc/def/index") - self.assertEqual(filter_url('http://domain.com/abc/def/ghi'), "/abc/def/ghi") - self.assertEqual(filter_url('http://domain.com/abc/def/ghi/'), "/abc/def/ghi") - self.assertEqual(filter_url('http://domain.com/abc/def/ghi/jkl'), "/abc/def/ghi ['jkl']") - self.assertEqual(filter_url('http://domain.com/abc/def/ghi/j%20kl'), "/abc/def/ghi ['j_kl']") + self.assertEqual( + filter_url('http://domain.com'), '/init/default/index') + self.assertEqual( + filter_url('http://domain.com/'), '/init/default/index') + self.assertEqual( + filter_url('http://domain.com/abc'), '/abc/default/index') + self.assertEqual( + filter_url('http://domain.com/abc/'), '/abc/default/index') + self.assertEqual( + filter_url('http://domain.com/abc/def'), "/abc/def/index") + self.assertEqual( + filter_url('http://domain.com/abc/def/'), "/abc/def/index") + self.assertEqual( + filter_url('http://domain.com/abc/def/ghi'), "/abc/def/ghi") + self.assertEqual( + filter_url('http://domain.com/abc/def/ghi/'), "/abc/def/ghi") + self.assertEqual(filter_url( + 'http://domain.com/abc/def/ghi/jkl'), "/abc/def/ghi ['jkl']") + self.assertEqual(filter_url( + 'http://domain.com/abc/def/ghi/j%20kl'), "/abc/def/ghi ['j_kl']") self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static'), "%s/applications/welcome/static/path/to/static" % root) self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/st~tic') try: @@ -96,9 +112,11 @@ class TestRoutes(unittest.TestCase): except AttributeError: pass # outgoing - self.assertEqual(filter_url('http://domain.com/init/default/index', out=True), '/init/default/index') + self.assertEqual(filter_url('http://domain.com/init/default/index', + out=True), '/init/default/index') self.assertEqual(filter_url('http://domain.com/init/default/index/arg1', out=True), '/init/default/index/arg1') - self.assertEqual(filter_url('http://domain.com/init/default/abc', out=True), '/init/default/abc') + self.assertEqual(filter_url('http://domain.com/init/default/abc', + out=True), '/init/default/abc') def test_routes_query(self): """ Test query appending """ @@ -125,8 +143,10 @@ routes_app = [ ] ''' load(data=data) - self.assertEqual(filter_url('http://domain.com/welcome'), '/welcome/default/index') - self.assertEqual(filter_url('http://domain.com/examples'), '/examples/default/exdef') + self.assertEqual( + filter_url('http://domain.com/welcome'), '/welcome/default/index') + self.assertEqual(filter_url( + 'http://domain.com/examples'), '/examples/default/exdef') def test_routes_defapp(self): """ Test the default-application function """ @@ -135,12 +155,17 @@ default_application = 'defapp' ''' load(data=data) # incoming - self.assertEqual(filter_url('http://domain.com'), '/defapp/default/index') - self.assertEqual(filter_url('http://domain.com/'), '/defapp/default/index') - self.assertEqual(filter_url('http://domain.com/welcome'), '/welcome/default/index') - self.assertEqual(filter_url('http://domain.com/app'), '/app/default/index') + self.assertEqual( + filter_url('http://domain.com'), '/defapp/default/index') + self.assertEqual( + filter_url('http://domain.com/'), '/defapp/default/index') + self.assertEqual( + filter_url('http://domain.com/welcome'), '/welcome/default/index') + self.assertEqual( + filter_url('http://domain.com/app'), '/app/default/index') self.assertEqual(filter_url('http://domain.com/welcome/default/index/abc'), "/welcome/default/index ['abc']") - self.assertEqual(filter_url('http://domain.com/welcome/static/abc'), '%s/applications/welcome/static/abc' % root) + self.assertEqual(filter_url('http://domain.com/welcome/static/abc'), + '%s/applications/welcome/static/abc' % root) self.assertEqual(filter_url('http://domain.com/defapp/static/path/to/static'), "%s/applications/defapp/static/path/to/static" % root) def test_routes_raise(self): @@ -149,15 +174,21 @@ default_application = 'defapp' ''' # test non-exception variants load(data='') - self.assertEqual(filter_url('http://domain.com/init'), "/init/default/index") - self.assertEqual(filter_url('http://domain.com/init/default'), "/init/default/index") - self.assertEqual(filter_url('http://domain.com/init/default/fcn.ext'), "/init/default/fcn.ext") - self.assertEqual(filter_url('http://domain.com/init/default/fcn/arg'), "/init/default/fcn ['arg']") + self.assertEqual( + filter_url('http://domain.com/init'), "/init/default/index") + self.assertEqual(filter_url( + 'http://domain.com/init/default'), "/init/default/index") + self.assertEqual(filter_url('http://domain.com/init/default/fcn.ext'), + "/init/default/fcn.ext") + self.assertEqual(filter_url('http://domain.com/init/default/fcn/arg'), + "/init/default/fcn ['arg']") # now raise-HTTP variants self.assertRaises(HTTP, filter_url, 'http://domain.com/bad!ctl') self.assertRaises(HTTP, filter_url, 'http://domain.com/ctl/bad!fcn') - self.assertRaises(HTTP, filter_url, 'http://domain.com/ctl/fcn.bad!ext') - self.assertRaises(HTTP, filter_url, 'http://domain.com/ctl/fcn/bad!arg') + self.assertRaises( + HTTP, filter_url, 'http://domain.com/ctl/fcn.bad!ext') + self.assertRaises( + HTTP, filter_url, 'http://domain.com/ctl/fcn/bad!arg') try: # 2.7+ only self.assertRaisesRegexp(HTTP, '400 BAD REQUEST \[invalid path\]', filter_url, 'http://domain.com/init/bad!ctl') @@ -167,7 +198,8 @@ default_application = 'defapp' except AttributeError: pass - self.assertEqual(filter_url('http://domain.com/welcome/default/fcn_1'), "/welcome/default/fcn_1") + self.assertEqual(filter_url('http://domain.com/welcome/default/fcn_1'), + "/welcome/default/fcn_1") #self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/default/fcn-1') #try: # # 2.7+ only @@ -194,17 +226,26 @@ default_application = 'defapp' ('/favicon.ico', '/welcome/static/favicon.ico'), ('/admin$anything', '/admin$anything'), ('.*:https?://(.*\\.)?domain1.com:$method /', '/app1/default'), - ('.*:https?://(.*\\.)?domain1.com:$method /static/$anything', '/app1/static/$anything'), - ('.*:https?://(.*\\.)?domain1.com:$method /appadmin/$anything', '/app1/appadmin/$anything'), - ('.*:https?://(.*\\.)?domain1.com:$method /$anything', '/app1/default/$anything'), + ('.*:https?://(.*\\.)?domain1.com:$method /static/$anything', + '/app1/static/$anything'), + ('.*:https?://(.*\\.)?domain1.com:$method /appadmin/$anything', + '/app1/appadmin/$anything'), + ('.*:https?://(.*\\.)?domain1.com:$method /$anything', + '/app1/default/$anything'), ('.*:https?://(.*\\.)?domain2.com:$method /', '/app2/default'), - ('.*:https?://(.*\\.)?domain2.com:$method /static/$anything', '/app2/static/$anything'), - ('.*:https?://(.*\\.)?domain2.com:$method /appadmin/$anything', '/app2/appadmin/$anything'), - ('.*:https?://(.*\\.)?domain2.com:$method /$anything', '/app2/default/$anything'), + ('.*:https?://(.*\\.)?domain2.com:$method /static/$anything', + '/app2/static/$anything'), + ('.*:https?://(.*\\.)?domain2.com:$method /appadmin/$anything', + '/app2/appadmin/$anything'), + ('.*:https?://(.*\\.)?domain2.com:$method /$anything', + '/app2/default/$anything'), ('.*:https?://(.*\\.)?domain3.com:$method /', '/app3/defcon3'), - ('.*:https?://(.*\\.)?domain3.com:$method /static/$anything', '/app3/static/$anything'), - ('.*:https?://(.*\\.)?domain3.com:$method /appadmin/$anything', '/app3/appadmin/$anything'), - ('.*:https?://(.*\\.)?domain3.com:$method /$anything', '/app3/defcon3/$anything'), + ('.*:https?://(.*\\.)?domain3.com:$method /static/$anything', + '/app3/static/$anything'), + ('.*:https?://(.*\\.)?domain3.com:$method /appadmin/$anything', + '/app3/appadmin/$anything'), + ('.*:https?://(.*\\.)?domain3.com:$method /$anything', + '/app3/defcon3/$anything'), ('/', '/welcome/default'), ('/welcome/default/$anything', '/welcome/default/$anything'), ('/welcome/$anything', '/welcome/default/$anything'), @@ -228,47 +269,69 @@ routes_out = [ ] ''' load(data=data) - self.assertEqual(filter_url('http://domain.com/welcome/default/f/arg1'), + self.assertEqual( + filter_url('http://domain.com/welcome/default/f/arg1'), "/welcome/default/f ['arg1']") - self.assertEqual(filter_url('http://domain.com/welcome/default/f/arg1/'), + self.assertEqual( + filter_url('http://domain.com/welcome/default/f/arg1/'), "/welcome/default/f ['arg1']") - self.assertEqual(filter_url('http://domain.com/welcome/default/f/arg1//'), + self.assertEqual( + filter_url('http://domain.com/welcome/default/f/arg1//'), "/welcome/default/f ['arg1', '']") - self.assertEqual(filter_url('http://domain.com/welcome/default/f//arg1'), + self.assertEqual( + filter_url('http://domain.com/welcome/default/f//arg1'), "/welcome/default/f ['', 'arg1']") - self.assertEqual(filter_url('http://domain.com/welcome/default/f/arg1/arg2'), + self.assertEqual( + filter_url('http://domain.com/welcome/default/f/arg1/arg2'), "/welcome/default/f ['arg1', 'arg2']") - self.assertEqual(filter_url('http://domain.com/welcome/default/f/arg1//arg2'), + self.assertEqual( + filter_url('http://domain.com/welcome/default/f/arg1//arg2'), "/welcome/default/f ['arg1', '', 'arg2']") - self.assertEqual(filter_url('http://domain.com/welcome/default/f/arg1//arg3/'), + self.assertEqual( + filter_url('http://domain.com/welcome/default/f/arg1//arg3/'), "/welcome/default/f ['arg1', '', 'arg3']") - self.assertEqual(filter_url('http://domain.com/welcome/default/f/arg1//arg3//'), + self.assertEqual( + filter_url('http://domain.com/welcome/default/f/arg1//arg3//'), "/welcome/default/f ['arg1', '', 'arg3', '']") - self.assertEqual(filter_url('http://domain.com/welcome/default/f', out=True), "/f") + self.assertEqual( + filter_url('http://domain.com/welcome/default/f', out=True), "/f") self.assertEqual(regex_filter_out('/welcome/default/f'), "/f") - self.assertEqual(str(URL(a='welcome', c='default', f='f', args=None)), "/f") - self.assertEqual(str(URL(a='welcome', c='default', f='f', args=['arg1'])), "/f/arg1") - self.assertEqual(str(URL(a='welcome', c='default', f='f', args=['arg1', ''])), "/f/arg1//") - self.assertEqual(str(URL(a='welcome', c='default', f='f', args=['arg1', '', 'arg3'])), "/f/arg1//arg3") - self.assertEqual(str(URL(a='welcome', c='default', f='f', args=['ar g'])), "/f/ar%20g") - self.assertEqual(str(URL(a='welcome', c='default', f='f', args=['årg'])), "/f/%C3%A5rg") - self.assertEqual(str(URL(a='welcome', c='default', f='fünc')), "/f\xc3\xbcnc") + self.assertEqual( + str(URL(a='welcome', c='default', f='f', args=None)), "/f") + self.assertEqual(str( + URL(a='welcome', c='default', f='f', args=['arg1'])), "/f/arg1") + self.assertEqual(str(URL( + a='welcome', c='default', f='f', args=['arg1', ''])), "/f/arg1//") + self.assertEqual(str(URL(a='welcome', c='default', f='f', + args=['arg1', '', 'arg3'])), "/f/arg1//arg3") + self.assertEqual(str( + URL(a='welcome', c='default', f='f', args=['ar g'])), "/f/ar%20g") + self.assertEqual(str(URL( + a='welcome', c='default', f='f', args=['årg'])), "/f/%C3%A5rg") + self.assertEqual( + str(URL(a='welcome', c='default', f='fünc')), "/f\xc3\xbcnc") def test_routes_anchor(self): ''' Test URL with anchor ''' - self.assertEqual(str(URL(a='a', c='c', f='f', anchor='anchor')), "/a/c/f#anchor") + self.assertEqual( + str(URL(a='a', c='c', f='f', anchor='anchor')), "/a/c/f#anchor") load(data='') - self.assertEqual(str(URL(a='a', c='c', f='f', anchor='anchor')), "/a/c/f#anchor") + self.assertEqual( + str(URL(a='a', c='c', f='f', anchor='anchor')), "/a/c/f#anchor") args = ['a1', 'a2'] - self.assertEqual(str(URL(a='a', c='c', f='f', args=args, anchor='anchor')), + self.assertEqual( + str(URL(a='a', c='c', f='f', args=args, anchor='anchor')), "/a/c/f/a1/a2#anchor") vars = dict(v1=1, v2=2) - self.assertEqual(str(URL(a='a', c='c', f='f', vars=vars, anchor='anchor')), + self.assertEqual( + str(URL(a='a', c='c', f='f', vars=vars, anchor='anchor')), "/a/c/f?v1=1&v2=2#anchor") - self.assertEqual(str(URL(a='a', c='c', f='f', args=args, vars=vars, anchor='anchor')), + self.assertEqual( + str(URL( + a='a', c='c', f='f', args=args, vars=vars, anchor='anchor')), "/a/c/f/a1/a2?v1=1&v2=2#anchor") data = r'''routes_out = [ @@ -276,8 +339,9 @@ routes_out = [ ]''' load(data=data) self.assertEqual(str(URL(a='init', c='default', f='index')), - "/") - self.assertEqual(str(URL(a='init', c='default', f='index', anchor='anchor')), + "/") + self.assertEqual( + str(URL(a='init', c='default', f='index', anchor='anchor')), "/init/default/index#anchor") data = r'''routes_out = [ @@ -285,8 +349,9 @@ routes_out = [ ]''' load(data=data) self.assertEqual(str(URL(a='init', c='default', f='index')), - "/") - self.assertEqual(str(URL(a='init', c='default', f='index', anchor='anchor')), + "/") + self.assertEqual( + str(URL(a='init', c='default', f='index', anchor='anchor')), "/#anchor") data = r'''routes_out = [ @@ -294,13 +359,17 @@ routes_out = [ ]''' load(data=data) self.assertEqual(str(URL(a='init', c='default', f='index')), - "/") - self.assertEqual(str(URL(a='init', c='default', f='index', anchor='anchor')), + "/") + self.assertEqual( + str(URL(a='init', c='default', f='index', anchor='anchor')), "/#anchor") query = dict(var='abc') - self.assertEqual(str(URL(a='init', c='default', f='index', vars=query)), + self.assertEqual( + str(URL(a='init', c='default', f='index', vars=query)), "/?var=abc") - self.assertEqual(str(URL(a='init', c='default', f='index', vars=query, anchor='anchor')), + self.assertEqual( + str(URL(a='init', c='default', f='index', + vars=query, anchor='anchor')), "/?var=abc#anchor") def test_routes_absolute(self): @@ -311,37 +380,46 @@ routes_out = [ r = Storage() r.env = Storage() r.env.http_host = 'domain.com' - r.env.wsgi_url_scheme = 'httpx' # distinguish incoming scheme + r.env.wsgi_url_scheme = 'httpx' # distinguish incoming scheme self.assertEqual(str(URL(r=r, a='a', c='c', f='f')), "/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', host=True)), - "httpx://domain.com/a/c/f") + "httpx://domain.com/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', host='host.com')), - "httpx://host.com/a/c/f") + "httpx://host.com/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=True)), - "httpx://domain.com/a/c/f") + "httpx://domain.com/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=False)), - "/a/c/f") + "/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme='https')), - "https://domain.com/a/c/f") + "https://domain.com/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme='wss')), - "wss://domain.com/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=True, host=True)), + "wss://domain.com/a/c/f") + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme=True, host=True)), "httpx://domain.com/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme='https', host=True)), + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme='https', host=True)), "https://domain.com/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=False, host=True)), + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme=False, host=True)), "httpx://domain.com/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=True, host='host.com')), + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme=True, host='host.com')), "httpx://host.com/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=False, host='host.com')), + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme=False, host='host.com')), "httpx://host.com/a/c/f") self.assertEqual(str(URL(r=r, a='a', c='c', f='f', port=1234)), + "httpx://domain.com:1234/a/c/f") + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme=True, port=1234)), "httpx://domain.com:1234/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=True, port=1234)), - "httpx://domain.com:1234/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', host='host.com', port=1234)), + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', host='host.com', port=1234)), "httpx://host.com:1234/a/c/f") - self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme='wss', host='host.com', port=1234)), + self.assertEqual( + str(URL(r=r, a='a', c='c', f='f', scheme='wss', + host='host.com', port=1234)), "wss://host.com:1234/a/c/f") def test_request_uri(self): @@ -354,13 +432,17 @@ routes_out = [ ] ''' load(data=data) - self.assertEqual(filter_url('http://domain.com/abc', env=True).request_uri, + self.assertEqual( + filter_url('http://domain.com/abc', env=True).request_uri, '/init/default/abc') - self.assertEqual(filter_url('http://domain.com/abc?def', env=True).request_uri, + self.assertEqual( + filter_url('http://domain.com/abc?def', env=True).request_uri, '/init/default/abc?def') - self.assertEqual(filter_url('http://domain.com/index/abc', env=True).request_uri, + self.assertEqual( + filter_url('http://domain.com/index/abc', env=True).request_uri, "/init/default/index/abc") - self.assertEqual(filter_url('http://domain.com/index/a%20bc', env=True).request_uri, + self.assertEqual( + filter_url('http://domain.com/index/a%20bc', env=True).request_uri, "/init/default/index/a bc") @@ -368,4 +450,3 @@ if __name__ == '__main__': setUpModule() # pre-2.7 unittest.main() tearDownModule() - diff --git a/gluon/tests/test_storage.py b/gluon/tests/test_storage.py index 6d35860b..828137e0 100644 --- a/gluon/tests/test_storage.py +++ b/gluon/tests/test_storage.py @@ -39,7 +39,6 @@ class TestStorage(unittest.TestCase): s.d = list() self.assertTrue(s.d is s['d']) - def test_store_none(self): """ Test Storage store-None handling s.key = None deletes an item @@ -59,7 +58,6 @@ class TestStorage(unittest.TestCase): self.assertTrue('a' in s) self.assertTrue(s.a is None) - def test_item(self): """ Tests Storage item handling """ @@ -76,4 +74,3 @@ class TestStorage(unittest.TestCase): if __name__ == '__main__': unittest.main() - diff --git a/gluon/tests/test_template.py b/gluon/tests/test_template.py index bba6cded..20fadbcd 100644 --- a/gluon/tests/test_template.py +++ b/gluon/tests/test_template.py @@ -14,15 +14,17 @@ else: import unittest from template import render + class TestVirtualFields(unittest.TestCase): def testRun(self): self.assertEqual(render(content='{{for i in range(n):}}{{=i}}{{pass}}', - context=dict(n=3)), '012') + context=dict(n=3)), '012') self.assertEqual(render(content='{{if n>2:}}ok{{pass}}', - context=dict(n=3)), 'ok') - self.assertEqual(render(content='{{try:}}{{n/0}}{{except:}}fail{{pass}}', - context=dict(n=3)), 'fail') + context=dict(n=3)), 'ok') + self.assertEqual( + render(content='{{try:}}{{n/0}}{{except:}}fail{{pass}}', + context=dict(n=3)), 'fail') self.assertEqual(render(content='{{="<&>"}}'), '<&>') self.assertEqual(render(content='"abc"'), '"abc"') self.assertEqual(render(content='"a\'bc"'), '"a\'bc"') @@ -38,13 +40,18 @@ class TestVirtualFields(unittest.TestCase): self.assertEqual(render(content='{{ ="abc" }}'), 'abc') self.assertEqual(render(content='{{pass\n="abc" }}'), 'abc') # = recognized only at the beginning of a physical line - self.assertEqual(render(content='{{xyz = "xyz"\n="abc"\n="def"\n=xyz }}'), 'abcdefxyz') + self.assertEqual(render( + content='{{xyz = "xyz"\n="abc"\n="def"\n=xyz }}'), 'abcdefxyz') # = in python blocks self.assertEqual(render(content='{{if True:\n="abc"\npass }}'), 'abc') - self.assertEqual(render(content='{{if True:\n="abc"\npass\n="def" }}'), 'abcdef') - self.assertEqual(render(content='{{if False:\n="abc"\npass\n="def" }}'), 'def') - self.assertEqual(render(content='{{if True:\n="abc"\nelse:\n="def"\npass }}'), 'abc') - self.assertEqual(render(content='{{if False:\n="abc"\nelse:\n="def"\npass }}'), 'def') + self.assertEqual( + render(content='{{if True:\n="abc"\npass\n="def" }}'), 'abcdef') + self.assertEqual( + render(content='{{if False:\n="abc"\npass\n="def" }}'), 'def') + self.assertEqual(render( + content='{{if True:\n="abc"\nelse:\n="def"\npass }}'), 'abc') + self.assertEqual(render( + content='{{if False:\n="abc"\nelse:\n="def"\npass }}'), 'def') # codeblock-leading = handles internal newlines, escaped or not self.assertEqual(render(content='{{=list((1,2,3))}}'), '[1, 2, 3]') self.assertEqual(render(content='{{=list((1,2,\\\n3))}}'), '[1, 2, 3]') @@ -52,10 +59,11 @@ class TestVirtualFields(unittest.TestCase): # ...but that means no more = operators in the codeblock self.assertRaises(SyntaxError, render, content='{{="abc"\n="def" }}') # = embedded in codeblock won't handle newlines in its argument - self.assertEqual(render(content='{{pass\n=list((1,2,\\\n3))}}'), '[1, 2, 3]') - self.assertRaises(SyntaxError, render, content='{{pass\n=list((1,2,\n3))}}') + self.assertEqual( + render(content='{{pass\n=list((1,2,\\\n3))}}'), '[1, 2, 3]') + self.assertRaises( + SyntaxError, render, content='{{pass\n=list((1,2,\n3))}}') if __name__ == '__main__': unittest.main() - diff --git a/gluon/tests/test_utils.py b/gluon/tests/test_utils.py index 7c0465dc..97d4d2b2 100644 --- a/gluon/tests/test_utils.py +++ b/gluon/tests/test_utils.py @@ -25,4 +25,3 @@ class TestUtils(unittest.TestCase): if __name__ == '__main__': unittest.main() - diff --git a/gluon/tests/test_web.py b/gluon/tests/test_web.py index 53e965dd..d50087e2 100644 --- a/gluon/tests/test_web.py +++ b/gluon/tests/test_web.py @@ -13,6 +13,7 @@ else: import unittest from contrib.webclient import WebClient + class TestWeb(unittest.TestCase): def testWebClient(self): client = WebClient('http://127.0.0.1:8000/welcome/default/') @@ -20,13 +21,13 @@ class TestWeb(unittest.TestCase): client.get('index') # register - data = dict(first_name = 'Homer', - last_name = 'Simpson', - email = 'homer@web2py.com', - password = 'test', - password_two = 'test', - _formname = 'register') - client.post('user/register',data = data) + data = dict(first_name='Homer', + last_name='Simpson', + email='homer@web2py.com', + password='test', + password_two='test', + _formname='register') + client.post('user/register', data=data) # logout client.get('user/logout') @@ -34,21 +35,22 @@ class TestWeb(unittest.TestCase): # login again data = dict(email='homer@web2py.com', password='test', - _formname = 'login') - client.post('user/login',data = data) + _formname='login') + client.post('user/login', data=data) # check registration and login were successful client.get('index') self.assertTrue('Welcome Homer' in client.text) client = WebClient('http://127.0.0.1:8000/admin/default/') - client.post('index',data=dict(password='hello')) + client.post('index', data=dict(password='hello')) client.get('site') client.get('design/welcome') + class TestStaticCacheControl(unittest.TestCase): def testWebClient(self): - s=WebClient('http://127.0.0.1:8000/welcome/') + s = WebClient('http://127.0.0.1:8000/welcome/') s.get('static/js/web2py.js') assert('expires' not in s.headers) assert(not s.headers['cache-control'].startswith('max-age')) @@ -60,4 +62,3 @@ class TestStaticCacheControl(unittest.TestCase): if __name__ == '__main__': unittest.main() - diff --git a/gluon/tools.py b/gluon/tools.py index 75eac67c..a323a3ff 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -53,23 +53,26 @@ logger = logging.getLogger("web2py") DEFAULT = lambda: None -def getarg(position,default=None): + +def getarg(position, default=None): args = current.request.args - if position<0 and len(args)>=-position: + if position < 0 and len(args) >= -position: return args[position] - elif position>=0 and len(args)>position: + elif position >= 0 and len(args) > position: return args[position] else: return default -def callback(actions,form,tablename=None): + +def callback(actions, form, tablename=None): if actions: - if tablename and isinstance(actions,dict): + if tablename and isinstance(actions, dict): actions = actions.get(tablename, []) - if not isinstance(actions,(list, tuple)): + if not isinstance(actions, (list, tuple)): actions = [actions] [action(form) for action in actions] + def validators(*a): b = [] for item in a: @@ -79,12 +82,14 @@ def validators(*a): b.append(item) return b -def call_or_redirect(f,*args): + +def call_or_redirect(f, *args): if callable(f): redirect(f(*args)) else: redirect(f) + def replace_id(url, form): if url: url = url.replace('[id]', str(form.vars.id)) @@ -92,6 +97,7 @@ def replace_id(url, form): return url return URL(url) + class Mail(object): """ Class for configuring and sending emails with alternative text / html @@ -154,7 +160,7 @@ class Mail(object): filename=None, content_id=None, content_type=None, - encoding='utf-8'): + encoding='utf-8'): if isinstance(payload, str): if filename is None: filename = os.path.basename(payload) @@ -268,7 +274,7 @@ class Mail(object): encoding='utf-8', raw=False, headers={} - ): + ): """ Sends an email using data specified in constructor @@ -341,8 +347,8 @@ class Mail(object): """ def encode_header(key): - if [c for c in key if 32>ord(c) or ord(c)>127]: - return Header.Header(key.encode('utf-8'),'utf-8') + if [c for c in key if 32 > ord(c) or ord(c) > 127]: + return Header.Header(key.encode('utf-8'), 'utf-8') else: return key @@ -370,7 +376,7 @@ class Mail(object): # unreadable mail contents. payload_in = MIMEText.MIMEText(text) if to: - if not isinstance(to, (list,tuple)): + if not isinstance(to, (list, tuple)): to = [to] else: raise Exception('Target receiver address not specified') @@ -385,7 +391,7 @@ class Mail(object): elif isinstance(message, (list, tuple)): text, html = message elif message.strip().startswith(''): - text = self.settings.server=='gae' and message or None + text = self.settings.server == 'gae' and message or None html = message else: text = message @@ -398,13 +404,14 @@ class Mail(object): text = text.decode(encoding).encode('utf-8') else: text = text.read().decode(encoding).encode('utf-8') - attachment.attach(MIMEText.MIMEText(text,_charset='utf-8')) + attachment.attach(MIMEText.MIMEText(text, _charset='utf-8')) if not html is None: if isinstance(html, basestring): html = html.decode(encoding).encode('utf-8') else: html = html.read().decode(encoding).encode('utf-8') - attachment.attach(MIMEText.MIMEText(html, 'html',_charset='utf-8')) + attachment.attach( + MIMEText.MIMEText(html, 'html', _charset='utf-8')) payload_in.attach(attachment) if (attachments is None) or raw: pass @@ -414,7 +421,6 @@ class Mail(object): else: payload_in.attach(attachments) - ####################################################### # CIPHER # ####################################################### @@ -431,7 +437,7 @@ class Mail(object): import os os.environ['GNUPGHOME'] = self.settings.gpg_home if not sign and not encrypt: - self.error="No sign and no encrypt is set but cipher type to gpg" + self.error = "No sign and no encrypt is set but cipher type to gpg" return False # need a python-pyme package and gpgme lib @@ -443,7 +449,7 @@ class Mail(object): if sign: import string core.check_version(None) - pin=string.replace(payload_in.as_string(),'\n','\r\n') + pin = string.replace(payload_in.as_string(), '\n', '\r\n') plain = core.Data(pin) sig = core.Data() c = core.Context() @@ -454,29 +460,30 @@ class Mail(object): if sigkey.can_sign: c.signers_add(sigkey) if not c.signers_enum(0): - self.error='No key for signing [%s]' % self.settings.sender + self.error = 'No key for signing [%s]' % self.settings.sender return False - c.set_passphrase_cb(lambda x,y,z: sign_passphrase) + c.set_passphrase_cb(lambda x, y, z: sign_passphrase) try: # make a signature - c.op_sign(plain,sig,mode.DETACH) - sig.seek(0,0) + c.op_sign(plain, sig, mode.DETACH) + sig.seek(0, 0) # make it part of the email - payload=MIMEMultipart.MIMEMultipart('signed', - boundary=None, - _subparts=None, - **dict(micalg="pgp-sha1", - protocol="application/pgp-signature")) + payload = MIMEMultipart.MIMEMultipart('signed', + boundary=None, + _subparts=None, + **dict( + micalg="pgp-sha1", + protocol="application/pgp-signature")) # insert the origin payload payload.attach(payload_in) # insert the detached signature - p=MIMEBase.MIMEBase("application",'pgp-signature') + p = MIMEBase.MIMEBase("application", 'pgp-signature') p.set_payload(sig.read()) payload.attach(p) # it's just a trick to handle the no encryption case - payload_in=payload + payload_in = payload except errors.GPGMEError, ex: - self.error="GPG error: %s" % ex.getstring() + self.error = "GPG error: %s" % ex.getstring() return False ############################################ # encrypt # @@ -488,36 +495,36 @@ class Mail(object): c = core.Context() c.set_armor(1) # collect the public keys for encryption - recipients=[] - rec=to[:] + recipients = [] + rec = to[:] if cc: rec.extend(cc) if bcc: rec.extend(bcc) for addr in rec: - c.op_keylist_start(addr,0) + c.op_keylist_start(addr, 0) r = c.op_keylist_next() if r is None: - self.error='No key for [%s]' % addr + self.error = 'No key for [%s]' % addr return False recipients.append(r) try: # make the encryption c.op_encrypt(recipients, 1, plain, cipher) - cipher.seek(0,0) + cipher.seek(0, 0) # make it a part of the email - payload=MIMEMultipart.MIMEMultipart('encrypted', - boundary=None, - _subparts=None, - **dict(protocol="application/pgp-encrypted")) - p=MIMEBase.MIMEBase("application",'pgp-encrypted') + payload = MIMEMultipart.MIMEMultipart('encrypted', + boundary=None, + _subparts=None, + **dict(protocol="application/pgp-encrypted")) + p = MIMEBase.MIMEBase("application", 'pgp-encrypted') p.set_payload("Version: 1\r\n") payload.attach(p) - p=MIMEBase.MIMEBase("application",'octet-stream') + p = MIMEBase.MIMEBase("application", 'octet-stream') p.set_payload(cipher.read()) payload.attach(p) except errors.GPGMEError, ex: - self.error="GPG error: %s" % ex.getstring() + self.error = "GPG error: %s" % ex.getstring() return False ####################################################### # X.509 # @@ -543,16 +550,17 @@ class Mail(object): except Exception, e: self.error = "Can't load M2Crypto module" return False - msg_bio = BIO.MemoryBuffer( payload_in.as_string() ) + msg_bio = BIO.MemoryBuffer(payload_in.as_string()) s = SMIME.SMIME() # SIGN if sign: #key for signing try: - s.load_key( x509_sign_keyfile, x509_sign_certfile, callback = lambda x: sign_passphrase ) + s.load_key(x509_sign_keyfile, x509_sign_certfile, + callback=lambda x: sign_passphrase) except Exception, e: - self.error = "Something went wrong on certificate / private key loading: <%s>" % str( e ) + self.error = "Something went wrong on certificate / private key loading: <%s>" % str(e) return False try: if x509_nocerts: @@ -561,10 +569,12 @@ class Mail(object): flags = 0 if not encrypt: flags += SMIME.PKCS7_DETACHED - p7 = s.sign( msg_bio, flags = flags ) - msg_bio = BIO.MemoryBuffer( payload_in.as_string() ) # Recreate coz sign() has consumed it. + p7 = s.sign(msg_bio, flags=flags) + msg_bio = BIO.MemoryBuffer(payload_in.as_string( + )) # Recreate coz sign() has consumed it. except Exception, e: - self.error = "Something went wrong on signing: <%s> %s" % ( str( e ), str( flags ) ) + self.error = "Something went wrong on signing: <%s> %s" % ( + str(e), str(flags)) return False # ENCRYPT @@ -586,8 +596,8 @@ class Mail(object): else: tmp_bio.write(payload_in.as_string()) p7 = s.encrypt(tmp_bio) - except Exception,e: - self.error="Something went wrong on encrypting: <%s>" %str(e) + except Exception, e: + self.error = "Something went wrong on encrypting: <%s>" % str(e) return False # Final stage in sign and encryption @@ -601,11 +611,11 @@ class Mail(object): out.write('\r\n') out.write(payload_in.as_string()) out.close() - st=str(out.read()) - payload=message_from_string(st) + st = str(out.read()) + payload = message_from_string(st) else: # no cryptography process as usual - payload=payload_in + payload = payload_in sender = sender % dict(sender=self.settings.sender) payload['From'] = encoded_or_raw(sender.decode(encoding)) @@ -622,15 +632,15 @@ class Mail(object): payload['Subject'] = encoded_or_raw(subject.decode(encoding)) payload['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) - for k,v in headers.iteritems(): + for k, v in headers.iteritems(): payload[k] = encoded_or_raw(v.decode(encoding)) result = {} try: if self.settings.server == 'logging': - logger.warn('email not sent\n%s\nFrom: %s\nTo: %s\nSubject: %s\n\n%s\n%s\n' % \ - ('-'*40,sender, - ', '.join(to),subject, - text or html,'-'*40)) + logger.warn('email not sent\n%s\nFrom: %s\nTo: %s\nSubject: %s\n\n%s\n%s\n' % + ('-' * 40, sender, + ', '.join(to), subject, + text or html, '-' * 40)) elif self.settings.server == 'gae': xcc = dict() if cc: @@ -640,17 +650,20 @@ class Mail(object): if reply_to: xcc['reply_to'] = reply_to from google.appengine.api import mail - attachments = attachments and [(a.my_filename,a.my_payload) for a in attachments if not raw] + attachments = attachments and [(a.my_filename, a.my_payload) for a in attachments if not raw] if attachments: - result = mail.send_mail(sender=self.settings.sender, to=origTo, - subject=subject, body=text, html=html, - attachments=attachments, **xcc) + result = mail.send_mail( + sender=self.settings.sender, to=origTo, + subject=subject, body=text, html=html, + attachments=attachments, **xcc) elif html and (not raw): - result = mail.send_mail(sender=self.settings.sender, to=origTo, - subject=subject, body=text, html=html, **xcc) + result = mail.send_mail( + sender=self.settings.sender, to=origTo, + subject=subject, body=text, html=html, **xcc) else: - result = mail.send_mail(sender=self.settings.sender, to=origTo, - subject=subject, body=text, **xcc) + result = mail.send_mail( + sender=self.settings.sender, to=origTo, + subject=subject, body=text, **xcc) else: smtp_args = self.settings.server.split(':') if self.settings.ssl: @@ -662,8 +675,9 @@ class Mail(object): server.starttls() server.ehlo() if self.settings.login: - server.login(*self.settings.login.split(':',1)) - result = server.sendmail(self.settings.sender, to, payload.as_string()) + server.login(*self.settings.login.split(':', 1)) + result = server.sendmail( + self.settings.sender, to, payload.as_string()) server.quit() except Exception, e: logger.warn('Mail.send failure:%s' % e) @@ -700,9 +714,9 @@ class Recaptcha(DIV): use_ssl=False, error=None, error_message='invalid', - label = 'Verify:', - options = '' - ): + label='Verify:', + options='' + ): self.request_vars = request and request.vars or current.request.vars self.remote_addr = request.env.remote_addr self.public_key = public_key @@ -728,8 +742,8 @@ class Recaptcha(DIV): private_key = self.private_key remoteip = self.remote_addr if not (recaptcha_response_field and recaptcha_challenge_field - and len(recaptcha_response_field) - and len(recaptcha_challenge_field)): + and len(recaptcha_response_field) + and len(recaptcha_challenge_field)): self.errors['captcha'] = self.error_message return False params = urllib.urlencode({ @@ -737,12 +751,12 @@ class Recaptcha(DIV): 'remoteip': remoteip, 'challenge': recaptcha_challenge_field, 'response': recaptcha_response_field, - }) + }) request = urllib2.Request( url=self.VERIFY_SERVER, data=params, headers={'Content-type': 'application/x-www-form-urlencoded', - 'User-agent': 'reCAPTCHA Python'}) + 'User-agent': 'reCAPTCHA Python'}) httpresp = urllib2.urlopen(request) return_values = httpresp.read().splitlines() httpresp.close() @@ -768,11 +782,15 @@ class Recaptcha(DIV): captcha = DIV( SCRIPT("var RecaptchaOptions = {%s};" % self.options), SCRIPT(_type="text/javascript", - _src="%s/challenge?k=%s%s" % (server,public_key,error_param)), - TAG.noscript(IFRAME(_src="%s/noscript?k=%s%s" % (server,public_key,error_param), - _height="300",_width="500",_frameborder="0"), BR(), - INPUT(_type='hidden', _name='recaptcha_response_field', - _value='manual_challenge')), _id='recaptcha') + _src="%s/challenge?k=%s%s" % (server, public_key, error_param)), + TAG.noscript( + IFRAME( + _src="%s/noscript?k=%s%s" % ( + server, public_key, error_param), + _height="300", _width="500", _frameborder="0"), BR(), + INPUT( + _type='hidden', _name='recaptcha_response_field', + _value='manual_challenge')), _id='recaptcha') if not self.errors.captcha: return XML(captcha).xml() else: @@ -782,166 +800,167 @@ class Recaptcha(DIV): def addrow(form, a, b, c, style, _id, position=-1): if style == "divs": - form[0].insert(position, DIV(DIV(LABEL(a),_class='w2p_fl'), + form[0].insert(position, DIV(DIV(LABEL(a), _class='w2p_fl'), DIV(b, _class='w2p_fw'), DIV(c, _class='w2p_fc'), - _id = _id)) + _id=_id)) elif style == "table2cols": - form[0].insert(position, TR(TD(LABEL(a),_class='w2p_fl'), - TD(c,_class='w2p_fc'))) - form[0].insert(position+1, TR(TD(b,_class='w2p_fw'), - _colspan=2, _id = _id)) + form[0].insert(position, TR(TD(LABEL(a), _class='w2p_fl'), + TD(c, _class='w2p_fc'))) + form[0].insert(position + 1, TR(TD(b, _class='w2p_fw'), + _colspan=2, _id=_id)) elif style == "ul": - form[0].insert(position, LI(DIV(LABEL(a),_class='w2p_fl'), + form[0].insert(position, LI(DIV(LABEL(a), _class='w2p_fl'), DIV(b, _class='w2p_fw'), DIV(c, _class='w2p_fc'), - _id = _id)) + _id=_id)) elif style == "bootstrap": - form[0].insert(position, DIV(LABEL(a,_class='control-label'), - DIV(b,SPAN(c, _class='inline-help'),_class='controls'), - _class='control-group',_id = _id)) + form[0].insert(position, DIV(LABEL(a, _class='control-label'), + DIV(b, SPAN(c, _class='inline-help'), + _class='controls'), + _class='control-group', _id=_id)) else: - form[0].insert(position, TR(TD(LABEL(a),_class='w2p_fl'), - TD(b,_class='w2p_fw'), - TD(c,_class='w2p_fc'),_id = _id)) + form[0].insert(position, TR(TD(LABEL(a), _class='w2p_fl'), + TD(b, _class='w2p_fw'), + TD(c, _class='w2p_fc'), _id=_id)) class Auth(object): default_settings = dict( - hideerror = False, - password_min_length = 4, - cas_maps = None, - reset_password_requires_verification = False, - registration_requires_verification = False, - registration_requires_approval = False, - login_after_registration = False, - login_after_password_change = True, - alternate_requires_registration = False, - create_user_groups = "user_%(id)s", - everybody_group_id = None, - login_captcha = None, - register_captcha = None, - retrieve_username_captcha = None, - retrieve_password_captcha = None, - captcha = None, - expiration = 3600, # one hour - long_expiration = 3600*30*24, # one month - remember_me_form = True, - allow_basic_login = False, - allow_basic_login_only = False, - on_failed_authentication = lambda x: redirect(x), - formstyle = "table3cols", - label_separator = ": ", - password_field = 'password', - table_user_name = 'auth_user', - table_group_name = 'auth_group', - table_membership_name = 'auth_membership', - table_permission_name = 'auth_permission', - table_event_name = 'auth_event', - table_cas_name = 'auth_cas', - table_user = None, - table_group = None, - table_membership = None, - table_permission = None, - table_event = None, - table_cas = None, - showid = False, - use_username = False, - login_email_validate = True, - login_userfield = None, - logout_onlogout = None, - register_fields = None, - register_verify_password = True, - profile_fields = None, - email_case_sensitive = True, - username_case_sensitive = True, - ) + hideerror=False, + password_min_length=4, + cas_maps=None, + reset_password_requires_verification=False, + registration_requires_verification=False, + registration_requires_approval=False, + login_after_registration=False, + login_after_password_change=True, + alternate_requires_registration=False, + create_user_groups="user_%(id)s", + everybody_group_id=None, + login_captcha=None, + register_captcha=None, + retrieve_username_captcha=None, + retrieve_password_captcha=None, + captcha=None, + expiration=3600, # one hour + long_expiration=3600 * 30 * 24, # one month + remember_me_form=True, + allow_basic_login=False, + allow_basic_login_only=False, + on_failed_authentication=lambda x: redirect(x), + formstyle="table3cols", + label_separator=": ", + password_field='password', + table_user_name='auth_user', + table_group_name='auth_group', + table_membership_name='auth_membership', + table_permission_name='auth_permission', + table_event_name='auth_event', + table_cas_name='auth_cas', + table_user=None, + table_group=None, + table_membership=None, + table_permission=None, + table_event=None, + table_cas=None, + showid=False, + use_username=False, + login_email_validate=True, + login_userfield=None, + logout_onlogout=None, + register_fields=None, + register_verify_password=True, + profile_fields=None, + email_case_sensitive=True, + username_case_sensitive=True, + ) # ## these are messages that can be customized default_messages = dict( - login_button = 'Login', - register_button = 'Register', - password_reset_button = 'Request reset password', - password_change_button = 'Change password', - profile_save_button = 'Save profile', - submit_button = 'Submit', - verify_password = 'Verify Password', - delete_label = 'Check to delete', - function_disabled = 'Function disabled', - access_denied = 'Insufficient privileges', - registration_verifying = 'Registration needs verification', - registration_pending = 'Registration is pending approval', - login_disabled = 'Login disabled by administrator', - logged_in = 'Logged in', - email_sent = 'Email sent', - unable_to_send_email = 'Unable to send email', - email_verified = 'Email verified', - logged_out = 'Logged out', - registration_successful = 'Registration successful', - invalid_email = 'Invalid email', - unable_send_email = 'Unable to send email', - invalid_login = 'Invalid login', - invalid_user = 'Invalid user', - invalid_password = 'Invalid password', - is_empty = "Cannot be empty", - mismatched_password = "Password fields don't match", - verify_email = 'Click on the link %(link)s to verify your email', - verify_email_subject = 'Email verification', - username_sent = 'Your username was emailed to you', - new_password_sent = 'A new password was emailed to you', - password_changed = 'Password changed', - retrieve_username = 'Your username is: %(username)s', - retrieve_username_subject = 'Username retrieve', - retrieve_password = 'Your password is: %(password)s', - retrieve_password_subject = 'Password retrieve', - reset_password = \ - 'Click on the link %(link)s to reset your password', - reset_password_subject = 'Password reset', - invalid_reset_password = 'Invalid reset password', - profile_updated = 'Profile updated', - new_password = 'New password', - old_password = 'Old password', - group_description = 'Group uniquely assigned to user %(id)s', - register_log = 'User %(id)s Registered', - login_log = 'User %(id)s Logged-in', - login_failed_log = None, - logout_log = 'User %(id)s Logged-out', - profile_log = 'User %(id)s Profile updated', - verify_email_log = 'User %(id)s Verification email sent', - retrieve_username_log = 'User %(id)s Username retrieved', - retrieve_password_log = 'User %(id)s Password retrieved', - reset_password_log = 'User %(id)s Password reset', - change_password_log = 'User %(id)s Password changed', - add_group_log = 'Group %(group_id)s created', - del_group_log = 'Group %(group_id)s deleted', - add_membership_log = None, - del_membership_log = None, - has_membership_log = None, - add_permission_log = None, - del_permission_log = None, - has_permission_log = None, - impersonate_log = 'User %(id)s is impersonating %(other_id)s', - label_first_name = 'First name', - label_last_name = 'Last name', - label_username = 'Username', - label_email = 'E-mail', - label_password = 'Password', - label_registration_key = 'Registration key', - label_reset_password_key = 'Reset Password key', - label_registration_id = 'Registration identifier', - label_role = 'Role', - label_description = 'Description', - label_user_id = 'User ID', - label_group_id = 'Group ID', - label_name = 'Name', - label_table_name = 'Object or table name', - label_record_id = 'Record ID', - label_time_stamp = 'Timestamp', - label_client_ip = 'Client IP', - label_origin = 'Origin', - label_remember_me = "Remember me (for 30 days)", - verify_password_comment = 'please input your password again', - ) + login_button='Login', + register_button='Register', + password_reset_button='Request reset password', + password_change_button='Change password', + profile_save_button='Save profile', + submit_button='Submit', + verify_password='Verify Password', + delete_label='Check to delete', + function_disabled='Function disabled', + access_denied='Insufficient privileges', + registration_verifying='Registration needs verification', + registration_pending='Registration is pending approval', + login_disabled='Login disabled by administrator', + logged_in='Logged in', + email_sent='Email sent', + unable_to_send_email='Unable to send email', + email_verified='Email verified', + logged_out='Logged out', + registration_successful='Registration successful', + invalid_email='Invalid email', + unable_send_email='Unable to send email', + invalid_login='Invalid login', + invalid_user='Invalid user', + invalid_password='Invalid password', + is_empty="Cannot be empty", + mismatched_password="Password fields don't match", + verify_email='Click on the link %(link)s to verify your email', + verify_email_subject='Email verification', + username_sent='Your username was emailed to you', + new_password_sent='A new password was emailed to you', + password_changed='Password changed', + retrieve_username='Your username is: %(username)s', + retrieve_username_subject='Username retrieve', + retrieve_password='Your password is: %(password)s', + retrieve_password_subject='Password retrieve', + reset_password= + 'Click on the link %(link)s to reset your password', + reset_password_subject='Password reset', + invalid_reset_password='Invalid reset password', + profile_updated='Profile updated', + new_password='New password', + old_password='Old password', + group_description='Group uniquely assigned to user %(id)s', + register_log='User %(id)s Registered', + login_log='User %(id)s Logged-in', + login_failed_log=None, + logout_log='User %(id)s Logged-out', + profile_log='User %(id)s Profile updated', + verify_email_log='User %(id)s Verification email sent', + retrieve_username_log='User %(id)s Username retrieved', + retrieve_password_log='User %(id)s Password retrieved', + reset_password_log='User %(id)s Password reset', + change_password_log='User %(id)s Password changed', + add_group_log='Group %(group_id)s created', + del_group_log='Group %(group_id)s deleted', + add_membership_log=None, + del_membership_log=None, + has_membership_log=None, + add_permission_log=None, + del_permission_log=None, + has_permission_log=None, + impersonate_log='User %(id)s is impersonating %(other_id)s', + label_first_name='First name', + label_last_name='Last name', + label_username='Username', + label_email='E-mail', + label_password='Password', + label_registration_key='Registration key', + label_reset_password_key='Reset Password key', + label_registration_id='Registration identifier', + label_role='Role', + label_description='Description', + label_user_id='User ID', + label_group_id='Group ID', + label_name='Name', + label_table_name='Object or table name', + label_record_id='Record ID', + label_time_stamp='Timestamp', + label_client_ip='Client IP', + label_origin='Origin', + label_remember_me="Remember me (for 30 days)", + verify_password_comment='please input your password again', + ) """ Class for authentication, authorization, role based access control. @@ -1031,22 +1050,24 @@ class Auth(object): def get_or_create_key(filename=None, alg='sha512'): request = current.request if not filename: - filename = os.path.join(request.folder,'private','auth.key') + filename = os.path.join(request.folder, 'private', 'auth.key') if os.path.exists(filename): - key = open(filename,'r').read().strip() + key = open(filename, 'r').read().strip() else: - key = alg+':'+web2py_uuid() - open(filename,'w').write(key) + key = alg + ':' + web2py_uuid() + open(filename, 'w').write(key) return key def url(self, f=None, args=None, vars=None, scheme=False): - if args is None: args=[] - if vars is None: vars={} + if args is None: + args = [] + if vars is None: + vars = {} return URL(c=self.settings.controller, - f=f, args=args, vars=vars,scheme=scheme) + f=f, args=args, vars=vars, scheme=scheme) def here(self): - return URL(args=current.request.args,vars=current.request.vars) + return URL(args=current.request.args, vars=current.request.vars) def __init__(self, environment=None, db=None, mailer=True, hmac_key=None, controller='default', function='user', @@ -1062,7 +1083,7 @@ class Auth(object): - cas_provider (delegate authentication to the URL, CAS2) """ ## next two lines for backward compatibility - if not db and environment and isinstance(environment,DAL): + if not db and environment and isinstance(environment, DAL): db = environment self.db = db self.environment = current @@ -1076,40 +1097,41 @@ class Auth(object): datetime.timedelta(days=0, seconds=auth.expiration) > request.now: self.user = auth.user # this is a trick to speed up sessions - if (request.now - auth.last_visit).seconds > (auth.expiration/10): + if (request.now - auth.last_visit).seconds > (auth.expiration / 10): auth.last_visit = request.now else: self.user = None - if session.auth: del session.auth + if session.auth: + del session.auth # ## what happens after login? self.next = current.request.vars._next - if isinstance(self.next,(list,tuple)): + if isinstance(self.next, (list, tuple)): self.next = self.next[0] - url_index = URL(controller,'index') - url_login = URL(controller,function,args='login') + url_index = URL(controller, 'index') + url_login = URL(controller, function, args='login') # ## what happens after registration? settings = self.settings = Settings() settings.update(Auth.default_settings) settings.update( - cas_domains = [request.env.http_host], - cas_provider = cas_provider, - cas_actions = dict(login ='login', - validate ='validate', - servicevalidate ='serviceValidate', - proxyvalidate ='proxyValidate', - logout ='logout'), - extra_fields = {}, - actions_disabled = [], - controller = controller, - function = function, - login_url = url_login, - logged_url = URL(controller, function, args='profile'), - download_url = URL(controller,'download'), - mailer = (mailer==True) and Mail() or mailer, - on_failed_authorization = \ - URL(controller,function, args='not_authorized'), + cas_domains=[request.env.http_host], + cas_provider=cas_provider, + cas_actions=dict(login='login', + validate='validate', + servicevalidate='serviceValidate', + proxyvalidate='proxyValidate', + logout='logout'), + extra_fields={}, + actions_disabled=[], + controller=controller, + function=function, + login_url=url_login, + logged_url=URL(controller, function, args='profile'), + download_url=URL(controller, 'download'), + mailer=(mailer == True) and Mail() or mailer, + on_failed_authorization = + URL(controller, function, args='not_authorized'), login_next = url_index, login_onvalidation = [], login_onaccept = [], @@ -1136,7 +1158,7 @@ class Auth(object): reset_password_onvalidation = [], reset_password_onaccept = [], hmac_key = hmac_key, - ) + ) settings.lock_keys = True # ## these are messages that can be customized @@ -1146,7 +1168,7 @@ class Auth(object): # for "remember me" option response = current.response - if auth and auth.remember: + if auth and auth.remember: # when user wants to be logged in for longer response.cookies[response.session_id_name]["expires"] = \ auth.expiration @@ -1156,21 +1178,26 @@ class Auth(object): self.signature = None def _get_user_id(self): - "accessor for auth.user_id" - return self.user and self.user.id or None + "accessor for auth.user_id" + return self.user and self.user.id or None user_id = property(_get_user_id, doc="user.id or None") def table_user(self): return self.db[self.settings.table_user_name] + def table_group(self): return self.db[self.settings.table_group_name] + def table_membership(self): return self.db[self.settings.table_membership_name] + def table_permission(self): return self.db[self.settings.table_permission_name] + def table_event(self): return self.db[self.settings.table_event_name] + def table_cas(self): return self.db[self.settings.table_cas_name] @@ -1191,19 +1218,19 @@ class Auth(object): request = current.request args = request.args if not args: - redirect(self.url(args='login',vars=request.vars)) + redirect(self.url(args='login', vars=request.vars)) elif args[0] in self.settings.actions_disabled: raise HTTP(404) - if args[0] in ('login','logout','register','verify_email', - 'retrieve_username','retrieve_password', - 'reset_password','request_reset_password', - 'change_password','profile','groups', - 'impersonate','not_authorized'): - if len(request.args) >= 2 and args[0]=='impersonate': - return getattr(self,args[0])(request.args[1]) + if args[0] in ('login', 'logout', 'register', 'verify_email', + 'retrieve_username', 'retrieve_password', + 'reset_password', 'request_reset_password', + 'change_password', 'profile', 'groups', + 'impersonate', 'not_authorized'): + if len(request.args) >= 2 and args[0] == 'impersonate': + return getattr(self, args[0])(request.args[1]) else: - return getattr(self,args[0])() - elif args[0]=='cas' and not self.settings.cas_provider: + return getattr(self, args[0])() + elif args[0] == 'cas' and not self.settings.cas_provider: if args(1) == self.settings.cas_actions['login']: return self.cas_login(version=2) elif args(1) == self.settings.cas_actions['validate']: @@ -1218,7 +1245,7 @@ class Auth(object): raise HTTP(404) def navbar(self, prefix='Welcome', action=None, - separators=(' [ ',' | ',' ] '), user_identifier=DEFAULT, + separators=(' [ ', ' | ', ' ] '), user_identifier=DEFAULT, referrer_actions=DEFAULT, mode='default'): referrer_actions = [] if not referrer_actions else referrer_actions request = current.request @@ -1230,14 +1257,14 @@ class Auth(object): prefix = prefix.strip() + ' ' if not action: action = self.url(self.settings.function) - s1,s2,s3 = separators + s1, s2, s3 = separators if URL() == action: next = '' else: next = '?_next=' + urllib.quote(URL(args=request.args, vars=request.get_vars)) href = lambda function: '%s/%s%s' % (action, function, - next if referrer_actions is DEFAULT or function in referrer_actions else '') + next if referrer_actions is DEFAULT or function in referrer_actions else '') if self.user_id: if user_identifier is DEFAULT: @@ -1254,20 +1281,26 @@ class Auth(object): (action, urllib.quote(self.settings.logout_next))) profile = A(T('Profile'), _href=href('profile')) password = A(T('Password'), _href=href('change_password')) - bar = SPAN(prefix, user_identifier, s1, logout, s3, _class='auth_navbar') + bar = SPAN( + prefix, user_identifier, s1, logout, s3, _class='auth_navbar') if asdropdown: - logout = LI(A(I(_class='icon-off'), ' '+T('Logout'), _href='%s/logout?_next=%s' % - (action, urllib.quote(self.settings.logout_next)))) # the space before T('Logout') is intentional. It creates a gap between icon and text - profile = LI(A(I(_class='icon-user'), ' '+T('Profile'), _href=href('profile'))) - password = LI(A(I(_class='icon-lock'), ' '+T('Password'), _href=href('change_password'))) - bar = UL(logout,_class='dropdown-menu') # logout will be the last item in list + logout = LI(A(I(_class='icon-off'), ' ' + T('Logout'), _href='%s/logout?_next=%s' % + (action, urllib.quote(self.settings.logout_next)))) # the space before T('Logout') is intentional. It creates a gap between icon and text + profile = LI(A(I(_class='icon-user'), ' ' + + T('Profile'), _href=href('profile'))) + password = LI(A(I(_class='icon-lock'), ' ' + + T('Password'), _href=href('change_password'))) + bar = UL(logout, _class='dropdown-menu') + # logout will be the last item in list if not 'profile' in self.settings.actions_disabled: - if not asdropdown: bar.insert(-1, s2) + if not asdropdown: + bar.insert(-1, s2) bar.insert(-1, profile) if not 'change_password' in self.settings.actions_disabled: - if not asdropdown: bar.insert(-1, s2) + if not asdropdown: + bar.insert(-1, s2) bar.insert(-1, password) else: login = A(T('Login'), _href=href('login')) @@ -1279,32 +1312,39 @@ class Auth(object): bar = SPAN(s1, login, s3, _class='auth_navbar') if asdropdown: - login = LI(A(I(_class='icon-off'), ' '+T('Login'), _href=href('login'))) #the space before T('Login') is intentional. It creates a gap between icon and text - register = LI(A(I(_class='icon-user'), ' '+T('Register'), _href=href('register'))) - retrieve_username = LI(A(I(_class='icon-edit'), ' '+T('Forgot username?'), _href=href('retrieve_username'))) - lost_password = LI(A(I(_class='icon-lock'), ' '+T('Lost password?'), _href=href('request_reset_password'))) - bar = UL(login,_class='dropdown-menu') # login will be the last item in list + login = LI(A(I(_class='icon-off'), ' ' + T('Login'), _href=href('login'))) # the space before T('Login') is intentional. It creates a gap between icon and text + register = LI(A(I(_class='icon-user'), + ' ' + T('Register'), _href=href('register'))) + retrieve_username = LI(A(I(_class='icon-edit'), ' ' + T( + 'Forgot username?'), _href=href('retrieve_username'))) + lost_password = LI(A(I(_class='icon-lock'), ' ' + T( + 'Lost password?'), _href=href('request_reset_password'))) + bar = UL(login, _class='dropdown-menu') + # login will be the last item in list if not 'register' in self.settings.actions_disabled: - if not asdropdown: bar.insert(-1, s2) + if not asdropdown: + bar.insert(-1, s2) bar.insert(-1, register) if self.settings.use_username and not 'retrieve_username' \ in self.settings.actions_disabled: - if not asdropdown: bar.insert(-1, s2) + if not asdropdown: + bar.insert(-1, s2) bar.insert(-1, retrieve_username) if not 'request_reset_password' \ in self.settings.actions_disabled: - if not asdropdown: bar.insert(-1, s2) + if not asdropdown: + bar.insert(-1, s2) bar.insert(-1, lost_password) if asdropdown: - bar.insert(-1, LI('',_class='divider')) + bar.insert(-1, LI('', _class='divider')) if self.user_id: bar = LI(A(prefix, user_identifier, _href='#'), - bar,_class='dropdown') + bar, _class='dropdown') else: bar = LI(A(T('Login'), _href='#'), - bar,_class='dropdown') + bar, _class='dropdown') return bar def __get_migrate(self, tablename, migrate=True): @@ -1318,7 +1358,7 @@ class Auth(object): def enable_record_versioning(self, tables, - archive_db = None, + archive_db=None, archive_names='%(tablename)s_archive', current_record='current_record'): """ @@ -1351,9 +1391,9 @@ class Auth(object): for table in tables: if 'modified_on' in table.fields(): table._enable_record_versioning( - archive_db = archive_db, - archive_name = archive_names, - current_record = current_record) + archive_db=archive_db, + archive_name=archive_names, + current_record=current_record) def define_signature(self): db = self.db @@ -1361,21 +1401,23 @@ class Auth(object): request = current.request T = current.T reference_user = 'reference %s' % settings.table_user_name - def lazy_user (auth = self): + + def lazy_user(auth=self): return auth.user_id - def represent(id,record=None,s=settings): + + def represent(id, record=None, s=settings): try: user = s.table_user(id) return '%(first_name)s %(last_name)s' % user except: return id self.signature = db.Table( - self.db,'auth_signature', - Field('is_active','boolean', + self.db, 'auth_signature', + Field('is_active', 'boolean', default=True, readable=False, writable=False, label=T('Is Active')), - Field('created_on','datetime', + Field('created_on', 'datetime', default=request.now, writable=False, readable=False, label=T('Created On')), @@ -1384,14 +1426,14 @@ class Auth(object): default=lazy_user, represent=represent, writable=False, readable=False, label=T('Created By')), - Field('modified_on','datetime', - update=request.now,default=request.now, - writable=False,readable=False, + Field('modified_on', 'datetime', + update=request.now, default=request.now, + writable=False, readable=False, label=T('Modified On')), Field('modified_by', - reference_user,represent=represent, - default=lazy_user,update=lazy_user, - writable=False,readable=False, + reference_user, represent=represent, + default=lazy_user, update=lazy_user, + writable=False, readable=False, label=T('Modified By'))) def define_tables(self, username=None, signature=None, @@ -1418,11 +1460,11 @@ class Auth(object): settings.use_username = username if not self.signature: self.define_signature() - if signature==True: + if signature == True: signature_list = [self.signature] elif not signature: signature_list = [] - elif isinstance(signature,self.db.Table): + elif isinstance(signature, self.db.Table): signature_list = [signature] else: signature_list = signature @@ -1433,34 +1475,34 @@ class Auth(object): IS_EMAIL(error_message=self.messages.invalid_email), IS_NOT_IN_DB(db, '%s.email' % settings.table_user_name)] if not settings.email_case_sensitive: - is_unique_email.insert(1,IS_LOWER()) + is_unique_email.insert(1, IS_LOWER()) if not settings.table_user_name in db.tables: passfield = settings.password_field extra_fields = settings.extra_fields.get( - settings.table_user_name,[])+signature_list + settings.table_user_name, []) + signature_list if username or settings.cas_provider: is_unique_username = \ [IS_MATCH('[\w\.\-]+'), - IS_NOT_IN_DB(db,'%s.username' % settings.table_user_name)] + IS_NOT_IN_DB(db, '%s.username' % settings.table_user_name)] if not settings.username_case_sensitive: - is_unique_username.insert(1,IS_LOWER()) + is_unique_username.insert(1, IS_LOWER()) table = db.define_table( settings.table_user_name, Field('first_name', length=128, default='', label=self.messages.label_first_name, - requires = is_not_empty), + requires=is_not_empty), Field('last_name', length=128, default='', label=self.messages.label_last_name, - requires = is_not_empty), + requires=is_not_empty), Field('email', length=512, default='', label=self.messages.label_email, - requires = is_unique_email), + requires=is_unique_email), Field('username', length=128, default='', label=self.messages.label_username, requires=is_unique_username), Field(passfield, 'password', length=512, readable=False, label=self.messages.label_password, - requires = [is_crypted]), + requires=[is_crypted]), Field('registration_key', length=512, writable=False, readable=False, default='', label=self.messages.label_registration_key), @@ -1481,16 +1523,16 @@ class Auth(object): settings.table_user_name, Field('first_name', length=128, default='', label=self.messages.label_first_name, - requires = is_not_empty), + requires=is_not_empty), Field('last_name', length=128, default='', label=self.messages.label_last_name, - requires = is_not_empty), + requires=is_not_empty), Field('email', length=512, default='', label=self.messages.label_email, - requires = is_unique_email), + requires=is_unique_email), Field(passfield, 'password', length=512, readable=False, label=self.messages.label_password, - requires = [is_crypted]), + requires=[is_crypted]), Field('registration_key', length=512, writable=False, readable=False, default='', label=self.messages.label_registration_key), @@ -1509,31 +1551,31 @@ class Auth(object): reference_table_user = 'reference %s' % settings.table_user_name if not settings.table_group_name in db.tables: extra_fields = settings.extra_fields.get( - settings.table_group_name,[])+signature_list + settings.table_group_name, []) + signature_list table = db.define_table( settings.table_group_name, Field('role', length=512, default='', - label=self.messages.label_role, - requires = IS_NOT_IN_DB( - db, '%s.role'% settings.table_group_name)), + label=self.messages.label_role, + requires=IS_NOT_IN_DB( + db, '%s.role' % settings.table_group_name)), Field('description', 'text', - label=self.messages.label_description), + label=self.messages.label_description), *extra_fields, **dict( migrate=self.__get_migrate( settings.table_group_name, migrate), fake_migrate=fake_migrate, - format = '%(role)s (%(id)s)')) + format='%(role)s (%(id)s)')) reference_table_group = 'reference %s' % settings.table_group_name if not settings.table_membership_name in db.tables: extra_fields = settings.extra_fields.get( - settings.table_membership_name,[])+signature_list + settings.table_membership_name, []) + signature_list table = db.define_table( settings.table_membership_name, Field('user_id', reference_table_user, - label=self.messages.label_user_id), + label=self.messages.label_user_id), Field('group_id', reference_table_group, - label=self.messages.label_group_id), + label=self.messages.label_group_id), *extra_fields, **dict( migrate=self.__get_migrate( @@ -1541,7 +1583,7 @@ class Auth(object): fake_migrate=fake_migrate)) if not settings.table_permission_name in db.tables: extra_fields = settings.extra_fields.get( - settings.table_permission_name,[])+signature_list + settings.table_permission_name, []) + signature_list table = db.define_table( settings.table_permission_name, Field('group_id', reference_table_group, @@ -1551,23 +1593,23 @@ class Auth(object): requires=is_not_empty), Field('table_name', length=512, label=self.messages.label_table_name), - Field('record_id', 'integer',default=0, + Field('record_id', 'integer', default=0, label=self.messages.label_record_id, - requires = IS_INT_IN_RANGE(0, 10 ** 9)), + requires=IS_INT_IN_RANGE(0, 10 ** 9)), *extra_fields, **dict( migrate=self.__get_migrate( settings.table_permission_name, migrate), fake_migrate=fake_migrate)) if not settings.table_event_name in db.tables: - table = db.define_table( + table = db.define_table( settings.table_event_name, Field('time_stamp', 'datetime', - default=current.request.now, - label=self.messages.label_time_stamp), + default=current.request.now, + label=self.messages.label_time_stamp), Field('client_ip', - default=current.request.client, - label=self.messages.label_client_ip), + default=current.request.client, + label=self.messages.label_client_ip), Field('user_id', reference_table_user, default=None, label=self.messages.label_user_id), Field('origin', default='auth', length=512, @@ -1576,7 +1618,7 @@ class Auth(object): Field('description', 'text', default='', label=self.messages.label_description, requires=is_not_empty), - *settings.extra_fields.get(settings.table_event_name,[]), + *settings.extra_fields.get(settings.table_event_name, []), **dict( migrate=self.__get_migrate( settings.table_event_name, migrate), @@ -1584,15 +1626,15 @@ class Auth(object): now = current.request.now if settings.cas_domains: if not settings.table_cas_name in db.tables: - table = db.define_table( + table = db.define_table( settings.table_cas_name, Field('user_id', reference_table_user, default=None, label=self.messages.label_user_id), - Field('created_on','datetime',default=now), - Field('service',requires=IS_URL()), + Field('created_on', 'datetime', default=now), + Field('service', requires=IS_URL()), Field('ticket'), Field('renew', 'boolean', default=False), - *settings.extra_fields.get(settings.table_cas_name,[]), + *settings.extra_fields.get(settings.table_cas_name, []), **dict( migrate=self.__get_migrate( settings.table_cas_name, migrate), @@ -1606,25 +1648,25 @@ class Auth(object): if settings.cas_domains: settings.table_cas = db[settings.table_cas_name] - if settings.cas_provider: ### THIS IS NOT LAZY + if settings.cas_provider: # THIS IS NOT LAZY settings.actions_disabled = \ - ['profile','register','change_password', - 'request_reset_password','retrieve_username'] + ['profile', 'register', 'change_password', + 'request_reset_password', 'retrieve_username'] from gluon.contrib.login_methods.cas_auth import CasAuth maps = settings.cas_maps if not maps: table_user = self.table_user() - maps = dict((name,lambda v,n=name:v.get(n,None)) for name in \ - table_user.fields if name!='id' \ - and table_user[name].readable) + maps = dict((name, lambda v, n=name: v.get(n, None)) for name in + table_user.fields if name != 'id' + and table_user[name].readable) maps['registration_id'] = \ - lambda v,p=settings.cas_provider:'%s/%s' % (p,v['user']) + lambda v, p=settings.cas_provider: '%s/%s' % (p, v['user']) actions = [settings.cas_actions['login'], settings.cas_actions['servicevalidate'], settings.cas_actions['logout']] settings.login_form = CasAuth( - casversion = 2, - urlbase = settings.cas_provider, + casversion=2, + urlbase=settings.cas_provider, actions=actions, maps=maps) return self @@ -1656,13 +1698,14 @@ class Auth(object): user = None checks = [] # make a guess about who this user is - for fieldname in ['registration_id','username','email']: + for fieldname in ['registration_id', 'username', 'email']: if fieldname in table_user.fields() and \ - keys.get(fieldname,None): + keys.get(fieldname, None): checks.append(fieldname) value = keys[fieldname] - user = table_user(**{fieldname:value}) - if user: break + user = table_user(**{fieldname: value}) + if user: + break if not checks: return None if not 'registration_id' in keys: @@ -1672,8 +1715,8 @@ class Auth(object): if 'registration_id' in checks \ and user \ and user.registration_id \ - and user.registration_id!=keys.get('registration_id',None): - user = None # THINK MORE ABOUT THIS? DO WE TRUST OPENID PROVIDER? + and user.registration_id != keys.get('registration_id', None): + user = None # THINK MORE ABOUT THIS? DO WE TRUST OPENID PROVIDER? if user: update_keys = dict(registration_id=keys['registration_id']) for key in update_fields: @@ -1682,10 +1725,10 @@ class Auth(object): user.update_record(**update_keys) elif checks: if not 'first_name' in keys and 'first_name' in table_user.fields: - guess = keys.get('email','anonymous').split('@')[0] - keys['first_name'] = keys.get('username',guess) + guess = keys.get('email', 'anonymous').split('@')[0] + keys['first_name'] = keys.get('username', guess) user_id = table_user.insert(**table_user._filter_fields(keys)) - user = self.user = table_user[user_id] + user = self.user = table_user[user_id] if self.settings.create_user_groups: group_id = self.add_group( self.settings.create_user_groups % user) @@ -1701,24 +1744,25 @@ class Auth(object): and returns basic_allowed,basic_accepted,user """ if not self.settings.allow_basic_login: - return (False,False,False) + return (False, False, False) basic = current.request.env.http_authorization if not basic or not basic[:6].lower() == 'basic ': return (True, False, False) (username, password) = base64.b64decode(basic[6:]).split(':') return (True, True, self.login_bare(username, password)) - def login_user(self,user): + def login_user(self, user): """ login the user = db.auth_user(id) """ - user = Storage(self.table_user()._filter_fields(user,id=True)) - if 'password' in user: del user.password + user = Storage(self.table_user()._filter_fields(user, id=True)) + if 'password' in user: + del user.password current.session.auth = Storage( - user = user, - last_visit = current.request.now, - expiration = self.settings.expiration, - hmac_key = web2py_uuid()) + user=user, + last_visit=current.request.now, + expiration=self.settings.expiration, + hmac_key=web2py_uuid()) self.user = user self.update_groups() @@ -1737,7 +1781,7 @@ class Auth(object): userfield = 'email' passfield = self.settings.password_field user = self.db(table_user[userfield] == username).select().first() - if user and user.get(passfield,False): + if user and user.get(passfield, False): password = table_user[passfield].validate(password)[0] if not user.registration_key and password == user[passfield]: self.login_user(user) @@ -1757,7 +1801,7 @@ class Auth(object): onaccept=DEFAULT, log=DEFAULT, version=2, - ): + ): request = current.request response = current.response session = current.session @@ -1765,13 +1809,14 @@ class Auth(object): session._cas_service = request.vars.service or session._cas_service if not request.env.http_host in self.settings.cas_domains or \ not session._cas_service: - raise HTTP(403,'not authorized') + raise HTTP(403, 'not authorized') + def allow_access(interactivelogin=False): - row = table(service=session._cas_service,user_id=self.user.id) + row = table(service=session._cas_service, user_id=self.user.id) if row: ticket = row.ticket else: - ticket = 'ST-'+web2py_uuid() + ticket = 'ST-' + web2py_uuid() table.insert(service=session._cas_service, user_id=self.user.id, ticket=ticket, @@ -1780,25 +1825,27 @@ class Auth(object): service = session._cas_service del session._cas_service if 'warn' in request.vars and not interactivelogin: - response.headers['refresh'] = "5;URL=%s"%service+"?ticket="+ticket - return A("Continue to %s"%service, - _href=service+"?ticket="+ticket) + response.headers[ + 'refresh'] = "5;URL=%s" % service + "?ticket=" + ticket + return A("Continue to %s" % service, + _href=service + "?ticket=" + ticket) else: - redirect(service+"?ticket="+ticket) + redirect(service + "?ticket=" + ticket) if self.is_logged_in() and not 'renew' in request.vars: return allow_access() elif not self.is_logged_in() and 'gateway' in request.vars: redirect(service) - def cas_onaccept(form, onaccept=onaccept): - if not onaccept is DEFAULT: onaccept(form) - return allow_access(interactivelogin=True) - return self.login(next,onvalidation,cas_onaccept,log) + def cas_onaccept(form, onaccept=onaccept): + if not onaccept is DEFAULT: + onaccept(form) + return allow_access(interactivelogin=True) + return self.login(next, onvalidation, cas_onaccept, log) def cas_validate(self, version=2, proxy=False): request = current.request db, table = self.db, self.table_cas() - current.response.headers['Content-Type']='text' + current.response.headers['Content-Type'] = 'text' ticket = request.vars.ticket renew = 'renew' in request.vars row = table(ticket=ticket) @@ -1816,32 +1863,33 @@ class Auth(object): user = self.table_user()(row.user_id) row.delete_record() success = True + def build_response(body): - return '\n'+\ + return '\n' +\ TAG['cas:serviceResponse']( - body,**{'_xmlns:cas':'http://www.yale.edu/tp/cas'}).xml() + body, **{'_xmlns:cas': 'http://www.yale.edu/tp/cas'}).xml() if success: if version == 1: message = 'yes\n%s' % user[userfield] - else: # assume version 2 - username = user.get('username',user[userfield]) + else: # assume version 2 + username = user.get('username', user[userfield]) message = build_response( TAG['cas:authenticationSuccess']( TAG['cas:user'](username), - *[TAG['cas:'+field.name](user[field.name]) \ - for field in self.table_user() \ - if field.readable])) + *[TAG['cas:' + field.name](user[field.name]) + for field in self.table_user() + if field.readable])) else: - if version == 1: - message = 'no\n' - elif row: - message = build_response(TAG['cas:authenticationFailure']()) - else: - message = build_response( - TAG['cas:authenticationFailure']( - 'Ticket %s not recognized' % ticket, - _code='INVALID TICKET')) - raise HTTP(200,message) + if version == 1: + message = 'no\n' + elif row: + message = build_response(TAG['cas:authenticationFailure']()) + else: + message = build_response( + TAG['cas:authenticationFailure']( + 'Ticket %s not recognized' % ticket, + _code='INVALID TICKET')) + raise HTTP(200, message) def login( self, @@ -1849,7 +1897,7 @@ class Auth(object): onvalidation=DEFAULT, onaccept=DEFAULT, log=DEFAULT, - ): + ): """ returns a login form @@ -1878,8 +1926,10 @@ class Auth(object): session = current.session passfield = self.settings.password_field - try: table_user[passfield].requires[-1].min_length = 0 - except: pass + try: + table_user[passfield].requires[-1].min_length = 0 + except: + pass ### use session for federated login if self.next: @@ -1897,56 +1947,56 @@ class Auth(object): if log is DEFAULT: log = self.messages.login_log - user = None # default + user = None # default # do we use our own login form, or from a central source? if self.settings.login_form == self: form = SQLFORM( table_user, fields=[username, passfield], - hidden = dict(_next=next), + hidden=dict(_next=next), showid=self.settings.showid, submit_button=self.messages.login_button, delete_label=self.messages.delete_label, formstyle=self.settings.formstyle, separator=self.settings.label_separator - ) + ) if self.settings.remember_me_form: ## adds a new input checkbox "remember me for longer" if self.settings.formstyle != 'bootstrap': - addrow(form,XML(" "), - DIV(XML(" "), - INPUT(_type='checkbox', - _class='checkbox', - _id="auth_user_remember", + addrow(form, XML(" "), + DIV(XML(" "), + INPUT(_type='checkbox', + _class='checkbox', + _id="auth_user_remember", _name="remember", - ), - XML("  "), - LABEL( - self.messages.label_remember_me, - _for="auth_user_remember", - )),"", - self.settings.formstyle, - 'auth_user_remember__row') + ), + XML("  "), + LABEL( + self.messages.label_remember_me, + _for="auth_user_remember", + )), "", + self.settings.formstyle, + 'auth_user_remember__row') elif self.settings.formstyle == 'bootstrap': addrow(form, "", LABEL( - INPUT(_type='checkbox', - _id="auth_user_remember", - _name="remember"), - self.messages.label_remember_me, - _class="checkbox"), - "", - self.settings.formstyle, - 'auth_user_remember__row') + INPUT(_type='checkbox', + _id="auth_user_remember", + _name="remember"), + self.messages.label_remember_me, + _class="checkbox"), + "", + self.settings.formstyle, + 'auth_user_remember__row') captcha = self.settings.login_captcha or \ - (self.settings.login_captcha!=False and self.settings.captcha) + (self.settings.login_captcha != False and self.settings.captcha) if captcha: addrow(form, captcha.label, captcha, captcha.comment, - self.settings.formstyle,'captcha__row') + self.settings.formstyle, 'captcha__row') accepted_form = False if form.accepts(request, session, @@ -1956,14 +2006,15 @@ class Auth(object): accepted_form = True # check for username in db - user = self.db(table_user[username] == form.vars[username]).select().first() + user = self.db(table_user[username] + == form.vars[username]).select().first() if user: # user in db, check if registration pending or disabled temp_user = user if temp_user.registration_key == 'pending': response.flash = self.messages.registration_pending return form - elif temp_user.registration_key in ('disabled','blocked'): + elif temp_user.registration_key in ('disabled', 'blocked'): response.flash = self.messages.login_disabled return form elif not temp_user.registration_key is None and \ @@ -2008,7 +2059,8 @@ class Auth(object): request.post_vars) # invalid login session.flash = self.messages.invalid_login - redirect(self.url(args=request.args,vars=request.get_vars)) + redirect( + self.url(args=request.args, vars=request.get_vars)) else: # use a central authentication server @@ -2017,8 +2069,9 @@ class Auth(object): if cas_user: cas_user[passfield] = None - user = self.get_or_create_user(table_user._filter_fields(cas_user)) - elif hasattr(cas,'login_form'): + user = self.get_or_create_user( + table_user._filter_fields(cas_user)) + elif hasattr(cas, 'login_form'): return cas.login_form() else: # we need to pass through login again before going on @@ -2032,7 +2085,7 @@ class Auth(object): # user wants to be logged in for longer self.login_user(user) session.auth.expiration = \ - request.vars.get('remember',False) and \ + request.vars.get('remember', False) and \ self.settings.long_expiration or \ self.settings.expiration session.auth.remember = 'remember' in request.vars @@ -2042,15 +2095,15 @@ class Auth(object): # how to continue if self.settings.login_form == self: if accepted_form: - callback(onaccept,form) + callback(onaccept, form) if next == session._auth_next: - session._auth_next = None + session._auth_next = None next = replace_id(next, form) redirect(next) table_user[username].requires = old_requires return form elif user: - callback(onaccept,None) + callback(onaccept, None) if next == session._auth_next: del session._auth_next redirect(next) @@ -2090,7 +2143,7 @@ class Auth(object): onvalidation=DEFAULT, onaccept=DEFAULT, log=DEFAULT, - ): + ): """ returns a registration form @@ -2133,13 +2186,13 @@ class Auth(object): table_user[username].requires += (unique_validator, ) elif not isinstance(table_user[username].requires, IS_NOT_IN_DB): table_user[username].requires = [table_user[username].requires, - unique_validator] + unique_validator] passfield = self.settings.password_field formstyle = self.settings.formstyle form = SQLFORM(table_user, - fields = self.settings.register_fields, - hidden = dict(_next=next), + fields=self.settings.register_fields, + hidden=dict(_next=next), showid=self.settings.showid, submit_button=self.messages.register_button, delete_label=self.messages.delete_label, @@ -2148,47 +2201,54 @@ class Auth(object): ) if self.settings.register_verify_password: for i, row in enumerate(form[0].components): - item = row.element('input',_name=passfield) + item = row.element('input', _name=passfield) if item: form.custom.widget.password_two = \ - INPUT(_name="password_two", _type="password", + INPUT(_name="password_two", _type="password", requires=IS_EXPR( - 'value==%s' % \ - repr(request.vars.get(passfield, None)), - error_message=self.messages.mismatched_password)) + 'value==%s' % + repr(request.vars.get(passfield, None)), + error_message=self.messages.mismatched_password)) - if formstyle == 'bootstrap' : - form.custom.widget.password_two['_class'] = 'input-xlarge' + if formstyle == 'bootstrap': + form.custom.widget.password_two[ + '_class'] = 'input-xlarge' - addrow(form, self.messages.verify_password + self.settings.label_separator, - form.custom.widget.password_two, - self.messages.verify_password_comment, + addrow( + form, self.messages.verify_password + + self.settings.label_separator, + form.custom.widget.password_two, + self.messages.verify_password_comment, formstyle, '%s_%s__row' % (table_user, 'password_two'), - position=i+1) + position=i + 1) break captcha = self.settings.register_captcha or self.settings.captcha if captcha: - addrow(form, captcha.label, captcha, captcha.comment,self.settings.formstyle, 'captcha__row') + addrow(form, captcha.label, captcha, + captcha.comment, self.settings.formstyle, 'captcha__row') table_user.registration_key.default = key = web2py_uuid() if form.accepts(request, session, formname='register', - onvalidation=onvalidation,hideerror=self.settings.hideerror): + onvalidation=onvalidation, hideerror=self.settings.hideerror): description = self.messages.group_description % form.vars if self.settings.create_user_groups: - group_id = self.add_group(self.settings.create_user_groups % form.vars, description) + group_id = self.add_group( + self.settings.create_user_groups % form.vars, description) self.add_membership(group_id, form.vars.id) if self.settings.everybody_group_id: - self.add_membership(self.settings.everybody_group_id, form.vars.id) + self.add_membership( + self.settings.everybody_group_id, form.vars.id) if self.settings.registration_requires_verification: - link = self.url('user',args=('verify_email',key),scheme=True) + link = self.url( + 'user', args=('verify_email', key), scheme=True) if not self.settings.mailer or \ not self.settings.mailer.send( to=form.vars.email, subject=self.messages.verify_email_subject, - message=self.messages.verify_email \ - % dict(key=key,link=link)): + message=self.messages.verify_email + % dict(key=key, link=link)): self.db.rollback() response.flash = self.messages.unable_send_email return form @@ -2197,7 +2257,7 @@ class Auth(object): not self.settings.registration_requires_verification: table_user[form.vars.id] = dict(registration_key='pending') session.flash = self.messages.registration_pending - elif (not self.settings.registration_requires_verification or \ + elif (not self.settings.registration_requires_verification or self.settings.login_after_registration): if not self.settings.registration_requires_verification: table_user[form.vars.id] = dict(registration_key='') @@ -2208,9 +2268,9 @@ class Auth(object): self.login_user(user) session.flash = self.messages.logged_in self.log_event(log, form.vars) - callback(onaccept,form) + callback(onaccept, form) if not next: - next = self.url(args = request.args) + next = self.url(args=request.args) else: next = replace_id(next, form) redirect(next) @@ -2246,10 +2306,10 @@ class Auth(object): if not user: redirect(self.settings.login_url) if self.settings.registration_requires_approval: - user.update_record(registration_key = 'pending') + user.update_record(registration_key='pending') current.session.flash = self.messages.registration_pending else: - user.update_record(registration_key = '') + user.update_record(registration_key='') current.session.flash = self.messages.email_verified # make sure session has same user.registrato_key as db record if current.session.auth and current.session.auth.user: @@ -2261,7 +2321,7 @@ class Auth(object): if onaccept is DEFAULT: onaccept = self.settings.verify_email_onaccept self.log_event(log, user) - callback(onaccept,user) + callback(onaccept, user) redirect(next) def retrieve_username( @@ -2287,7 +2347,7 @@ class Auth(object): response = current.response session = current.session captcha = self.settings.retrieve_username_captcha or \ - (self.settings.retrieve_username_captcha!=False and self.settings.captcha) + (self.settings.retrieve_username_captcha != False and self.settings.captcha) if not self.settings.mailer: response.flash = self.messages.function_disabled return '' @@ -2304,7 +2364,7 @@ class Auth(object): error_message=self.messages.invalid_email)] form = SQLFORM(table_user, fields=['email'], - hidden = dict(_next=next), + hidden=dict(_next=next), showid=self.settings.showid, submit_button=self.messages.submit_button, delete_label=self.messages.delete_label, @@ -2312,11 +2372,12 @@ class Auth(object): separator=self.settings.label_separator ) if captcha: - addrow(form, captcha.label, captcha, captcha.comment,self.settings.formstyle, 'captcha__row') + addrow(form, captcha.label, captcha, + captcha.comment, self.settings.formstyle, 'captcha__row') if form.accepts(request, session, formname='retrieve_username', dbio=False, - onvalidation=onvalidation,hideerror=self.settings.hideerror): + onvalidation=onvalidation, hideerror=self.settings.hideerror): user = table_user(email=form.vars.email) if not user: current.session.flash = \ @@ -2329,9 +2390,9 @@ class Auth(object): % dict(username=username)) session.flash = self.messages.email_sent self.log_event(log, user) - callback(onaccept,form) + callback(onaccept, form) if not next: - next = self.url(args = request.args) + next = self.url(args=request.args) else: next = replace_id(next, form) redirect(next) @@ -2342,13 +2403,13 @@ class Auth(object): import string import random password = '' - specials=r'!#$*' - for i in range(0,3): + specials = r'!#$*' + for i in range(0, 3): password += random.choice(string.lowercase) password += random.choice(string.uppercase) password += random.choice(string.digits) password += random.choice(specials) - return ''.join(random.sample(password,len(password))) + return ''.join(random.sample(password, len(password))) def reset_password_deprecated( self, @@ -2385,7 +2446,7 @@ class Auth(object): error_message=self.messages.invalid_email)] form = SQLFORM(table_user, fields=['email'], - hidden = dict(_next=next), + hidden=dict(_next=next), showid=self.settings.showid, submit_button=self.messages.submit_button, delete_label=self.messages.delete_label, @@ -2394,34 +2455,34 @@ class Auth(object): ) if form.accepts(request, session, formname='retrieve_password', dbio=False, - onvalidation=onvalidation,hideerror=self.settings.hideerror): + onvalidation=onvalidation, hideerror=self.settings.hideerror): user = table_user(email=form.vars.email) if not user: current.session.flash = \ self.messages.invalid_email redirect(self.url(args=request.args)) - elif user.registration_key in ('pending','disabled','blocked'): + elif user.registration_key in ('pending', 'disabled', 'blocked'): current.session.flash = \ self.messages.registration_pending redirect(self.url(args=request.args)) password = self.random_password() passfield = self.settings.password_field d = dict( - passfield = str(table_user[passfield].validate(password)[0]), - registration_key = '') + passfield=str(table_user[passfield].validate(password)[0]), + registration_key='') user.update_record(**d) if self.settings.mailer and \ self.settings.mailer.send(to=form.vars.email, subject=self.messages.retrieve_password_subject, - message=self.messages.retrieve_password \ + message=self.messages.retrieve_password % dict(password=password)): session.flash = self.messages.email_sent else: session.flash = self.messages.unable_to_send_email self.log_event(log, user) - callback(onaccept,form) + callback(onaccept, form) if not next: - next = self.url(args = request.args) + next = self.url(args=request.args) else: next = replace_id(next, form) redirect(next) @@ -2453,9 +2514,11 @@ class Auth(object): try: key = request.vars.key or getarg(-1) t0 = int(key.split('-')[0]) - if time.time()-t0 > 60*60*24: raise Exception + if time.time() - t0 > 60 * 60 * 24: + raise Exception user = table_user(reset_password_key=key) - if not user: raise Exception + if not user: + raise Exception except Exception: session.flash = self.messages.invalid_reset_password redirect(next) @@ -2466,19 +2529,20 @@ class Auth(object): requires=self.table_user()[passfield].requires), Field('new_password2', 'password', label=self.messages.verify_password, - requires=[IS_EXPR('value==%s' % repr(request.vars.new_password), + requires=[IS_EXPR( + 'value==%s' % repr(request.vars.new_password), self.messages.mismatched_password)]), submit_button=self.messages.password_reset_button, - hidden = dict(_next=next), + hidden=dict(_next=next), formstyle=self.settings.formstyle, separator=self.settings.label_separator ) - if form.accepts(request,session, + if form.accepts(request, session, hideerror=self.settings.hideerror): user.update_record( - **{passfield:str(form.vars.new_password), - 'registration_key':'', - 'reset_password_key':''}) + **{passfield: str(form.vars.new_password), + 'registration_key': '', + 'reset_password_key': ''}) session.flash = self.messages.password_changed if self.settings.login_after_password_change: self.login_user(user) @@ -2504,7 +2568,7 @@ class Auth(object): response = current.response session = current.session captcha = self.settings.retrieve_password_captcha or \ - (self.settings.retrieve_password_captcha!=False and self.settings.captcha) + (self.settings.retrieve_password_captcha != False and self.settings.captcha) if next is DEFAULT: next = self.next or self.settings.request_reset_password_next @@ -2523,7 +2587,7 @@ class Auth(object): error_message=self.messages.invalid_email)] form = SQLFORM(table_user, fields=['email'], - hidden = dict(_next=next), + hidden=dict(_next=next), showid=self.settings.showid, submit_button=self.messages.password_reset_button, delete_label=self.messages.delete_label, @@ -2531,7 +2595,8 @@ class Auth(object): separator=self.settings.label_separator ) if captcha: - addrow(form, captcha.label, captcha, captcha.comment, self.settings.formstyle,'captcha__row') + addrow(form, captcha.label, captcha, + captcha.comment, self.settings.formstyle, 'captcha__row') if form.accepts(request, session, formname='reset_password', dbio=False, onvalidation=onvalidation, @@ -2540,7 +2605,7 @@ class Auth(object): if not user: session.flash = self.messages.invalid_email redirect(self.url(args=request.args)) - elif user.registration_key in ('pending','disabled','blocked'): + elif user.registration_key in ('pending', 'disabled', 'blocked'): session.flash = self.messages.registration_pending redirect(self.url(args=request.args)) if self.email_reset_password(user): @@ -2548,25 +2613,25 @@ class Auth(object): else: session.flash = self.messages.unable_to_send_email self.log_event(log, user) - callback(onaccept,form) + callback(onaccept, form) if not next: - next = self.url(args = request.args) + next = self.url(args=request.args) else: next = replace_id(next, form) redirect(next) # old_requires = table_user.email.requires return form - def email_reset_password(self,user): - reset_password_key = str(int(time.time()))+'-' + web2py_uuid() + def email_reset_password(self, user): + reset_password_key = str(int(time.time())) + '-' + web2py_uuid() link = self.url('user', - args=('reset_password',reset_password_key), + args=('reset_password', reset_password_key), scheme=True) if self.settings.mailer.send( to=user.email, subject=self.messages.reset_password_subject, - message=self.messages.reset_password % \ - dict(key=reset_password_key,link=link)): + message=self.messages.reset_password % + dict(key=reset_password_key, link=link)): user.update_record(reset_password_key=reset_password_key) return True return False @@ -2579,9 +2644,9 @@ class Auth(object): log=DEFAULT, ): if self.settings.reset_password_requires_verification: - return self.request_reset_password(next,onvalidation,onaccept,log) + return self.request_reset_password(next, onvalidation, onaccept, log) else: - return self.reset_password_deprecated(next,onvalidation,onaccept,log) + return self.reset_password_deprecated(next, onvalidation, onaccept, log) def change_password( self, @@ -2624,11 +2689,12 @@ class Auth(object): requires=table_user[passfield].requires), Field('new_password2', 'password', label=self.messages.verify_password, - requires=[IS_EXPR('value==%s' % repr(request.vars.new_password), + requires=[IS_EXPR( + 'value==%s' % repr(request.vars.new_password), self.messages.mismatched_password)]), submit_button=self.messages.password_change_button, - hidden = dict(_next=next), - formstyle = self.settings.formstyle, + hidden=dict(_next=next), + formstyle=self.settings.formstyle, separator=self.settings.label_separator ) if form.accepts(request, session, @@ -2643,7 +2709,7 @@ class Auth(object): s.update(**d) session.flash = self.messages.password_changed self.log_event(log, self.user) - callback(onaccept,form) + callback(onaccept, form) if not next: next = self.url(args=request.args) else: @@ -2684,13 +2750,13 @@ class Auth(object): form = SQLFORM( table_user, self.user.id, - fields = self.settings.profile_fields, - hidden = dict(_next=next), - showid = self.settings.showid, - submit_button = self.messages.profile_save_button, - delete_label = self.messages.delete_label, - upload = self.settings.download_url, - formstyle = self.settings.formstyle, + fields=self.settings.profile_fields, + hidden=dict(_next=next), + showid=self.settings.showid, + submit_button=self.messages.profile_save_button, + delete_label=self.messages.delete_label, + upload=self.settings.download_url, + formstyle=self.settings.formstyle, separator=self.settings.label_separator ) if form.accepts(request, session, @@ -2698,8 +2764,8 @@ class Auth(object): onvalidation=onvalidation, hideerror=self.settings.hideerror): self.user.update(table_user._filter_fields(form.vars)) session.flash = self.messages.profile_updated - self.log_event(log,self.user) - callback(onaccept,form) + self.log_event(log, self.user) + callback(onaccept, form) if not next: next = self.url(args=request.args) else: @@ -2745,7 +2811,7 @@ class Auth(object): for callback in self.settings.login_onaccept: callback(form) log = self.messages.impersonate_log - self.log_event(log,dict(id=current_id, other_id=auth.user.id)) + self.log_event(log, dict(id=current_id, other_id=auth.user.id)) elif user_id in (0, '0'): if self.is_impersonating(): session.clear() @@ -2764,7 +2830,8 @@ class Auth(object): current.session.auth.user_groups = self.user_groups table_group = self.table_group() table_membership = self.table_membership() - memberships = self.db(table_membership.user_id==self.user.id).select() + memberships = self.db( + table_membership.user_id == self.user.id).select() for membership in memberships: group = table_group(membership.group_id) if group: @@ -2778,11 +2845,12 @@ class Auth(object): if not self.is_logged_in(): redirect(self.settings.login_url) table_membership = self.table_membership() - memberships = self.db(table_membership.user_id==self.user.id).select() + memberships = self.db( + table_membership.user_id == self.user.id).select() table = TABLE() for membership in memberships: table_group = self.db[self.settings.table_group_name] - groups = self.db(table_group.id==membership.group_id).select() + groups = self.db(table_group.id == membership.group_id).select() if groups: group = groups[0] table.append(TR(H3(group.role, '(%s)' % group.id))) @@ -2796,7 +2864,7 @@ class Auth(object): you can change the view for this page to make it look as you like """ if current.request.ajax: - raise HTTP(403,'ACCESS DENIED') + raise HTTP(403, 'ACCESS DENIED') return 'ACCESS DENIED' def requires(self, condition, requires_login=True, otherwise=None): @@ -2808,7 +2876,7 @@ class Auth(object): def f(*a, **b): - basic_allowed,basic_accepted,user = self.basic() + basic_allowed, basic_accepted, user = self.basic() user = user or self.user if requires_login: if not user: @@ -2818,16 +2886,16 @@ class Auth(object): redirect(otherwise) elif self.settings.allow_basic_login_only or \ basic_accepted or current.request.is_restful: - raise HTTP(403,"Not authorized") + raise HTTP(403, "Not authorized") elif current.request.ajax: - return A('login',_href=self.settings.login_url) + return A('login', _href=self.settings.login_url) else: next = self.here() current.session.flash = current.response.flash return call_or_redirect( self.settings.on_failed_authentication, - self.settings.login_url+\ - '?_next='+urllib.quote(next)) + self.settings.login_url + + '?_next=' + urllib.quote(next)) if callable(condition): flag = condition() @@ -2845,13 +2913,13 @@ class Auth(object): return decorator - def requires_login(self,otherwise=None): + def requires_login(self, otherwise=None): """ decorator that prevents access to action if not logged in """ - return self.requires(True,otherwise=otherwise) + return self.requires(True, otherwise=otherwise) - def requires_membership(self, role=None, group_id=None,otherwise=None): + def requires_membership(self, role=None, group_id=None, otherwise=None): """ decorator that prevents access to action if not logged in or if user logged in is not a member of group_id. @@ -2859,7 +2927,7 @@ class Auth(object): group_id is calculated. """ return self.requires(lambda: self.has_membership( - group_id=group_id, role=role),otherwise=otherwise) + group_id=group_id, role=role), otherwise=otherwise) def requires_permission(self, name, table_name='', record_id=0, otherwise=None): @@ -2869,9 +2937,9 @@ class Auth(object): has 'name' access to 'table_name', 'record_id'. """ return self.requires(lambda: self.has_permission( - name, table_name, record_id),otherwise=otherwise) + name, table_name, record_id), otherwise=otherwise) - def requires_signature(self,otherwise=None): + def requires_signature(self, otherwise=None): """ decorator that prevents access to action if not logged in or if user logged in is not a member of group_id. @@ -2879,7 +2947,7 @@ class Auth(object): group_id is calculated. """ return self.requires(lambda: URL.verify( - current.request,user_signature=True),otherwise=otherwise) + current.request, user_signature=True), otherwise=otherwise) def add_group(self, role, description=''): """ @@ -2900,7 +2968,7 @@ class Auth(object): self.db(self.table_membership().group_id == group_id).delete() self.db(self.table_permission().group_id == group_id).delete() self.update_groups() - self.log_event(self.messages.del_group_log,dict(group_id=group_id)) + self.log_event(self.messages.del_group_log, dict(group_id=group_id)) def id_group(self, role): """ @@ -2911,7 +2979,7 @@ class Auth(object): return None return rows[0].id - def user_group(self, user_id = None): + def user_group(self, user_id=None): """ returns the group_id of the group uniquely associated to this user i.e. role=user:[user_id] @@ -2925,7 +2993,6 @@ class Auth(object): user = self.user return self.settings.create_user_groups % user - def has_membership(self, group_id=None, user_id=None, role=None): """ checks if user is member of group_id or role @@ -2935,7 +3002,7 @@ class Auth(object): try: group_id = int(group_id) except: - group_id = self.id_group(group_id) # interpret group_id as a role + group_id = self.id_group(group_id) # interpret group_id as a role if not user_id and self.user: user_id = self.user.id membership = self.table_membership() @@ -2945,7 +3012,7 @@ class Auth(object): else: r = False self.log_event(self.messages.has_membership_log, - dict(user_id=user_id,group_id=group_id, check=r)) + dict(user_id=user_id, group_id=group_id, check=r)) return r def add_membership(self, group_id=None, user_id=None, role=None): @@ -2958,11 +3025,11 @@ class Auth(object): try: group_id = int(group_id) except: - group_id = self.id_group(group_id) # interpret group_id as a role + group_id = self.id_group(group_id) # interpret group_id as a role if not user_id and self.user: user_id = self.user.id membership = self.table_membership() - record = membership(user_id = user_id,group_id = group_id) + record = membership(user_id=user_id, group_id=group_id) if record: return record.id else: @@ -2983,7 +3050,7 @@ class Auth(object): user_id = self.user.id membership = self.table_membership() self.log_event(self.messages.del_membership_log, - dict(user_id=user_id,group_id=group_id)) + dict(user_id=user_id, group_id=group_id)) ret = self.db(membership.user_id == user_id)(membership.group_id == group_id).delete() @@ -3006,8 +3073,9 @@ class Auth(object): if not group_id and self.settings.everybody_group_id and \ self.has_permission( - name,table_name,record_id,user_id=None, - group_id=self.settings.everybody_group_id): return True + name, table_name, record_id, user_id=None, + group_id=self.settings.everybody_group_id): + return True if not user_id and not group_id and self.user: user_id = self.user.id @@ -3056,9 +3124,8 @@ class Auth(object): permission = self.table_permission() if group_id == 0: group_id = self.user_group() - record = self.db(permission.group_id==group_id)(permission.name==name)\ - (permission.table_name==str(table_name))\ - (permission.record_id==long(record_id)).select().first() + record = self.db(permission.group_id == group_id)(permission.name == name)(permission.table_name == str(table_name))( + permission.record_id == long(record_id)).select().first() if record: id = record.id else: @@ -3104,25 +3171,25 @@ class Auth(object): """ if not user_id: user_id = self.user_id - if isinstance(table,str) and table in self.db.tables(): + if isinstance(table, str) and table in self.db.tables(): table = self.db[table] - if not isinstance(table,str) and\ + if not isinstance(table, str) and\ self.has_permission(name, table, 0, user_id): return table.id > 0 db = self.db membership = self.table_membership() permission = self.table_permission() query = table.id.belongs( - db(membership.user_id == user_id)\ - (membership.group_id == permission.group_id)\ - (permission.name == name)\ - (permission.table_name == table)\ + db(membership.user_id == user_id) + (membership.group_id == permission.group_id) + (permission.name == name) + (permission.table_name == table) ._select(permission.record_id)) if self.settings.everybody_group_id: - query|=table.id.belongs( - db(permission.group_id==self.settings.everybody_group_id)\ - (permission.name == name)\ - (permission.table_name == table)\ + query |= table.id.belongs( + db(permission.group_id == self.settings.everybody_group_id) + (permission.name == name) + (permission.table_name == table) ._select(permission.record_id)) return query @@ -3192,16 +3259,16 @@ class Auth(object): if not archive_table_name in table._db: table._db.define_table( archive_table_name, - Field(current_record,table), + Field(current_record, table), *[field.clone(unique=False) for field in table]) archive_table = table._db[archive_table_name] - new_record = {current_record:form.vars.id} + new_record = {current_record: form.vars.id} for fieldname in archive_table.fields: - if not fieldname in ['id',current_record]: + if not fieldname in ['id', current_record]: if archive_current and fieldname in form.vars: - new_record[fieldname]=form.vars[fieldname] + new_record[fieldname] = form.vars[fieldname] elif form.record and fieldname in form.record: - new_record[fieldname]=form.record[fieldname] + new_record[fieldname] = form.record[fieldname] if fields: new_record.update(fields) id = archive_table.insert(**new_record) @@ -3215,8 +3282,8 @@ class Auth(object): force_prefix='', restrict_search=False, resolve=True): - if not hasattr(self,'_wiki'): - self._wiki = Wiki(self,render=render, + if not hasattr(self, '_wiki'): + self._wiki = Wiki(self, render=render, manage_permissions=manage_permissions, force_prefix=force_prefix, restrict_search=restrict_search, @@ -3228,6 +3295,7 @@ class Auth(object): if resolve: return self._wiki.read(slug)['content'] if slug else self._wiki() + class Crud(object): def url(self, f=None, args=None, vars=None): @@ -3235,16 +3303,18 @@ class Crud(object): this should point to the controller that exposes download and crud """ - if args is None: args=[] - if vars is None: vars={} + if args is None: + args = [] + if vars is None: + vars = {} return URL(c=self.settings.controller, f=f, args=args, vars=vars) def __init__(self, environment, db=None, controller='default'): self.db = db - if not db and environment and isinstance(environment,DAL): + if not db and environment and isinstance(environment, DAL): self.db = environment elif not db: - raise SyntaxError, "must pass db as first or second argument" + raise SyntaxError("must pass db as first or second argument") self.environment = current settings = self.settings = Settings() settings.auth = None @@ -3301,10 +3371,10 @@ class Crud(object): if args[0] == 'create': return self.create(table) elif args[0] == 'select': - return self.select(table,linkto=self.url(args='read')) + return self.select(table, linkto=self.url(args='read')) elif args[0] == 'search': - form, rows = self.search(table,linkto=self.url(args='read')) - return DIV(form,SQLTABLE(rows)) + form, rows = self.search(table, linkto=self.url(args='read')) + return DIV(form, SQLTABLE(rows)) elif args[0] == 'read': return self.read(table, args(2)) elif args[0] == 'update': @@ -3316,7 +3386,7 @@ class Crud(object): def log_event(self, message, vars): if self.settings.logger: - self.settings.logger.log_event(message, vars, origin = 'crud') + self.settings.logger.log_event(message, vars, origin='crud') def has_permission(self, name, table, record=0): if not self.settings.auth: @@ -3329,12 +3399,12 @@ class Crud(object): def tables(self): return TABLE(*[TR(A(name, - _href=self.url(args=('select',name)))) \ + _href=self.url(args=('select', name)))) for name in self.db.tables]) @staticmethod - def archive(form,archive_table=None,current_record='current_record'): - return Auth.archive(form,archive_table=archive_table, + def archive(form, archive_table=None, current_record='current_record'): + return Auth.archive(form, archive_table=archive_table, current_record=current_record) def update( @@ -3410,26 +3480,27 @@ class Crud(object): captcha = self.settings.update_captcha or self.settings.captcha if record and captcha: addrow(form, captcha.label, captcha, captcha.comment, - self.settings.formstyle,'captcha__row') + self.settings.formstyle, 'captcha__row') captcha = self.settings.create_captcha or self.settings.captcha if not record and captcha: addrow(form, captcha.label, captcha, captcha.comment, - self.settings.formstyle,'captcha__row') - if not request.extension in ('html','load'): + self.settings.formstyle, 'captcha__row') + if not request.extension in ('html', 'load'): (_session, _formname) = (None, None) else: - (_session, _formname) = (session, '%s/%s' % (table._tablename, form.record_id)) + (_session, _formname) = ( + session, '%s/%s' % (table._tablename, form.record_id)) if not formname is DEFAULT: _formname = formname keepvalues = self.settings.keepvalues if request.vars.delete_this_record: keepvalues = False - if isinstance(onvalidation,StorageList): - onvalidation=onvalidation.get(table._tablename, []) + if isinstance(onvalidation, StorageList): + onvalidation = onvalidation.get(table._tablename, []) if form.accepts(request, _session, formname=_formname, onvalidation=onvalidation, keepvalues=keepvalues, hideerror=self.settings.hideerror, - detect_record_change = self.settings.detect_record_change): + detect_record_change=self.settings.detect_record_change): self.accepted = True response.flash = message if log: @@ -3437,19 +3508,19 @@ class Crud(object): if request.vars.delete_this_record: self.deleted = True message = self.messages.record_deleted - callback(ondelete,form,table._tablename) + callback(ondelete, form, table._tablename) response.flash = message - callback(onaccept,form,table._tablename) - if not request.extension in ('html','load'): + callback(onaccept, form, table._tablename) + if not request.extension in ('html', 'load'): raise HTTP(200, 'RECORD CREATED/UPDATED') - if isinstance(next, (list, tuple)): ### fix issue with 2.6 - next = next[0] - if next: # Only redirect when explicit + if isinstance(next, (list, tuple)): # fix issue with 2.6 + next = next[0] + if next: # Only redirect when explicit next = replace_id(next, form) session.flash = response.flash redirect(next) - elif not request.extension in ('html','load'): - raise HTTP(401,serializers.json(dict(errors=form.errors))) + elif not request.extension in ('html', 'load'): + raise HTTP(401, serializers.json(dict(errors=form.errors))) return form def create( @@ -3509,7 +3580,7 @@ class Crud(object): formstyle=self.settings.formstyle, separator=self.settings.label_separator ) - if not current.request.extension in ('html','load'): + if not current.request.extension in ('html', 'load'): return table._filter_fields(form.record, id=True) return form @@ -3540,9 +3611,9 @@ class Crud(object): message = self.messages.record_deleted record = table[record_id] if record: - callback(self.settings.delete_onvalidation,record) + callback(self.settings.delete_onvalidation, record) del table[record_id] - callback(self.settings.delete_onaccept,record,table._tablename) + callback(self.settings.delete_onaccept, record, table._tablename) session.flash = message redirect(next) @@ -3567,8 +3638,8 @@ class Crud(object): if not fields: fields = [field for field in table if field.readable] else: - fields = [table[f] if isinstance(f,str) else f for f in fields] - rows = self.db(query).select(*fields,**dict(orderby=orderby, + fields = [table[f] if isinstance(f, str) else f for f in fields] + rows = self.db(query).select(*fields, **dict(orderby=orderby, limitby=limitby)) return rows @@ -3583,18 +3654,18 @@ class Crud(object): **attr ): headers = headers or {} - rows = self.rows(table,query,fields,orderby,limitby) + rows = self.rows(table, query, fields, orderby, limitby) if not rows: - return None # Nicer than an empty table. + return None # Nicer than an empty table. if not 'upload' in attr: attr['upload'] = self.url('download') - if not current.request.extension in ('html','load'): + if not current.request.extension in ('html', 'load'): return rows.as_list() if not headers: - if isinstance(table,str): + if isinstance(table, str): table = self.db[table] - headers = dict((str(k),k.label) for k in table) - return SQLTABLE(rows,headers=headers,**attr) + headers = dict((str(k), k.label) for k in table) + return SQLTABLE(rows, headers=headers, **attr) def get_format(self, field): rtable = field._db[field.type[10:]] @@ -3605,7 +3676,8 @@ class Crud(object): def get_query(self, field, op, value, refsearch=False): try: - if refsearch: format = self.get_format(field) + if refsearch: + format = self.get_format(field) if op == 'equals': if not refsearch: return field == value @@ -3628,17 +3700,17 @@ class Crud(object): return lambda row: row[field.name][format] < value elif op == 'starts with': if not refsearch: - return field.like(value+'%') + return field.like(value + '%') else: return lambda row: str(row[field.name][format]).startswith(value) elif op == 'ends with': if not refsearch: - return field.like('%'+value) + return field.like('%' + value) else: return lambda row: str(row[field.name][format]).endswith(value) elif op == 'contains': if not refsearch: - return field.like('%'+value+'%') + return field.like('%' + value + '%') else: return lambda row: value in row[field.name][format] except: @@ -3653,7 +3725,8 @@ class Crud(object): query_labels={'equals':'Equals', 'not equal':'Not equal'}, fields = ['id','children'], - field_labels = {'id':'ID','children':'Children'}, + field_labels = { + 'id':'ID','children':'Children'}, zero='Please choose', query = (db.test.id > 0)&(db.test.id != 3) ) """ @@ -3664,47 +3737,51 @@ class Crud(object): if not (isinstance(table, db.Table) or table in db.tables): raise HTTP(404) attributes = {} - for key in ('orderby','groupby','left','distinct','limitby','cache'): - if key in args: attributes[key]=args[key] + for key in ('orderby', 'groupby', 'left', 'distinct', 'limitby', 'cache'): + if key in args: + attributes[key] = args[key] tbl = TABLE() - selected = []; refsearch = []; results = [] + selected = [] + refsearch = [] + results = [] showall = args.get('showall', False) if showall: selected = fields chkall = args.get('chkall', False) if chkall: for f in fields: - request.vars['chk%s'%f] = 'on' + request.vars['chk%s' % f] = 'on' ops = args.get('queries', []) zero = args.get('zero', '') if not ops: ops = ['equals', 'not equal', 'greater than', 'less than', 'starts with', 'ends with', 'contains'] - ops.insert(0,zero) + ops.insert(0, zero) query_labels = args.get('query_labels', {}) - query = args.get('query',table.id > 0) - field_labels = args.get('field_labels',{}) + query = args.get('query', table.id > 0) + field_labels = args.get('field_labels', {}) for field in fields: field = table[field] - if not field.readable: continue + if not field.readable: + continue fieldname = field.name chkval = request.vars.get('chk' + fieldname, None) txtval = request.vars.get('txt' + fieldname, None) opval = request.vars.get('op' + fieldname, None) - row = TR(TD(INPUT(_type = "checkbox", _name = "chk" + fieldname, - _disabled = (field.type == 'id'), - value = (field.type == 'id' or chkval == 'on'))), - TD(field_labels.get(fieldname,field.label)), - TD(SELECT([OPTION(query_labels.get(op,op), + row = TR(TD(INPUT(_type="checkbox", _name="chk" + fieldname, + _disabled=(field.type == 'id'), + value=(field.type == 'id' or chkval == 'on'))), + TD(field_labels.get(fieldname, field.label)), + TD(SELECT([OPTION(query_labels.get(op, op), _value=op) for op in ops], - _name = "op" + fieldname, - value = opval)), - TD(INPUT(_type = "text", _name = "txt" + fieldname, - _value = txtval, _id='txt' + fieldname, - _class = str(field.type)))) + _name="op" + fieldname, + value=opval)), + TD(INPUT(_type="text", _name="txt" + fieldname, + _value=txtval, _id='txt' + fieldname, + _class=str(field.type)))) tbl.append(row) - if request.post_vars and (chkval or field.type=='id'): + if request.post_vars and (chkval or field.type == 'id'): if txtval and opval != '': if field.type[0:10] == 'reference ': refsearch.append(self.get_query(field, @@ -3715,29 +3792,32 @@ class Crud(object): ### TODO deal with 'starts with', 'ends with', 'contains' on GAE query &= self.get_query(field, opval, value) else: - row[3].append(DIV(error,_class='error')) + row[3].append(DIV(error, _class='error')) selected.append(field) - form = FORM(tbl,INPUT(_type="submit")) + form = FORM(tbl, INPUT(_type="submit")) if selected: try: - results = db(query).select(*selected,**attributes) + results = db(query).select(*selected, **attributes) for r in refsearch: results = results.find(r) - except: # hmmm, we should do better here + except: # hmmm, we should do better here results = None return form, results urllib2.install_opener(urllib2.build_opener(urllib2.HTTPCookieProcessor())) + def fetch(url, data=None, headers=None, cookie=Cookie.SimpleCookie(), user_agent='Mozilla/5.0'): headers = headers or {} if not data is None: data = urllib.urlencode(data) - if user_agent: headers['User-agent'] = user_agent - headers['Cookie'] = ' '.join(['%s=%s;'%(c.key,c.value) for c in cookie.values()]) + if user_agent: + headers['User-agent'] = user_agent + headers['Cookie'] = ' '.join( + ['%s=%s;' % (c.key, c.value) for c in cookie.values()]) try: from google.appengine.api import urlfetch except ImportError: @@ -3748,7 +3828,7 @@ def fetch(url, data=None, headers=None, while url is not None: response = urlfetch.fetch(url=url, payload=data, method=method, headers=headers, - allow_truncated=False,follow_redirects=False, + allow_truncated=False, follow_redirects=False, deadline=10) # next request will be a get, so no need to send the data again data = None @@ -3762,6 +3842,7 @@ def fetch(url, data=None, headers=None, regex_geocode = \ re.compile(r"""[\W]*?[\W]*?(?P[^<]*)[\W]*?(?P[^<]*)[\W]*?""") + def geocode(address): try: a = urllib.quote(address) @@ -3786,7 +3867,8 @@ def universal_caller(f, *a, **b): # Fill the arg_dict with name and value for the submitted, positional values for pos_index, pos_val in enumerate(a[:c]): - arg_dict[n[pos_index]] = pos_val # n[pos_index] is the name of the argument + arg_dict[n[pos_index] + ] = pos_val # n[pos_index] is the name of the argument # There might be pos_args left, that are sent as named_values. Gather them as well. # If a argument already is populated with values we simply replaces them. @@ -3992,17 +4074,17 @@ class Service(object): """ if not isinstance(domain, str): - raise SyntaxError, "AMF3 requires a domain for function" + raise SyntaxError("AMF3 requires a domain for function") def _amfrpc3(f): if domain: - self.amfrpc3_procedures[domain+'.'+f.__name__] = f + self.amfrpc3_procedures[domain + '.' + f.__name__] = f else: self.amfrpc3_procedures[f.__name__] = f return f return _amfrpc3 - def soap(self, name=None, returns=None, args=None,doc=None): + def soap(self, name=None, returns=None, args=None, doc=None): """ example: @@ -4064,7 +4146,7 @@ class Service(object): writer = csv.writer(s) writer.writerow(r[0].keys()) for line in r: - writer.writerow([none_exception(v) \ + writer.writerow([none_exception(v) for v in line.values()]) else: import csv @@ -4085,7 +4167,7 @@ class Service(object): *args[1:], **dict(request.vars)) if hasattr(s, 'as_list'): s = s.as_list() - return serializers.xml(s,quote=False) + return serializers.xml(s, quote=False) self.error() def serve_rss(self, args=None): @@ -4109,20 +4191,21 @@ class Service(object): args = request.args d = dict(request.vars) if args and args[0] in self.json_procedures: - s = universal_caller(self.json_procedures[args[0]],*args[1:],**d) + s = universal_caller(self.json_procedures[args[0]], *args[1:], **d) if hasattr(s, 'as_list'): s = s.as_list() return response.json(s) self.error() class JsonRpcException(Exception): - def __init__(self,code,info): - self.code,self.info = code,info + def __init__(self, code, info): + self.code, self.info = code, info def serve_jsonrpc(self): def return_response(id, result): return serializers.json({'version': '1.1', 'id': id, 'result': result, 'error': None}) + def return_error(id, code, message, data=None): error = {'name': 'JSONRPCError', 'code': code, 'message': message} @@ -4138,7 +4221,7 @@ class Service(object): response.headers['Content-Type'] = 'application/json; charset=utf-8' methods = self.jsonrpc_procedures data = json_parser.loads(request.body.read()) - id, method, params = data['id'], data['method'], data.get('params','') + id, method, params = data['id'], data['method'], data.get('params', '') if not method in methods: return return_error(id, 100, 'method "%s" does not exist' % method) try: @@ -4185,7 +4268,7 @@ class Service(object): for name, message in pyamf_request: pyamf_response[name] = base_gateway.getProcessor(message)(message) response.headers['Content-Type'] = pyamf.remoting.CONTENT_TYPE - if version==3: + if version == 3: return pyamf.remoting.encode(pyamf_response).getvalue() else: return pyamf.remoting.encode(pyamf_response, context).getvalue() @@ -4202,17 +4285,17 @@ class Service(object): location = "%s://%s%s" % ( request.env.wsgi_url_scheme, request.env.http_host, - URL(r=request,f="call/soap",vars={})) + URL(r=request, f="call/soap", vars={})) namespace = 'namespace' in response and response.namespace or location documentation = response.description or '' dispatcher = SoapDispatcher( - name = response.title, - location = location, - action = location, # SOAPAction - namespace = namespace, + name=response.title, + location=location, + action=location, # SOAPAction + namespace=namespace, prefix='pys', - documentation = documentation, - ns = True) + documentation=documentation, + ns=True) for method, (function, returns, args, doc) in procedures.iteritems(): dispatcher.register_function(method, function, returns, args, doc) if request.env.request_method == 'POST': @@ -4226,11 +4309,11 @@ class Service(object): elif 'op' in request.vars: # Return method help webpage response.headers['Content-Type'] = 'text/html' - method = request.vars['op'] + method = request.vars['op'] sample_req_xml, sample_res_xml, doc = dispatcher.help(method) body = [H1("Welcome to Web2Py SOAP webservice gateway"), A("See all webservice operations", - _href=URL(r=request,f="call/soap",vars={})), + _href=URL(r=request, f="call/soap", vars={})), H2(method), P(doc), UL(LI("Location: %s" % dispatcher.location), @@ -4238,9 +4321,9 @@ class Service(object): LI("SoapAction: %s" % dispatcher.action), ), H3("Sample SOAP XML Request Message:"), - CODE(sample_req_xml,language="xml"), + CODE(sample_req_xml, language="xml"), H3("Sample SOAP XML Response Message:"), - CODE(sample_res_xml,language="xml"), + CODE(sample_res_xml, language="xml"), ] return {'body': body} else: @@ -4250,9 +4333,9 @@ class Service(object): P(response.description), P("The following operations are available"), A("See WSDL for webservice description", - _href=URL(r=request,f="call/soap",vars={"WSDL":None})), + _href=URL(r=request, f="call/soap", vars={"WSDL":None})), UL([LI(A("%s: %s" % (method, doc or ''), - _href=URL(r=request,f="call/soap",vars={'op': method}))) + _href=URL(r=request, f="call/soap", vars={'op': method}))) for method, doc in dispatcher.list_methods()]), ] return {'body': body} @@ -4329,20 +4412,21 @@ def completion(callback): The argument of completion is executed in a new thread. """ def _completion(f): - def __completion(*a,**b): + def __completion(*a, **b): d = None try: - d = f(*a,**b) + d = f(*a, **b) return d finally: - thread.start_new_thread(callback,(d,)) + thread.start_new_thread(callback, (d,)) return __completion return _completion -def prettydate(d,T=lambda x:x): - if isinstance(d,datetime.datetime): + +def prettydate(d, T=lambda x: x): + if isinstance(d, datetime.datetime): dt = datetime.datetime.now() - d - elif isinstance(d,datetime.date): + elif isinstance(d, datetime.date): dt = datetime.date.today() - d elif not d: return '' @@ -4353,55 +4437,57 @@ def prettydate(d,T=lambda x:x): dt = -dt else: suffix = ' ago' - if dt.days >= 2*365: - return T('%d years'+suffix) % int(dt.days / 365) + if dt.days >= 2 * 365: + return T('%d years' + suffix) % int(dt.days / 365) elif dt.days >= 365: - return T('1 year'+suffix) + return T('1 year' + suffix) elif dt.days >= 60: - return T('%d months'+suffix) % int(dt.days / 30) + return T('%d months' + suffix) % int(dt.days / 30) elif dt.days > 21: - return T('1 month'+suffix) + return T('1 month' + suffix) elif dt.days >= 14: - return T('%d weeks'+suffix) % int(dt.days / 7) + return T('%d weeks' + suffix) % int(dt.days / 7) elif dt.days >= 7: - return T('1 week'+suffix) + return T('1 week' + suffix) elif dt.days > 1: - return T('%d days'+suffix) % dt.days + return T('%d days' + suffix) % dt.days elif dt.days == 1: - return T('1 day'+suffix) - elif dt.seconds >= 2*60*60: - return T('%d hours'+suffix) % int(dt.seconds / 3600) - elif dt.seconds >= 60*60: - return T('1 hour'+suffix) - elif dt.seconds >= 2*60: - return T('%d minutes'+suffix) % int(dt.seconds / 60) + return T('1 day' + suffix) + elif dt.seconds >= 2 * 60 * 60: + return T('%d hours' + suffix) % int(dt.seconds / 3600) + elif dt.seconds >= 60 * 60: + return T('1 hour' + suffix) + elif dt.seconds >= 2 * 60: + return T('%d minutes' + suffix) % int(dt.seconds / 60) elif dt.seconds >= 60: - return T('1 minute'+suffix) + return T('1 minute' + suffix) elif dt.seconds > 1: - return T('%d seconds'+suffix) % dt.seconds + return T('%d seconds' + suffix) % dt.seconds elif dt.seconds == 1: - return T('1 second'+suffix) + return T('1 second' + suffix) else: return T('now') + def test_thread_separation(): def f(): - c=PluginManager() + c = PluginManager() lock1.acquire() lock2.acquire() - c.x=7 + c.x = 7 lock1.release() lock2.release() - lock1=thread.allocate_lock() - lock2=thread.allocate_lock() + lock1 = thread.allocate_lock() + lock2 = thread.allocate_lock() lock1.acquire() - thread.start_new_thread(f,()) - a=PluginManager() - a.x=5 + thread.start_new_thread(f, ()) + a = PluginManager() + a.x = 5 lock1.release() lock2.acquire() return a.x + class PluginManager(object): """ @@ -4456,7 +4542,8 @@ class PluginManager(object): True """ instances = {} - def __new__(cls,*a,**b): + + def __new__(cls, *a, **b): id = thread.get_ident() lock = thread.allocate_lock() try: @@ -4464,77 +4551,83 @@ class PluginManager(object): try: return cls.instances[id] except KeyError: - instance = object.__new__(cls,*a,**b) + instance = object.__new__(cls, *a, **b) cls.instances[id] = instance return instance finally: lock.release() - def __init__(self,plugin=None,**defaults): + + def __init__(self, plugin=None, **defaults): if not plugin: self.__dict__.clear() settings = self.__getattr__(plugin) settings.installed = True settings.update( - (k,v) for k,v in defaults.items() if not k in settings) + (k, v) for k, v in defaults.items() if not k in settings) def __getattr__(self, key): if not key in self.__dict__: self.__dict__[key] = Storage() return self.__dict__[key] + def keys(self): return self.__dict__.keys() - def __contains__(self,key): + + def __contains__(self, key): return key in self.__dict__ + class Expose(object): - def __init__(self,base=None,basename='base'): + def __init__(self, base=None, basename='base'): current.session.forget() - base = base or os.path.join(current.request.folder,'static') + base = base or os.path.join(current.request.folder, 'static') self.basename = basename args = self.args = current.request.raw_args and \ current.request.raw_args.split('/') or [] - filename = os.path.join(base,*args) + filename = os.path.join(base, *args) if not os.path.normpath(filename).startswith(base): - raise HTTP(401,"NOT AUTHORIZED") + raise HTTP(401, "NOT AUTHORIZED") if not os.path.isdir(filename): current.response.headers['Content-Type'] = contenttype(filename) - raise HTTP(200,open(filename,'rb'),**current.response.headers) - self.path = path = os.path.join(filename,'*') - self.folders = [f[len(path)-1:] for f in sorted(glob.glob(path)) \ + raise HTTP(200, open(filename, 'rb'), **current.response.headers) + self.path = path = os.path.join(filename, '*') + self.folders = [f[len(path) - 1:] for f in sorted(glob.glob(path)) if os.path.isdir(f) and not self.isprivate(f)] - self.filenames = [f[len(path)-1:] for f in sorted(glob.glob(path)) \ + self.filenames = [f[len(path) - 1:] for f in sorted(glob.glob(path)) if not os.path.isdir(f) and not self.isprivate(f)] def breadcrumbs(self, basename): path = [] span = SPAN() - span.append(A(basename,_href=URL())) + span.append(A(basename, _href=URL())) span.append('/') args = current.request.raw_args and \ current.request.raw_args.split('/') or [] for arg in args: path.append(arg) - span.append(A(arg,_href=URL(args='/'.join(path)))) + span.append(A(arg, _href=URL(args='/'.join(path)))) span.append('/') return span def table_folders(self): - return TABLE(*[TR(TD(A(folder,_href=URL(args=self.args+[folder])))) \ + return TABLE(*[TR(TD(A(folder, _href=URL(args=self.args + [folder])))) for folder in self.folders]) + @staticmethod def isprivate(f): return 'private' in f or f.startswith('.') or f.endswith('~') @staticmethod def isimage(f): - return f.rsplit('.')[-1].lower() in ('png','jpg','jpeg','gif','tiff') + return f.rsplit('.')[-1].lower() in ('png', 'jpg', 'jpeg', 'gif', 'tiff') - def table_files(self,width=160): - return TABLE(*[TR(TD(A(f,_href=URL(args=self.args+[f]))), - TD(IMG(_src=URL(args=self.args+[f]), - _style='max-width:%spx' % width) \ - if width and self.isimage(f) else '')) \ + def table_files(self, width=160): + return TABLE(*[TR(TD(A(f, _href=URL(args=self.args + [f]))), + TD(IMG(_src=URL(args=self.args + [f]), + _style='max-width:%spx' % width) + if width and self.isimage(f) else '')) for f in self.filenames]) + def xml(self): return DIV( H2(self.breadcrumbs(self.basename)), @@ -4547,22 +4640,25 @@ class Expose(object): class Wiki(object): everybody = 'everybody' rows_page = 25 - def markmin_render(self,page): - html = MARKMIN(page.body,url=True,environment=self.env, - autolinks=lambda link: expand_one(link,{})).xml() + + def markmin_render(self, page): + html = MARKMIN(page.body, url=True, environment=self.env, + autolinks=lambda link: expand_one(link, {})).xml() html += DIV(_class='w2p_wiki_tags', - *[A(t.strip(),_href=URL(args='_search',vars=dict(q=t))) + *[A(t.strip(), _href=URL(args='_search', vars=dict(q=t))) for t in page.tags or [] if t.strip()]).xml() return html - def html_render(self,page): + + def html_render(self, page): html = page.body # @///function -> http://..../function - html = replace_at_urls(html,URL) + html = replace_at_urls(html, URL) # http://...jpg -> Preview'); var preview = $('
').hide(); var table = $('form'); @@ -4810,11 +4928,13 @@ class Wiki(object): prevbutton.on('click', function(e) { e.preventDefault(); if (prevbutton.hasClass('nopreview')) { - prevbutton.addClass('preview').removeClass('nopreview').html('Edit Source'); + prevbutton.addClass('preview').removeClass( + 'nopreview').html('Edit Source'); web2py_ajax_page('post', '%(url)s', {body : $('#wiki_page_body').val()}, 'preview'); table.fadeOut('medium', function() {preview.fadeIn()}); } else { - prevbutton.addClass('nopreview').removeClass('preview').html('Preview'); + prevbutton.addClass( + 'nopreview').removeClass('preview').html('Preview'); preview.fadeOut('medium', function() {table.fadeIn()}); } }) @@ -4822,191 +4942,204 @@ class Wiki(object): """ % dict(url=URL(args=('_preview'))) return dict(content=TAG[''](form, SCRIPT(script))) - def editmedia(self,slug): + def editmedia(self, slug): auth = self.auth db = auth.db page = db.wiki_page(slug=slug) - if not (page and self.can_edit(page)): return self.not_authorized(page) - self.auth.db.wiki_media.id.represent = lambda id,row: \ + if not (page and self.can_edit(page)): + return self.not_authorized(page) + self.auth.db.wiki_media.id.represent = lambda id, row: \ id if not row.filename else \ - SPAN('@////%i/%s.%s' % \ - (id,IS_SLUG.urlify(row.title.split('.')[0]), + SPAN('@////%i/%s.%s' % + (id, IS_SLUG.urlify(row.title.split('.')[0]), row.filename.split('.')[-1])) self.auth.db.wiki_media.wiki_page.default = page.id self.auth.db.wiki_media.wiki_page.writable = False content = SQLFORM.grid( - self.auth.db.wiki_media.wiki_page==page.id, - orderby = self.auth.db.wiki_media.title, - args=['_editmedia',slug], + self.auth.db.wiki_media.wiki_page == page.id, + orderby=self.auth.db.wiki_media.title, + args=['_editmedia', slug], user_signature=False) return dict(content=content) + def create(self): - if not self.can_edit(): return self.not_authorized() + if not self.can_edit(): + return self.not_authorized() db = self.auth.db - form = FORM(INPUT(_name='slug',value=current.request.args(1), + form = FORM(INPUT(_name='slug', value=current.request.args(1), requires=(IS_SLUG(), - IS_NOT_IN_DB(db,db.wiki_page.slug))), + IS_NOT_IN_DB(db, db.wiki_page.slug))), INPUT(_type='submit', _value=current.T('Create Page from Slug'))) if form.process().accepted: - redirect(URL(args=('_edit',form.vars.slug))) + redirect(URL(args=('_edit', form.vars.slug))) return dict(content=form) + def pages(self): if not self.can_manage(): return self.not_authorized() - self.auth.db.wiki_page.id.represent = lambda id,row:SPAN('@////%s' % row.slug) - self.auth.db.wiki_page.title.represent = lambda title,row: \ - A(title,_href=URL(args=row.slug)) - content=SQLFORM.grid( + self.auth.db.wiki_page.id.represent = lambda id, row: SPAN( + '@////%s' % row.slug) + self.auth.db.wiki_page.title.represent = lambda title, row: \ + A(title, _href=URL(args=row.slug)) + content = SQLFORM.grid( self.auth.db.wiki_page, - links = [ - lambda row: \ - A('edit',_href=URL(args=('_edit',row.slug))), - lambda row: \ - A('media',_href=URL(args=('_editmedia',row.slug)))], - details=False,editable=False,deletable=False,create=False, + links=[ + lambda row: + A('edit', _href=URL(args=('_edit', row.slug))), + lambda row: + A('media', _href=URL(args=('_editmedia', row.slug)))], + details=False, editable=False, deletable=False, create=False, orderby=self.auth.db.wiki_page.title, args=['_pages'], user_signature=False) return dict(content=content) + def media(self, id): request, db = current.request, self.auth.db media = db.wiki_media(id) if media: if self.manage_permissions: page = db.wiki_page(media.wiki_page) - if not self.can_read(page): return self.not_authorized(page) + if not self.can_read(page): + return self.not_authorized(page) request.args = [media.filename] - return current.response.download(request,db) + return current.response.download(request, db) else: raise HTTP(404) - def menu(self,controller='default',function='index'): + + def menu(self, controller='default', function='index'): db = self.auth.db request = current.request menu_page = db.wiki_page(slug='wiki-menu') menu = [] if menu_page: - tree = {'':menu} + tree = {'': menu} regex = re.compile('[\r\n\t]*(?P(\s*\-\s*)+)(?P\w.*?)\s+\>\s+(?P<link>\S+)') for match in regex.finditer(self.fix_hostname(menu_page.body)): - base = match.group('base').replace(' ','') + base = match.group('base').replace(' ', '') title = match.group('title') link = match.group('link') if link.startswith('@'): items = link[2:].split('/') - if len(items)>3: - link = URL(a=items[0] or None,c=items[1] or None,f=items[2] or None, args=items[3:]) - parent = tree.get(base[1:],tree['']) + if len(items) > 3: + link = URL(a=items[0] or None, c=items[1] or None, + f=items[2] or None, args=items[3:]) + parent = tree.get(base[1:], tree['']) subtree = [] tree[base] = subtree - parent.append((current.T(title),False,link,subtree)) + parent.append((current.T(title), False, link, subtree)) if True: submenu = [] - menu.append((current.T('[Wiki]'),None,None,submenu)) - if URL() == URL(controller,function): + menu.append((current.T('[Wiki]'), None, None, submenu)) + if URL() == URL(controller, function): if not str(request.args(0)).startswith('_'): slug = request.args(0) or 'index' - mode=1 - elif request.args(0)=='_edit': + mode = 1 + elif request.args(0) == '_edit': slug = request.args(1) or 'index' - mode=2 - elif request.args(0)=='_editmedia': + mode = 2 + elif request.args(0) == '_editmedia': slug = request.args(1) or 'index' - mode=3 + mode = 3 else: - mode=0 - if mode in (2,3): - submenu.append((current.T('View Page'),None, - URL(controller,function,args=slug))) - if mode in (1,3): - submenu.append((current.T('Edit Page'),None, - URL(controller,function,args=('_edit',slug)))) - if mode in (1,2): - submenu.append((current.T('Edit Page Media'),None, - URL(controller,function,args=('_editmedia',slug)))) + mode = 0 + if mode in (2, 3): + submenu.append((current.T('View Page'), None, + URL(controller, function, args=slug))) + if mode in (1, 3): + submenu.append((current.T('Edit Page'), None, + URL(controller, function, args=('_edit', slug)))) + if mode in (1, 2): + submenu.append((current.T('Edit Page Media'), None, + URL(controller, function, args=('_editmedia', slug)))) - submenu.append((current.T('Create New Page'),None, - URL(controller,function,args=('_create')))) + submenu.append((current.T('Create New Page'), None, + URL(controller, function, args=('_create')))) if self.can_manage(): - submenu.append((current.T('Manage Pages'),None, - URL(controller,function,args=('_pages')))) - submenu.append((current.T('Edit Menu'),None, - URL(controller,function,args=('_edit','wiki-menu')))) - submenu.append((current.T('Search Pages'),None, - URL(controller,function,args=('_search')))) + submenu.append((current.T('Manage Pages'), None, + URL(controller, function, args=('_pages')))) + submenu.append((current.T('Edit Menu'), None, + URL(controller, function, args=('_edit', 'wiki-menu')))) + submenu.append((current.T('Search Pages'), None, + URL(controller, function, args=('_search')))) return menu - def search(self,tags=None,query=None,cloud=True,preview=True, - limitby=(0,100),orderby=None): - if not self.can_search(): return self.not_authorized() + def search(self, tags=None, query=None, cloud=True, preview=True, + limitby=(0, 100), orderby=None): + if not self.can_search(): + return self.not_authorized() request = current.request content = CAT() if tags is None and query is None: - form = FORM(INPUT(_name='q',requires=IS_NOT_EMPTY(), + form = FORM(INPUT(_name='q', requires=IS_NOT_EMPTY(), value=request.vars.q), - INPUT(_type="submit",_value=current.T('Search')), + INPUT(_type="submit", _value=current.T('Search')), _method='GET') - content.append(DIV(form,_class='w2p_wiki_form')) + content.append(DIV(form, _class='w2p_wiki_form')) if request.vars.q: tags = [v.strip() for v in request.vars.q.split(',')] tags = [v for v in tags if v] if tags or not query is None: db = self.auth.db count = db.wiki_tag.wiki_page.count() - fields = [db.wiki_page.id,db.wiki_page.slug, - db.wiki_page.title,db.wiki_page.tags, + fields = [db.wiki_page.id, db.wiki_page.slug, + db.wiki_page.title, db.wiki_page.tags, db.wiki_page.can_read] if preview: fields.append(db.wiki_page.body) if query is None: - query = (db.wiki_page.id==db.wiki_tag.wiki_page)&\ + query = (db.wiki_page.id == db.wiki_tag.wiki_page) &\ (db.wiki_tag.name.belongs(tags)) - query = query|db.wiki_page.title.contains(request.vars.q) + query = query | db.wiki_page.title.contains(request.vars.q) if self.restrict_search and not self.manage(): - query = query&(db.wiki_page.created_by==self.auth.user_id) + query = query & (db.wiki_page.created_by == self.auth.user_id) pages = db(query).select(count, - *fields,**dict(orderby=orderby or ~count, - groupby=reduce(lambda a,b:a|b,fields), + *fields, **dict(orderby=orderby or ~count, + groupby=reduce(lambda a, b: a | b, fields), distinct=True, limitby=limitby)) - if request.extension in ('html','load'): + if request.extension in ('html', 'load'): if not pages: content.append(DIV(current.T("No results"), _class='w2p_wiki_form')) + def link(t): - return A(t,_href=URL(args='_search',vars=dict(q=t))) - items = [DIV(H3(A(p.wiki_page.title,_href=URL( + return A(t, _href=URL(args='_search', vars=dict(q=t))) + items = [DIV(H3(A(p.wiki_page.title, _href=URL( args=p.wiki_page.slug))), - MARKMIN(self.first_paragraph(p.wiki_page)) \ + MARKMIN(self.first_paragraph(p.wiki_page)) if preview else '', DIV(_class='w2p_wiki_tags', - *[link(t.strip()) for t in \ + *[link(t.strip()) for t in p.wiki_page.tags or [] if t.strip()]), _class='w2p_wiki_search_item') for p in pages] - content.append(DIV(_class='w2p_wiki_pages',*items)) + content.append(DIV(_class='w2p_wiki_pages', *items)) else: - cloud=False + cloud = False content = [p.wiki_page.as_dict() for p in pages] elif cloud: content.append(self.cloud()['content']) - if request.extension=='load': + if request.extension == 'load': return content return dict(content=content) + def cloud(self): db = self.auth.db count = db.wiki_tag.wiki_page.count(distinct=True) ids = db(db.wiki_tag).select( - db.wiki_tag.name,count, + db.wiki_tag.name, count, distinct=True, - groupby = db.wiki_tag.name, - orderby = ~count, limitby=(0,20)) + groupby=db.wiki_tag.name, + orderby=~count, limitby=(0, 20)) if ids: - a,b = ids[0](count), ids[-1](count) + a, b = ids[0](count), ids[-1](count) + def style(c): - STYLE ='padding:0 0.2em;line-height:%.2fem;font-size:%.2fem' - size = (1.5*(c-b)/max(a-b,1)+1.3) - return STYLE % (1.3,size) + STYLE = 'padding:0 0.2em;line-height:%.2fem;font-size:%.2fem' + size = (1.5 * (c - b) / max(a - b, 1) + 1.3) + return STYLE % (1.3, size) items = [] for item in ids: items.append(A(item.wiki_tag.name, @@ -5014,7 +5147,8 @@ class Wiki(object): _href=URL(args='_search', vars=dict(q=item.wiki_tag.name)))) items.append(' ') - return dict(content = DIV(_class='w2p_cloud',*items)) + return dict(content=DIV(_class='w2p_cloud', *items)) + def preview(self, render): request = current.request return render(request.post_vars) diff --git a/gluon/utf8.py b/gluon/utf8.py index 5882c400..111327aa 100644 --- a/gluon/utf8.py +++ b/gluon/utf8.py @@ -14,18 +14,20 @@ Utilities and class for UTF8 strings managing import __builtin__ __all__ = ['Utf8'] -repr_escape_tab={} -for i in range(1,32): repr_escape_tab[i]=ur'\x%02x'%i -repr_escape_tab[7]=u'\\a' -repr_escape_tab[8]=u'\\b' -repr_escape_tab[9]=u'\\t' -repr_escape_tab[10]=u'\\n' -repr_escape_tab[11]=u'\\v' -repr_escape_tab[12]=u'\\f' -repr_escape_tab[13]=u'\\r' -repr_escape_tab[ord('\\')]=u'\\\\' -repr_escape_tab2=repr_escape_tab.copy() -repr_escape_tab2[ord('\'')]=u"\\'" +repr_escape_tab = {} +for i in range(1, 32): + repr_escape_tab[i] = ur'\x%02x' % i +repr_escape_tab[7] = u'\\a' +repr_escape_tab[8] = u'\\b' +repr_escape_tab[9] = u'\\t' +repr_escape_tab[10] = u'\\n' +repr_escape_tab[11] = u'\\v' +repr_escape_tab[12] = u'\\f' +repr_escape_tab[13] = u'\\r' +repr_escape_tab[ord('\\')] = u'\\\\' +repr_escape_tab2 = repr_escape_tab.copy() +repr_escape_tab2[ord('\'')] = u"\\'" + def sort_key(s): """ Unicode Collation Algorithm (UCA) (http://www.unicode.org/reports/tr10/) @@ -45,10 +47,11 @@ def sort_key(s): try: from contrib.pyuca import unicode_collator unicode_sort_key = unicode_collator.sort_key - sort_key=lambda s: unicode_sort_key( - unicode(s, 'utf-8') if isinstance(s, str) else s) + sort_key = lambda s: unicode_sort_key( + unicode(s, 'utf-8') if isinstance(s, str) else s) except: - sort_key=lambda s: (unicode(s, 'utf-8') if isinstance(s, str) else s).lower() + sort_key = lambda s: ( + unicode(s, 'utf-8') if isinstance(s, str) else s).lower() return sort_key(s) @@ -57,13 +60,16 @@ def ord(char): SUPPOSE that *char* is an utf-8 or unicode character only """ - if isinstance(char, unicode): return __builtin__.ord(char) + if isinstance(char, unicode): + return __builtin__.ord(char) return __builtin__.ord(unicode(char, 'utf-8')) + def chr(code): """ return utf8-character with *code* unicode id """ return Utf8(unichr(code)) + def size(string): """ return length of utf-8 string in bytes NOTE! The length of correspondent utf-8 @@ -71,6 +77,7 @@ def size(string): """ return Utf8(string).__size__() + def truncate(string, length, dots='...'): """ returns string of length < *length* or truncate string with adding *dots* suffix to the string's end @@ -85,570 +92,668 @@ def truncate(string, length, dots='...'): text = unicode(string, 'utf-8') dots = unicode(dots, 'utf-8') if isinstance(dots, str) else dots if len(text) > length: - text = text[:length-len(dots)] + dots + text = text[:length - len(dots)] + dots return str.__new__(Utf8, text.encode('utf-8')) class Utf8(str): - """ - Class for utf8 string storing and manipulations + """ + Class for utf8 string storing and manipulations - The base presupposition of this class usage is: - "ALL strings in the application are either of - utf-8 or unicode type, even when simple str - type is used. UTF-8 is only a "packed" version - of unicode, so Utf-8 and unicode strings are - interchangeable." + The base presupposition of this class usage is: + "ALL strings in the application are either of + utf-8 or unicode type, even when simple str + type is used. UTF-8 is only a "packed" version + of unicode, so Utf-8 and unicode strings are + interchangeable." - CAUTION! This class is slower than str/unicode! - Do NOT use it inside intensive loops. Simply - decode string(s) to unicode before loop and - encode it back to utf-8 string(s) after - intensive calculation. + CAUTION! This class is slower than str/unicode! + Do NOT use it inside intensive loops. Simply + decode string(s) to unicode before loop and + encode it back to utf-8 string(s) after + intensive calculation. - You can see the benefit of this class in doctests() below - """ - def __new__(cls, content='', codepage='utf-8'): - if isinstance(content, unicode): - return str.__new__(cls, unicode.encode(content, 'utf-8')) - elif codepage in ('utf-8', 'utf8') or isinstance(content, cls): - return str.__new__(cls, content) - else: - return str.__new__(cls, unicode(content, codepage).encode('utf-8')) + You can see the benefit of this class in doctests() below + """ + def __new__(cls, content='', codepage='utf-8'): + if isinstance(content, unicode): + return str.__new__(cls, unicode.encode(content, 'utf-8')) + elif codepage in ('utf-8', 'utf8') or isinstance(content, cls): + return str.__new__(cls, content) + else: + return str.__new__(cls, unicode(content, codepage).encode('utf-8')) - def __repr__(self): - r''' # note that we use raw strings to avoid having to use double back slashes below - NOTE! This function is a clone of web2py:gluon.languages.utf_repl() function + def __repr__(self): + r''' # note that we use raw strings to avoid having to use double back slashes below + NOTE! This function is a clone of web2py:gluon.languages.utf_repl() function - utf8.__repr__() works same as str.repr() when processing ascii string - >>> repr(Utf8('abc')) == repr(Utf8("abc")) == repr('abc') == repr("abc") == "'abc'" - True - >>> repr(Utf8('a"b"c')) == repr('a"b"c') == '\'a"b"c\'' - True - >>> repr(Utf8("a'b'c")) == repr("a'b'c") == '"a\'b\'c"' - True - >>> repr(Utf8('a\'b"c')) == repr('a\'b"c') == repr(Utf8("a'b\"c")) == repr("a'b\"c") == '\'a\\\'b"c\'' - True - >>> repr(Utf8('a\r\nb')) == repr('a\r\nb') == "'a\\r\\nb'" # Test for \r, \n - True + utf8.__repr__() works same as str.repr() when processing ascii string + >>> repr(Utf8('abc')) == repr(Utf8("abc")) == repr('abc') == repr("abc") == "'abc'" + True + >>> repr(Utf8('a"b"c')) == repr('a"b"c') == '\'a"b"c\'' + True + >>> repr(Utf8("a'b'c")) == repr("a'b'c") == '"a\'b\'c"' + True + >>> repr(Utf8('a\'b"c')) == repr('a\'b"c') == repr(Utf8("a'b\"c")) == repr("a'b\"c") == '\'a\\\'b"c\'' + True + >>> repr(Utf8('a\r\nb')) == repr('a\r\nb') == "'a\\r\\nb'" # Test for \r, \n + True - Unlike str.repr(), Utf8.__repr__() remains utf8 content when processing utf8 string - >>> repr(Utf8('中文字')) == repr(Utf8("中文字")) == "'中文字'" != repr('中文字') - True - >>> repr(Utf8('中"文"字')) == "'中\"文\"字'" != repr('中"文"字') - True - >>> repr(Utf8("中'文'字")) == '"中\'文\'字"' != repr("中'文'字") - True - >>> repr(Utf8('中\'文"字')) == repr(Utf8("中'文\"字")) == '\'中\\\'文"字\'' != repr('中\'文"字') == repr("中'文\"字") - True - >>> repr(Utf8('中\r\n文')) == "'中\\r\\n文'" != repr('中\r\n文') # Test for \r, \n - True - ''' - if str.find(self,"'") >= 0 and str.find(self,'"') < 0: # only single quote exists - return '"'+unicode(self, 'utf-8').translate(repr_escape_tab).encode('utf-8')+'"' - else: - return "'"+unicode(self, 'utf-8').translate(repr_escape_tab2).encode('utf-8')+"'" + Unlike str.repr(), Utf8.__repr__() remains utf8 content when processing utf8 string + >>> repr(Utf8('中文字')) == repr(Utf8("中文字")) == "'中文字'" != repr('中文字') + True + >>> repr(Utf8('中"文"字')) == "'中\"文\"字'" != repr('中"文"字') + True + >>> repr(Utf8("中'文'字")) == '"中\'文\'字"' != repr("中'文'字") + True + >>> repr(Utf8('中\'文"字')) == repr(Utf8("中'文\"字")) == '\'中\\\'文"字\'' != repr('中\'文"字') == repr("中'文\"字") + True + >>> repr(Utf8('中\r\n文')) == "'中\\r\\n文'" != repr('中\r\n文') # Test for \r, \n + True + ''' + if str.find(self, "'") >= 0 and str.find(self, '"') < 0: # only single quote exists + return '"' + unicode(self, 'utf-8').translate(repr_escape_tab).encode('utf-8') + '"' + else: + return "'" + unicode(self, 'utf-8').translate(repr_escape_tab2).encode('utf-8') + "'" - def __size__(self): - """ length of utf-8 string in bytes """ - return str.__len__(self) + def __size__(self): + """ length of utf-8 string in bytes """ + return str.__len__(self) - def __contains__(self, other): return str.__contains__(self, Utf8(other)) - def __getitem__(self, index): return str.__new__(Utf8, unicode(self, 'utf-8')[index].encode('utf-8')) - def __getslice__(self, begin, end): return str.__new__(Utf8, unicode(self, 'utf-8')[begin:end].encode('utf-8')) + def __contains__(self, other): + return str.__contains__(self, Utf8(other)) - def __add__(self, other): return str.__new__(Utf8, str.__add__(self, unicode.encode(other, 'utf-8') - if isinstance(other, unicode) else other)) - def __len__(self): return len(unicode(self, 'utf-8')) - def __mul__(self, integer):return str.__new__(Utf8, str.__mul__(self, integer)) - def __eq__(self, string): return str.__eq__(self, Utf8(string)) - def __ne__(self, string): return str.__ne__(self, Utf8(string)) - def capitalize(self): return str.__new__(Utf8, unicode(self, 'utf-8').capitalize().encode('utf-8')) - def center(self, length): return str.__new__(Utf8, unicode(self, 'utf-8').center(length).encode('utf-8')) - def upper(self): return str.__new__(Utf8, unicode(self, 'utf-8').upper().encode('utf-8')) - def lower(self): return str.__new__(Utf8, unicode(self, 'utf-8').lower().encode('utf-8')) - def title(self): return str.__new__(Utf8, unicode(self, 'utf-8').title().encode('utf-8')) - def index(self, string): return unicode(self, 'utf-8').index(string if isinstance(string,unicode) else unicode(string, 'utf-8')) - def isalnum(self): return unicode(self, 'utf-8').isalnum() - def isalpha(self): return unicode(self, 'utf-8').isalpha() - def isdigit(self): return unicode(self, 'utf-8').isdigit() - def islower(self): return unicode(self, 'utf-8').islower() - def isspace(self): return unicode(self, 'utf-8').isspace() - def istitle(self): return unicode(self, 'utf-8').istitle() - def isupper(self): return unicode(self, 'utf-8').isupper() - def zfill(self, length): return str.__new__(Utf8, unicode(self, 'utf-8').zfill(length).encode('utf-8')) - def join(self, iter): return str.__new__(Utf8, str.join(self, [Utf8(c) for c in - list(unicode(iter, 'utf-8') if - isinstance(iter, str) else - iter)])) - def lstrip(self, chars=None): return str.__new__(Utf8, str.lstrip(self, None if chars is None else Utf8(chars))) - def rstrip(self, chars=None ): return str.__new__(Utf8, str.rstrip(self, None if chars is None else Utf8(chars))) - def strip(self, chars=None): return str.__new__(Utf8, str.strip(self, None if chars is None else Utf8(chars))) - def swapcase(self): return str.__new__(Utf8, unicode(self, 'utf-8').swapcase().encode('utf-8')) + def __getitem__(self, index): + return str.__new__(Utf8, unicode(self, 'utf-8')[index].encode('utf-8')) - def count(self, sub, start=0, end=None): - unistr = unicode(self, 'utf-8') - return unistr.count(unicode(sub, 'utf-8') if isinstance(sub, str) else sub, - start, len(unistr) if end is None else end) - def decode(self, encoding='utf-8', errors='strict'): return str.decode(self, encoding, errors) - def encode(self, encoding, errors='strict'): return unicode(self, 'utf-8').encode(encoding, errors) - def expandtabs(self, tabsize=8): return str.__new__(Utf8, unicode(self, 'utf-8').expandtabs(tabsize).encode('utf-8')) - def find(self, sub, start=None, end=None): return unicode(self, 'utf-8').find(unicode(sub, 'utf-8') - if isinstance(sub, str) else sub, start, end) - def ljust(self, width, fillchar=' '): return str.__new__(Utf8, unicode(self, 'utf-8').ljust(width, unicode(fillchar, 'utf-8') + def __getslice__(self, begin, end): + return str.__new__(Utf8, unicode(self, 'utf-8')[begin:end].encode('utf-8')) + + def __add__(self, other): + return str.__new__(Utf8, str.__add__(self, unicode.encode(other, 'utf-8') + if isinstance(other, unicode) else other)) + + def __len__(self): + return len(unicode(self, 'utf-8')) + + def __mul__(self, integer): + return str.__new__(Utf8, str.__mul__(self, integer)) + + def __eq__(self, string): + return str.__eq__(self, Utf8(string)) + + def __ne__(self, string): + return str.__ne__(self, Utf8(string)) + + def capitalize(self): + return str.__new__(Utf8, unicode(self, 'utf-8').capitalize().encode('utf-8')) + + def center(self, length): + return str.__new__(Utf8, unicode(self, 'utf-8').center(length).encode('utf-8')) + + def upper(self): + return str.__new__(Utf8, unicode(self, 'utf-8').upper().encode('utf-8')) + + def lower(self): + return str.__new__(Utf8, unicode(self, 'utf-8').lower().encode('utf-8')) + + def title(self): + return str.__new__(Utf8, unicode(self, 'utf-8').title().encode('utf-8')) + + def index(self, string): + return unicode(self, 'utf-8').index(string if isinstance(string, unicode) else unicode(string, 'utf-8')) + + def isalnum(self): + return unicode(self, 'utf-8').isalnum() + + def isalpha(self): + return unicode(self, 'utf-8').isalpha() + + def isdigit(self): + return unicode(self, 'utf-8').isdigit() + + def islower(self): + return unicode(self, 'utf-8').islower() + + def isspace(self): + return unicode(self, 'utf-8').isspace() + + def istitle(self): + return unicode(self, 'utf-8').istitle() + + def isupper(self): + return unicode(self, 'utf-8').isupper() + + def zfill(self, length): + return str.__new__(Utf8, unicode(self, 'utf-8').zfill(length).encode('utf-8')) + + def join(self, iter): + return str.__new__(Utf8, str.join(self, [Utf8(c) for c in + list(unicode(iter, 'utf-8') if + isinstance(iter, str) else + iter)])) + + def lstrip(self, chars=None): + return str.__new__(Utf8, str.lstrip(self, None if chars is None else Utf8(chars))) + + def rstrip(self, chars=None): + return str.__new__(Utf8, str.rstrip(self, None if chars is None else Utf8(chars))) + + def strip(self, chars=None): + return str.__new__(Utf8, str.strip(self, None if chars is None else Utf8(chars))) + + def swapcase(self): + return str.__new__(Utf8, unicode(self, 'utf-8').swapcase().encode('utf-8')) + + def count(self, sub, start=0, end=None): + unistr = unicode(self, 'utf-8') + return unistr.count( + unicode(sub, 'utf-8') if isinstance(sub, str) else sub, + start, len(unistr) if end is None else end) + + def decode(self, encoding='utf-8', errors='strict'): + return str.decode(self, encoding, errors) + + def encode(self, encoding, errors='strict'): + return unicode(self, 'utf-8').encode(encoding, errors) + + def expandtabs(self, tabsize=8): + return str.__new__(Utf8, unicode(self, 'utf-8').expandtabs(tabsize).encode('utf-8')) + + def find(self, sub, start=None, end=None): + return unicode(self, 'utf-8').find(unicode(sub, 'utf-8') + if isinstance(sub, str) else sub, start, end) + + def ljust(self, width, fillchar=' '): + return str.__new__(Utf8, unicode(self, 'utf-8').ljust(width, unicode(fillchar, 'utf-8') if isinstance(fillchar, str) else fillchar).encode('utf-8')) - def partition(self, sep): - (head, sep, tail) = str.partition(self, Utf8(sep)) - return ( str.__new__(Utf8, head), - str.__new__(Utf8, sep), - str.__new__(Utf8, tail) ) - def replace(self, old, new, count=-1): return str.__new__(Utf8, str.replace(self, Utf8(old), Utf8(new), count)) - def rfind(self, sub, start=None, end=None): return unicode(self, 'utf-8').rfind(unicode(sub, 'utf-8') - if isinstance(sub, str) else sub, start, end) - def rindex(self, string): return unicode(self, 'utf-8').rindex(string if isinstance(string,unicode) - else unicode(string, 'utf-8')) - def rjust(self, width, fillchar=' '): return str.__new__(Utf8, unicode(self, 'utf-8').rjust(width, unicode(fillchar, 'utf-8') - if isinstance(fillchar, str) else fillchar).encode('utf-8')) - def rpartition(self, sep): - (head, sep, tail) = str.rpartition(self, Utf8(sep)) - return ( str.__new__(Utf8, head), + def partition(self, sep): + (head, sep, tail) = str.partition(self, Utf8(sep)) + return (str.__new__(Utf8, head), str.__new__(Utf8, sep), - str.__new__(Utf8, tail) ) - def rsplit(self, sep=None, maxsplit=-1): return [str.__new__(Utf8, part) for part in str.rsplit(self, - None if sep is None else Utf8(sep), maxsplit)] - def split(self, sep=None, maxsplit=-1): return [str.__new__(Utf8, part) for part in str.split(self, - None if sep is None else Utf8(sep), maxsplit)] - def splitlines(self,keepends=False): return [str.__new__(Utf8, part) for part in str.splitlines(self,keepends)] - def startswith(self, prefix, start=0, end=None): - unistr = unicode(self, 'utf-8') - if isinstance(prefix, tuple): - prefix = tuple(unicode(s,'utf-8') if isinstance(s, str) else s for s in prefix) - elif isinstance(prefix, str): - prefix = unicode(prefix, 'utf-8') - return unistr.startswith(prefix, start, len(unistr) if end is None else end) - def translate(self, table, deletechars=''): - if isinstance(table, dict): - return str.__new__(Utf8, unicode(self, 'utf-8').translate(table).encode('utf-8')) - else: - return str.__new__(Utf8, str.translate(self, table, deletechars)) - def endswith(self, prefix, start=0, end=None): - unistr = unicode(self, 'utf-8') - if isinstance(prefix, tuple): - prefix = tuple(unicode(s,'utf-8') if isinstance(s, str) else s for s in prefix) - elif isinstance(prefix, str): - prefix = unicode(prefix, 'utf-8') - return unistr.endswith(prefix, start, len(unistr) if end is None else end) - if hasattr(str, 'format'): # Python 2.5 hasn't got str.format() method - def format(self, *args, **kwargs): - args = [unicode(s, 'utf-8') if isinstance(s, str) else s for s in args] - kwargs = dict((unicode(k, 'utf-8') if isinstance(k, str) else k, + str.__new__(Utf8, tail)) + + def replace(self, old, new, count=-1): + return str.__new__(Utf8, str.replace(self, Utf8(old), Utf8(new), count)) + + def rfind(self, sub, start=None, end=None): + return unicode(self, 'utf-8').rfind(unicode(sub, 'utf-8') + if isinstance(sub, str) else sub, start, end) + + def rindex(self, string): + return unicode(self, 'utf-8').rindex(string if isinstance(string, unicode) + else unicode(string, 'utf-8')) + + def rjust(self, width, fillchar=' '): + return str.__new__(Utf8, unicode(self, 'utf-8').rjust(width, unicode(fillchar, 'utf-8') + if isinstance(fillchar, str) else fillchar).encode('utf-8')) + + def rpartition(self, sep): + (head, sep, tail) = str.rpartition(self, Utf8(sep)) + return (str.__new__(Utf8, head), + str.__new__(Utf8, sep), + str.__new__(Utf8, tail)) + + def rsplit(self, sep=None, maxsplit=-1): + return [str.__new__(Utf8, part) for part in str.rsplit(self, + None if sep is None else Utf8(sep), maxsplit)] + + def split(self, sep=None, maxsplit=-1): + return [str.__new__(Utf8, part) for part in str.split(self, + None if sep is None else Utf8(sep), maxsplit)] + + def splitlines(self, keepends=False): + return [str.__new__(Utf8, part) for part in str.splitlines(self, keepends)] + + def startswith(self, prefix, start=0, end=None): + unistr = unicode(self, 'utf-8') + if isinstance(prefix, tuple): + prefix = tuple(unicode( + s, 'utf-8') if isinstance(s, str) else s for s in prefix) + elif isinstance(prefix, str): + prefix = unicode(prefix, 'utf-8') + return unistr.startswith(prefix, start, len(unistr) if end is None else end) + + def translate(self, table, deletechars=''): + if isinstance(table, dict): + return str.__new__(Utf8, unicode(self, 'utf-8').translate(table).encode('utf-8')) + else: + return str.__new__(Utf8, str.translate(self, table, deletechars)) + + def endswith(self, prefix, start=0, end=None): + unistr = unicode(self, 'utf-8') + if isinstance(prefix, tuple): + prefix = tuple(unicode( + s, 'utf-8') if isinstance(s, str) else s for s in prefix) + elif isinstance(prefix, str): + prefix = unicode(prefix, 'utf-8') + return unistr.endswith(prefix, start, len(unistr) if end is None else end) + if hasattr(str, 'format'): # Python 2.5 hasn't got str.format() method + def format(self, *args, **kwargs): + args = [unicode( + s, 'utf-8') if isinstance(s, str) else s for s in args] + kwargs = dict((unicode(k, 'utf-8') if isinstance(k, str) else k, + unicode(v, 'utf-8') if isinstance(v, str) else v) + for k, v in kwargs.iteritems()) + return str.__new__(Utf8, unicode(self, 'utf-8'). + format(*args, **kwargs).encode('utf-8')) + + def __mod__(self, right): + if isinstance(right, tuple): + right = tuple(unicode(v, 'utf-8') if isinstance(v, str) else v + for v in right) + elif isinstance(right, dict): + right = dict((unicode(k, 'utf-8') if isinstance(k, str) else k, unicode(v, 'utf-8') if isinstance(v, str) else v) - for k,v in kwargs.iteritems()) - return str.__new__(Utf8, unicode(self, 'utf-8'). - format(*args, **kwargs).encode('utf-8')) - def __mod__(self, right): - if isinstance(right, tuple): - right = tuple(unicode(v, 'utf-8') if isinstance(v, str) else v - for v in right) - elif isinstance(right, dict): - right = dict((unicode(k, 'utf-8') if isinstance(k, str) else k, - unicode(v, 'utf-8') if isinstance(v, str) else v) - for k,v in right.iteritems()) - elif isinstance(right, str): - right = unicode(right, 'utf-8') - return str.__new__(Utf8, unicode(self, 'utf-8').__mod__(right).encode('utf-8')) - def __ge__(self, string): return sort_key(self) >= sort_key(string) - def __gt__(self, string): return sort_key(self) > sort_key(string) - def __le__(self, string): return sort_key(self) <= sort_key(string) - def __lt__(self, string): return sort_key(self) < sort_key(string) + for k, v in right.iteritems()) + elif isinstance(right, str): + right = unicode(right, 'utf-8') + return str.__new__(Utf8, unicode(self, 'utf-8').__mod__(right).encode('utf-8')) + + def __ge__(self, string): + return sort_key(self) >= sort_key(string) + + def __gt__(self, string): + return sort_key(self) > sort_key(string) + + def __le__(self, string): + return sort_key(self) <= sort_key(string) + + def __lt__(self, string): + return sort_key(self) < sort_key(string) if __name__ == '__main__': def doctests(): - u""" - doctests: - >>> test_unicode=u'ПРоба Є PRobe' - >>> test_unicode_word=u'ПРоба' - >>> test_number_str='12345' - >>> test_unicode - u'\\u041f\\u0420\\u043e\\u0431\\u0430 \\u0404 PRobe' - >>> print test_unicode - ПРоба Є PRobe - >>> test_word=test_unicode_word.encode('utf-8') - >>> test_str=test_unicode.encode('utf-8') - >>> s=Utf8(test_str) - >>> s - 'ПРоба Є PRobe' - >>> type(s) - <class '__main__.Utf8'> - >>> s == test_str - True - >>> len(test_str) # wrong length of utf8-string! - 19 - >>> len(test_unicode) # RIGHT! - 13 - >>> len(s) # RIGHT! - 13 - >>> size(test_str) # size of utf-8 string (in bytes) == len(str) - 19 - >>> size(test_unicode) # size of unicode string in bytes (packed to utf-8 string) - 19 - >>> size(s) # size of utf-8 string in bytes - 19 - >>> try: # utf-8 is a multibyte string. Convert it to unicode for use with builtin ord() - ... __builtin__.ord('б') # ascii string - ... except Exception, e: - ... print 'Exception:', e - Exception: ord() expected a character, but string of length 2 found - >>> ord('б') # utf8.ord() is used(!!!) - 1073 - >>> ord(u'б') # utf8.ord() is used(!!!) - 1073 - >>> ord(s[3]) # utf8.ord() is used(!!!) - 1073 - >>> chr(ord(s[3])) # utf8.chr() and utf8.chr() is used(!!!) - 'б' - >>> type(chr(1073)) # utf8.chr() is used(!!!) - <class '__main__.Utf8'> - >>> s=Utf8(test_unicode) - >>> s - 'ПРоба Є PRobe' - >>> s == test_str - True - >>> test_str == s - True - >>> s == test_unicode - True - >>> test_unicode == s - True - >>> print test_str.upper() # only ASCII characters uppered - ПРоба Є PROBE - >>> print test_unicode.upper() # unicode gives right result - ПРОБА Є PROBE - >>> s.upper() # utf8 class use unicode.upper() - 'ПРОБА Є PROBE' - >>> type(s.upper()) - <class '__main__.Utf8'> - >>> s.lower() - 'проба є probe' - >>> type(s.lower()) - <class '__main__.Utf8'> - >>> s.capitalize() - 'Проба є probe' - >>> type(s.capitalize()) - <class '__main__.Utf8'> - >>> len(s) - 13 - >>> len(test_unicode) - 13 - >>> s+'. Probe is проба' - 'ПРоба Є PRobe. Probe is проба' - >>> type(s+'. Probe is проба') - <class '__main__.Utf8'> - >>> s+u'. Probe is проба' - 'ПРоба Є PRobe. Probe is проба' - >>> type(s+u'. Probe is проба') - <class '__main__.Utf8'> - >>> s+s - 'ПРоба Є PRobeПРоба Є PRobe' - >>> type(s+s) - <class '__main__.Utf8'> - >>> a=s - >>> a+=s - >>> a+=test_unicode - >>> a+=test_str - >>> a - 'ПРоба Є PRobeПРоба Є PRobeПРоба Є PRobeПРоба Є PRobe' - >>> type(a) - <class '__main__.Utf8'> - >>> s*3 - 'ПРоба Є PRobeПРоба Є PRobeПРоба Є PRobe' - >>> type(s*3) - <class '__main__.Utf8'> - >>> a=Utf8("-проба-") - >>> a*=10 - >>> a - '-проба--проба--проба--проба--проба--проба--проба--проба--проба--проба-' - >>> type(a) - <class '__main__.Utf8'> - >>> print "'"+test_str.center(17)+"'" # WRONG RESULT! - 'ПРоба Є PRobe' - >>> s.center(17) # RIGHT! - ' ПРоба Є PRobe ' - >>> type(s.center(17)) - <class '__main__.Utf8'> - >>> (test_word+test_number_str).isalnum() # WRONG RESULT! non ASCII chars are detected as non alpha - False - >>> Utf8(test_word+test_number_str).isalnum() - True - >>> s.isalnum() - False - >>> test_word.isalpha() # WRONG RESULT! Non ASCII characters are detected as non alpha - False - >>> Utf8(test_word).isalpha() # RIGHT! - True - >>> s.lower().islower() - True - >>> s.upper().isupper() - True - >>> print test_str.zfill(17) # WRONG RESULT! - ПРоба Є PRobe - >>> s.zfill(17) # RIGHT! - '0000ПРоба Є PRobe' - >>> type(s.zfill(17)) - <class '__main__.Utf8'> - >>> s.istitle() - False - >>> s.title().istitle() - True - >>> Utf8('1234').isdigit() - True - >>> Utf8(' \t').isspace() - True - >>> s.join('•|•') - '•ПРоба Є PRobe|ПРоба Є PRobe•' - >>> s.join((str('(utf8 тест1)'), unicode('(unicode тест2)','utf-8'), '(ascii test3)')) - '(utf8 тест1)ПРоба Є PRobe(unicode тест2)ПРоба Є PRobe(ascii test3)' - >>> type(s) - <class '__main__.Utf8'> - >>> s==test_str - True - >>> s==test_unicode - True - >>> s.swapcase() - 'прОБА є prOBE' - >>> type(s.swapcase()) - <class '__main__.Utf8'> - >>> truncate(s, 10) - 'ПРоба Є...' - >>> truncate(s, 20) - 'ПРоба Є PRobe' - >>> truncate(s, 10, '•••') # utf-8 string as *dots* - 'ПРоба Є•••' - >>> truncate(s, 10, u'®') # you can use unicode string as *dots* - 'ПРоба Є P®' - >>> type(truncate(s, 10)) - <class '__main__.Utf8'> - >>> Utf8(s.encode('koi8-u'), 'koi8-u') - 'ПРоба Є PRobe' - >>> s.decode() # convert utf-8 string to unicode - u'\\u041f\\u0420\\u043e\\u0431\\u0430 \\u0404 PRobe' - >>> a='про\\tba' - >>> str_tmp=a.expandtabs() - >>> utf8_tmp=Utf8(a).expandtabs() - >>> utf8_tmp.replace(' ','.') # RIGHT! (default tabsize is 8) - 'про.....ba' - >>> utf8_tmp.index('b') - 8 - >>> print "'"+str_tmp.replace(' ','.')+"'" # WRONG STRING LENGTH! - 'про..ba' - >>> str_tmp.index('b') # WRONG index of 'b' character - 8 - >>> print "'"+a.expandtabs(4).replace(' ','.')+"'" # WRONG RESULT! - 'про..ba' - >>> Utf8(a).expandtabs(4).replace(' ','.') # RIGHT! - 'про.ba' - >>> s.find('Є') - 6 - >>> s.find(u'Є') - 6 - >>> s.find(' ', 6) - 7 - >>> s.rfind(' ') - 7 - >>> s.partition('Є') - ('ПРоба ', 'Є', ' PRobe') - >>> s.partition(u'Є') - ('ПРоба ', 'Є', ' PRobe') - >>> (a,b,c) = s.partition('Є') - >>> type(a), type(b), type(c) - (<class '__main__.Utf8'>, <class '__main__.Utf8'>, <class '__main__.Utf8'>) - >>> s.partition(' ') - ('ПРоба', ' ', 'Є PRobe') - >>> s.rpartition(' ') - ('ПРоба Є', ' ', 'PRobe') - >>> s.index('Є') - 6 - >>> s.rindex(u'Є') - 6 - >>> s.index(' ') - 5 - >>> s.rindex(' ') - 7 - >>> a=Utf8('а б ц д е а б ц д е а\\tб ц д е') - >>> a.split() - ['а', 'б', 'ц', 'д', 'е', 'а', 'б', 'ц', 'д', 'е', 'а', 'б', 'ц', 'д', 'е'] - >>> a.rsplit() - ['а', 'б', 'ц', 'д', 'е', 'а', 'б', 'ц', 'д', 'е', 'а', 'б', 'ц', 'д', 'е'] - >>> a.expandtabs().split('б') - ['а ', ' ц д е а ', ' ц д е а ', ' ц д е'] - >>> a.expandtabs().rsplit('б') - ['а ', ' ц д е а ', ' ц д е а ', ' ц д е'] - >>> a.expandtabs().split(u'б', 1) - ['а ', ' ц д е а б ц д е а б ц д е'] - >>> a.expandtabs().rsplit(u'б', 1) - ['а б ц д е а б ц д е а ', ' ц д е'] - >>> a=Utf8("рядок1\\nрядок2\\nрядок3") - >>> a.splitlines() - ['рядок1', 'рядок2', 'рядок3'] - >>> a.splitlines(True) - ['рядок1\\n', 'рядок2\\n', 'рядок3'] - >>> s[6] - 'Є' - >>> s[0] - 'П' - >>> s[-1] - 'e' - >>> s[:10] - 'ПРоба Є PR' - >>> s[2:-2:2] - 'оаЄPo' - >>> s[::-1] - 'eboRP Є абоРП' - >>> s.startswith('ПР') - True - >>> s.startswith(('ПР', u'об'),0) - True - >>> s.startswith(u'об', 2, 4) - True - >>> s.endswith('be') - True - >>> s.endswith(('be', 'PR', u'Є')) - True - >>> s.endswith('PR', 8, 10) - True - >>> s.endswith('Є', -7, -6) - True - >>> s.count(' ') - 2 - >>> s.count(' ',6) - 1 - >>> s.count(u'Є') - 1 - >>> s.count('Є', 0, 5) - 0 - >>> Utf8("Parameters: '%(проба)s', %(probe)04d, %(проба2)s") % { u"проба": s, - ... "not used": "???", "probe": 2, "проба2": u"ПРоба Probe" } - "Parameters: 'ПРоба Є PRobe', 0002, ПРоба Probe" - >>> a=Utf8(u"Параметр: (%s)-(%s)-[%s]") - >>> a%=(s, s[::-1], 1000) - >>> a - 'Параметр: (ПРоба Є PRobe)-(eboRP Є абоРП)-[1000]' - >>> if hasattr(Utf8, 'format'): - ... Utf8("Проба <{0}>, {1}, {param1}, {param2}").format(s, u"中文字", - ... param1="барабан", param2=1000) == 'Проба <ПРоба Є PRobe>, 中文字, барабан, 1000' - ... else: # format() method is not used in python with version <2.6: - ... print True - True - >>> u'Б'<u'Ї' # WRONG ORDER! - False - >>> 'Б'<'Ї' # WRONG ORDER! - False - >>> Utf8('Б')<'Ї' # RIGHT! - True - >>> u'д'>u'ґ' # WRONG ORDER! - False - >>> Utf8('д')>Utf8('ґ') # RIGHT! - True - >>> u'є'<=u'ж' # WRONG ORDER! - False - >>> Utf8('є')<=u'ж' # RIGHT! - True - >>> Utf8('є')<=u'є' - True - >>> u'Ї'>=u'И' # WRONG ORDER! - False - >>> Utf8(u'Ї') >= u'И' # RIGHT - True - >>> Utf8('Є') >= 'Є' - True - >>> a="яжертиуіопшщїасдфгґхйклчєзьцвбнмюЯЖЕРТИУІОПШЩЇАСДФГҐХЙКЛЧЗЬЦВБНМЮЄ" # str type - >>> b=u"яжертиуіопшщїасдфгґхйклчєзьцвбнмюЯЖЕРТИУІОПШЩЇАСДФГҐХЙКЛЧЗЬЦВБНМЮЄ" # unicode type - >>> c=Utf8("яжертиуіопшщїасдфгґхйклчєзьцвбнмюЯЖЕРТИУІОПШЩЇАСДФГҐХЙКЛЧЗЬЦВБНМЮЄ") # utf8 class - >>> result = "".join(sorted(a)) - >>> result[0:20] # result is not utf8 string, because bytes, not utf8-characters were sorted - '\\x80\\x81\\x82\\x83\\x84\\x84\\x85\\x86\\x86\\x87\\x87\\x88\\x89\\x8c\\x8e\\x8f\\x90\\x90\\x91\\x91' - >>> try: - ... unicode(result, 'utf-8') # try to convert result (utf-8?) to unicode - ... except Exception, e: - ... print 'Exception:', e - Exception: 'utf8' codec can't decode byte 0x80 in position 0: unexpected code byte - >>> try: # FAILED! (working with bytes, not with utf8-charactes) - ... "".join( sorted(a, key=sort_key) ) # utf8.sort_key may be used with utf8 or unicode strings only! - ... except Exception, e: - ... print 'Exception:', e - Exception: 'utf8' codec can't decode byte 0xd1 in position 0: unexpected end of data - >>> print "".join( sorted(Utf8(a))) # converting *a* to unicode or utf8-string gives us correct result - аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ - >>> print u"".join( sorted(b) ) # WRONG ORDER! Default sort key is used - ЄІЇАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЮЯабвгдежзийклмнопрстуфхцчшщьюяєіїҐґ - >>> print u"".join( sorted(b, key=sort_key) ) # RIGHT ORDER! utf8.sort_key is used - аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ - >>> print "".join( sorted(c) ) # RIGHT ORDER! Utf8 "rich comparison" methods are used - аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ - >>> print "".join( sorted(c, key=sort_key) ) # RIGHT ORDER! utf8.sort_key is used - аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ - >>> Utf8().join(sorted(c.decode(), key=sort_key)) # convert to unicode for better performance - 'аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ' - >>> for result in sorted(["Іа", "Астро", u"гала", Utf8("Гоша"), "Єва", "шовк", "аякс", "Їжа", - ... "ґанок", Utf8("Дар'я"), "білінг", "веб", u"Жужа", "проба", u"тест", - ... "абетка", "яблуко", "Юляся", "Київ", "лимонад", "ложка", "Матриця", - ... ], key=sort_key): - ... print result.ljust(20), type(result) - абетка <type 'str'> - Астро <type 'str'> - аякс <type 'str'> - білінг <type 'str'> - веб <type 'str'> - гала <type 'unicode'> - ґанок <type 'str'> - Гоша <class '__main__.Utf8'> - Дар'я <class '__main__.Utf8'> - Єва <type 'str'> - Жужа <type 'unicode'> - Іа <type 'str'> - Їжа <type 'str'> - Київ <type 'str'> - лимонад <type 'str'> - ложка <type 'str'> - Матриця <type 'str'> - проба <type 'str'> - тест <type 'unicode'> - шовк <type 'str'> - Юляся <type 'str'> - яблуко <type 'str'> - >>> a=Utf8("中文字") - >>> L=list(a) - >>> L - ['中', '文', '字'] - >>> a="".join(L) - >>> print a - 中文字 - >>> type(a) - <type 'str'> - >>> a="中文字" # standard str type - >>> L=list(a) - >>> L - ['\\xe4', '\\xb8', '\\xad', '\\xe6', '\\x96', '\\x87', '\\xe5', '\\xad', '\\x97'] - >>> from string import maketrans - >>> str_tab=maketrans('PRobe','12345') - >>> unicode_tab={ord(u'П'):ord(u'Ж'), - ... ord(u'Р') : u'Ш', - ... ord(Utf8('о')) : None, # utf8.ord() is used - ... ord('б') : None, # -//-//- - ... ord(u'а') : u"中文字", - ... ord(u'Є') : Utf8('•').decode(), # only unicode type is supported - ... } - >>> s.translate(unicode_tab).translate(str_tab, deletechars=' ') - 'ЖШ中文字•12345' - """ - import sys - reload(sys) - sys.setdefaultencoding("UTF-8") - import doctest - print "DOCTESTS STARTED..." - doctest.testmod() - print "DOCTESTS FINISHED" + u""" + doctests: + >>> test_unicode=u'ПРоба Є PRobe' + >>> test_unicode_word=u'ПРоба' + >>> test_number_str='12345' + >>> test_unicode + u'\\u041f\\u0420\\u043e\\u0431\\u0430 \\u0404 PRobe' + >>> print test_unicode + ПРоба Є PRobe + >>> test_word=test_unicode_word.encode('utf-8') + >>> test_str=test_unicode.encode('utf-8') + >>> s=Utf8(test_str) + >>> s + 'ПРоба Є PRobe' + >>> type(s) + <class '__main__.Utf8'> + >>> s == test_str + True + >>> len(test_str) # wrong length of utf8-string! + 19 + >>> len(test_unicode) # RIGHT! + 13 + >>> len(s) # RIGHT! + 13 + >>> size(test_str) # size of utf-8 string (in bytes) == len(str) + 19 + >>> size(test_unicode) # size of unicode string in bytes (packed to utf-8 string) + 19 + >>> size(s) # size of utf-8 string in bytes + 19 + >>> try: # utf-8 is a multibyte string. Convert it to unicode for use with builtin ord() + ... __builtin__.ord('б') # ascii string + ... except Exception, e: + ... print 'Exception:', e + Exception: ord() expected a character, but string of length 2 found + >>> ord('б') # utf8.ord() is used(!!!) + 1073 + >>> ord(u'б') # utf8.ord() is used(!!!) + 1073 + >>> ord(s[3]) # utf8.ord() is used(!!!) + 1073 + >>> chr(ord(s[3])) # utf8.chr() and utf8.chr() is used(!!!) + 'б' + >>> type(chr(1073)) # utf8.chr() is used(!!!) + <class '__main__.Utf8'> + >>> s=Utf8(test_unicode) + >>> s + 'ПРоба Є PRobe' + >>> s == test_str + True + >>> test_str == s + True + >>> s == test_unicode + True + >>> test_unicode == s + True + >>> print test_str.upper() # only ASCII characters uppered + ПРоба Є PROBE + >>> print test_unicode.upper() # unicode gives right result + ПРОБА Є PROBE + >>> s.upper() # utf8 class use unicode.upper() + 'ПРОБА Є PROBE' + >>> type(s.upper()) + <class '__main__.Utf8'> + >>> s.lower() + 'проба є probe' + >>> type(s.lower()) + <class '__main__.Utf8'> + >>> s.capitalize() + 'Проба є probe' + >>> type(s.capitalize()) + <class '__main__.Utf8'> + >>> len(s) + 13 + >>> len(test_unicode) + 13 + >>> s+'. Probe is проба' + 'ПРоба Є PRobe. Probe is проба' + >>> type(s+'. Probe is проба') + <class '__main__.Utf8'> + >>> s+u'. Probe is проба' + 'ПРоба Є PRobe. Probe is проба' + >>> type(s+u'. Probe is проба') + <class '__main__.Utf8'> + >>> s+s + 'ПРоба Є PRobeПРоба Є PRobe' + >>> type(s+s) + <class '__main__.Utf8'> + >>> a=s + >>> a+=s + >>> a+=test_unicode + >>> a+=test_str + >>> a + 'ПРоба Є PRobeПРоба Є PRobeПРоба Є PRobeПРоба Є PRobe' + >>> type(a) + <class '__main__.Utf8'> + >>> s*3 + 'ПРоба Є PRobeПРоба Є PRobeПРоба Є PRobe' + >>> type(s*3) + <class '__main__.Utf8'> + >>> a=Utf8("-проба-") + >>> a*=10 + >>> a + '-проба--проба--проба--проба--проба--проба--проба--проба--проба--проба-' + >>> type(a) + <class '__main__.Utf8'> + >>> print "'"+test_str.center(17)+"'" # WRONG RESULT! + 'ПРоба Є PRobe' + >>> s.center(17) # RIGHT! + ' ПРоба Є PRobe ' + >>> type(s.center(17)) + <class '__main__.Utf8'> + >>> (test_word+test_number_str).isalnum() # WRONG RESULT! non ASCII chars are detected as non alpha + False + >>> Utf8(test_word+test_number_str).isalnum() + True + >>> s.isalnum() + False + >>> test_word.isalpha() # WRONG RESULT! Non ASCII characters are detected as non alpha + False + >>> Utf8(test_word).isalpha() # RIGHT! + True + >>> s.lower().islower() + True + >>> s.upper().isupper() + True + >>> print test_str.zfill(17) # WRONG RESULT! + ПРоба Є PRobe + >>> s.zfill(17) # RIGHT! + '0000ПРоба Є PRobe' + >>> type(s.zfill(17)) + <class '__main__.Utf8'> + >>> s.istitle() + False + >>> s.title().istitle() + True + >>> Utf8('1234').isdigit() + True + >>> Utf8(' \t').isspace() + True + >>> s.join('•|•') + '•ПРоба Є PRobe|ПРоба Є PRobe•' + >>> s.join((str('(utf8 тест1)'), unicode('(unicode тест2)','utf-8'), '(ascii test3)')) + '(utf8 тест1)ПРоба Є PRobe(unicode тест2)ПРоба Є PRobe(ascii test3)' + >>> type(s) + <class '__main__.Utf8'> + >>> s==test_str + True + >>> s==test_unicode + True + >>> s.swapcase() + 'прОБА є prOBE' + >>> type(s.swapcase()) + <class '__main__.Utf8'> + >>> truncate(s, 10) + 'ПРоба Є...' + >>> truncate(s, 20) + 'ПРоба Є PRobe' + >>> truncate(s, 10, '•••') # utf-8 string as *dots* + 'ПРоба Є•••' + >>> truncate(s, 10, u'®') # you can use unicode string as *dots* + 'ПРоба Є P®' + >>> type(truncate(s, 10)) + <class '__main__.Utf8'> + >>> Utf8(s.encode('koi8-u'), 'koi8-u') + 'ПРоба Є PRobe' + >>> s.decode() # convert utf-8 string to unicode + u'\\u041f\\u0420\\u043e\\u0431\\u0430 \\u0404 PRobe' + >>> a='про\\tba' + >>> str_tmp=a.expandtabs() + >>> utf8_tmp=Utf8(a).expandtabs() + >>> utf8_tmp.replace(' ','.') # RIGHT! (default tabsize is 8) + 'про.....ba' + >>> utf8_tmp.index('b') + 8 + >>> print "'"+str_tmp.replace(' ','.')+"'" # WRONG STRING LENGTH! + 'про..ba' + >>> str_tmp.index('b') # WRONG index of 'b' character + 8 + >>> print "'"+a.expandtabs(4).replace(' ','.')+"'" # WRONG RESULT! + 'про..ba' + >>> Utf8(a).expandtabs(4).replace(' ','.') # RIGHT! + 'про.ba' + >>> s.find('Є') + 6 + >>> s.find(u'Є') + 6 + >>> s.find(' ', 6) + 7 + >>> s.rfind(' ') + 7 + >>> s.partition('Є') + ('ПРоба ', 'Є', ' PRobe') + >>> s.partition(u'Є') + ('ПРоба ', 'Є', ' PRobe') + >>> (a,b,c) = s.partition('Є') + >>> type(a), type(b), type(c) + (<class '__main__.Utf8'>, <class '__main__.Utf8'>, <class '__main__.Utf8'>) + >>> s.partition(' ') + ('ПРоба', ' ', 'Є PRobe') + >>> s.rpartition(' ') + ('ПРоба Є', ' ', 'PRobe') + >>> s.index('Є') + 6 + >>> s.rindex(u'Є') + 6 + >>> s.index(' ') + 5 + >>> s.rindex(' ') + 7 + >>> a=Utf8('а б ц д е а б ц д е а\\tб ц д е') + >>> a.split() + ['а', 'б', 'ц', 'д', 'е', 'а', 'б', 'ц', 'д', + 'е', 'а', 'б', 'ц', 'д', 'е'] + >>> a.rsplit() + ['а', 'б', 'ц', 'д', 'е', 'а', 'б', 'ц', 'д', + 'е', 'а', 'б', 'ц', 'д', 'е'] + >>> a.expandtabs().split('б') + ['а ', ' ц д е а ', ' ц д е а ', ' ц д е'] + >>> a.expandtabs().rsplit('б') + ['а ', ' ц д е а ', ' ц д е а ', ' ц д е'] + >>> a.expandtabs().split(u'б', 1) + ['а ', ' ц д е а б ц д е а б ц д е'] + >>> a.expandtabs().rsplit(u'б', 1) + ['а б ц д е а б ц д е а ', ' ц д е'] + >>> a=Utf8("рядок1\\nрядок2\\nрядок3") + >>> a.splitlines() + ['рядок1', 'рядок2', 'рядок3'] + >>> a.splitlines(True) + ['рядок1\\n', 'рядок2\\n', 'рядок3'] + >>> s[6] + 'Є' + >>> s[0] + 'П' + >>> s[-1] + 'e' + >>> s[:10] + 'ПРоба Є PR' + >>> s[2:-2:2] + 'оаЄPo' + >>> s[::-1] + 'eboRP Є абоРП' + >>> s.startswith('ПР') + True + >>> s.startswith(('ПР', u'об'),0) + True + >>> s.startswith(u'об', 2, 4) + True + >>> s.endswith('be') + True + >>> s.endswith(('be', 'PR', u'Є')) + True + >>> s.endswith('PR', 8, 10) + True + >>> s.endswith('Є', -7, -6) + True + >>> s.count(' ') + 2 + >>> s.count(' ',6) + 1 + >>> s.count(u'Є') + 1 + >>> s.count('Є', 0, 5) + 0 + >>> Utf8( + "Parameters: '%(проба)s', %(probe)04d, %(проба2)s") % { u"проба": s, + ... "not used": "???", "probe": 2, "проба2": u"ПРоба Probe" } + "Parameters: 'ПРоба Є PRobe', 0002, ПРоба Probe" + >>> a=Utf8(u"Параметр: (%s)-(%s)-[%s]") + >>> a%=(s, s[::-1], 1000) + >>> a + 'Параметр: (ПРоба Є PRobe)-(eboRP Є абоРП)-[1000]' + >>> if hasattr(Utf8, 'format'): + ... Utf8("Проба <{0}>, {1}, {param1}, {param2}").format(s, u"中文字", + ... param1="барабан", param2=1000) == 'Проба <ПРоба Є PRobe>, 中文字, барабан, 1000' + ... else: # format() method is not used in python with version <2.6: + ... print True + True + >>> u'Б'<u'Ї' # WRONG ORDER! + False + >>> 'Б'<'Ї' # WRONG ORDER! + False + >>> Utf8('Б')<'Ї' # RIGHT! + True + >>> u'д'>u'ґ' # WRONG ORDER! + False + >>> Utf8('д')>Utf8('ґ') # RIGHT! + True + >>> u'є'<=u'ж' # WRONG ORDER! + False + >>> Utf8('є')<=u'ж' # RIGHT! + True + >>> Utf8('є')<=u'є' + True + >>> u'Ї'>=u'И' # WRONG ORDER! + False + >>> Utf8(u'Ї') >= u'И' # RIGHT + True + >>> Utf8('Є') >= 'Є' + True + >>> a="яжертиуіопшщїасдфгґхйклчєзьцвбнмюЯЖЕРТИУІОПШЩЇАСДФГҐХЙКЛЧЗЬЦВБНМЮЄ" # str type + >>> b=u"яжертиуіопшщїасдфгґхйклчєзьцвбнмюЯЖЕРТИУІОПШЩЇАСДФГҐХЙКЛЧЗЬЦВБНМЮЄ" # unicode type + >>> c=Utf8("яжертиуіопшщїасдфгґхйклчєзьцвбнмюЯЖЕРТИУІОПШЩЇАСДФГҐХЙКЛЧЗЬЦВБНМЮЄ") # utf8 class + >>> result = "".join(sorted(a)) + >>> result[0:20] # result is not utf8 string, because bytes, not utf8-characters were sorted + '\\x80\\x81\\x82\\x83\\x84\\x84\\x85\\x86\\x86\\x87\\x87\\x88\\x89\\x8c\\x8e\\x8f\\x90\\x90\\x91\\x91' + >>> try: + ... unicode(result, 'utf-8') # try to convert result (utf-8?) to unicode + ... except Exception, e: + ... print 'Exception:', e + Exception: 'utf8' codec can't decode byte 0x80 in position 0: unexpected code byte + >>> try: # FAILED! (working with bytes, not with utf8-charactes) + ... "".join( sorted(a, key=sort_key) ) # utf8.sort_key may be used with utf8 or unicode strings only! + ... except Exception, e: + ... print 'Exception:', e + Exception: 'utf8' codec can't decode byte 0xd1 in position 0: unexpected end of data + >>> print "".join( sorted(Utf8(a))) # converting *a* to unicode or utf8-string gives us correct result + аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ + >>> print u"".join( sorted(b) ) # WRONG ORDER! Default sort key is used + ЄІЇАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЮЯабвгдежзийклмнопрстуфхцчшщьюяєіїҐґ + >>> print u"".join( sorted(b, key=sort_key) ) # RIGHT ORDER! utf8.sort_key is used + аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ + >>> print "".join( sorted(c) ) # RIGHT ORDER! Utf8 "rich comparison" methods are used + аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ + >>> print "".join( sorted(c, key=sort_key) ) # RIGHT ORDER! utf8.sort_key is used + аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ + >>> Utf8().join(sorted(c.decode(), key=sort_key)) # convert to unicode for better performance + 'аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ' + >>> for result in sorted( + ["Іа", "Астро", u"гала", Utf8("Гоша"), "Єва", "шовк", "аякс", "Їжа", + ... "ґанок", Utf8("Дар'я"), "білінг", "веб", u"Жужа", "проба", u"тест", + ... "абетка", "яблуко", "Юляся", "Київ", "лимонад", "ложка", "Матриця", + ... ], key=sort_key): + ... print result.ljust(20), type(result) + абетка <type 'str'> + Астро <type 'str'> + аякс <type 'str'> + білінг <type 'str'> + веб <type 'str'> + гала <type 'unicode'> + ґанок <type 'str'> + Гоша <class '__main__.Utf8'> + Дар'я <class '__main__.Utf8'> + Єва <type 'str'> + Жужа <type 'unicode'> + Іа <type 'str'> + Їжа <type 'str'> + Київ <type 'str'> + лимонад <type 'str'> + ложка <type 'str'> + Матриця <type 'str'> + проба <type 'str'> + тест <type 'unicode'> + шовк <type 'str'> + Юляся <type 'str'> + яблуко <type 'str'> + >>> a=Utf8("中文字") + >>> L=list(a) + >>> L + ['中', '文', '字'] + >>> a="".join(L) + >>> print a + 中文字 + >>> type(a) + <type 'str'> + >>> a="中文字" # standard str type + >>> L=list(a) + >>> L + ['\\xe4', '\\xb8', '\\xad', '\\xe6', '\\x96', '\\x87', + '\\xe5', '\\xad', '\\x97'] + >>> from string import maketrans + >>> str_tab=maketrans('PRobe','12345') + >>> unicode_tab={ord(u'П'):ord(u'Ж'), + ... ord(u'Р') : u'Ш', + ... ord(Utf8('о')) : None, # utf8.ord() is used + ... ord('б') : None, # -//-//- + ... ord(u'а') : u"中文字", + ... ord(u'Є') : Utf8('•').decode(), # only unicode type is supported + ... } + >>> s.translate(unicode_tab).translate(str_tab, deletechars=' ') + 'ЖШ中文字•12345' + """ + import sys + reload(sys) + sys.setdefaultencoding("UTF-8") + import doctest + print "DOCTESTS STARTED..." + doctest.testmod() + print "DOCTESTS FINISHED" doctests() diff --git a/gluon/utils.py b/gluon/utils.py index 085f6d44..3ef03909 100644 --- a/gluon/utils.py +++ b/gluon/utils.py @@ -38,7 +38,8 @@ except ImportError: logger = logging.getLogger("web2py") -def compare(a,b): + +def compare(a, b): """ compares two strings and not vulnerable to timing attacks """ if len(a) != len(b): return False @@ -47,36 +48,39 @@ def compare(a,b): result |= ord(x) ^ ord(y) return result == 0 + def md5_hash(text): """ Generate a md5 hash with the given text """ return hashlib.md5(text).hexdigest() -def simple_hash(text, key='', salt = '', digest_alg = 'md5'): + +def simple_hash(text, key='', salt='', digest_alg='md5'): """ Generates hash with the given text using the specified digest hashing algorithm """ if not digest_alg: - raise RuntimeError, "simple_hash with digest_alg=None" - elif not isinstance(digest_alg,str): # manual approach - h = digest_alg(text+key+salt) - elif digest_alg.startswith('pbkdf2'): # latest and coolest! + raise RuntimeError("simple_hash with digest_alg=None") + elif not isinstance(digest_alg, str): # manual approach + h = digest_alg(text + key + salt) + elif digest_alg.startswith('pbkdf2'): # latest and coolest! iterations, keylen, alg = digest_alg[7:-1].split(',') return pbkdf2_hex(text, salt, int(iterations), - int(keylen),get_digest(alg)) - elif key: # use hmac + int(keylen), get_digest(alg)) + elif key: # use hmac digest_alg = get_digest(digest_alg) - h = hmac.new(key+salt,text,digest_alg) - else: # compatible with third party systems + h = hmac.new(key + salt, text, digest_alg) + else: # compatible with third party systems h = hashlib.new(digest_alg) - h.update(text+salt) + h.update(text + salt) return h.hexdigest() + def get_digest(value): """ Returns a hashlib digest algorithm from a string """ - if not isinstance(value,str): + if not isinstance(value, str): return value value = value.lower() if value == "md5": @@ -95,50 +99,55 @@ def get_digest(value): raise ValueError("Invalid digest algorithm: %s" % value) DIGEST_ALG_BY_SIZE = { - 128/4: 'md5', - 160/4: 'sha1', - 224/4: 'sha224', - 256/4: 'sha256', - 384/4: 'sha384', - 512/4: 'sha512', - } + 128 / 4: 'md5', + 160 / 4: 'sha1', + 224 / 4: 'sha224', + 256 / 4: 'sha256', + 384 / 4: 'sha384', + 512 / 4: 'sha512', +} -def pad(s,n=32,padchar='.'): + +def pad(s, n=32, padchar='.'): return s + (32 - len(s) % 32) * padchar -def secure_dumps(data,encryption_key,hash_key=None,compression_level=None): + +def secure_dumps(data, encryption_key, hash_key=None, compression_level=None): if not hash_key: hash_key = hashlib.sha1(encryption_key).hexdigest() dump = cPickle.dumps(data) if compression_level: dump = zlib.compress(dump, compression_level) key = pad(encryption_key[:32]) - cipher = AES.new(key,IV=key[:16]) + cipher = AES.new(key, IV=key[:16]) encrypted_data = base64.urlsafe_b64encode(cipher.encrypt(pad(dump))) - signature = hmac.new(hash_key,encrypted_data).hexdigest() - return signature+':'+encrypted_data + signature = hmac.new(hash_key, encrypted_data).hexdigest() + return signature + ':' + encrypted_data -def secure_loads(data,encryption_key,hash_key=None, compression_level=None): + +def secure_loads(data, encryption_key, hash_key=None, compression_level=None): if not ':' in data: return None if not hash_key: hash_key = hashlib.sha1(encryption_key).hexdigest() - signature, encrypted_data = data.split(':',1) - actual_signature = hmac.new(hash_key,encrypted_data).hexdigest() - if signature!=actual_signature: + signature, encrypted_data = data.split(':', 1) + actual_signature = hmac.new(hash_key, encrypted_data).hexdigest() + if signature != actual_signature: return None key = pad(encryption_key[:32]) - cipher = AES.new(key,IV=key[:16]) + cipher = AES.new(key, IV=key[:16]) try: data = cipher.decrypt(base64.urlsafe_b64decode(encrypted_data)) data = data.rstrip(' ') if compression_level: data = zlib.decompress(data) return cPickle.loads(data) - except (TypeError,cPickle.UnpicklingError): + except (TypeError, cPickle.UnpicklingError): return None ### compute constant CTOKENS + + def initialize_urandom(): """ This function and the web2py_uuid follow from the following discussion: @@ -154,14 +163,15 @@ def initialize_urandom(): """ node_id = uuid.getnode() microseconds = int(time.time() * 1e6) - ctokens = [((node_id + microseconds) >> ((i%6)*8)) % 256 for i in range(16)] + ctokens = [((node_id + microseconds) >> ((i % 6) * 8)) % + 256 for i in range(16)] random.seed(node_id + microseconds) try: os.urandom(1) have_urandom = True try: # try to add process-specific entropy - frandom = open('/dev/urandom','wb') + frandom = open('/dev/urandom', 'wb') try: frandom.write(''.join(chr(t) for t in ctokens)) finally: @@ -172,15 +182,16 @@ def initialize_urandom(): except NotImplementedError: have_urandom = False logger.warning( -"""Cryptographically secure session management is not possible on your system because + """Cryptographically secure session management is not possible on your system because your system does not provide a cryptographically secure entropy source. This is not specific to web2py; consider deploying on a different operating system.""") - unpacked_ctokens = struct.unpack('=QQ',string.join( - (chr(x) for x in ctokens),'')) + unpacked_ctokens = struct.unpack('=QQ', string.join( + (chr(x) for x in ctokens), '')) return unpacked_ctokens, have_urandom UNPACKED_CTOKENS, HAVE_URANDOM = initialize_urandom() -def fast_urandom16(urandom=[], locker = threading.RLock()): + +def fast_urandom16(urandom=[], locker=threading.RLock()): """ this is 4x faster than calling os.urandom(16) and prevents the "too many files open" issue with concurrent access to os.urandom() @@ -190,12 +201,13 @@ def fast_urandom16(urandom=[], locker = threading.RLock()): except IndexError: try: locker.acquire() - ur = os.urandom(16*1024) - urandom += [ur[i:i+16] for i in xrange(16,1024*16,16)] + ur = os.urandom(16 * 1024) + urandom += [ur[i:i + 16] for i in xrange(16, 1024 * 16, 16)] return ur[0:16] finally: locker.release() + def web2py_uuid(ctokens=UNPACKED_CTOKENS): """ This function follows from the following discussion: @@ -204,20 +216,21 @@ def web2py_uuid(ctokens=UNPACKED_CTOKENS): It works like uuid.uuid4 except that tries to use os.urandom() if possible and it XORs the output with the tokens uniquely associated with this machine. """ - rand_longs = (random.getrandbits(64),random.getrandbits(64)) + rand_longs = (random.getrandbits(64), random.getrandbits(64)) if HAVE_URANDOM: urand_longs = struct.unpack('=QQ', fast_urandom16()) byte_s = struct.pack('=QQ', - rand_longs[0]^urand_longs[0]^ctokens[0], - rand_longs[1]^urand_longs[1]^ctokens[1]) + rand_longs[0] ^ urand_longs[0] ^ ctokens[0], + rand_longs[1] ^ urand_longs[1] ^ ctokens[1]) else: byte_s = struct.pack('=QQ', - rand_longs[0]^ctokens[0], - rand_longs[1]^ctokens[1]) + rand_longs[0] ^ ctokens[0], + rand_longs[1] ^ ctokens[1]) return str(uuid.UUID(bytes=byte_s, version=4)) REGEX_IPv4 = re.compile('(\d+)\.(\d+)\.(\d+)\.(\d+)') + def is_valid_ip_address(address): """ >>> is_valid_ip_address('127.0') @@ -228,29 +241,29 @@ def is_valid_ip_address(address): True """ # deal with special cases - if address.lower() in ('127.0.0.1','localhost','::1','::ffff:127.0.0.1'): + if address.lower() in ('127.0.0.1', 'localhost', '::1', '::ffff:127.0.0.1'): return True - elif address.lower() in ('unkown',''): + elif address.lower() in ('unkown', ''): return False - elif address.count('.')==3: # assume IPv4 + elif address.count('.') == 3: # assume IPv4 if address.startswith('::ffff:'): address = address[7:] - if hasattr(socket,'inet_aton'): # try validate using the OS + if hasattr(socket, 'inet_aton'): # try validate using the OS try: addr = socket.inet_aton(address) return True - except socket.error: # invalid address + except socket.error: # invalid address return False - else: # try validate using Regex + else: # try validate using Regex match = REGEX_IPv4.match(address) - if match and all(0<=int(match.group(i))<256 for i in (1,2,3,4)): + if match and all(0 <= int(match.group(i)) < 256 for i in (1, 2, 3, 4)): return True return False - elif hasattr(socket,'inet_pton'): # assume IPv6, try using the OS + elif hasattr(socket, 'inet_pton'): # assume IPv6, try using the OS try: addr = socket.inet_pton(socket.AF_INET6, address) return True - except socket.error: # invalid address + except socket.error: # invalid address return False - else: # do not know what to do? assume it is a valid address + else: # do not know what to do? assume it is a valid address return True diff --git a/gluon/validators.py b/gluon/validators.py index e577dbcc..e3a33480 100644 --- a/gluon/validators.py +++ b/gluon/validators.py @@ -53,7 +53,7 @@ __all__ = [ 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', - ] +] try: from globals import current @@ -61,16 +61,19 @@ try: except ImportError: have_current = False + def translate(text): if text is None: return None - elif isinstance(text,(str,unicode)) and have_current: - if hasattr(current,'T'): + elif isinstance(text, (str, unicode)) and have_current: + if hasattr(current, 'T'): return str(current.T(text)) return str(text) -def options_sorter(x,y): - return (str(x[1]).upper()>str(y[1]).upper() and 1) or -1 + +def options_sorter(x, y): + return (str(x[1]).upper() > str(y[1]).upper() and 1) or -1 + class Validator(object): """ @@ -117,7 +120,7 @@ class Validator(object): """ return value - def __call__(self,value): + def __call__(self, value): raise NotImplementedError return (value, None) @@ -277,7 +280,7 @@ class IS_LENGTH(Validator): value.file.seek(0, os.SEEK_END) length = value.file.tell() value.file.seek(0, os.SEEK_SET) - elif hasattr(value,'value'): + elif hasattr(value, 'value'): val = value.value if val: length = len(val) @@ -294,8 +297,8 @@ class IS_LENGTH(Validator): return (value, None) except: pass - return (value, translate(self.error_message) \ - % dict(min=self.minsize, max=self.maxsize)) + return (value, translate(self.error_message) + % dict(min=self.minsize, max=self.maxsize)) class IS_IN_SET(Validator): @@ -334,15 +337,15 @@ class IS_IN_SET(Validator): multiple=False, zero='', sort=False, - ): + ): self.multiple = multiple if isinstance(theset, dict): self.theset = [str(item) for item in theset] self.labels = theset.values() - elif theset and isinstance(theset, (tuple,list)) \ - and isinstance(theset[0], (tuple,list)) and len(theset[0])==2: - self.theset = [str(item) for item,label in theset] - self.labels = [str(label) for item,label in theset] + elif theset and isinstance(theset, (tuple, list)) \ + and isinstance(theset[0], (tuple, list)) and len(theset[0]) == 2: + self.theset = [str(item) for item, label in theset] + self.labels = [str(label) for item, label in theset] else: self.theset = [str(item) for item in theset] self.labels = labels @@ -350,7 +353,7 @@ class IS_IN_SET(Validator): self.zero = zero self.sort = sort - def options(self,zero=True): + def options(self, zero=True): if not self.labels: items = [(k, k) for (i, k) in enumerate(self.theset)] else: @@ -358,7 +361,7 @@ class IS_IN_SET(Validator): if self.sort: items.sort(options_sorter) if zero and not self.zero is None and not self.multiple: - items.insert(0,('',self.zero)) + items.insert(0, ('', self.zero)) return items def __call__(self, value): @@ -379,8 +382,8 @@ class IS_IN_SET(Validator): return ([], None) return (value, translate(self.error_message)) if self.multiple: - if isinstance(self.multiple,(tuple,list)) and \ - not self.multiple[0]<=len(values)<self.multiple[1]: + if isinstance(self.multiple, (tuple, list)) and \ + not self.multiple[0] <= len(values) < self.multiple[1]: return (values, translate(self.error_message)) return (values, None) return (value, None) @@ -414,9 +417,10 @@ class IS_IN_DB(Validator): zero='', sort=False, _and=None, - ): + ): from dal import Table - if isinstance(field,Table): field = field._id + if isinstance(field, Table): + field = field._id if hasattr(dbset, 'define_table'): self.dbset = dbset() @@ -425,7 +429,7 @@ class IS_IN_DB(Validator): (ktable, kfield) = str(field).split('.') if not label: label = '%%(%s)s' % kfield - if isinstance(label,str): + if isinstance(label, str): if regex1.match(str(label)): label = '%%(%s)s' % str(label).split('.')[-1] ks = regex2.findall(label) @@ -462,7 +466,7 @@ class IS_IN_DB(Validator): else: fields = [table[k] for k in self.fields] if self.dbset.db._dbname != 'gae': - orderby = self.orderby or reduce(lambda a,b:a|b,fields) + orderby = self.orderby or reduce(lambda a, b: a | b, fields) groupby = self.groupby distinct = self.distinct dd = dict(orderby=orderby, groupby=groupby, @@ -471,11 +475,12 @@ class IS_IN_DB(Validator): records = self.dbset(table).select(*fields, **dd) else: orderby = self.orderby or \ - reduce(lambda a,b:a|b,(f for f in fields if not f.name=='id')) + reduce(lambda a, b: a | b, ( + f for f in fields if not f.name == 'id')) dd = dict(orderby=orderby, cache=self.cache, cacheable=True) records = self.dbset(table).select(table.ALL, **dd) self.theset = [str(r[self.kfield]) for r in records] - if isinstance(self.label,str): + if isinstance(self.label, str): self.labels = [self.label % dict(r) for r in records] else: self.labels = [self.label(r) for r in records] @@ -486,7 +491,7 @@ class IS_IN_DB(Validator): if self.sort: items.sort(options_sorter) if zero and not self.zero is None and not self.multiple: - items.insert(0,('',self.zero)) + items.insert(0, ('', self.zero)) return items def __call__(self, value): @@ -495,25 +500,26 @@ class IS_IN_DB(Validator): if self.multiple: if self._and: raise NotImplementedError - if isinstance(value,list): - values=value + if isinstance(value, list): + values = value elif value: values = [value] else: values = [] - if isinstance(self.multiple,(tuple,list)) and \ - not self.multiple[0]<=len(values)<self.multiple[1]: + if isinstance(self.multiple, (tuple, list)) and \ + not self.multiple[0] <= len(values) < self.multiple[1]: return (values, translate(self.error_message)) if self.theset: if not [v for v in values if not v in self.theset]: return (values, None) else: from dal import GoogleDatastoreAdapter + def count(values, s=self.dbset, f=field): - return s(f.belongs(map(int,values))).count() + return s(f.belongs(map(int, values))).count() if isinstance(self.dbset.db._adapter, GoogleDatastoreAdapter): - range_ids = range(0,len(values),30) - total = sum(count(values[i:i+30]) for i in range_ids) + range_ids = range(0, len(values), 30) + total = sum(count(values[i:i + 30]) for i in range_ids) if total == len(values): return (values, None) elif count(values) == len(values): @@ -549,10 +555,11 @@ class IS_NOT_IN_DB(Validator): error_message='value already in database or empty', allowed_override=[], ignore_common_filters=False, - ): + ): from dal import Table - if isinstance(field,Table): field = field._id + if isinstance(field, Table): + field = field._id if hasattr(dbset, 'define_table'): self.dbset = dbset() @@ -568,7 +575,7 @@ class IS_NOT_IN_DB(Validator): self.record_id = id def __call__(self, value): - value=str(value) + value = str(value) if not value.strip(): return (value, translate(self.error_message)) if value in self.allowed_override: @@ -576,7 +583,7 @@ class IS_NOT_IN_DB(Validator): (tablename, fieldname) = str(self.field).split('.') table = self.dbset.db[tablename] field = table[fieldname] - rows = self.dbset(field == value, ignore_common_filters = self.ignore_common_filters).select(limitby=(0, 1)) + rows = self.dbset(field == value, ignore_common_filters=self.ignore_common_filters).select(limitby=(0, 1)) if len(rows) > 0: if isinstance(self.record_id, dict): for f in self.record_id: @@ -631,7 +638,7 @@ class IS_INT_IN_RANGE(Validator): minimum=None, maximum=None, error_message=None, - ): + ): self.minimum = self.maximum = None if minimum is None: if maximum is None: @@ -640,19 +647,21 @@ class IS_INT_IN_RANGE(Validator): self.maximum = int(maximum) if error_message is None: error_message = 'enter an integer less than or equal to %(max)g' - self.error_message = translate(error_message) % dict(max=self.maximum-1) + self.error_message = translate( + error_message) % dict(max=self.maximum - 1) elif maximum is None: self.minimum = int(minimum) if error_message is None: error_message = 'enter an integer greater than or equal to %(min)g' - self.error_message = translate(error_message) % dict(min=self.minimum) + self.error_message = translate( + error_message) % dict(min=self.minimum) else: self.minimum = int(minimum) self.maximum = int(maximum) if error_message is None: error_message = 'enter an integer between %(min)g and %(max)g' self.error_message = translate(error_message) \ - % dict(min=self.minimum, max=self.maximum-1) + % dict(min=self.minimum, max=self.maximum - 1) def __call__(self, value): try: @@ -672,12 +681,16 @@ class IS_INT_IN_RANGE(Validator): pass return (value, self.error_message) + def str2dec(number): s = str(number) - if not '.' in s: s+='.00' - else: s+='0'*(2-len(s.split('.')[1])) + if not '.' in s: + s += '.00' + else: + s += '0' * (2 - len(s.split('.')[1])) return s + class IS_FLOAT_IN_RANGE(Validator): """ Determine that the argument is (or can be represented as) a float, @@ -723,7 +736,7 @@ class IS_FLOAT_IN_RANGE(Validator): maximum=None, error_message=None, dot='.' - ): + ): self.minimum = self.maximum = None self.dot = dot if minimum is None: @@ -748,10 +761,10 @@ class IS_FLOAT_IN_RANGE(Validator): def __call__(self, value): try: - if self.dot=='.': + if self.dot == '.': fvalue = float(value) else: - fvalue = float(str(value).replace(self.dot,'.')) + fvalue = float(str(value).replace(self.dot, '.')) if self.minimum is None: if self.maximum is None or fvalue <= self.maximum: return (fvalue, None) @@ -764,8 +777,8 @@ class IS_FLOAT_IN_RANGE(Validator): pass return (value, self.error_message) - def formatter(self,value): - return str2dec(value).replace('.',self.dot) + def formatter(self, value): + return str2dec(value).replace('.', self.dot) class IS_DECIMAL_IN_RANGE(Validator): @@ -827,7 +840,7 @@ class IS_DECIMAL_IN_RANGE(Validator): maximum=None, error_message=None, dot='.' - ): + ): self.minimum = self.maximum = None self.dot = dot if minimum is None: @@ -852,10 +865,10 @@ class IS_DECIMAL_IN_RANGE(Validator): def __call__(self, value): try: - if isinstance(value,decimal.Decimal): + if isinstance(value, decimal.Decimal): v = value else: - v = decimal.Decimal(str(value).replace(self.dot,'.')) + v = decimal.Decimal(str(value).replace(self.dot, '.')) if self.minimum is None: if self.maximum is None or v <= self.maximum: return (v, None) @@ -869,7 +882,8 @@ class IS_DECIMAL_IN_RANGE(Validator): return (value, self.error_message) def formatter(self, value): - return str2dec(value).replace('.',self.dot) + return str2dec(value).replace('.', self.dot) + def is_empty(value, empty_regex=None): "test empty field" @@ -881,6 +895,7 @@ def is_empty(value, empty_regex=None): return (value, True) return (value, False) + class IS_NOT_EMPTY(Validator): """ example:: @@ -1048,7 +1063,8 @@ class IS_EMAIL(Validator): localhost | ( - [a-z0-9] # [sub]domain begins with alphanumeric + [a-z0-9] + # [sub]domain begins with alphanumeric ( [-\w]* # alphanumeric, underscore, dot, hyphen [a-z0-9] # ending alphanumeric @@ -1057,9 +1073,9 @@ class IS_EMAIL(Validator): )+ [a-z]{2,} # TLD alpha-only )$ - ''', re.VERBOSE|re.IGNORECASE) + ''', re.VERBOSE | re.IGNORECASE) - regex_proposed_but_failed = re.compile('^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$',re.VERBOSE|re.IGNORECASE) + regex_proposed_but_failed = re.compile('^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$', re.VERBOSE | re.IGNORECASE) def __init__(self, banned=None, @@ -1153,7 +1169,7 @@ official_url_schemes = [ 'xmpp', 'z39.50r', 'z39.50s', - ] +] unofficial_url_schemes = [ 'about', 'adiumxtra', @@ -1207,7 +1223,7 @@ unofficial_url_schemes = [ 'xfire', 'xri', 'ymsgr', - ] +] all_url_schemes = [None] + official_url_schemes + unofficial_url_schemes http_schemes = [None, 'http', 'https'] @@ -1298,7 +1314,7 @@ def unicode_to_ascii_authority(authority): #don't modify the URL asciiLabels.append('') except: - asciiLabels=[str(label) for label in labels] + asciiLabels = [str(label) for label in labels] #RFC 3490, Section 4, Step 5 return str(reduce(lambda x, y: x + unichr(0x002E) + y, asciiLabels)) @@ -1345,8 +1361,8 @@ def unicode_to_ascii_url(url, prepend_scheme): unicode(scheme_to_prepend) + u'://' + url).groups() #if we still can't find the authority if not groups[3]: - raise Exception('No authority component found, '+ \ - 'could not decode unicode to US-ASCII') + raise Exception('No authority component found, ' + + 'could not decode unicode to US-ASCII') #We're here if we found an authority, let's rebuild the URL scheme = groups[1] @@ -1395,13 +1411,12 @@ class IS_GENERIC_URL(Validator): """ - def __init__( self, error_message='enter a valid URL', allowed_schemes=None, prepend_scheme=None, - ): + ): """ :param error_message: a string, the error message to give the end user if the URL does not validate @@ -1418,9 +1433,8 @@ class IS_GENERIC_URL(Validator): self.allowed_schemes = allowed_schemes self.prepend_scheme = prepend_scheme if self.prepend_scheme not in self.allowed_schemes: - raise SyntaxError, \ - "prepend_scheme='%s' is not in allowed_schemes=%s" \ - % (self.prepend_scheme, self.allowed_schemes) + raise SyntaxError("prepend_scheme='%s' is not in allowed_schemes=%s" + % (self.prepend_scheme, self.allowed_schemes)) GENERIC_URL = re.compile(r"%[^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]$") GENERIC_URL_VALID = re.compile(r"[A-Za-z0-9;/?:@&=+$,\-_\.!~*'\(\)%#]+$") @@ -1452,7 +1466,7 @@ class IS_GENERIC_URL(Validator): # ports, check to see if adding a valid scheme fixes # the problem (but only do this if it doesn't have # one already!) - if value.find('://')<0 and None in self.allowed_schemes: + if value.find('://') < 0 and None in self.allowed_schemes: schemeToUse = self.prepend_scheme or 'http' prependTest = self.__call__( schemeToUse + '://' + value) @@ -1763,7 +1777,7 @@ official_top_level_domains = [ 'za', 'zm', 'zw', - ] +] class IS_HTTP_URL(Validator): @@ -1810,7 +1824,8 @@ class IS_HTTP_URL(Validator): """ - GENERIC_VALID_IP = re.compile("([\w.!~*'|;:&=+$,-]+@)?\d+\.\d+\.\d+\.\d+(:\d*)*$") + GENERIC_VALID_IP = re.compile( + "([\w.!~*'|;:&=+$,-]+@)?\d+\.\d+\.\d+\.\d+(:\d*)*$") GENERIC_VALID_DOMAIN = re.compile("([\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*)*$") def __init__( @@ -1818,7 +1833,7 @@ class IS_HTTP_URL(Validator): error_message='enter a valid URL', allowed_schemes=None, prepend_scheme='http', - ): + ): """ :param error_message: a string, the error message to give the end user if the URL does not validate @@ -1837,14 +1852,12 @@ class IS_HTTP_URL(Validator): for i in self.allowed_schemes: if i not in http_schemes: - raise SyntaxError, \ - "allowed_scheme value '%s' is not in %s" % \ - (i, http_schemes) + raise SyntaxError("allowed_scheme value '%s' is not in %s" % + (i, http_schemes)) if self.prepend_scheme not in self.allowed_schemes: - raise SyntaxError, \ - "prepend_scheme='%s' is not in allowed_schemes=%s" % \ - (self.prepend_scheme, self.allowed_schemes) + raise SyntaxError("prepend_scheme='%s' is not in allowed_schemes=%s" % + (self.prepend_scheme, self.allowed_schemes)) def __call__(self, value): """ @@ -1870,11 +1883,12 @@ class IS_HTTP_URL(Validator): return (value, None) else: # else if authority is a valid domain name - domainMatch = self.GENERIC_VALID_DOMAIN.match(authority) + domainMatch = self.GENERIC_VALID_DOMAIN.match( + authority) if domainMatch: # if the top-level domain really exists if domainMatch.group(5).lower()\ - in official_top_level_domains: + in official_top_level_domains: # Then this HTTP URL is valid return (value, None) else: @@ -1889,10 +1903,10 @@ class IS_HTTP_URL(Validator): else: # abbreviated case: if we haven't already, prepend a # scheme and see if it fixes the problem - if value.find('://')<0: + if value.find('://') < 0: schemeToUse = self.prepend_scheme or 'http' prependTest = self.__call__(schemeToUse - + '://' + value) + + '://' + value) # if the prepend test succeeded if prependTest[1] is None: # if prepending in the output is enabled @@ -1989,7 +2003,7 @@ class IS_URL(Validator): mode='http', allowed_schemes=None, prepend_scheme='http', - ): + ): """ :param error_message: a string, the error message to give the end user if the URL does not validate @@ -2002,14 +2016,13 @@ class IS_URL(Validator): self.error_message = error_message self.mode = mode.lower() if not self.mode in ['generic', 'http']: - raise SyntaxError, "invalid mode '%s' in IS_URL" % self.mode + raise SyntaxError("invalid mode '%s' in IS_URL" % self.mode) self.allowed_schemes = allowed_schemes if self.allowed_schemes: if prepend_scheme not in self.allowed_schemes: - raise SyntaxError, \ - "prepend_scheme='%s' is not in allowed_schemes=%s" \ - % (prepend_scheme, self.allowed_schemes) + raise SyntaxError("prepend_scheme='%s' is not in allowed_schemes=%s" + % (prepend_scheme, self.allowed_schemes)) # if allowed_schemes is None, then we will defer testing # prepend_scheme's validity to a sub-method @@ -2036,7 +2049,7 @@ class IS_URL(Validator): allowed_schemes=self.allowed_schemes, prepend_scheme=self.prepend_scheme) else: - raise SyntaxError, "invalid mode '%s' in IS_URL" % self.mode + raise SyntaxError("invalid mode '%s' in IS_URL" % self.mode) if type(value) != unicode: return subMethod(value) @@ -2117,9 +2130,8 @@ class IS_TIME(Validator): if value.group('d') == 'pm' and 0 < h < 12: h = h + 12 if not (h in range(24) and m in range(60) and s - in range(60)): - raise ValueError\ - ('Hours or minutes or seconds are outside of allowed range') + in range(60)): + raise ValueError('Hours or minutes or seconds are outside of allowed range') value = datetime.time(h, m, s) return (value, None) except AttributeError: @@ -2145,8 +2157,8 @@ class IS_DATE(Validator): self.extremes = {} def __call__(self, value): - if isinstance(value,datetime.date): - return (value,None) + if isinstance(value, datetime.date): + return (value, None) try: (y, m, d, hh, mm, ss, t0, t1, t2) = \ time.strptime(value, str(self.format)) @@ -2160,11 +2172,11 @@ class IS_DATE(Validator): format = self.format year = value.year y = '%.4i' % year - format = format.replace('%y',y[-2:]) - format = format.replace('%Y',y) - if year<1900: + format = format.replace('%y', y[-2:]) + format = format.replace('%Y', y) + if year < 1900: year = 2000 - d = datetime.date(year,value.month,value.day) + d = datetime.date(year, value.month, value.day) return d.strftime(format) @@ -2181,19 +2193,19 @@ class IS_DATETIME(Validator): @staticmethod def nice(format): - code=(('%Y','1963'), - ('%y','63'), - ('%d','28'), - ('%m','08'), - ('%b','Aug'), - ('%B','August'), - ('%H','14'), - ('%I','02'), - ('%p','PM'), - ('%M','30'), - ('%S','59')) - for (a,b) in code: - format=format.replace(a,b) + code = (('%Y', '1963'), + ('%y', '63'), + ('%d', '28'), + ('%m', '08'), + ('%b', 'Aug'), + ('%B', 'August'), + ('%H', '14'), + ('%I', '02'), + ('%p', 'PM'), + ('%M', '30'), + ('%S', '59')) + for (a, b) in code: + format = format.replace(a, b) return dict(format=format) def __init__(self, format='%Y-%m-%d %H:%M:%S', @@ -2203,8 +2215,8 @@ class IS_DATETIME(Validator): self.extremes = {} def __call__(self, value): - if isinstance(value,datetime.datetime): - return (value,None) + if isinstance(value, datetime.datetime): + return (value, None) try: (y, m, d, hh, mm, ss, t0, t1, t2) = \ time.strptime(value, str(self.format)) @@ -2214,19 +2226,19 @@ class IS_DATETIME(Validator): self.extremes.update(IS_DATETIME.nice(self.format)) return (value, translate(self.error_message) % self.extremes) - def formatter(self, value): format = self.format year = value.year y = '%.4i' % year - format = format.replace('%y',y[-2:]) - format = format.replace('%Y',y) - if year<1900: + format = format.replace('%y', y[-2:]) + format = format.replace('%Y', y) + if year < 1900: year = 2000 - d = datetime.datetime(year,value.month,value.day, - value.hour,value.minute,value.second) + d = datetime.datetime(year, value.month, value.day, + value.hour, value.minute, value.second) return d.strftime(format) + class IS_DATE_IN_RANGE(IS_DATE): """ example:: @@ -2249,10 +2261,10 @@ class IS_DATE_IN_RANGE(IS_DATE): """ def __init__(self, - minimum = None, - maximum = None, + minimum=None, + maximum=None, format='%Y-%m-%d', - error_message = None): + error_message=None): self.minimum = minimum self.maximum = maximum if error_message is None: @@ -2263,12 +2275,12 @@ class IS_DATE_IN_RANGE(IS_DATE): else: error_message = "enter date in range %(min)s %(max)s" IS_DATE.__init__(self, - format = format, - error_message = error_message) + format=format, + error_message=error_message) self.extremes = dict(min=minimum, max=maximum) def __call__(self, value): - (value, msg) = IS_DATE.__call__(self,value) + (value, msg) = IS_DATE.__call__(self, value) if msg is not None: return (value, msg) if self.minimum and self.minimum > value: @@ -2299,10 +2311,10 @@ class IS_DATETIME_IN_RANGE(IS_DATETIME): (datetime.datetime(2010, 3, 3, 0, 0), 'oops') """ def __init__(self, - minimum = None, - maximum = None, - format = '%Y-%m-%d %H:%M:%S', - error_message = None): + minimum=None, + maximum=None, + format='%Y-%m-%d %H:%M:%S', + error_message=None): self.minimum = minimum self.maximum = maximum if error_message is None: @@ -2313,9 +2325,9 @@ class IS_DATETIME_IN_RANGE(IS_DATETIME): else: error_message = "enter date and time in range %(min)s %(max)s" IS_DATETIME.__init__(self, - format = format, - error_message = error_message) - self.extremes = dict(min = minimum, max = maximum) + format=format, + error_message=error_message) + self.extremes = dict(min=minimum, max=maximum) def __call__(self, value): (value, msg) = IS_DATETIME.__call__(self, value) @@ -2331,7 +2343,7 @@ class IS_DATETIME_IN_RANGE(IS_DATETIME): class IS_LIST_OF(Validator): def __init__(self, other=None, minimum=0, maximum=100, - error_message = None): + error_message=None): self.other = other self.minimum = minimum self.maximum = maximum @@ -2341,10 +2353,10 @@ class IS_LIST_OF(Validator): ivalue = value if not isinstance(value, list): ivalue = [ivalue] - if not self.minimum is None and len(ivalue)<self.minimum: - return (ivalue, translate(self.error_message) % dict(min=self.minimum,max=self.maximum)) - if not self.maximum is None and len(ivalue)>self.maximum: - return (ivalue, translate(self.error_message) % dict(min=self.minimum,max=self.maximum)) + if not self.minimum is None and len(ivalue) < self.minimum: + return (ivalue, translate(self.error_message) % dict(min=self.minimum, max=self.maximum)) + if not self.maximum is None and len(ivalue) > self.maximum: + return (ivalue, translate(self.error_message) % dict(min=self.minimum, max=self.maximum)) new_value = [] if self.other: for item in ivalue: @@ -2399,7 +2411,8 @@ def urlify(value, maxlen=80, keep_underscores=False): s = re.sub('&\w+;', '', s) # strip html entities if keep_underscores: s = re.sub('\s+', '-', s) # whitespace to hyphens - s = re.sub('[^\w\-]', '', s) # strip all but alphanumeric/underscore/hyphen + s = re.sub('[^\w\-]', '', s) + # strip all but alphanumeric/underscore/hyphen else: s = re.sub('[\s_]+', '-', s) # whitespace & underscores to hyphens s = re.sub('[^a-z0-9\-]', '', s) # strip all but alphanumeric/hyphen @@ -2465,7 +2478,8 @@ class IS_SLUG(Validator): def __call__(self, value): if self.check and value != urlify(value, self.maxlen, self.keep_underscores): return (value, translate(self.error_message)) - return (urlify(value,self.maxlen, self.keep_underscores), None) + return (urlify(value, self.maxlen, self.keep_underscores), None) + class IS_EMPTY_OR(Validator): """ @@ -2494,12 +2508,12 @@ class IS_EMPTY_OR(Validator): if hasattr(other, 'multiple'): self.multiple = other.multiple if hasattr(other, 'options'): - self.options=self._options + self.options = self._options def _options(self): options = self.other.options() - if (not options or options[0][0]!='') and not self.multiple: - options.insert(0,('','')) + if (not options or options[0][0] != '') and not self.multiple: + options.insert(0, ('', '')) return options def set_self_id(self, id): @@ -2519,7 +2533,8 @@ class IS_EMPTY_OR(Validator): error = None for item in self.other: value, error = item(value) - if error: break + if error: + break return value, error else: return self.other(value) @@ -2547,14 +2562,15 @@ class CLEANUP(Validator): else re.compile(regex) def __call__(self, value): - v = self.regex.sub('',str(value).strip()) + v = self.regex.sub('', str(value).strip()) return (v, None) + class LazyCrypt(object): """ Stores a lazy password hash """ - def __init__(self,crypt,password): + def __init__(self, crypt, password): """ crypt is an instance of the CRYPT validator, password is the password as inserted by the user @@ -2585,14 +2601,14 @@ class LazyCrypt(object): return self.crypted if self.crypt.key: if ':' in self.crypt.key: - digest_alg, key = self.crypt.key.split(':',1) + digest_alg, key = self.crypt.key.split(':', 1) else: digest_alg, key = self.crypt.digest_alg, self.crypt.key else: digest_alg, key = self.crypt.digest_alg, '' if self.crypt.salt: if self.crypt.salt == True: - salt = str(web2py_uuid()).replace('-','')[-16:] + salt = str(web2py_uuid()).replace('-', '')[-16:] else: salt = self.crypt.salt else: @@ -2614,13 +2630,13 @@ class LazyCrypt(object): key = '' if stored_password is None: return False - elif stored_password.count('$')==2: + elif stored_password.count('$') == 2: (digest_alg, salt, hash) = stored_password.split('$') h = simple_hash(self.password, key, salt, digest_alg) temp_pass = '%s$%s$%s' % (digest_alg, salt, h) - else: # no salting + else: # no salting # guess digest_alg - digest_alg = DIGEST_ALG_BY_SIZE.get(len(stored_password),None) + digest_alg = DIGEST_ALG_BY_SIZE.get(len(stored_password), None) if not digest_alg: return False else: @@ -2725,9 +2741,9 @@ class CRYPT(object): self.salt = salt def __call__(self, value): - if len(value)<self.min_length: + if len(value) < self.min_length: return ('', translate(self.error_message)) - return (LazyCrypt(self,value),None) + return (LazyCrypt(self, value), None) # entropy calculator for IS_STRONG # @@ -2736,7 +2752,9 @@ upperset = frozenset(unicode('ABCDEFGHIJKLMNOPQRSTUVWXYZ')) numberset = frozenset(unicode('0123456789')) sym1set = frozenset(unicode('!@#$%^&*()')) sym2set = frozenset(unicode('~`-_=+[]{}\\|;:\'",.<>?/')) -otherset = frozenset(unicode('0123456789abcdefghijklmnopqrstuvwxyz')) # anything else +otherset = frozenset( + unicode('0123456789abcdefghijklmnopqrstuvwxyz')) # anything else + def calc_entropy(string): " calculate a simple entropy for a given string " @@ -2764,9 +2782,11 @@ def calc_entropy(string): if inset is not lastset: alphabet += 1 # credit for set transitions lastset = cset - entropy = len(string) * math.log(alphabet) / 0.6931471805599453 # math.log(2) + entropy = len( + string) * math.log(alphabet) / 0.6931471805599453 # math.log(2) return round(entropy, 2) + class IS_STRONG(object): """ example:: @@ -2777,7 +2797,8 @@ class IS_STRONG(object): enforces complexity requirements on a field >>> IS_STRONG(es=True)('Abcd1234') - ('Abcd1234', 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|') + ('Abcd1234', + 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|') >>> IS_STRONG(es=True)('Abcd1234!') ('Abcd1234!', None) >>> IS_STRONG(es=True, entropy=1)('a') @@ -2828,8 +2849,8 @@ class IS_STRONG(object): if self.entropy is not None: entropy = calc_entropy(value) if entropy < self.entropy: - failures.append(translate("Entropy (%(have)s) less than required (%(need)s)") \ - % dict(have=entropy, need=self.entropy)) + failures.append(translate("Entropy (%(have)s) less than required (%(need)s)") + % dict(have=entropy, need=self.entropy)) if type(self.min) == int and self.min > 0: if not len(value) >= self.min: failures.append(translate("Minimum length is %s") % self.min) @@ -2840,31 +2861,33 @@ class IS_STRONG(object): all_special = [ch in value for ch in self.specials] if self.special > 0: if not all_special.count(True) >= self.special: - failures.append(translate("Must include at least %s of the following: %s") \ - % (self.special, self.specials)) + failures.append(translate("Must include at least %s of the following: %s") + % (self.special, self.specials)) if self.invalid: all_invalid = [ch in value for ch in self.invalid] if all_invalid.count(True) > 0: - failures.append(translate("May not contain any of the following: %s") \ - % self.invalid) + failures.append(translate("May not contain any of the following: %s") + % self.invalid) if type(self.upper) == int: all_upper = re.findall("[A-Z]", value) if self.upper > 0: if not len(all_upper) >= self.upper: - failures.append(translate("Must include at least %s upper case") \ - % str(self.upper)) + failures.append(translate("Must include at least %s upper case") + % str(self.upper)) else: if len(all_upper) > 0: - failures.append(translate("May not include any upper case letters")) + failures.append( + translate("May not include any upper case letters")) if type(self.lower) == int: all_lower = re.findall("[a-z]", value) if self.lower > 0: if not len(all_lower) >= self.lower: - failures.append(translate("Must include at least %s lower case") \ - % str(self.lower)) + failures.append(translate("Must include at least %s lower case") + % str(self.lower)) else: if len(all_lower) > 0: - failures.append(translate("May not include any lower case letters")) + failures.append( + translate("May not include any lower case letters")) if type(self.number) == int: all_number = re.findall("[0-9]", value) if self.number > 0: @@ -2872,8 +2895,8 @@ class IS_STRONG(object): if self.number > 1: numbers = "numbers" if not len(all_number) >= self.number: - failures.append(translate("Must include at least %s %s") \ - % (str(self.number), numbers)) + failures.append(translate("Must include at least %s %s") + % (str(self.number), numbers)) else: if len(all_number) > 0: failures.append(translate("May not include any numbers")) @@ -3050,7 +3073,7 @@ class IS_UPLOAD_FILENAME(Validator): """ def __init__(self, filename=None, extension=None, lastdot=True, case=1, - error_message='enter valid filename'): + error_message='enter valid filename'): if isinstance(filename, str): filename = re.compile(filename) if isinstance(extension, str): @@ -3193,7 +3216,7 @@ class IS_IPV4(Validator): is_localhost=None, is_private=None, is_automatic=None, - error_message='enter valid IPv4 address'): + error_message='enter valid IPv4 address'): for n, value in enumerate((minip, maxip)): temp = [] if isinstance(value, str): @@ -3232,14 +3255,14 @@ class IS_IPV4(Validator): for bottom, top in zip(self.minip, self.maxip): if self.invert != (bottom <= number <= top): ok = True - if not (self.is_localhost is None or self.is_localhost == \ - (number == self.localhost)): + if not (self.is_localhost is None or self.is_localhost == + (number == self.localhost)): ok = False - if not (self.is_private is None or self.is_private == \ - (sum([number[0] <= number <= number[1] for number in self.private]) > 0)): + if not (self.is_private is None or self.is_private == + (sum([number[0] <= number <= number[1] for number in self.private]) > 0)): ok = False - if not (self.is_automatic is None or self.is_automatic == \ - (self.automatic[0] <= number <= self.automatic[1])): + if not (self.is_automatic is None or self.is_automatic == + (self.automatic[0] <= number <= self.automatic[1])): ok = False if ok: return (value, None) @@ -3247,4 +3270,5 @@ class IS_IPV4(Validator): if __name__ == '__main__': import doctest - doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS) + doctest.testmod( + optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS) diff --git a/gluon/widget.py b/gluon/widget.py index 18b98bab..d4a38d95 100644 --- a/gluon/widget.py +++ b/gluon/widget.py @@ -30,7 +30,8 @@ from settings import global_settings from shell import run, test try: - import Tkinter, tkMessageBox + import Tkinter + import tkMessageBox import contrib.taskbar_widget from winservice import web2py_windows_service_handler have_winservice = True @@ -44,7 +45,8 @@ except NameError: BaseException = Exception ProgramName = 'web2py Web Framework' -ProgramAuthor = 'Created by Massimo Di Pierro, Copyright 2007-' + str(datetime.datetime.now().year) +ProgramAuthor = 'Created by Massimo Di Pierro, Copyright 2007-' + str( + datetime.datetime.now().year) ProgramVersion = read_file('VERSION').strip() ProgramInfo = '''%s @@ -58,6 +60,7 @@ if not sys.version[:3] in ['2.4', '2.5', '2.6', '2.7']: logger = logging.getLogger("web2py") + def run_system_tests(): major_version = sys.version_info[0] minor_version = sys.version_info[1] @@ -77,6 +80,7 @@ def run_system_tests(): ret = 256 sys.exit(ret and 1) + class IO(object): """ """ @@ -141,7 +145,7 @@ def presentation(root): pnl = Tkinter.Label(canvas, image=img, background='white', bd=0) pnl.pack(side='top', fill='both', expand='yes') # Prevent garbage collection of img - pnl.image=img + pnl.image = img def add_label(text='Change Me', font_size=12, foreground='#195866', height=1): return Tkinter.Label( @@ -153,7 +157,7 @@ def presentation(root): anchor=Tkinter.CENTER, foreground=foreground, background='white' - ) + ) add_label('Welcome to...').pack(side='top') add_label(ProgramName, 18, '#FF5C1F', 2).pack() @@ -231,17 +235,18 @@ class web2pyDialog(object): sticky=sticky) self.ips = {} self.selected_ip = Tkinter.StringVar() - row=0 - ips = [('127.0.0.1','Local')] + \ - [(ip,'Public') for ip in options.ips] + \ - [('0.0.0.0','Public')] - for ip,legend in ips: + row = 0 + ips = [('127.0.0.1', 'Local')] + \ + [(ip, 'Public') for ip in options.ips] + \ + [('0.0.0.0', 'Public')] + for ip, legend in ips: self.ips[ip] = Tkinter.Radiobutton( - self.root,text='%s (%s)' % (legend,ip), + self.root, text='%s (%s)' % (legend, ip), variable=self.selected_ip, value=ip) self.ips[ip].grid(row=row, column=1, sticky=sticky) - if row==0: self.ips[ip].select() - row+=1 + if row == 0: + self.ips[ip].select() + row += 1 shift = row # Port Tkinter.Label(self.root, @@ -257,26 +262,26 @@ class web2pyDialog(object): # Password Tkinter.Label(self.root, text='Choose Password:', - justify=Tkinter.LEFT).grid(row=shift+1, + justify=Tkinter.LEFT).grid(row=shift + 1, column=0, sticky=sticky) self.password = Tkinter.Entry(self.root, show='*') self.password.bind('<Return>', lambda e: self.start()) self.password.focus_force() - self.password.grid(row=shift+1, column=1, sticky=sticky) + self.password.grid(row=shift + 1, column=1, sticky=sticky) # Prepare the canvas self.canvas = Tkinter.Canvas(self.root, width=300, height=100, bg='black') - self.canvas.grid(row=shift+2, column=0, columnspan=2) + self.canvas.grid(row=shift + 2, column=0, columnspan=2) self.canvas.after(1000, self.update_canvas) # Prepare the frame frame = Tkinter.Frame(self.root) - frame.grid(row=shift+3, column=0, columnspan=2) + frame.grid(row=shift + 3, column=0, columnspan=2) # Start button self.button_start = Tkinter.Button(frame, @@ -309,12 +314,12 @@ class web2pyDialog(object): apps = [] available_apps = [arq for arq in os.listdir('applications/')] available_apps = [arq for arq in available_apps - if os.path.exists('applications/%s/models/scheduler.py' % arq)] + if os.path.exists('applications/%s/models/scheduler.py' % arq)] if start: #the widget takes care of starting the scheduler if self.options.scheduler and self.options.with_scheduler: apps = [app.strip() for app in self.options.scheduler.split(',') - if app in available_apps] + if app in available_apps] for app in apps: self.try_start_scheduler(app) @@ -324,11 +329,11 @@ class web2pyDialog(object): if arq not in self.scheduler_processes: item = lambda u = arq: self.try_start_scheduler(u) self.schedmenu.add_command(label="start %s" % arq, - command=item) + command=item) if arq in self.scheduler_processes: item = lambda u = arq: self.try_stop_scheduler(u) self.schedmenu.add_command(label="stop %s" % arq, - command=item) + command=item) def start_schedulers(self, app): try: @@ -338,12 +343,13 @@ class web2pyDialog(object): return code = "from gluon import current;current._scheduler.loop()" print 'starting scheduler from widget for "%s"...' % app - args = (app,True,True,None,False,code) + args = (app, True, True, None, False, code) logging.getLogger().setLevel(self.options.debuglevel) p = Process(target=run, args=args) self.scheduler_processes[app] = p self.update_schedulers() - print "Currently running %s scheduler processes" % (len(self.scheduler_processes)) + print "Currently running %s scheduler processes" % ( + len(self.scheduler_processes)) p.start() print "Processes started" @@ -360,7 +366,6 @@ class web2pyDialog(object): t = threading.Thread(target=self.start_schedulers, args=(app,)) t.start() - def checkTaskBar(self): """ Check taskbar status """ @@ -573,7 +578,8 @@ def console(): description = textwrap.dedent(description) - parser = optparse.OptionParser(usage, None, optparse.Option, ProgramVersion) + parser = optparse.OptionParser( + usage, None, optparse.Option, ProgramVersion) parser.description = description @@ -857,7 +863,6 @@ def console(): dest='nobanner', help='Do not print header banner') - msg = 'listen on multiple addresses: "ip:port:cert:key:ca_cert;ip2:port2:cert2:key2:ca_cert2;..." (:cert:key optional; no spaces)' parser.add_option('--interfaces', action='store', @@ -865,7 +870,6 @@ def console(): default=None, help=msg) - msg = 'runs web2py tests' parser.add_option('--run_system_tests', action='store_true', @@ -873,10 +877,13 @@ def console(): default=False, help=msg) - if '-A' in sys.argv: k = sys.argv.index('-A') - elif '--args' in sys.argv: k = sys.argv.index('--args') - else: k=len(sys.argv) - sys.argv, other_args = sys.argv[:k], sys.argv[k+1:] + if '-A' in sys.argv: + k = sys.argv.index('-A') + elif '--args' in sys.argv: + k = sys.argv.index('--args') + else: + k = len(sys.argv) + sys.argv, other_args = sys.argv[:k], sys.argv[k + 1:] (options, args) = parser.parse_args() options.args = [options.run] + other_args global_settings.cmd_options = options @@ -885,7 +892,7 @@ def console(): try: options.ips = [ ip for ip in socket.gethostbyname_ex(socket.getfqdn())[2] - if ip!='127.0.0.1'] + if ip != '127.0.0.1'] except socket.gaierror: options.ips = [] @@ -943,7 +950,7 @@ def console(): if not os.path.exists('welcome.w2p') or os.path.exists('NEWINSTALL'): try: - w2p_pack('welcome.w2p','applications/welcome') + w2p_pack('welcome.w2p', 'applications/welcome') os.unlink('NEWINSTALL') except: msg = "New installation: unable to create welcome.w2p file" @@ -951,12 +958,14 @@ def console(): return (options, args) -def check_existent_app(options,appname): + +def check_existent_app(options, appname): if os.path.isdir(os.path.join(options.folder, 'applications', appname)): return True + def get_code_for_scheduler(app, options): - if len(app) == 1 or app[1] == None: + if len(app) == 1 or app[1] is None: code = "from gluon import current;current._scheduler.loop()" else: code = "from gluon import current;current._scheduler.group_names = ['%s'];" @@ -968,6 +977,7 @@ def get_code_for_scheduler(app, options): return None, None return app_, code + def start_schedulers(options): try: from multiprocessing import Process @@ -985,14 +995,14 @@ def start_schedulers(options): if not app_: return print 'starting single-scheduler for "%s"...' % app_ - run(app_,True,True,None,False,code) + run(app_, True, True, None, False, code) return for app in apps: app_, code = get_code_for_scheduler(app, options) if not app_: continue print 'starting scheduler for "%s"...' % app_ - args = (app_,True,True,None,False,code) + args = (app_, True, True, None, False, code) p = Process(target=run, args=args) processes.append(p) print "Currently running %s scheduler processes" % (len(processes)) @@ -1024,7 +1034,6 @@ def start(cron=True): if not options.nobanner: print 'Database drivers available: %s' % ', '.join(DRIVERS) - # ## if -L load options from options.config file if options.config: try: @@ -1037,8 +1046,8 @@ def start(cron=True): print 'Cannot import config file [%s]' % options.config sys.exit(1) for key in dir(options2): - if hasattr(options,key): - setattr(options,key,getattr(options2,key)) + if hasattr(options, key): + setattr(options, key, getattr(options2, key)) if False and not os.path.exists('logging.conf') and \ os.path.exists('logging.example.conf'): @@ -1048,7 +1057,7 @@ def start(cron=True): sys.stdout.write("OK\n") # ## if -T run doctests (no cron) - if hasattr(options,'test') and options.test: + if hasattr(options, 'test') and options.test: test(options.test, verbose=options.verbose) return @@ -1066,7 +1075,8 @@ def start(cron=True): logger.debug('Starting extcron...') global_settings.web2py_crontype = 'external' if options.scheduler: # -K - apps = [app.strip() for app in options.scheduler.split(',') if check_existent_app(options, app.strip())] + apps = [app.strip() for app in options.scheduler.split( + ',') if check_existent_app(options, app.strip())] else: apps = None extcron = newcron.extcron(options.folder, apps=apps) @@ -1082,7 +1092,6 @@ def start(cron=True): pass return - # ## if -H cron is enabled in this *process* # ## if --softcron use softcron # ## use hardcron in all other cases @@ -1127,7 +1136,8 @@ def start(cron=True): import Tkinter havetk = True except ImportError: - logger.warn('GUI not available because Tk library is not installed') + logger.warn( + 'GUI not available because Tk library is not installed') havetk = False if options.password == '<ask>' and havetk or options.taskbar and havetk: diff --git a/gluon/winservice.py b/gluon/winservice.py index 3d448496..38efe812 100644 --- a/gluon/winservice.py +++ b/gluon/winservice.py @@ -30,6 +30,7 @@ from fileutils import up __all__ = ['web2py_windows_service_handler'] + class Service(win32serviceutil.ServiceFramework): _svc_name_ = '_unNamed' @@ -48,7 +49,7 @@ class Service(win32serviceutil.ServiceFramework): self.ReportServiceStatus(win32service.SERVICE_RUNNING) self.start() win32event.WaitForSingleObject(self.stop_event, - win32event.INFINITE) + win32event.INFINITE) except: self.log(traceback.format_exc(sys.exc_info)) self.SvcStop() @@ -85,7 +86,7 @@ class Web2pyService(Service): try: h = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, r'SYSTEM\CurrentControlSet\Services\%s' - % self._svc_name_) + % self._svc_name_) try: cls = _winreg.QueryValue(h, 'PythonClass') finally: @@ -108,11 +109,13 @@ class Web2pyService(Service): else: opt_mod = self._exe_args_ options = __import__(opt_mod, [], [], '') - if True: # legacy support for old options files, which have only (deprecated) numthreads + if True: # legacy support for old options files, which have only (deprecated) numthreads if hasattr(options, 'numthreads') and not hasattr(options, 'minthreads'): options.minthreads = options.numthreads - if not hasattr(options, 'minthreads'): options.minthreads = None - if not hasattr(options, 'maxthreads'): options.maxthreads = None + if not hasattr(options, 'minthreads'): + options.minthreads = None + if not hasattr(options, 'maxthreads'): + options.maxthreads = None import main self.server = main.HttpServer( ip=options.ip, @@ -130,7 +133,7 @@ class Web2pyService(Service): timeout=options.timeout, shutdown_timeout=options.shutdown_timeout, path=options.folder - ) + ) try: self.server.start() except: @@ -152,17 +155,17 @@ class Web2pyService(Service): def web2py_windows_service_handler(argv=None, opt_file='options'): path = os.path.dirname(__file__) web2py_path = up(path) - if web2py_path.endswith('.zip'): # in case bianry distro 'library.zip' + if web2py_path.endswith('.zip'): # in case bianry distro 'library.zip' web2py_path = os.path.dirname(web2py_path) os.chdir(web2py_path) classstring = os.path.normpath( - os.path.join(web2py_path,'gluon.winservice.Web2pyService')) + os.path.join(web2py_path, 'gluon.winservice.Web2pyService')) if opt_file: Web2pyService._exe_args_ = opt_file win32serviceutil.HandleCommandLine(Web2pyService, - serviceClassString=classstring, argv=['', 'install']) + serviceClassString=classstring, argv=['', 'install']) win32serviceutil.HandleCommandLine(Web2pyService, - serviceClassString=classstring, argv=argv) + serviceClassString=classstring, argv=argv) if __name__ == '__main__': web2py_windows_service_handler() diff --git a/isapiwsgihandler.py b/isapiwsgihandler.py index ce10023c..e3f38789 100644 --- a/isapiwsgihandler.py +++ b/isapiwsgihandler.py @@ -3,21 +3,23 @@ web2py handler for isapi-wsgi for IIS. Requires: http://code.google.com/p/isapi-wsgi/ """ # The entry point for the ISAPI extension. + + def __ExtensionFactory__(): import os import sys path = os.path.dirname(os.path.abspath(__file__)) os.chdir(path) - sys.path = [path]+[p for p in sys.path if not p==path] + sys.path = [path] + [p for p in sys.path if not p == path] import gluon.main import isapi_wsgi - application=gluon.main.wsgibase + application = gluon.main.wsgibase return isapi_wsgi.ISAPIThreadPoolHandler(application) # ISAPI installation: -if __name__=='__main__': +if __name__ == '__main__': import sys - if len(sys.argv)<2: + if len(sys.argv) < 2: print "USAGE: python isapiwsgihandler.py install --server=Sitename" sys.exit(0) from isapi.install import ISAPIParameters @@ -26,10 +28,10 @@ if __name__=='__main__': from isapi.install import HandleCommandLine params = ISAPIParameters() - sm = [ ScriptMapParams(Extension="*", Flags=0) ] + sm = [ScriptMapParams(Extension="*", Flags=0)] vd = VirtualDirParameters(Name="appname", - Description = "Web2py in Python", - ScriptMaps = sm, - ScriptMapUpdate = "replace") + Description="Web2py in Python", + ScriptMaps=sm, + ScriptMapUpdate="replace") params.VirtualDirs = [vd] HandleCommandLine(params) diff --git a/modpythonhandler.py b/modpythonhandler.py index ae2fa49e..de4ee65f 100755 --- a/modpythonhandler.py +++ b/modpythonhandler.py @@ -34,7 +34,7 @@ from mod_python import apache path = os.path.dirname(os.path.abspath(__file__)) os.chdir(path) -sys.path = [path]+[p for p in sys.path if not p==path] +sys.path = [path] + [p for p in sys.path if not p == path] import gluon.main diff --git a/options_std.py b/options_std.py index feb0a48f..c531e7f3 100644 --- a/options_std.py +++ b/options_std.py @@ -14,13 +14,14 @@ import os ip = '0.0.0.0' port = 80 -interfaces=[('0.0.0.0',80)] #,('0.0.0.0',443,'ssl_private_key.pem','ssl_certificate.pem')] +interfaces = [('0.0.0.0', 80)] + #,('0.0.0.0',443,'ssl_private_key.pem','ssl_certificate.pem')] password = '<recycle>' # ## <recycle> means use the previous password pid_filename = 'httpserver.pid' log_filename = 'httpserver.log' profiler_filename = None -ssl_certificate = None #'ssl_certificate.pem' # ## path to certificate file -ssl_private_key = None #'ssl_private_key.pem' # ## path to private key file +ssl_certificate = None # 'ssl_certificate.pem' # ## path to certificate file +ssl_private_key = None # 'ssl_private_key.pem' # ## path to private key file #numthreads = 50 # ## deprecated; remove minthreads = None maxthreads = None diff --git a/router.example.py b/router.example.py index f2e11498..8822ba54 100644 --- a/router.example.py +++ b/router.example.py @@ -102,8 +102,8 @@ routers = dict( # base router - BASE = dict( - default_application = 'welcome', + BASE=dict( + default_application='welcome', ), ) @@ -145,6 +145,7 @@ logging = 'debug' # error_message = '<html><body><h1>%s</h1></body></html>' # error_message_ticket = '<html><body><h1>Internal error</h1>Ticket issued: <a href="/admin/default/ticket/%(ticket)s" target="_blank">%(ticket)s</a></body></html>' + def __routes_doctest(): ''' Dummy function for doctesting routes.py. diff --git a/routes.example.py b/routes.example.py index cab718b3..fdcfebc6 100644 --- a/routes.example.py +++ b/routes.example.py @@ -31,25 +31,25 @@ routes_app = ((r'/(?P<app>welcome|admin|app)\b.*', r'\g<app>'), # routes_in=( (r'/static/(?P<file>[\w./-]+)', r'/init/static/\g<file>') ) # -BASE = '' # optonal prefix for incoming URLs +BASE = '' # optonal prefix for incoming URLs routes_in = ( # do not reroute admin unless you want to disable it - (BASE+'/admin','/admin/default/index'), - (BASE+'/admin/$anything','/admin/$anything'), + (BASE + '/admin', '/admin/default/index'), + (BASE + '/admin/$anything', '/admin/$anything'), # do not reroute appadmin unless you want to disable it - (BASE+'/$app/appadmin','/$app/appadmin/index'), - (BASE+'/$app/appadmin/$anything','/$app/appadmin/$anything'), + (BASE + '/$app/appadmin', '/$app/appadmin/index'), + (BASE + '/$app/appadmin/$anything', '/$app/appadmin/$anything'), # do not reroute static files - (BASE+'/$app/static/$anything','/$app/static/$anything'), + (BASE + '/$app/static/$anything', '/$app/static/$anything'), # reroute favicon and robots, use exable for lack of better choice ('/favicon.ico', '/examples/static/favicon.ico'), ('/robots.txt', '/examples/static/robots.txt'), # do other stuff ((r'.*http://otherdomain.com.* (?P<any>.*)', r'/app/ctr\g<any>')), # remove the BASE prefix - (BASE+'/$anything','/$anything'), - ) + (BASE + '/$anything', '/$anything'), +) # routes_out, like routes_in translates URL paths created with the web2py URL() # function in the same manner that route_in translates inbound URL paths. @@ -57,16 +57,16 @@ routes_in = ( routes_out = ( # do not reroute admin unless you want to disable it - ('/admin/$anything', BASE+'/admin/$anything'), + ('/admin/$anything', BASE + '/admin/$anything'), # do not reroute appadmin unless you want to disable it - ('/$app/appadmin/$anything',BASE+'/$app/appadmin/$anything'), + ('/$app/appadmin/$anything', BASE + '/$app/appadmin/$anything'), # do not reroute static files - ('/$app/static/$anything', BASE+'/$app/static/$anything'), + ('/$app/static/$anything', BASE + '/$app/static/$anything'), # do other stuff (r'.*http://otherdomain.com.* /app/ctr(?P<any>.*)', r'\g<any>'), (r'/app(?P<any>.*)', r'\g<any>'), # restore the BASE prefix - ('/$anything',BASE+'/$anything'), + ('/$anything', BASE + '/$anything'), ) # Specify log level for rewrite's debug logging @@ -112,6 +112,7 @@ logging = 'debug' #routes_apps_raw=['myapp'] #routes_apps_raw=['myapp', 'myotherapp'] + def __routes_doctest(): ''' Dummy function for doctesting routes.py. diff --git a/scgihandler.py b/scgihandler.py index acb055f8..c7f51cfd 100755 --- a/scgihandler.py +++ b/scgihandler.py @@ -47,7 +47,7 @@ import os path = os.path.dirname(os.path.abspath(__file__)) os.chdir(path) -sys.path = [path]+[p for p in sys.path if not p==path] +sys.path = [path] + [p for p in sys.path if not p == path] import gluon.main @@ -56,7 +56,7 @@ import gluon.main from wsgitools.scgi.forkpool import SCGIServer from wsgitools.filters import WSGIFilterMiddleware, GzipWSGIFilter -wsgiapp=WSGIFilterMiddleware(gluon.main.wsgibase, GzipWSGIFilter) +wsgiapp = WSGIFilterMiddleware(gluon.main.wsgibase, GzipWSGIFilter) if LOGGING: application = gluon.main.appfactory(wsgiapp=wsgiapp, diff --git a/setup.py b/setup.py index 37e06275..b1ce74f7 100644 --- a/setup.py +++ b/setup.py @@ -5,6 +5,7 @@ from gluon.fileutils import tar, untar, read_file, write_file import tarfile import sys + def tar(file, filelist, expression='^.+$'): """ tars dir/files into file, only tars file that match expression @@ -21,9 +22,10 @@ def tar(file, filelist, expression='^.+$'): finally: tar.close() + def start(): if 'sdist' in sys.argv: - tar('gluon/env.tar',['applications','VERSION','splashlogo.gif']) + tar('gluon/env.tar', ['applications', 'VERSION', 'splashlogo.gif']) setup(name='web2py', version=read_file("VERSION").split()[1], @@ -45,10 +47,10 @@ def start(): """, author='Massimo Di Pierro', author_email='mdipierro@cs.depaul.edu', - license = 'http://web2py.com/examples/default/license', - classifiers = ["Development Status :: 5 - Production/Stable"], + license='http://web2py.com/examples/default/license', + classifiers=["Development Status :: 5 - Production/Stable"], url='http://web2py.com', - platforms ='Windows, Linux, Mac, Unix,Windows Mobile', + platforms='Windows, Linux, Mac, Unix,Windows Mobile', packages=['gluon', 'gluon/contrib', 'gluon/contrib/gateways', @@ -63,8 +65,8 @@ def start(): 'gluon/contrib/simplejson', 'gluon/tests', ], - package_data = {'gluon':['env.tar']}, - scripts = ['w2p_apps','w2p_run','w2p_clone'], + package_data={'gluon': ['env.tar']}, + scripts=['w2p_apps', 'w2p_run', 'w2p_clone'], ) if __name__ == '__main__': diff --git a/setup_app.py b/setup_app.py index 11df7c33..c26c61bc 100755 --- a/setup_app.py +++ b/setup_app.py @@ -13,12 +13,14 @@ from gluon.import_all import base_modules, contributed_modules import os import fnmatch + class reglob: def __init__(self, directory, pattern="*"): self.stack = [directory] self.pattern = pattern self.files = [] self.index = 0 + def __getitem__(self, index): while 1: try: @@ -38,17 +40,17 @@ class reglob: setup(app=['web2py.py'], data_files=[ - 'NEWINSTALL', - 'ABOUT', - 'LICENSE', - 'VERSION', - ] + \ - [x for x in reglob('applications/examples')] + \ - [x for x in reglob('applications/welcome')] + \ - [x for x in reglob('applications/admin')], + 'NEWINSTALL', + 'ABOUT', + 'LICENSE', + 'VERSION', + ] + + [x for x in reglob('applications/examples')] + + [x for x in reglob('applications/welcome')] + + [x for x in reglob('applications/admin')], options={'py2app': { - 'argv_emulation': True, - 'includes': base_modules, - 'packages': contributed_modules, - }}, + 'argv_emulation': True, + 'includes': base_modules, + 'packages': contributed_modules, + }}, setup_requires=['py2app']) diff --git a/setup_exe.py b/setup_exe.py index 92d29639..2da2cf69 100755 --- a/setup_exe.py +++ b/setup_exe.py @@ -62,16 +62,16 @@ if python_version == '2.6': setup( - console=['web2py.py'], - windows=[{'script':'web2py.py', - 'dest_base':'web2py_no_console' # MUST NOT be just 'web2py' otherwise it overrides the standard web2py.exe - }], - name="web2py", - version=web2py_version, - description="web2py web framework", - author="Massimo DiPierro", - license = "LGPL v3", - data_files=[ + console=['web2py.py'], + windows=[{'script':'web2py.py', + 'dest_base':'web2py_no_console' # MUST NOT be just 'web2py' otherwise it overrides the standard web2py.exe + }], + name="web2py", + version=web2py_version, + description="web2py web framework", + author="Massimo DiPierro", + license="LGPL v3", + data_files=[ 'ABOUT', 'LICENSE', 'VERSION', @@ -80,20 +80,21 @@ setup( 'options_std.py', 'app.example.yaml', 'queue.example.yaml' - ], - options={'py2exe': { - 'packages': contributed_modules, - 'includes': base_modules, - }}, - ) + ], + options={'py2exe': { + 'packages': contributed_modules, + 'includes': base_modules, + }}, +) print "web2py binary successfully built" + def copy_folders(source, destination): """Copy files & folders from source to destination (within dist/)""" - if os.path.exists(os.path.join('dist',destination)): - shutil.rmtree(os.path.join('dist',destination)) - shutil.copytree(os.path.join(source), os.path.join('dist',destination)) + if os.path.exists(os.path.join('dist', destination)): + shutil.rmtree(os.path.join('dist', destination)) + shutil.copytree(os.path.join(source), os.path.join('dist', destination)) #should we remove Windows OS dlls user is unlikely to be able to distribute @@ -101,15 +102,16 @@ if remove_msft_dlls: print "Deleted Microsoft files not licensed for open source distribution" print "You are still responsible for making sure you have the rights to distribute any other included files!" #delete the API-MS-Win-Core DLLs - for f in glob ('dist/API-MS-Win-*.dll'): - os.unlink (f) + for f in glob('dist/API-MS-Win-*.dll'): + os.unlink(f) #then delete some other files belonging to Microsoft - other_ms_files = ['KERNELBASE.dll', 'MPR.dll', 'MSWSOCK.dll', 'POWRPROF.dll'] + other_ms_files = ['KERNELBASE.dll', 'MPR.dll', 'MSWSOCK.dll', + 'POWRPROF.dll'] for f in other_ms_files: try: - os.unlink(os.path.join('dist',f)) + os.unlink(os.path.join('dist', f)) except: - print "unable to delete dist/"+f + print "unable to delete dist/" + f #sys.exit(1) @@ -144,29 +146,32 @@ else: pass - #borrowed from http://bytes.com/topic/python/answers/851018-how-zip-directory-python-using-zipfile -def recursive_zip(zipf, directory, folder = ""): - for item in os.listdir(directory): - if os.path.isfile(os.path.join(directory, item)): - zipf.write(os.path.join(directory, item), folder + os.sep + item) - elif os.path.isdir(os.path.join(directory, item)): - recursive_zip(zipf, os.path.join(directory, item), folder + os.sep + item) +def recursive_zip(zipf, directory, folder=""): + for item in os.listdir(directory): + if os.path.isfile(os.path.join(directory, item)): + zipf.write(os.path.join(directory, item), folder + os.sep + item) + elif os.path.isdir(os.path.join(directory, item)): + recursive_zip( + zipf, os.path.join(directory, item), folder + os.sep + item) #should we create a zip file of the build? if make_zip: #to keep consistent with how official web2py windows zip file is setup, #create a web2py folder & copy dist's files into it - shutil.copytree('dist','zip_temp/web2py') + shutil.copytree('dist', 'zip_temp/web2py') #create zip file #use filename specified via command line - zipf = zipfile.ZipFile(zip_filename+".zip", "w", compression=zipfile.ZIP_DEFLATED ) - path = 'zip_temp' #just temp so the web2py directory is included in our zip file - recursive_zip(zipf, path) #leave the first folder as None, as path is root. + zipf = zipfile.ZipFile( + zip_filename + ".zip", "w", compression=zipfile.ZIP_DEFLATED) + path = 'zip_temp' # just temp so the web2py directory is included in our zip file + recursive_zip( + zipf, path) # leave the first folder as None, as path is root. zipf.close() shutil.rmtree('zip_temp') - print "Your Windows binary version of web2py can be found in "+zip_filename+".zip" + print "Your Windows binary version of web2py can be found in " + \ + zip_filename + ".zip" print "You may extract the archive anywhere and then run web2py/web2py.exe" #should py2exe build files be removed? @@ -181,4 +186,4 @@ if not make_zip and not remove_build_files: print "Your Windows binary & associated files can also be found in /dist" print "Finished!" -print "Enjoy web2py " +web2py_version_line +print "Enjoy web2py " + web2py_version_line diff --git a/web2py.py b/web2py.py index 125ddcdb..1c6ac8ae 100755 --- a/web2py.py +++ b/web2py.py @@ -7,12 +7,12 @@ import sys if '__file__' in globals(): path = os.path.dirname(os.path.abspath(__file__)) elif hasattr(sys, 'frozen'): - path = os.path.dirname(os.path.abspath(sys.executable)) # for py2exe -else: #should never happen + path = os.path.dirname(os.path.abspath(sys.executable)) # for py2exe +else: # should never happen path = os.getcwd() os.chdir(path) -sys.path = [path]+[p for p in sys.path if not p == path] +sys.path = [path] + [p for p in sys.path if not p == path] # import gluon.import_all ##### This should be uncommented for py2exe.py import gluon.widget diff --git a/wsgihandler.py b/wsgihandler.py index d43f37ca..e98c45e4 100644 --- a/wsgihandler.py +++ b/wsgihandler.py @@ -26,9 +26,9 @@ import os path = os.path.dirname(os.path.abspath(__file__)) os.chdir(path) -sys.path = [path]+[p for p in sys.path if not p==path] +sys.path = [path] + [p for p in sys.path if not p == path] -sys.stdout=sys.stderr +sys.stdout = sys.stderr import gluon.main