diff --git a/CHANGELOG b/CHANGELOG index 8a8548d1..49a97ad1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,25 +1,35 @@ ## 2.1.0 -- if apps are accidentally deleted there is copy left in deposit folder -- db.table.field.epoch() counts seconds from epoch -- fixed many aith.wiki problems -- better welcome layout, thanks Paolo -- db.define_table(...,redefine=True) -- DAL, Row, and Rows object can now be pickled/unpickled, thanks to zombie DAL. -- admin uses codemirror -- support for auth.wiki(render='html') -- upgraded jQuery 1.8 -- upgraded Bootstrap 2.1 -- fixed problem with dropbox_account.py -- cache.with_prefix(cache.ram,'prefix') -- mail.send(...,sender='Mr X <%(sender)s>') -- allow entropy computation in IS_STRONG and web2py.js, thanks Jonathan and Niphlod -- renamed luon/contrib/comet_messaging.py -> gluon/contrib/websocket_messaging.py -- DAL support for SQL CASE, example: db().select(...query.case('true','false)) +- overall faster web2py +- when apps are deleted, a w2p copy left in deposit folder +- change in cron (it is now disabled by default). removed -N option and introduced -Y. - faster web2py_uuid() and request initialization logic, thanks Michele - static asset management, thanks Niphlod - improved mobile admin - request.requires_https and Auth(secure=True), thanks Yarin and Niphlod +- better custom_import (works per app and is faster), thanks Michele +- redis_sesssion.py, thanks Niphlod +- allow entropy computation in IS_STRONG and web2py.js, thanks Jonathan and Niphlod +- fixed many aith.wiki problems +- support for auth.wiki(render='html') +- better welcome layout, thanks Paolo +- db.define_table(...,redefine=True) +- DAL, Row, and Rows object can now be pickled/unpickled, thanks to zombie DAL. +- admin uses codemirror +- allow syntax auth = Auth(db).define_tables() +- better auth.wiki with preview, thanks Alan +- better auth.impersonate, thanks Alan +- upgraded jQuery 1.8 +- upgraded Bootstrap 2.1 +- fixed problem with dropbox_account.py +- many fixes to cache.ram, cache.disk, memcache and gae_memcache +- cache.with_prefix(cache.ram,'prefix') +- db.table.field.epoch() counts seconds from epoch +- DAL support for SQL CASE, example: db().select(...query.case('true','false)) +- DAL(...,do_connect=False) allows faking connections +- DAL(...,auto_import=True) now retieves some fiel attributes +- mail can specify a sender: mail.send(...,sender='Mr X <%(sender)s>') +- renamed gluon/contrib/comet_messaging.py -> gluon/contrib/websocket_messaging.py ## 2.0.1-11 diff --git a/Makefile b/Makefile index 0a7acd84..28c222ed 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ update: wget -O gluon/contrib/simplejsonrpc.py http://rad2py.googlecode.com/hg/ide2py/simplejsonrpc.py echo "remember that pymysql was tweaked" src: - echo 'Version 2.1.0 ('`date +%Y-%m-%d\ %H:%M:%S`') dev' > VERSION + echo 'Version 2.1.1 ('`date +%Y-%m-%d\ %H:%M:%S`') dev' > VERSION ### rm -f all junk files make clean ### clean up baisc apps @@ -130,5 +130,5 @@ pip: # after upload clean Web2py sources with rm -R ./dist # http://guide.python-distribute.org/creation.html python setup.py sdist - python setup.py register - python setup.py sdist upload + sudo python setup.py register + sudo python setup.py sdist upload diff --git a/VERSION b/VERSION index 968fa96d..827cea5a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.1.0 (2012-10-14 16:40:34) dev +Version 2.1.1 (2012-10-20 15:27:09) dev diff --git a/__init__.py b/__init__.py index 584ba879..e69de29b 100644 --- a/__init__.py +++ b/__init__.py @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/anyserver.py b/anyserver.py index 53985442..473cceaa 100644 --- a/anyserver.py +++ b/anyserver.py @@ -9,47 +9,54 @@ License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) This file is based, although a rewrite, on MIT-licensed code from the Bottle web framework. """ -import os, sys, optparse, urllib +import os +import sys +import optparse +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 @@ -59,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 @@ -106,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 @@ -118,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', @@ -163,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 @@ -190,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] @@ -211,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 environ.has_key('Content-Length'): - environ['CONTENT_LENGTH'] = environ['Content-Length'] # necessary for POST to work with Django + if 'Content-Length' in environ: + 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 @@ -230,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, @@ -254,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" @@ -278,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', @@ -300,13 +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/appengine_config.py b/appengine_config.py index 4bb11668..a36d1db6 100644 --- a/appengine_config.py +++ b/appengine_config.py @@ -2,8 +2,3 @@ def webapp_add_wsgi_middleware(app): from google.appengine.ext.appstats import recording app = recording.appstats_wsgi_middleware(app) return app - - - - - diff --git a/applications/__init__.py b/applications/__init__.py index e69de29b..8b137891 100644 --- a/applications/__init__.py +++ b/applications/__init__.py @@ -0,0 +1 @@ + diff --git a/applications/admin/__init__.py b/applications/admin/__init__.py index e69de29b..8b137891 100644 --- a/applications/admin/__init__.py +++ b/applications/admin/__init__.py @@ -0,0 +1 @@ + diff --git a/applications/admin/controllers/appadmin.py b/applications/admin/controllers/appadmin.py index c25fed29..f62af94c 100644 --- a/applications/admin/controllers/appadmin.py +++ b/applications/admin/controllers/appadmin.py @@ -23,7 +23,7 @@ remote_addr = request.env.remote_addr try: hosts = (http_host, socket.gethostname(), socket.gethostbyname(http_host), - '::1','127.0.0.1','::ffff:127.0.0.1') + '::1', '127.0.0.1', '::ffff:127.0.0.1') except: hosts = (http_host, ) @@ -32,10 +32,10 @@ if request.env.http_x_forwarded_for or request.is_https: elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1"): raise HTTP(200, T('appadmin is disabled because insecure channel')) -if (request.application=='admin' and not session.authorized) or \ - (request.application!='admin' and not gluon.fileutils.check_credentials(request)): +if (request.application == 'admin' and not session.authorized) or \ + (request.application != 'admin' and not gluon.fileutils.check_credentials(request)): redirect(URL('admin', 'default', 'index', - vars=dict(send=URL(args=request.args,vars=request.vars)))) + vars=dict(send=URL(args=request.args, vars=request.vars)))) ignore_rw = True response.view = 'appadmin.html' @@ -95,24 +95,23 @@ def get_query(request): return None -def query_by_table_type(tablename,db,request=request): - keyed = hasattr(db[tablename],'_primarykey') +def query_by_table_type(tablename, db, request=request): + keyed = hasattr(db[tablename], '_primarykey') if keyed: firstkey = db[tablename][db[tablename]._primarykey[0]] cond = '>0' if firstkey.type in ['string', 'text']: cond = '!=""' - qry = '%s.%s.%s%s' % (request.args[0], request.args[1], firstkey.name, cond) + qry = '%s.%s.%s%s' % ( + request.args[0], request.args[1], firstkey.name, cond) else: qry = '%s.%s.id>0' % tuple(request.args[:2]) return qry - # ########################################################## # ## list all databases and tables # ########################################################### - def index(): return dict(databases=databases) @@ -127,7 +126,7 @@ def insert(): form = SQLFORM(db[table], ignore_rw=ignore_rw) if form.accepts(request.vars, session): response.flash = T('new record inserted') - return dict(form=form,table=db[table]) + return dict(form=form, table=db[table]) # ########################################################## @@ -138,7 +137,8 @@ def insert(): def download(): import os db = get_database(request) - return response.download(request,db) + return response.download(request, db) + def csv(): import gluon.contenttype @@ -149,26 +149,27 @@ def csv(): if not query: return None response.headers['Content-disposition'] = 'attachment; filename=%s_%s.csv'\ - % tuple(request.vars.query.split('.')[:2]) - return str(db(query,ignore_common_filters=True).select()) + % tuple(request.vars.query.split('.')[:2]) + return str(db(query, ignore_common_filters=True).select()) def import_csv(table, file): table.import_from_csv_file(file) + def select(): import re db = get_database(request) dbname = request.args[0] regex = re.compile('(?P\w+)\.(?P\w+)=(?P\d+)') - if len(request.args)>1 and hasattr(db[request.args[1]],'_primarykey'): + if len(request.args) > 1 and hasattr(db[request.args[1]], '_primarykey'): regex = re.compile('(?P
\w+)\.(?P\w+)=(?P.+)') if request.vars.query: match = regex.match(request.vars.query) if match: request.vars.query = '%s.%s.%s==%s' % (request.args[0], - match.group('table'), match.group('field'), - match.group('value')) + match.group('table'), match.group('field'), + match.group('value')) else: request.vars.query = session.last_query query = get_query(request) @@ -192,46 +193,50 @@ def select(): session.last_query = request.vars.query form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px', _name='query', _value=request.vars.query or '', - requires=IS_NOT_EMPTY(error_message=T("Cannot be empty")))), TR(T('Update:'), + requires=IS_NOT_EMPTY( + error_message=T("Cannot be empty")))), TR(T('Update:'), INPUT(_name='update_check', _type='checkbox', value=False), INPUT(_style='width:400px', _name='update_fields', _value=request.vars.update_fields - or '')), TR(T('Delete:'), INPUT(_name='delete_check', + or '')), TR(T('Delete:'), INPUT(_name='delete_check', _class='delete', _type='checkbox', value=False), ''), TR('', '', INPUT(_type='submit', _value=T('submit')))), - _action=URL(r=request,args=request.args)) + _action=URL(r=request, args=request.args)) + + tb = None if form.accepts(request.vars, formname=None): regex = re.compile(request.args[0] + '\.(?P
\w+)\..+') match = regex.match(form.vars.query.strip()) if match: table = match.group('table') try: - tb = None nrows = db(query).count() if form.vars.update_check and form.vars.update_fields: db(query).update(**eval_in_global_env('dict(%s)' - % form.vars.update_fields)) + % form.vars.update_fields)) response.flash = T('%s %%{row} updated', nrows) elif form.vars.delete_check: db(query).delete() response.flash = T('%s %%{row} deleted', nrows) nrows = db(query).count() if orderby: - rows = db(query,ignore_common_filters=True).select(limitby=(start, stop), orderby=eval_in_global_env(orderby)) + rows = db(query, ignore_common_filters=True).select(limitby=( + start, stop), orderby=eval_in_global_env(orderby)) else: - rows = db(query,ignore_common_filters=True).select(limitby=(start, stop)) + rows = db(query, ignore_common_filters=True).select( + limitby=(start, stop)) except Exception, e: import traceback tb = traceback.format_exc() (rows, nrows) = ([], 0) - response.flash = DIV(T('Invalid Query'),PRE(str(e))) + response.flash = DIV(T('Invalid Query'), PRE(str(e))) # begin handle upload csv csv_table = table or request.vars.table if csv_table: - formcsv = FORM(str(T('or import from csv file'))+" ", - INPUT(_type='file',_name='csvfile'), - INPUT(_type='hidden',_value=csv_table,_name='table'), - INPUT(_type='submit',_value=T('import'))) + formcsv = FORM(str(T('or import from csv file')) + " ", + INPUT(_type='file', _name='csvfile'), + INPUT(_type='hidden', _value=csv_table, _name='table'), + INPUT(_type='submit', _value=T('import'))) else: formcsv = None if formcsv and formcsv.process().accepted: @@ -240,7 +245,7 @@ def select(): request.vars.csvfile.file) response.flash = T('data uploaded') except Exception, e: - response.flash = DIV(T('unable to parse csv file'),PRE(str(e))) + response.flash = DIV(T('unable to parse csv file'), PRE(str(e))) # end handle upload csv return dict( @@ -251,9 +256,9 @@ def select(): nrows=nrows, rows=rows, query=request.vars.query, - formcsv = formcsv, - tb = tb, - ) + formcsv=formcsv, + tb=tb, + ) # ########################################################## @@ -263,14 +268,16 @@ def select(): def update(): (db, table) = get_table(request) - keyed = hasattr(db[table],'_primarykey') + keyed = hasattr(db[table], '_primarykey') record = None if keyed: key = [f for f in request.vars if f in db[table]._primarykey] if key: - record = db(db[table][key[0]] == request.vars[key[0]], ignore_common_filters=True).select().first() + record = db(db[table][key[0]] == request.vars[key[ + 0]], ignore_common_filters=True).select().first() else: - record = db(db[table].id == request.args(2),ignore_common_filters=True).select().first() + record = db(db[table].id == request.args( + 2), ignore_common_filters=True).select().first() if not record: qry = query_by_table_type(table, db) @@ -280,20 +287,21 @@ def update(): if keyed: for k in db[table]._primarykey: - db[table][k].writable=False + db[table][k].writable = False - form = SQLFORM(db[table], record, deletable=True, delete_label=T('Check to delete'), - ignore_rw=ignore_rw and not keyed, - linkto=URL('select', + form = SQLFORM( + db[table], record, deletable=True, delete_label=T('Check to delete'), + ignore_rw=ignore_rw and not keyed, + linkto=URL('select', args=request.args[:1]), upload=URL(r=request, - f='download', args=request.args[:1])) + f='download', args=request.args[:1])) if form.accepts(request.vars, session): session.flash = T('done!') qry = query_by_table_type(table, db) redirect(URL('select', args=request.args[:1], vars=dict(query=qry))) - return dict(form=form,table=db[table]) + return dict(form=form, table=db[table]) # ########################################################## @@ -304,11 +312,15 @@ def update(): def state(): return dict() + def ccache(): form = FORM( - P(TAG.BUTTON(T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")), - P(TAG.BUTTON(T("Clear RAM"), _type="submit", _name="ram", _value="ram")), - P(TAG.BUTTON(T("Clear DISK"), _type="submit", _name="disk", _value="disk")), + P(TAG.BUTTON( + T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")), + P(TAG.BUTTON( + T("Clear RAM"), _type="submit", _name="ram", _value="ram")), + P(TAG.BUTTON( + T("Clear DISK"), _type="submit", _name="disk", _value="disk")), ) if form.accepts(request.vars, session): @@ -332,11 +344,16 @@ def ccache(): redirect(URL(r=request)) try: - from guppy import hpy; hp=hpy() + from guppy import hpy + hp = hpy() except ImportError: hp = False - import shelve, os, copy, time, math + import shelve + import os + import copy + import time + import math from gluon import portalocker ram = { @@ -381,9 +398,10 @@ def ccache(): ram['keys'].append((key, GetInHMS(time.time() - value[0]))) locker = open(os.path.join(request.folder, - 'cache/cache.lock'), 'a') + 'cache/cache.lock'), 'a') portalocker.lock(locker, portalocker.LOCK_EX) - disk_storage = shelve.open(os.path.join(request.folder, 'cache/cache.shelve')) + disk_storage = shelve.open( + os.path.join(request.folder, 'cache/cache.shelve')) try: for key, value in disk_storage.items(): if isinstance(value, dict): @@ -414,7 +432,8 @@ def ccache(): total['misses'] = ram['misses'] + disk['misses'] total['keys'] = ram['keys'] + disk['keys'] try: - total['ratio'] = total['hits'] * 100 / (total['hits'] + total['misses']) + total['ratio'] = total['hits'] * 100 / (total['hits'] + + total['misses']) except (KeyError, ZeroDivisionError): total['ratio'] = 0 @@ -440,6 +459,3 @@ def ccache(): return dict(form=form, total=total, ram=ram, disk=disk, object_stats=hp != False) - - - diff --git a/applications/admin/controllers/debug.py b/applications/admin/controllers/debug.py index 6cf1b40d..0b289420 100644 --- a/applications/admin/controllers/debug.py +++ b/applications/admin/controllers/debug.py @@ -5,35 +5,39 @@ import gluon.contrib.shell import gluon.dal import gluon.html import gluon.validators -import code, thread +import code +import thread from gluon.debug import communicate, web_debugger, qdb_debugger import pydoc if DEMO_MODE or MULTI_USER_MODE: session.flash = T('disabled in demo mode') - redirect(URL('default','site')) + redirect(URL('default', 'site')) + +FE = 10 ** 9 -FE=10**9 def index(): app = request.args(0) or 'admin' reset() # read buffer data = communicate() - return dict(app=app,data=data) + return dict(app=app, data=data) + def callback(): app = request.args[0] command = request.vars.statement - session['debug_commands:'+app].append(command) + session['debug_commands:' + app].append(command) output = communicate(command) - k = len(session['debug_commands:'+app]) - 1 + k = len(session['debug_commands:' + app]) - 1 return '[%i] %s%s\n' % (k + 1, command, output) + def reset(): app = request.args(0) or 'admin' - session['debug_commands:'+app] = [] + session['debug_commands:' + app] = [] return 'done' @@ -50,9 +54,9 @@ def interact(): filename = web_debugger.filename lineno = web_debugger.lineno if filename: - lines = dict([(i+1, l) for (i, l) in enumerate( - [l.strip("\n").strip("\r") for l - in open(filename).readlines()])]) + lines = dict([(i + 1, l) for (i, l) in enumerate( + [l.strip("\n").strip("\r") for l + in open(filename).readlines()])]) filename = os.path.basename(filename) else: lines = {} @@ -64,8 +68,8 @@ def interact(): f_globals = {} for name, value in env['globals'].items(): if name not in gluon.html.__all__ and \ - name not in gluon.validators.__all__ and \ - name not in gluon.dal.__all__: + name not in gluon.validators.__all__ and \ + name not in gluon.dal.__all__: f_globals[name] = pydoc.text.repr(value) else: f_locals = {} @@ -76,42 +80,48 @@ def interact(): response.flash = T('"User Exception" debug mode. ' 'An error ticket could be issued!') - return dict(app=app, data="", - filename=web_debugger.filename, lines=lines, lineno=lineno, - f_globals=f_globals, f_locals=f_locals, + return dict(app=app, data="", + filename=web_debugger.filename, lines=lines, lineno=lineno, + f_globals=f_globals, f_locals=f_locals, exception=web_debugger.exception_info) + def step(): web_debugger.do_step() redirect(URL("interact")) + def next(): web_debugger.do_next() redirect(URL("interact")) + def cont(): web_debugger.do_continue() redirect(URL("interact")) + def ret(): web_debugger.do_return() redirect(URL("interact")) + def stop(): web_debugger.do_quit() redirect(URL("interact")) + def execute(): app = request.args[0] command = request.vars.statement - session['debug_commands:'+app].append(command) + session['debug_commands:' + app].append(command) try: output = web_debugger.do_exec(command) if output is None: output = "" except Exception, e: - output = T("Exception %s") % str(e) - k = len(session['debug_commands:'+app]) - 1 + output = T("Exception %s") % str(e) + k = len(session['debug_commands:' + app]) - 1 return '[%i] %s%s\n' % (k + 1, command, output) @@ -120,51 +130,51 @@ def breakpoints(): # Get all .py files files = listdir(apath('', r=request), '.*\.py$') - files = [filename for filename in files - if filename and 'languages' not in filename - and not filename.startswith("admin") - and not filename.startswith("examples")] + files = [filename for filename in files + if filename and 'languages' not in filename + and not filename.startswith("admin") + and not filename.startswith("examples")] form = SQLFORM.factory( Field('filename', requires=IS_IN_SET(files), label=T("Filename")), Field('lineno', 'integer', label=T("Line number"), requires=IS_NOT_EMPTY()), - Field('temporary', 'boolean', label=T("Temporary"), + Field('temporary', 'boolean', label=T("Temporary"), comment=T("deleted after first hit")), Field('condition', 'string', label=T("Condition"), comment=T("honored only if the expression evaluates to true")), - ) + ) if form.accepts(request.vars, session): - filename = os.path.join(request.env['applications_parent'], + filename = os.path.join(request.env['applications_parent'], 'applications', form.vars.filename) - err = qdb_debugger.do_set_breakpoint(filename, - form.vars.lineno, - form.vars.temporary, - form.vars.condition) + err = qdb_debugger.do_set_breakpoint(filename, + form.vars.lineno, + form.vars.temporary, + form.vars.condition) response.flash = T("Set Breakpoint on %s at line %s: %s") % ( - filename, form.vars.lineno, err or T('successful')) + filename, form.vars.lineno, err or T('successful')) for item in request.vars: if item[:7] == 'delete_': qdb_debugger.do_clear(item[7:]) breakpoints = [{'number': bp[0], 'filename': os.path.basename(bp[1]), - 'path': bp[1], 'lineno': bp[2], - 'temporary': bp[3], 'enabled': bp[4], 'hits': bp[5], - 'condition': bp[6]} - for bp in qdb_debugger.do_list_breakpoint()] + 'path': bp[1], 'lineno': bp[2], + 'temporary': bp[3], 'enabled': bp[4], 'hits': bp[5], + 'condition': bp[6]} + for bp in qdb_debugger.do_list_breakpoint()] return dict(breakpoints=breakpoints, form=form) def toggle_breakpoint(): "Set or clear a breakpoint" - + lineno = None ok = None try: - filename = os.path.join(request.env['applications_parent'], + filename = os.path.join(request.env['applications_parent'], 'applications', request.vars.filename) if not request.vars.data: # ace send us the line number! @@ -184,18 +194,17 @@ def toggle_breakpoint(): no, bp_filename, bp_lineno, temporary, enabled, hits, cond = bp if filename == bp_filename and lineno == bp_lineno: err = qdb_debugger.do_clear_breakpoint(filename, lineno) - response.flash = T("Removed Breakpoint on %s at line %s", ( - filename, lineno)) + response.flash = T("Removed Breakpoint on %s at line %s", ( + filename, lineno)) ok = False break else: err = qdb_debugger.do_set_breakpoint(filename, lineno) response.flash = T("Set Breakpoint on %s at line %s: %s") % ( - filename, lineno, err or T('successful')) + filename, lineno, err or T('successful')) ok = True else: response.flash = T("Unable to determine the line number!") except Exception, e: session.flash = str(e) return response.json({'ok': ok, 'lineno': lineno}) - diff --git a/applications/admin/controllers/default.py b/applications/admin/controllers/default.py index 7b3a0621..97293161 100644 --- a/applications/admin/controllers/default.py +++ b/applications/admin/controllers/default.py @@ -4,7 +4,7 @@ EXPERIMENTAL_STUFF = True if EXPERIMENTAL_STUFF: if is_mobile: - response.view = response.view.replace('default/','default.mobile/') + response.view = response.view.replace('default/', 'default.mobile/') response.menu = [] import re @@ -25,12 +25,12 @@ from gluon.languages import (read_possible_languages, read_dict, write_dict, read_plural_dict, write_plural_dict) -if DEMO_MODE and request.function in ['change_password','pack','pack_plugin','upgrade_web2py','uninstall','cleanup','compile_app','remove_compiled_app','delete','delete_plugin','create_file','upload_file','update_languages','reload_routes','git_push','git_pull']: +if DEMO_MODE and request.function in ['change_password', 'pack', 'pack_plugin', 'upgrade_web2py', 'uninstall', 'cleanup', 'compile_app', 'remove_compiled_app', 'delete', 'delete_plugin', 'create_file', 'upload_file', 'update_languages', 'reload_routes', 'git_push', 'git_pull']: session.flash = T('disabled in demo mode') redirect(URL('site')) -if not is_manager() and request.function in ['change_password','upgrade_web2py']: +if not is_manager() and request.function in ['change_password', 'upgrade_web2py']: session.flash = T('disabled in multi user mode') redirect(URL('site')) @@ -39,25 +39,32 @@ if FILTER_APPS and request.args(0) and not request.args(0) in FILTER_APPS: redirect(URL('site')) -if not session.token: session.token = web2py_uuid() +if not session.token: + session.token = web2py_uuid() + def count_lines(data): return len([line for line in data.split('\n') if line.strip() and not line.startswith('#')]) -def log_progress(app,mode='EDIT',filename=None,progress=0): + +def log_progress(app, mode='EDIT', filename=None, progress=0): progress_file = os.path.join(apath(app, r=request), 'progress.log') now = str(request.now)[:19] if not os.path.exists(progress_file): - safe_open(progress_file,'w').write('[%s] START\n' % now) + safe_open(progress_file, 'w').write('[%s] START\n' % now) if filename: - safe_open(progress_file,'a').write('[%s] %s %s: %s\n' % (now,mode,filename,progress)) + safe_open(progress_file, 'a').write( + '[%s] %s %s: %s\n' % (now, mode, filename, progress)) -def safe_open(a,b): + +def safe_open(a, b): if DEMO_MODE and ('w' in b or 'a' in b): class tmp: - def write(self,data): pass + def write(self, data): + pass return tmp() - return open(a,b) + return open(a, b) + def safe_read(a, b='r'): safe_file = safe_open(a, b) @@ -66,6 +73,7 @@ def safe_read(a, b='r'): finally: safe_file.close() + def safe_write(a, value, b='w'): safe_file = safe_open(a, b) try: @@ -73,14 +81,16 @@ def safe_write(a, value, b='w'): finally: safe_file.close() + def get_app(name=None): app = name or request.args(0) - if app and (not MULTI_USER_MODE or is_manager() or \ - db(db.app.name==app)(db.app.owner==auth.user.id).count()): + if app and (not MULTI_USER_MODE or is_manager() or + db(db.app.name == app)(db.app.owner == auth.user.id).count()): return app session.flash = T('App does not exist or your are not authorized') redirect(URL('site')) + def index(): """ Index handler """ @@ -127,18 +137,19 @@ def check_version(): session._unlock(response) new_version, version_number = check_new_version(request.env.web2py_version, - WEB2PY_VERSION_URL) + WEB2PY_VERSION_URL) if new_version == -1: return A(T('Unable to check for upgrades'), _href=WEB2PY_URL) elif new_version != True: return A(T('web2py is up to date'), _href=WEB2PY_URL) - elif platform.system().lower() in ('windows','win32','win64') and os.path.exists("web2py.exe"): + elif platform.system().lower() in ('windows', 'win32', 'win64') and os.path.exists("web2py.exe"): return SPAN('You should upgrade to version %s.%s.%s' % version_number[:3]) else: return sp_button(URL('upgrade_web2py'), T('upgrade now')) \ - + XML(' %s.%s.%s' \ - % version_number[:3]) + + XML(' %s.%s.%s' + % version_number[:3]) + def logout(): """ Logout handler """ @@ -151,11 +162,13 @@ def logout(): def change_password(): if session.pam_user: - session.flash = T('PAM authenticated user, cannot change password here') + session.flash = T( + 'PAM authenticated user, cannot change password here') redirect(URL('site')) - form=SQLFORM.factory(Field('current_admin_password','password'), - Field('new_admin_password','password',requires=IS_STRONG()), - Field('new_admin_password_again','password')) + form = SQLFORM.factory(Field('current_admin_password', 'password'), + Field('new_admin_password', + 'password', requires=IS_STRONG()), + Field('new_admin_password_again', 'password')) if form.accepts(request.vars): if not verify_password(request.vars.current_admin_password): form.errors.current_admin_password = T('invalid password') @@ -163,7 +176,8 @@ def change_password(): form.errors.new_admin_password_again = T('no match') else: path = abspath('parameters_%s.py' % request.env.server_port) - safe_write(path, 'password="%s"' % CRYPT()(request.vars.new_admin_password)[0]) + safe_write(path, 'password="%s"' % CRYPT()( + request.vars.new_admin_password)[0]) session.flash = T('password changed') redirect(URL('site')) return dict(form=form) @@ -178,21 +192,21 @@ def site(): file_or_appurl = 'file' in request.vars or 'appurl' in request.vars class IS_VALID_APPNAME(object): - def __call__(self,value): + def __call__(self, value): if not re.compile('\w+').match(value): - return (value,T('Invalid application name')) + return (value, T('Invalid application name')) if not request.vars.overwrite and \ - os.path.exists(os.path.join(apath(r=request),value)): - return (value,T('Application exists already')) - return (value,None) + os.path.exists(os.path.join(apath(r=request), value)): + return (value, T('Application exists already')) + return (value, None) is_appname = IS_VALID_APPNAME() - form_create = SQLFORM.factory(Field('name',requires=is_appname), + form_create = SQLFORM.factory(Field('name', requires=is_appname), table_name='appcreate') - form_update = SQLFORM.factory(Field('name',requires=is_appname), - Field('file','upload',uploadfield=False), + form_update = SQLFORM.factory(Field('name', requires=is_appname), + Field('file', 'upload', uploadfield=False), Field('url'), - Field('overwrite','boolean'), + Field('overwrite', 'boolean'), table_name='appupdate') form_create.process() form_update.process() @@ -203,17 +217,17 @@ def site(): elif form_create.accepted: # create a new application appname = cleanpath(form_create.vars.name) - created, error = app_create(appname, request,info=True) + created, error = app_create(appname, request, info=True) if created: if MULTI_USER_MODE: - db.app.insert(name=appname,owner=auth.user.id) + db.app.insert(name=appname, owner=auth.user.id) log_progress(appname) session.flash = T('new application "%s" created', appname) - redirect(URL('design',args=appname)) + redirect(URL('design', args=appname)) else: session.flash = \ DIV(T('unable to create application "%s"' % appname), - PRE(error)) + PRE(error)) redirect(URL(r=request)) elif form_update.accepted: @@ -221,9 +235,9 @@ def site(): if not have_git: session.flash = GIT_MISSING redirect(URL(r=request)) - target = os.path.join(apath(r=request),form_update.vars.name) + target = os.path.join(apath(r=request), form_update.vars.name) try: - new_repo = Repo.clone_from(form_update.vars.url,target) + new_repo = Repo.clone_from(form_update.vars.url, target) session.flash = T('new application "%s" imported', form_update.vars.name) except GitCommandError, err: @@ -238,27 +252,27 @@ def site(): raise Exception("404 file not found") except Exception, e: session.flash = \ - DIV(T('Unable to download app because:'),PRE(str(e))) + DIV(T('Unable to download app because:'), PRE(str(e))) redirect(URL(r=request)) fname = form_update.vars.url - + elif form_update.accepted and form_update.vars.file: fname = request.vars.file.filename f = request.vars.file.file - + else: session.flash = 'No file uploaded and no URL specified' redirect(URL(r=request)) if f: appname = cleanpath(form_update.vars.name) - installed = app_install(appname, f, + installed = app_install(appname, f, request, fname, overwrite=form_update.vars.overwrite) if f and installed: msg = 'application %(appname)s installed with md5sum: %(digest)s' if MULTI_USER_MODE: - db.app.insert(name=appname,owner=auth.user.id) + db.app.insert(name=appname, owner=auth.user.id) log_progress(appname) session.flash = T(msg, dict(appname=appname, digest=md5_hash(installed))) @@ -275,14 +289,14 @@ def site(): if is_manager(): apps = [f for f in os.listdir(apath(r=request)) if regex.match(f)] else: - apps = [f.name for f in db(db.app.owner==auth.user_id).select()] + apps = [f.name for f in db(db.app.owner == auth.user_id).select()] if FILTER_APPS: apps = [f for f in apps if f in FILTER_APPS] - apps = sorted(apps,lambda a,b:cmp(a.upper(),b.upper())) + apps = sorted(apps, lambda a, b: cmp(a.upper(), b.upper())) - return dict(app=None, apps=apps, myversion=myversion, + return dict(app=None, apps=apps, myversion=myversion, form_create=form_create, form_update=form_update) @@ -292,13 +306,15 @@ def report_progress(app): regex = re.compile('\[(.*?)\][^\:]+\:\s+(\-?\d+)') if not os.path.exists(progress_file): return [] - matches = regex.findall(open(progress_file,'r').read()) - events,counter = [],0 + matches = regex.findall(open(progress_file, 'r').read()) + events, counter = [], 0 for m in matches: - if not m: continue - days = -(request.now - datetime.datetime.strptime(m[0],'%Y-%m-%d %H:%M:%S')).days + if not m: + continue + days = -(request.now - datetime.datetime.strptime(m[0], + '%Y-%m-%d %H:%M:%S')).days counter += int(m[1]) - events.append([days,counter]) + events.append([days, counter]) return events @@ -324,6 +340,7 @@ def pack(): session.flash = T('internal error: %s' % e) redirect(URL('site')) + def pack_plugin(): app = get_app() if len(request.args) == 2: @@ -336,11 +353,12 @@ def pack_plugin(): return safe_read(filename, 'rb') else: session.flash = T('internal error') - redirect(URL('plugin',args=request.args)) + redirect(URL('plugin', args=request.args)) + def upgrade_web2py(): dialog = FORM.confirm(T('Upgrade'), - {T('Cancel'):URL('site')}) + {T('Cancel'): URL('site')}) if dialog.accepted: (success, error) = upgrade(request) if success: @@ -350,17 +368,18 @@ def upgrade_web2py(): redirect(URL('site')) return dict(dialog=dialog) + def uninstall(): app = get_app() dialog = FORM.confirm(T('Uninstall'), - {T('Cancel'):URL('site')}) - + {T('Cancel'): URL('site')}) + if dialog.accepted: if MULTI_USER_MODE: - if is_manager() and db(db.app.name==app).delete(): + if is_manager() and db(db.app.name == app).delete(): pass - elif db(db.app.name==app)(db.app.owner==auth.user.id).delete(): + elif db(db.app.name == app)(db.app.owner == auth.user.id).delete(): pass else: session.flash = T('no permission to uninstall "%s"', app) @@ -396,7 +415,7 @@ def compile_app(): session.flash = T('application compiled') else: session.flash = DIV(T('Cannot compile: there are errors in your app:'), - CODE(c)) + CODE(c)) redirect(URL('site')) @@ -407,6 +426,7 @@ def remove_compiled_app(): session.flash = T('compiled application removed') redirect(URL('site')) + def delete(): """ Object delete handler """ app = get_app() @@ -421,9 +441,9 @@ def delete(): elif 'delete' in request.vars: try: full_path = apath(filename, r=request) - lineno = count_lines(open(full_path,'r').read()) + lineno = count_lines(open(full_path, 'r').read()) os.unlink(full_path) - log_progress(app,'DELETE',filename,progress=-lineno) + log_progress(app, 'DELETE', filename, progress=-lineno) session.flash = T('file "%(filename)s" deleted', dict(filename=filename)) except Exception: @@ -432,6 +452,7 @@ def delete(): redirect(URL(sender, anchor=request.vars.id2)) return dict(filename=filename, sender=sender) + def delete(): """ Object delete handler """ app = get_app() @@ -442,33 +463,35 @@ def delete(): sender = sender[0] dialog = FORM.confirm(T('Delete'), - {T('Cancel'):URL(sender, anchor=request.vars.id)}) + {T('Cancel'): URL(sender, anchor=request.vars.id)}) if dialog.accepted: try: full_path = apath(filename, r=request) - lineno = count_lines(open(full_path,'r').read()) + lineno = count_lines(open(full_path, 'r').read()) os.unlink(full_path) - log_progress(app,'DELETE',filename,progress=-lineno) + log_progress(app, 'DELETE', filename, progress=-lineno) session.flash = T('file "%(filename)s" deleted', dict(filename=filename)) except Exception: session.flash = T('unable to delete file "%(filename)s"', dict(filename=filename)) redirect(URL(sender, anchor=request.vars.id2)) - return dict(dialog=dialog,filename=filename) + return dict(dialog=dialog, filename=filename) + def enable(): app = get_app() - filename = os.path.join(apath(app, r=request),'DISABLED') + filename = os.path.join(apath(app, r=request), 'DISABLED') if is_gae: - return SPAN(T('Not supported'),_style='color:yellow') + return SPAN(T('Not supported'), _style='color:yellow') elif os.path.exists(filename): os.unlink(filename) - return SPAN(T('Disable'),_style='color:green') + return SPAN(T('Disable'), _style='color:green') else: - safe_open(filename,'wb').write(time.ctime()) - return SPAN(T('Enable'),_style='color:red') + safe_open(filename, 'wb').write(time.ctime()) + return SPAN(T('Enable'), _style='color:red') + def peek(): """ Visualize object code """ @@ -479,7 +502,7 @@ def peek(): else: path = apath(filename, r=request) try: - data = safe_read(path).replace('\r','') + data = safe_read(path).replace('\r', '') except IOError: session.flash = T('file does not exist') redirect(URL('site')) @@ -491,6 +514,7 @@ def peek(): data=data, extension=extension) + def test(): """ Execute controller tests """ app = get_app() @@ -499,28 +523,34 @@ def test(): else: file = '.*\.py' - controllers = listdir(apath('%s/controllers/' % app, r=request), file + '$') + controllers = listdir( + apath('%s/controllers/' % app, r=request), file + '$') return dict(app=app, controllers=controllers) + def keepalive(): return '' + def search(): - keywords=request.vars.keywords or '' + keywords = request.vars.keywords or '' app = get_app() - def match(filename,keywords): - filename=os.path.join(apath(app, r=request),filename) - if keywords in read_file(filename,'rb'): + + def match(filename, keywords): + filename = os.path.join(apath(app, r=request), filename) + if keywords in read_file(filename, 'rb'): return True return False path = apath(request.args[0], r=request) - files1 = glob(os.path.join(path,'*/*.py')) - files2 = glob(os.path.join(path,'*/*.html')) - files3 = glob(os.path.join(path,'*/*/*.html')) - files=[x[len(path)+1:].replace('\\','/') for x in files1+files2+files3 if match(x,keywords)] + files1 = glob(os.path.join(path, '*/*.py')) + files2 = glob(os.path.join(path, '*/*.html')) + files3 = glob(os.path.join(path, '*/*/*.html')) + files = [x[len(path) + 1:].replace( + '\\', '/') for x in files1 + files2 + files3 if match(x, keywords)] return response.json(dict(files=files, message=T.M('Searching: **%s** %%{file}', len(files)))) + def edit(): """ File edit handler """ # Load json only if it is ajax edited... @@ -552,7 +582,7 @@ def edit(): except IOError: session.flash = T('Invalid action') if 'from_ajax' in request.vars: - return response.json({'error': str(T('Invalid action'))}) + return response.json({'error': str(T('Invalid action'))}) else: redirect(URL('site')) @@ -590,7 +620,8 @@ def edit(): data = request.vars.data.replace('\r\n', '\n').strip() + '\n' safe_write(path, data) lineno_new = count_lines(data) - log_progress(app,'EDIT',filename,progress=lineno_new-lineno_old) + log_progress( + app, 'EDIT', filename, progress=lineno_new - lineno_old) file_hash = md5_hash(data) saved_on = time.ctime(os.stat(path)[stat.ST_MTIME]) response.flash = T('file saved on %s', saved_on) @@ -602,35 +633,40 @@ def edit(): if filetype == 'python' and request.vars.data: import _ast try: - code = request.vars.data.rstrip().replace('\r\n','\n')+'\n' + code = request.vars.data.rstrip().replace('\r\n', '\n') + '\n' compile(code, path, "exec", _ast.PyCF_ONLY_AST) except Exception, e: - start = sum([len(line)+1 for l, line - in enumerate(request.vars.data.split("\n")) - if l < e.lineno-1]) + start = sum([len(line) + 1 for l, line + in enumerate(request.vars.data.split("\n")) + if l < e.lineno - 1]) if e.text and e.offset: - offset = e.offset - (len(e.text) - len(e.text.splitlines()[-1])) + offset = e.offset - (len(e.text) - len( + e.text.splitlines()[-1])) else: offset = 0 - highlight = {'start': start, 'end': start + offset + 1, 'lineno': e.lineno} + highlight = {'start': start, 'end': start + + offset + 1, 'lineno': e.lineno} try: ex_name = e.__class__.__name__ except: ex_name = 'unknown exception!' response.flash = DIV(T('failed to compile file because:'), BR(), - B(ex_name), ' '+T('at line %s', e.lineno), - offset and ' '+T('at char %s', offset) or '', + B(ex_name), ' ' + T('at line %s', e.lineno), + offset and ' ' + + T('at char %s', offset) or '', PRE(str(e))) if data_or_revert and request.args[1] == 'modules': # Lets try to reload the modules try: mopath = '.'.join(request.args[2:])[:-3] - exec 'import applications.%s.modules.%s' % (request.args[0], mopath) + exec 'import applications.%s.modules.%s' % ( + request.args[0], mopath) reload(sys.modules['applications.%s.modules.%s' - % (request.args[0], mopath)]) + % (request.args[0], mopath)]) except Exception, e: - response.flash = DIV(T('failed to reload module because:'),PRE(str(e))) + response.flash = DIV( + T('failed to reload module because:'), PRE(str(e))) edit_controller = None editviewlinks = None @@ -640,28 +676,28 @@ def edit(): request.args[2] + '.py') if os.path.exists(apath(cfilename, r=request)): edit_controller = URL('edit', args=[cfilename]) - view = request.args[3].replace('.html','') - view_link = URL(request.args[0],request.args[2],view) + view = request.args[3].replace('.html', '') + view_link = URL(request.args[0], request.args[2], view) elif filetype == 'python' and request.args[1] == 'controllers': ## it's a controller file. ## Create links to all of the associated view files. app = get_app() viewname = os.path.splitext(request.args[2])[0] - viewpath = os.path.join(app,'views',viewname) + viewpath = os.path.join(app, 'views', viewname) aviewpath = apath(viewpath, r=request) viewlist = [] if os.path.exists(aviewpath): if os.path.isdir(aviewpath): - viewlist = glob(os.path.join(aviewpath,'*.html')) - elif os.path.exists(aviewpath+'.html'): - viewlist.append(aviewpath+'.html') + viewlist = glob(os.path.join(aviewpath, '*.html')) + elif os.path.exists(aviewpath + '.html'): + viewlist.append(aviewpath + '.html') if len(viewlist): editviewlinks = [] for v in viewlist: vf = os.path.split(v)[-1] - vargs = "/".join([viewpath.replace(os.sep,"/"),vf]) - editviewlinks.append(A(vf.split(".")[0],\ - _href=URL('edit',args=[vargs]))) + vargs = "/".join([viewpath.replace(os.sep, "/"), vf]) + editviewlinks.append(A(vf.split(".")[0], + _href=URL('edit', args=[vargs]))) if len(request.args) > 2 and request.args[1] == 'controllers': controller = (request.args[2])[:-3] @@ -670,7 +706,7 @@ def edit(): (controller, functions) = (None, None) if 'from_ajax' in request.vars: - return response.json({'file_hash': file_hash, 'saved_on': saved_on, 'functions':functions, 'controller': controller, 'application': request.args[0], 'highlight': highlight }) + return response.json({'file_hash': file_hash, 'saved_on': saved_on, 'functions': functions, 'controller': controller, 'application': request.args[0], 'highlight': highlight}) else: editarea_preferences = {} @@ -680,8 +716,8 @@ def edit(): editarea_preferences['REPLACE_TAB_BY_SPACES'] = '4' editarea_preferences['DISPLAY'] = 'onload' for key in editarea_preferences: - if globals().has_key(key): - editarea_preferences[key]=globals()[key] + if key in globals(): + editarea_preferences[key] = globals()[key] return dict(app=request.args[0], filename=filename, filetype=filetype, @@ -695,6 +731,7 @@ def edit(): editarea_preferences=editarea_preferences, editviewlinks=editviewlinks) + def resolve(): """ """ @@ -739,19 +776,19 @@ def resolve(): return 'minus' if request.vars: - c = '\n'.join([item[2:].rstrip() for (i, item) in enumerate(d) if item[0] \ - == ' ' or 'line%i' % i in request.vars]) + c = '\n'.join([item[2:].rstrip() for (i, item) in enumerate(d) if item[0] + == ' ' or 'line%i' % i in request.vars]) safe_write(path, c) session.flash = 'files merged' redirect(URL('edit', args=request.args)) else: # Making the short circuit compatible with <= python2.4 - gen_data = lambda index,item: not item[:1] in ['+','-'] and "" \ - or INPUT(_type='checkbox', - _name='line%i' % index, - value=item[0] == '+') + gen_data = lambda index, item: not item[:1] in ['+', '-'] and "" \ + or INPUT(_type='checkbox', + _name='line%i' % index, + value=item[0] == '+') - diff = TABLE(*[TR(TD(gen_data(i,item)), + diff = TABLE(*[TR(TD(gen_data(i, item)), TD(item[0]), TD(leading(item[2:]), TT(item[2:].rstrip())), _class=getclass(item)) @@ -767,10 +804,11 @@ def edit_language(): strings = read_dict(apath(filename, r=request)) if '__corrupted__' in strings: - form = SPAN(strings['__corrupted__'],_class='error') - return dict(filename=filename, form=form) + form = SPAN(strings['__corrupted__'], _class='error') + return dict(filename=filename, form=form) - keys = sorted(strings.keys(),lambda x,y: cmp(unicode(x,'utf-8').lower(), unicode(y,'utf-8').lower())) + keys = sorted(strings.keys(), lambda x, y: cmp( + unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())) rows = [] rows.append(H2(T('Original/Translation'))) @@ -779,16 +817,16 @@ def edit_language(): s = strings[key] (prefix, sep, key) = key.partition('\x01') if sep: - prefix = SPAN(prefix+': ', _class='tm_ftag') + prefix = SPAN(prefix + ': ', _class='tm_ftag') k = key else: (k, prefix) = (prefix, '') - _class='untranslated' if k==s else 'translated' + _class = 'untranslated' if k == s else 'translated' if len(s) <= 40: elem = INPUT(_type='text', _name=name, value=s, - _size=70,_class=_class) + _size=70, _class=_class) else: elem = TEXTAREA(_name=name, value=s, _cols=70, _rows=5, _class=_class) @@ -797,7 +835,7 @@ def edit_language(): k = (s != k) and k or B(k) rows.append(P(prefix, k, BR(), elem, TAG.BUTTON(T('delete'), - _onclick='return delkey("%s")' % name), _id=name)) + _onclick='return delkey("%s")' % name), _id=name)) rows.append(INPUT(_type='submit', _value=T('update'))) form = FORM(*rows) @@ -805,57 +843,66 @@ def edit_language(): strs = dict() for key in keys: name = md5_hash(key) - if form.vars[name]==chr(127): continue + if form.vars[name] == chr(127): + continue strs[key] = form.vars[name] write_dict(apath(filename, r=request), strs) session.flash = T('file saved on %(time)s', dict(time=time.ctime())) - redirect(URL(r=request,args=request.args)) + redirect(URL(r=request, args=request.args)) return dict(app=request.args[0], filename=filename, form=form) + def edit_plurals(): """ Edit plurals file """ app = get_app() filename = '/'.join(request.args) - plurals = read_plural_dict(apath(filename, r=request)) # plural forms dictionary - nplurals = int(request.vars.nplurals)-1 # plural forms quantity + plurals = read_plural_dict( + apath(filename, r=request)) # plural forms dictionary + nplurals = int(request.vars.nplurals) - 1 # plural forms quantity xnplurals = xrange(nplurals) if '__corrupted__' in plurals: - # show error message and exit - form = SPAN(plurals['__corrupted__'],_class='error') - return dict(filename=filename, form=form) + # show error message and exit + form = SPAN(plurals['__corrupted__'], _class='error') + return dict(filename=filename, form=form) - keys = sorted(plurals.keys(),lambda x,y: cmp(unicode(x,'utf-8').lower(), unicode(y,'utf-8').lower())) + keys = sorted(plurals.keys(), lambda x, y: cmp( + unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())) rows = [] - row=[T("Singular Form")] - row.extend([T("Plural Form #%s", n+1) for n in xnplurals]) - table=TABLE(THEAD(TR(row))) + row = [T("Singular Form")] + row.extend([T("Plural Form #%s", n + 1) for n in xnplurals]) + table = TABLE(THEAD(TR(row))) for key in keys: name = md5_hash(key) forms = plurals[key] if len(forms) < nplurals: - forms.extend(None for i in xrange(nplurals-len(forms))) + forms.extend(None for i in xrange(nplurals - len(forms))) row = [B(key)] - row.extend([INPUT(_type='text', _name=name+'_'+str(n), value=forms[n], _size=20) for n in xnplurals]) - row.append(TD(TAG.BUTTON(T('delete'), _onclick='return delkey("%s")' % name))) + row.extend([INPUT(_type='text', _name=name + '_' + str(n), + value=forms[n], _size=20) for n in xnplurals]) + row.append(TD( + TAG.BUTTON(T('delete'), _onclick='return delkey("%s")' % name))) rows.append(TR(row, _id=name)) if rows: table.append(TBODY(rows)) - rows=[table, INPUT(_type='submit', _value=T('update'))] + rows = [table, INPUT(_type='submit', _value=T('update'))] form = FORM(*rows) if form.accepts(request.vars, keepvalues=True): new_plurals = dict() for key in keys: name = md5_hash(key) - if form.vars[name+'_0']==chr(127): continue - new_plurals[key] = [form.vars[name+'_'+str(n)] for n in xnplurals] + if form.vars[name + '_0'] == chr(127): + continue + new_plurals[key] = [form.vars[name + '_' + str(n)] + for n in xnplurals] write_plural_dict(apath(filename, r=request), new_plurals) session.flash = T('file saved on %(time)s', dict(time=time.ctime())) - redirect(URL(r=request, args=request.args, vars=dict(nplurals=request.vars.nplurals))) + redirect(URL(r=request, args=request.args, vars=dict( + nplurals=request.vars.nplurals))) return dict(app=request.args[0], filename=filename, form=form) @@ -865,7 +912,7 @@ def about(): # ## check if file is not there about = safe_read(apath('%s/ABOUT' % app, r=request)) license = safe_read(apath('%s/LICENSE' % app, r=request)) - return dict(app=app, about=MARKMIN(about), license=MARKMIN(license),progress=report_progress(app)) + return dict(app=app, about=MARKMIN(about), license=MARKMIN(license), progress=report_progress(app)) def design(): @@ -876,24 +923,23 @@ def design(): msg = T('ATTENTION: you cannot edit the running application!') response.flash = msg - if request.vars and not request.vars.token==session.token: + if request.vars and not request.vars.token == session.token: redirect(URL('logout')) - if request.vars.pluginfile!=None and not isinstance(request.vars.pluginfile,str): - filename=os.path.basename(request.vars.pluginfile.filename) + if request.vars.pluginfile is not None and not isinstance(request.vars.pluginfile, str): + filename = os.path.basename(request.vars.pluginfile.filename) if plugin_install(app, request.vars.pluginfile.file, request, filename): session.flash = T('new plugin installed') - redirect(URL('design',args=app)) + redirect(URL('design', args=app)) else: session.flash = \ T('unable to create application "%s"', request.vars.filename) redirect(URL(r=request)) - elif isinstance(request.vars.pluginfile,str): + elif isinstance(request.vars.pluginfile, str): session.flash = T('plugin not specified') redirect(URL(r=request)) - # If we have only pyc files it means that # we cannot design if os.path.exists(apath('%s/compiled' % app, r=request)): @@ -903,7 +949,7 @@ def design(): # Get all models models = listdir(apath('%s/models/' % app, r=request), '.*\.py$') - models=[x.replace('\\','/') for x in models] + models = [x.replace('\\', '/') for x in models] defines = {} for m in models: data = safe_read(apath('%s/models/%s' % (app, m), r=request)) @@ -911,8 +957,9 @@ def design(): defines[m].sort() # Get all controllers - controllers = sorted(listdir(apath('%s/controllers/' % app, r=request), '.*\.py$')) - controllers = [x.replace('\\','/') for x in controllers] + controllers = sorted( + listdir(apath('%s/controllers/' % app, r=request), '.*\.py$')) + controllers = [x.replace('\\', '/') for x in controllers] functions = {} for c in controllers: data = safe_read(apath('%s/controllers/%s' % (app, c), r=request)) @@ -920,8 +967,9 @@ def design(): functions[c] = items # Get all views - views = sorted(listdir(apath('%s/views/' % app, r=request), '[\w/\-]+(\.\w+)+$')) - views = [x.replace('\\','/') for x in views if not x.endswith('.bak')] + views = sorted( + listdir(apath('%s/views/' % app, r=request), '[\w/\-]+(\.\w+)+$')) + views = [x.replace('\\', '/') for x in views if not x.endswith('.bak')] extend = {} include = {} for c in views: @@ -936,72 +984,76 @@ def design(): # Get all modules modules = listdir(apath('%s/modules/' % app, r=request), '.*\.py$') - modules = modules=[x.replace('\\','/') for x in modules] + modules = modules = [x.replace('\\', '/') for x in modules] modules.sort() # Get all private files privates = listdir(apath('%s/private/' % app, r=request), '[^\.#].*') - privates = [x.replace('\\','/') for x in privates] + privates = [x.replace('\\', '/') for x in privates] privates.sort() # Get all static files statics = listdir(apath('%s/static/' % app, r=request), '[^\.#].*') - statics = [x.replace('\\','/') for x in statics] + statics = [x.replace('\\', '/') for x in statics] statics.sort() # Get all languages - languages=dict([(lang,info) for lang,info - in read_possible_languages( - apath(app, r=request)).iteritems() - if info[2]!=0]) # info[2] is langfile_mtime: + languages = dict([(lang, info) for lang, info + in read_possible_languages( + apath(app, r=request)).iteritems() + if info[2] != 0]) # info[2] is langfile_mtime: # get only existed files #Get crontab cronfolder = apath('%s/cron' % app, r=request) - if not os.path.exists(cronfolder): os.mkdir(cronfolder) + if not os.path.exists(cronfolder): + os.mkdir(cronfolder) crontab = apath('%s/cron/crontab' % app, r=request) if not os.path.exists(crontab): safe_write(crontab, '#crontab') - plugins=[] - def filter_plugins(items,plugins): - plugins+=[item[7:].split('/')[0].split('.')[0] for item in items if item.startswith('plugin_')] - plugins[:]=list(set(plugins)) + plugins = [] + + def filter_plugins(items, plugins): + plugins += [item[7:].split('/')[0].split( + '.')[0] for item in items if item.startswith('plugin_')] + plugins[:] = list(set(plugins)) plugins.sort() return [item for item in items if not item.startswith('plugin_')] return dict(app=app, - models=filter_plugins(models,plugins), + models=filter_plugins(models, plugins), defines=defines, - controllers=filter_plugins(controllers,plugins), + controllers=filter_plugins(controllers, plugins), functions=functions, - views=filter_plugins(views,plugins), - modules=filter_plugins(modules,plugins), + views=filter_plugins(views, plugins), + modules=filter_plugins(modules, plugins), extend=extend, include=include, - privates=filter_plugins(privates,plugins), - statics=filter_plugins(statics,plugins), + privates=filter_plugins(privates, plugins), + statics=filter_plugins(statics, plugins), languages=languages, crontab=crontab, plugins=plugins) + def delete_plugin(): """ Object delete handler """ - app=request.args(0) + app = request.args(0) plugin = request.args(1) - plugin_name='plugin_'+plugin + plugin_name = 'plugin_' + plugin dialog = FORM.confirm( T('Delete'), - {T('Cancel'):URL('design', args=app)}) + {T('Cancel'): URL('design', args=app)}) if dialog.accepted: try: - for folder in ['models','views','controllers','static','modules', 'private']: - path=os.path.join(apath(app,r=request),folder) + for folder in ['models', 'views', 'controllers', 'static', 'modules', 'private']: + path = os.path.join(apath(app, r=request), folder) for item in os.listdir(path): - if item.rsplit('.',1)[0] == plugin_name: - filename=os.path.join(path,item) + if item.rsplit('.', 1)[0] == plugin_name: + filename = os.path.join(path, item) if os.path.isdir(filename): shutil.rmtree(filename) else: @@ -1012,7 +1064,8 @@ def delete_plugin(): session.flash = T('unable to delete file plugin "%(plugin)s"', dict(plugin=plugin)) redirect(URL('design', args=request.args(0), anchor=request.vars.id2)) - return dict(dialog=dialog,plugin=plugin) + return dict(dialog=dialog, plugin=plugin) + def plugin(): """ Application design handler """ @@ -1032,7 +1085,7 @@ def plugin(): # Get all models models = listdir(apath('%s/models/' % app, r=request), '.*\.py$') - models=[x.replace('\\','/') for x in models] + models = [x.replace('\\', '/') for x in models] defines = {} for m in models: data = safe_read(apath('%s/models/%s' % (app, m), r=request)) @@ -1040,8 +1093,9 @@ def plugin(): defines[m].sort() # Get all controllers - controllers = sorted(listdir(apath('%s/controllers/' % app, r=request), '.*\.py$')) - controllers = [x.replace('\\','/') for x in controllers] + controllers = sorted( + listdir(apath('%s/controllers/' % app, r=request), '.*\.py$')) + controllers = [x.replace('\\', '/') for x in controllers] functions = {} for c in controllers: data = safe_read(apath('%s/controllers/%s' % (app, c), r=request)) @@ -1049,8 +1103,9 @@ def plugin(): functions[c] = items # Get all views - views = sorted(listdir(apath('%s/views/' % app, r=request), '[\w/\-]+\.\w+$')) - views = [x.replace('\\','/') for x in views] + views = sorted( + listdir(apath('%s/views/' % app, r=request), '[\w/\-]+\.\w+$')) + views = [x.replace('\\', '/') for x in views] extend = {} include = {} for c in views: @@ -1064,23 +1119,23 @@ def plugin(): # Get all modules modules = listdir(apath('%s/modules/' % app, r=request), '.*\.py$') - modules = modules=[x.replace('\\','/') for x in modules] + modules = modules = [x.replace('\\', '/') for x in modules] modules.sort() # Get all private files privates = listdir(apath('%s/private/' % app, r=request), '[^\.#].*') - privates = [x.replace('\\','/') for x in privates] + privates = [x.replace('\\', '/') for x in privates] privates.sort() # Get all static files statics = listdir(apath('%s/static/' % app, r=request), '[^\.#].*') - statics = [x.replace('\\','/') for x in statics] + statics = [x.replace('\\', '/') for x in statics] statics.sort() # Get all languages - languages = sorted([lang+'.py' for lang, info in - T.get_possible_languages_info().iteritems() - if info[2]!=0]) # info[2] is langfile_mtime: + languages = sorted([lang + '.py' for lang, info in + T.get_possible_languages_info().iteritems() + if info[2] != 0]) # info[2] is langfile_mtime: # get only existed files #Get crontab @@ -1089,7 +1144,7 @@ def plugin(): safe_write(crontab, '#crontab') def filter_plugins(items): - regex=re.compile('^plugin_'+plugin+'(/.*|\..*)?$') + regex = re.compile('^plugin_' + plugin + '(/.*|\..*)?$') return [item for item in items if item and regex.match(item)] return dict(app=app, @@ -1109,10 +1164,10 @@ def plugin(): def create_file(): """ Create files handler """ - if request.vars and not request.vars.token==session.token: + if request.vars and not request.vars.token == session.token: redirect(URL('logout')) try: - anchor='#'+request.vars.id if request.vars.id else '' + anchor = '#' + request.vars.id if request.vars.id else '' if request.vars.app: app = get_app(request.vars.app) path = abspath(request.vars.location) @@ -1126,7 +1181,7 @@ def create_file(): raise SyntaxError if not filename[-3:] == '.py': filename += '.py' - lang = re.match('^plural_rules-(.*)\.py$',filename).group(1) + lang = re.match('^plural_rules-(.*)\.py$', filename).group(1) langinfo = read_possible_languages(apath(app, r=request))[lang] text = dedent(""" #!/usr/bin/env python @@ -1154,14 +1209,14 @@ def create_file(): raise SyntaxError if not filename[-3:] == '.py': filename += '.py' - path=os.path.join(apath(app, r=request),'languages',filename) + path = os.path.join(apath(app, r=request), 'languages', filename) if not os.path.exists(path): safe_write(path, '') # create language xx[-yy].py file: findT(apath(app, r=request), filename[:-3]) session.flash = T('language file "%(filename)s" created/updated', - dict(filename=filename)) - redirect(request.vars.sender+anchor) + dict(filename=filename)) + redirect(request.vars.sender + anchor) elif path[-8:] == '/models/': # Handle python models @@ -1188,21 +1243,22 @@ def create_file(): if request.vars.plugin and not filename.startswith('plugin_%s/' % request.vars.plugin): filename = 'plugin_%s/%s' % (request.vars.plugin, filename) # Handle template (html) views - if filename.find('.')<0: + if filename.find('.') < 0: filename += '.html' extension = filename.split('.')[-1].lower() if len(filename) == 5: raise SyntaxError - msg = T('This is the %(filename)s template', dict(filename=filename)) + msg = T( + 'This is the %(filename)s template', dict(filename=filename)) if extension == 'html': text = dedent(""" {{extend 'layout.html'}}

%s

{{=BEAUTIFY(response._vars)}}""" % msg)[1:] else: - generic = os.path.join(path,'generic.'+extension) + generic = os.path.join(path, 'generic.' + extension) if os.path.exists(generic): text = read_file(generic) else: @@ -1227,9 +1283,9 @@ def create_file(): if request.vars.plugin and not filename.startswith('plugin_%s/' % request.vars.plugin): filename = 'plugin_%s/%s' % (request.vars.plugin, filename) text = '' - + else: - redirect(request.vars.sender+anchor) + redirect(request.vars.sender + anchor) full_filename = os.path.join(path, filename) dirpath = os.path.dirname(full_filename) @@ -1241,24 +1297,26 @@ def create_file(): raise SyntaxError safe_write(full_filename, text) - log_progress(app,'CREATE',filename) + log_progress(app, 'CREATE', filename) session.flash = T('file "%(filename)s" created', dict(filename=full_filename[len(path):])) - vars={} - if request.vars.id: vars['id']=request.vars.id - if request.vars.app: vars['app']=request.vars.app + vars = {} + if request.vars.id: + vars['id'] = request.vars.id + if request.vars.app: + vars['app'] = request.vars.app redirect(URL('edit', args=[os.path.join(request.vars.location, filename)], vars=vars)) except Exception, e: - if not isinstance(e,HTTP): + if not isinstance(e, HTTP): session.flash = T('cannot create file') - redirect(request.vars.sender+anchor) + redirect(request.vars.sender + anchor) def upload_file(): """ File uploading handler """ - if request.vars and not request.vars.token==session.token: + if request.vars and not request.vars.token == session.token: redirect(URL('logout')) try: filename = None @@ -1294,14 +1352,14 @@ def upload_file(): data = request.vars.file.file.read() lineno = count_lines(data) safe_write(filename, data, 'wb') - log_progress(app,'UPLOAD',filename,lineno) + log_progress(app, 'UPLOAD', filename, lineno) session.flash = T('file "%(filename)s" uploaded', dict(filename=filename[len(path):])) except Exception: if filename: - d = dict(filename = filename[len(path):]) + d = dict(filename=filename[len(path):]) else: - d = dict(filename = 'unkown') + d = dict(filename='unkown') session.flash = T('cannot upload file "%(filename)s"', d) redirect(request.vars.sender) @@ -1319,7 +1377,8 @@ def errors(): method = request.args(1) or 'new' db_ready = {} db_ready['status'] = get_ticket_storage(app) - db_ready['errmessage'] = T("No ticket_storage.txt found under /private folder") + db_ready['errmessage'] = T( + "No ticket_storage.txt found under /private folder") db_ready['errlink'] = "http://web2py.com/books/default/chapter/29/13#Collecting-tickets" if method == 'new': @@ -1334,7 +1393,8 @@ def errors(): for fn in listdir(errors_path, '^[a-fA-F0-9.\-]+$'): fullpath = os.path.join(errors_path, fn) - if not os.path.isfile(fullpath): continue + if not os.path.isfile(fullpath): + continue try: fullpath_file = open(fullpath, 'r') try: @@ -1360,13 +1420,12 @@ def errors(): hash2error[hash] = dict(count=1, pickel=error, causer=error_causer, last_line=last_line, - hash=hash,ticket=fn) + hash=hash, ticket=fn) decorated = [(x['count'], x) for x in hash2error.values()] decorated.sort(key=operator.itemgetter(0), reverse=True) - return dict(errors = [x[1] for x in decorated], app=app, method=method, db_ready=db_ready) - + return dict(errors=[x[1] for x in decorated], app=app, method=method, db_ready=db_ready) elif method == 'dbnew': errors_path = apath('%s/errors' % app, r=request) @@ -1379,7 +1438,7 @@ def errors(): hash2error = dict() - for fn in tk_db(tk_table.id>0).select(): + for fn in tk_db(tk_table.id > 0).select(): try: error = pickle.loads(fn.ticket_data) except AttributeError: @@ -1401,13 +1460,13 @@ def errors(): hash2error[hash] = dict(count=1, pickel=error, causer=error_causer, last_line=last_line, - hash=hash,ticket=fn.ticket_id) + hash=hash, ticket=fn.ticket_id) decorated = [(x['count'], x) for x in hash2error.values()] decorated.sort(key=operator.itemgetter(0), reverse=True) - return dict(errors = [x[1] for x in decorated], app=app, method=method) + return dict(errors=[x[1] for x in decorated], app=app, method=method) elif method == 'dbold': tk_db, tk_table = get_ticket_storage(app) @@ -1415,9 +1474,10 @@ def errors(): if item[:7] == 'delete_': tk_db(tk_table.ticket_id == item[7:]).delete() tk_db.commit() - tickets_ = tk_db(tk_table.id>0).select(tk_table.ticket_id, tk_table.created_datetime, orderby=~tk_table.created_datetime) + tickets_ = tk_db(tk_table.id > 0).select(tk_table.ticket_id, tk_table.created_datetime, orderby=~tk_table.created_datetime) tickets = [row.ticket_id for row in tickets_] - times = dict([(row.ticket_id, row.created_datetime) for row in tickets_]) + times = dict( + [(row.ticket_id, row.created_datetime) for row in tickets_]) return dict(app=app, tickets=tickets, method=method, times=times) @@ -1425,20 +1485,22 @@ def errors(): for item in request.vars: if item[:7] == 'delete_': os.unlink(apath('%s/errors/%s' % (app, item[7:]), r=request)) - func = lambda p: os.stat(apath('%s/errors/%s' % \ - (app, p), r=request)).st_mtime - tickets = sorted(listdir(apath('%s/errors/' % app, r=request), '^\w.*'), - key=func, - reverse=True) + func = lambda p: os.stat(apath('%s/errors/%s' % + (app, p), r=request)).st_mtime + tickets = sorted( + listdir(apath('%s/errors/' % app, r=request), '^\w.*'), + key=func, + reverse=True) return dict(app=app, tickets=tickets, method=method, db_ready=db_ready) + def get_ticket_storage(app): private_folder = apath('%s/private' % app, r=request) ticket_file = os.path.join(private_folder, 'ticket_storage.txt') if os.path.exists(ticket_file): db_string = open(ticket_file).read() - db_string = db_string.strip().replace('\r','').replace('\n','') + db_string = db_string.strip().replace('\r', '').replace('\n', '') else: return False tickets_table = 'web2py_ticket' @@ -1447,12 +1509,13 @@ def get_ticket_storage(app): ticketsdb = DAL(db_string, folder=db_path, auto_import=True) if not ticketsdb.get(tablename): table = ticketsdb.define_table( - tablename, - Field('ticket_id', length=100), - Field('ticket_data', 'text'), - Field('created_datetime', 'datetime'), - ) - return ticketsdb , ticketsdb.get(tablename) + tablename, + Field('ticket_id', length=100), + Field('ticket_data', 'text'), + Field('created_datetime', 'datetime'), + ) + return ticketsdb, ticketsdb.get(tablename) + def make_link(path): """ Create a link from a path """ @@ -1465,7 +1528,7 @@ def make_link(path): editable = {'controllers': '.py', 'models': '.py', 'views': '.html'} for key in editable.keys(): - check_extension = folder.endswith("%s/%s" % (app,key)) + check_extension = folder.endswith("%s/%s" % (app, key)) if ext.lower() == editable[key] and check_extension: return A('"' + tryFile + '"', _href=URL(r=request, @@ -1536,6 +1599,7 @@ def ticket(): layer=e.layer, myversion=myversion) + def ticketdb(): """ Ticket handler """ @@ -1559,17 +1623,19 @@ def ticketdb(): layer=e.layer, myversion=myversion) + def error(): """ Generate a ticket (for testing) """ raise RuntimeError('admin ticket generator at your service') + def update_languages(): """ Update available languages """ app = get_app() update_all_languages(apath(app, r=request)) session.flash = T('Language files (static strings) updated') - redirect(URL('design',args=app,anchor='languages')) + redirect(URL('design', args=app, anchor='languages')) def twitter(): @@ -1585,11 +1651,11 @@ def twitter(): for e in data: d[e["id"]] = e r = reversed(sorted(d)) - return dict(tweets = [d[k] for k in r]) + return dict(tweets=[d[k] for k in r]) else: return 'disabled' except Exception, e: - return DIV(T('Unable to download because:'),BR(),str(e)) + return DIV(T('Unable to download because:'), BR(), str(e)) def user(): @@ -1600,12 +1666,14 @@ def user(): else: return dict(form=T("Disabled")) + def reload_routes(): """ Reload routes.py """ import gluon.rewrite gluon.rewrite.load() redirect(URL('site')) + def manage_students(): if not (MULTI_USER_MODE and is_manager()): session.flash = T('Not Authorized') @@ -1614,18 +1682,19 @@ def manage_students(): grid = SQLFORM.grid(db.auth_user) return locals() + def bulk_register(): if not (MULTI_USER_MODE and is_manager()): session.flash = T('Not Authorized') redirect(URL('site')) - form = SQLFORM.factory(Field('emails','text')) + form = SQLFORM.factory(Field('emails', 'text')) if form.process().accepted: emails = [x.strip() for x in form.vars.emails.split('\n') if x.strip()] n = 0 for email in emails: if not db.auth_user(email=email): - n += db.auth_user.insert(email = email) and 1 or 0 - session.flash = T('%s students registered',n) + n += db.auth_user.insert(email=email) and 1 or 0 + session.flash = T('%s students registered', n) redirect(URL('site')) return locals() @@ -1634,6 +1703,7 @@ def bulk_register(): # 2) should not prompt user at console # 3) should give option to force commit and not reuqire manual merge + def git_pull(): """ Git Pull handler """ app = get_app() @@ -1641,10 +1711,10 @@ def git_pull(): session.flash = GIT_MISSING redirect(URL('site')) dialog = FORM.confirm(T('Pull'), - {T('Cancel'):URL('site')}) + {T('Cancel'): URL('site')}) if dialog.accepted: try: - repo = Repo(os.path.join(apath(r=request),app)) + repo = Repo(os.path.join(apath(r=request), app)) origin = repo.remotes.origin origin.fetch() origin.pull() @@ -1660,14 +1730,16 @@ def git_pull(): session.flash = T("Pull is not possible because you have unmerged files. Fix them up in the work tree, and then try again.") redirect(URL('site')) except GitCommandError, status: - session.flash = T("Pull failed, git exited abnormally. See logs for details.") + session.flash = T( + "Pull failed, git exited abnormally. See logs for details.") redirect(URL('site')) - except Exception,e: - session.flash = T("Pull failed, git exited abnormally. See logs for details.") + except Exception, e: + session.flash = T( + "Pull failed, git exited abnormally. See logs for details.") redirect(URL('site')) elif 'cancel' in request.vars: redirect(URL('site')) - return dict(app=app,dialog=dialog) + return dict(app=app, dialog=dialog) def git_push(): @@ -1676,25 +1748,26 @@ def git_push(): if not have_git: session.flash = GIT_MISSING redirect(URL('site')) - form = SQLFORM.factory(Field('changelog',requires=IS_NOT_EMPTY())) - form.element('input[type=submit]')['_value']=T('Push') - form.add_button(T('Cancel'),URL('site')) + form = SQLFORM.factory(Field('changelog', requires=IS_NOT_EMPTY())) + form.element('input[type=submit]')['_value'] = T('Push') + form.add_button(T('Cancel'), URL('site')) form.process() if form.accepted: try: - repo = Repo(os.path.join(apath(r=request),app)) + repo = Repo(os.path.join(apath(r=request), app)) index = repo.index - index.add([apath(r=request)+app+'/*']) + index.add([apath(r=request) + app + '/*']) new_commit = index.commit(form.vars.changelog) origin = repo.remotes.origin origin.push() - session.flash = T("Git repo updated with latest application changes.") + session.flash = T( + "Git repo updated with latest application changes.") redirect(URL('site')) except UnmergedEntriesError: session.flash = T("Push failed, there are unmerged entries in the cache. Resolve merge issues manually and try again.") redirect(URL('site')) except Exception, e: - session.flash = T("Push failed, git exited abnormally. See logs for details.") + session.flash = T( + "Push failed, git exited abnormally. See logs for details.") redirect(URL('site')) - return dict(app=app,form=form) - + return dict(app=app, form=form) diff --git a/applications/admin/controllers/gae.py b/applications/admin/controllers/gae.py index ee20442b..ddb13e0a 100644 --- a/applications/admin/controllers/gae.py +++ b/applications/admin/controllers/gae.py @@ -9,85 +9,92 @@ try: import shutil from gluon.fileutils import read_file, write_file except: - session.flash='sorry, only on Unix systems' - redirect(URL(request.application,'default','site')) + session.flash = 'sorry, only on Unix systems' + redirect(URL(request.application, 'default', 'site')) if MULTI_USER_MODE and not is_manager(): session.flash = 'Not Authorized' - redirect(URL('default','site')) + redirect(URL('default', 'site')) + +forever = 10 ** 8 -forever=10**8 def kill(): - p = cache.ram('gae_upload',lambda:None,forever) - if not p or p.poll()!=None: + p = cache.ram('gae_upload', lambda: None, forever) + if not p or p.poll() is not None: return 'oops' os.kill(p.pid, signal.SIGKILL) - cache.ram('gae_upload',lambda:None,-1) + cache.ram('gae_upload', lambda: None, -1) + class EXISTS(object): def __init__(self, error_message='file not found'): self.error_message = error_message + def __call__(self, value): if os.path.exists(value): - return (value,None) - return (value,self.error_message) + return (value, None) + return (value, self.error_message) + def deploy(): regex = re.compile('^\w+$') - apps = sorted(file for file in os.listdir(apath(r=request)) if regex.match(file)) + apps = sorted( + file for file in os.listdir(apath(r=request)) if regex.match(file)) form = SQLFORM.factory( - Field('appcfg',default=GAE_APPCFG,label=T('Path to appcfg.py'), + Field('appcfg', default=GAE_APPCFG, label=T('Path to appcfg.py'), requires=EXISTS(error_message=T('file not found'))), - Field('google_application_id',requires=IS_MATCH('[\w\-]+'),label=T('Google Application Id')), - Field('applications','list:string', - requires=IS_IN_SET(apps,multiple=True), + Field('google_application_id', requires=IS_MATCH( + '[\w\-]+'), label=T('Google Application Id')), + Field('applications', 'list:string', + requires=IS_IN_SET(apps, multiple=True), label=T('web2py apps to deploy')), - Field('email',requires=IS_EMAIL(),label=T('GAE Email')), - Field('password','password',requires=IS_NOT_EMPTY(),label=T('GAE Password'))) - cmd = output = errors= "" - if form.accepts(request,session): + Field('email', requires=IS_EMAIL(), label=T('GAE Email')), + Field('password', 'password', requires=IS_NOT_EMPTY(), label=T('GAE Password'))) + cmd = output = errors = "" + if form.accepts(request, session): try: kill() except: pass - ignore_apps = [item for item in apps \ - if not item in form.vars.applications] + ignore_apps = [item for item in apps + if not item in form.vars.applications] regex = re.compile('\(applications/\(.*') yaml = apath('../app.yaml', r=request) if not os.path.exists(yaml): example = apath('../app.example.yaml', r=request) - shutil.copyfile(example,yaml) + shutil.copyfile(example, yaml) data = read_file(yaml) - data = re.sub('application:.*','application: %s' % form.vars.google_application_id,data) - data = regex.sub('(applications/(%s)/.*)|' % '|'.join(ignore_apps),data) + data = re.sub('application:.*', 'application: %s' % + form.vars.google_application_id, data) + data = regex.sub( + '(applications/(%s)/.*)|' % '|'.join(ignore_apps), data) write_file(yaml, data) path = request.env.applications_parent cmd = '%s --email=%s --passin update %s' % \ (form.vars.appcfg, form.vars.email, path) p = cache.ram('gae_upload', - lambda s=subprocess,c=cmd:s.Popen(c, shell=True, - stdin=s.PIPE, - stdout=s.PIPE, - stderr=s.PIPE, close_fds=True),-1) - p.stdin.write(form.vars.password+'\n') + lambda s=subprocess, c=cmd: s.Popen(c, shell=True, + stdin=s.PIPE, + stdout=s.PIPE, + stderr=s.PIPE, close_fds=True), -1) + p.stdin.write(form.vars.password + '\n') fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) fcntl.fcntl(p.stderr.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) - return dict(form=form,command=cmd) + return dict(form=form, command=cmd) + def callback(): - p = cache.ram('gae_upload',lambda:None,forever) - if not p or p.poll()!=None: + p = cache.ram('gae_upload', lambda: None, forever) + if not p or p.poll() is not None: return '' try: output = p.stdout.read() except: - output='' + output = '' try: errors = p.stderr.read() except: - errors='' - return (output+errors).replace('\n','
') - - + errors = '' + return (output + errors).replace('\n', '
') diff --git a/applications/admin/controllers/mercurial.py b/applications/admin/controllers/mercurial.py index 6c063097..dd70a85e 100644 --- a/applications/admin/controllers/mercurial.py +++ b/applications/admin/controllers/mercurial.py @@ -2,10 +2,10 @@ from gluon.fileutils import read_file, write_file if DEMO_MODE or MULTI_USER_MODE: session.flash = T('disabled in demo mode') - redirect(URL('default','site')) + redirect(URL('default', 'site')) if not have_mercurial: - session.flash=T("Sorry, could not find mercurial installed") - redirect(URL('default','design',args=request.args(0))) + session.flash = T("Sorry, could not find mercurial installed") + redirect(URL('default', 'design', args=request.args(0))) _hgignore_content = """\ syntax: glob @@ -22,6 +22,7 @@ sessions/* errors/* """ + def hg_repo(path): import os uio = ui.ui() @@ -37,13 +38,14 @@ def hg_repo(path): write_file(hgignore, _hgignore_content) return repo + def commit(): app = request.args(0) path = apath(app, r=request) repo = hg_repo(path) - form = FORM('Comment:',INPUT(_name='comment',requires=IS_NOT_EMPTY()), - INPUT(_type='submit',_value=T('Commit'))) - if form.accepts(request.vars,session): + form = FORM('Comment:', INPUT(_name='comment', requires=IS_NOT_EMPTY()), + INPUT(_type='submit', _value=T('Commit'))) + if form.accepts(request.vars, session): oldid = repo[repo.lookup('.')] addremove(repo) repo.commit(text=form.vars.comment) @@ -51,34 +53,33 @@ def commit(): response.flash = 'no changes' try: files = TABLE(*[TR(file) for file in repo[repo.lookup('.')].files()]) - changes = TABLE(TR(TH('revision'),TH('description'))) + changes = TABLE(TR(TH('revision'), TH('description'))) for change in repo.changelog: - ctx=repo.changectx(change) + ctx = repo.changectx(change) revision, description = ctx.rev(), ctx.description() - changes.append(TR(A(revision,_href=URL('revision', - args=(app,revision))), + changes.append(TR(A(revision, _href=URL('revision', + args=(app, revision))), description)) except: files = [] changes = [] - return dict(form=form,files=files,changes=changes,repo=repo) + return dict(form=form, files=files, changes=changes, repo=repo) + def revision(): app = request.args(0) path = apath(app, r=request) repo = hg_repo(path) revision = request.args(1) - ctx=repo.changectx(revision) - form=FORM(INPUT(_type='submit',_value=T('Revert'))) + ctx = repo.changectx(revision) + form = FORM(INPUT(_type='submit', _value=T('Revert'))) if form.accepts(request.vars): hg.update(repo, revision) session.flash = "reverted to revision %s" % ctx.rev() - redirect(URL('default','design',args=app)) + redirect(URL('default', 'design', args=app)) return dict( files=ctx.files(), rev=str(ctx.rev()), desc=ctx.description(), form=form - ) - - + ) diff --git a/applications/admin/controllers/openshift.py b/applications/admin/controllers/openshift.py index 88945f74..e42b61fc 100644 --- a/applications/admin/controllers/openshift.py +++ b/applications/admin/controllers/openshift.py @@ -1,29 +1,36 @@ import os -from distutils import dir_util +try: + from distutils import dir_util +except ImportError: + session.flash = T('requires distutils, but not installed') + redirect(URL('default', 'site')) try: from git import * except ImportError: session.flash = T('requires python-git, but not installed') - redirect(URL('default','site')) + redirect(URL('default', 'site')) + def deploy(): apps = sorted(file for file in os.listdir(apath(r=request))) form = SQLFORM.factory( - Field('osrepo',default='/tmp',label=T('Path to local openshift repo root.'), - requires=EXISTS(error_message=T('directory not found'))), - Field('osname',default='web2py',label=T('WSGI reference name')), - Field('applications','list:string', - requires=IS_IN_SET(apps,multiple=True), + Field( + 'osrepo', default='/tmp', label=T('Path to local openshift repo root.'), + requires=EXISTS(error_message=T('directory not found'))), + Field('osname', default='web2py', label=T('WSGI reference name')), + Field('applications', 'list:string', + requires=IS_IN_SET(apps, multiple=True), label=T('web2py apps to deploy'))) - cmd = output = errors= "" - if form.accepts(request,session): + cmd = output = errors = "" + if form.accepts(request, session): try: kill() except: pass - - ignore_apps = [item for item in apps if not item in form.vars.applications] + + ignore_apps = [ + item for item in apps if not item in form.vars.applications] regex = re.compile('\(applications/\(.*') w2p_origin = os.getcwd() osrepo = form.vars.osrepo @@ -34,23 +41,25 @@ def deploy(): assert repo.bare == False for i in form.vars.applications: - appsrc = os.path.join(apath(r=request),i) - appdest = os.path.join(osrepo,'wsgi',osname,'applications',i) - dir_util.copy_tree(appsrc,appdest) + appsrc = os.path.join(apath(r=request), i) + appdest = os.path.join(osrepo, 'wsgi', osname, 'applications', i) + dir_util.copy_tree(appsrc, appdest) #shutil.copytree(appsrc,appdest) - index.add(['wsgi/'+osname+'/applications/'+i]) + index.add(['wsgi/' + osname + '/applications/' + i]) new_commit = index.commit("Deploy from Web2py IDE") - + origin = repo.remotes.origin origin.push origin.push() #Git code ends here - return dict(form=form,command=cmd) - + return dict(form=form, command=cmd) + + class EXISTS(object): def __init__(self, error_message='file not found'): self.error_message = error_message + def __call__(self, value): if os.path.exists(value): - return (value,None) - return (value,self.error_message) + return (value, None) + return (value, self.error_message) diff --git a/applications/admin/controllers/plugin_jqmobile.py b/applications/admin/controllers/plugin_jqmobile.py index bc72c15d..18fc67d7 100644 --- a/applications/admin/controllers/plugin_jqmobile.py +++ b/applications/admin/controllers/plugin_jqmobile.py @@ -1,10 +1,10 @@ -response.files=response.files[:3] -response.menu=[] +response.files = response.files[:3] +response.menu = [] + def index(): return locals() + def about(): return locals() - - diff --git a/applications/admin/controllers/shell.py b/applications/admin/controllers/shell.py index 9ce45262..1abc0f72 100644 --- a/applications/admin/controllers/shell.py +++ b/applications/admin/controllers/shell.py @@ -1,25 +1,29 @@ import sys import cStringIO import gluon.contrib.shell -import code, thread +import code +import thread from gluon.shell import env if DEMO_MODE or MULTI_USER_MODE: session.flash = T('disabled in demo mode') - redirect(URL('default','site')) + redirect(URL('default', 'site')) + +FE = 10 ** 9 -FE=10**9 def index(): app = request.args(0) or 'admin' reset() return dict(app=app) + def callback(): app = request.args[0] command = request.vars.statement - escape = command[:1]!='!' - history = session['history:'+app] = session.get('history:'+app,gluon.contrib.shell.History()) + escape = command[:1] != '!' + history = session['history:' + app] = session.get( + 'history:' + app, gluon.contrib.shell.History()) if not escape: command = command[1:] if command == '%reset': @@ -27,21 +31,20 @@ def callback(): return '*** reset ***' elif command[0] == '%': try: - command=session['commands:'+app][int(command[1:])] + command = session['commands:' + app][int(command[1:])] except ValueError: return '' - session['commands:'+app].append(command) - environ=env(app,True) - output = gluon.contrib.shell.run(history,command,environ) - k = len(session['commands:'+app]) - 1 + session['commands:' + app].append(command) + environ = env(app, True) + output = gluon.contrib.shell.run(history, command, environ) + k = len(session['commands:' + app]) - 1 #output = PRE(output) #return TABLE(TR('In[%i]:'%k,PRE(command)),TR('Out[%i]:'%k,output)) return 'In [%i] : %s%s\n' % (k + 1, command, output) + def reset(): app = request.args(0) or 'admin' - session['commands:'+app] = [] - session['history:'+app] = gluon.contrib.shell.History() + session['commands:' + app] = [] + session['history:' + app] = gluon.contrib.shell.History() return 'done' - - diff --git a/applications/admin/controllers/toolbar.py b/applications/admin/controllers/toolbar.py index 25e83a3e..83a25f83 100644 --- a/applications/admin/controllers/toolbar.py +++ b/applications/admin/controllers/toolbar.py @@ -2,10 +2,12 @@ import os from gluon.settings import global_settings, read_file # + def index(): app = request.args(0) return dict(app=app) + def profiler(): """ to use the profiler start web2py with -F profiler.log @@ -19,13 +21,11 @@ def profiler(): else: size = 0 if os.path.exists(filename): - data = read_file('profiler.log','rb') - if size=m: redirect(URL('step2')) - table=session.app['tables'][n] - form=SQLFORM.factory(Field('field_names','list:string', - default=session.app.get('table_'+table,[]))) + response.view = 'wizard/step.html' + n = int(request.args(0) or 0) + m = len(session.app['tables']) + if n >= m: + redirect(URL('step2')) + table = session.app['tables'][n] + form = SQLFORM.factory(Field('field_names', 'list:string', + default=session.app.get('table_' + table, []))) if form.accepts(request.vars) and form.vars.field_names: - fields=listify(form.vars.field_names) - if table=='auth_user': - for field in ['first_name','last_name','username','email','password']: + fields = listify(form.vars.field_names) + if table == 'auth_user': + for field in ['first_name', 'last_name', 'username', 'email', 'password']: if not field in fields: fields.append(field) - session.app['table_'+table]=[t.strip().lower() - for t in listify(form.vars.field_names) - if t.strip()] + session.app['table_' + table] = [t.strip().lower() + for t in listify(form.vars.field_names) + if t.strip()] try: - tables=sort_tables(session.app['tables']) + tables = sort_tables(session.app['tables']) except RuntimeError: - response.flash=T('invalid circular reference') + response.flash = T('invalid circular reference') else: - if n=m: redirect(URL('step4')) - page=session.app['pages'][n] - markmin_url='http://web2py.com/examples/static/markmin.html' - form=SQLFORM.factory(Field('content','text', - default=session.app.get('page_'+page,[]), - comment=A('use markmin', - _href=markmin_url,_target='_blank')), - formstyle='table2cols') + response.view = 'wizard/step.html' + n = int(request.args(0) or 0) + m = len(session.app['pages']) + if n >= m: + redirect(URL('step4')) + page = session.app['pages'][n] + markmin_url = 'http://web2py.com/examples/static/markmin.html' + form = SQLFORM.factory(Field('content', 'text', + default=session.app.get('page_' + page, []), + comment=A('use markmin', + _href=markmin_url, _target='_blank')), + formstyle='table2cols') if form.accepts(request.vars): - session.app['page_'+page]=form.vars.content - if n:<переклад> для вибраної мови', 'try something like': 'спробуйте щось схоже на', +'Try the mobile interface': 'Спробуйте мобільний інтерфейс', 'try view': 'дивитись результат', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'наберіть тут будь-які команди ладнача PDB і натисніть клавішу [Return] ([Enter]), щоб запустити їх на виконання.', 'Type python statement in here and hit Return (Enter) to execute it.': 'Наберіть тут будь-які вирази Python і натисніть клавішу [Return] ([Enter]), щоб запустити їх на виконання.', @@ -463,7 +468,7 @@ 'Views': 'Відображення (Views)', 'views': 'відображення', 'WARNING:': 'ПОПЕРЕДЖЕННЯ:', -'Web Framework': 'Web Framework', +'Web Framework': 'Веб-каркас (Web Framework)', 'web2py apps to deploy': 'Готові до розгортання додатки web2py', 'web2py Debugger': 'Ладнач web2py', 'web2py downgrade': 'повернення на попередню версію web2py', diff --git a/applications/admin/models/0.py b/applications/admin/models/0.py index ec92f27a..be8fe37a 100644 --- a/applications/admin/models/0.py +++ b/applications/admin/models/0.py @@ -1,7 +1,7 @@ EXPIRATION = 60 * 60 # logout after 60 minutes of inactivity CHECK_VERSION = True WEB2PY_URL = 'http://web2py.com' -WEB2PY_VERSION_URL = WEB2PY_URL+'/examples/default/version' +WEB2PY_VERSION_URL = WEB2PY_URL + '/examples/default/version' ########################################################################### # Preferences for EditArea @@ -13,15 +13,15 @@ TEXT_EDITOR = 'codemirror' or 'ace' or 'edit_area' or 'amy' ## Editor Color scheme (only for ace) TEXT_EDITOR_THEME = ( - "chrome", "clouds", "clouds_midnight", "cobalt", "crimson_editor", "dawn", + "chrome", "clouds", "clouds_midnight", "cobalt", "crimson_editor", "dawn", "dreamweaver", "eclipse", "idle_fingers", "kr_theme", "merbivore", - "merbivore_soft", "monokai", "mono_industrial", "pastel_on_dark", + "merbivore_soft", "monokai", "mono_industrial", "pastel_on_dark", "solarized_dark", "solarized_light", "textmate", "tomorrow", "tomorrow_night", "tomorrow_night_blue", "tomorrow_night_bright", "tomorrow_night_eighties", "twilight", "vibrant_ink")[0] ## Editor Keyboard bindings (only for ace and codemirror) -TEXT_EDITOR_KEYBINDING = '' # 'emacs' or 'vi' +TEXT_EDITOR_KEYBINDING = '' # 'emacs' or 'vi' ### edit_area only # The default font size, measured in 'points'. The value must be an integer > 0 @@ -59,9 +59,9 @@ GAE_APPCFG = os.path.abspath(os.path.join('/usr/local/bin/appcfg.py')) # To use web2py as a teaching tool, set MULTI_USER_MODE to True MULTI_USER_MODE = False -EMAIL_SERVER = 'localhost' -EMAIL_SENDER = 'professor@example.com' -EMAIL_LOGIN = None +EMAIL_SERVER = 'localhost' +EMAIL_SENDER = 'professor@example.com' +EMAIL_LOGIN = None # configurable twitterbox, set to None/False to suppress TWITTER_HASH = "web2py" @@ -78,5 +78,3 @@ PLUGINS_APP = 'http://web2py.com/plugins' # set the language if 'adminLanguage' in request.cookies and not (request.cookies['adminLanguage'] is None): T.force(request.cookies['adminLanguage'].value) - - diff --git a/applications/admin/models/0_imports.py b/applications/admin/models/0_imports.py index 0b3b4597..b911c0e7 100644 --- a/applications/admin/models/0_imports.py +++ b/applications/admin/models/0_imports.py @@ -28,5 +28,3 @@ from gluon.languages import findT, update_all_languages from gluon.myregex import * from gluon.restricted import * from gluon.compileapp import compile_application, remove_compiled_application - - diff --git a/applications/admin/models/access.py b/applications/admin/models/access.py index a7066e4e..d504b035 100644 --- a/applications/admin/models/access.py +++ b/applications/admin/models/access.py @@ -1,4 +1,6 @@ -import base64, os, time +import base64 +import os +import time from gluon import portalocker from gluon.admin import apath from gluon.fileutils import read_file @@ -24,7 +26,8 @@ elif not request.is_local and not DEMO_MODE: try: _config = {} port = int(request.env.server_port or 0) - restricted(read_file(apath('../parameters_%i.py' % port, request)), _config) + restricted( + read_file(apath('../parameters_%i.py' % port, request)), _config) if not 'password' in _config or not _config['password']: raise HTTP(200, T('admin disabled because no admin password')) @@ -38,7 +41,8 @@ except IOError: raise HTTP(200, T('admin disabled because not supported on google app engine')) else: - raise HTTP(200, T('admin disabled because unable to access password file')) + raise HTTP( + 200, T('admin disabled because unable to access password file')) def verify_password(password): @@ -50,7 +54,7 @@ def verify_password(password): elif _config['password'].startswith('pam_user:'): session.pam_user = _config['password'][9:].strip() import gluon.contrib.pam - return gluon.contrib.pam.authenticate(session.pam_user,password) + return gluon.contrib.pam.authenticate(session.pam_user, password) else: return _config['password'] == CRYPT()(password)[0] @@ -63,6 +67,7 @@ deny_file = os.path.join(request.folder, 'private', 'hosts.deny') allowed_number_of_attempts = 5 expiration_failed_logins = 3600 + def read_hosts_deny(): import datetime hosts = {} @@ -75,7 +80,7 @@ def read_hosts_deny(): continue fields = line.strip().split() if len(fields) > 2: - hosts[fields[0].strip()] = ( # ip + hosts[fields[0].strip()] = ( # ip int(fields[1].strip()), # n attemps int(fields[2].strip()) # last attempts ) @@ -83,28 +88,30 @@ def read_hosts_deny(): f.close() return hosts + def write_hosts_deny(denied_hosts): f = open(deny_file, 'w') portalocker.lock(f, portalocker.LOCK_EX) for key, val in denied_hosts.items(): - if time.time()-val[1] < expiration_failed_logins: + if time.time() - val[1] < expiration_failed_logins: line = '%s %s %s\n' % (key, val[0], val[1]) f.write(line) portalocker.unlock(f) f.close() + def login_record(success=True): denied_hosts = read_hosts_deny() - val = (0,0) + val = (0, 0) if success and request.client in denied_hosts: del denied_hosts[request.client] elif not success and not request.is_local: - val = denied_hosts.get(request.client,(0,0)) - if time.time()-val[1]= allowed_number_of_attempts: - return val[0] # locked out - time.sleep(2**val[0]) - val = (val[0]+1,int(time.time())) + return val[0] # locked out + time.sleep(2 ** val[0]) + val = (val[0] + 1, int(time.time())) denied_hosts[request.client] = val write_hosts_deny(denied_hosts) return val[0] @@ -124,9 +131,9 @@ if session.authorized: session.last_time = t0 -if request.vars.is_mobile in ('true','false','auto'): +if request.vars.is_mobile in ('true', 'false', 'auto'): session.is_mobile = request.vars.is_mobile or 'auto' -if request.controller=='default' and request.function=='index': +if request.controller == 'default' and request.function == 'index': if not request.vars.is_mobile: session.is_mobile = 'auto' if not session.is_mobile: @@ -141,14 +148,14 @@ else: if request.controller == "webservices": basic = request.env.http_authorization if not basic or not basic[:6].lower() == 'basic ': - raise HTTP(401,"Wrong credentials") + raise HTTP(401, "Wrong credentials") (username, password) = base64.b64decode(basic[6:]).split(':') if not verify_password(password) or MULTI_USER_MODE: time.sleep(10) - raise HTTP(403,"Not authorized") + raise HTTP(403, "Not authorized") elif not session.authorized and not \ - (request.controller+'/'+request.function in - ('default/index','default/user','plugin_jqmobile/index','plugin_jqmobile/about')): + (request.controller + '/' + request.function in + ('default/index', 'default/user', 'plugin_jqmobile/index', 'plugin_jqmobile/about')): if request.env.query_string: query_string = '?' + request.env.query_string @@ -165,7 +172,6 @@ elif session.authorized and \ request.function == 'index': redirect(URL(request.application, 'default', 'site')) -if request.controller=='appadmin' and DEMO_MODE: +if request.controller == 'appadmin' and DEMO_MODE: session.flash = 'Appadmin disabled in demo mode' - redirect(URL('default','sites')) - + redirect(URL('default', 'sites')) diff --git a/applications/admin/models/buttons.py b/applications/admin/models/buttons.py index 594acd3e..a7bbbedc 100644 --- a/applications/admin/models/buttons.py +++ b/applications/admin/models/buttons.py @@ -2,37 +2,41 @@ import os -def A_button(*a,**b): + +def A_button(*a, **b): b['_data-role'] = 'button' b['_data-inline'] = 'true' - return A(*a,**b) + return A(*a, **b) + def button(href, label): if is_mobile: ret = A_button(SPAN(label), _href=href) else: - ret = A(SPAN(label),_class='button',_href=href) + ret = A(SPAN(label), _class='button', _href=href) return ret + def button_enable(href, app): - if os.path.exists(os.path.join(apath(app,r=request),'DISABLED')): - label = SPAN(T('Enable'),_style='color:red') + if os.path.exists(os.path.join(apath(app, r=request), 'DISABLED')): + label = SPAN(T('Enable'), _style='color:red') else: - label = SPAN(T('Disable'),_style='color:green') - id = 'enable_'+app - return A(label,_class='button',_id=id,callback=href,target=id) + label = SPAN(T('Disable'), _style='color:green') + id = 'enable_' + app + return A(label, _class='button', _id=id, callback=href, target=id) + def sp_button(href, label): if request.user_agent().is_mobile: ret = A_button(SPAN(label), _href=href) else: - ret = A(SPAN(label),_class='button special',_href=href) + ret = A(SPAN(label), _class='button special', _href=href) return ret + def helpicon(): return IMG(_src=URL('static', 'images/help.png'), _alt='help') + def searchbox(elementid): - return TAG[''](LABEL(IMG(_id="search_start",_src=URL('static', 'images/search.png'), _alt=T('filter')), _class='icon', _for=elementid), ' ', INPUT(_id=elementid, _type='text', _size=12)) - - + return TAG[''](LABEL(IMG(_id="search_start", _src=URL('static', 'images/search.png'), _alt=T('filter')), _class='icon', _for=elementid), ' ', INPUT(_id=elementid, _type='text', _size=12)) diff --git a/applications/admin/models/db.py b/applications/admin/models/db.py index 4b98283a..d6871cb2 100644 --- a/applications/admin/models/db.py +++ b/applications/admin/models/db.py @@ -4,37 +4,39 @@ if MULTI_USER_MODE: db = DAL('sqlite://storage.sqlite') # if not, use SQLite or other DB from gluon.tools import * - auth = Auth(globals(),db) # authentication/authorization - crud = Crud(globals(),db) # for CRUD helpers using auth - service = Service(globals()) # for json, xml, jsonrpc, xmlrpc, amfrpc + auth = Auth( + globals(), db) # authentication/authorization + crud = Crud( + globals(), db) # for CRUD helpers using auth + service = Service( + globals()) # for json, xml, jsonrpc, xmlrpc, amfrpc plugins = PluginManager() mail = auth.settings.mailer mail.settings.server = EMAIL_SERVER mail.settings.sender = EMAIL_SENDER - mail.settings.login = EMAIL_LOGIN + mail.settings.login = EMAIL_LOGIN auth.settings.extra_fields['auth_user'] = \ - [Field('is_manager','boolean',default=False,writable=False)] + [Field('is_manager', 'boolean', default=False, writable=False)] auth.define_tables() # creates all needed tables auth.settings.registration_requires_verification = False auth.settings.registration_requires_approval = True auth.settings.reset_password_requires_verification = True - db.define_table('app',Field('name'),Field('owner',db.auth_user)) + db.define_table('app', Field('name'), Field('owner', db.auth_user)) if not session.authorized and MULTI_USER_MODE: - if auth.user and not request.function=='user': + if auth.user and not request.function == 'user': session.authorized = True - elif not request.function=='user': - redirect(URL('default','user/login')) + elif not request.function == 'user': + redirect(URL('default', 'user/login')) + def is_manager(): if not MULTI_USER_MODE: return True - elif auth.user and (auth.user.id==1 or auth.user.is_manager): + elif auth.user and (auth.user.id == 1 or auth.user.is_manager): return True else: return False - - diff --git a/applications/admin/models/menu.py b/applications/admin/models/menu.py index 0ec6a810..7429def1 100644 --- a/applications/admin/models/menu.py +++ b/applications/admin/models/menu.py @@ -7,31 +7,30 @@ _c = request.controller _f = request.function response.title = '%s %s' % (_f, '/'.join(request.args)) response.subtitle = 'admin' -response.menu = [(T('Site'), _f == 'site', URL(_a,'default','site'))] +response.menu = [(T('Site'), _f == 'site', URL(_a, 'default', 'site'))] if request.vars.app or request.args: _t = request.vars.app or request.args[0] response.menu.append((T('Edit'), _c == 'default' and _f == 'design', - URL(_a,'default','design',args=_t))) + URL(_a, 'default', 'design', args=_t))) response.menu.append((T('About'), _c == 'default' and _f == 'about', - URL(_a,'default','about',args=_t,))) + URL(_a, 'default', 'about', args=_t,))) response.menu.append((T('Errors'), _c == 'default' and _f == 'errors', - URL(_a,'default','errors',args=_t))) + URL(_a, 'default', 'errors', args=_t))) response.menu.append((T('Versioning'), _c == 'mercurial' and _f == 'commit', - URL(_a,'mercurial','commit',args=_t))) + URL(_a, 'mercurial', 'commit', args=_t))) if not session.authorized: response.menu = [(T('Login'), True, URL('site'))] else: response.menu.append((T('Logout'), False, - URL(_a,'default',f='logout'))) - response.menu.append((T('Debug'), False, - URL(_a, 'debug','interact'))) + URL(_a, 'default', f='logout'))) + response.menu.append((T('Debug'), False, + URL(_a, 'debug', 'interact'))) if os.path.exists('applications/examples'): - response.menu.append((T('Help'), False, URL('examples','default','index'))) + response.menu.append( + (T('Help'), False, URL('examples', 'default', 'index'))) else: response.menu.append((T('Help'), False, 'http://web2py.com/examples')) - - diff --git a/applications/admin/models/plugin_multiselect.py b/applications/admin/models/plugin_multiselect.py index d6e0320a..39fa2a1e 100644 --- a/applications/admin/models/plugin_multiselect.py +++ b/applications/admin/models/plugin_multiselect.py @@ -1,5 +1,4 @@ -response.files.append(URL('static','plugin_multiselect/jquery.multi-select.js')) -response.files.append(URL('static','plugin_multiselect/multi-select.css')) -response.files.append(URL('static','plugin_multiselect/start.js')) - - +response.files.append( + URL('static', 'plugin_multiselect/jquery.multi-select.js')) +response.files.append(URL('static', 'plugin_multiselect/multi-select.css')) +response.files.append(URL('static', 'plugin_multiselect/start.js')) diff --git a/applications/admin/modules/__init__.py b/applications/admin/modules/__init__.py index 139597f9..8b137891 100644 --- a/applications/admin/modules/__init__.py +++ b/applications/admin/modules/__init__.py @@ -1,2 +1 @@ - diff --git a/applications/admin/views/default/edit.html b/applications/admin/views/default/edit.html index 6555ab50..c8b1a93f 100644 --- a/applications/admin/views/default/edit.html +++ b/applications/admin/views/default/edit.html @@ -134,7 +134,7 @@ jQuery(document).ready(function(){ {{elif TEXT_EDITOR == 'codemirror':}} - + {{elif TEXT_EDITOR == 'ace':}}
{{=data}}
diff --git a/applications/admin/views/plugin_jqmobile/about.html b/applications/admin/views/plugin_jqmobile/about.html index ae3e1e24..c02f87d3 100644 --- a/applications/admin/views/plugin_jqmobile/about.html +++ b/applications/admin/views/plugin_jqmobile/about.html @@ -9,6 +9,13 @@ body { background: url('{{=URL('static','plugin_jqmobile/images/iphone.jpg')}}') no-repeat white; } + #back { + z-index: 1000; + padding: 10px; + position: absolute; + top: 0; + right: 0; + } iframe { position: absolute; margin-left: 320px; @@ -35,6 +42,7 @@ +

web2py plugin

for jQuery Mobile

diff --git a/applications/examples/__init__.py b/applications/examples/__init__.py index e69de29b..8b137891 100644 --- a/applications/examples/__init__.py +++ b/applications/examples/__init__.py @@ -0,0 +1 @@ + diff --git a/applications/examples/controllers/ajax_examples.py b/applications/examples/controllers/ajax_examples.py index 56652e5b..455e2f84 100644 --- a/applications/examples/controllers/ajax_examples.py +++ b/applications/examples/controllers/ajax_examples.py @@ -18,6 +18,3 @@ def flash(): def fade(): return dict() - - - diff --git a/applications/examples/controllers/appadmin.py b/applications/examples/controllers/appadmin.py index c25fed29..f62af94c 100644 --- a/applications/examples/controllers/appadmin.py +++ b/applications/examples/controllers/appadmin.py @@ -23,7 +23,7 @@ remote_addr = request.env.remote_addr try: hosts = (http_host, socket.gethostname(), socket.gethostbyname(http_host), - '::1','127.0.0.1','::ffff:127.0.0.1') + '::1', '127.0.0.1', '::ffff:127.0.0.1') except: hosts = (http_host, ) @@ -32,10 +32,10 @@ if request.env.http_x_forwarded_for or request.is_https: elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1"): raise HTTP(200, T('appadmin is disabled because insecure channel')) -if (request.application=='admin' and not session.authorized) or \ - (request.application!='admin' and not gluon.fileutils.check_credentials(request)): +if (request.application == 'admin' and not session.authorized) or \ + (request.application != 'admin' and not gluon.fileutils.check_credentials(request)): redirect(URL('admin', 'default', 'index', - vars=dict(send=URL(args=request.args,vars=request.vars)))) + vars=dict(send=URL(args=request.args, vars=request.vars)))) ignore_rw = True response.view = 'appadmin.html' @@ -95,24 +95,23 @@ def get_query(request): return None -def query_by_table_type(tablename,db,request=request): - keyed = hasattr(db[tablename],'_primarykey') +def query_by_table_type(tablename, db, request=request): + keyed = hasattr(db[tablename], '_primarykey') if keyed: firstkey = db[tablename][db[tablename]._primarykey[0]] cond = '>0' if firstkey.type in ['string', 'text']: cond = '!=""' - qry = '%s.%s.%s%s' % (request.args[0], request.args[1], firstkey.name, cond) + qry = '%s.%s.%s%s' % ( + request.args[0], request.args[1], firstkey.name, cond) else: qry = '%s.%s.id>0' % tuple(request.args[:2]) return qry - # ########################################################## # ## list all databases and tables # ########################################################### - def index(): return dict(databases=databases) @@ -127,7 +126,7 @@ def insert(): form = SQLFORM(db[table], ignore_rw=ignore_rw) if form.accepts(request.vars, session): response.flash = T('new record inserted') - return dict(form=form,table=db[table]) + return dict(form=form, table=db[table]) # ########################################################## @@ -138,7 +137,8 @@ def insert(): def download(): import os db = get_database(request) - return response.download(request,db) + return response.download(request, db) + def csv(): import gluon.contenttype @@ -149,26 +149,27 @@ def csv(): if not query: return None response.headers['Content-disposition'] = 'attachment; filename=%s_%s.csv'\ - % tuple(request.vars.query.split('.')[:2]) - return str(db(query,ignore_common_filters=True).select()) + % tuple(request.vars.query.split('.')[:2]) + return str(db(query, ignore_common_filters=True).select()) def import_csv(table, file): table.import_from_csv_file(file) + def select(): import re db = get_database(request) dbname = request.args[0] regex = re.compile('(?P
\w+)\.(?P\w+)=(?P\d+)') - if len(request.args)>1 and hasattr(db[request.args[1]],'_primarykey'): + if len(request.args) > 1 and hasattr(db[request.args[1]], '_primarykey'): regex = re.compile('(?P
\w+)\.(?P\w+)=(?P.+)') if request.vars.query: match = regex.match(request.vars.query) if match: request.vars.query = '%s.%s.%s==%s' % (request.args[0], - match.group('table'), match.group('field'), - match.group('value')) + match.group('table'), match.group('field'), + match.group('value')) else: request.vars.query = session.last_query query = get_query(request) @@ -192,46 +193,50 @@ def select(): session.last_query = request.vars.query form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px', _name='query', _value=request.vars.query or '', - requires=IS_NOT_EMPTY(error_message=T("Cannot be empty")))), TR(T('Update:'), + requires=IS_NOT_EMPTY( + error_message=T("Cannot be empty")))), TR(T('Update:'), INPUT(_name='update_check', _type='checkbox', value=False), INPUT(_style='width:400px', _name='update_fields', _value=request.vars.update_fields - or '')), TR(T('Delete:'), INPUT(_name='delete_check', + or '')), TR(T('Delete:'), INPUT(_name='delete_check', _class='delete', _type='checkbox', value=False), ''), TR('', '', INPUT(_type='submit', _value=T('submit')))), - _action=URL(r=request,args=request.args)) + _action=URL(r=request, args=request.args)) + + tb = None if form.accepts(request.vars, formname=None): regex = re.compile(request.args[0] + '\.(?P
\w+)\..+') match = regex.match(form.vars.query.strip()) if match: table = match.group('table') try: - tb = None nrows = db(query).count() if form.vars.update_check and form.vars.update_fields: db(query).update(**eval_in_global_env('dict(%s)' - % form.vars.update_fields)) + % form.vars.update_fields)) response.flash = T('%s %%{row} updated', nrows) elif form.vars.delete_check: db(query).delete() response.flash = T('%s %%{row} deleted', nrows) nrows = db(query).count() if orderby: - rows = db(query,ignore_common_filters=True).select(limitby=(start, stop), orderby=eval_in_global_env(orderby)) + rows = db(query, ignore_common_filters=True).select(limitby=( + start, stop), orderby=eval_in_global_env(orderby)) else: - rows = db(query,ignore_common_filters=True).select(limitby=(start, stop)) + rows = db(query, ignore_common_filters=True).select( + limitby=(start, stop)) except Exception, e: import traceback tb = traceback.format_exc() (rows, nrows) = ([], 0) - response.flash = DIV(T('Invalid Query'),PRE(str(e))) + response.flash = DIV(T('Invalid Query'), PRE(str(e))) # begin handle upload csv csv_table = table or request.vars.table if csv_table: - formcsv = FORM(str(T('or import from csv file'))+" ", - INPUT(_type='file',_name='csvfile'), - INPUT(_type='hidden',_value=csv_table,_name='table'), - INPUT(_type='submit',_value=T('import'))) + formcsv = FORM(str(T('or import from csv file')) + " ", + INPUT(_type='file', _name='csvfile'), + INPUT(_type='hidden', _value=csv_table, _name='table'), + INPUT(_type='submit', _value=T('import'))) else: formcsv = None if formcsv and formcsv.process().accepted: @@ -240,7 +245,7 @@ def select(): request.vars.csvfile.file) response.flash = T('data uploaded') except Exception, e: - response.flash = DIV(T('unable to parse csv file'),PRE(str(e))) + response.flash = DIV(T('unable to parse csv file'), PRE(str(e))) # end handle upload csv return dict( @@ -251,9 +256,9 @@ def select(): nrows=nrows, rows=rows, query=request.vars.query, - formcsv = formcsv, - tb = tb, - ) + formcsv=formcsv, + tb=tb, + ) # ########################################################## @@ -263,14 +268,16 @@ def select(): def update(): (db, table) = get_table(request) - keyed = hasattr(db[table],'_primarykey') + keyed = hasattr(db[table], '_primarykey') record = None if keyed: key = [f for f in request.vars if f in db[table]._primarykey] if key: - record = db(db[table][key[0]] == request.vars[key[0]], ignore_common_filters=True).select().first() + record = db(db[table][key[0]] == request.vars[key[ + 0]], ignore_common_filters=True).select().first() else: - record = db(db[table].id == request.args(2),ignore_common_filters=True).select().first() + record = db(db[table].id == request.args( + 2), ignore_common_filters=True).select().first() if not record: qry = query_by_table_type(table, db) @@ -280,20 +287,21 @@ def update(): if keyed: for k in db[table]._primarykey: - db[table][k].writable=False + db[table][k].writable = False - form = SQLFORM(db[table], record, deletable=True, delete_label=T('Check to delete'), - ignore_rw=ignore_rw and not keyed, - linkto=URL('select', + form = SQLFORM( + db[table], record, deletable=True, delete_label=T('Check to delete'), + ignore_rw=ignore_rw and not keyed, + linkto=URL('select', args=request.args[:1]), upload=URL(r=request, - f='download', args=request.args[:1])) + f='download', args=request.args[:1])) if form.accepts(request.vars, session): session.flash = T('done!') qry = query_by_table_type(table, db) redirect(URL('select', args=request.args[:1], vars=dict(query=qry))) - return dict(form=form,table=db[table]) + return dict(form=form, table=db[table]) # ########################################################## @@ -304,11 +312,15 @@ def update(): def state(): return dict() + def ccache(): form = FORM( - P(TAG.BUTTON(T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")), - P(TAG.BUTTON(T("Clear RAM"), _type="submit", _name="ram", _value="ram")), - P(TAG.BUTTON(T("Clear DISK"), _type="submit", _name="disk", _value="disk")), + P(TAG.BUTTON( + T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")), + P(TAG.BUTTON( + T("Clear RAM"), _type="submit", _name="ram", _value="ram")), + P(TAG.BUTTON( + T("Clear DISK"), _type="submit", _name="disk", _value="disk")), ) if form.accepts(request.vars, session): @@ -332,11 +344,16 @@ def ccache(): redirect(URL(r=request)) try: - from guppy import hpy; hp=hpy() + from guppy import hpy + hp = hpy() except ImportError: hp = False - import shelve, os, copy, time, math + import shelve + import os + import copy + import time + import math from gluon import portalocker ram = { @@ -381,9 +398,10 @@ def ccache(): ram['keys'].append((key, GetInHMS(time.time() - value[0]))) locker = open(os.path.join(request.folder, - 'cache/cache.lock'), 'a') + 'cache/cache.lock'), 'a') portalocker.lock(locker, portalocker.LOCK_EX) - disk_storage = shelve.open(os.path.join(request.folder, 'cache/cache.shelve')) + disk_storage = shelve.open( + os.path.join(request.folder, 'cache/cache.shelve')) try: for key, value in disk_storage.items(): if isinstance(value, dict): @@ -414,7 +432,8 @@ def ccache(): total['misses'] = ram['misses'] + disk['misses'] total['keys'] = ram['keys'] + disk['keys'] try: - total['ratio'] = total['hits'] * 100 / (total['hits'] + total['misses']) + total['ratio'] = total['hits'] * 100 / (total['hits'] + + total['misses']) except (KeyError, ZeroDivisionError): total['ratio'] = 0 @@ -440,6 +459,3 @@ def ccache(): return dict(form=form, total=total, ram=ram, disk=disk, object_stats=hp != False) - - - diff --git a/applications/examples/controllers/cache_examples.py b/applications/examples/controllers/cache_examples.py index 930f23c0..857833d5 100644 --- a/applications/examples/controllers/cache_examples.py +++ b/applications/examples/controllers/cache_examples.py @@ -1,25 +1,24 @@ - import time def cache_in_ram(): """cache the output of the lambda function in ram""" - t = cache.ram('time', lambda : time.ctime(), time_expire=5) + t = cache.ram('time', lambda: time.ctime(), time_expire=5) return dict(time=t, link=A('click to reload', _href=URL(r=request))) def cache_on_disk(): """cache the output of the lambda function on disk""" - t = cache.disk('time', lambda : time.ctime(), time_expire=5) + t = cache.disk('time', lambda: time.ctime(), time_expire=5) return dict(time=t, link=A('click to reload', _href=URL(r=request))) def cache_in_ram_and_disk(): """cache the output of the lambda function on disk and in ram""" - t = cache.ram('time', lambda : cache.disk('time', lambda : \ + t = cache.ram('time', lambda: cache.disk('time', lambda: time.ctime(), time_expire=5), time_expire=5) return dict(time=t, link=A('click to reload', _href=URL(r=request))) @@ -47,5 +46,3 @@ def cache_controller_and_view(): t = time.ctime() d = dict(time=t, link=A('click to reload', _href=URL(r=request))) return response.render(d) - - diff --git a/applications/examples/controllers/default.py b/applications/examples/controllers/default.py index 80f00979..7063a6fd 100644 --- a/applications/examples/controllers/default.py +++ b/applications/examples/controllers/default.py @@ -9,68 +9,83 @@ response.description = T('web2py Web Framework') session.forget() cache_expire = not request.is_local and 300 or 0 + @cache('index', time_expire=cache_expire) def index(): return response.render() + @cache('what', time_expire=cache_expire) def what(): - import urllib; + import urllib try: - images = XML(urllib.urlopen('http://web2py.com/poweredby/default/images').read()) + images = XML(urllib.urlopen( + 'http://web2py.com/poweredby/default/images').read()) except: images = [] return response.render(images=images) + @cache('download', time_expire=cache_expire) def download(): return response.render() + @cache('who', time_expire=cache_expire) def who(): return response.render() + @cache('support', time_expire=cache_expire) def support(): return response.render() + @cache('documentation', time_expire=cache_expire) def documentation(): return response.render() + @cache('usergroups', time_expire=cache_expire) def usergroups(): return response.render() + def contact(): - redirect(URL('default','usergroups')) + redirect(URL('default', 'usergroups')) + @cache('videos', time_expire=cache_expire) def videos(): return response.render() + def security(): redirect('http://www.web2py.com/book/default/chapter/01#security') + def api(): redirect('http://web2py.com/book/default/chapter/04#API') + @cache('license', time_expire=cache_expire) def license(): import os filename = os.path.join(request.env.gluon_parent, 'LICENSE') return response.render(dict(license=MARKMIN(read_file(filename)))) + def version(): return 'Version %s.%s.%s (%s) %s' % request.env.web2py_version + @cache('examples', time_expire=cache_expire) def examples(): return response.render() + @cache('changelog', time_expire=cache_expire) def changelog(): import os filename = os.path.join(request.env.gluon_parent, 'CHANGELOG') return response.render(dict(changelog=MARKMIN(read_file(filename)))) - diff --git a/applications/examples/controllers/form_examples.py b/applications/examples/controllers/form_examples.py index ffcc46e1..944aa08f 100644 --- a/applications/examples/controllers/form_examples.py +++ b/applications/examples/controllers/form_examples.py @@ -1,6 +1,3 @@ - - - def form(): """ a simple entry form with various types of objects """ @@ -15,7 +12,7 @@ def form(): TR('Profile', TEXTAREA(_name='profile', value='write something here')), TR('', INPUT(_type='submit', _value='SUBMIT')), - )) + )) if form.process().accepted: response.flash = 'form accepted' elif form.errors: @@ -23,6 +20,3 @@ def form(): else: response.flash = 'please fill the form' return dict(form=form, vars=form.vars) - - - diff --git a/applications/examples/controllers/global.py b/applications/examples/controllers/global.py index 1c5f2a78..a186037c 100644 --- a/applications/examples/controllers/global.py +++ b/applications/examples/controllers/global.py @@ -1,4 +1,3 @@ - session.forget() response.menu = [['home', False, '/%s/default/index' @@ -17,14 +16,14 @@ def vars(): c, d, value, - ) = ( + ) = ( 'Global variables', globals(), None, None, (), None, - ) + ) (title, args) = ('globals()', '') elif len(request.args) < 3: args = '.'.join(request.args) @@ -76,7 +75,4 @@ def vars(): d=d, doc=doc, attributes=attributes, - ) - - - + ) diff --git a/applications/examples/controllers/layout_examples.py b/applications/examples/controllers/layout_examples.py index f20c19ec..b05ac9d7 100644 --- a/applications/examples/controllers/layout_examples.py +++ b/applications/examples/controllers/layout_examples.py @@ -1,6 +1,6 @@ def civilized(): response.menu = [['civilized', True, URL('civilized' - )], ['slick', False, URL('slick')], + )], ['slick', False, URL('slick')], ['basic', False, URL('basic')]] response.flash = 'you clicked on civilized' return dict(message='you clicked on civilized') @@ -8,7 +8,7 @@ def civilized(): def slick(): response.menu = [['civilized', False, URL('civilized' - )], ['slick', True, URL('slick')], + )], ['slick', True, URL('slick')], ['basic', False, URL('basic')]] response.flash = 'you clicked on slick' return dict(message='you clicked on slick') @@ -16,10 +16,7 @@ def slick(): def basic(): response.menu = [['civilized', False, URL('civilized' - )], ['slick', False, URL('slick')], + )], ['slick', False, URL('slick')], ['basic', True, URL('basic')]] response.flash = 'you clicked on basic' return dict(message='you clicked on basic') - - - diff --git a/applications/examples/controllers/session_examples.py b/applications/examples/controllers/session_examples.py index 806c395b..1160b211 100644 --- a/applications/examples/controllers/session_examples.py +++ b/applications/examples/controllers/session_examples.py @@ -1,6 +1,3 @@ - - - def counter(): """ every time you reload, it increases the session.counter """ @@ -8,6 +5,3 @@ def counter(): session.counter = 0 session.counter += 1 return dict(counter=session.counter) - - - diff --git a/applications/examples/controllers/simple_examples.py b/applications/examples/controllers/simple_examples.py index 94dfd729..35840bbb 100644 --- a/applications/examples/controllers/simple_examples.py +++ b/applications/examples/controllers/simple_examples.py @@ -102,9 +102,8 @@ def rss_aggregator(): return rss2.dumps(rss) - def ajaxwiki(): - default=""" + default = """ # section ## subsection @@ -129,12 +128,12 @@ Quoted text 3 | 0 | 0 --------- """ - form = FORM(TEXTAREA(_id='text',_name='text',value=default), + form = FORM(TEXTAREA(_id='text', _name='text', value=default), INPUT(_type='button', _value='markmin', _onclick="ajax('ajaxwiki_onclick',['text'],'html')")) return dict(form=form, html=DIV(_id='html')) + def ajaxwiki_onclick(): return MARKMIN(request.vars.text).xml() - diff --git a/applications/examples/controllers/spreadsheet.py b/applications/examples/controllers/spreadsheet.py index 95c906bf..f46df702 100644 --- a/applications/examples/controllers/spreadsheet.py +++ b/applications/examples/controllers/spreadsheet.py @@ -1,10 +1,11 @@ from gluon.contrib.spreadsheet import Sheet + def callback(): - return cache.ram('sheet1',lambda:None,None).process(request) + return cache.ram('sheet1', lambda: None, None).process(request) + def index(): - sheet = cache.ram('sheet1',lambda:Sheet(10,10,URL('callback')),0) + sheet = cache.ram('sheet1', lambda: Sheet(10, 10, URL('callback')), 0) #sheet.cell('r0c3',value='=r0c0+r0c1+r0c2',readonly=True) return dict(sheet=sheet) - diff --git a/applications/examples/controllers/template_examples.py b/applications/examples/controllers/template_examples.py index 3a6c913f..addb8d07 100644 --- a/applications/examples/controllers/template_examples.py +++ b/applications/examples/controllers/template_examples.py @@ -1,6 +1,3 @@ - - - def variables(): return dict(a=10, b=20) @@ -31,6 +28,3 @@ def xml(): def beautify(): return dict(message=BEAUTIFY(request)) - - - diff --git a/applications/examples/models/feeds_reader.py b/applications/examples/models/feeds_reader.py index 5921dfa8..865fa048 100644 --- a/applications/examples/models/feeds_reader.py +++ b/applications/examples/models/feeds_reader.py @@ -1,49 +1,44 @@ - -def group_feed_reader(group,mode='div',counter='5'): +def group_feed_reader(group, mode='div', counter='5'): """parse group feeds""" url = "http://groups.google.com/group/%s/feed/rss_v2_0_topics.xml?num=%s" %\ - (group,counter) + (group, counter) from gluon.contrib import feedparser g = feedparser.parse(url) if mode == 'div': - html = XML(TAG.BLOCKQUOTE(UL(*[LI(A(entry['title']+' - ' +\ - entry['author'][entry['author'].rfind('('):],\ - _href=entry['link'],_target='_blank'))\ - for entry in g['entries'] ]),\ - _class="boxInfo",\ - _style="padding-bottom:5px;")) + html = XML(TAG.BLOCKQUOTE(UL(*[LI(A(entry['title'] + ' - ' + + entry['author'][ + entry['author'].rfind('('):], + _href=entry['link'], _target='_blank')) + for entry in g['entries']]), + _class="boxInfo", + _style="padding-bottom:5px;")) else: - html = XML(UL(*[LI(A(entry['title']+' - ' +\ - entry['author'][entry['author'].rfind('('):],\ - _href=entry['link'],_target='_blank'))\ - for entry in g['entries'] ])) + html = XML(UL(*[LI(A(entry['title'] + ' - ' + + entry['author'][entry['author'].rfind('('):], + _href=entry['link'], _target='_blank')) + for entry in g['entries']])) return html -def code_feed_reader(project,mode='div'): +def code_feed_reader(project, mode='div'): """parse code feeds""" url = "http://code.google.com/feeds/p/%s/hgchanges/basic" % project from gluon.contrib import feedparser g = feedparser.parse(url) if mode == 'div': - html = XML(DIV(UL(*[LI(A(entry['title'],_href=entry['link'],\ - _target='_blank'))\ - for entry in g['entries'][0:5]]),\ - _class="boxInfo",\ + html = XML(DIV(UL(*[LI(A(entry['title'], _href=entry['link'], + _target='_blank')) + for entry in g['entries'][0:5]]), + _class="boxInfo", _style="padding-bottom:5px;")) else: - html = XML(UL(*[LI(A(entry['title'],_href=entry['link'],\ - _target='_blank'))\ - for entry in g['entries'][0:5]])) - + html = XML(UL(*[LI(A(entry['title'], _href=entry['link'], + _target='_blank')) + for entry in g['entries'][0:5]])) return html - - - - diff --git a/applications/examples/models/markmin.py b/applications/examples/models/markmin.py index da761a6a..3376bac6 100644 --- a/applications/examples/models/markmin.py +++ b/applications/examples/models/markmin.py @@ -2,18 +2,19 @@ import gluon.template markmin_dict = dict( code_python=lambda code: str(CODE(code)), - template=lambda \ - code:gluon.template.render(code,context=globals()), - sup=lambda \ - code:'%s'%code, - br=lambda n:'
'*int(n), - groupdates=lambda group:group_feed_reader(group), - ) + template=lambda + code: gluon.template.render(code, context=globals()), + sup=lambda + code: '%s' % code, + br=lambda n: '
' * int(n), + groupdates=lambda group: group_feed_reader(group), +) -def get_content(b=None,\ - c=request.controller,\ - f=request.function,\ - l='en',\ + +def get_content(b=None, + c=request.controller, + f=request.function, + l='en', format='markmin'): """Gets and renders the file in /private/content////. @@ -21,20 +22,20 @@ def get_content(b=None,\ def openfile(): import os - path = os.path.join(request.folder,'private','content',l,c,f,b+'.'+format) + path = os.path.join( + request.folder, 'private', 'content', l, c, f, b + '.' + format) return open(path) try: openedfile = openfile() except Exception, IOError: - l='en' + l = 'en' openedfile = openfile() if format == 'markmin': - html = MARKMIN(str(T(openedfile.read())),markmin_dict) + html = MARKMIN(str(T(openedfile.read())), markmin_dict) else: html = str(T(openedfile.read())) openedfile.close() return html - diff --git a/applications/examples/models/menu.py b/applications/examples/models/menu.py index 9c7eeefb..d81bfa36 100644 --- a/applications/examples/models/menu.py +++ b/applications/examples/models/menu.py @@ -1,28 +1,29 @@ # -*- coding: utf-8 -*- response.menu = [ - (T('Home'),False,URL('default','index')), - (T('About'),False,URL('default','what')), - (T('Download'),False,URL('default','download')), - (T('Docs & Resources'),False,URL('default','documentation')), - (T('Support'),False,URL('default','support')), - (T('Contributors'),False,URL('default','who'))] + (T('Home'), False, URL('default', 'index')), + (T('About'), False, URL('default', 'what')), + (T('Download'), False, URL('default', 'download')), + (T('Docs & Resources'), False, URL('default', 'documentation')), + (T('Support'), False, URL('default', 'support')), + (T('Contributors'), False, URL('default', 'who'))] ######################################################################### ## Changes the menu active item ######################################################################### -def toggle_menuclass(cssclass='pressed',menuid='headermenu'): + + +def toggle_menuclass(cssclass='pressed', menuid='headermenu'): """This function changes the menu class to put pressed appearance""" positions = dict( - index='', - what='-108px -115px', - download='-211px -115px', - who='-315px -115px', - support='-418px -115px', - documentation='-520px -115px' - ) - + index='', + what='-108px -115px', + download='-211px -115px', + who='-315px -115px', + support='-418px -115px', + documentation='-520px -115px' + ) if request.function in positions.keys(): jscript = """ @@ -34,12 +35,11 @@ def toggle_menuclass(cssclass='pressed',menuid='headermenu'): }); """ % dict(cssclass=cssclass, - menuid=menuid, - function=request.function, - cssposition=positions[request.function] - ) + menuid=menuid, + function=request.function, + cssposition=positions[request.function] + ) return XML(jscript) else: return '' - diff --git a/applications/examples/views/default/who.html b/applications/examples/views/default/who.html index 64eef3bf..23fc8e2b 100644 --- a/applications/examples/views/default/who.html +++ b/applications/examples/views/default/who.html @@ -29,6 +29,7 @@
  • Alvaro Justen (dynamical translations)
  • Anders Roos (file locking)
  • Andrew Willimott (documentation, TeraData support) +
  • Andriy Kornatskyy (benchmarks and profiling)
  • Angelo Compagnucci (mobile devices)
  • Anthony Bastardi (book, poweredby site, multiple contributions)
  • Arun K. Rajeevan (plugin_wiki) diff --git a/applications/welcome/__init__.py b/applications/welcome/__init__.py index e69de29b..8b137891 100644 --- a/applications/welcome/__init__.py +++ b/applications/welcome/__init__.py @@ -0,0 +1 @@ + diff --git a/applications/welcome/controllers/appadmin.py b/applications/welcome/controllers/appadmin.py index c25fed29..f62af94c 100644 --- a/applications/welcome/controllers/appadmin.py +++ b/applications/welcome/controllers/appadmin.py @@ -23,7 +23,7 @@ remote_addr = request.env.remote_addr try: hosts = (http_host, socket.gethostname(), socket.gethostbyname(http_host), - '::1','127.0.0.1','::ffff:127.0.0.1') + '::1', '127.0.0.1', '::ffff:127.0.0.1') except: hosts = (http_host, ) @@ -32,10 +32,10 @@ if request.env.http_x_forwarded_for or request.is_https: elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1"): raise HTTP(200, T('appadmin is disabled because insecure channel')) -if (request.application=='admin' and not session.authorized) or \ - (request.application!='admin' and not gluon.fileutils.check_credentials(request)): +if (request.application == 'admin' and not session.authorized) or \ + (request.application != 'admin' and not gluon.fileutils.check_credentials(request)): redirect(URL('admin', 'default', 'index', - vars=dict(send=URL(args=request.args,vars=request.vars)))) + vars=dict(send=URL(args=request.args, vars=request.vars)))) ignore_rw = True response.view = 'appadmin.html' @@ -95,24 +95,23 @@ def get_query(request): return None -def query_by_table_type(tablename,db,request=request): - keyed = hasattr(db[tablename],'_primarykey') +def query_by_table_type(tablename, db, request=request): + keyed = hasattr(db[tablename], '_primarykey') if keyed: firstkey = db[tablename][db[tablename]._primarykey[0]] cond = '>0' if firstkey.type in ['string', 'text']: cond = '!=""' - qry = '%s.%s.%s%s' % (request.args[0], request.args[1], firstkey.name, cond) + qry = '%s.%s.%s%s' % ( + request.args[0], request.args[1], firstkey.name, cond) else: qry = '%s.%s.id>0' % tuple(request.args[:2]) return qry - # ########################################################## # ## list all databases and tables # ########################################################### - def index(): return dict(databases=databases) @@ -127,7 +126,7 @@ def insert(): form = SQLFORM(db[table], ignore_rw=ignore_rw) if form.accepts(request.vars, session): response.flash = T('new record inserted') - return dict(form=form,table=db[table]) + return dict(form=form, table=db[table]) # ########################################################## @@ -138,7 +137,8 @@ def insert(): def download(): import os db = get_database(request) - return response.download(request,db) + return response.download(request, db) + def csv(): import gluon.contenttype @@ -149,26 +149,27 @@ def csv(): if not query: return None response.headers['Content-disposition'] = 'attachment; filename=%s_%s.csv'\ - % tuple(request.vars.query.split('.')[:2]) - return str(db(query,ignore_common_filters=True).select()) + % tuple(request.vars.query.split('.')[:2]) + return str(db(query, ignore_common_filters=True).select()) def import_csv(table, file): table.import_from_csv_file(file) + def select(): import re db = get_database(request) dbname = request.args[0] regex = re.compile('(?P
  • \w+)\.(?P\w+)=(?P\d+)') - if len(request.args)>1 and hasattr(db[request.args[1]],'_primarykey'): + if len(request.args) > 1 and hasattr(db[request.args[1]], '_primarykey'): regex = re.compile('(?P
    \w+)\.(?P\w+)=(?P.+)') if request.vars.query: match = regex.match(request.vars.query) if match: request.vars.query = '%s.%s.%s==%s' % (request.args[0], - match.group('table'), match.group('field'), - match.group('value')) + match.group('table'), match.group('field'), + match.group('value')) else: request.vars.query = session.last_query query = get_query(request) @@ -192,46 +193,50 @@ def select(): session.last_query = request.vars.query form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px', _name='query', _value=request.vars.query or '', - requires=IS_NOT_EMPTY(error_message=T("Cannot be empty")))), TR(T('Update:'), + requires=IS_NOT_EMPTY( + error_message=T("Cannot be empty")))), TR(T('Update:'), INPUT(_name='update_check', _type='checkbox', value=False), INPUT(_style='width:400px', _name='update_fields', _value=request.vars.update_fields - or '')), TR(T('Delete:'), INPUT(_name='delete_check', + or '')), TR(T('Delete:'), INPUT(_name='delete_check', _class='delete', _type='checkbox', value=False), ''), TR('', '', INPUT(_type='submit', _value=T('submit')))), - _action=URL(r=request,args=request.args)) + _action=URL(r=request, args=request.args)) + + tb = None if form.accepts(request.vars, formname=None): regex = re.compile(request.args[0] + '\.(?P
    \w+)\..+') match = regex.match(form.vars.query.strip()) if match: table = match.group('table') try: - tb = None nrows = db(query).count() if form.vars.update_check and form.vars.update_fields: db(query).update(**eval_in_global_env('dict(%s)' - % form.vars.update_fields)) + % form.vars.update_fields)) response.flash = T('%s %%{row} updated', nrows) elif form.vars.delete_check: db(query).delete() response.flash = T('%s %%{row} deleted', nrows) nrows = db(query).count() if orderby: - rows = db(query,ignore_common_filters=True).select(limitby=(start, stop), orderby=eval_in_global_env(orderby)) + rows = db(query, ignore_common_filters=True).select(limitby=( + start, stop), orderby=eval_in_global_env(orderby)) else: - rows = db(query,ignore_common_filters=True).select(limitby=(start, stop)) + rows = db(query, ignore_common_filters=True).select( + limitby=(start, stop)) except Exception, e: import traceback tb = traceback.format_exc() (rows, nrows) = ([], 0) - response.flash = DIV(T('Invalid Query'),PRE(str(e))) + response.flash = DIV(T('Invalid Query'), PRE(str(e))) # begin handle upload csv csv_table = table or request.vars.table if csv_table: - formcsv = FORM(str(T('or import from csv file'))+" ", - INPUT(_type='file',_name='csvfile'), - INPUT(_type='hidden',_value=csv_table,_name='table'), - INPUT(_type='submit',_value=T('import'))) + formcsv = FORM(str(T('or import from csv file')) + " ", + INPUT(_type='file', _name='csvfile'), + INPUT(_type='hidden', _value=csv_table, _name='table'), + INPUT(_type='submit', _value=T('import'))) else: formcsv = None if formcsv and formcsv.process().accepted: @@ -240,7 +245,7 @@ def select(): request.vars.csvfile.file) response.flash = T('data uploaded') except Exception, e: - response.flash = DIV(T('unable to parse csv file'),PRE(str(e))) + response.flash = DIV(T('unable to parse csv file'), PRE(str(e))) # end handle upload csv return dict( @@ -251,9 +256,9 @@ def select(): nrows=nrows, rows=rows, query=request.vars.query, - formcsv = formcsv, - tb = tb, - ) + formcsv=formcsv, + tb=tb, + ) # ########################################################## @@ -263,14 +268,16 @@ def select(): def update(): (db, table) = get_table(request) - keyed = hasattr(db[table],'_primarykey') + keyed = hasattr(db[table], '_primarykey') record = None if keyed: key = [f for f in request.vars if f in db[table]._primarykey] if key: - record = db(db[table][key[0]] == request.vars[key[0]], ignore_common_filters=True).select().first() + record = db(db[table][key[0]] == request.vars[key[ + 0]], ignore_common_filters=True).select().first() else: - record = db(db[table].id == request.args(2),ignore_common_filters=True).select().first() + record = db(db[table].id == request.args( + 2), ignore_common_filters=True).select().first() if not record: qry = query_by_table_type(table, db) @@ -280,20 +287,21 @@ def update(): if keyed: for k in db[table]._primarykey: - db[table][k].writable=False + db[table][k].writable = False - form = SQLFORM(db[table], record, deletable=True, delete_label=T('Check to delete'), - ignore_rw=ignore_rw and not keyed, - linkto=URL('select', + form = SQLFORM( + db[table], record, deletable=True, delete_label=T('Check to delete'), + ignore_rw=ignore_rw and not keyed, + linkto=URL('select', args=request.args[:1]), upload=URL(r=request, - f='download', args=request.args[:1])) + f='download', args=request.args[:1])) if form.accepts(request.vars, session): session.flash = T('done!') qry = query_by_table_type(table, db) redirect(URL('select', args=request.args[:1], vars=dict(query=qry))) - return dict(form=form,table=db[table]) + return dict(form=form, table=db[table]) # ########################################################## @@ -304,11 +312,15 @@ def update(): def state(): return dict() + def ccache(): form = FORM( - P(TAG.BUTTON(T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")), - P(TAG.BUTTON(T("Clear RAM"), _type="submit", _name="ram", _value="ram")), - P(TAG.BUTTON(T("Clear DISK"), _type="submit", _name="disk", _value="disk")), + P(TAG.BUTTON( + T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")), + P(TAG.BUTTON( + T("Clear RAM"), _type="submit", _name="ram", _value="ram")), + P(TAG.BUTTON( + T("Clear DISK"), _type="submit", _name="disk", _value="disk")), ) if form.accepts(request.vars, session): @@ -332,11 +344,16 @@ def ccache(): redirect(URL(r=request)) try: - from guppy import hpy; hp=hpy() + from guppy import hpy + hp = hpy() except ImportError: hp = False - import shelve, os, copy, time, math + import shelve + import os + import copy + import time + import math from gluon import portalocker ram = { @@ -381,9 +398,10 @@ def ccache(): ram['keys'].append((key, GetInHMS(time.time() - value[0]))) locker = open(os.path.join(request.folder, - 'cache/cache.lock'), 'a') + 'cache/cache.lock'), 'a') portalocker.lock(locker, portalocker.LOCK_EX) - disk_storage = shelve.open(os.path.join(request.folder, 'cache/cache.shelve')) + disk_storage = shelve.open( + os.path.join(request.folder, 'cache/cache.shelve')) try: for key, value in disk_storage.items(): if isinstance(value, dict): @@ -414,7 +432,8 @@ def ccache(): total['misses'] = ram['misses'] + disk['misses'] total['keys'] = ram['keys'] + disk['keys'] try: - total['ratio'] = total['hits'] * 100 / (total['hits'] + total['misses']) + total['ratio'] = total['hits'] * 100 / (total['hits'] + + total['misses']) except (KeyError, ZeroDivisionError): total['ratio'] = 0 @@ -440,6 +459,3 @@ def ccache(): return dict(form=form, total=total, ram=ram, disk=disk, object_stats=hp != False) - - - diff --git a/applications/welcome/controllers/default.py b/applications/welcome/controllers/default.py index 4c4b563d..9ac34c21 100644 --- a/applications/welcome/controllers/default.py +++ b/applications/welcome/controllers/default.py @@ -9,6 +9,7 @@ ## - call exposes all registered services (none by default) ######################################################################### + def index(): """ example action using the internationalization operator T and flash @@ -20,6 +21,7 @@ def index(): response.flash = T("Welcome to web2py!") return dict(message=T('Hello World')) + def user(): """ exposes: @@ -42,7 +44,7 @@ def download(): allows downloading of uploaded files http://..../[app]/default/download/[filename] """ - return response.download(request,db) + return response.download(request, db) def call(): diff --git a/applications/welcome/languages/uk.py b/applications/welcome/languages/uk.py index 453745c9..9597e58e 100644 --- a/applications/welcome/languages/uk.py +++ b/applications/welcome/languages/uk.py @@ -89,6 +89,7 @@ 'export as csv file': 'експортувати як файл csv', 'FAQ': 'ЧаПи (FAQ)', 'First name': "Ім'я", +'Forgot username?': "Забули ім'я користувача?", 'Forms and Validators': 'Форми та коректність даних', 'Free Applications': 'Вільні додатки', 'Group %(group_id)s created': 'Групу %(group_id)s створено', diff --git a/applications/welcome/models/db.py b/applications/welcome/models/db.py index 54a0b231..2cd6638e 100644 --- a/applications/welcome/models/db.py +++ b/applications/welcome/models/db.py @@ -11,12 +11,12 @@ if not request.env.web2py_runtime_gae: ## if NOT running on Google App Engine use SQLite or other DB - db = DAL('sqlite://storage.sqlite') + db = DAL('sqlite://storage.sqlite') else: ## connect to Google BigTable (optional 'google:datastore://namespace') db = DAL('google:datastore') ## store sessions and tickets there - session.connect(request, response, db = db) + session.connect(request, response, db=db) ## or store session in Memcache, Redis, etc. ## from gluon.contrib.memdb import MEMDB ## from google.appengine.api.memcache import Client @@ -47,7 +47,7 @@ crud, service, plugins = Crud(db), Service(), PluginManager() auth.define_tables(username=False, signature=False) ## configure email -mail=auth.settings.mailer +mail = auth.settings.mailer mail.settings.server = 'logging' or 'smtp.gmail.com:587' mail.settings.sender = 'you@gmail.com' mail.settings.login = 'username:password' @@ -60,7 +60,7 @@ auth.settings.reset_password_requires_verification = True ## if you need to use OpenID, Facebook, MySpace, Twitter, Linkedin, etc. ## register with janrain.com, write your domain:api_key in private/janrain.key from gluon.contrib.login_methods.rpx_account import use_janrain -use_janrain(auth,filename='private/janrain.key') +use_janrain(auth, filename='private/janrain.key') ######################################################################### ## Define your tables below (or better in another model file) for example diff --git a/applications/welcome/models/menu.py b/applications/welcome/models/menu.py index dcf1c275..ae2cdbf2 100644 --- a/applications/welcome/models/menu.py +++ b/applications/welcome/models/menu.py @@ -5,7 +5,8 @@ ## Customize your APP title, subtitle and menus here ######################################################################### -response.title = ' '.join(word.capitalize() for word in request.application.split('_')) +response.title = ' '.join( + word.capitalize() for word in request.application.split('_')) response.subtitle = T('customize me!') ## read more at http://dev.w3.org/html5/markup/meta.name.html @@ -22,80 +23,114 @@ response.google_analytics_id = None ######################################################################### response.menu = [ - (T('Home'), False, URL('default','index'), []) - ] + (T('Home'), False, URL('default', 'index'), []) +] ######################################################################### ## provide shortcuts for development. remove in production ######################################################################### + def _(): # shortcuts app = request.application ctr = request.controller - # useful links to internal and external resources - response.menu+=[ - (SPAN('web2py',_class='highlighted'),False, 'http://web2py.com', [ - (T('My Sites'),False,URL('admin','default','site')), - (T('This App'),False,URL('admin','default','design/%s' % app), [ - (T('Controller'),False, - URL('admin','default','edit/%s/controllers/%s.py' % (app,ctr))), - (T('View'),False, - URL('admin','default','edit/%s/views/%s' % (app,response.view))), - (T('Layout'),False, - URL('admin','default','edit/%s/views/layout.html' % app)), - (T('Stylesheet'),False, - URL('admin','default','edit/%s/static/css/web2py.css' % app)), - (T('DB Model'),False, - URL('admin','default','edit/%s/models/db.py' % app)), - (T('Menu Model'),False, - URL('admin','default','edit/%s/models/menu.py' % app)), - (T('Database'),False, URL(app,'appadmin','index')), - (T('Errors'),False, URL('admin','default','errors/' + app)), - (T('About'),False, URL('admin','default','about/' + app)), + # useful links to internal and external resources + response.menu += [ + (SPAN('web2py', _class='highlighted'), False, 'http://web2py.com', [ + (T('My Sites'), False, URL('admin', 'default', 'site')), + (T('This App'), False, URL('admin', 'default', 'design/%s' % app), [ + (T('Controller'), False, + URL( + 'admin', 'default', 'edit/%s/controllers/%s.py' % (app, ctr))), + (T('View'), False, + URL( + 'admin', 'default', 'edit/%s/views/%s' % (app, response.view))), + (T('Layout'), False, + URL( + 'admin', 'default', 'edit/%s/views/layout.html' % app)), + (T('Stylesheet'), False, + URL( + 'admin', 'default', 'edit/%s/static/css/web2py.css' % app)), + (T('DB Model'), False, + URL( + 'admin', 'default', 'edit/%s/models/db.py' % app)), + (T('Menu Model'), False, + URL( + 'admin', 'default', 'edit/%s/models/menu.py' % app)), + (T('Database'), False, URL(app, 'appadmin', 'index')), + (T('Errors'), False, URL( + 'admin', 'default', 'errors/' + app)), + (T('About'), False, URL( + 'admin', 'default', 'about/' + app)), + ]), + ('web2py.com', False, 'http://www.web2py.com', [ + (T('Download'), False, + 'http://www.web2py.com/examples/default/download'), + (T('Support'), False, + 'http://www.web2py.com/examples/default/support'), + (T('Demo'), False, 'http://web2py.com/demo_admin'), + (T('Quick Examples'), False, + 'http://web2py.com/examples/default/examples'), + (T('FAQ'), False, 'http://web2py.com/AlterEgo'), + (T('Videos'), False, + 'http://www.web2py.com/examples/default/videos/'), + (T('Free Applications'), + False, 'http://web2py.com/appliances'), + (T('Plugins'), False, 'http://web2py.com/plugins'), + (T('Layouts'), False, 'http://web2py.com/layouts'), + (T('Recipes'), False, 'http://web2pyslices.com/'), + (T('Semantic'), False, 'http://web2py.com/semantic'), + ]), + (T('Documentation'), False, 'http://www.web2py.com/book', [ + (T('Preface'), False, + 'http://www.web2py.com/book/default/chapter/00'), + (T('Introduction'), False, + 'http://www.web2py.com/book/default/chapter/01'), + (T('Python'), False, + 'http://www.web2py.com/book/default/chapter/02'), + (T('Overview'), False, + 'http://www.web2py.com/book/default/chapter/03'), + (T('The Core'), False, + 'http://www.web2py.com/book/default/chapter/04'), + (T('The Views'), False, + 'http://www.web2py.com/book/default/chapter/05'), + (T('Database'), False, + 'http://www.web2py.com/book/default/chapter/06'), + (T('Forms and Validators'), False, + 'http://www.web2py.com/book/default/chapter/07'), + (T('Email and SMS'), False, + 'http://www.web2py.com/book/default/chapter/08'), + (T('Access Control'), False, + 'http://www.web2py.com/book/default/chapter/09'), + (T('Services'), False, + 'http://www.web2py.com/book/default/chapter/10'), + (T('Ajax Recipes'), False, + 'http://www.web2py.com/book/default/chapter/11'), + (T('Components and Plugins'), False, + 'http://www.web2py.com/book/default/chapter/12'), + (T('Deployment Recipes'), False, + 'http://www.web2py.com/book/default/chapter/13'), + (T('Other Recipes'), False, + 'http://www.web2py.com/book/default/chapter/14'), + (T('Buy this book'), False, + 'http://stores.lulu.com/web2py'), + ]), + (T('Community'), False, None, [ + (T('Groups'), False, + 'http://www.web2py.com/examples/default/usergroups'), + (T('Twitter'), False, 'http://twitter.com/web2py'), + (T('Live Chat'), False, + 'http://webchat.freenode.net/?channels=web2py'), ]), - ('web2py.com',False,'http://www.web2py.com', [ - (T('Download'),False,'http://www.web2py.com/examples/default/download'), - (T('Support'),False,'http://www.web2py.com/examples/default/support'), - (T('Demo'),False,'http://web2py.com/demo_admin'), - (T('Quick Examples'),False,'http://web2py.com/examples/default/examples'), - (T('FAQ'),False,'http://web2py.com/AlterEgo'), - (T('Videos'),False,'http://www.web2py.com/examples/default/videos/'), - (T('Free Applications'),False,'http://web2py.com/appliances'), - (T('Plugins'),False,'http://web2py.com/plugins'), - (T('Layouts'),False,'http://web2py.com/layouts'), - (T('Recipes'),False,'http://web2pyslices.com/'), - (T('Semantic'),False,'http://web2py.com/semantic'), - ]), - (T('Documentation'),False,'http://www.web2py.com/book', [ - (T('Preface'),False,'http://www.web2py.com/book/default/chapter/00'), - (T('Introduction'),False,'http://www.web2py.com/book/default/chapter/01'), - (T('Python'),False,'http://www.web2py.com/book/default/chapter/02'), - (T('Overview'),False,'http://www.web2py.com/book/default/chapter/03'), - (T('The Core'),False,'http://www.web2py.com/book/default/chapter/04'), - (T('The Views'),False,'http://www.web2py.com/book/default/chapter/05'), - (T('Database'),False,'http://www.web2py.com/book/default/chapter/06'), - (T('Forms and Validators'),False,'http://www.web2py.com/book/default/chapter/07'), - (T('Email and SMS'),False,'http://www.web2py.com/book/default/chapter/08'), - (T('Access Control'),False,'http://www.web2py.com/book/default/chapter/09'), - (T('Services'),False,'http://www.web2py.com/book/default/chapter/10'), - (T('Ajax Recipes'),False,'http://www.web2py.com/book/default/chapter/11'), - (T('Components and Plugins'),False,'http://www.web2py.com/book/default/chapter/12'), - (T('Deployment Recipes'),False,'http://www.web2py.com/book/default/chapter/13'), - (T('Other Recipes'),False,'http://www.web2py.com/book/default/chapter/14'), - (T('Buy this book'),False,'http://stores.lulu.com/web2py'), - ]), - (T('Community'),False, None, [ - (T('Groups'),False,'http://www.web2py.com/examples/default/usergroups'), - (T('Twitter'),False,'http://twitter.com/web2py'), - (T('Live Chat'),False,'http://webchat.freenode.net/?channels=web2py'), - ]), - (T('Plugins'),False,None, [ - ('plugin_wiki',False,'http://web2py.com/examples/default/download'), - (T('Other Plugins'),False,'http://web2py.com/plugins'), - (T('Layout Plugins'),False,'http://web2py.com/layouts'), + (T('Plugins'), False, None, [ + ('plugin_wiki', False, + 'http://web2py.com/examples/default/download'), + (T('Other Plugins'), False, + 'http://web2py.com/plugins'), + (T('Layout Plugins'), + False, 'http://web2py.com/layouts'), ]) ] )] _() - diff --git a/applications/welcome/routes.example.py b/applications/welcome/routes.example.py index 363080a8..5194cb8e 100644 --- a/applications/welcome/routes.example.py +++ b/applications/welcome/routes.example.py @@ -37,4 +37,3 @@ routers = { #NOTE! To change language in your application using these rules add this line #in one of your models files: # if request.uri_language: T.force(request.uri_language) - diff --git a/cgihandler.py b/cgihandler.py index c4c14993..0929ed15 100755 --- a/cgihandler.py +++ b/cgihandler.py @@ -56,13 +56,8 @@ 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 wsgiref.handlers.CGIHandler().run(gluon.main.wsgibase) - - - - - diff --git a/fcgihandler.py b/fcgihandler.py index f45067a6..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 @@ -51,8 +51,3 @@ if SOFTCRON: global_settings.web2py_crontype = 'soft' fcgi.WSGIServer(application, bindAddress='/tmp/fcgi.sock').run() - - - - - diff --git a/gaehandler.py b/gaehandler.py index 70e52806..30c49ecc 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'] @@ -78,12 +78,18 @@ def wsgiapp(env, res): """Return the wsgiapp""" env['PATH_INFO'] = env['PATH_INFO'].decode('latin1').encode('utf8') + #when using the blobstore image uploader GAE dev SDK passes these as unicode + # they should be regular strings as they are parts of URLs + env['wsgi.url_scheme'] = str(env['wsgi.url_scheme']) + env['QUERY_STRING'] = str(env['QUERY_STRING']) + env['SERVER_NAME'] = str(env['SERVER_NAME']) + #this deals with a problem where GAE development server seems to forget # the path between requests 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,14 +97,10 @@ def wsgiapp(env, res): if LOG_STATS or DEBUG: wsgiapp = log_stats(wsgiapp) + def main(): """Run the wsgi app""" run_wsgi_app(wsgiapp) if __name__ == '__main__': main() - - - - - diff --git a/gluon/__init__.py b/gluon/__init__.py index fd88cd43..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 * @@ -42,14 +42,3 @@ if 0: mail = Mail() service = Service() plugins = PluginManager() - - - - - - - - - - - diff --git a/gluon/admin.py b/gluon/admin.py index f6e70f01..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,42 +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 80fcabae..6c52c418 100644 --- a/gluon/cache.py +++ b/gluon/cache.py @@ -40,19 +40,20 @@ __all__ = ['Cache', 'lazy_cache'] DEFAULT_TIME_EXPIRE = 300 + class CacheAbstract(object): """ Abstract class for cache implementations. Main function is now to provide referenced api documentation. Use CacheInRam or CacheOnDisk instead which are derived from this class. - + Attentions, Michele says: There are signatures inside gdbm files that are used directly by the python gdbm adapter that often are lagging behind in the detection code in python part. - On every occasion that a gdbm store is probed by the python adapter, + On every occasion that a gdbm store is probed by the python adapter, the probe fails, because gdbm file version is newer. Using gdbm directly from C would work, because there is backward compatibility, but not from python! @@ -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: @@ -258,7 +263,7 @@ class CacheOnDisk(CacheAbstract): on self.locker first. Replaces the close method of the returned shelf instance with one that releases the lock upon closing.""" - + storage = None locker = None locked = False @@ -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 @@ -296,10 +303,12 @@ class CacheOnDisk(CacheAbstract): self.initialized = False self.request = request 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 @@ -468,12 +479,12 @@ class Cache(object): """ allow replacing cache.ram with cache.with_prefix(cache.ram,'prefix') it will add prefix to all the cache keys used. - """ + """ return lambda key, f, time_expire=DEFAULT_TIME_EXPIRE, prefix=prefix:\ 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,14 +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/cfs.py b/gluon/cfs.py index 358540ca..6e610d49 100644 --- a/gluon/cfs.py +++ b/gluon/cfs.py @@ -51,10 +51,3 @@ def getcfs(key, filename, filter=None): cfs[key] = (t, data) cfs_lock.release() return data - - - - - - - diff --git a/gluon/compileapp.py b/gluon/compileapp.py index 3bc81108..5f145888 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 @@ -371,7 +380,8 @@ _base_environment_['SQLField'] = SQLField # for backward compatibility _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:]) @@ -442,7 +453,10 @@ def compile_views(folder): path = pjoin(folder, 'views') for file in listdir(path, '^[\w/\-]+(\.\w+)+$'): - data = parse_template(file, path) + try: + data = parse_template(file, path) + except Exception, e: + raise Exception("%s in %s" % (e, file)) filename = ('views/%s.py' % file).replace('/', '_').replace('\\', '_') filename = pjoin(folder, 'compiled', filename) write_file(filename, data) @@ -458,7 +472,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 +487,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 +514,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 +552,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 +562,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 +593,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 +622,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 +642,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 +664,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 +679,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 @@ -700,10 +717,3 @@ def test(): if __name__ == '__main__': import doctest doctest.testmod() - - - - - - - diff --git a/gluon/contenttype.py b/gluon/contenttype.py index cb11c687..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,18 +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 8b24ef0a..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,16 +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 d71c3911..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,16 +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 12a6f48e..e69de29b 100644 --- a/gluon/contrib/__init__.py +++ b/gluon/contrib/__init__.py @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/gluon/contrib/aes.py b/gluon/contrib/aes.py index cfc212b5..24e40878 100644 --- a/gluon/contrib/aes.py +++ b/gluon/contrib/aes.py @@ -500,5 +500,3 @@ aes_Rcon = array('B', 'c697356ad4b37dfaefc5913972e4d3bd' '61c29f254a943366cc831d3a74e8cb'.decode('hex') ) - - diff --git a/gluon/contrib/autolinks.py b/gluon/contrib/autolinks.py index be72db03..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,10 +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/feedparser.py b/gluon/contrib/feedparser.py index 1afb2c11..f8075c90 100755 --- a/gluon/contrib/feedparser.py +++ b/gluon/contrib/feedparser.py @@ -1,17 +1,19 @@ -#!/usr/bin/env python """Universal feed parser Handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds -Visit http://feedparser.org/ for the latest version -Visit http://feedparser.org/docs/ for the latest documentation +Visit https://code.google.com/p/feedparser/ for the latest version +Visit http://packages.python.org/feedparser/ for the latest documentation Required: Python 2.4 or later -Recommended: CJKCodecs and iconv_codec +Recommended: iconv_codec """ -__version__ = "5.0.1" -__license__ = """Copyright (c) 2002-2008, Mark Pilgrim, All rights reserved. +__version__ = "5.1.2" +__license__ = """ +Copyright (c) 2010-2012 Kurt McKee +Copyright (c) 2002-2008 Mark Pilgrim +All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -47,7 +49,7 @@ __contributors__ = ["Jason Diamond ", # HTTP "User-Agent" header to send to servers when downloading feeds. # If you are embedding feedparser in a larger application, you should # change this to your application name and URL. -USER_AGENT = "UniversalFeedParser/%s +http://feedparser.org/" % __version__ +USER_AGENT = "UniversalFeedParser/%s +https://code.google.com/p/feedparser/" % __version__ # HTTP "Accept" header to send to servers when downloading feeds. If you don't # want to send an Accept header, set this to None. @@ -75,6 +77,10 @@ RESOLVE_RELATIVE_URIS = 1 # HTML content, set this to 1. SANITIZE_HTML = 1 +# If you want feedparser to automatically parse microformat content embedded +# in entry contents, set this to 1 +PARSE_MICROFORMATS = 1 + # ---------- Python 3 modules (make it work if possible) ---------- try: import rfc822 @@ -98,25 +104,25 @@ else: # Python 3.1 deprecates decodestring in favor of decodebytes _base64decode = getattr(base64, 'decodebytes', base64.decodestring) -def _s2bytes(s): - # Convert a UTF-8 str to bytes if the interpreter is Python 3 - try: - return bytes(s, 'utf8') - except (NameError, TypeError): - # In Python 2.5 and below, bytes doesn't exist (NameError) - # In Python 2.6 and above, bytes and str are the same (TypeError) - return s - -def _l2bytes(l): - # Convert a list of ints to bytes if the interpreter is Python 3 - try: - if bytes is not str: - # In Python 2.6 and above, this call won't raise an exception - # but it will return bytes([65]) as '[65]' instead of 'A' - return bytes(l) - raise NameError - except NameError: - return ''.join(map(chr, l)) +# _s2bytes: convert a UTF-8 str to bytes if the interpreter is Python 3 +# _l2bytes: convert a list of ints to bytes if the interpreter is Python 3 +try: + if bytes is str: + # In Python 2.5 and below, bytes doesn't exist (NameError) + # In Python 2.6 and above, bytes and str are the same type + raise NameError +except NameError: + # Python 2 + def _s2bytes(s): + return s + def _l2bytes(l): + return ''.join(map(chr, l)) +else: + # Python 3 + def _s2bytes(s): + return bytes(s, 'utf8') + def _l2bytes(l): + return bytes(l) # If you want feedparser to allow all URL schemes, set this to () # List culled from Python's urlparse documentation at: @@ -125,9 +131,10 @@ def _l2bytes(l): # https://secure.wikimedia.org/wikipedia/en/wiki/URI_scheme # Many more will likely need to be added! ACCEPTABLE_URI_SCHEMES = ( - 'file', 'ftp', 'gopher', 'h323', 'hdl', 'http', 'https', 'imap', 'mailto', - 'mms', 'news', 'nntp', 'prospero', 'rsync', 'rtsp', 'rtspu', 'sftp', - 'shttp', 'sip', 'sips', 'snews', 'svn', 'svn+ssh', 'telnet', 'wais', + 'file', 'ftp', 'gopher', 'h323', 'hdl', 'http', 'https', 'imap', 'magnet', + 'mailto', 'mms', 'news', 'nntp', 'prospero', 'rsync', 'rtsp', 'rtspu', + 'sftp', 'shttp', 'sip', 'sips', 'snews', 'svn', 'svn+ssh', 'telnet', + 'wais', # Additional common-but-unofficial schemes 'aim', 'callto', 'cvs', 'facetime', 'feed', 'git', 'gtalk', 'irc', 'ircs', 'irc6', 'itms', 'mms', 'msnim', 'skype', 'ssh', 'smb', 'svn', 'ymsg', @@ -136,16 +143,17 @@ ACCEPTABLE_URI_SCHEMES = ( # ---------- required modules (should come with any Python distribution) ---------- import cgi +import codecs import copy import datetime import re import struct -import sys import time import types import urllib import urllib2 import urlparse +import warnings from htmlentitydefs import name2codepoint, codepoint2name, entitydefs @@ -170,7 +178,7 @@ except ImportError: zlib = None # If a real XML parser is available, feedparser will attempt to use it. feedparser has -# been tested with the built-in SAX parser, PyXML, and libxml2. On platforms where the +# been tested with the built-in SAX parser and libxml2. On platforms where the # Python distribution does not come with an XML parser (such as Mac OS X 10.2 and some # versions of FreeBSD), feedparser will quietly fall back on regex-based parsing. try: @@ -220,9 +228,12 @@ else: # feedparser's scope instead of sgmllib's scope. charref = re.compile('&#(\d+|[xX][0-9a-fA-F]+);') tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*') + attrfind = re.compile( + r'\s*([a-zA-Z_][-:.a-zA-Z_0-9]*)[$]?(\s*=\s*' + r'(\'[^\']*\'|"[^"]*"|[][\-a-zA-Z0-9./,:;+*%?!&$\(\)_#=~\'"@]*))?' + ) # Unfortunately, these must be copied over to prevent NameError exceptions - attrfind = sgmllib.attrfind entityref = sgmllib.entityref incomplete = sgmllib.incomplete interesting = sgmllib.interesting @@ -250,12 +261,8 @@ else: endbracket = _EndBracketRegEx() -# cjkcodecs and iconv_codec provide support for more character encodings. -# Both are available from http://cjkpython.i18n.org/ -try: - import cjkcodecs.aliases -except ImportError: - pass +# iconv_codec provides support for more character encodings. +# It's available from http://cjkpython.i18n.org/ try: import iconv_codec except ImportError: @@ -268,15 +275,14 @@ try: except ImportError: chardet = None -# BeautifulSoup parser used for parsing microformats from embedded HTML content +# BeautifulSoup is used to extract microformat content from HTML +# feedparser is tested using BeautifulSoup 3.2.0 # http://www.crummy.com/software/BeautifulSoup/ -# feedparser is tested with BeautifulSoup 3.0.x, but it might work with the -# older 2.x series. If it doesn't, and you can figure out why, I'll accept a -# patch and modify the compatibility statement accordingly. try: import BeautifulSoup except ImportError: BeautifulSoup = None + PARSE_MICROFORMATS = False # ---------- don't touch these ---------- class ThingsNobodyCaresAboutButMe(Exception): pass @@ -310,6 +316,7 @@ class FeedParserDict(dict): 'date': 'updated', 'date_parsed': 'updated_parsed', 'description': ['summary', 'subtitle'], + 'description_detail': ['summary_detail', 'subtitle_detail'], 'url': ['href'], 'modified': 'updated', 'modified_parsed': 'updated_parsed', @@ -330,10 +337,32 @@ class FeedParserDict(dict): return [norel(link) for link in dict.__getitem__(self, 'links') if link['rel']==u'enclosure'] elif key == 'license': for link in dict.__getitem__(self, 'links'): - if link['rel']==u'license' and link.has_key('href'): + if link['rel']==u'license' and 'href' in link: return link['href'] - elif key == 'categories': - return [(tag['scheme'], tag['term']) for tag in dict.__getitem__(self, 'tags')] + elif key == 'updated': + # Temporarily help developers out by keeping the old + # broken behavior that was reported in issue 310. + # This fix was proposed in issue 328. + if not dict.__contains__(self, 'updated') and \ + dict.__contains__(self, 'published'): + warnings.warn("To avoid breaking existing software while " + "fixing issue 310, a temporary mapping has been created " + "from `updated` to `published` if `updated` doesn't " + "exist. This fallback will be removed in a future version " + "of feedparser.", DeprecationWarning) + return dict.__getitem__(self, 'published') + return dict.__getitem__(self, 'updated') + elif key == 'updated_parsed': + if not dict.__contains__(self, 'updated_parsed') and \ + dict.__contains__(self, 'published_parsed'): + warnings.warn("To avoid breaking existing software while " + "fixing issue 310, a temporary mapping has been created " + "from `updated_parsed` to `published_parsed` if " + "`updated_parsed` doesn't exist. This fallback will be " + "removed in a future version of feedparser.", + DeprecationWarning) + return dict.__getitem__(self, 'published_parsed') + return dict.__getitem__(self, 'updated_parsed') else: realkey = self.keymap.get(key, key) if isinstance(realkey, list): @@ -345,6 +374,11 @@ class FeedParserDict(dict): return dict.__getitem__(self, key) def __contains__(self, key): + if key in ('updated', 'updated_parsed'): + # Temporarily help developers out by keeping the old + # broken behavior that was reported in issue 310. + # This fix was proposed in issue 328. + return dict.__contains__(self, key) try: self.__getitem__(key) except KeyError: @@ -380,66 +414,45 @@ class FeedParserDict(dict): except KeyError: raise AttributeError, "object has no attribute '%s'" % key - -_ebcdic_to_ascii_map = None -def _ebcdic_to_ascii(s): - global _ebcdic_to_ascii_map - if not _ebcdic_to_ascii_map: - emap = ( - 0,1,2,3,156,9,134,127,151,141,142,11,12,13,14,15, - 16,17,18,19,157,133,8,135,24,25,146,143,28,29,30,31, - 128,129,130,131,132,10,23,27,136,137,138,139,140,5,6,7, - 144,145,22,147,148,149,150,4,152,153,154,155,20,21,158,26, - 32,160,161,162,163,164,165,166,167,168,91,46,60,40,43,33, - 38,169,170,171,172,173,174,175,176,177,93,36,42,41,59,94, - 45,47,178,179,180,181,182,183,184,185,124,44,37,95,62,63, - 186,187,188,189,190,191,192,193,194,96,58,35,64,39,61,34, - 195,97,98,99,100,101,102,103,104,105,196,197,198,199,200,201, - 202,106,107,108,109,110,111,112,113,114,203,204,205,206,207,208, - 209,126,115,116,117,118,119,120,121,122,210,211,212,213,214,215, - 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231, - 123,65,66,67,68,69,70,71,72,73,232,233,234,235,236,237, - 125,74,75,76,77,78,79,80,81,82,238,239,240,241,242,243, - 92,159,83,84,85,86,87,88,89,90,244,245,246,247,248,249, - 48,49,50,51,52,53,54,55,56,57,250,251,252,253,254,255 - ) - _ebcdic_to_ascii_map = _maketrans( \ - _l2bytes(range(256)), _l2bytes(emap)) - return s.translate(_ebcdic_to_ascii_map) + def __hash__(self): + return id(self) _cp1252 = { - unichr(128): unichr(8364), # euro sign - unichr(130): unichr(8218), # single low-9 quotation mark - unichr(131): unichr( 402), # latin small letter f with hook - unichr(132): unichr(8222), # double low-9 quotation mark - unichr(133): unichr(8230), # horizontal ellipsis - unichr(134): unichr(8224), # dagger - unichr(135): unichr(8225), # double dagger - unichr(136): unichr( 710), # modifier letter circumflex accent - unichr(137): unichr(8240), # per mille sign - unichr(138): unichr( 352), # latin capital letter s with caron - unichr(139): unichr(8249), # single left-pointing angle quotation mark - unichr(140): unichr( 338), # latin capital ligature oe - unichr(142): unichr( 381), # latin capital letter z with caron - unichr(145): unichr(8216), # left single quotation mark - unichr(146): unichr(8217), # right single quotation mark - unichr(147): unichr(8220), # left double quotation mark - unichr(148): unichr(8221), # right double quotation mark - unichr(149): unichr(8226), # bullet - unichr(150): unichr(8211), # en dash - unichr(151): unichr(8212), # em dash - unichr(152): unichr( 732), # small tilde - unichr(153): unichr(8482), # trade mark sign - unichr(154): unichr( 353), # latin small letter s with caron - unichr(155): unichr(8250), # single right-pointing angle quotation mark - unichr(156): unichr( 339), # latin small ligature oe - unichr(158): unichr( 382), # latin small letter z with caron - unichr(159): unichr( 376)} # latin capital letter y with diaeresis + 128: unichr(8364), # euro sign + 130: unichr(8218), # single low-9 quotation mark + 131: unichr( 402), # latin small letter f with hook + 132: unichr(8222), # double low-9 quotation mark + 133: unichr(8230), # horizontal ellipsis + 134: unichr(8224), # dagger + 135: unichr(8225), # double dagger + 136: unichr( 710), # modifier letter circumflex accent + 137: unichr(8240), # per mille sign + 138: unichr( 352), # latin capital letter s with caron + 139: unichr(8249), # single left-pointing angle quotation mark + 140: unichr( 338), # latin capital ligature oe + 142: unichr( 381), # latin capital letter z with caron + 145: unichr(8216), # left single quotation mark + 146: unichr(8217), # right single quotation mark + 147: unichr(8220), # left double quotation mark + 148: unichr(8221), # right double quotation mark + 149: unichr(8226), # bullet + 150: unichr(8211), # en dash + 151: unichr(8212), # em dash + 152: unichr( 732), # small tilde + 153: unichr(8482), # trade mark sign + 154: unichr( 353), # latin small letter s with caron + 155: unichr(8250), # single right-pointing angle quotation mark + 156: unichr( 339), # latin small ligature oe + 158: unichr( 382), # latin small letter z with caron + 159: unichr( 376), # latin capital letter y with diaeresis +} _urifixer = re.compile('^([A-Za-z][A-Za-z0-9+-.]*://)(/*)(.*?)') def _urljoin(base, uri): uri = _urifixer.sub(r'\1\3', uri) #try: + if not isinstance(uri, unicode): + uri = uri.decode('utf-8', 'ignore') uri = urlparse.urljoin(base, uri) if not isinstance(uri, unicode): return uri.decode('utf-8', 'ignore') @@ -449,75 +462,76 @@ def _urljoin(base, uri): # return urlparse.urljoin(base, uri) class _FeedParserMixin: - namespaces = {'': '', - 'http://backend.userland.com/rss': '', - 'http://blogs.law.harvard.edu/tech/rss': '', - 'http://purl.org/rss/1.0/': '', - 'http://my.netscape.com/rdf/simple/0.9/': '', - 'http://example.com/newformat#': '', - 'http://example.com/necho': '', - 'http://purl.org/echo/': '', - 'uri/of/echo/namespace#': '', - 'http://purl.org/pie/': '', - 'http://purl.org/atom/ns#': '', - 'http://www.w3.org/2005/Atom': '', - 'http://purl.org/rss/1.0/modules/rss091#': '', + namespaces = { + '': '', + 'http://backend.userland.com/rss': '', + 'http://blogs.law.harvard.edu/tech/rss': '', + 'http://purl.org/rss/1.0/': '', + 'http://my.netscape.com/rdf/simple/0.9/': '', + 'http://example.com/newformat#': '', + 'http://example.com/necho': '', + 'http://purl.org/echo/': '', + 'uri/of/echo/namespace#': '', + 'http://purl.org/pie/': '', + 'http://purl.org/atom/ns#': '', + 'http://www.w3.org/2005/Atom': '', + 'http://purl.org/rss/1.0/modules/rss091#': '', - 'http://webns.net/mvcb/': 'admin', - 'http://purl.org/rss/1.0/modules/aggregation/': 'ag', - 'http://purl.org/rss/1.0/modules/annotate/': 'annotate', - 'http://media.tangent.org/rss/1.0/': 'audio', - 'http://backend.userland.com/blogChannelModule': 'blogChannel', - 'http://web.resource.org/cc/': 'cc', - 'http://backend.userland.com/creativeCommonsRssModule': 'creativeCommons', - 'http://purl.org/rss/1.0/modules/company': 'co', - 'http://purl.org/rss/1.0/modules/content/': 'content', - 'http://my.theinfo.org/changed/1.0/rss/': 'cp', - 'http://purl.org/dc/elements/1.1/': 'dc', - 'http://purl.org/dc/terms/': 'dcterms', - 'http://purl.org/rss/1.0/modules/email/': 'email', - 'http://purl.org/rss/1.0/modules/event/': 'ev', - 'http://rssnamespace.org/feedburner/ext/1.0': 'feedburner', - 'http://freshmeat.net/rss/fm/': 'fm', - 'http://xmlns.com/foaf/0.1/': 'foaf', - 'http://www.w3.org/2003/01/geo/wgs84_pos#': 'geo', - 'http://postneo.com/icbm/': 'icbm', - 'http://purl.org/rss/1.0/modules/image/': 'image', - 'http://www.itunes.com/DTDs/PodCast-1.0.dtd': 'itunes', - 'http://example.com/DTDs/PodCast-1.0.dtd': 'itunes', - 'http://purl.org/rss/1.0/modules/link/': 'l', - 'http://search.yahoo.com/mrss': 'media', - #Version 1.1.2 of the Media RSS spec added the trailing slash on the namespace - 'http://search.yahoo.com/mrss/': 'media', - 'http://madskills.com/public/xml/rss/module/pingback/': 'pingback', - 'http://prismstandard.org/namespaces/1.2/basic/': 'prism', - 'http://www.w3.org/1999/02/22-rdf-syntax-ns#': 'rdf', - 'http://www.w3.org/2000/01/rdf-schema#': 'rdfs', - 'http://purl.org/rss/1.0/modules/reference/': 'ref', - 'http://purl.org/rss/1.0/modules/richequiv/': 'reqv', - 'http://purl.org/rss/1.0/modules/search/': 'search', - 'http://purl.org/rss/1.0/modules/slash/': 'slash', - 'http://schemas.xmlsoap.org/soap/envelope/': 'soap', - 'http://purl.org/rss/1.0/modules/servicestatus/': 'ss', - 'http://hacks.benhammersley.com/rss/streaming/': 'str', - 'http://purl.org/rss/1.0/modules/subscription/': 'sub', - 'http://purl.org/rss/1.0/modules/syndication/': 'sy', - 'http://schemas.pocketsoap.com/rss/myDescModule/': 'szf', - 'http://purl.org/rss/1.0/modules/taxonomy/': 'taxo', - 'http://purl.org/rss/1.0/modules/threading/': 'thr', - 'http://purl.org/rss/1.0/modules/textinput/': 'ti', - 'http://madskills.com/public/xml/rss/module/trackback/':'trackback', - 'http://wellformedweb.org/commentAPI/': 'wfw', - 'http://purl.org/rss/1.0/modules/wiki/': 'wiki', - 'http://www.w3.org/1999/xhtml': 'xhtml', - 'http://www.w3.org/1999/xlink': 'xlink', - 'http://www.w3.org/XML/1998/namespace': 'xml' -} + 'http://webns.net/mvcb/': 'admin', + 'http://purl.org/rss/1.0/modules/aggregation/': 'ag', + 'http://purl.org/rss/1.0/modules/annotate/': 'annotate', + 'http://media.tangent.org/rss/1.0/': 'audio', + 'http://backend.userland.com/blogChannelModule': 'blogChannel', + 'http://web.resource.org/cc/': 'cc', + 'http://backend.userland.com/creativeCommonsRssModule': 'creativeCommons', + 'http://purl.org/rss/1.0/modules/company': 'co', + 'http://purl.org/rss/1.0/modules/content/': 'content', + 'http://my.theinfo.org/changed/1.0/rss/': 'cp', + 'http://purl.org/dc/elements/1.1/': 'dc', + 'http://purl.org/dc/terms/': 'dcterms', + 'http://purl.org/rss/1.0/modules/email/': 'email', + 'http://purl.org/rss/1.0/modules/event/': 'ev', + 'http://rssnamespace.org/feedburner/ext/1.0': 'feedburner', + 'http://freshmeat.net/rss/fm/': 'fm', + 'http://xmlns.com/foaf/0.1/': 'foaf', + 'http://www.w3.org/2003/01/geo/wgs84_pos#': 'geo', + 'http://postneo.com/icbm/': 'icbm', + 'http://purl.org/rss/1.0/modules/image/': 'image', + 'http://www.itunes.com/DTDs/PodCast-1.0.dtd': 'itunes', + 'http://example.com/DTDs/PodCast-1.0.dtd': 'itunes', + 'http://purl.org/rss/1.0/modules/link/': 'l', + 'http://search.yahoo.com/mrss': 'media', + # Version 1.1.2 of the Media RSS spec added the trailing slash on the namespace + 'http://search.yahoo.com/mrss/': 'media', + 'http://madskills.com/public/xml/rss/module/pingback/': 'pingback', + 'http://prismstandard.org/namespaces/1.2/basic/': 'prism', + 'http://www.w3.org/1999/02/22-rdf-syntax-ns#': 'rdf', + 'http://www.w3.org/2000/01/rdf-schema#': 'rdfs', + 'http://purl.org/rss/1.0/modules/reference/': 'ref', + 'http://purl.org/rss/1.0/modules/richequiv/': 'reqv', + 'http://purl.org/rss/1.0/modules/search/': 'search', + 'http://purl.org/rss/1.0/modules/slash/': 'slash', + 'http://schemas.xmlsoap.org/soap/envelope/': 'soap', + 'http://purl.org/rss/1.0/modules/servicestatus/': 'ss', + 'http://hacks.benhammersley.com/rss/streaming/': 'str', + 'http://purl.org/rss/1.0/modules/subscription/': 'sub', + 'http://purl.org/rss/1.0/modules/syndication/': 'sy', + 'http://schemas.pocketsoap.com/rss/myDescModule/': 'szf', + 'http://purl.org/rss/1.0/modules/taxonomy/': 'taxo', + 'http://purl.org/rss/1.0/modules/threading/': 'thr', + 'http://purl.org/rss/1.0/modules/textinput/': 'ti', + 'http://madskills.com/public/xml/rss/module/trackback/': 'trackback', + 'http://wellformedweb.org/commentAPI/': 'wfw', + 'http://purl.org/rss/1.0/modules/wiki/': 'wiki', + 'http://www.w3.org/1999/xhtml': 'xhtml', + 'http://www.w3.org/1999/xlink': 'xlink', + 'http://www.w3.org/XML/1998/namespace': 'xml', + } _matchnamespaces = {} - can_be_relative_uri = ['link', 'id', 'wfw_comment', 'wfw_commentrss', 'docs', 'url', 'href', 'comments', 'icon', 'logo'] - can_contain_relative_uris = ['content', 'title', 'summary', 'info', 'tagline', 'subtitle', 'copyright', 'rights', 'description'] - can_contain_dangerous_markup = ['content', 'title', 'summary', 'info', 'tagline', 'subtitle', 'copyright', 'rights', 'description'] + can_be_relative_uri = set(['link', 'id', 'wfw_comment', 'wfw_commentrss', 'docs', 'url', 'href', 'comments', 'icon', 'logo']) + can_contain_relative_uris = set(['content', 'title', 'summary', 'info', 'tagline', 'subtitle', 'copyright', 'rights', 'description']) + can_contain_dangerous_markup = set(['content', 'title', 'summary', 'info', 'tagline', 'subtitle', 'copyright', 'rights', 'description']) html_types = [u'text/html', u'application/xhtml+xml'] def __init__(self, baseuri=None, baselang=None, encoding=u'utf-8'): @@ -551,10 +565,20 @@ class _FeedParserMixin: self.baseuri = baseuri or u'' self.lang = baselang or None self.svgOK = 0 - self.hasTitle = 0 + self.title_depth = -1 + self.depth = 0 if baselang: self.feeddata['language'] = baselang.replace('_','-') + # A map of the following form: + # { + # object_that_value_is_set_on: { + # property_name: depth_of_node_property_was_extracted_from, + # other_property: depth_of_node_property_was_extracted_from, + # }, + # } + self.property_depth_map = {} + def _normalize_attributes(self, kv): k = kv[0].lower() v = k in ('rel', 'type') and kv[1].lower() or kv[1] @@ -568,6 +592,9 @@ class _FeedParserMixin: return (k, v) def unknown_starttag(self, tag, attrs): + # increment depth counter + self.depth += 1 + # normalize attrs attrs = map(self._normalize_attributes, attrs) @@ -604,8 +631,8 @@ class _FeedParserMixin: self.trackNamespace(None, uri) # track inline content - if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', u'xml').endswith(u'xml'): - if tag in ['xhtml:div', 'div']: + if self.incontent and not self.contentparams.get('type', u'xml').endswith(u'xml'): + if tag in ('xhtml:div', 'div'): return # typepad does this 10/2007 # element declared itself as escaped markup, but it isn't really self.contentparams['type'] = u'application/xhtml+xml' @@ -675,9 +702,9 @@ class _FeedParserMixin: self.pop(prefix + suffix) # track inline content - if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', u'xml').endswith(u'xml'): + if self.incontent and not self.contentparams.get('type', u'xml').endswith(u'xml'): # element declared itself as escaped markup, but it isn't really - if tag in ['xhtml:div', 'div']: + if tag in ('xhtml:div', 'div'): return # typepad does this 10/2007 self.contentparams['type'] = u'application/xhtml+xml' if self.incontent and self.contentparams.get('type') == u'application/xhtml+xml': @@ -694,6 +721,8 @@ class _FeedParserMixin: if self.langstack: # and (self.langstack[-1] is not None): self.lang = self.langstack[-1] + self.depth -= 1 + def handle_charref(self, ref): # called for each character reference, e.g. for ' ', ref will be '160' if not self.elementstack: @@ -715,7 +744,7 @@ class _FeedParserMixin: return if ref in ('lt', 'gt', 'quot', 'amp', 'apos'): text = '&%s;' % ref - elif ref in self.entities.keys(): + elif ref in self.entities: text = self.entities[ref] if text.startswith('&#') and text.endswith(';'): return self.handle_entityref(text) @@ -778,17 +807,18 @@ class _FeedParserMixin: def trackNamespace(self, prefix, uri): loweruri = uri.lower() - if (prefix, loweruri) == (None, 'http://my.netscape.com/rdf/simple/0.9/') and not self.version: - self.version = u'rss090' - if loweruri == 'http://purl.org/rss/1.0/' and not self.version: - self.version = u'rss10' - if loweruri == 'http://www.w3.org/2005/atom' and not self.version: - self.version = u'atom10' + if not self.version: + if (prefix, loweruri) == (None, 'http://my.netscape.com/rdf/simple/0.9/'): + self.version = u'rss090' + elif loweruri == 'http://purl.org/rss/1.0/': + self.version = u'rss10' + elif loweruri == 'http://www.w3.org/2005/atom': + self.version = u'atom10' if loweruri.find(u'backend.userland.com/rss') <> -1: # match any backend.userland.com namespace uri = u'http://backend.userland.com/rss' loweruri = uri - if self._matchnamespaces.has_key(loweruri): + if loweruri in self._matchnamespaces: self.namespacemap[prefix] = self._matchnamespaces[loweruri] self.namespacesInUse[self._matchnamespaces[loweruri]] = uri else: @@ -892,7 +922,7 @@ class _FeedParserMixin: # parse microformats # (must do this before sanitizing because some microformats # rely on elements that we sanitize) - if is_htmlish and element in ['content', 'description', 'summary']: + if PARSE_MICROFORMATS and is_htmlish and element in ['content', 'description', 'summary']: mfresults = _parseMicroformats(output, self.baseuri, self.encoding) if mfresults: for tag in mfresults.get('tags', []): @@ -923,13 +953,13 @@ class _FeedParserMixin: # map win-1252 extensions to the proper code points if isinstance(output, unicode): - output = u''.join([c in _cp1252.keys() and _cp1252[c] or c for c in output]) + output = output.translate(_cp1252) # categories/tags/keywords/whatever are handled in _end_category if element == 'category': return output - if element == 'title' and self.hasTitle: + if element == 'title' and -1 < self.title_depth <= self.depth: return output # store output in appropriate place(s) @@ -951,7 +981,10 @@ class _FeedParserMixin: else: if element == 'description': element = 'summary' - self.entries[-1][element] = output + old_value_depth = self.property_depth_map.setdefault(self.entries[-1], {}).get(element) + if old_value_depth is None or self.depth <= old_value_depth: + self.property_depth_map[self.entries[-1]][element] = self.depth + self.entries[-1][element] = output if self.incontent: contentparams = copy.deepcopy(self.contentparams) contentparams['value'] = output @@ -995,7 +1028,7 @@ class _FeedParserMixin: # data loss, this function errs on the conservative side. @staticmethod def lookslikehtml(s): - # must have a close tag or a entity reference to qualify + # must have a close tag or an entity reference to qualify if not (re.search(r'',s) or re.search("&#?\w+;",s)): return @@ -1077,11 +1110,11 @@ class _FeedParserMixin: self._cdf_common(attrsD) def _cdf_common(self, attrsD): - if attrsD.has_key('lastmod'): + if 'lastmod' in attrsD: self._start_modified({}) self.elementstack[-1][-1] = attrsD['lastmod'] self._end_modified() - if attrsD.has_key('href'): + if 'href' in attrsD: self._start_link({}) self.elementstack[-1][-1] = attrsD['href'] self._end_link() @@ -1108,7 +1141,7 @@ class _FeedParserMixin: if not self.inentry: context.setdefault('image', FeedParserDict()) self.inimage = 1 - self.hasTitle = 0 + self.title_depth = -1 self.push('image', 0) def _end_image(self): @@ -1119,7 +1152,7 @@ class _FeedParserMixin: context = self._getContext() context.setdefault('textinput', FeedParserDict()) self.intextinput = 1 - self.hasTitle = 0 + self.title_depth = -1 self.push('textinput', 0) _start_textInput = _start_textinput @@ -1254,7 +1287,7 @@ class _FeedParserMixin: def _getContext(self): if self.insource: context = self.sourcedata - elif self.inimage and self.feeddata.has_key('image'): + elif self.inimage and 'image' in self.feeddata: context = self.feeddata['image'] elif self.intextinput: context = self.feeddata['textinput'] @@ -1339,7 +1372,7 @@ class _FeedParserMixin: self.push('item', 0) self.inentry = 1 self.guidislink = 0 - self.hasTitle = 0 + self.title_depth = -1 id = self._getAttribute(attrsD, 'rdf:about') if id: context = self._getContext() @@ -1373,18 +1406,19 @@ class _FeedParserMixin: self.push('published', 1) _start_dcterms_issued = _start_published _start_issued = _start_published + _start_pubdate = _start_published def _end_published(self): value = self.pop('published') self._save('published_parsed', _parse_date(value), overwrite=True) _end_dcterms_issued = _end_published _end_issued = _end_published + _end_pubdate = _end_published def _start_updated(self, attrsD): self.push('updated', 1) _start_modified = _start_updated _start_dcterms_modified = _start_updated - _start_pubdate = _start_updated _start_dc_date = _start_updated _start_lastbuilddate = _start_updated @@ -1394,7 +1428,6 @@ class _FeedParserMixin: self._save('updated_parsed', parsed_value, overwrite=True) _end_modified = _end_updated _end_dcterms_modified = _end_updated - _end_pubdate = _end_updated _end_dc_date = _end_updated _end_lastbuilddate = _end_updated @@ -1467,8 +1500,9 @@ class _FeedParserMixin: self._start_category(attrsD) def _end_itunes_keywords(self): - for term in self.pop('itunes_keywords').split(): - self._addTag(term, u'http://www.itunes.com/', None) + for term in self.pop('itunes_keywords').split(','): + if term.strip(): + self._addTag(term.strip(), u'http://www.itunes.com/', None) def _start_itunes_category(self, attrsD): self._addTag(attrsD.get('text'), u'http://www.itunes.com/', None) @@ -1500,13 +1534,13 @@ class _FeedParserMixin: attrsD.setdefault('type', u'text/html') context = self._getContext() attrsD = self._itsAnHrefDamnIt(attrsD) - if attrsD.has_key('href'): + if 'href' in attrsD: attrsD['href'] = self.resolveURI(attrsD['href']) expectingText = self.infeed or self.inentry or self.insource context.setdefault('links', []) if not (self.inentry and self.inimage): context['links'].append(FeedParserDict(attrsD)) - if attrsD.has_key('href'): + if 'href' in attrsD: expectingText = 0 if (attrsD.get('rel') == u'alternate') and (self.mapContentType(attrsD.get('type')) in self.html_types): context['link'] = attrsD['href'] @@ -1515,19 +1549,20 @@ class _FeedParserMixin: def _end_link(self): value = self.pop('link') - context = self._getContext() def _start_guid(self, attrsD): self.guidislink = (attrsD.get('ispermalink', 'true') == 'true') self.push('id', 1) + _start_id = _start_guid def _end_guid(self): value = self.pop('id') - self._save('guidislink', self.guidislink and not self._getContext().has_key('link')) + self._save('guidislink', self.guidislink and 'link' not in self._getContext()) if self.guidislink: # guid acts as link, but only if 'ispermalink' is not present or is 'true', # and only if the item doesn't already have a link element self._save('link', value) + _end_id = _end_guid def _start_title(self, attrsD): if self.svgOK: @@ -1542,18 +1577,17 @@ class _FeedParserMixin: value = self.popContent('title') if not value: return - context = self._getContext() - self.hasTitle = 1 + self.title_depth = self.depth _end_dc_title = _end_title def _end_media_title(self): - hasTitle = self.hasTitle + title_depth = self.title_depth self._end_title() - self.hasTitle = hasTitle + self.title_depth = title_depth def _start_description(self, attrsD): context = self._getContext() - if context.has_key('summary'): + if 'summary' in context: self._summaryKey = 'content' self._start_content(attrsD) else: @@ -1583,7 +1617,7 @@ class _FeedParserMixin: def _start_generator(self, attrsD): if attrsD: attrsD = self._itsAnHrefDamnIt(attrsD) - if attrsD.has_key('href'): + if 'href' in attrsD: attrsD['href'] = self.resolveURI(attrsD['href']) self._getContext()['generator_detail'] = FeedParserDict(attrsD) self.push('generator', 1) @@ -1591,7 +1625,7 @@ class _FeedParserMixin: def _end_generator(self): value = self.pop('generator') context = self._getContext() - if context.has_key('generator_detail'): + if 'generator_detail' in context: context['generator_detail']['name'] = value def _start_admin_generatoragent(self, attrsD): @@ -1611,7 +1645,7 @@ class _FeedParserMixin: def _start_summary(self, attrsD): context = self._getContext() - if context.has_key('summary'): + if 'summary' in context: self._summaryKey = 'content' self._start_content(attrsD) else: @@ -1635,17 +1669,17 @@ class _FeedParserMixin: def _start_source(self, attrsD): if 'url' in attrsD: - # This means that we're processing a source element from an RSS 2.0 feed - self.sourcedata['href'] = attrsD[u'url'] + # This means that we're processing a source element from an RSS 2.0 feed + self.sourcedata['href'] = attrsD[u'url'] self.push('source', 1) self.insource = 1 - self.hasTitle = 0 + self.title_depth = -1 def _end_source(self): self.insource = 0 value = self.pop('source') if value: - self.sourcedata['title'] = value + self.sourcedata['title'] = value self._getContext()['source'] = copy.deepcopy(self.sourcedata) self.sourcedata.clear() @@ -1679,6 +1713,8 @@ class _FeedParserMixin: self.push('itunes_image', 0) if attrsD.get('href'): self._getContext()['image'] = FeedParserDict({'href': attrsD.get('href')}) + elif attrsD.get('url'): + self._getContext()['image'] = FeedParserDict({'href': attrsD.get('url')}) _start_itunes_link = _start_itunes_image def _end_itunes_block(self): @@ -1707,7 +1743,7 @@ class _FeedParserMixin: url = self.pop('url') context = self._getContext() if url != None and len(url.strip()) != 0: - if not context['media_thumbnail'][-1].has_key('url'): + if 'url' not in context['media_thumbnail'][-1]: context['media_thumbnail'][-1]['url'] = url def _start_media_player(self, attrsD): @@ -1760,8 +1796,8 @@ if _XML_AVAILABLE: else: givenprefix = None prefix = self._matchnamespaces.get(lowernamespace, givenprefix) - if givenprefix and (prefix == None or (prefix == '' and lowernamespace == '')) and not self.namespacesInUse.has_key(givenprefix): - raise UndeclaredNamespace, "'%s' is not associated with a namespace" % givenprefix + if givenprefix and (prefix == None or (prefix == '' and lowernamespace == '')) and givenprefix not in self.namespacesInUse: + raise UndeclaredNamespace, "'%s' is not associated with a namespace" % givenprefix localname = str(localname).lower() # qname implementation is horribly broken in Python 2.1 (it @@ -1781,9 +1817,9 @@ if _XML_AVAILABLE: localname = prefix.lower() + ':' + localname elif namespace and not qname: #Expat for name,value in self.namespacesInUse.items(): - if name and value == namespace: - localname = name + ':' + localname - break + if name and value == namespace: + localname = name + ':' + localname + break for (namespace, attrlocalname), attrvalue in attrs.items(): lowernamespace = (namespace or '').lower() @@ -1810,9 +1846,9 @@ if _XML_AVAILABLE: localname = prefix + ':' + localname elif namespace and not qname: #Expat for name,value in self.namespacesInUse.items(): - if name and value == namespace: - localname = name + ':' + localname - break + if name and value == namespace: + localname = name + ':' + localname + break localname = str(localname).lower() self.unknown_endtag(localname) @@ -1820,6 +1856,9 @@ if _XML_AVAILABLE: self.bozo = 1 self.exc = exc + # drv_libxml2 calls warning() in some cases + warning = error + def fatalError(self, exc): self.error(exc) raise exc @@ -1827,11 +1866,11 @@ if _XML_AVAILABLE: class _BaseHTMLProcessor(sgmllib.SGMLParser): special = re.compile('''[<>'"]''') bare_ampersand = re.compile("&(?!#\d+;|#x[0-9a-fA-F]+;|\w+;)") - elements_no_end_tag = [ + elements_no_end_tag = set([ 'area', 'base', 'basefont', 'br', 'col', 'command', 'embed', 'frame', 'hr', 'img', 'input', 'isindex', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr' - ] + ]) def __init__(self, encoding, _type): self.encoding = encoding @@ -1871,7 +1910,6 @@ class _BaseHTMLProcessor(sgmllib.SGMLParser): def feed(self, data): data = re.compile(r'', self._shorttag_replace, data) # bug [ 1399464 ] Bad regexp for _shorttag_replace data = re.sub(r'<([^<>\s]+?)\s*/>', self._shorttag_replace, data) data = data.replace(''', "'") data = data.replace('"', '"') @@ -1920,36 +1958,36 @@ class _BaseHTMLProcessor(sgmllib.SGMLParser): except (UnicodeEncodeError, LookupError): pass if tag in self.elements_no_end_tag: - self.pieces.append('<%(tag)s%(strattrs)s />' % locals()) + self.pieces.append('<%s%s />' % (tag, strattrs)) else: - self.pieces.append('<%(tag)s%(strattrs)s>' % locals()) + self.pieces.append('<%s%s>' % (tag, strattrs)) def unknown_endtag(self, tag): # called for each end tag, e.g. for , tag will be 'pre' # Reconstruct the original end tag. if tag not in self.elements_no_end_tag: - self.pieces.append("" % locals()) + self.pieces.append("" % tag) def handle_charref(self, ref): # called for each character reference, e.g. for ' ', ref will be '160' # Reconstruct the original character reference. if ref.startswith('x'): - value = unichr(int(ref[1:],16)) + value = int(ref[1:], 16) else: - value = unichr(int(ref)) + value = int(ref) - if value in _cp1252.keys(): + if value in _cp1252: self.pieces.append('&#%s;' % hex(ord(_cp1252[value]))[1:]) else: - self.pieces.append('&#%(ref)s;' % locals()) + self.pieces.append('&#%s;' % ref) def handle_entityref(self, ref): # called for each entity reference, e.g. for '©', ref will be 'copy' # Reconstruct the original entity reference. - if name2codepoint.has_key(ref): - self.pieces.append('&%(ref)s;' % locals()) + if ref in name2codepoint or ref == 'apos': + self.pieces.append('&%s;' % ref) else: - self.pieces.append('&%(ref)s' % locals()) + self.pieces.append('&%s' % ref) def handle_data(self, text): # called for each block of plain text, i.e. outside of any tag and @@ -1960,19 +1998,19 @@ class _BaseHTMLProcessor(sgmllib.SGMLParser): def handle_comment(self, text): # called for each HTML comment, e.g. # Reconstruct the original comment. - self.pieces.append('' % locals()) + self.pieces.append('' % text) def handle_pi(self, text): # called for each processing instruction, e.g. # Reconstruct original processing instruction. - self.pieces.append('' % locals()) + self.pieces.append('' % text) def handle_decl(self, text): # called for the DOCTYPE, if present, e.g. # # Reconstruct original DOCTYPE - self.pieces.append('' % locals()) + self.pieces.append('' % text) _new_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9:]*\s*').match def _scan_name(self, i, declstartpos): @@ -2030,7 +2068,7 @@ class _LooseFeedParser(_FeedParserMixin, _BaseHTMLProcessor): data = data.replace('"', '"') data = data.replace(''', ''') data = data.replace(''', ''') - if self.contentparams.has_key('type') and not self.contentparams.get('type', u'xml').endswith(u'xml'): + if not self.contentparams.get('type', u'xml').endswith(u'xml'): data = data.replace('<', '<') data = data.replace('>', '>') data = data.replace('&', '&') @@ -2048,8 +2086,8 @@ class _MicroformatsParser: NODE = 4 EMAIL = 5 - known_xfn_relationships = ['contact', 'acquaintance', 'friend', 'met', 'co-worker', 'coworker', 'colleague', 'co-resident', 'coresident', 'neighbor', 'child', 'parent', 'sibling', 'brother', 'sister', 'spouse', 'wife', 'husband', 'kin', 'relative', 'muse', 'crush', 'date', 'sweetheart', 'me'] - known_binary_extensions = ['zip','rar','exe','gz','tar','tgz','tbz2','bz2','z','7z','dmg','img','sit','sitx','hqx','deb','rpm','bz2','jar','rar','iso','bin','msi','mp2','mp3','ogg','ogm','mp4','m4v','m4a','avi','wma','wmv'] + known_xfn_relationships = set(['contact', 'acquaintance', 'friend', 'met', 'co-worker', 'coworker', 'colleague', 'co-resident', 'coresident', 'neighbor', 'child', 'parent', 'sibling', 'brother', 'sister', 'spouse', 'wife', 'husband', 'kin', 'relative', 'muse', 'crush', 'date', 'sweetheart', 'me']) + known_binary_extensions = set(['zip','rar','exe','gz','tar','tgz','tbz2','bz2','z','7z','dmg','img','sit','sitx','hqx','deb','rpm','bz2','jar','rar','iso','bin','msi','mp2','mp3','ogg','ogm','mp4','m4v','m4a','avi','wma','wmv']) def __init__(self, data, baseuri, encoding): self.document = BeautifulSoup.BeautifulSoup(data) @@ -2410,7 +2448,7 @@ class _MicroformatsParser: def isProbablyDownloadable(self, elm): attrsD = elm.attrMap - if not attrsD.has_key('href'): + if 'href' not in attrsD: return 0 linktype = attrsD.get('type', '').strip() if linktype.startswith('audio/') or \ @@ -2459,10 +2497,7 @@ class _MicroformatsParser: all = lambda x: 1 for elm in self.document(all, {'rel': re.compile('.+'), 'href': re.compile('.+')}): rels = elm.get('rel', u'').split() - xfn_rels = [] - for rel in rels: - if rel in self.known_xfn_relationships: - xfn_rels.append(rel) + xfn_rels = [r for r in rels if r in self.known_xfn_relationships] if xfn_rels: self.xfn.append({"relationships": xfn_rels, "href": elm.get('href', ''), "name": elm.string}) @@ -2482,7 +2517,7 @@ def _parseMicroformats(htmlSource, baseURI, encoding): return {"tags": p.tags, "enclosures": p.enclosures, "xfn": p.xfn, "vcard": p.vcard} class _RelativeURIResolver(_BaseHTMLProcessor): - relative_uris = [('a', 'href'), + relative_uris = set([('a', 'href'), ('applet', 'codebase'), ('area', 'href'), ('blockquote', 'cite'), @@ -2506,14 +2541,14 @@ class _RelativeURIResolver(_BaseHTMLProcessor): ('object', 'data'), ('object', 'usemap'), ('q', 'cite'), - ('script', 'src')] + ('script', 'src')]) def __init__(self, baseuri, encoding, _type): _BaseHTMLProcessor.__init__(self, encoding, _type) self.baseuri = baseuri def resolveURI(self, uri): - return _makeSafeAbsoluteURI(_urljoin(self.baseuri, uri.strip())) + return _makeSafeAbsoluteURI(self.baseuri, uri.strip()) def unknown_starttag(self, tag, attrs): attrs = self.normalize_attrs(attrs) @@ -2531,21 +2566,30 @@ def _resolveRelativeURIs(htmlSource, baseURI, encoding, _type): def _makeSafeAbsoluteURI(base, rel=None): # bail if ACCEPTABLE_URI_SCHEMES is empty if not ACCEPTABLE_URI_SCHEMES: - return _urljoin(base, rel or u'') + try: + return _urljoin(base, rel or u'') + except ValueError: + return u'' if not base: return rel or u'' if not rel: - scheme = urlparse.urlparse(base)[0] + try: + scheme = urlparse.urlparse(base)[0] + except ValueError: + return u'' if not scheme or scheme in ACCEPTABLE_URI_SCHEMES: return base return u'' - uri = _urljoin(base, rel) + try: + uri = _urljoin(base, rel) + except ValueError: + return u'' if uri.strip().split(':', 1)[0] not in ACCEPTABLE_URI_SCHEMES: return u'' return uri class _HTMLSanitizer(_BaseHTMLProcessor): - acceptable_elements = ['a', 'abbr', 'acronym', 'address', 'area', + acceptable_elements = set(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'big', 'blockquote', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'command', 'datagrid', 'datalist', 'dd', 'del', 'details', 'dfn', @@ -2557,9 +2601,9 @@ class _HTMLSanitizer(_BaseHTMLProcessor): 'p', 'pre', 'progress', 'q', 's', 'samp', 'section', 'select', 'small', 'sound', 'source', 'spacer', 'span', 'strike', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'time', 'tfoot', - 'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'video', 'noscript'] + 'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'video', 'noscript']) - acceptable_attributes = ['abbr', 'accept', 'accept-charset', 'accesskey', + acceptable_attributes = set(['abbr', 'accept', 'accept-charset', 'accesskey', 'action', 'align', 'alt', 'autocomplete', 'autofocus', 'axis', 'background', 'balance', 'bgcolor', 'bgproperties', 'border', 'bordercolor', 'bordercolordark', 'bordercolorlight', 'bottompadding', @@ -2580,11 +2624,11 @@ class _HTMLSanitizer(_BaseHTMLProcessor): 'start', 'step', 'summary', 'suppress', 'tabindex', 'target', 'template', 'title', 'toppadding', 'type', 'unselectable', 'usemap', 'urn', 'valign', 'value', 'variable', 'volume', 'vspace', 'vrml', 'width', 'wrap', - 'xml:lang'] + 'xml:lang']) - unacceptable_elements_with_end_tag = ['script', 'applet', 'style'] + unacceptable_elements_with_end_tag = set(['script', 'applet', 'style']) - acceptable_css_properties = ['azimuth', 'background-color', + acceptable_css_properties = set(['azimuth', 'background-color', 'border-bottom-color', 'border-collapse', 'border-color', 'border-left-color', 'border-right-color', 'border-top-color', 'clear', 'color', 'cursor', 'direction', 'display', 'elevation', 'float', 'font', @@ -2594,26 +2638,26 @@ class _HTMLSanitizer(_BaseHTMLProcessor): 'speak', 'speak-header', 'speak-numeral', 'speak-punctuation', 'speech-rate', 'stress', 'text-align', 'text-decoration', 'text-indent', 'unicode-bidi', 'vertical-align', 'voice-family', 'volume', - 'white-space', 'width'] + 'white-space', 'width']) # survey of common keywords found in feeds - acceptable_css_keywords = ['auto', 'aqua', 'black', 'block', 'blue', + acceptable_css_keywords = set(['auto', 'aqua', 'black', 'block', 'blue', 'bold', 'both', 'bottom', 'brown', 'center', 'collapse', 'dashed', 'dotted', 'fuchsia', 'gray', 'green', '!important', 'italic', 'left', 'lime', 'maroon', 'medium', 'none', 'navy', 'normal', 'nowrap', 'olive', 'pointer', 'purple', 'red', 'right', 'solid', 'silver', 'teal', 'top', - 'transparent', 'underline', 'white', 'yellow'] + 'transparent', 'underline', 'white', 'yellow']) valid_css_values = re.compile('^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|' + '\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$') - mathml_elements = ['annotation', 'annotation-xml', 'maction', 'math', + mathml_elements = set(['annotation', 'annotation-xml', 'maction', 'math', 'merror', 'mfenced', 'mfrac', 'mi', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mprescripts', 'mroot', 'mrow', 'mspace', 'msqrt', 'mstyle', 'msub', 'msubsup', 'msup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', - 'munderover', 'none', 'semantics'] + 'munderover', 'none', 'semantics']) - mathml_attributes = ['actiontype', 'align', 'columnalign', 'columnalign', + mathml_attributes = set(['actiontype', 'align', 'columnalign', 'columnalign', 'columnalign', 'close', 'columnlines', 'columnspacing', 'columnspan', 'depth', 'display', 'displaystyle', 'encoding', 'equalcolumns', 'equalrows', 'fence', 'fontstyle', 'fontweight', 'frame', 'height', 'linethickness', @@ -2621,18 +2665,18 @@ class _HTMLSanitizer(_BaseHTMLProcessor): 'maxsize', 'minsize', 'open', 'other', 'rowalign', 'rowalign', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'scriptlevel', 'selection', 'separator', 'separators', 'stretchy', 'width', 'width', 'xlink:href', - 'xlink:show', 'xlink:type', 'xmlns', 'xmlns:xlink'] + 'xlink:show', 'xlink:type', 'xmlns', 'xmlns:xlink']) # svgtiny - foreignObject + linearGradient + radialGradient + stop - svg_elements = ['a', 'animate', 'animateColor', 'animateMotion', + svg_elements = set(['a', 'animate', 'animateColor', 'animateMotion', 'animateTransform', 'circle', 'defs', 'desc', 'ellipse', 'foreignObject', 'font-face', 'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern', 'linearGradient', 'line', 'marker', 'metadata', 'missing-glyph', 'mpath', 'path', 'polygon', 'polyline', 'radialGradient', 'rect', 'set', 'stop', - 'svg', 'switch', 'text', 'title', 'tspan', 'use'] + 'svg', 'switch', 'text', 'title', 'tspan', 'use']) # svgtiny + class + opacity + offset + xmlns + xmlns:xlink - svg_attributes = ['accent-height', 'accumulate', 'additive', 'alphabetic', + svg_attributes = set(['accent-height', 'accumulate', 'additive', 'alphabetic', 'arabic-form', 'ascent', 'attributeName', 'attributeType', 'baseProfile', 'bbox', 'begin', 'by', 'calcMode', 'cap-height', 'class', 'color', 'color-rendering', 'content', 'cx', 'cy', 'd', 'dx', @@ -2658,14 +2702,14 @@ class _HTMLSanitizer(_BaseHTMLProcessor): 'widths', 'x', 'x-height', 'x1', 'x2', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:lang', 'xml:space', 'xmlns', 'xmlns:xlink', 'y', 'y1', - 'y2', 'zoomAndPan'] + 'y2', 'zoomAndPan']) svg_attr_map = None svg_elem_map = None - acceptable_svg_properties = [ 'fill', 'fill-opacity', 'fill-rule', + acceptable_svg_properties = set([ 'fill', 'fill-opacity', 'fill-rule', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', - 'stroke-opacity'] + 'stroke-opacity']) def reset(self): _BaseHTMLProcessor.reset(self) @@ -2718,7 +2762,7 @@ class _HTMLSanitizer(_BaseHTMLProcessor): # declare xlink namespace, if needed if self.mathmlOK or self.svgOK: - if filter((lambda n,v: n.startswith('xlink:')),attrs): + if filter(lambda (n,v): n.startswith('xlink:'),attrs): if not ('xmlns:xlink','http://www.w3.org/1999/xlink') in attrs: attrs.append(('xmlns:xlink','http://www.w3.org/1999/xlink')) @@ -2923,9 +2967,6 @@ def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, h if hasattr(url_file_stream_or_string, 'read'): return url_file_stream_or_string - if url_file_stream_or_string == '-': - return sys.stdin - if isinstance(url_file_stream_or_string, basestring) \ and urlparse.urlparse(url_file_stream_or_string)[0] in ('http', 'https', 'ftp', 'file', 'feed'): # Deal with the feed URI scheme @@ -2952,7 +2993,7 @@ def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, h # try to open with urllib2 (to use optional headers) request = _build_urllib2_request(url_file_stream_or_string, agent, etag, modified, referrer, auth, request_headers) - opener = apply(urllib2.build_opener, tuple(handlers + [_FeedURLHandler()])) + opener = urllib2.build_opener(*tuple(handlers + [_FeedURLHandler()])) opener.addheaders = [] # RMK - must clear so we only send our custom User-Agent try: return opener.open(request) @@ -2962,7 +3003,14 @@ def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, h # try to open with native open function (if url_file_stream_or_string is a filename) try: return open(url_file_stream_or_string, 'rb') - except IOError: + except (IOError, UnicodeEncodeError, TypeError): + # if url_file_stream_or_string is a unicode object that + # cannot be converted to the encoding returned by + # sys.getfilesystemencoding(), a UnicodeEncodeError + # will be thrown + # If url_file_stream_or_string is a string that contains NULL + # (such as an XML document encoded in UTF-32), TypeError will + # be thrown. pass # treat url_file_stream_or_string as string @@ -3122,7 +3170,7 @@ def _parse_date_iso8601(dateString): day = int(day) # special case of the century - is the first year of the 21st century # 2000 or 2001 ? The debate goes on... - if 'century' in params.keys(): + if 'century' in params: year = (int(params['century']) - 1) * 100 + 1 # in ISO 8601 most fields are optional for field in ['hour', 'minute', 'second', 'tzhour', 'tzmin']: @@ -3197,20 +3245,6 @@ def _parse_date_nate(dateString): return _parse_date_w3dtf(w3dtfdate) registerDateHandler(_parse_date_nate) -_mssql_date_re = \ - re.compile('(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})(\.\d+)?') -def _parse_date_mssql(dateString): - '''Parse a string according to the MS SQL date format''' - m = _mssql_date_re.match(dateString) - if not m: - return - w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % \ - {'year': m.group(1), 'month': m.group(2), 'day': m.group(3),\ - 'hour': m.group(4), 'minute': m.group(5), 'second': m.group(6),\ - 'zonediff': '+09:00'} - return _parse_date_w3dtf(w3dtfdate) -registerDateHandler(_parse_date_mssql) - # Unicode strings for Greek date strings _greek_months = \ { \ @@ -3306,6 +3340,9 @@ registerDateHandler(_parse_date_hungarian) # Drake and licensed under the Python license. Removed all range checking # for month, day, hour, minute, and second, since mktime will normalize # these later +# Modified to also support MSSQL-style datetimes as defined at: +# http://msdn.microsoft.com/en-us/library/ms186724.aspx +# (which basically means allowing a space as a date/time/timezone separator) def _parse_date_w3dtf(dateString): def __extract_date(m): year = int(m.group('year')) @@ -3331,7 +3368,7 @@ def _parse_date_w3dtf(dateString): day = 31 elif jday < julian: if day + diff < 28: - day = day + diff + day = day + diff else: month = month + 1 return year, month, day @@ -3387,12 +3424,11 @@ def _parse_date_w3dtf(dateString): '(?:(?P-|)' '(?:(?P\d\d)(?:(?P=dsep)(?P\d\d))?' '|(?P\d\d\d)))?') - __tzd_re = '(?P[-+](?P\d\d)(?::?(?P\d\d))|Z)' - __tzd_rx = re.compile(__tzd_re) + __tzd_re = ' ?(?P[-+](?P\d\d)(?::?(?P\d\d))|Z)?' __time_re = ('(?P\d\d)(?P:|)(?P\d\d)' '(?:(?P=tsep)(?P\d\d)(?:[.,]\d+)?)?' + __tzd_re) - __datetime_re = '%s(?:T%s)?' % (__date_re, __time_re) + __datetime_re = '%s(?:[T ]%s)?' % (__date_re, __time_re) __datetime_rx = re.compile(__datetime_re) m = __datetime_rx.match(dateString) if (m is None) or (m.group() != dateString): @@ -3403,41 +3439,91 @@ def _parse_date_w3dtf(dateString): return time.gmtime(time.mktime(gmt) + __extract_tzd(m) - time.timezone) registerDateHandler(_parse_date_w3dtf) -def _parse_date_rfc822(dateString): - '''Parse an RFC822, RFC1123, RFC2822, or asctime-style date''' - data = dateString.split() - if not data: +# Define the strings used by the RFC822 datetime parser +_rfc822_months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', + 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'] +_rfc822_daynames = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] + +# Only the first three letters of the month name matter +_rfc822_month = "(?P%s)(?:[a-z]*,?)" % ('|'.join(_rfc822_months)) +# The year may be 2 or 4 digits; capture the century if it exists +_rfc822_year = "(?P(?:\d{2})?\d{2})" +_rfc822_day = "(?P *\d{1,2})" +_rfc822_date = "%s %s %s" % (_rfc822_day, _rfc822_month, _rfc822_year) + +_rfc822_hour = "(?P\d{2}):(?P\d{2})(?::(?P\d{2}))?" +_rfc822_tz = "(?Put|gmt(?:[+-]\d{2}:\d{2})?|[aecmp][sd]?t|[zamny]|[+-]\d{4})" +_rfc822_tznames = { + 'ut': 0, 'gmt': 0, 'z': 0, + 'adt': -3, 'ast': -4, 'at': -4, + 'edt': -4, 'est': -5, 'et': -5, + 'cdt': -5, 'cst': -6, 'ct': -6, + 'mdt': -6, 'mst': -7, 'mt': -7, + 'pdt': -7, 'pst': -8, 'pt': -8, + 'a': -1, 'n': 1, + 'm': -12, 'y': 12, + } +# The timezone may be prefixed by 'Etc/' +_rfc822_time = "%s (?:etc/)?%s" % (_rfc822_hour, _rfc822_tz) + +_rfc822_dayname = "(?P%s)" % ('|'.join(_rfc822_daynames)) +_rfc822_match = re.compile( + "(?:%s, )?%s(?: %s)?" % (_rfc822_dayname, _rfc822_date, _rfc822_time) +).match + +def _parse_date_rfc822(dt): + """Parse RFC 822 dates and times, with one minor + difference: years may be 4DIGIT or 2DIGIT. + http://tools.ietf.org/html/rfc822#section-5""" + try: + m = _rfc822_match(dt.lower()).groupdict(0) + except AttributeError: return None - if data[0][-1] in (',', '.') or data[0].lower() in rfc822._daynames: - del data[0] - if len(data) == 4: - s = data[3] - i = s.find('+') - if i > 0: - data[3:] = [s[:i], s[i+1:]] - else: - data.append('') - dateString = " ".join(data) - # Account for the Etc/GMT timezone by stripping 'Etc/' - elif len(data) == 5 and data[4].lower().startswith('etc/'): - data[4] = data[4][4:] - dateString = " ".join(data) - if len(data) < 5: - dateString += ' 00:00:00 GMT' - tm = rfc822.parsedate_tz(dateString) - if tm: - # Jython doesn't adjust for 2-digit years like CPython does, - # so account for it by shifting the year so that it's in the - # range 1970-2069 (1970 being the year of the Unix epoch). - if tm[0] < 100: - tm = (tm[0] + (1900, 2000)[tm[0] < 70],) + tm[1:] - return time.gmtime(rfc822.mktime_tz(tm)) -# rfc822.py defines several time zones, but we define some extra ones. -# 'ET' is equivalent to 'EST', etc. -_additional_timezones = {'AT': -400, 'ET': -500, 'CT': -600, 'MT': -700, 'PT': -800} -rfc822._timezones.update(_additional_timezones) + + # Calculate a date and timestamp + for k in ('year', 'day', 'hour', 'minute', 'second'): + m[k] = int(m[k]) + m['month'] = _rfc822_months.index(m['month']) + 1 + # If the year is 2 digits, assume everything in the 90's is the 1990's + if m['year'] < 100: + m['year'] += (1900, 2000)[m['year'] < 90] + stamp = datetime.datetime(*[m[i] for i in + ('year', 'month', 'day', 'hour', 'minute', 'second')]) + + # Use the timezone information to calculate the difference between + # the given date and timestamp and Universal Coordinated Time + tzhour = 0 + tzmin = 0 + if m['tz'] and m['tz'].startswith('gmt'): + # Handle GMT and GMT+hh:mm timezone syntax (the trailing + # timezone info will be handled by the next `if` block) + m['tz'] = ''.join(m['tz'][3:].split(':')) or 'gmt' + if not m['tz']: + pass + elif m['tz'].startswith('+'): + tzhour = int(m['tz'][1:3]) + tzmin = int(m['tz'][3:]) + elif m['tz'].startswith('-'): + tzhour = int(m['tz'][1:3]) * -1 + tzmin = int(m['tz'][3:]) * -1 + else: + tzhour = _rfc822_tznames[m['tz']] + delta = datetime.timedelta(0, 0, 0, 0, tzmin, tzhour) + + # Return the date and timestamp in UTC + return (stamp - delta).utctimetuple() registerDateHandler(_parse_date_rfc822) +def _parse_date_asctime(dt): + """Parse asctime-style dates""" + dayname, month, day, remainder = dt.split(None, 3) + # Convert month and day into zero-padded integers + month = '%02i ' % (_rfc822_months.index(month.lower()) + 1) + day = '%02i ' % (int(day),) + dt = month + day + remainder + return time.strptime(dt, '%m %d %H:%M:%S %Y')[:-1] + (0, ) +registerDateHandler(_parse_date_asctime) + def _parse_date_perforce(aDateString): """parse a date in yyyy/mm/dd hh:mm:ss TTT format""" # Fri, 2006/09/15 08:19:53 EDT @@ -3471,211 +3557,283 @@ def _parse_date(dateString): return date9tuple return None -def _getCharacterEncoding(http_headers, xml_data): - '''Get the character encoding of the XML document +# Each marker represents some of the characters of the opening XML +# processing instruction (' +RE_XML_DECLARATION = re.compile('^<\?xml[^>]*?>') + +# Capture the value of the XML processing instruction's encoding attribute. +# Example: +RE_XML_PI_ENCODING = re.compile(_s2bytes('^<\?.*encoding=[\'"](.*?)[\'"].*\?>')) + +def convert_to_utf8(http_headers, data): + '''Detect and convert the character encoding to UTF-8. http_headers is a dictionary - xml_data is a raw string (not Unicode) + data is a raw string (not Unicode)''' - This is so much trickier than it sounds, it's not even funny. - According to RFC 3023 ('XML Media Types'), if the HTTP Content-Type - is application/xml, application/*+xml, - application/xml-external-parsed-entity, or application/xml-dtd, - the encoding given in the charset parameter of the HTTP Content-Type - takes precedence over the encoding given in the XML prefix within the - document, and defaults to 'utf-8' if neither are specified. But, if - the HTTP Content-Type is text/xml, text/*+xml, or - text/xml-external-parsed-entity, the encoding given in the XML prefix - within the document is ALWAYS IGNORED and only the encoding given in - the charset parameter of the HTTP Content-Type header should be - respected, and it defaults to 'us-ascii' if not specified. + # This is so much trickier than it sounds, it's not even funny. + # According to RFC 3023 ('XML Media Types'), if the HTTP Content-Type + # is application/xml, application/*+xml, + # application/xml-external-parsed-entity, or application/xml-dtd, + # the encoding given in the charset parameter of the HTTP Content-Type + # takes precedence over the encoding given in the XML prefix within the + # document, and defaults to 'utf-8' if neither are specified. But, if + # the HTTP Content-Type is text/xml, text/*+xml, or + # text/xml-external-parsed-entity, the encoding given in the XML prefix + # within the document is ALWAYS IGNORED and only the encoding given in + # the charset parameter of the HTTP Content-Type header should be + # respected, and it defaults to 'us-ascii' if not specified. - Furthermore, discussion on the atom-syntax mailing list with the - author of RFC 3023 leads me to the conclusion that any document - served with a Content-Type of text/* and no charset parameter - must be treated as us-ascii. (We now do this.) And also that it - must always be flagged as non-well-formed. (We now do this too.) + # Furthermore, discussion on the atom-syntax mailing list with the + # author of RFC 3023 leads me to the conclusion that any document + # served with a Content-Type of text/* and no charset parameter + # must be treated as us-ascii. (We now do this.) And also that it + # must always be flagged as non-well-formed. (We now do this too.) - If Content-Type is unspecified (input was local file or non-HTTP source) - or unrecognized (server just got it totally wrong), then go by the - encoding given in the XML prefix of the document and default to - 'iso-8859-1' as per the HTTP specification (RFC 2616). + # If Content-Type is unspecified (input was local file or non-HTTP source) + # or unrecognized (server just got it totally wrong), then go by the + # encoding given in the XML prefix of the document and default to + # 'iso-8859-1' as per the HTTP specification (RFC 2616). - Then, assuming we didn't find a character encoding in the HTTP headers - (and the HTTP Content-type allowed us to look in the body), we need - to sniff the first few bytes of the XML data and try to determine - whether the encoding is ASCII-compatible. Section F of the XML - specification shows the way here: - http://www.w3.org/TR/REC-xml/#sec-guessing-no-ext-info - - If the sniffed encoding is not ASCII-compatible, we need to make it - ASCII compatible so that we can sniff further into the XML declaration - to find the encoding attribute, which will tell us the true encoding. - - Of course, none of this guarantees that we will be able to parse the - feed in the declared character encoding (assuming it was declared - correctly, which many are not). CJKCodecs and iconv_codec help a lot; - you should definitely install them if you can. - http://cjkpython.i18n.org/ - ''' - - def _parseHTTPContentType(content_type): - '''takes HTTP Content-Type header and returns (content type, charset) - - If no charset is specified, returns (content type, '') - If no content type is specified, returns ('', '') - Both return parameters are guaranteed to be lowercase strings - ''' - content_type = content_type or '' - content_type, params = cgi.parse_header(content_type) - charset = params.get('charset', '').replace("'", "") - if not isinstance(charset, unicode): - charset = charset.decode('utf-8', 'ignore') - return content_type, charset - - sniffed_xml_encoding = u'' - xml_encoding = u'' - true_encoding = u'' - http_content_type, http_encoding = _parseHTTPContentType(http_headers.get('content-type', http_headers.get('Content-type'))) - # Must sniff for non-ASCII-compatible character encodings before - # searching for XML declaration. This heuristic is defined in - # section F of the XML specification: + # Then, assuming we didn't find a character encoding in the HTTP headers + # (and the HTTP Content-type allowed us to look in the body), we need + # to sniff the first few bytes of the XML data and try to determine + # whether the encoding is ASCII-compatible. Section F of the XML + # specification shows the way here: # http://www.w3.org/TR/REC-xml/#sec-guessing-no-ext-info + + # If the sniffed encoding is not ASCII-compatible, we need to make it + # ASCII compatible so that we can sniff further into the XML declaration + # to find the encoding attribute, which will tell us the true encoding. + + # Of course, none of this guarantees that we will be able to parse the + # feed in the declared character encoding (assuming it was declared + # correctly, which many are not). iconv_codec can help a lot; + # you should definitely install it if you can. + # http://cjkpython.i18n.org/ + + bom_encoding = u'' + xml_encoding = u'' + rfc3023_encoding = u'' + + # Look at the first few bytes of the document to guess what + # its encoding may be. We only need to decode enough of the + # document that we can use an ASCII-compatible regular + # expression to search for an XML encoding declaration. + # The heuristic follows the XML specification, section F: + # http://www.w3.org/TR/REC-xml/#sec-guessing-no-ext-info + # Check for BOMs first. + if data[:4] == codecs.BOM_UTF32_BE: + bom_encoding = u'utf-32be' + data = data[4:] + elif data[:4] == codecs.BOM_UTF32_LE: + bom_encoding = u'utf-32le' + data = data[4:] + elif data[:2] == codecs.BOM_UTF16_BE and data[2:4] != ZERO_BYTES: + bom_encoding = u'utf-16be' + data = data[2:] + elif data[:2] == codecs.BOM_UTF16_LE and data[2:4] != ZERO_BYTES: + bom_encoding = u'utf-16le' + data = data[2:] + elif data[:3] == codecs.BOM_UTF8: + bom_encoding = u'utf-8' + data = data[3:] + # Check for the characters '= 4) and (xml_data[:2] == _l2bytes([0xfe, 0xff])) and (xml_data[2:4] != _l2bytes([0x00, 0x00])): - # UTF-16BE with BOM - sniffed_xml_encoding = u'utf-16be' - xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8') - elif xml_data[:4] == _l2bytes([0x3c, 0x00, 0x3f, 0x00]): - # UTF-16LE - sniffed_xml_encoding = u'utf-16le' - xml_data = unicode(xml_data, 'utf-16le').encode('utf-8') - elif (len(xml_data) >= 4) and (xml_data[:2] == _l2bytes([0xff, 0xfe])) and (xml_data[2:4] != _l2bytes([0x00, 0x00])): - # UTF-16LE with BOM - sniffed_xml_encoding = u'utf-16le' - xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8') - elif xml_data[:4] == _l2bytes([0x00, 0x00, 0x00, 0x3c]): - # UTF-32BE - sniffed_xml_encoding = u'utf-32be' - xml_data = unicode(xml_data, 'utf-32be').encode('utf-8') - elif xml_data[:4] == _l2bytes([0x3c, 0x00, 0x00, 0x00]): - # UTF-32LE - sniffed_xml_encoding = u'utf-32le' - xml_data = unicode(xml_data, 'utf-32le').encode('utf-8') - elif xml_data[:4] == _l2bytes([0x00, 0x00, 0xfe, 0xff]): - # UTF-32BE with BOM - sniffed_xml_encoding = u'utf-32be' - xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8') - elif xml_data[:4] == _l2bytes([0xff, 0xfe, 0x00, 0x00]): - # UTF-32LE with BOM - sniffed_xml_encoding = u'utf-32le' - xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8') - elif xml_data[:3] == _l2bytes([0xef, 0xbb, 0xbf]): - # UTF-8 with BOM - sniffed_xml_encoding = u'utf-8' - xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8') - else: - # ASCII-compatible - pass - xml_encoding_match = re.compile(_s2bytes('^<\?.*encoding=[\'"](.*?)[\'"].*\?>')).match(xml_data) - except UnicodeDecodeError: + if bom_encoding: + tempdata = data.decode(bom_encoding).encode('utf-8') + except (UnicodeDecodeError, LookupError): + # feedparser recognizes UTF-32 encodings that aren't + # available in Python 2.4 and 2.5, so it's possible to + # encounter a LookupError during decoding. xml_encoding_match = None + else: + xml_encoding_match = RE_XML_PI_ENCODING.match(tempdata) + if xml_encoding_match: xml_encoding = xml_encoding_match.groups()[0].decode('utf-8').lower() - if sniffed_xml_encoding and (xml_encoding in (u'iso-10646-ucs-2', u'ucs-2', u'csunicode', u'iso-10646-ucs-4', u'ucs-4', u'csucs4', u'utf-16', u'utf-32', u'utf_16', u'utf_32', u'utf16', u'u16')): - xml_encoding = sniffed_xml_encoding + # Normalize the xml_encoding if necessary. + if bom_encoding and (xml_encoding in ( + u'u16', u'utf-16', u'utf16', u'utf_16', + u'u32', u'utf-32', u'utf32', u'utf_32', + u'iso-10646-ucs-2', u'iso-10646-ucs-4', + u'csucs4', u'csunicode', u'ucs-2', u'ucs-4' + )): + xml_encoding = bom_encoding + + # Find the HTTP Content-Type and, hopefully, a character + # encoding provided by the server. The Content-Type is used + # to choose the "correct" encoding among the BOM encoding, + # XML declaration encoding, and HTTP encoding, following the + # heuristic defined in RFC 3023. + http_content_type = http_headers.get('content-type') or '' + http_content_type, params = cgi.parse_header(http_content_type) + http_encoding = params.get('charset', '').replace("'", "") + if not isinstance(http_encoding, unicode): + http_encoding = http_encoding.decode('utf-8', 'ignore') + acceptable_content_type = 0 - application_content_types = (u'application/xml', u'application/xml-dtd', u'application/xml-external-parsed-entity') + application_content_types = (u'application/xml', u'application/xml-dtd', + u'application/xml-external-parsed-entity') text_content_types = (u'text/xml', u'text/xml-external-parsed-entity') if (http_content_type in application_content_types) or \ - (http_content_type.startswith(u'application/') and http_content_type.endswith(u'+xml')): + (http_content_type.startswith(u'application/') and + http_content_type.endswith(u'+xml')): acceptable_content_type = 1 - true_encoding = http_encoding or xml_encoding or u'utf-8' + rfc3023_encoding = http_encoding or xml_encoding or u'utf-8' elif (http_content_type in text_content_types) or \ - (http_content_type.startswith(u'text/')) and http_content_type.endswith(u'+xml'): + (http_content_type.startswith(u'text/') and + http_content_type.endswith(u'+xml')): acceptable_content_type = 1 - true_encoding = http_encoding or u'us-ascii' + rfc3023_encoding = http_encoding or u'us-ascii' elif http_content_type.startswith(u'text/'): - true_encoding = http_encoding or u'us-ascii' - elif http_headers and (not (http_headers.has_key('content-type') or http_headers.has_key('Content-type'))): - true_encoding = xml_encoding or u'iso-8859-1' + rfc3023_encoding = http_encoding or u'us-ascii' + elif http_headers and 'content-type' not in http_headers: + rfc3023_encoding = xml_encoding or u'iso-8859-1' else: - true_encoding = xml_encoding or u'utf-8' - # some feeds claim to be gb2312 but are actually gb18030. - # apparently MSIE and Firefox both do the following switch: - if true_encoding.lower() == u'gb2312': - true_encoding = u'gb18030' - return true_encoding, http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type + rfc3023_encoding = xml_encoding or u'utf-8' + # gb18030 is a superset of gb2312, so always replace gb2312 + # with gb18030 for greater compatibility. + if rfc3023_encoding.lower() == u'gb2312': + rfc3023_encoding = u'gb18030' + if xml_encoding.lower() == u'gb2312': + xml_encoding = u'gb18030' -def _toUTF8(data, encoding): - '''Changes an XML data stream on the fly to specify a new encoding + # there are four encodings to keep track of: + # - http_encoding is the encoding declared in the Content-Type HTTP header + # - xml_encoding is the encoding declared in the = 4) and (data[:2] == _l2bytes([0xfe, 0xff])) and (data[2:4] != _l2bytes([0x00, 0x00])): - encoding = 'utf-16be' - data = data[2:] - elif (len(data) >= 4) and (data[:2] == _l2bytes([0xff, 0xfe])) and (data[2:4] != _l2bytes([0x00, 0x00])): - encoding = 'utf-16le' - data = data[2:] - elif data[:3] == _l2bytes([0xef, 0xbb, 0xbf]): - encoding = 'utf-8' - data = data[3:] - elif data[:4] == _l2bytes([0x00, 0x00, 0xfe, 0xff]): - encoding = 'utf-32be' - data = data[4:] - elif data[:4] == _l2bytes([0xff, 0xfe, 0x00, 0x00]): - encoding = 'utf-32le' - data = data[4:] - newdata = unicode(data, encoding) - declmatch = re.compile('^<\?xml[^>]*?>') - newdecl = '''''' - if declmatch.search(newdata): - newdata = declmatch.sub(newdecl, newdata) - else: - newdata = newdecl + u'\n' + newdata - return newdata.encode('utf-8') + if http_headers and (not acceptable_content_type): + if 'content-type' in http_headers: + msg = '%s is not an XML media type' % http_headers['content-type'] + else: + msg = 'no Content-type specified' + error = NonXMLContentType(msg) -def _stripDoctype(data): - '''Strips DOCTYPE from XML document, returns (rss_version, stripped_data) + # determine character encoding + known_encoding = 0 + chardet_encoding = None + tried_encodings = [] + if chardet: + chardet_encoding = unicode(chardet.detect(data)['encoding'] or '', 'ascii', 'ignore') + # try: HTTP encoding, declared XML encoding, encoding sniffed from BOM + for proposed_encoding in (rfc3023_encoding, xml_encoding, bom_encoding, + chardet_encoding, u'utf-8', u'windows-1252', u'iso-8859-2'): + if not proposed_encoding: + continue + if proposed_encoding in tried_encodings: + continue + tried_encodings.append(proposed_encoding) + try: + data = data.decode(proposed_encoding) + except (UnicodeDecodeError, LookupError): + pass + else: + known_encoding = 1 + # Update the encoding in the opening XML processing instruction. + new_declaration = '''''' + if RE_XML_DECLARATION.search(data): + data = RE_XML_DECLARATION.sub(new_declaration, data) + else: + data = new_declaration + u'\n' + data + data = data.encode('utf-8') + break + # if still no luck, give up + if not known_encoding: + error = CharacterEncodingUnknown( + 'document encoding unknown, I tried ' + + '%s, %s, utf-8, windows-1252, and iso-8859-2 but nothing worked' % + (rfc3023_encoding, xml_encoding)) + rfc3023_encoding = u'' + elif proposed_encoding != rfc3023_encoding: + error = CharacterEncodingOverride( + 'document declared as %s, but parsed as %s' % + (rfc3023_encoding, proposed_encoding)) + rfc3023_encoding = proposed_encoding + + return data, rfc3023_encoding, error + +# Match XML entity declarations. +# Example: +RE_ENTITY_PATTERN = re.compile(_s2bytes(r'^\s*]*?)>'), re.MULTILINE) + +# Match XML DOCTYPE declarations. +# Example: +RE_DOCTYPE_PATTERN = re.compile(_s2bytes(r'^\s*]*?)>'), re.MULTILINE) + +# Match safe entity declarations. +# This will allow hexadecimal character references through, +# as well as text, but not arbitrary nested entities. +# Example: cubed "³" +# Example: copyright "(C)" +# Forbidden: explode1 "&explode2;&explode2;" +RE_SAFE_ENTITY_PATTERN = re.compile(_s2bytes('\s+(\w+)\s+"(&#\w+;|[^&"]*)"')) + +def replace_doctype(data): + '''Strips and replaces the DOCTYPE, returns (rss_version, stripped_data) rss_version may be 'rss091n' or None - stripped_data is the same XML document, minus the DOCTYPE + stripped_data is the same XML document with a replaced DOCTYPE ''' + + # Divide the document into two groups by finding the location + # of the first element that doesn't begin with ']*?)>'), re.MULTILINE) - entity_results=entity_pattern.findall(head) - head = entity_pattern.sub(_s2bytes(''), head) - doctype_pattern = re.compile(_s2bytes(r'^\s*]*?)>'), re.MULTILINE) - doctype_results = doctype_pattern.findall(head) + # Save and then remove all of the ENTITY declarations. + entity_results = RE_ENTITY_PATTERN.findall(head) + head = RE_ENTITY_PATTERN.sub(_s2bytes(''), head) + + # Find the DOCTYPE declaration and check the feed type. + doctype_results = RE_DOCTYPE_PATTERN.findall(head) doctype = doctype_results and doctype_results[0] or _s2bytes('') - if doctype.lower().count(_s2bytes('netscape')): + if _s2bytes('netscape') in doctype.lower(): version = u'rss091n' else: version = None - # only allow in 'safe' inline entity definitions - replacement=_s2bytes('') - if len(doctype_results)==1 and entity_results: - safe_pattern=re.compile(_s2bytes('\s+(\w+)\s+"(&#\w+;|[^&"]*)"')) - safe_entities=filter(lambda e: safe_pattern.match(e),entity_results) - if safe_entities: - replacement=_s2bytes('\n \n]>') - data = doctype_pattern.sub(replacement, head) + data + # Re-insert the safe ENTITY declarations if a DOCTYPE was found. + replacement = _s2bytes('') + if len(doctype_results) == 1 and entity_results: + match_safe_entities = lambda e: RE_SAFE_ENTITY_PATTERN.match(e) + safe_entities = filter(match_safe_entities, entity_results) + if safe_entities: + replacement = _s2bytes('\n\n]>') + data = RE_DOCTYPE_PATTERN.sub(replacement, head) + data - return version, data, dict(replacement and [(k.decode('utf-8'), v.decode('utf-8')) for k, v in safe_pattern.findall(replacement)]) + # Precompute the safe entities for the loose parser. + safe_entities = dict((k.decode('utf-8'), v.decode('utf-8')) + for k, v in RE_SAFE_ENTITY_PATTERN.findall(replacement)) + return version, data, safe_entities def parse(url_file_stream_or_string, etag=None, modified=None, agent=None, referrer=None, handlers=None, request_headers=None, response_headers=None): '''Parse a feed from a URL, file, stream, or string. @@ -3714,41 +3872,51 @@ def parse(url_file_stream_or_string, etag=None, modified=None, agent=None, refer elif response_headers: result['headers'] = copy.deepcopy(response_headers) + # lowercase all of the HTTP headers for comparisons per RFC 2616 + if 'headers' in result: + http_headers = dict((k.lower(), v) for k, v in result['headers'].items()) + else: + http_headers = {} + # if feed is gzip-compressed, decompress it - if f and data and 'headers' in result: - if gzip and 'gzip' in (result['headers'].get('content-encoding'), result['headers'].get('Content-Encoding')): + if f and data and http_headers: + if gzip and 'gzip' in http_headers.get('content-encoding', ''): try: data = gzip.GzipFile(fileobj=_StringIO(data)).read() except (IOError, struct.error), e: - # IOError can occur if the gzip header is bad - # struct.error can occur if the data is damaged - # Some feeds claim to be gzipped but they're not, so - # we get garbage. Ideally, we should re-request the - # feed without the 'Accept-encoding: gzip' header, - # but we don't. + # IOError can occur if the gzip header is bad. + # struct.error can occur if the data is damaged. result['bozo'] = 1 result['bozo_exception'] = e - data = None - elif zlib and 'deflate' in (result['headers'].get('content-encoding'), result['headers'].get('Content-Encoding')): + if isinstance(e, struct.error): + # A gzip header was found but the data is corrupt. + # Ideally, we should re-request the feed without the + # 'Accept-encoding: gzip' header, but we don't. + data = None + elif zlib and 'deflate' in http_headers.get('content-encoding', ''): try: data = zlib.decompress(data) except zlib.error, e: - result['bozo'] = 1 - result['bozo_exception'] = e - data = None + try: + # The data may have no headers and no checksum. + data = zlib.decompress(data, -15) + except zlib.error, e: + result['bozo'] = 1 + result['bozo_exception'] = e # save HTTP headers - if 'headers' in result: - if 'etag' in result['headers'] or 'ETag' in result['headers']: - etag = result['headers'].get('etag', result['headers'].get('ETag', u'')) + if http_headers: + if 'etag' in http_headers: + etag = http_headers.get('etag', u'') if not isinstance(etag, unicode): etag = etag.decode('utf-8', 'ignore') if etag: result['etag'] = etag - if 'last-modified' in result['headers'] or 'Last-Modified' in result['headers']: - modified = result['headers'].get('last-modified', result['headers'].get('Last-Modified')) + if 'last-modified' in http_headers: + modified = http_headers.get('last-modified', u'') if modified: - result['modified'] = _parse_date(modified) + result['modified'] = modified + result['modified_parsed'] = _parse_date(modified) if hasattr(f, 'url'): if not isinstance(f.url, unicode): result['href'] = f.url.decode('utf-8', 'ignore') @@ -3763,118 +3931,29 @@ def parse(url_file_stream_or_string, etag=None, modified=None, agent=None, refer if data is None: return result - # there are four encodings to keep track of: - # - http_encoding is the encoding declared in the Content-Type HTTP header - # - xml_encoding is the encoding declared in the time.time() - dt): + if obj and (dt is None or obj[0] > time.time() - dt): value = obj[1] elif f is None: if obj: @@ -44,24 +45,24 @@ class MemcacheClient(object): obj = self.client.get(key) if obj: value = obj[1] + value - self.client.set(key, (time.time(), value)) + 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 134112a4..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: @@ -84,10 +87,3 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0): setattr(wrapper, '_autoretry_datastore_timeouts', False) if getattr(wrapped, '_autoretry_datastore_timeouts', True): apiproxy_stub_map.MakeSyncCall = wrapper - - - - - - - diff --git a/gluon/contrib/generics.py b/gluon/contrib/generics.py index 0c9c2a23..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,46 +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 9ea64ea6..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,15 +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 92b637e9..5438888b 100644 --- a/gluon/contrib/gql.py +++ b/gluon/contrib/gql.py @@ -1,12 +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 e8094458..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: @@ -57,5 +59,3 @@ def THUMB(image, nx=120, ny=120, gae=False, name='thumb'): return thumb else: return image - - 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/markmin/markmin2html.py b/gluon/contrib/markmin/markmin2html.py index 78b29748..94084b0b 100755 --- a/gluon/contrib/markmin/markmin2html.py +++ b/gluon/contrib/markmin/markmin2html.py @@ -545,14 +545,14 @@ regex_bq_headline=re.compile('^(?:(\.+|\++|\-+)(\.)?\s+)?(-{3}-*)$') regex_tq=re.compile('^(-{3}-*)(?::(?P[a-zA-Z][_a-zA-Z\-\d]*)(?:\[(?P

    [a-zA-Z][_a-zA-Z\-\d]*)\])?)?$') regex_proto = re.compile(r'(?/=])(?P

    \w+):(?P\w+://[\w\d\-+=?%&/:.]+)', re.M) regex_auto = re.compile(r'(?/=])(?P\w+://[\w\d\-+_=?%&/:.]+)',re.M) -regex_link=re.compile(r'('+LINK+r')|\[\[(?P.+?)\]\]') -regex_link_level2=re.compile(r'^(?P\S.*?)?(?:\s+\[(?P.+?)\])?(?:\s+(?P\S+))?(?:\s+(?P

    popup))?\s*$') -regex_media_level2=re.compile(r'^(?P\S.*?)?(?:\s+\[(?P.+?)\])?(?:\s+(?P\S+))?\s+(?P

    img|IMG|left|right|center|video|audio)(?:\s+(?P\d+px))?\s*$') +regex_link=re.compile(r'('+LINK+r')|\[\[(?P.+?)\]\]',re.S) +regex_link_level2=re.compile(r'^(?P\S.*?)?(?:\s+\[(?P.+?)\])?(?:\s+(?P\S+))?(?:\s+(?P

    popup))?\s*$',re.S) +regex_media_level2=re.compile(r'^(?P\S.*?)?(?:\s+\[(?P.+?)\])?(?:\s+(?P\S+))?\s+(?P

    img|IMG|left|right|center|video|audio)(?:\s+(?P\d+px))?\s*$',re.S) -regex_markmin_escape = re.compile(r"(\\*)(['`:*~\\[\]{}@\$+\-.#])") -regex_backslash = re.compile(r"\\(['`:*~\\[\]{}@\$+\-.#])") -ttab_in = maketrans("'`:*~\\[]{}@$+-.#", '\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b') -ttab_out = maketrans('\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b',"'`:*~\\[]{}@$+-.#") +regex_markmin_escape = re.compile(r"(\\*)(['`:*~\\[\]{}@\$+\-.#\n])") +regex_backslash = re.compile(r"\\(['`:*~\\[\]{}@\$+\-.#\n])") +ttab_in = maketrans("'`:*~\\[]{}@$+-.#\n", '\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x05') +ttab_out = maketrans('\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x05',"'`:*~\\[]{}@$+-.#\n") def markmin_escape(text): """ insert \\ before markmin control characters: '`:*~[]{}@$ """ @@ -686,6 +686,15 @@ def render(text, >>> render("----\\nhello world\\n----\\n") '

    hello world
    ' + >>> render('[[http://example.com]]') + '

    ' + + >>> render('[[ http://example.com]]') + '

    http://example.com

    ' + + >>> render('[[bookmark [http://example.com] ]]') + '

    http://example.com

    ' + >>> render('[[this is a link http://example.com]]') '

    this is a link

    ' @@ -701,6 +710,9 @@ def render(text, >>> render("[[Your browser doesn't support
    \n') for col in colnames: - out.write('\n') + out.write('\n') out.write('\n') out.write('
    '+str(row[col[0]][col[1]])+'' + str(row[col[0]][col[1]]) + '
    \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 4a1900d5..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 @@ -47,9 +48,8 @@ class Storage(dict): # http://stackoverflow.com/questions/5247250/why-does-pickle-getstate-accept-as-a-return-value-the-very-instance-it-requi __getstate__ = lambda self: None __copy__ = lambda self: Storage(self) - - - def getlist(self,key): + + def getlist(self, key): """ Return a Storage value as a list. @@ -69,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. @@ -94,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. @@ -116,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 @@ -204,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): """ @@ -236,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 179b8396..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,34 +208,36 @@ 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) # Indent - 1 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 @@ -504,7 +514,7 @@ class TemplateParser(object): self.content.nodes = [] t_content = t.content - + # Set our include, unique by filename t_content.blocks['__include__' + filename] = buf @@ -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: @@ -889,10 +911,3 @@ def render(content = "hello world", if __name__ == '__main__': import doctest doctest.testmod() - - - - - - - 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 8023171b..3b20d71d 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -25,25 +25,28 @@ import Cookie import cStringIO from email import MIMEBase, MIMEMultipart, MIMEText, Encoders, Header, message_from_string -from contenttype import contenttype -from storage import Storage, StorageList, Settings, Messages -from utils import web2py_uuid -from fileutils import read_file, check_credentials +from gluon.contenttype import contenttype +from gluon.storage import Storage, StorageList, Settings, Messages +from gluon.utils import web2py_uuid +from gluon.fileutils import read_file, check_credentials from gluon import * from gluon.contrib.autolinks import expand_one from gluon.contrib.markmin.markmin2html import \ replace_at_urls, replace_autolinks, replace_components from gluon.dal import Row -import serializers +import gluon.serializers as serializers try: - import json as json_parser # try stdlib (Python 2.6) + # try stdlib (Python 2.6) + import json as json_parser except ImportError: try: - import simplejson as json_parser # try external module + # try external module + import simplejson as json_parser except: - import contrib.simplejson as json_parser # fallback to pure-Python module + # fallback to pure-Python module + import contrib.simplejson as json_parser __all__ = ['Mail', 'Auth', 'Recaptcha', 'Crud', 'Service', 'Wiki', 'PluginManager', 'fetch', 'geocode', 'prettydate'] @@ -53,23 +56,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 +85,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 +100,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 +163,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 +277,7 @@ class Mail(object): encoding='utf-8', raw=False, headers={} - ): + ): """ Sends an email using data specified in constructor @@ -341,8 +350,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 +379,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 +394,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 +407,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 +424,6 @@ class Mail(object): else: payload_in.attach(attachments) - ####################################################### # CIPHER # ####################################################### @@ -431,7 +440,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 +452,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 +463,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 +498,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 +553,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 +572,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 +599,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 +614,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 +635,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: + 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 +653,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 +678,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 +717,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 +745,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 +754,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 +785,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 +803,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 +1053,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={} - return URL(c=self.settings.controller, - f=f, args=args, vars=vars,scheme=scheme) + if args is None: + args = [] + if vars is None: + vars = {} + return URL(c=self.settings.controller, + 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 +1086,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 +1100,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 - session.auth = None + 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 +1161,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 +1171,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 +1181,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 +1221,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 +1248,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 +1260,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 +1284,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 +1315,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')) - if self.user_id: + 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 +1361,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 +1394,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 +1404,23 @@ class Auth(object): request = current.request T = current.T reference_user = 'reference %s' % settings.table_user_name - def lazy_user (auth = self): - return auth.user_id - def represent(id,record=None,s=settings): + + def lazy_user(auth=self): + return auth.user_id + + 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 +1429,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,14 +1463,14 @@ 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 + signature_list = signature is_not_empty = IS_NOT_EMPTY(error_message=self.messages.is_empty) is_crypted = CRYPT(key=settings.hmac_key, min_length=settings.password_min_length) @@ -1433,34 +1478,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 +1526,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 +1554,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 +1586,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 +1596,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 +1621,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 +1629,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 +1651,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 +1701,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 +1718,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 +1728,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) @@ -1698,27 +1744,28 @@ class Auth(object): """ perform basic login. reads current.request.env.http_authorization - and returns basic_allowed,basic_accepted,user + 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 +1784,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 +1804,7 @@ class Auth(object): onaccept=DEFAULT, log=DEFAULT, version=2, - ): + ): request = current.request response = current.response session = current.session @@ -1765,13 +1812,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 +1828,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 +1866,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 +1900,7 @@ class Auth(object): onvalidation=DEFAULT, onaccept=DEFAULT, log=DEFAULT, - ): + ): """ returns a login form @@ -1878,8 +1929,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 +1950,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 +2009,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 +2062,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 +2072,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 +2088,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 +2098,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 +2146,7 @@ class Auth(object): onvalidation=DEFAULT, onaccept=DEFAULT, log=DEFAULT, - ): + ): """ returns a registration form @@ -2133,13 +2189,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 +2204,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,20 +2260,20 @@ 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='') session.flash = self.messages.registration_successful user = self.db( table_user[username] == form.vars[username] - ).select().first() + ).select().first() 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 +2309,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 +2324,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 +2350,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 +2367,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 +2375,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 +2393,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 +2406,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 +2449,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 +2458,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 +2517,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 +2532,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 +2571,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 +2590,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 +2598,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 +2608,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 +2616,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), - scheme=True) + 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 +2647,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 +2692,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 +2712,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 +2753,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 +2767,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,11 +2814,13 @@ 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)) - elif user_id in (0, '0') and self.is_impersonating(): - session.clear() - session.update(cPickle.loads(auth.impersonator)) - self.user = session.auth.user + 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() + session.update(cPickle.loads(auth.impersonator)) + self.user = session.auth.user + return None if requested_id is DEFAULT and not request.post_vars: return SQLFORM.factory(Field('user_id', 'integer')) return SQLFORM(table_user, user.id, readonly=True) @@ -2762,7 +2833,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: @@ -2776,11 +2848,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))) @@ -2794,7 +2867,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): @@ -2806,7 +2879,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: @@ -2816,16 +2889,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() @@ -2843,13 +2916,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. @@ -2857,7 +2930,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): @@ -2867,9 +2940,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. @@ -2877,7 +2950,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=''): """ @@ -2898,7 +2971,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): """ @@ -2909,7 +2982,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] @@ -2923,7 +2996,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 @@ -2933,7 +3005,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() @@ -2943,7 +3015,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): @@ -2956,11 +3028,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: @@ -2981,7 +3053,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() @@ -3004,8 +3076,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 @@ -3054,9 +3127,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: @@ -3102,25 +3174,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 @@ -3190,16 +3262,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) @@ -3213,8 +3285,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, @@ -3226,6 +3298,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): @@ -3233,16 +3306,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 @@ -3299,10 +3374,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': @@ -3314,7 +3389,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: @@ -3327,12 +3402,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( @@ -3408,26 +3483,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: @@ -3435,19 +3511,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( @@ -3507,7 +3583,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 @@ -3538,9 +3614,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) @@ -3565,8 +3641,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 @@ -3581,18 +3657,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:]] @@ -3603,7 +3679,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 @@ -3626,17 +3703,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: @@ -3651,7 +3728,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) ) """ @@ -3662,47 +3740,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, @@ -3713,29 +3795,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: @@ -3746,7 +3831,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 @@ -3760,6 +3845,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) @@ -3784,7 +3870,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. @@ -3990,17 +4077,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: @@ -4062,7 +4149,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 @@ -4083,7 +4170,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): @@ -4107,20 +4194,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} @@ -4136,7 +4224,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: @@ -4183,7 +4271,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() @@ -4200,17 +4288,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': @@ -4224,11 +4312,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), @@ -4236,9 +4324,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: @@ -4248,9 +4336,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} @@ -4327,20 +4415,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 '' @@ -4351,55 +4440,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): """ @@ -4454,7 +4545,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: @@ -4462,77 +4554,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)), @@ -4545,22 +4643,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'); @@ -4808,11 +4931,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()}); } }) @@ -4820,191 +4945,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, @@ -5012,7 +5150,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) @@ -5020,8 +5159,3 @@ class Wiki(object): if __name__ == '__main__': import doctest doctest.testmod() - - - - - diff --git a/gluon/utf8.py b/gluon/utf8.py index 1af3fe15..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,573 +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 13314c5e..3ef03909 100644 --- a/gluon/utils.py +++ b/gluon/utils.py @@ -21,6 +21,15 @@ import os import re import logging import socket +import cPickle +import base64 +import zlib + +try: + from Crypto.Cipher import AES +except ImportError: + from contrib import aes as AES + try: from contrib.pbkdf2 import pbkdf2_hex HAVE_PBKDF2 = True @@ -29,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 @@ -38,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": @@ -86,16 +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='.'): + return s + (32 - len(s) % 32) * padchar + + +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]) + encrypted_data = base64.urlsafe_b64encode(cipher.encrypt(pad(dump))) + signature = hmac.new(hash_key, encrypted_data).hexdigest() + return signature + ':' + encrypted_data + + +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: + return None + key = pad(encryption_key[:32]) + 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): + return None + ### compute constant CTOKENS + + def initialize_urandom(): """ This function and the web2py_uuid follow from the following discussion: @@ -111,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: @@ -129,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() @@ -147,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: @@ -161,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]) + byte_s = struct.pack('=QQ', + 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') @@ -185,36 +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 dbf59fc5..af3f6629 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) @@ -1756,6 +1770,7 @@ official_top_level_domains = [ 'xn--hlcj6aya9esc7a', 'xn--jxalpdlp', 'xn--kgbechtv', + 'xn--p1ai', 'xn--zckzah', 'ye', 'yt', @@ -1763,7 +1778,7 @@ official_top_level_domains = [ 'za', 'zm', 'zw', - ] +] class IS_HTTP_URL(Validator): @@ -1810,7 +1825,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 +1834,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 +1853,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 +1884,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 +1904,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 +2004,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 +2017,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 +2050,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 +2131,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 +2158,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 +2173,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 +2194,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 +2216,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 +2227,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 +2262,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 +2276,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 +2312,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 +2326,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 +2344,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 +2354,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 +2412,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 +2479,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 +2509,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 +2534,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 +2563,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 +2602,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 +2631,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 +2742,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 +2753,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 +2783,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 +2798,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') @@ -2797,7 +2819,7 @@ class IS_STRONG(object): """ - def __init__(self, min=None, max=None, upper=None, lower=None, number=None, + def __init__(self, min=None, max=None, upper=None, lower=None, number=None, entropy=None, special=None, specials=r'~!@#$%^&*()_+-=?<>,.:;{}[]|', invalid=' "', error_message=None, es=False): @@ -2828,8 +2850,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 +2862,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 +2896,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 +3074,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 +3217,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 +3256,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,12 +3271,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 241e9fff..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 @@ -804,12 +810,12 @@ def console(): default=False, help=msg) - parser.add_option('-N', - '--no-cron', + parser.add_option('-Y', + '--run-cron', action='store_true', - dest='nocron', + dest='runcron', default=False, - help='do not start cron automatically') + help='start the background cron process') parser.add_option('-J', '--cronjob', @@ -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 = [] @@ -904,7 +911,7 @@ def console(): if options.cronjob: global_settings.cronjob = True # tell the world - options.nocron = True # don't start cron jobs + options.run = False # don't start cron jobs options.plain = True # cronjobs use a plain shell options.nobanner = True options.nogui = True @@ -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,14 +1092,13 @@ def start(cron=True): pass return - - # ## if -N or not cron disable cron in this *process* + # ## if -H cron is enabled in this *process* # ## if --softcron use softcron # ## use hardcron in all other cases - if cron and not options.nocron and options.softcron: + if cron and options.runcron and options.softcron: print 'Using softcron (but this is not very efficient)' global_settings.web2py_crontype = 'soft' - elif cron and not options.nocron: + elif cron and options.runcron: logger.debug('Starting hardcron...') global_settings.web2py_crontype = 'hard' newcron.hardcron(options.folder).start() @@ -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: @@ -1218,12 +1228,3 @@ end tell except: pass logging.shutdown() - - - - - - - - - diff --git a/gluon/winservice.py b/gluon/winservice.py index 3b518214..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,24 +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/gluon/xmlrpc.py b/gluon/xmlrpc.py index 6bf931cb..7fa29978 100644 --- a/gluon/xmlrpc.py +++ b/gluon/xmlrpc.py @@ -19,10 +19,3 @@ def handler(request, response, methods): response.headers['Content-Type'] = 'text/xml' dispatch = getattr(dispatcher, '_dispatch', None) return dispatcher._marshaled_dispatch(request.body.read(), dispatch) - - - - - - - diff --git a/isapiwsgihandler.py b/isapiwsgihandler.py index 28973d2d..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,15 +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 4ed135f1..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 @@ -222,8 +222,3 @@ def handler(req): Handler(req).run(gluon.main.wsgibase) return apache.OK - - - - - diff --git a/options_std.py b/options_std.py index 85cd071b..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 @@ -31,4 +32,3 @@ shutdown_timeout = 5 folder = os.getcwd() extcron = None nocron = None - diff --git a/router.example.py b/router.example.py index fef9737c..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. @@ -209,8 +210,3 @@ def __routes_doctest(): if __name__ == '__main__': import doctest doctest.testmod() - - - - - diff --git a/routes.example.py b/routes.example.py index f44d05bf..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'), + ('/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. @@ -197,8 +198,3 @@ def __routes_doctest(): if __name__ == '__main__': import doctest doctest.testmod() - - - - - diff --git a/scgihandler.py b/scgihandler.py index e0dd6593..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, @@ -72,7 +72,3 @@ if SOFTCRON: # uncomment one of the two rows below depending on the SCGIWSGI server installed #scgi.serve_application(application, '', 4000).run() SCGIServer(application, port=4000).enable_sighandler().run() - - - - diff --git a/scripts/autoroutes.py b/scripts/autoroutes.py index c1119ab0..aaef82dd 100644 --- a/scripts/autoroutes.py +++ b/scripts/autoroutes.py @@ -40,48 +40,58 @@ domain3.com /app3/defcon3 ''' if not config.strip(): try: - config_file = open('routes.conf','r') + config_file = open('routes.conf', 'r') try: config = config_file.read() finally: config_file.close() except: - config='' + config = '' + def auto_in(apps): routes = [ - ('/robots.txt','/welcome/static/robots.txt'), - ('/favicon.ico','/welcome/static/favicon.ico'), - ('/admin$anything','/admin$anything'), - ] - for domain,path in [x.strip().split() for x in apps.split('\n') if x.strip() and not x.strip().startswith('#')]: - if not path.startswith('/'): path = '/'+path - if path.endswith('/'): path = path[:-1] + ('/robots.txt', '/welcome/static/robots.txt'), + ('/favicon.ico', '/welcome/static/favicon.ico'), + ('/admin$anything', '/admin$anything'), + ] + for domain, path in [x.strip().split() for x in apps.split('\n') if x.strip() and not x.strip().startswith('#')]: + if not path.startswith('/'): + path = '/' + path + if path.endswith('/'): + path = path[:-1] app = path.split('/')[1] routes += [ - ('.*:https?://(.*\.)?%s:$method /' % domain,'%s' % path), - ('.*:https?://(.*\.)?%s:$method /static/$anything' % domain,'/%s/static/$anything' % app), - ('.*:https?://(.*\.)?%s:$method /appadmin/$anything' % domain,'/%s/appadmin/$anything' % app), - ('.*:https?://(.*\.)?%s:$method /$anything' % domain,'%s/$anything' % path), - ] + ('.*:https?://(.*\.)?%s:$method /' % domain, '%s' % path), + ('.*:https?://(.*\.)?%s:$method /static/$anything' % + domain, '/%s/static/$anything' % app), + ('.*:https?://(.*\.)?%s:$method /appadmin/$anything' % + domain, '/%s/appadmin/$anything' % app), + ('.*:https?://(.*\.)?%s:$method /$anything' % + domain, '%s/$anything' % path), + ] return routes + def auto_out(apps): routes = [] - for domain,path in [x.strip().split() for x in apps.split('\n') if x.strip() and not x.strip().startswith('#')]: - if not path.startswith('/'): path = '/'+path - if path.endswith('/'): path = path[:-1] + for domain, path in [x.strip().split() for x in apps.split('\n') if x.strip() and not x.strip().startswith('#')]: + if not path.startswith('/'): + path = '/' + path + if path.endswith('/'): + path = path[:-1] app = path.split('/')[1] routes += [ - ('/%s/static/$anything' % app,'/static/$anything'), + ('/%s/static/$anything' % app, '/static/$anything'), ('/%s/appadmin/$anything' % app, '/appadmin/$anything'), ('%s/$anything' % path, '/$anything'), - ] + ] return routes routes_in = auto_in(config) routes_out = auto_out(config) + def __routes_doctest(): ''' Dummy function for doctesting autoroutes.py. @@ -128,7 +138,8 @@ if __name__ == '__main__': try: import gluon.main except ImportError: - import sys, os + import sys + import os os.chdir(os.path.dirname(os.path.dirname(__file__))) sys.path.append(os.path.dirname(os.path.dirname(__file__))) import gluon.main @@ -138,4 +149,3 @@ if __name__ == '__main__': import doctest doctest.testmod() - diff --git a/scripts/bench.py b/scripts/bench.py index ffb98452..75e1917b 100644 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -5,11 +5,12 @@ import urllib2 n = int(sys.argv[1]) url = sys.argv[2] -headers = {"Accept-Language" : "en" } +headers = {"Accept-Language": "en"} req = urllib2.Request(url, None, headers) t0 = time.time() for k in xrange(n): data = urllib2.urlopen(req).read() -print (time.time()-t0)/n -if n==1: print data +print (time.time() - t0) / n +if n == 1: + print data diff --git a/scripts/cleancss.py b/scripts/cleancss.py index 7380773f..c2d2a113 100755 --- a/scripts/cleancss.py +++ b/scripts/cleancss.py @@ -8,26 +8,25 @@ filename = sys.argv[1] datafile = open(filename, 'r') try: - data = '\n'+datafile.read() + data = '\n' + datafile.read() finally: datafile.close() SPACE = '\n ' if '-n' in sys.argv[1:] else ' ' -data = re.compile('(?<!\:)//(?P<a>.*)').sub('/* \g<a> */',data) +data = re.compile('(?<!\:)//(?P<a>.*)').sub('/* \g<a> */', data) data = re.compile('[ ]+').sub(' ', data) -data = re.compile('\s*{\s*').sub(' {'+SPACE, data) -data = re.compile('\s*;\s*').sub(';'+SPACE, data) +data = re.compile('\s*{\s*').sub(' {' + SPACE, data) +data = re.compile('\s*;\s*').sub(';' + SPACE, data) data = re.compile(',\s*').sub(', ', data) -data = re.compile('\s*\*/\s*').sub('*/'+SPACE, data) -data = re.compile('\s*}\s*').sub(SPACE+'}\n', data) +data = re.compile('\s*\*/\s*').sub('*/' + SPACE, data) +data = re.compile('\s*}\s*').sub(SPACE + '}\n', data) data = re.compile('\n\s*\n').sub('\n', data) -data = re.compile(';\s+/\*').sub('; /*',data) -data = re.compile('\*/\s+/\*').sub(' ',data) +data = re.compile(';\s+/\*').sub('; /*', data) +data = re.compile('\*/\s+/\*').sub(' ', data) data = re.compile('[ ]+\n').sub('\n', data) -data = re.compile('\n\s*/[\*]+(?P<a>.*?)[\*]+/',re.DOTALL).sub( - '\n/*\g<a>*/\n',data) -data = re.compile('[ \t]+(?P<a>\S.+?){').sub('\g<a>{',data) -data = data.replace('}','}\n') +data = re.compile('\n\s*/[\*]+(?P<a>.*?)[\*]+/', re.DOTALL).sub( + '\n/*\g<a>*/\n', data) +data = re.compile('[ \t]+(?P<a>\S.+?){').sub('\g<a>{', data) +data = data.replace('}', '}\n') print data - diff --git a/scripts/cleanhtml.py b/scripts/cleanhtml.py index 0bbfe823..e0df55b2 100755 --- a/scripts/cleanhtml.py +++ b/scripts/cleanhtml.py @@ -1,51 +1,55 @@ import sys import re + def cleancss(text): - text=re.compile('\s+').sub(' ', text) - text=re.compile('\s*(?P<a>,|:)\s*').sub('\g<a> ', text) - text=re.compile('\s*;\s*').sub(';\n ', text) - text=re.compile('\s*\{\s*').sub(' {\n ', text) - text=re.compile('\s*\}\s*').sub('\n}\n\n', text) + text = re.compile('\s+').sub(' ', text) + text = re.compile('\s*(?P<a>,|:)\s*').sub('\g<a> ', text) + text = re.compile('\s*;\s*').sub(';\n ', text) + text = re.compile('\s*\{\s*').sub(' {\n ', text) + text = re.compile('\s*\}\s*').sub('\n}\n\n', text) return text + def cleanhtml(text): - text=text.lower() - r=re.compile('\<script.+?/script\>', re.DOTALL) - scripts=r.findall(text) - text=r.sub('<script />', text) - r=re.compile('\<style.+?/style\>', re.DOTALL) - styles=r.findall(text) - text=r.sub('<style />', text) - text=re.compile( + text = text.lower() + r = re.compile('\<script.+?/script\>', re.DOTALL) + scripts = r.findall(text) + text = r.sub('<script />', text) + r = re.compile('\<style.+?/style\>', re.DOTALL) + styles = r.findall(text) + text = r.sub('<style />', text) + text = re.compile( '<(?P<tag>(input|meta|link|hr|br|img|param))(?P<any>[^\>]*)\s*(?<!/)>')\ .sub('<\g<tag>\g<any> />', text) - text=text.replace('\n', ' ') - text=text.replace('>', '>\n') - text=text.replace('<', '\n<') - text=re.compile('\s*\n\s*').sub('\n', text) - lines=text.split('\n') - (indent, newlines)=(0, []) + text = text.replace('\n', ' ') + text = text.replace('>', '>\n') + text = text.replace('<', '\n<') + text = re.compile('\s*\n\s*').sub('\n', text) + lines = text.split('\n') + (indent, newlines) = (0, []) for line in lines: - if line[:2]=='</': indent=indent-1 - newlines.append(indent*' '+line) - if not line[:2]=='</' and line[-1:]=='>' and \ - not line[-2:] in ['/>', '->']: indent=indent+1 - text='\n'.join(newlines) - text=re.compile('\<div(?P<a>( .+)?)\>\s+\</div\>').sub('<div\g<a>></div>',text) - text=re.compile('\<a(?P<a>( .+)?)\>\s+(?P<b>[\w\s\(\)\/]+?)\s+\</a\>').sub('<a\g<a>>\g<b></a>',text) - text=re.compile('\<b(?P<a>( .+)?)\>\s+(?P<b>[\w\s\(\)\/]+?)\s+\</b\>').sub('<b\g<a>>\g<b></b>',text) - text=re.compile('\<i(?P<a>( .+)?)\>\s+(?P<b>[\w\s\(\)\/]+?)\s+\</i\>').sub('<i\g<a>>\g<b></i>',text) - text=re.compile('\<span(?P<a>( .+)?)\>\s+(?P<b>[\w\s\(\)\/]+?)\s+\</span\>').sub('<span\g<a>>\g<b></span>',text) - text=re.compile('\s+\<br(?P<a>.*?)\/\>').sub('<br\g<a>/>',text) - text=re.compile('\>(?P<a>\s+)(?P<b>[\.\,\:\;])').sub('>\g<b>\g<a>',text) - text=re.compile('\n\s*\n').sub('\n',text) + if line[:2] == '</': indent = indent - 1 + newlines.append(indent * ' ' + line) + if not line[:2] == '</' and line[-1:] == '>' and \ + not line[-2:] in ['/>', '->']: indent = indent + 1 + text = '\n'.join(newlines) + text = re.compile( + '\<div(?P<a>( .+)?)\>\s+\</div\>').sub('<div\g<a>></div>', text) + text = re.compile('\<a(?P<a>( .+)?)\>\s+(?P<b>[\w\s\(\)\/]+?)\s+\</a\>').sub('<a\g<a>>\g<b></a>', text) + text = re.compile('\<b(?P<a>( .+)?)\>\s+(?P<b>[\w\s\(\)\/]+?)\s+\</b\>').sub('<b\g<a>>\g<b></b>', text) + text = re.compile('\<i(?P<a>( .+)?)\>\s+(?P<b>[\w\s\(\)\/]+?)\s+\</i\>').sub('<i\g<a>>\g<b></i>', text) + text = re.compile('\<span(?P<a>( .+)?)\>\s+(?P<b>[\w\s\(\)\/]+?)\s+\</span\>').sub('<span\g<a>>\g<b></span>', text) + text = re.compile('\s+\<br(?P<a>.*?)\/\>').sub('<br\g<a>/>', text) + text = re.compile('\>(?P<a>\s+)(?P<b>[\.\,\:\;])').sub('>\g<b>\g<a>', text) + text = re.compile('\n\s*\n').sub('\n', text) for script in scripts: - text=text.replace('<script />', script, 1) + text = text.replace('<script />', script, 1) for style in styles: - text=text.replace('<style />', cleancss(style), 1) + text = text.replace('<style />', cleancss(style), 1) return text + def read_file(filename): f = open(filename, 'r') try: @@ -53,9 +57,8 @@ def read_file(filename): finally: f.close() -file=sys.argv[1] -if file[-4:]=='.css': +file = sys.argv[1] +if file[-4:] == '.css': print cleancss(read_file(file)) -if file[-5:]=='.html': +if file[-5:] == '.html': print cleanhtml(read_file(file)) - diff --git a/scripts/cleanjs.py b/scripts/cleanjs.py index 6a09b979..c15d2a67 100644 --- a/scripts/cleanjs.py +++ b/scripts/cleanjs.py @@ -1,17 +1,17 @@ import re + def cleanjs(text): - text = re.sub('\s*}\s*','\n}\n',text) - text = re.sub('\s*{\s*',' {\n',text) - text = re.sub('\s*;\s*',';\n',text) - text = re.sub('\s*,\s*',', ',text) - text = re.sub('\s*(?P<a>[\+\-\*/\=]+)\s*',' \g<a> ',text) + text = re.sub('\s*}\s*', '\n}\n', text) + text = re.sub('\s*{\s*', ' {\n', text) + text = re.sub('\s*;\s*', ';\n', text) + text = re.sub('\s*,\s*', ', ', text) + text = re.sub('\s*(?P<a>[\+\-\*/\=]+)\s*', ' \g<a> ', text) lines = text.split('\n') - text='' - indent=0 + text = '' + indent = 0 for line in lines: - rline=line.strip() + rline = line.strip() if rline: pass return text - diff --git a/scripts/contentparser.py b/scripts/contentparser.py index f679d65d..89385e2d 100755 --- a/scripts/contentparser.py +++ b/scripts/contentparser.py @@ -22,7 +22,7 @@ Internet connection is required to perform the update. OVERRIDE = [ ('.pdb', 'chemical/x-pdb'), ('.xyz', 'chemical/x-pdb') - ] +] class MIMEParser(dict): @@ -72,14 +72,15 @@ if __name__ == '__main__': sys.stdout.write('Checking freedesktop.org database version:') sys.stdout.flush() try: - search = re.search('(?P<url>http://freedesktop.org/.+?/shared-mime-info-(?P<version>.+?)\.tar\.(?P<type>[gb]z2?))', + search = re.search( + '(?P<url>http://freedesktop.org/.+?/shared-mime-info-(?P<version>.+?)\.tar\.(?P<type>[gb]z2?))', urllib.urlopen('http://www.freedesktop.org/wiki/Software/shared-mime-info').read()) url = search.group('url') - assert url != None + assert url is not None nversion = search.group('version') - assert nversion != None + assert nversion is not None ftype = search.group('type') - assert ftype != None + assert ftype is not None sys.stdout.write('\t[OK] version %s\n' % nversion) except: sys.stdout.write('\t[ERROR] unknown version\n') @@ -129,4 +130,3 @@ if __name__ == '__main__': sys.stdout.write('\t\t\t[OK] done\n') except Exception, e: sys.stdout.write('\t\t\t[ERROR] %s\n' % e) - diff --git a/scripts/cpdb.py b/scripts/cpdb.py index a269bdf1..0a2bb7f8 100644 --- a/scripts/cpdb.py +++ b/scripts/cpdb.py @@ -1,269 +1,278 @@ -import os,sys -from collections import deque -import string +import os +import sys +from collections import deque +import string import argparse -import cStringIO,operator +import cStringIO +import operator import cPickle as pickle from collections import deque import math import re import cmd import readline -try: +try: from gluon import DAL except ImportError as err: - print('gluon path not found') + print('gluon path not found') + class refTable(object): def __init__(self): self.columns = None self.rows = None - - def getcolHeader(self,colHeader): + + def getcolHeader(self, colHeader): return "{0}".format(' | '.join([string.join(string.strip('**{0}**'.format(item)), - '') for item in colHeader])) - - - def wrapTable(self,rows, hasHeader=False, headerChar='-', delim=' | ', justify='left', - separateRows=False, prefix='', postfix='', wrapfunc=lambda x:x): - + '') for item in colHeader])) + + def wrapTable( + self, rows, hasHeader=False, headerChar='-', delim=' | ', justify='left', + separateRows=False, prefix='', postfix='', wrapfunc=lambda x: x): + def rowWrapper(row): - + '''--- - newRows is returned like + newRows is returned like [['w'], ['x'], ['y'], ['z']] - ---''' + ---''' newRows = [wrapfunc(item).split('\n') for item in row] self.rows = newRows '''--- - rowList gives like newRows but + rowList gives like newRows but formatted like [[w, x, y, z]] ---''' - rowList = [[substr or '' for substr in item] for item in map(None,*newRows)] + rowList = [[substr or '' for substr in item] + for item in map(None, *newRows)] return rowList - + logicalRows = [rowWrapper(row) for row in rows] - - columns = map(None,*reduce(operator.add,logicalRows)) - self.columns = columns - - maxWidths = [max(\ - [len(str\ - (item)) for \ - item in column]\ - ) for column \ - in columns] - - rowSeparator = headerChar * (len(prefix) + len(postfix) + sum(maxWidths) + \ - len(delim)*(len(maxWidths)-1)) - - justify = {'center'\ - :str\ - .center, - 'right'\ - :str\ - .rjust, - 'left'\ - :str.\ - ljust\ - }[justify\ - .lower(\ - )] - - output=cStringIO.StringIO() - - if separateRows: + + columns = map(None, *reduce(operator.add, logicalRows)) + self.columns = columns + + maxWidths = [max( + [len(str + (item)) for + item in column] + ) for column + in columns] + + rowSeparator = headerChar * (len(prefix) + len(postfix) + sum(maxWidths) + + len(delim) * (len(maxWidths) - 1)) + + justify = {'center': str + .center, + 'right': str + .rjust, + 'left': str. + ljust + }[justify + .lower( + )] + + output = cStringIO.StringIO() + + if separateRows: print >> output, rowSeparator - + for physicalRows in logicalRows: for row in physicalRows: print >> output,\ - prefix + delim.join([\ - justify(str(item),width) for (\ - item,width) in zip(row,maxWidths)]\ - ) + postfix - - if separateRows or hasHeader: - print >> output, rowSeparator; hasHeader=False + prefix + delim.join([ + justify(str(item), width) for ( + item, width) in zip(row, maxWidths)] + ) + postfix + + if separateRows or hasHeader: + print >> output, rowSeparator + hasHeader = False return output.getvalue() - - def wrap_onspace(self,text,width): - return reduce(lambda line, word, width=width: '{0}{1}{2}'\ - .format(line\ - ,' \n'[(len(\ - line[line.rfind('\n'\ - ) + 1:]) + len(\ - word.split('\n',1)[0]) >=\ - width)],word),text.split(' ')) - - def wrap_onspace_strict(self,text,width): - wordRegex = re.compile(r'\S{'+str(width)+r',}') - return self.wrap_onspace(\ - wordRegex.sub(\ - lambda m: self.\ - wrap_always(\ - m.group(),width),text\ - ),width) - - def wrap_always(self,text,width): - return '\n'.join(\ - [ text[width*i:width*(i+1\ - )] for i in xrange(\ - int(math.ceil(1.*len(\ - text)/width))) ]) + + def wrap_onspace(self, text, width): + return reduce(lambda line, word, width=width: '{0}{1}{2}' + .format(line, ' \n'[(len( + line[line.rfind('\n' + ) + 1:]) + len( + word.split('\n', 1)[0]) >= + width)], word), text.split(' ')) + + def wrap_onspace_strict(self, text, width): + wordRegex = re.compile(r'\S{' + str(width) + r',}') + return self.wrap_onspace( + wordRegex.sub( + lambda m: self. + wrap_always( + m.group(), width), text + ), width) + + def wrap_always(self, text, width): + return '\n'.join( + [text[width * i:width * (i + 1 + )] for i in xrange( + int(math.ceil(1. * len( + text) / width)))]) + class tableHelper(): - def __init__(self): + def __init__(self): self.oTable = refTable() - - def getAsRows(self,data): - return [row.strip().split(',') for row in data.splitlines()] - - def getTable_noWrap(self,data,header=None): + + def getAsRows(self, data): + return [row.strip().split(',') for row in data.splitlines()] + + def getTable_noWrap(self, data, header=None): rows = self.getAsRows(data) - if header is not None:hRows = [header]+rows - else:hRows = rows + if header is not None: + hRows = [header] + rows + else: + hRows = rows table = self.oTable.wrapTable(hRows, hasHeader=True) return table - - def getTable_Wrap(self,data,wrapStyle,header=None,width=65): - wrapper = None - if len(wrapStyle) > 1: + + def getTable_Wrap(self, data, wrapStyle, header=None, width=65): + wrapper = None + if len(wrapStyle) > 1: rows = self.getAsRows(data) - if header is not None:hRows = [header]+rows - else:hRows = rows - + if header is not None: + hRows = [header] + rows + else: + hRows = rows + for wrapper in (self.oTable.wrap_always, - self.oTable.wrap_onspace, - self.oTable.wrap_onspace_strict): - return self.oTable.wrapTable(hRows\ - ,hasHeader=True\ - ,separateRows=True\ - ,prefix='| '\ - ,postfix=' |'\ - ,wrapfunc\ - =lambda x:\ - wrapper(x,width)) + self.oTable.wrap_onspace, + self.oTable.wrap_onspace_strict): + return self.oTable.wrapTable(hRows, hasHeader=True, separateRows=True, prefix='| ', postfix=' |', wrapfunc=lambda x: + wrapper(x, width)) else: - return self.getTable_noWrap(data,header) - - def getAsErrorTable(self,err): - return self.getTable_Wrap(err,None) + return self.getTable_noWrap(data, header) + + def getAsErrorTable(self, err): + return self.getTable_Wrap(err, None) class console: - def __init__(self,prompt,banner=None): - self.prompt=prompt - self.banner=banner - self.commands={} - self.commandSort=[] - self.db=None + def __init__(self, prompt, banner=None): + self.prompt = prompt + self.banner = banner + self.commands = {} + self.commandSort = [] + self.db = None for i in dir(self): - if "cmd_"==i[:4]: - cmd=i.split("cmd_")[1].lower() - self.commands[cmd]=getattr(self,i) - try:self.commandSort.append((int(self\ - .commands[cmd].__doc__.split(\ - "|")[0]),cmd)) - except:pass - + if "cmd_" == i[:4]: + cmd = i.split("cmd_")[1].lower() + self.commands[cmd] = getattr(self, i) + try: + self.commandSort.append((int(self + .commands[cmd].__doc__.split( + "|")[0]), cmd)) + except: + pass + self.commandSort.sort() - self.commandSort=[i[1] for i in self.commandSort] - - self.var_DEBUG=False - self.var_tableStyle='' + self.commandSort = [i[1] for i in self.commandSort] - self.configvars={} + self.var_DEBUG = False + self.var_tableStyle = '' + + self.configvars = {} for i in dir(self): - if "var_"==i[:4]: - var=i.split("var_")[1] - self.configvars[var]=i + if "var_" == i[:4]: + var = i.split("var_")[1] + self.configvars[var] = i - def setBanner(self,banner): - self.banner=banner + def setBanner(self, banner): + self.banner = banner - def execCmd(self,db): - self.db=db - print self.banner + def execCmd(self, db): + self.db = db + print self.banner while True: try: - command=raw_input(self.prompt) + command = raw_input(self.prompt) try: self.execCommand(command) except: self.execute(command) - except KeyboardInterrupt:break - except EOFError:break - except Exception,a:self.printError (a) + except KeyboardInterrupt: + break + except EOFError: + break + except Exception, a: + self.printError(a) print ("\r\n\r\nBye!...") sys.exit(0) - def printError(self,err): + def printError(self, err): sys.stderr.write("Error: {0}\r\n".format(str(err),)) - if self.var_DEBUG:pass + if self.var_DEBUG: + pass - def execute(self,cmd): + def execute(self, cmd): try: if not '-table ' in cmd: exec '{0}'.format(cmd) - else: - file=None - table=None + else: + file = None + table = None - fields=[] - items=string.split(cmd,' ') - invalidParams=[] - table=self.getTable(items[1]) - allowedParams=['fields','file'] + fields = [] + items = string.split(cmd, ' ') + invalidParams = [] + table = self.getTable(items[1]) + allowedParams = ['fields', 'file'] for i in items: - if '=' in i and not string.split(i,'=')[0] in allowedParams: + if '=' in i and not string.split(i, '=')[0] in allowedParams: try: invalidParams.append(i) except Exception, err: - raise Exception, 'invalid parameter\n{0}'.format(i) + raise Exception('invalid parameter\n{0}'.format(i)) else: if 'file=' in i: - file=os.path.abspath(string.strip(string.split(i,'=')[1])) + file = os.path.abspath(string.strip(string.split( + i, '=')[1])) if 'fields=' in i: - for field in string.split(string.split(i,'=')[1],','): + for field in string.split(string.split(i, '=')[1], ','): if field in self.db[table].fields: fields.append(string.strip(field)) - - if len(invalidParams)>0: - print('the following parameter(s) is not valid\n{0}'.format(\ - string.join(invalidParams,','))) + + if len(invalidParams) > 0: + print('the following parameter(s) is not valid\n{0}'.format( + string.join(invalidParams, ','))) else: try: - self.cmd_table(table,file,fields) + self.cmd_table(table, file, fields) except Exception, err: - print('could not generate table for table {0}\n{1}'\ - .format(table,err)) + print('could not generate table for table {0}\n{1}' + .format(table, err)) except Exception, err: print('sorry, can not do that!\n{0}'.format(err)) - - def getTable(self,tbl): + + def getTable(self, tbl): for mTbl in db.tables: if tbl in mTbl: if mTbl.startswith(tbl): - return mTbl - - def execCommand(self,cmd): - words=cmd.split(" ") - words=[i for i in words if i] - if not words:return - cmd,parameters=words[0].lower(),words[1:] + return mTbl + + def execCommand(self, cmd): + words = cmd.split(" ") + words = [i for i in words if i] + if not words: + return + cmd, parameters = words[0].lower(), words[1:] if not cmd in self.commands: - raise Exception("Command {0} not found. Try 'help'\r\n".format(cmd)) + raise Exception( + "Command {0} not found. Try 'help'\r\n".format(cmd)) self.commands[cmd](*parameters) - + '''--- DEFAULT COMMANDS (begins with cmd_) ---''' - def cmd_clear(self,numlines=100): + def cmd_clear(self, numlines=100): """-5|clear|clear the screen""" if os.name == "posix": '''--- @@ -279,124 +288,132 @@ class console: '''--- Fallback for other operating systems. ---''' - print '\n'*numlines + print '\n' * numlines - def cmd_table(self,tbl,file=None,fields=[]): + def cmd_table(self, tbl, file=None, fields=[]): """-4|-table [TABLENAME] optional[file=None] [fields=None]|\ the default tableStyle is no_wrap - use the 'set x y' command to change the style\n\ style choices: -\twrap_always +\twrap_always \twrap_onspace \twrap_onspace_strict \tno_wrap (value '')\n \t the 2nd optional param is a path to a file where the table will be written \t the 3rd optional param is a list of fields you want displayed\n""" - table=None + table = None for mTbl in db.tables: if tbl in mTbl: if mTbl.startswith(tbl): - table=mTbl - break - oTable=tableHelper() - '''--- + table = mTbl + break + oTable = tableHelper() + '''--- tablestyle: - wrap_always - wrap_onspace - wrap_onspace_strict - or set set to "" for no wrapping + wrap_always + wrap_onspace + wrap_onspace_strict + or set set to "" for no wrapping ---''' - tableStyle=self.var_tableStyle - filedNotFound=[] - table_fields=None - if len(fields)==0: - table_fields=self.db[table].fields + tableStyle = self.var_tableStyle + filedNotFound = [] + table_fields = None + if len(fields) == 0: + table_fields = self.db[table].fields else: - table_fields=fields - + table_fields = fields + for field in fields: if not field in self.db[table].fields: filedNotFound.append(field) - if len(filedNotFound)==0: - rows=self.db(self.db[table].id>0).select() - rows_data=[] + if len(filedNotFound) == 0: + rows = self.db(self.db[table].id > 0).select() + rows_data = [] for row in rows: - rowdata=[] + rowdata = [] for f in table_fields: rowdata.append('{0}'.format(row[f])) - rows_data.append(string.join(rowdata,',')) - data=string.join(rows_data,'\n') - dataTable=oTable.getTable_Wrap(data,tableStyle,table_fields) - print('TABLE {0}\n{1}'.format(table,dataTable)) - if file!=None: + rows_data.append(string.join(rowdata, ',')) + data = string.join(rows_data, '\n') + dataTable = oTable.getTable_Wrap(data, tableStyle, table_fields) + print('TABLE {0}\n{1}'.format(table, dataTable)) + if file is not None: try: - tail,head=os.path.split(file) + tail, head = os.path.split(file) try: os.makedirs(tail) - except:'do nothing, folders exist' - oFile=open(file,'w') - oFile.write('TABLE: {0}\n{1}'.format(table,dataTable)) + except: + 'do nothing, folders exist' + oFile = open(file, 'w') + oFile.write('TABLE: {0}\n{1}'.format(table, dataTable)) oFile.close() - print('{0} has been created and populated with all available data from table {1}\n'.format(file,table)) + print('{0} has been created and populated with all available data from table {1}\n'.format(file, table)) except Exception, err: - print("EXCEPTION: could not create table {0}\n{1}".format(table,err)) + print("EXCEPTION: could not create table {0}\n{1}".format( + table, err)) else: - print('the following fields are not valid [{0}]'.format(string.join(filedNotFound,','))) - - def cmd_help(self,*args): - '''-3|help|Show's help''' - alldata=[] - lengths=[] + print('the following fields are not valid [{0}]'.format( + string.join(filedNotFound, ','))) - for i in self.commandSort:alldata.append(\ - self.commands[i].__doc__.split("|")[1:]) + def cmd_help(self, *args): + '''-3|help|Show's help''' + alldata = [] + lengths = [] + + for i in self.commandSort: + alldata.append( + self.commands[i].__doc__.split("|")[1:]) for i in alldata: if len(i) > len(lengths): - for j in range(len(i)\ - -len(lengths)): + for j in range(len(i) + - len(lengths)): lengths.append(0) - j=0 - while j<len(i): - if len(i[j])>lengths[j]: - lengths[j]=len(i[j]) - j+=1 - - print ("-"*(lengths[0]+lengths[1]+4)) - for i in alldata: - print (("%-"+str(lengths[0])+"s - %-"+str(lengths[1])+"s") % (i[0],i[1])) - if len(i)>2: - for j in i[2:]:print (("%"+str(lengths[0]+9)+"s* %s") % (" ",j)) + j = 0 + while j < len(i): + if len(i[j]) > lengths[j]: + lengths[j] = len(i[j]) + j += 1 + + print ("-" * (lengths[0] + lengths[1] + 4)) + for i in alldata: + print (("%-" + str(lengths[0]) + "s - %-" + str( + lengths[1]) + "s") % (i[0], i[1])) + if len(i) > 2: + for j in i[2:]: print (("%" + str(lengths[ + 0] + 9) + "s* %s") % (" ", j)) print - def cmd_vars(self,*args): + def cmd_vars(self, *args): '''-2|vars|Show variables''' - print ("variables\r\n"+"-"*79) - for i,j in self.configvars.items(): - value=self.parfmt(repr(getattr(self,j)),52) - print ("| %20s | %52s |" % (i,value[0])) - for k in value[1:]:print ("| %20s | %52s |" % ("",k)) - if len(value)>1:print("| %20s | %52s |" % ("","")) - print ("-"*79) - - def parfmt(self,txt,width): - res=[] - pos=0 + print ("variables\r\n" + "-" * 79) + for i, j in self.configvars.items(): + value = self.parfmt(repr(getattr(self, j)), 52) + print ("| %20s | %52s |" % (i, value[0])) + for k in value[1:]: print ("| %20s | %52s |" % ("", k)) + if len(value) > 1: + print("| %20s | %52s |" % ("", "")) + print ("-" * 79) + + def parfmt(self, txt, width): + res = [] + pos = 0 while True: - a=txt[pos:pos+width] - if not a:break + a = txt[pos:pos + width] + if not a: + break res.append(a) - pos+=width + pos += width return res - - def cmd_set(self,*args): + + def cmd_set(self, *args): '''-1|set [variable_name] [value]|Set configuration variable value|Values are an expressions (100 | string.lower('ABC') | etc.''' - value=" ".join(args[1:]) + value = " ".join(args[1:]) if args[0] not in self.configvars: - setattr(self,"var_{0}".format(args[0]),eval(value)) - setattr(self,"var_{0}".format(args[0]),eval(value)) - - def cmd_clearscreen(self,numlines=50): + setattr(self, "var_{0}".format(args[0]), eval(value)) + setattr(self, "var_{0}".format(args[0]), eval(value)) + + def cmd_clearscreen(self, numlines=50): '''---Clear the console. ---''' if os.name == "posix": @@ -413,75 +430,81 @@ style choices: '''--- Fallback for other operating systems. ---''' - print '\n'*numlines + print '\n' * numlines + class dalShell(console): def __init__(self): pass - - def shell(self,db): - console.__init__(self,prompt=">>> ",banner='dal interactive shell') + + def shell(self, db): + console.__init__(self, prompt=">>> ", banner='dal interactive shell') self.execCmd(db) + class setCopyDB(): def __init__(self): '''--- non source or target specific vars - ---''' - self.strModel=None - self.dalPath=None - self.db=None + ---''' + self.strModel = None + self.dalPath = None + self.db = None '''--- source vars - ---''' - self.sourceModel=None - self.sourceFolder=None - self.sourceConnectionString=None - self.sourcedbType=None - self.sourcedbName=None + ---''' + self.sourceModel = None + self.sourceFolder = None + self.sourceConnectionString = None + self.sourcedbType = None + self.sourcedbName = None '''--- target vars ---''' - self.targetdbType=None - self.targetdbName=None - self.targetModel=None - self.targetFolder=None - self.targetConnectionString=None - self.truncate=False + self.targetdbType = None + self.targetdbName = None + self.targetModel = None + self.targetFolder = None + self.targetConnectionString = None + self.truncate = False def _getDal(self): - mDal=None + mDal = None if self.dalPath is not None: global DAL - sys.path.append(self.dalPath) - mDal=__import__('dal',globals={},locals={},fromlist=['DAL'],level=0) - DAL=mDal.DAL - return mDal - - def instDB(self,storageFolder,storageConnectionString,autoImport): - self.db=DAL(storageConnectionString,folder=os.path.abspath(storageFolder),auto_import=autoImport) + sys.path.append(self.dalPath) + mDal = __import__( + 'dal', globals={}, locals={}, fromlist=['DAL'], level=0) + DAL = mDal.DAL + return mDal + + def instDB(self, storageFolder, storageConnectionString, autoImport): + self.db = DAL(storageConnectionString, folder=os.path.abspath( + storageFolder), auto_import=autoImport) return self.db - - def delete_DB_tables(self,storageFolder,storageType): - print 'delete_DB_tablesn\n\t{0}\n\t{1}'.format(storageFolder,storageType) - dataFiles=[storageType,"sql.log"] + + def delete_DB_tables(self, storageFolder, storageType): + print 'delete_DB_tablesn\n\t{0}\n\t{1}'.format( + storageFolder, storageType) + dataFiles = [storageType, "sql.log"] try: for f in os.listdir(storageFolder): - if ".table" in f: - fTable="{0}/{1}".format(storageFolder,f) - os.remove(fTable) - print('deleted {0}'.format(fTable)) - for dFile in dataFiles: - os.remove("{0}/{1}".format(storageFolder,dFile)) - print('deleted {0}'.format("{0}/{1}".format(storageFolder,dFile))) + if ".table" in f: + fTable = "{0}/{1}".format(storageFolder, f) + os.remove(fTable) + print('deleted {0}'.format(fTable)) + for dFile in dataFiles: + os.remove("{0}/{1}".format(storageFolder, dFile)) + print('deleted {0}'.format( + "{0}/{1}".format(storageFolder, dFile))) except Exception, errObj: - print(str(errObj)) - - def truncatetables(self,tables=[]): - if len(tables)!=0: + print(str(errObj)) + + def truncatetables(self, tables=[]): + if len(tables) != 0: try: - print 'table value: {0}'.format(tables) - for tbl in self.db.tables: + print 'table value: {0}'.format(tables) + for tbl in self.db.tables: for mTbl in tables: if mTbl.startswith(tbl): self.db[mTbl].truncate() @@ -490,64 +513,61 @@ class setCopyDB(): else: try: for tbl in self.db.tables: - self.db[tbl].truncate() + self.db[tbl].truncate() except Exception, err: - print('EXCEPTION: {0}'.format(err)) - + print('EXCEPTION: {0}'.format(err)) + def copyDB(self): - other_db=DAL("{0}://{1}".format(self.targetdbType,self.targetdbName),folder=self.targetFolder) - + other_db = DAL("{0}://{1}".format( + self.targetdbType, self.targetdbName), folder=self.targetFolder) + print 'creating tables...' - + for table in self.db: - other_db.define_table(table._tablename,*[field for field in table]) + other_db.define_table( + table._tablename, *[field for field in table]) ''' should there be an option to truncAte target DB? if yes, then change args to allow for choice and set self.trancate to the art value - + if self.truncate==True: other_db[table._tablename].truncate() ''' - + print 'exporting data...' - self.db.export_to_csv_file(open('tmp.sql','wb')) - + self.db.export_to_csv_file(open('tmp.sql', 'wb')) + print 'importing data...' - other_db.import_from_csv_file(open('tmp.sql','rb')) + other_db.import_from_csv_file(open('tmp.sql', 'rb')) other_db.commit() print 'done!' print 'Attention: do not run this program again or you end up with duplicate records' - def createfolderPath(self,folder): + def createfolderPath(self, folder): try: - if folder!=None:os.makedirs(folder) + if folder is not None: + os.makedirs(folder) except Exception, err: - pass + pass if __name__ == '__main__': - oCopy=setCopyDB() - db=None - targetDB=None - dbfolder=None - clean=False - model=None - truncate=False + oCopy = setCopyDB() + db = None + targetDB = None + dbfolder = None + clean = False + model = None + truncate = False - parser=argparse.ArgumentParser(description='\ + parser = argparse.ArgumentParser(description='\ samplecmd line:\n\ -f ./blueLite/db_storage -i -y sqlite://storage.sqlite -Y sqlite://storage2.sqlite -d ./blueLite/pyUtils/sql/blueSQL -t True', - epilog = '') - reqGroup=parser.add_argument_group('Required arguments') - reqGroup.add_argument('-f','--sourceFolder'\ - ,required=True\ - ,help="path to the 'source' folder of the 'source' DB") - reqGroup.add_argument('-F','--targetFolder'\ - ,required=False\ - ,help="path to the 'target' folder of the 'target' DB") - reqGroup.add_argument('-y','--sourceConnectionString'\ - ,required=True\ - ,help="source db connection string ()\n\ + epilog='') + reqGroup = parser.add_argument_group('Required arguments') + reqGroup.add_argument('-f', '--sourceFolder', required=True, help="path to the 'source' folder of the 'source' DB") + reqGroup.add_argument('-F', '--targetFolder', required=False, help="path to the 'target' folder of the 'target' DB") + reqGroup.add_argument('-y', '--sourceConnectionString', required=True, help="source db connection string ()\n\ ------------------------------------------------\n\ \ sqlite://storage.db\n\ @@ -561,19 +581,16 @@ ingres://username:password@localhost/test\n\ informix://username:password@test\n\ \ ------------------------------------------------") - reqGroup.add_argument('-Y','--targetConnectionString'\ - ,required=True\ - ,help="target db type (sqlite,mySql,etc.)") - autoImpGroup=parser.add_argument_group('optional args (auto_import)') - autoImpGroup.add_argument('-a','--autoimport'\ - ,required=False\ - ,help='set to True to bypass loading of the model') - + reqGroup.add_argument('-Y', '--targetConnectionString', required=True, + help="target db type (sqlite,mySql,etc.)") + autoImpGroup = parser.add_argument_group('optional args (auto_import)') + autoImpGroup.add_argument('-a', '--autoimport', required=False, help='set to True to bypass loading of the model') + """ - - *** removing -m/-M options for now --> i need a + + *** removing -m/-M options for now --> i need a better regex to match db.define('bla')...with optional db.commit() - + modelGroup=parser.add_argument_group('optional args (create model)') modelGroup.add_argument('-m','--sourcemodel'\ ,required=False\ @@ -581,72 +598,64 @@ informix://username:password@test\n\ modelGroup.add_argument('-M','--targetmodel'\ ,required=False\ ,help='to create a model from an existing model, point to the target model') - + """ - - miscGroup=parser.add_argument_group('optional args/tasks') - miscGroup.add_argument('-i','--interactive'\ - ,required=False\ - ,action='store_true'\ - ,help='run in interactive mode') - miscGroup.add_argument('-d','--dal'\ - ,required=False\ - ,help='path to dal.py') - miscGroup.add_argument('-t','--truncate'\ - ,choices=['True','False']\ - ,help='delete the records but *not* the table of the SOURCE DB') - miscGroup.add_argument('-b','--tables'\ - ,required=False\ - ,type=list\ - ,help='optional list (comma delimited) of SOURCE tables to truncate, defaults to all') - miscGroup.add_argument('-c','--clean'\ - ,required=False\ - ,help='delete the DB,tables and the log file, WARNING: this is unrecoverable') - - args=parser.parse_args() - db=None - mDal=None + miscGroup = parser.add_argument_group('optional args/tasks') + miscGroup.add_argument('-i', '--interactive', required=False, action='store_true', help='run in interactive mode') + miscGroup.add_argument( + '-d', '--dal', required=False, help='path to dal.py') + miscGroup.add_argument('-t', '--truncate', choices=['True', 'False'], help='delete the records but *not* the table of the SOURCE DB') + miscGroup.add_argument('-b', '--tables', required=False, type=list, help='optional list (comma delimited) of SOURCE tables to truncate, defaults to all') + miscGroup.add_argument('-c', '--clean', required=False, help='delete the DB,tables and the log file, WARNING: this is unrecoverable') + + args = parser.parse_args() + db = None + mDal = None try: - oCopy.sourceFolder=args.sourceFolder - oCopy.targetFolder=args.sourceFolder - sourceItems=string.split(args.sourceConnectionString,'://') - oCopy.sourcedbType=sourceItems[0] - oCopy.sourcedbName=sourceItems[1] - targetItems=string.split(args.targetConnectionString,'://') - oCopy.targetdbType=targetItems[0] - oCopy.targetdbName=targetItems[1] + oCopy.sourceFolder = args.sourceFolder + oCopy.targetFolder = args.sourceFolder + sourceItems = string.split(args.sourceConnectionString, '://') + oCopy.sourcedbType = sourceItems[0] + oCopy.sourcedbName = sourceItems[1] + targetItems = string.split(args.targetConnectionString, '://') + oCopy.targetdbType = targetItems[0] + oCopy.targetdbName = targetItems[1] except Exception, err: print('EXCEPTION: {0}'.format(err)) if args.dal: - try: - autoImport=True - if args.autoimport:autoImport=args.autoimport + try: + autoImport = True + if args.autoimport: + autoImport = args.autoimport #sif not DAL in globals: #if not sys.path.__contains__(): - oCopy.dalPath=args.dal - mDal=oCopy._getDal() - db=oCopy.instDB(args.sourceFolder,args.sourceConnectionString,autoImport) + oCopy.dalPath = args.dal + mDal = oCopy._getDal() + db = oCopy.instDB(args.sourceFolder, args.sourceConnectionString, + autoImport) except Exception, err: - print('EXCEPTION: could not set DAL\n{0}'.format(err)) + print('EXCEPTION: could not set DAL\n{0}'.format(err)) if args.truncate: try: if args.truncate: - if args.tables:tables=string.split(string.strip(args.tables),',') - else:oCopy.truncatetables([]) + if args.tables: + tables = string.split(string.strip(args.tables), ',') + else: + oCopy.truncatetables([]) except Exception, err: print('EXCEPTION: could not truncate tables\n{0}'.format(err)) try: - if args.clean:oCopy.delete_DB_tables(oCopy.targetFolder,oCopy.targetType) + if args.clean: + oCopy.delete_DB_tables(oCopy.targetFolder, oCopy.targetType) except Exception, err: print('EXCEPTION: could not clean db\n{0}'.format(err)) - """ *** goes with -m/-M options... removed for now - + if args.sourcemodel: try: oCopy.sourceModel=args.sourcemodel @@ -658,25 +667,26 @@ source model: {0}\n\ target model: {1}\n\ {2}'.format(args.sourcemodel,args.targetmodel,err)) """ - + if args.sourceFolder: try: - oCopy.sourceFolder=os.path.abspath(args.sourceFolder) + oCopy.sourceFolder = os.path.abspath(args.sourceFolder) oCopy.createfolderPath(oCopy.sourceFolder) except Exception, err: - print('EXCEPTION: could not create folder path\n{0}'.format(err)) - else:oCopy.dbStorageFolder=os.path.abspath(os.getcwd()) + print('EXCEPTION: could not create folder path\n{0}'.format(err)) + else: + oCopy.dbStorageFolder = os.path.abspath(os.getcwd()) if args.targetFolder: try: - oCopy.targetFolder=os.path.abspath(args.targetFolder) + oCopy.targetFolder = os.path.abspath(args.targetFolder) oCopy.createfolderPath(oCopy.targetFolder) except Exception, err: - print('EXCEPTION: could not create folder path\n{0}'.format(err)) + print('EXCEPTION: could not create folder path\n{0}'.format(err)) if not args.interactive: - try: + try: oCopy.copyDB() except Exception, err: - print('EXCEPTION: could not make a copy of the database\n{0}'.format(err)) + print('EXCEPTION: could not make a copy of the database\n{0}'.format(err)) else: - s=dalShell() + s = dalShell() s.shell(db) diff --git a/scripts/cpplugin.py b/scripts/cpplugin.py index fbb311c4..652b8747 100644 --- a/scripts/cpplugin.py +++ b/scripts/cpplugin.py @@ -1,26 +1,32 @@ -import sys, glob, os, shutil -name=sys.argv[1] -app=sys.argv[2] -dest=sys.argv[3] -a=glob.glob('applications/%(app)s/*/plugin_%(name)s.*' % dict(app=app,name=name)) -b=glob.glob('applications/%(app)s/*/plugin_%(name)s/*' % dict(app=app,name=name)) +import sys +import glob +import os +import shutil +name = sys.argv[1] +app = sys.argv[2] +dest = sys.argv[3] +a = glob.glob( + 'applications/%(app)s/*/plugin_%(name)s.*' % dict(app=app, name=name)) +b = glob.glob( + 'applications/%(app)s/*/plugin_%(name)s/*' % dict(app=app, name=name)) for f in a: print 'cp %s ...' % f, - shutil.copyfile(f,os.path.join('applications',dest,*f.split('/')[2:])) + shutil.copyfile(f, os.path.join('applications', dest, *f.split('/')[2:])) print 'done' for f in b: print 'cp %s ...' % f, path = f.split('/') - for i in range(3,len(path)): - try: os.mkdir(os.path.join('applications',dest,*path[2:i])) - except: pass - path = os.path.join('applications',dest,*f.split('/')[2:]) + for i in range(3, len(path)): + try: + os.mkdir(os.path.join('applications', dest, *path[2:i])) + except: + pass + path = os.path.join('applications', dest, *f.split('/')[2:]) if os.path.isdir(f): if not os.path.exists(path): - shutil.copytree(f,path) + shutil.copytree(f, path) else: - shutil.copyfile(f,path) + shutil.copyfile(f, path) print 'done' - diff --git a/scripts/dbsessions2trash.py b/scripts/dbsessions2trash.py index 7226166c..a8ccd69d 100644 --- a/scripts/dbsessions2trash.py +++ b/scripts/dbsessions2trash.py @@ -10,17 +10,16 @@ DB_URI = 'sqlite://sessions.sqlite' EXPIRATION_MINUTES = 60 SLEEP_MINUTES = 5 -while 1: # Infinite loop - now = time() # get current Unix timestamp +while 1: # Infinite loop + now = time() # get current Unix timestamp for row in db().select(db.web2py_session_welcome.ALL): t = row.modified_datetime # Convert to a Unix timestamp - t = mktime(t.timetuple())+1e-6*t.microsecond + t = mktime(t.timetuple()) + 1e-6 * t.microsecond if now - t > EXPIRATION_MINUTES * 60: del db.web2py_session_welcome[row.id] - db.commit() # Write changes to database + db.commit() # Write changes to database sleep(SLEEP_MINUTES * 60) - diff --git a/scripts/dict_diff.py b/scripts/dict_diff.py index b3af81e1..f11b41ce 100644 --- a/scripts/dict_diff.py +++ b/scripts/dict_diff.py @@ -6,11 +6,11 @@ @license: MIT @since: 2011-06-17 -Usage: dict_diff [OPTION]... dict1 dict2 +Usage: dict_diff [OPTION]... dict1 dict2 Show the differences for two dictionaries. -h, --help Display this help message. - + dict1 and dict2 are two web2py dictionary files to compare. These are the files located in the "languages" directory of a web2py app. The tools show the differences between the two files. @@ -22,34 +22,38 @@ import getopt import os.path import sys + def main(argv): """Parse the arguments and start the main process.""" - try: + try: opts, args = getopt.getopt(argv, "h", ["help"]) except getopt.GetoptError: exit_with_parsing_error() for opt, arg in opts: arg = arg # To avoid a warning from Pydev if opt in ("-h", "--help"): - usage() + usage() sys.exit() if len(args) == 2: params = list(get_dicts(*args)) params.extend(get_dict_names(*args)) compare_dicts(*params) else: - exit_with_parsing_error() + exit_with_parsing_error() -def exit_with_parsing_error(): + +def exit_with_parsing_error(): """Report invalid arguments and usage.""" print("Invalid argument(s).") usage() sys.exit(2) + def usage(): """Display the documentation""" print(__doc__) + def get_dicts(dict_path1, dict_path2): """ Parse the dictionaries. @@ -58,7 +62,8 @@ def get_dicts(dict_path1, dict_path2): @return: The two dictionaries as a sequence. """ - return eval(open(dict_path1).read()), eval(open(dict_path2).read()) + return eval(open(dict_path1).read()), eval(open(dict_path2).read()) + def get_dict_names(dict1_path, dict2_path): """ @@ -75,7 +80,8 @@ def get_dict_names(dict1_path, dict2_path): dict1_name = "dict1" dict2_name = "dict2" return dict1_name, dict2_name - + + def compare_dicts(dict1, dict2, dict1_name, dict2_name): """ Compare the two dictionaries. Print out the result. @@ -100,7 +106,8 @@ def compare_dicts(dict1, dict2, dict1_name, dict2_name): has_value_differences = True if not has_value_differences: print " None" - + + def print_key_diff(key_diff, dict1_name, dict2_name): """ Prints the keys in the first dictionary and are in the second dictionary. @@ -116,6 +123,6 @@ def print_key_diff(key_diff, dict1_name, dict2_name): else: print " None" print - + if __name__ == "__main__": main(sys.argv[1:]) # Start the process (without the application name) diff --git a/scripts/extract_mysql_models.py b/scripts/extract_mysql_models.py index b39c27c6..82efb9a5 100644 --- a/scripts/extract_mysql_models.py +++ b/scripts/extract_mysql_models.py @@ -24,45 +24,48 @@ import subprocess import re import sys data_type_map = dict( - varchar = 'string', - int = 'integer', - integer = 'integer', - tinyint = 'integer', - smallint = 'integer', - mediumint = 'integer', - bigint = 'integer', - float = 'double', - double = 'double', - char = 'string', - decimal = 'integer', - date = 'date', + varchar='string', + int='integer', + integer='integer', + tinyint='integer', + smallint='integer', + mediumint='integer', + bigint='integer', + float='double', + double='double', + char='string', + decimal='integer', + date='date', #year = 'date', - time = 'time', - timestamp = 'datetime', - datetime = 'datetime', - binary = 'blob', - blob = 'blob', - tinyblob = 'blob', - mediumblob = 'blob', - longblob = 'blob', - text = 'text', - tinytext = 'text', - mediumtext = 'text', - longtext = 'text', - ) + time='time', + timestamp='datetime', + datetime='datetime', + binary='blob', + blob='blob', + tinyblob='blob', + mediumblob='blob', + longblob='blob', + text='text', + tinytext='text', + mediumtext='text', + longtext='text', +) + def mysql(database_name, username, password): p = subprocess.Popen(['mysql', '--user=%s' % username, - '--password=%s'% password, + '--password=%s' % password, '--execute=show tables;', database_name], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) sql_showtables, stderr = p.communicate() - tables = [re.sub('\|\s+([^\|*])\s+.*', '\1', x) for x in sql_showtables.split()[1:]] - connection_string = "legacy_db = DAL('mysql://%s:%s@localhost/%s')"%(username, password, database_name) + tables = [re.sub( + '\|\s+([^\|*])\s+.*', '\1', x) for x in sql_showtables.split()[1:]] + connection_string = "legacy_db = DAL('mysql://%s:%s@localhost/%s')" % ( + username, password, database_name) legacy_db_table_web2py_code = [] for table_name in tables: #get the sql create statement @@ -71,37 +74,40 @@ def mysql(database_name, username, password): '--password=%s' % password, '--skip-add-drop-table', '--no-data', database_name, - table_name], stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE) - sql_create_stmnt,stderr = p.communicate() - if 'CREATE' in sql_create_stmnt:#check if the table exists + table_name], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + sql_create_stmnt, stderr = p.communicate() + if 'CREATE' in sql_create_stmnt: # check if the table exists #remove garbage lines from sql statement sql_lines = sql_create_stmnt.split('\n') - sql_lines = [x for x in sql_lines if not(x.startswith('--') or x.startswith('/*') or x =='')] + sql_lines = [x for x in sql_lines if not( + x.startswith('--') or x.startswith('/*') or x == '')] #generate the web2py code from the create statement web2py_table_code = '' - table_name = re.search('CREATE TABLE .(\S+). \(', sql_lines[0]).group(1) + table_name = re.search( + 'CREATE TABLE .(\S+). \(', sql_lines[0]).group(1) fields = [] for line in sql_lines[1:-1]: if re.search('KEY', line) or re.search('PRIMARY', line) or re.search(' ID', line) or line.startswith(')'): continue hit = re.search('(\S+)\s+(\S+)(,| )( .*)?', line) - if hit!=None: + if hit is not None: name, d_type = hit.group(1), hit.group(2) - d_type = re.sub(r'(\w+)\(.*',r'\1',d_type) - name = re.sub('`','',name) - web2py_table_code += "\n Field('%s','%s'),"%(name,data_type_map[d_type]) - web2py_table_code = "legacy_db.define_table('%s',%s\n migrate=False)"%(table_name,web2py_table_code) + d_type = re.sub(r'(\w+)\(.*', r'\1', d_type) + name = re.sub('`', '', name) + web2py_table_code += "\n Field('%s','%s')," % ( + name, data_type_map[d_type]) + web2py_table_code = "legacy_db.define_table('%s',%s\n migrate=False)" % (table_name, web2py_table_code) legacy_db_table_web2py_code.append(web2py_table_code) #---------------------------------------- #write the legacy db to file - legacy_db_web2py_code = connection_string+"\n\n" - legacy_db_web2py_code += "\n\n#--------\n".join(legacy_db_table_web2py_code) + legacy_db_web2py_code = connection_string + "\n\n" + legacy_db_web2py_code += "\n\n#--------\n".join( + legacy_db_table_web2py_code) return legacy_db_web2py_code regex = re.compile('(.*?):(.*?)@(.*)') -if len(sys.argv)<2 or not regex.match(sys.argv[1]): +if len(sys.argv) < 2 or not regex.match(sys.argv[1]): print 'USAGE:\n\n extract_mysql_models.py username:password@data_basename\n\n' else: m = regex.match(sys.argv[1]) - print mysql(m.group(3),m.group(1),m.group(2)) - + print mysql(m.group(3), m.group(1), m.group(2)) diff --git a/scripts/extract_pgsql_models.py b/scripts/extract_pgsql_models.py index a6b80016..79a1699f 100644 --- a/scripts/extract_pgsql_models.py +++ b/scripts/extract_pgsql_models.py @@ -46,19 +46,21 @@ KWARGS = ('type', 'length', 'default', 'required', 'ondelete', import sys -def query(conn, sql,*args): +def query(conn, sql, *args): "Execute a SQL query and return rows as a list of dicts" cur = conn.cursor() ret = [] try: - if DEBUG: print >> sys.stderr, "QUERY: ", sql % args + if DEBUG: + print >> sys.stderr, "QUERY: ", sql % args cur.execute(sql, args) for row in cur: dic = {} for i, value in enumerate(row): field = cur.description[i][0] dic[field] = value - if DEBUG: print >> sys.stderr, "RET: ", dic + if DEBUG: + print >> sys.stderr, "RET: ", dic ret.append(dic) return ret finally: @@ -75,7 +77,8 @@ def get_tables(conn, schema=SCHEMA): def get_fields(conn, table): "Retrieve field list for a given table" - if DEBUG: print >> sys.stderr, "Processing TABLE", table + if DEBUG: + print >> sys.stderr, "Processing TABLE", table rows = query(conn, """ SELECT column_name, data_type, is_nullable, @@ -90,13 +93,13 @@ def get_fields(conn, table): def define_field(conn, table, field, pks): "Determine field type, default value, references, etc." - f={} + f = {} ref = references(conn, table, field['column_name']) if ref: f.update(ref) elif field['column_default'] and \ - field['column_default'].startswith("nextval") and \ - field['column_name'] in pks: + field['column_default'].startswith("nextval") and \ + field['column_name'] in pks: # postgresql sequence (SERIAL) and primary key! f['type'] = "'id'" elif field['data_type'].startswith('character'): @@ -109,7 +112,7 @@ def define_field(conn, table, field, pks): f['type'] = "'boolean'" elif field['data_type'] in ('integer', 'smallint', 'bigint'): f['type'] = "'integer'" - elif field['data_type'] in ('double precision', 'real' ): + elif field['data_type'] in ('double precision', 'real'): f['type'] = "'double'" elif field['data_type'] in ('timestamp', 'timestamp without time zone'): f['type'] = "'datetime'" @@ -124,17 +127,17 @@ def define_field(conn, table, field, pks): elif field['data_type'] in ('bytea', ): f['type'] = "'blob'" elif field['data_type'] in ('point', 'lseg', 'polygon', 'unknown', 'USER-DEFINED'): - f['type'] = "" # unsupported? + f['type'] = "" # unsupported? else: raise RuntimeError("Data Type not supported: %s " % str(field)) try: if field['column_default']: - if field['column_default']=="now()": + if field['column_default'] == "now()": d = "request.now" - elif field['column_default']=="true": + elif field['column_default'] == "true": d = "True" - elif field['column_default']=="false": + elif field['column_default'] == "false": d = "False" else: d = repr(eval(field['column_default'])) @@ -142,7 +145,8 @@ def define_field(conn, table, field, pks): except (ValueError, SyntaxError): pass except Exception, e: - raise RuntimeError("Default unsupported '%s'" % field['column_default']) + raise RuntimeError( + "Default unsupported '%s'" % field['column_default']) if not field['is_nullable']: f['notnull'] = "True" @@ -203,40 +207,40 @@ def references(conn, table, field): AND information_schema.key_column_usage.column_name=%s AND information_schema.table_constraints.constraint_type='FOREIGN KEY' ;""", table, field) - if len(rows1)==1: + if len(rows1) == 1: rows2 = query(conn, """ SELECT table_name, column_name, * FROM information_schema.constraint_column_usage WHERE constraint_name=%s """, rows1[0]['constraint_name']) row = None - if len(rows2)>1: - row = rows2[int(rows1[0]['ordinal_position'])-1] + if len(rows2) > 1: + row = rows2[int(rows1[0]['ordinal_position']) - 1] keyed = True - if len(rows2)==1: + if len(rows2) == 1: row = rows2[0] keyed = False if row: - if keyed: # THIS IS BAD, DON'T MIX "id" and primarykey!!! + if keyed: # THIS IS BAD, DON'T MIX "id" and primarykey!!! ref = {'type': "'reference %s.%s'" % (row['table_name'], row['column_name'])} else: ref = {'type': "'reference %s'" % (row['table_name'],)} - if rows1[0]['delete_rule']!="NO ACTION": + if rows1[0]['delete_rule'] != "NO ACTION": ref['ondelete'] = repr(rows1[0]['delete_rule']) return ref elif rows2: raise RuntimeError("Unsupported foreign key reference: %s" % - str(rows2)) + str(rows2)) elif rows1: raise RuntimeError("Unsupported referential constraint: %s" % - str(rows1)) + str(rows1)) def define_table(conn, table): "Output single table definition" - fields = get_fields(conn, table) + fields = get_fields(conn, table) pks = primarykeys(conn, table) print "db.define_table('%s'," % (table, ) for field in fields: @@ -244,11 +248,11 @@ def define_table(conn, table): fdef = define_field(conn, table, field, pks) if fname not in pks and is_unique(conn, table, field): fdef['unique'] = "True" - if fdef['type']=="'id'" and fname in pks: + if fdef['type'] == "'id'" and fname in pks: pks.pop(pks.index(fname)) print " Field('%s', %s)," % (fname, - ', '.join(["%s=%s" % (k, fdef[k]) for k in KWARGS - if k in fdef and fdef[k]])) + ', '.join(["%s=%s" % (k, fdef[k]) for k in KWARGS + if k in fdef and fdef[k]])) if pks: print " primarykey=[%s]," % ", ".join(["'%s'" % pk for pk in pks]) print " migrate=migrate)" @@ -280,5 +284,3 @@ if __name__ == "__main__": ) # Start model code generation: define_db(cnn, db, host, port, user, passwd) - - diff --git a/scripts/fixws.py b/scripts/fixws.py index 744657d7..706a413d 100755 --- a/scripts/fixws.py +++ b/scripts/fixws.py @@ -1,4 +1,6 @@ -import sys, glob +import sys +import glob + def read_fileb(filename, mode='rb'): f = open(filename, mode) @@ -7,6 +9,7 @@ def read_fileb(filename, mode='rb'): finally: f.close() + def write_fileb(filename, value, mode='wb'): f = open(filename, mode) try: @@ -17,8 +20,8 @@ def write_fileb(filename, value, mode='wb'): for filename in glob.glob(sys.argv[1]): data1 = read_fileb(filename) write_fileb(filename + '.bak2', data1) - data2lines = read_fileb(filename).split('\n') - data2 = '\n'.join([line.rstrip().replace('\t',' '*2) for line in data2lines])+'\n' + data2lines = read_fileb(filename).strip().split('\n') + data2 = '\n'.join([line.rstrip( + ).replace('\t', ' ' * 2) for line in data2lines]) + '\n' write_fileb(filename, data2) - print filename, len(data1)-len(data2) - + print filename, len(data1) - len(data2) diff --git a/scripts/layout_make.py b/scripts/layout_make.py index 84c86910..fe8baf34 100755 --- a/scripts/layout_make.py +++ b/scripts/layout_make.py @@ -5,10 +5,11 @@ import sys import re from BeautifulSoup import BeautifulSoup as BS + def head(styles): title = '<title>{{=response.title or request.application}}' items = '\n'.join(["{{response.files.append(URL(request.application,'static','%s'))}}" % (style) for style in styles]) - loc="""""" - return "\n%s\n%s\n{{include 'web2py_ajax.html'}}\n%s" % (title,items,loc) + return "\n%s\n%s\n{{include 'web2py_ajax.html'}}\n%s" % (title, items, loc) + def content(): return """
    {{=response.flash or ''}}
    {{include}}""" + def process(folder): - indexfile = open(os.path.join(folder,'index.html'),'rb') + indexfile = open(os.path.join(folder, 'index.html'), 'rb') try: soup = BS(indexfile.read()) finally: indexfile.close() styles = [x['href'] for x in soup.findAll('link')] - soup.find('head').contents=BS(head(styles)) + soup.find('head').contents = BS(head(styles)) try: - soup.find('h1').contents=BS('{{=response.title or request.application}}') - soup.find('h2').contents=BS("{{=response.subtitle or '=response.subtitle'}}") + soup.find( + 'h1').contents = BS('{{=response.title or request.application}}') + soup.find('h2').contents = BS( + "{{=response.subtitle or '=response.subtitle'}}") except: pass - for match in (soup.find('div',id='menu'), - soup.find('div',{'class':'menu'}), - soup.find('div',id='nav'), - soup.find('div',{'class':'nav'})): + for match in (soup.find('div', id='menu'), + soup.find('div', {'class': 'menu'}), + soup.find('div', id='nav'), + soup.find('div', {'class': 'nav'})): if match: - match.contents=BS('{{=MENU(response.menu)}}') + match.contents = BS('{{=MENU(response.menu)}}') break - done=False - for match in (soup.find('div',id='content'), - soup.find('div',{'class':'content'}), - soup.find('div',id='main'), - soup.find('div',{'class':'main'})): + done = False + for match in (soup.find('div', id='content'), + soup.find('div', {'class': 'content'}), + soup.find('div', id='main'), + soup.find('div', {'class': 'main'})): if match: - match.contents=BS(content()) - done=True + match.contents = BS(content()) + done = True break if done: page = soup.prettify() - page = re.compile("\s*\{\{=response\.flash or ''\}\}\s*",re.MULTILINE)\ - .sub("{{=response.flash or ''}}",page) + page = re.compile("\s*\{\{=response\.flash or ''\}\}\s*", re.MULTILINE)\ + .sub("{{=response.flash or ''}}", page) print page else: - raise Exception, "Unable to convert" + raise Exception("Unable to convert") -if __name__=='__main__': - if len(sys.argv)<2: +if __name__ == '__main__': + if len(sys.argv) < 2: print """USAGE: 1) start a new web2py application 2) Download a sample free layout from the web into the static/ folder of @@ -96,4 +101,3 @@ if __name__=='__main__': print 'Folder %s does not exist' % sys.argv[1] else: process(sys.argv[1]) - diff --git a/scripts/make_min_web2py.py b/scripts/make_min_web2py.py index 4c3fec52..9fd0c19c 100644 --- a/scripts/make_min_web2py.py +++ b/scripts/make_min_web2py.py @@ -38,51 +38,60 @@ gluon/contrib/pyrtf/ gluon/contrib/pysimplesoap/ """ -import sys, os, shutil, glob +import sys +import os +import shutil +import glob + def main(): global REQUIRED, IGNORED - - if len(sys.argv)<2: + + if len(sys.argv) < 2: print USAGE - + # make target folder target = sys.argv[1] os.mkdir(target) - + # change to os specificsep - REQUIRED = REQUIRED.replace('/',os.sep) - IGNORED = IGNORED.replace('/',os.sep) - + REQUIRED = REQUIRED.replace('/', os.sep) + IGNORED = IGNORED.replace('/', os.sep) # make a list of all files to include - files = [x.strip() for x in REQUIRED.split('\n') \ - if x and not x[0]=='#'] - ignore = [x.strip() for x in IGNORED.split('\n') \ - if x and not x[0]=='#'] - + files = [x.strip() for x in REQUIRED.split('\n') + if x and not x[0] == '#'] + ignore = [x.strip() for x in IGNORED.split('\n') + if x and not x[0] == '#'] + def accept(filename): for p in ignore: if filename.startswith(p): return False return True - pattern = os.path.join('gluon','*.py') + pattern = os.path.join('gluon', '*.py') while True: newfiles = [x for x in glob.glob(pattern) if accept(x)] - if not newfiles: break + if not newfiles: + break files += newfiles - pattern = os.path.join(pattern[:-3],'*.py') + pattern = os.path.join(pattern[:-3], '*.py') # copy all files, make missing folder, build default.py files.sort() - defaultpy = os.path.join('applications','welcome','controllers','default.py') + defaultpy = os.path.join( + 'applications', 'welcome', 'controllers', 'default.py') for f in files: dirs = f.split(os.path.sep) - for i in range(1,len(dirs)): - try: os.mkdir(target+os.sep+os.path.join(*dirs[:i])) - except OSError: pass - if f==defaultpy: - open(os.path.join(target,f),'w').write('def index(): return "hello"\n') + for i in range(1, len(dirs)): + try: + os.mkdir(target + os.sep + os.path.join(*dirs[:i])) + except OSError: + pass + if f == defaultpy: + open(os.path.join( + target, f), 'w').write('def index(): return "hello"\n') else: - shutil.copyfile(f,os.path.join(target,f)) - -if __name__=='__main__': main() + shutil.copyfile(f, os.path.join(target, f)) + +if __name__ == '__main__': + main() diff --git a/scripts/rmorphans.py b/scripts/rmorphans.py index d297939b..8b50ef61 100644 --- a/scripts/rmorphans.py +++ b/scripts/rmorphans.py @@ -6,18 +6,18 @@ paths2 = [] while paths: path = paths.pop() for filename in os.listdir(path): - fullname = os.path.join(path,filename) + fullname = os.path.join(path, filename) if os.path.isdir(fullname): paths.append(fullname) else: extension = filename.split('.')[-1] - if extension.lower() in ('png','gif','jpg','jpeg','js','css'): - paths1.append((filename,fullname)) - if extension.lower() in ('css','js','py','html'): + if extension.lower() in ('png', 'gif', 'jpg', 'jpeg', 'js', 'css'): + paths1.append((filename, fullname)) + if extension.lower() in ('css', 'js', 'py', 'html'): paths2.append(fullname) -for filename,fullname in paths1: +for filename, fullname in paths1: for otherfullname in paths2: - if open(otherfullname).read().find(filename)>=0: + if open(otherfullname).read().find(filename) >= 0: break else: print fullname diff --git a/scripts/sessions2trash.py b/scripts/sessions2trash.py index 5357659f..11e685fc 100755 --- a/scripts/sessions2trash.py +++ b/scripts/sessions2trash.py @@ -162,7 +162,7 @@ class SessionFile(object): def last_visit_default(self): return datetime.datetime.fromtimestamp( - os.stat(self.filename)[stat.ST_MTIME]) + os.stat(self.filename)[stat.ST_MTIME]) def __str__(self): return self.filename @@ -175,7 +175,7 @@ def total_seconds(delta): Args: delta: datetime.timedelta instance. """ - return (delta.microseconds + (delta.seconds + (delta.days * 24 * 3600)) * \ + return (delta.microseconds + (delta.seconds + (delta.days * 24 * 3600)) * 10 ** 6) / 10 ** 6 @@ -186,25 +186,25 @@ def main(): parser = OptionParser(usage=usage) parser.add_option('-f', '--force', - action='store_true', dest='force', default=False, - help=('Ignore session expiration. ' - 'Force expiry based on -x option or auth.settings.expiration.') - ) + action='store_true', dest='force', default=False, + help=('Ignore session expiration. ' + 'Force expiry based on -x option or auth.settings.expiration.') + ) parser.add_option('-o', '--once', - action='store_true', dest='once', default=False, - help='Delete sessions, then exit.', - ) + action='store_true', dest='once', default=False, + help='Delete sessions, then exit.', + ) parser.add_option('-s', '--sleep', - dest='sleep', default=SLEEP_MINUTES * 60, type="int", - help='Number of seconds to sleep between executions. Default 300.', - ) + dest='sleep', default=SLEEP_MINUTES * 60, type="int", + help='Number of seconds to sleep between executions. Default 300.', + ) parser.add_option('-v', '--verbose', - default=0, action='count', - help="print verbose output, a second -v increases verbosity") + default=0, action='count', + help="print verbose output, a second -v increases verbosity") parser.add_option('-x', '--expiration', - dest='expiration', default=None, type="int", - help='Expiration value for sessions without expiration (in seconds)', - ) + dest='expiration', default=None, type="int", + help='Expiration value for sessions without expiration (in seconds)', + ) (options, unused_args) = parser.parse_args() diff --git a/scripts/standalone_exe_cxfreeze.py b/scripts/standalone_exe_cxfreeze.py index 9eb90d14..a21451ff 100644 --- a/scripts/standalone_exe_cxfreeze.py +++ b/scripts/standalone_exe_cxfreeze.py @@ -30,35 +30,35 @@ if sys.platform == 'win32': base_modules.remove('macpath') buildOptions = dict( - compressed = True, - excludes = ["macpath","PyQt4"], - includes = base_modules, - include_files=[ - 'applications', - 'ABOUT', - 'LICENSE', - 'VERSION', - 'logging.example.conf', - 'options_std.py', - 'app.example.yaml', - 'queue.example.yaml', - ], - # append any extra module by extending the list below - - # "contributed_modules+["lxml"]" - packages = contributed_modules, - ) + compressed=True, + excludes=["macpath", "PyQt4"], + includes=base_modules, + include_files=[ + 'applications', + 'ABOUT', + 'LICENSE', + 'VERSION', + 'logging.example.conf', + 'options_std.py', + 'app.example.yaml', + 'queue.example.yaml', + ], + # append any extra module by extending the list below - + # "contributed_modules+["lxml"]" + packages=contributed_modules, +) setup( - name = "Web2py", - version=web2py_version, - author="Massimo DiPierro", - description="web2py web framework", - license = "LGPL v3", - options = dict(build_exe = buildOptions), - executables = [Executable("web2py.py", - base=base, - compress = True, - icon = "web2py.ico", - targetName="web2py.exe", - copyDependentFiles = True)], - ) + name="Web2py", + version=web2py_version, + author="Massimo DiPierro", + description="web2py web framework", + license="LGPL v3", + options=dict(build_exe=buildOptions), + executables=[Executable("web2py.py", + base=base, + compress=True, + icon="web2py.ico", + targetName="web2py.exe", + copyDependentFiles=True)], +) diff --git a/scripts/sync_languages.py b/scripts/sync_languages.py index 8f1f8d6d..ff36c779 100755 --- a/scripts/sync_languages.py +++ b/scripts/sync_languages.py @@ -14,26 +14,27 @@ sys.path.insert(0, '.') file = sys.argv[1] apps = sys.argv[2:] + def sync_language(d, data): - ''' this function makes sure a translated string will be prefered over an untranslated - string when syncing languages between apps. when both are translated, it prefers the + ''' this function makes sure a translated string will be prefered over an untranslated + string when syncing languages between apps. when both are translated, it prefers the latter app, as did the original script ''' - + for key in data: # if this string is not in the allready translated data, add it if key not in d: d[key] = data[key] # see if there is a translated string in the original list, but not in the new list - elif ( - ((d[key] != '') or (d[key] != key)) and - ((data[key] == '') or (data[key] == key)) - ): + elif ( + ((d[key] != '') or (d[key] != key)) and + ((data[key] == '') or (data[key] == key)) + ): d[key] = d[key] # any other case (wether there is or there isn't a translated string) else: d[key] = data[key] - + return d d = {} @@ -45,7 +46,7 @@ for app in apps: data = eval(langfile.read()) finally: langfile.close() - + d = sync_language(d, data) path = 'applications/%s/' % apps[-1] @@ -68,4 +69,3 @@ for app in oapps: path2 = 'applications/%s/' % app file2 = os.path.join(path2, 'languages', '%s.py' % file) shutil.copyfile(file1, file2) - diff --git a/scripts/tickets2db.py b/scripts/tickets2db.py index cfcb15b3..ecb1b4cd 100755 --- a/scripts/tickets2db.py +++ b/scripts/tickets2db.py @@ -15,7 +15,7 @@ SLEEP_MINUTES = 5 errors_path = os.path.join(request.folder, 'errors') try: - db_string = open(os.path.join(request.folder, 'private', 'ticket_storage.txt')).read().replace('\r','').replace('\n','').strip() + db_string = open(os.path.join(request.folder, 'private', 'ticket_storage.txt')).read().replace('\r', '').replace('\n', '').strip() except: db_string = 'sqlite://storage.db' @@ -23,7 +23,8 @@ db_path = os.path.join(request.folder, 'databases') tk_db = DAL(db_string, folder=db_path, auto_import=True) ts = TicketStorage(db=tk_db) -tk_table = ts._get_table(db=tk_db, tablename=ts.tablename, app=request.application) +tk_table = ts._get_table( + db=tk_db, tablename=ts.tablename, app=request.application) hashes = {} @@ -46,6 +47,5 @@ while 1: ) tk_db.commit() os.unlink(filename) - - time.sleep(SLEEP_MINUTES * 60) + time.sleep(SLEEP_MINUTES * 60) diff --git a/scripts/tickets2email.py b/scripts/tickets2email.py index 2a557282..8fe4e896 100755 --- a/scripts/tickets2email.py +++ b/scripts/tickets2email.py @@ -42,8 +42,8 @@ while 1: error = RestrictedError() error.load(request, request.application, file) - mail.send(to=administrator_email, subject='new web2py ticket', message=error.traceback) + mail.send(to=administrator_email, + subject='new web2py ticket', message=error.traceback) os.unlink(os.path.join(path, file)) time.sleep(SLEEP_MINUTES * 60) - diff --git a/scripts/update_web2py.py b/scripts/update_web2py.py index c5606966..a570ced6 100644 --- a/scripts/update_web2py.py +++ b/scripts/update_web2py.py @@ -1,9 +1,9 @@ -#!/usr/bin/env python +#!/usr/bin/env python # -*- coding: utf-8 -*- """ crontab -e -* 3 * * * root path/to/this/file +* 3 * * * root path/to/this/file """ USER = 'www-data' @@ -14,7 +14,7 @@ import os import urllib import zipfile -if len(sys.argv)>1 and sys.argv[1] == 'nightly': +if len(sys.argv) > 1 and sys.argv[1] == 'nightly': version = 'http://web2py.com/examples/static/nightly/web2py_src.zip' else: version = 'http://web2py.com/examples/static/web2py_src.zip' @@ -23,11 +23,11 @@ realpath = os.path.realpath(__file__) path = os.path.dirname(os.path.dirname(os.path.dirname(realpath))) os.chdir(path) try: - old_version = open('web2py/VERSION','r').read().strip() + old_version = open('web2py/VERSION', 'r').read().strip() except IOError: old_version = '' -open(TMPFILENAME,'wb').write(urllib.urlopen(version).read()) +open(TMPFILENAME, 'wb').write(urllib.urlopen(version).read()) new_version = zipfile.ZipFile(TMPFILENAME).read('web2py/VERSION').strip() -if new_version>old_version: - os.system('sudo -u %s unzip -o %s' % (USER,TMPFILENAME)) +if new_version > old_version: + os.system('sudo -u %s unzip -o %s' % (USER, TMPFILENAME)) os.system('apachectl restart | apache2ctl restart') diff --git a/scripts/zip_static_files.py b/scripts/zip_static_files.py index 67d342f0..57054e5b 100644 --- a/scripts/zip_static_files.py +++ b/scripts/zip_static_files.py @@ -7,6 +7,7 @@ import os import gzip + def zip_static(filelist=[]): tsave = 0 for fi in filelist: @@ -24,17 +25,18 @@ def zip_static(filelist=[]): if zatime == atime and zmtime == mtime: print 'skipping %s, already gzipped to the latest version' % os.path.basename(fi) continue - print 'gzipping %s to %s' % (os.path.basename(fi), os.path.basename(gfi)) + print 'gzipping %s to %s' % ( + os.path.basename(fi), os.path.basename(gfi)) f_in = open(fi, 'rb') f_out = gzip.open(gfi, 'wb') f_out.writelines(f_in) f_out.close() f_in.close() - os.utime(gfi, (atime,mtime)) + os.utime(gfi, (atime, mtime)) saved = fstats.st_size - os.stat(gfi).st_size - tsave+= saved + tsave += saved - print 'saved %s KB' % (int(tsave)/1000.0) + print 'saved %s KB' % (int(tsave) / 1000.0) if __name__ == '__main__': ALLOWED_EXTS = ['.css', '.js'] 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 cb57c8fc..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,22 +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 9b6e88e0..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,9 +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 26425777..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 @@ -25,8 +25,3 @@ if __name__ == '__main__': except: sys.stderr.write('Sorry, -K only supported for python 2.6-2.7\n') gluon.widget.start(cron=True) - - - - - diff --git a/wsgihandler.py b/wsgihandler.py index 85368eb3..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 @@ -42,8 +42,3 @@ else: if SOFTCRON: from gluon.settings import global_settings global_settings.web2py_crontype = 'soft' - - - - -