many pep8 improvements

This commit is contained in:
mdipierro
2012-10-19 12:33:53 -05:00
parent b716df1a05
commit 4b2ba185ae
117 changed files with 7160 additions and 5670 deletions
+1 -1
View File
@@ -1 +1 @@
Version 2.1.1 (2012-10-19 10:37:01) dev
Version 2.1.1 (2012-10-19 12:33:48) dev
-1
View File
@@ -1 +0,0 @@
+66 -46
View File
@@ -16,44 +16,47 @@ import urllib
path = os.path.dirname(os.path.abspath(__file__))
os.chdir(path)
sys.path = [path]+[p for p in sys.path if not p==path]
sys.path = [path] + [p for p in sys.path if not p == path]
import gluon.main
from gluon.fileutils import read_file, write_file
class Servers:
@staticmethod
def cgi(app, address=None, **options):
from wsgiref.handlers import CGIHandler
CGIHandler().run(app) # Just ignore host and port here
CGIHandler().run(app) # Just ignore host and port here
@staticmethod
def flup(app,address, **options):
def flup(app, address, **options):
import flup.server.fcgi
flup.server.fcgi.WSGIServer(app, bindAddress=address).run()
@staticmethod
def wsgiref(app,address,**options): # pragma: no cover
def wsgiref(app, address, **options): # pragma: no cover
from wsgiref.simple_server import make_server, WSGIRequestHandler
class QuietHandler(WSGIRequestHandler):
def log_request(*args, **kw): pass
def log_request(*args, **kw):
pass
options['handler_class'] = QuietHandler
srv = make_server(address[0],address[1],app,**options)
srv = make_server(address[0], address[1], app, **options)
srv.serve_forever()
@staticmethod
def cherrypy(app,address, **options):
def cherrypy(app, address, **options):
from cherrypy import wsgiserver
server = wsgiserver.CherryPyWSGIServer(address, app)
server.start()
@staticmethod
def rocket(app,address, **options):
def rocket(app, address, **options):
from gluon.rocket import CherryPyWSGIServer
server = CherryPyWSGIServer(address, app)
server.start()
@staticmethod
def rocket_with_repoze_profiler(app,address, **options):
def rocket_with_repoze_profiler(app, address, **options):
from gluon.rocket import CherryPyWSGIServer
from repoze.profile.profiler import AccumulatingProfileMiddleware
from gluon.settings import global_settings
@@ -63,44 +66,46 @@ class Servers:
log_filename='wsgi.prof',
discard_first_request=True,
flush_at_shutdown=True,
path = '/__profile__'
)
path='/__profile__'
)
server = CherryPyWSGIServer(address, wrapped)
server.start()
@staticmethod
def paste(app,address,**options):
def paste(app, address, **options):
from paste import httpserver
from paste.translogger import TransLogger
httpserver.serve(app, host=address[0], port=address[1], **options)
@staticmethod
def fapws(app,address, **options):
def fapws(app, address, **options):
import fapws._evwsgi as evwsgi
from fapws import base
evwsgi.start(address[0],str(address[1]))
evwsgi.start(address[0], str(address[1]))
evwsgi.set_base_module(base)
def app(environ, start_response):
environ['wsgi.multiprocess'] = False
return app(environ, start_response)
evwsgi.wsgi_cb(('',app))
evwsgi.wsgi_cb(('', app))
evwsgi.run()
@staticmethod
def gevent(app,address, **options):
from gevent import monkey; monkey.patch_all()
def gevent(app, address, **options):
from gevent import monkey
monkey.patch_all()
from gevent import pywsgi
from gevent.pool import Pool
pywsgi.WSGIServer(address, app, spawn = 'workers' in options and Pool(int(options.workers)) or 'default').serve_forever()
pywsgi.WSGIServer(address, app, spawn='workers' in options and Pool(
int(options.workers)) or 'default').serve_forever()
@staticmethod
def bjoern(app,address, **options):
def bjoern(app, address, **options):
import bjoern
bjoern.run(app, *address)
@staticmethod
def tornado(app,address, **options):
def tornado(app, address, **options):
import tornado.wsgi
import tornado.httpserver
import tornado.ioloop
@@ -110,7 +115,7 @@ class Servers:
tornado.ioloop.IOLoop.instance().start()
@staticmethod
def twisted(app,address, **options):
def twisted(app, address, **options):
from twisted.web import server, wsgi
from twisted.python.threadpool import ThreadPool
from twisted.internet import reactor
@@ -122,42 +127,44 @@ class Servers:
reactor.run()
@staticmethod
def diesel(app,address, **options):
def diesel(app, address, **options):
from diesel.protocols.wsgi import WSGIApplication
app = WSGIApplication(app, port=address[1])
app.run()
@staticmethod
def gunicorn(app,address, **options):
def gunicorn(app, address, **options):
from gunicorn.app.base import Application
config = {'bind': "%s:%d" % address}
config.update(options)
sys.argv = ['anyserver.py']
class GunicornApplication(Application):
def init(self, parser, opts, args):
return config
def load(self):
return app
g = GunicornApplication()
g.run()
@staticmethod
def eventlet(app,address, **options):
def eventlet(app, address, **options):
from eventlet import wsgi, listen
wsgi.server(listen(address), app)
@staticmethod
def mongrel2(app,address,**options):
def mongrel2(app, address, **options):
import uuid
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
from mongrel2 import handler
conn = handler.Connection(str(uuid.uuid4()),
"tcp://127.0.0.1:9997",
"tcp://127.0.0.1:9996")
mongrel2_handler(app,conn,debug=False)
mongrel2_handler(app, conn, debug=False)
def run(servername,ip,port,softcron=True,logging=False,profiler=None):
def run(servername, ip, port, softcron=True, logging=False, profiler=None):
if logging:
application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase,
logfilename='httpserver.log',
@@ -167,9 +174,10 @@ def run(servername,ip,port,softcron=True,logging=False,profiler=None):
if softcron:
from gluon.settings import global_settings
global_settings.web2py_crontype = 'soft'
getattr(Servers,servername)(application,(ip,int(port)))
getattr(Servers, servername)(application, (ip, int(port)))
def mongrel2_handler(application,conn,debug=False):
def mongrel2_handler(application, conn, debug=False):
"""
Based on :
https://github.com/berry/Mongrel2-WSGI-Handler/blob/master/wsgi-handler.py
@@ -194,20 +202,23 @@ def mongrel2_handler(application,conn,debug=False):
# and responses. Unless I have missed something.
while True:
if debug: print "WAITING FOR REQUEST"
if debug:
print "WAITING FOR REQUEST"
# receive a request
req = conn.recv()
if debug: print "REQUEST BODY: %r\n" % req.body
if debug:
print "REQUEST BODY: %r\n" % req.body
if req.is_disconnect():
if debug: print "DISCONNECT"
continue #effectively ignore the disconnect from the client
if debug:
print "DISCONNECT"
continue # effectively ignore the disconnect from the client
# Set a couple of environment attributes a.k.a. header attributes
# that are a must according to PEP 333
environ = req.headers
environ['SERVER_PROTOCOL'] = 'HTTP/1.1' # SimpleHandler expects a server_protocol, lets assume it is HTTP 1.1
environ['SERVER_PROTOCOL'] = 'HTTP/1.1' # SimpleHandler expects a server_protocol, lets assume it is HTTP 1.1
environ['REQUEST_METHOD'] = environ['METHOD']
if ':' in environ['Host']:
environ['SERVER_NAME'] = environ['Host'].split(':')[0]
@@ -215,17 +226,19 @@ def mongrel2_handler(application,conn,debug=False):
else:
environ['SERVER_NAME'] = environ['Host']
environ['SERVER_PORT'] = ''
environ['SCRIPT_NAME'] = '' # empty for now
environ['SCRIPT_NAME'] = '' # empty for now
environ['PATH_INFO'] = urllib.unquote(environ['PATH'])
if '?' in environ['URI']:
environ['QUERY_STRING'] = environ['URI'].split('?')[1]
else:
environ['QUERY_STRING'] = ''
if 'Content-Length' in environ:
environ['CONTENT_LENGTH'] = environ['Content-Length'] # necessary for POST to work with Django
environ['CONTENT_LENGTH'] = environ[
'Content-Length'] # necessary for POST to work with Django
environ['wsgi.input'] = req.body
if debug: print "ENVIRON: %r\n" % environ
if debug:
print "ENVIRON: %r\n" % environ
# SimpleHandler needs file-like stream objects for
# requests, errors and responses
@@ -234,7 +247,8 @@ def mongrel2_handler(application,conn,debug=False):
respIO = StringIO.StringIO()
# execute the application
handler = SimpleHandler(reqIO, respIO, errIO, environ, multithread = False, multiprocess = False)
handler = SimpleHandler(reqIO, respIO, errIO, environ,
multithread=False, multiprocess=False)
handler.run(application)
# Get the response and filter out the response (=data) itself,
@@ -258,11 +272,15 @@ def mongrel2_handler(application,conn,debug=False):
errors = errIO.getvalue()
# return the response
if debug: print "RESPONSE: %r\n" % response
if debug:
print "RESPONSE: %r\n" % response
if errors:
if debug: print "ERRORS: %r" % errors
if debug:
print "ERRORS: %r" % errors
data = "%s\r\n\r\n%s" % (data, errors)
conn.reply_http(req, data, code = code, status = status, headers = headers)
conn.reply_http(
req, data, code=code, status=status, headers=headers)
def main():
usage = "python anyserver.py -s tornado -i 127.0.0.1 -p 8000 -l -P"
@@ -282,7 +300,7 @@ def main():
default=False,
dest='profiler',
help='profiler filename')
servers = ', '.join(x for x in dir(Servers) if not x[0]=='_')
servers = ', '.join(x for x in dir(Servers) if not x[0] == '_')
parser.add_option('-s',
'--server',
default='rocket',
@@ -304,8 +322,10 @@ def main():
dest='workers',
help='number of workers number')
(options, args) = parser.parse_args()
print 'starting %s on %s:%s...' % (options.server,options.ip,options.port)
run(options.server,options.ip,options.port,logging=options.logging,profiler=options.profiler)
print 'starting %s on %s:%s...' % (
options.server, options.ip, options.port)
run(options.server, options.ip, options.port,
logging=options.logging, profiler=options.profiler)
if __name__=='__main__':
if __name__ == '__main__':
main()
+1 -1
View File
@@ -56,7 +56,7 @@ import wsgiref.handlers
path = os.path.dirname(os.path.abspath(__file__))
os.chdir(path)
sys.path = [path]+[p for p in sys.path if not p==path]
sys.path = [path] + [p for p in sys.path if not p == path]
import gluon.main
+1 -1
View File
@@ -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
+3 -2
View File
@@ -33,7 +33,7 @@ import wsgiref.handlers
import datetime
path = os.path.dirname(os.path.abspath(__file__))
sys.path = [path]+[p for p in sys.path if not p==path]
sys.path = [path] + [p for p in sys.path if not p == path]
sys.modules['cPickle'] = sys.modules['pickle']
@@ -83,7 +83,7 @@ def wsgiapp(env, res):
if global_settings.web2py_runtime == 'gae:development':
gluon.admin.create_missing_folders()
web2py_path = global_settings.applications_parent # backward compatibility
web2py_path = global_settings.applications_parent # backward compatibility
return gluon.main.wsgibase(env, res)
@@ -91,6 +91,7 @@ def wsgiapp(env, res):
if LOG_STATS or DEBUG:
wsgiapp = log_stats(wsgiapp)
def main():
"""Run the wsgi app"""
run_wsgi_app(wsgiapp)
+1 -1
View File
@@ -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 *
+31 -18
View File
@@ -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('<your secret key>',
'sha512:'+(key or web2py_uuid()))
'sha512:' + (key or web2py_uuid()))
write_file(db, data)
if info:
return True, None
@@ -283,6 +286,7 @@ def app_uninstall(app, request):
except Exception:
return False
def plugin_pack(app, plugin_name, request):
"""
Builds a w2p package for the application
@@ -302,12 +306,14 @@ def plugin_pack(app, plugin_name, request):
filename of the w2p file or None on error
"""
try:
filename = apath('../deposit/web2py.plugin.%s.w2p' % plugin_name, request)
filename = apath(
'../deposit/web2py.plugin.%s.w2p' % plugin_name, request)
w2p_pack_plugin(filename, apath(app, request), plugin_name)
return filename
except Exception:
return False
def plugin_install(app, fobj, request, filename):
"""
Installs an application:
@@ -345,6 +351,7 @@ def plugin_install(app, fobj, request, filename):
os.unlink(upname)
return False
def check_new_version(myversion, version_URL):
"""
Compares current web2py's version with the latest stable web2py version.
@@ -375,6 +382,7 @@ def check_new_version(myversion, version_URL):
else:
return False, version
def unzip(filename, dir, subfolder=''):
"""
Unzips filename into dir (.zip only, no .gz etc)
@@ -382,7 +390,7 @@ def unzip(filename, dir, subfolder=''):
"""
filename = abspath(filename)
if not zipfile.is_zipfile(filename):
raise RuntimeError, 'Not a valid zipfile'
raise RuntimeError('Not a valid zipfile')
zf = zipfile.ZipFile(filename)
if not subfolder.endswith('/'):
subfolder = subfolder + '/'
@@ -392,7 +400,7 @@ def unzip(filename, dir, subfolder=''):
continue
#print name[n:]
if name.endswith('/'):
folder = os.path.join(dir,name[n:])
folder = os.path.join(dir, name[n:])
if not os.path.exists(folder):
os.mkdir(folder)
else:
@@ -421,7 +429,7 @@ def upgrade(request, url='http://web2py.com'):
if not gluon_parent.endswith('/'):
gluon_parent = gluon_parent + '/'
(check, version) = check_new_version(web2py_version,
url+'/examples/default/version')
url + '/examples/default/version')
if not check:
return (False, 'Already latest version')
if os.path.exists(os.path.join(gluon_parent, 'web2py.exe')):
@@ -442,35 +450,40 @@ def upgrade(request, url='http://web2py.com'):
file = None
try:
write_file(filename, urllib.urlopen(full_url).read(), 'wb')
except Exception,e:
except Exception, e:
return False, e
try:
unzip(filename, destination, subfolder)
return True, None
except Exception,e:
except Exception, e:
return False, e
def add_path_first(path):
sys.path = [path]+[p for p in sys.path if (not p==path and not p==(path+'/'))]
sys.path = [path] + [p for p in sys.path if (
not p == path and not p == (path + '/'))]
if not global_settings.web2py_runtime_gae:
site.addsitedir(path)
def create_missing_folders():
if not global_settings.web2py_runtime_gae:
for path in ('applications', 'deposit', 'site-packages', 'logs'):
path = abspath(path, gluon=True)
if not os.path.exists(path):
os.mkdir(path)
paths = (global_settings.gluon_parent, abspath('site-packages', gluon=True), abspath('gluon', gluon=True), '')
paths = (global_settings.gluon_parent, abspath(
'site-packages', gluon=True), abspath('gluon', gluon=True), '')
[add_path_first(path) for path in paths]
def create_missing_app_folders(request):
if not global_settings.web2py_runtime_gae:
if request.folder not in global_settings.app_folders:
for subfolder in ('models', 'views', 'controllers', 'databases',
'modules', 'cron', 'errors', 'sessions',
'languages', 'static', 'private', 'uploads'):
path = os.path.join(request.folder, subfolder)
path = os.path.join(request.folder, subfolder)
if not os.path.exists(path):
os.mkdir(path)
global_settings.app_folders.add(request.folder)
+45 -33
View File
@@ -40,6 +40,7 @@ __all__ = ['Cache', 'lazy_cache']
DEFAULT_TIME_EXPIRE = 300
class CacheAbstract(object):
"""
Abstract class for cache implementations.
@@ -73,7 +74,7 @@ class CacheAbstract(object):
raise NotImplementedError
def __call__(self, key, f,
time_expire = DEFAULT_TIME_EXPIRE):
time_expire=DEFAULT_TIME_EXPIRE):
"""
Tries retrieve the value corresponding to `key` from the cache of the
object exists and if it did not expire, else it called the function `f`
@@ -130,6 +131,7 @@ class CacheAbstract(object):
if r.match(str(key)):
del storage[key]
class CacheInRam(CacheAbstract):
"""
Ram based caching
@@ -147,8 +149,10 @@ class CacheInRam(CacheAbstract):
self.request = request
def initialize(self):
if self.initialized: return
else: self.initialized = True
if self.initialized:
return
else:
self.initialized = True
self.locker.acquire()
request = self.request
if request:
@@ -172,13 +176,14 @@ class CacheInRam(CacheAbstract):
self._clear(storage, regex)
if not CacheAbstract.cache_stats_name in storage.keys():
storage[CacheAbstract.cache_stats_name] = {'hit_total': 0,'misses': 0}
storage[CacheAbstract.cache_stats_name] = {
'hit_total': 0, 'misses': 0}
self.locker.release()
def __call__(self, key, f,
time_expire = DEFAULT_TIME_EXPIRE,
destroyer = None):
time_expire=DEFAULT_TIME_EXPIRE,
destroyer=None):
"""
Attention! cache.ram does not copy the cached object. It just stores a reference to it.
Turns out the deepcopying the object has some problems:
@@ -269,14 +274,15 @@ class CacheOnDisk(CacheAbstract):
try:
storage = shelve.open(self.shelve_name)
except:
logger.error('corrupted cache file %s, will try rebuild it' \
% (self.shelve_name))
logger.error('corrupted cache file %s, will try rebuild it'
% (self.shelve_name))
storage = None
if not storage and os.path.exists(self.shelve_name):
os.unlink(self.shelve_name)
storage = shelve.open(self.shelve_name)
if not CacheAbstract.cache_stats_name in storage.keys():
storage[CacheAbstract.cache_stats_name] = {'hit_total':0, 'misses': 0}
storage[CacheAbstract.cache_stats_name] = {
'hit_total': 0, 'misses': 0}
storage.sync()
except Exception, e:
if storage:
@@ -286,7 +292,8 @@ class CacheOnDisk(CacheAbstract):
portalocker.unlock(locker)
locker.close()
locked = False
raise RuntimeError, 'unable to create/re-create cache file %s' % self.shelve_name
raise RuntimeError(
'unable to create/re-create cache file %s' % self.shelve_name)
self.locker = locker
self.locked = locked
self.storage = storage
@@ -298,8 +305,10 @@ class CacheOnDisk(CacheAbstract):
self.folder = folder
def initialize(self):
if self.initialized: return
else: self.initialized = True
if self.initialized:
return
else:
self.initialized = True
folder = self.folder
request = self.request
@@ -312,8 +321,8 @@ class CacheOnDisk(CacheAbstract):
### we need this because of a possible bug in shelve that may
### or may not lock
self.locker_name = os.path.join(folder,'cache.lock')
self.shelve_name = os.path.join(folder,'cache.shelve')
self.locker_name = os.path.join(folder, 'cache.lock')
self.shelve_name = os.path.join(folder, 'cache.shelve')
def clear(self, regex=None):
self.initialize()
@@ -328,7 +337,7 @@ class CacheOnDisk(CacheAbstract):
self._close_shelve_and_unlock()
def __call__(self, key, f,
time_expire = DEFAULT_TIME_EXPIRE):
time_expire=DEFAULT_TIME_EXPIRE):
self.initialize()
dt = time_expire
storage = self._open_shelve_and_lock()
@@ -346,7 +355,7 @@ class CacheOnDisk(CacheAbstract):
else:
value = f()
storage[key] = (now, value)
storage[CacheAbstract.cache_stats_name]['misses']+=1
storage[CacheAbstract.cache_stats_name]['misses'] += 1
storage.sync()
finally:
self._close_shelve_and_unlock()
@@ -365,8 +374,9 @@ class CacheOnDisk(CacheAbstract):
self._close_shelve_and_unlock()
return value
class CacheAction(object):
def __init__(self,func,key,time_expire,cache,cache_model):
def __init__(self, func, key, time_expire, cache, cache_model):
self.__name__ = func.__name__
self.__doc__ = func.__doc__
self.func = func
@@ -374,17 +384,18 @@ class CacheAction(object):
self.time_expire = time_expire
self.cache = cache
self.cache_model = cache_model
def __call__(self,*a,**b):
def __call__(self, *a, **b):
if not self.key:
key2 = self.__name__+':'+repr(a)+':'+repr(b)
key2 = self.__name__ + ':' + repr(a) + ':' + repr(b)
else:
key2 = self.key.replace('%(name)s',self.__name__)\
.replace('%(args)s',str(a)).replace('%(vars)s',str(b))
key2 = self.key.replace('%(name)s', self.__name__)\
.replace('%(args)s', str(a)).replace('%(vars)s', str(b))
cache_model = self.cache_model
if not cache_model or isinstance(cache_model,str):
cache_model = getattr(self.cache,cache_model or 'ram')
if not cache_model or isinstance(cache_model, str):
cache_model = getattr(self.cache, cache_model or 'ram')
return cache_model(key2,
lambda a=a,b=b:self.func(*a,**b),
lambda a=a, b=b: self.func(*a, **b),
self.time_expire)
@@ -424,9 +435,9 @@ class Cache(object):
logger.warning('no cache.disk (AttributeError)')
def __call__(self,
key = None,
time_expire = DEFAULT_TIME_EXPIRE,
cache_model = None):
key=None,
time_expire=DEFAULT_TIME_EXPIRE,
cache_model=None):
"""
Decorator function that can be used to cache any function/method.
@@ -459,8 +470,8 @@ class Cache(object):
`request.env.path_info` as key.
"""
def tmp(func,cache=self,cache_model=cache_model):
return CacheAction(func,key,time_expire,self,cache_model)
def tmp(func, cache=self, cache_model=cache_model):
return CacheAction(func, key, time_expire, self, cache_model)
return tmp
@staticmethod
@@ -473,7 +484,7 @@ class Cache(object):
cache_model(prefix + key, f, time_expire)
def lazy_cache(key=None,time_expire=None,cache_model='ram'):
def lazy_cache(key=None, time_expire=None, cache_model='ram'):
"""
can be used to cache any function including in modules,
as long as the cached function is only called within a web2py request
@@ -481,11 +492,12 @@ def lazy_cache(key=None,time_expire=None,cache_model='ram'):
the time_expire defaults to None (no cache expiration)
if cache_model is "ram" then the model is current.cache.ram, etc.
"""
def decorator(f,key=key,time_expire=time_expire,cache_model=cache_model):
def decorator(f, key=key, time_expire=time_expire, cache_model=cache_model):
key = key or repr(f)
def g(*c,**d):
def g(*c, **d):
from gluon import current
return current.cache(key,time_expire,cache_model)(f)(*c,**d)
return current.cache(key, time_expire, cache_model)(f)(*c, **d)
g.__name__ = f.__name__
return g
return decorator
+79 -65
View File
@@ -48,7 +48,7 @@ except:
logger.warning('unable to import py_compile')
is_pypy = settings.global_settings.is_pypy
is_gae = settings.global_settings.web2py_runtime_gae
is_gae = settings.global_settings.web2py_runtime_gae
is_jython = settings.global_settings.is_jython
pjoin = os.path.join
@@ -95,6 +95,7 @@ _TEST()
CACHED_REGEXES = {}
CACHED_REGEXES_MAX_SIZE = 1000
def re_compile(regex):
try:
return CACHED_REGEXES[regex]
@@ -104,6 +105,7 @@ def re_compile(regex):
compiled_regex = CACHED_REGEXES[regex] = re.compile(regex)
return compiled_regex
class mybuiltin(object):
"""
NOTE could simple use a dict and populate it,
@@ -114,14 +116,16 @@ class mybuiltin(object):
try:
return getattr(__builtin__, key)
except AttributeError:
raise KeyError, key
raise KeyError(key)
def __setitem__(self, key, value):
setattr(self, key, value)
def LOAD(c=None, f='index', args=None, vars=None,
extension=None, target=None,ajax=False,ajax_trap=False,
url=None,user_signature=False, timeout=None, times=1,
content='loading...',**attr):
extension=None, target=None, ajax=False, ajax_trap=False,
url=None, user_signature=False, timeout=None, times=1,
content='loading...', **attr):
""" LOAD a component into the action's document
Timing options:
@@ -134,13 +138,14 @@ def LOAD(c=None, f='index', args=None, vars=None,
is added on page loading without delay.
"""
from html import TAG, DIV, URL, SCRIPT, XML
if args is None: args = []
if args is None:
args = []
vars = Storage(vars or {})
target = target or 'c'+str(random.random())[2:]
attr['_id']=target
target = target or 'c' + str(random.random())[2:]
attr['_id'] = target
request = current.request
if '.' in f:
f, extension = f.rsplit('.',1)
f, extension = f.rsplit('.', 1)
if url or ajax:
url = url or URL(request.application, c, f, r=request,
args=args, vars=vars, extension=extension,
@@ -160,19 +165,20 @@ def LOAD(c=None, f='index', args=None, vars=None,
if not isinstance(timeout, (int, long)):
raise ValueError("Timeout argument must be an integer or None")
elif timeout <= 0:
raise ValueError("Timeout argument must be greater than zero or None")
raise ValueError(
"Timeout argument must be greater than zero or None")
statement = "web2py_component('%s','%s', %s, %s);" \
% (url, target, timeout, times)
% (url, target, timeout, times)
else:
statement = "web2py_component('%s','%s');" % (url, target)
script = SCRIPT(statement, _type="text/javascript")
if not content is None:
return TAG[''](script, DIV(content,**attr))
return TAG[''](script, DIV(content, **attr))
else:
return TAG[''](script)
else:
if not isinstance(args,(list,tuple)):
if not isinstance(args, (list, tuple)):
args = [args]
c = c or request.controller
other_request = Storage(request)
@@ -186,17 +192,17 @@ def LOAD(c=None, f='index', args=None, vars=None,
other_request.post_vars = Storage()
other_response = Response()
other_request.env.path_info = '/' + \
'/'.join([request.application,c,f] + \
map(str, other_request.args))
'/'.join([request.application, c, f] +
map(str, other_request.args))
other_request.env.query_string = \
vars and URL(vars=vars).split('?')[1] or ''
other_request.env.http_web2py_component_location = \
request.env.path_info
other_request.cid = target
other_request.env.http_web2py_component_element = target
other_response.view = '%s/%s.%s' % (c,f, other_request.extension)
other_response.view = '%s/%s.%s' % (c, f, other_request.extension)
other_environment = copy.copy(current.globalenv) ### NASTY
other_environment = copy.copy(current.globalenv) # NASTY
other_response._view_environment = other_environment
other_response.generic_patterns = \
@@ -218,40 +224,41 @@ def LOAD(c=None, f='index', args=None, vars=None,
js = None
if ajax_trap:
link = URL(request.application, c, f, r=request,
args=args, vars=vars, extension=extension,
user_signature=user_signature)
args=args, vars=vars, extension=extension,
user_signature=user_signature)
js = "web2py_trap_form('%s','%s');" % (link, target)
script = js and SCRIPT(js,_type="text/javascript") or ''
return TAG[''](DIV(XML(page),**attr),script)
script = js and SCRIPT(js, _type="text/javascript") or ''
return TAG[''](DIV(XML(page), **attr), script)
class LoadFactory(object):
"""
Attention: this helper is new and experimental
"""
def __init__(self,environment):
def __init__(self, environment):
self.environment = environment
def __call__(self, c=None, f='index', args=None, vars=None,
extension=None, target=None,ajax=False,ajax_trap=False,
url=None,user_signature=False, content='loading...',**attr):
if args is None: args = []
extension=None, target=None, ajax=False, ajax_trap=False,
url=None, user_signature=False, content='loading...', **attr):
if args is None:
args = []
vars = Storage(vars or {})
import globals
target = target or 'c'+str(random.random())[2:]
attr['_id']=target
target = target or 'c' + str(random.random())[2:]
attr['_id'] = target
request = self.environment['request']
if '.' in f:
f, extension = f.rsplit('.',1)
f, extension = f.rsplit('.', 1)
if url or ajax:
url = url or html.URL(request.application, c, f, r=request,
args=args, vars=vars, extension=extension,
user_signature=user_signature)
script = html.SCRIPT('web2py_component("%s","%s")' % (url, target),
_type="text/javascript")
return html.TAG[''](script, html.DIV(content,**attr))
return html.TAG[''](script, html.DIV(content, **attr))
else:
if not isinstance(args,(list,tuple)):
if not isinstance(args, (list, tuple)):
args = [args]
c = c or request.controller
@@ -266,15 +273,15 @@ class LoadFactory(object):
other_request.post_vars = Storage()
other_response = globals.Response()
other_request.env.path_info = '/' + \
'/'.join([request.application,c,f] + \
map(str, other_request.args))
'/'.join([request.application, c, f] +
map(str, other_request.args))
other_request.env.query_string = \
vars and html.URL(vars=vars).split('?')[1] or ''
other_request.env.http_web2py_component_location = \
request.env.path_info
other_request.cid = target
other_request.env.http_web2py_component_element = target
other_response.view = '%s/%s.%s' % (c,f, other_request.extension)
other_response.view = '%s/%s.%s' % (c, f, other_request.extension)
other_environment = copy.copy(self.environment)
other_response._view_environment = other_environment
other_response.generic_patterns = \
@@ -299,8 +306,8 @@ class LoadFactory(object):
args=args, vars=vars, extension=extension,
user_signature=user_signature)
js = "web2py_trap_form('%s','%s');" % (link, target)
script = js and html.SCRIPT(js,_type="text/javascript") or ''
return html.TAG[''](html.DIV(html.XML(page),**attr),script)
script = js and html.SCRIPT(js, _type="text/javascript") or ''
return html.TAG[''](html.DIV(html.XML(page), **attr), script)
def local_import_aux(name, reload_force=False, app='welcome'):
@@ -321,7 +328,7 @@ def local_import_aux(name, reload_force=False, app='welcome'):
This prevents conflict between applications and un-necessary execs.
It can be used to import any module, including regular Python modules.
"""
items = name.replace('/','.')
items = name.replace('/', '.')
name = "applications.%s.modules.%s" % (app, items)
module = __import__(name)
for item in name.split(".")[1:]:
@@ -355,12 +362,14 @@ OLD IMPLEMENTATION:
file.close()
imp.release_lock()
if not module:
raise ImportError, "cannot find module %s in %s" % (filename, modulepath)
raise ImportError, "cannot find module %s in %s" % (
filename, modulepath)
return module
"""
_base_environment_ = dict((k,getattr(html,k)) for k in html.__all__)
_base_environment_.update((k,getattr(validators,k)) for k in validators.__all__)
_base_environment_ = dict((k, getattr(html, k)) for k in html.__all__)
_base_environment_.update(
(k, getattr(validators, k)) for k in validators.__all__)
_base_environment_['__builtins__'] = __builtins__
_base_environment_['HTTP'] = HTTP
_base_environment_['redirect'] = redirect
@@ -372,6 +381,7 @@ _base_environment_['SQLFORM'] = SQLFORM
_base_environment_['SQLTABLE'] = SQLTABLE
_base_environment_['LOAD'] = LOAD
def build_environment(request, response, session, store_current=True):
"""
Build the environment dictionary into which web2py files are executed.
@@ -384,7 +394,7 @@ def build_environment(request, response, session, store_current=True):
# Enable standard conditional models (i.e., /*.py, /[controller]/*.py, and
# /[controller]/[function]/*.py)
response.models_to_run = [r'^\w+\.py$', r'^%s/\w+\.py$' % request.controller,
r'^%s/%s/\w+\.py$' % (request.controller, request.function)]
r'^%s/%s/\w+\.py$' % (request.controller, request.function)]
t = environment['T'] = translator(request)
c = environment['cache'] = Cache(request)
@@ -398,23 +408,24 @@ def build_environment(request, response, session, store_current=True):
current.cache = c
global __builtins__
if is_jython: # jython hack
if is_jython: # jython hack
__builtins__ = mybuiltin()
elif is_pypy: # apply the same hack to pypy too
elif is_pypy: # apply the same hack to pypy too
__builtins__ = mybuiltin()
else:
__builtins__['__import__'] = __builtin__.__import__ ### WHY?
__builtins__['__import__'] = __builtin__.__import__ # WHY?
environment['request'] = request
environment['response'] = response
environment['session'] = session
environment['local_import'] = \
lambda name, reload=False, app=request.application:\
local_import_aux(name,reload,app)
lambda name, reload=False, app=request.application:\
local_import_aux(name, reload, app)
BaseAdapter.set_folder(pjoin(request.folder, 'databases'))
response._view_environment = copy.copy(environment)
custom_import_install()
return environment
def save_pyc(filename):
"""
Bytecode compiles the file `filename`
@@ -431,7 +442,7 @@ def read_pyc(filename):
"""
data = read_file(filename, 'rb')
if not is_gae and data[:4] != imp.get_magic():
raise SystemError, 'compiled code is incompatible'
raise SystemError('compiled code is incompatible')
return marshal.loads(data[8:])
@@ -458,7 +469,7 @@ def compile_models(folder):
path = pjoin(folder, 'models')
for file in listdir(path, '.+\.py$'):
data = read_file(pjoin(path, file))
filename = pjoin(folder, 'compiled','models',file)
filename = pjoin(folder, 'compiled', 'models', file)
mktree(filename)
write_file(filename, data)
save_pyc(filename)
@@ -473,14 +484,14 @@ def compile_controllers(folder):
path = pjoin(folder, 'controllers')
for file in listdir(path, '.+\.py$'):
### why is this here? save_pyc(pjoin(path, file))
data = read_file(pjoin(path,file))
data = read_file(pjoin(path, file))
exposed = regex_expose.findall(data)
for function in exposed:
command = data + "\nresponse._vars=response._caller(%s)\n" % \
function
filename = pjoin(folder, 'compiled', ('controllers/'
+ file[:-3]).replace('/', '_')
+ '_' + function + '.py')
+ '_' + function + '.py')
write_file(filename, command)
save_pyc(filename)
os.unlink(filename)
@@ -500,19 +511,19 @@ def run_models_in(environment):
for model in listdir(cpath, '^models_\w+\.pyc$', 0):
restricted(read_pyc(model), environment, layer=model)
path = pjoin(cpath, 'models')
models = listdir(path, '^\w+\.pyc$',0,sort=False)
compiled=True
models = listdir(path, '^\w+\.pyc$', 0, sort=False)
compiled = True
else:
path = pjoin(folder, 'models')
models = listdir(path, '^\w+\.py$',0,sort=False)
compiled=False
models = listdir(path, '^\w+\.py$', 0, sort=False)
compiled = False
n = len(path) + 1
for model in models:
regex = environment['response'].models_to_run
if isinstance(regex, list):
regex = re_compile('|'.join(regex))
file = model[n:].replace(os.path.sep, '/').replace('.pyc', '.py')
if not regex.search(file) and c!= 'appadmin':
if not regex.search(file) and c != 'appadmin':
continue
elif compiled:
code = read_pyc(model)
@@ -538,7 +549,7 @@ def run_controller_in(controller, function, environment):
badf = 'invalid function (%s/%s)' % (controller, function)
if os.path.exists(path):
filename = pjoin(path, 'controllers_%s_%s.pyc'
% (controller, function))
% (controller, function))
if not os.path.exists(filename):
raise HTTP(404,
rewrite.THREAD_LOCAL.routes.error_message % badf,
@@ -548,7 +559,8 @@ def run_controller_in(controller, function, environment):
# TESTING: adjust the path to include site packages
from settings import global_settings
from admin import abspath, add_path_first
paths = (global_settings.gluon_parent, abspath('site-packages', gluon=True), abspath('gluon', gluon=True), '')
paths = (global_settings.gluon_parent, abspath(
'site-packages', gluon=True), abspath('gluon', gluon=True), '')
[add_path_first(path) for path in paths]
# TESTING END
@@ -578,18 +590,19 @@ def run_controller_in(controller, function, environment):
code = "%s\nresponse._vars=response._caller(%s)\n" % (code, function)
if is_gae:
layer = filename + ':' + function
code = getcfs(layer, filename, lambda: compile2(code,layer))
code = getcfs(layer, filename, lambda: compile2(code, layer))
restricted(code, environment, filename)
response = environment['response']
vars=response._vars
vars = response._vars
if response.postprocessing:
vars = reduce(lambda vars, p: p(vars), response.postprocessing, vars)
if isinstance(vars,unicode):
if isinstance(vars, unicode):
vars = vars.encode('utf8')
elif hasattr(vars,'xml') and callable(vars.xml):
elif hasattr(vars, 'xml') and callable(vars.xml):
vars = vars.xml()
return vars
def run_view_in(environment):
"""
Executes the view for the requested action.
@@ -606,7 +619,7 @@ def run_view_in(environment):
if response.generic_patterns:
patterns = response.generic_patterns
regex = re_compile('|'.join(map(fnmatch.translate, patterns)))
short_action = '%(controller)s/%(function)s.%(extension)s' % request
short_action = '%(controller)s/%(function)s.%(extension)s' % request
allow_generic = regex.search(short_action)
else:
allow_generic = False
@@ -626,7 +639,7 @@ def run_view_in(environment):
files.append('views_generic.pyc')
# end backward compatibility code
for f in files:
filename = pjoin(path,f)
filename = pjoin(path, f)
if os.path.exists(filename):
code = read_pyc(filename)
restricted(code, environment, layer=filename)
@@ -648,13 +661,14 @@ def run_view_in(environment):
ccode = getcfs(layer, filename,
lambda: compile2(parse_template(view,
pjoin(folder, 'views'),
context=environment),layer))
context=environment), layer))
else:
ccode = parse_template(view,
pjoin(folder, 'views'),
context=environment)
restricted(ccode, environment, layer)
def remove_compiled_application(folder):
"""
Deletes the folder `compiled` containing the compiled application.
@@ -662,7 +676,7 @@ def remove_compiled_application(folder):
try:
shutil.rmtree(pjoin(folder, 'compiled'))
path = pjoin(folder, 'controllers')
for file in listdir(path,'.*\.pyc$',drop=False):
for file in listdir(path, '.*\.pyc$', drop=False):
os.unlink(file)
except OSError:
pass
+5 -5
View File
@@ -700,7 +700,7 @@ CONTENT_TYPE = {
'.zabw': 'application/x-abiword',
'.zip': 'application/zip',
'.zoo': 'application/x-zoo',
}
}
def contenttype(filename, default='text/plain'):
@@ -709,11 +709,11 @@ def contenttype(filename, default='text/plain'):
"""
i = filename.rfind('.')
if i>=0:
default = CONTENT_TYPE.get(filename[i:].lower(),default)
if i >= 0:
default = CONTENT_TYPE.get(filename[i:].lower(), default)
j = filename.rfind('.', 0, i)
if j>=0:
default = CONTENT_TYPE.get(filename[j:].lower(),default)
if j >= 0:
default = CONTENT_TYPE.get(filename[j:].lower(), default)
if default.startswith('text/'):
default += '; charset=utf-8'
return default
+38 -27
View File
@@ -21,6 +21,7 @@ import urllib
_known_tuple_types = {}
class NamedTupleBase(tuple):
"""Base class for named tuples with the __new__ operator set, named tuples
yielded by the namedtuple() function will subclass this and add
@@ -29,7 +30,7 @@ class NamedTupleBase(tuple):
"""Create a new instance of this fielded tuple"""
# May need to unpack named field values here
if kws:
values = list(args) + [None]*(len(cls._fields) - len(args))
values = list(args) + [None] * (len(cls._fields) - len(args))
fields = dict((val, idx) for idx, val in enumerate(cls._fields))
for kw, val in kws.iteritems():
assert kw in kws, "%r not in field list" % kw
@@ -37,6 +38,7 @@ class NamedTupleBase(tuple):
args = tuple(values)
return tuple.__new__(cls, args)
def namedtuple(typename, fieldnames):
"""
>>> import namedtuples
@@ -75,24 +77,26 @@ def namedtuple(typename, fieldnames):
# Done
return new_tuple_type
class AIM:
class AIMError(Exception):
def __init__(self, value):
self.parameter = value
def __str__(self):
return str(self.parameter)
def __init__(self, login, transkey, testmode=False):
if str(login).strip() == '' or login == None:
if str(login).strip() == '' or login is None:
raise AIM.AIMError('No login name provided')
if str(transkey).strip() == '' or transkey == None:
if str(transkey).strip() == '' or transkey is None:
raise AIM.AIMError('No transaction key provided')
if testmode != True and testmode != False:
raise AIM.AIMError('Invalid value for testmode. Must be True or False. "{0}" given.'.format(testmode))
self.testmode = testmode
self.proxy = None;
self.proxy = None
self.delimiter = '|'
self.results = []
self.error = True
@@ -117,8 +121,9 @@ class AIM:
else:
url = 'https://secure.authorize.net/gateway/transact.dll'
if self.proxy == None:
self.results += str(urllib.urlopen(url, encoded_args).read()).split(self.delimiter)
if self.proxy is None:
self.results += str(urllib.urlopen(
url, encoded_args).read()).split(self.delimiter)
else:
opener = urllib.FancyURLopener(self.proxy)
opened = opener.open(url, encoded_args)
@@ -147,36 +152,37 @@ class AIM:
raise AIM.AIMError(self.response.ResponseText)
def setTransaction(self, creditcard, expiration, total, cvv=None, tax=None, invoice=None):
if str(creditcard).strip() == '' or creditcard == None:
if str(creditcard).strip() == '' or creditcard is None:
raise AIM.AIMError('No credit card number passed to setTransaction(): {0}'.format(creditcard))
if str(expiration).strip() == '' or expiration == None:
if str(expiration).strip() == '' or expiration is None:
raise AIM.AIMError('No expiration number to setTransaction(): {0}'.format(expiration))
if str(total).strip() == '' or total == None:
if str(total).strip() == '' or total is None:
raise AIM.AIMError('No total amount passed to setTransaction(): {0}'.format(total))
self.setParameter('x_card_num', creditcard)
self.setParameter('x_exp_date', expiration)
self.setParameter('x_amount', total)
if cvv != None:
if cvv is not None:
self.setParameter('x_card_code', cvv)
if tax != None:
if tax is not None:
self.setParameter('x_tax', tax)
if invoice != None:
if invoice is not None:
self.setParameter('x_invoice_num', invoice)
def setTransactionType(self, transtype=None):
types = ['AUTH_CAPTURE', 'AUTH_ONLY', 'PRIOR_AUTH_CAPTURE', 'CREDIT', 'CAPTURE_ONLY', 'VOID']
types = ['AUTH_CAPTURE', 'AUTH_ONLY', 'PRIOR_AUTH_CAPTURE',
'CREDIT', 'CAPTURE_ONLY', 'VOID']
if transtype.upper() not in types:
raise AIM.AIMError('Incorrect Transaction Type passed to setTransactionType(): {0}'.format(transtype))
self.setParameter('x_type', transtype.upper())
def setProxy(self, proxy=None):
if str(proxy).strip() == '' or proxy == None:
if str(proxy).strip() == '' or proxy is None:
raise AIM.AIMError('No proxy passed to setProxy()')
self.proxy = {'http': str(proxy).strip()}
def setParameter(self, key=None, value=None):
if key != None and value != None and str(key).strip() != '' and str(value).strip() != '':
if key is not None and value is not None and str(key).strip() != '' and str(value).strip() != '':
self.parameters[key] = str(value).strip()
else:
raise AIM.AIMError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value))
@@ -194,10 +200,11 @@ class AIM:
responses = ['', 'Approved', 'Declined', 'Error']
return responses[int(self.results[0])]
def process(creditcard,expiration,total,cvv=None,tax=None,invoice=None,
login='cnpdev4289', transkey='SR2P8g4jdEn7vFLQ',testmode=True):
payment = AIM(login,transkey,testmode)
expiration = expiration.replace('/','')
def process(creditcard, expiration, total, cvv=None, tax=None, invoice=None,
login='cnpdev4289', transkey='SR2P8g4jdEn7vFLQ', testmode=True):
payment = AIM(login, transkey, testmode)
expiration = expiration.replace('/', '')
payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice)
try:
payment.process()
@@ -205,6 +212,7 @@ def process(creditcard,expiration,total,cvv=None,tax=None,invoice=None,
except AIM.AIMError:
return False
def test():
import socket
import sys
@@ -215,12 +223,14 @@ def test():
total = '1.00'
cvv = '123'
tax = '0.00'
invoice = str(time())[4:10] # get a random invoice number
invoice = str(time())[4:10] # get a random invoice number
try:
payment = AIM('cnpdev4289', 'SR2P8g4jdEn7vFLQ', True)
payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice)
payment.setParameter('x_duplicate_window', 180) # three minutes duplicate windows
payment.setTransaction(
creditcard, expiration, total, cvv, tax, invoice)
payment.setParameter(
'x_duplicate_window', 180) # three minutes duplicate windows
payment.setParameter('x_cust_id', '1324') # customer ID
payment.setParameter('x_first_name', 'John')
payment.setParameter('x_last_name', 'Conde')
@@ -232,7 +242,8 @@ def test():
payment.setParameter('x_country', 'US')
payment.setParameter('x_phone', '800-555-1234')
payment.setParameter('x_description', 'Test Transaction')
payment.setParameter('x_customer_ip', socket.gethostbyname(socket.gethostname()))
payment.setParameter(
'x_customer_ip', socket.gethostbyname(socket.gethostname()))
payment.setParameter('x_email', 'john@example.com')
payment.setParameter('x_email_customer', False)
payment.process()
@@ -251,9 +262,9 @@ def test():
except AIM.AIMError, e:
print "Exception thrown:", e
print 'An error occured'
print 'approved',payment.isApproved()
print 'declined',payment.isDeclined()
print 'error',payment.isError()
print 'approved', payment.isApproved()
print 'declined', payment.isDeclined()
print 'error', payment.isError()
if __name__=='__main__':
if __name__ == '__main__':
test()
+31 -26
View File
@@ -15,25 +15,27 @@ __all__ = ['DowCommerce']
from operator import itemgetter
import urllib
class DowCommerce:
class DowCommerceError(Exception):
def __init__(self, value):
self.parameter = value
def __str__(self):
return str(self.parameter)
def __init__(self, username=None, password=None, demomode=False):
if not demomode:
if str(username).strip() == '' or username == None:
if str(username).strip() == '' or username is None:
raise DowCommerce.DowCommerceError('No username provided')
if str(password).strip() == '' or password == None:
if str(password).strip() == '' or password is None:
raise DowCommerce.DowCommerceError('No password provided')
else:
username = 'demo'
password = 'password'
self.proxy = None;
self.proxy = None
self.delimiter = '&'
self.results = {}
self.error = True
@@ -45,11 +47,11 @@ class DowCommerce:
self.setParameter('username', username)
self.setParameter('password', password)
def process(self):
encoded_args = urllib.urlencode(self.parameters)
if self.proxy == None:
results = str(urllib.urlopen(self.url, encoded_args).read()).split(self.delimiter)
if self.proxy is None:
results = str(urllib.urlopen(
self.url, encoded_args).read()).split(self.delimiter)
else:
opener = urllib.FancyURLopener(self.proxy)
opened = opener.open(self.url, encoded_args)
@@ -59,7 +61,7 @@ class DowCommerce:
opened.close()
for result in results:
(key,val) = result.split('=')
(key, val) = result.split('=')
self.results[key] = val
if self.results['response'] == '1':
@@ -80,17 +82,18 @@ class DowCommerce:
self.declined = False
raise DowCommerce.DowCommerceError(self.results)
def setTransaction(self, creditcard, expiration, total, cvv=None, orderid=None, orderdescription=None,
ipaddress=None, tax=None, shipping=None,
firstname=None, lastname=None, company=None, address1=None, address2=None, city=None, state=None, zipcode=None,
country=None, phone=None, fax=None, emailaddress=None, website=None,
shipping_firstname=None, shipping_lastname=None, shipping_company=None, shipping_address1=None, shipping_address2=None,
shipping_city=None, shipping_state=None, shipping_zipcode = None, shipping_country=None, shipping_emailaddress=None):
if str(creditcard).strip() == '' or creditcard == None:
def setTransaction(
self, creditcard, expiration, total, cvv=None, orderid=None, orderdescription=None,
ipaddress=None, tax=None, shipping=None,
firstname=None, lastname=None, company=None, address1=None, address2=None, city=None, state=None, zipcode=None,
country=None, phone=None, fax=None, emailaddress=None, website=None,
shipping_firstname=None, shipping_lastname=None, shipping_company=None, shipping_address1=None, shipping_address2=None,
shipping_city=None, shipping_state=None, shipping_zipcode=None, shipping_country=None, shipping_emailaddress=None):
if str(creditcard).strip() == '' or creditcard is None:
raise DowCommerce.DowCommerceError('No credit card number passed to setTransaction(): {0}'.format(creditcard))
if str(expiration).strip() == '' or expiration == None:
if str(expiration).strip() == '' or expiration is None:
raise DowCommerce.DowCommerceError('No expiration number passed to setTransaction(): {0}'.format(expiration))
if str(total).strip() == '' or total == None:
if str(total).strip() == '' or total is None:
raise DowCommerce.DowCommerceError('No total amount passed to setTransaction(): {0}'.format(total))
self.setParameter('ccnumber', creditcard)
@@ -165,12 +168,12 @@ class DowCommerce:
self.setParameter('type', transtype.lower())
def setProxy(self, proxy=None):
if str(proxy).strip() == '' or proxy == None:
if str(proxy).strip() == '' or proxy is None:
raise DowCommerce.DowCommerceError('No proxy passed to setProxy()')
self.proxy = {'http': str(proxy).strip()}
def setParameter(self, key=None, value=None):
if key != None and value != None and str(key).strip() != '' and str(value).strip() != '':
if key is not None and value is not None and str(key).strip() != '' and str(value).strip() != '':
self.parameters[key] = str(value).strip()
else:
raise DowCommerce.DowCommerceError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value))
@@ -194,6 +197,7 @@ class DowCommerce:
def getResponseText(self):
return self.results['responsetext']
def test():
import socket
import sys
@@ -212,13 +216,14 @@ def test():
total = '1.00'
cvv = '999'
tax = '0.00'
orderid = str(time())[4:10] # get a random invoice number
orderid = str(time())[4:10] # get a random invoice number
try:
payment = DowCommerce(demomode=True)
payment.setTransaction(creditcard, expiration, total, cvv=cvv, tax=tax, orderid=orderid, orderdescription='Test Transaction',
firstname='John', lastname='Doe', company='Acme', address1='123 Min Street', city='Hometown', state='VA',
zipcode='12345', country='US', phone='888-555-1212', emailaddress='john@noemail.local', ipaddress='192.168.1.1')
payment.setTransaction(
creditcard, expiration, total, cvv=cvv, tax=tax, orderid=orderid, orderdescription='Test Transaction',
firstname='John', lastname='Doe', company='Acme', address1='123 Min Street', city='Hometown', state='VA',
zipcode='12345', country='US', phone='888-555-1212', emailaddress='john@noemail.local', ipaddress='192.168.1.1')
payment.process()
if payment.isApproved():
@@ -231,9 +236,9 @@ def test():
except DowCommerce.DowCommerceError, e:
print "Exception thrown:", e
print 'An error occured'
print 'approved',payment.isApproved()
print 'declined',payment.isDeclined()
print 'error',payment.isError()
print 'approved', payment.isApproved()
print 'declined', payment.isDeclined()
print 'error', payment.isError()
if __name__=='__main__':
if __name__ == '__main__':
test()
-1
View File
@@ -1 +0,0 @@
+29 -16
View File
@@ -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 '<img src="%s" style="max-width:100%%"/>' % url
def audio(url):
return '<audio controls="controls" style="max-width:100%%"><source src="%s" /></audio>' % url
def video(url):
return '<video controls="controls" style="max-width:100%%"><source src="%s" /></video>' % url
def googledoc_viewer(url):
return '<iframe src="http://docs.google.com/viewer?url=%s&embedded=true" style="max-width:100%%"></iframe>' % urllib.quote(url)
def web2py_component(url):
code = str(uuid.uuid4())
return '<div id="%s"></div><script>\nweb2py_component("%s","%s");\n</script>' % (code,url,code)
return '<div id="%s"></div><script>\nweb2py_component("%s","%s");\n</script>' % (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 '<a href="%(u)s">%(u)s</a>' % 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 = """
<h3>Fringilla nisi parturient nullam</h3>
<p>http://www.youtube.com/watch?v=IWBFiI5RrA0</p>
<p>http://www.web2py.com/examples/static/images/logo_bw.png</p>
@@ -198,8 +211,8 @@ laoreet tortor.</p>
"""
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()
+12 -11
View File
@@ -12,6 +12,7 @@ cache.ram=cache.disk=MemcacheClient(request)
import time
from google.appengine.api.memcache import Client
class MemcacheClient(object):
client = Client()
@@ -24,12 +25,12 @@ class MemcacheClient(object):
key,
f,
time_expire=300,
):
):
key = '%s/%s' % (self.request.application, key)
dt = time_expire
value = None
obj = self.client.get(key)
if obj and (dt == None or obj[0] > time.time() - dt):
if obj and (dt is None or obj[0] > time.time() - dt):
value = obj[1]
elif f is None:
if obj:
@@ -47,21 +48,21 @@ class MemcacheClient(object):
self.client.set(key, (time.time(), value))
return value
def clear(self, key = None):
def clear(self, key=None):
if key:
key = '%s/%s' % (self.request.application, key)
self.client.delete(key)
else:
self.client.flush_all()
def delete(self,*a,**b):
return self.client.delete(*a,**b)
def delete(self, *a, **b):
return self.client.delete(*a, **b)
def get(self,*a,**b):
return self.client.delete(*a,**b)
def get(self, *a, **b):
return self.client.delete(*a, **b)
def set(self,*a,**b):
return self.client.delete(*a,**b)
def set(self, *a, **b):
return self.client.delete(*a, **b)
def flush_all(self,*a,**b):
return self.client.delete(*a,**b)
def flush_all(self, *a, **b):
return self.client.delete(*a, **b)
+8 -5
View File
@@ -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:
+25 -16
View File
@@ -12,6 +12,7 @@ from gluon.sanitizer import sanitize
from gluon.contrib.markmin.markmin2latex import markmin2latex
from gluon.contrib.markmin.markmin2pdf import markmin2pdf
def wrapper(f):
def g(data):
try:
@@ -25,39 +26,47 @@ def wrapper(f):
raise HTTP(405, '%s error' % e)
return g
def latex_from_html(html):
markmin=TAG(html).element('body').flatten(markmin_serializer)
markmin = TAG(html).element('body').flatten(markmin_serializer)
return XML(markmin2latex(markmin))
def pdflatex_from_html(html):
if os.system('which pdflatex > /dev/null')==0:
markmin=TAG(html).element('body').flatten(markmin_serializer)
out,warnings,errors=markmin2pdf(markmin)
if os.system('which pdflatex > /dev/null') == 0:
markmin = TAG(html).element('body').flatten(markmin_serializer)
out, warnings, errors = markmin2pdf(markmin)
if errors:
current.response.headers['Content-Type']='text/html'
raise HTTP(405,HTML(BODY(H1('errors'),
UL(*errors),
H1('warnings'),
UL(*warnings))).xml())
current.response.headers['Content-Type'] = 'text/html'
raise HTTP(405, HTML(BODY(H1('errors'),
UL(*errors),
H1('warnings'),
UL(*warnings))).xml())
else:
return XML(out)
def pyfpdf_from_html(html):
request = current.request
def image_map(path):
if path.startswith('/%s/static/' % request.application):
return os.path.join(request.folder,path.split('/',2)[2])
return 'http%s://%s%s' % (request.is_https and 's' or '',request.env.http_host, path)
class MyFPDF(FPDF, HTMLMixin): pass
pdf=MyFPDF()
return os.path.join(request.folder, path.split('/', 2)[2])
return 'http%s://%s%s' % (request.is_https and 's' or '', request.env.http_host, path)
class MyFPDF(FPDF, HTMLMixin):
pass
pdf = MyFPDF()
pdf.add_page()
html = sanitize(html, escape=False) #### should have better list of allowed tags
pdf.write_html(html,image_map=image_map)
html = sanitize(
html, escape=False) # should have better list of allowed tags
pdf.write_html(html, image_map=image_map)
return XML(pdf.output(dest='S'))
def pdf_from_html(html):
# try use latex and pdflatex
if os.system('which pdflatex > /dev/null')==0:
if os.system('which pdflatex > /dev/null') == 0:
return pdflatex_from_html(html)
else:
return pyfpdf_from_html(html)
+4 -3
View File
@@ -1,5 +1,6 @@
from gluon import XML
def button(merchant_id="123456789012345",
products=[dict(name="shoes",
quantity=1,
@@ -8,8 +9,8 @@ def button(merchant_id="123456789012345",
description="running shoes black")]):
t = '<input name="item_%(key)s_%(k)s" type="hidden" value="%(value)s"/>'
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 = '<form action="https://checkout.google.com/api/checkout/v2/checkoutForm/Merchant/%s" id="BB_BuyButtonForm" method="post" name="BB_BuyButtonForm" target="_top">%s<input name="_charset_" type="hidden" value="utf-8"/><input alt="" src="https://checkout.google.com/buttons/buy.gif?merchant_id=%s&amp;w=117&amp;h=48&amp;style=white&amp;variant=text&amp;loc=en_US" type="image"/></form>' % (merchant_id, list_products, merchant_id)
return XML(button)
+1 -1
View File
@@ -1,5 +1,5 @@
# this file exists for backward compatibility
__all__ = ['DAL','Field','drivers','gae']
__all__ = ['DAL', 'Field', 'drivers', 'gae']
from gluon.dal import DAL, Field, Table, Query, Set, Expression, Row, Rows, drivers, BaseAdapter, SQLField, SQLTable, SQLXorable, SQLQuery, SQLSet, SQLRows, SQLStorage, SQLDB, GQLDB, SQLALL, SQLCustomType, gae
+2
View File
@@ -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:
-3
View File
@@ -1,3 +0,0 @@
+3 -5
View File
@@ -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
@@ -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
+57 -52
View File
@@ -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))
+41 -38
View File
@@ -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)
+4 -4
View File
@@ -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
@@ -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
@@ -11,6 +11,7 @@ Thanks to Hans Donner <hans.donner@pobox.com> 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")
+28 -15
View File
@@ -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
+11 -11
View File
@@ -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)
+40 -38
View File
@@ -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
+17 -11
View File
@@ -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
+15 -20
View File
@@ -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 <a href='" + auth_request_url + "'> authentication server</a>",
Location=auth_request_url)
return None
+24 -25
View File
@@ -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 <a href='" + auth_request_url + "'> authentication server</a>",
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
+93 -76
View File
@@ -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()
+1 -2
View File
@@ -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
+47 -41
View File
@@ -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)
+19 -26
View File
@@ -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
+61 -62
View File
@@ -44,22 +44,21 @@ SQL_DIALECTS = {'memcache': {
'is not null': 'IS NOT NULL',
'extract': None,
'left join': None,
}}
}}
def cleanup(text):
if re.compile('[^0-9a-zA-Z_]').findall(text):
raise SyntaxError, \
'Can\'t cleanup \'%s\': only [0-9a-zA-Z_] allowed in table and field names' % text
raise SyntaxError('Can\'t cleanup \'%s\': only [0-9a-zA-Z_] allowed in table and field names' % text)
return text
def assert_filter_fields(*fields):
for field in fields:
if isinstance(field, (Field, Expression)) and field.type\
in ['text', 'blob']:
raise SyntaxError, 'AppEngine does not index by: %s'\
% field.type
in ['text', 'blob']:
raise SyntaxError('AppEngine does not index by: %s'
% field.type)
def dateobj_to_datetime(object):
@@ -78,7 +77,7 @@ def dateobj_to_datetime(object):
object.minute,
object.second,
object.microsecond,
)
)
return object
@@ -96,7 +95,7 @@ def sqlhtml_validators(field_type, length):
'time': validators.IS_TIME(),
'datetime': validators.IS_DATETIME(),
'reference': validators.IS_INT_IN_RANGE(0, 1e100),
}
}
try:
return v[field_type[:9]]
except KeyError:
@@ -114,7 +113,8 @@ class DALStorage(dict):
def __setattr__(self, key, value):
if key in self:
raise SyntaxError, 'Object \'%s\'exists and cannot be redefined' % key
raise SyntaxError(
'Object \'%s\'exists and cannot be redefined' % key)
self[key] = value
def __repr__(self):
@@ -151,14 +151,14 @@ class MEMDB(DALStorage):
tablename,
*fields,
**args
):
):
tablename = cleanup(tablename)
if tablename in dir(self) or tablename[0] == '_':
raise SyntaxError, 'invalid table name: %s' % tablename
raise SyntaxError('invalid table name: %s' % tablename)
if not tablename in self.tables:
self.tables.append(tablename)
else:
raise SyntaxError, 'table already defined: %s' % tablename
raise SyntaxError('table already defined: %s' % tablename)
t = self[tablename] = Table(self, tablename, *fields)
t._create()
return t
@@ -190,7 +190,7 @@ class Table(DALStorage):
db,
tablename,
*fields
):
):
self._db = db
self._tablename = tablename
self.fields = SQLCallableList()
@@ -219,24 +219,24 @@ class Table(DALStorage):
if field.type[:9] == 'reference':
referenced = field.type[10:].strip()
if not referenced:
raise SyntaxError, \
'Table %s: reference \'%s\' to nothing!' % (self._tablename, k)
raise SyntaxError('Table %s: reference \'%s\' to nothing!' % (
self._tablename, k))
if not referenced in self._db:
raise SyntaxError, \
'Table: table %s does not exist' % referenced
raise SyntaxError(
'Table: table %s does not exist' % referenced)
referee = self._db[referenced]
ftype = \
self._db._translator[field.type[:9]](
self._db[referenced]._tableobj)
if self._tablename in referee.fields: # ## THIS IS OK
raise SyntaxError, \
'Field: table \'%s\' has same name as a field ' \
'in referenced table \'%s\'' % (self._tablename, referenced)
raise SyntaxError('Field: table \'%s\' has same name as a field '
'in referenced table \'%s\'' % (
self._tablename, referenced))
self._db[referenced]._referenced_by.append((self._tablename,
field.name))
field.name))
elif not field.type in self._db._translator\
or not self._db._translator[field.type]:
raise SyntaxError, 'Field: unkown field type %s' % field.type
or not self._db._translator[field.type]:
raise SyntaxError('Field: unkown field type %s' % field.type)
self._tableobj = self._db.client
return None
@@ -269,11 +269,11 @@ class Table(DALStorage):
def update(self, id, **fields):
for field in fields:
if not field in fields and self[field].default\
!= None:
is not None:
fields[field] = self[field].default
if field in fields:
fields[field] = obj_represent(fields[field],
self[field].type, self._db)
self[field].type, self._db)
return self._tableobj.set(self._id_to_key(id), fields)
def delete(self, id):
@@ -293,7 +293,7 @@ class Table(DALStorage):
if self._tableobj.set(shard_id, '0'):
id = 0
else:
raise Exception, 'cannot set memcache'
raise Exception('cannot set memcache')
return long(str(shard) + str(id))
def __str__(self):
@@ -307,7 +307,7 @@ class Expression(object):
name,
type='string',
db=None,
):
):
(self.name, self.type, self._db) = (name, type, db)
def __str__(self):
@@ -393,11 +393,11 @@ class Field(Expression):
notnull=False,
unique=False,
uploadfield=True,
):
):
self.name = cleanup(fieldname)
if fieldname in dir(Table) or fieldname[0] == '_':
raise SyntaxError, 'Field: invalid field name: %s' % fieldname
raise SyntaxError('Field: invalid field name: %s' % fieldname)
if isinstance(type, Table):
type = 'reference ' + type._tablename
if not length:
@@ -437,9 +437,9 @@ MEMDB.Field = Field # ## required by gluon/globals.py session.connect
def obj_represent(object, fieldtype, db):
if object != None:
if object is not None:
if fieldtype == 'date' and not isinstance(object,
datetime.date):
datetime.date):
(y, m, d) = [int(x) for x in str(object).strip().split('-')]
object = datetime.date(y, m, d)
elif fieldtype == 'time' and not isinstance(object, datetime.time):
@@ -450,7 +450,7 @@ def obj_represent(object, fieldtype, db):
(h, mi, s) = time_items + [0]
object = datetime.time(h, mi, s)
elif fieldtype == 'datetime' and not isinstance(object,
datetime.datetime):
datetime.datetime):
(y, m, d) = [int(x) for x in
str(object)[:10].strip().split('-')]
time_items = [int(x) for x in
@@ -466,7 +466,7 @@ def obj_represent(object, fieldtype, db):
h,
mi,
s,
)
)
elif fieldtype == 'integer' and not isinstance(object, long):
object = long(object)
@@ -496,10 +496,10 @@ class Query(object):
left,
op=None,
right=None,
):
):
if isinstance(right, (Field, Expression)):
raise SyntaxError, \
'Query: right side of filter must be a value or entity'
raise SyntaxError(
'Query: right side of filter must be a value or entity')
if isinstance(left, Field) and left.name == 'id':
if op == '=':
self.get_one = \
@@ -507,8 +507,8 @@ class Query(object):
id=long(right))
return
else:
raise SyntaxError, 'only equality by id is supported'
raise SyntaxError, 'not supported'
raise SyntaxError('only equality by id is supported')
raise SyntaxError('not supported')
def __str__(self):
return str(self.left)
@@ -539,7 +539,7 @@ class Set(object):
self.where = where
self._tables.insert(0, where.get_all)
elif hasattr(where, 'get_one') and isinstance(where.get_one,
QueryException):
QueryException):
self.where = where.get_one
else:
@@ -553,9 +553,8 @@ class Set(object):
def __call__(self, where):
if isinstance(self.where, QueryException) or isinstance(where,
QueryException):
raise SyntaxError, \
'neither self.where nor where can be a QueryException instance'
QueryException):
raise SyntaxError('neither self.where nor where can be a QueryException instance')
if self.where:
return Set(self._db, self.where & where)
else:
@@ -564,9 +563,9 @@ class Set(object):
def _get_table_or_raise(self):
tablenames = list(set(self._tables)) # unique
if len(tablenames) < 1:
raise SyntaxError, 'Set: no tables selected'
raise SyntaxError('Set: no tables selected')
if len(tablenames) > 1:
raise SyntaxError, 'Set: no join in appengine'
raise SyntaxError('Set: no join in appengine')
return self._db[tablenames[0]]._tableobj
def _getitem_exception(self):
@@ -597,7 +596,7 @@ class Set(object):
if isinstance(self.where, QueryException):
return self._select_except()
else:
raise SyntaxError, 'select arguments not supported'
raise SyntaxError('select arguments not supported')
def count(self):
return len(self.select())
@@ -609,7 +608,7 @@ class Set(object):
return
self._db[tablename].delete(id)
else:
raise Exception, 'deletion not implemented'
raise Exception('deletion not implemented')
def update(self, **update_fields):
if isinstance(self.where, QueryException):
@@ -620,7 +619,7 @@ class Set(object):
setattr(item, key, value)
self._db[tablename].update(id, **item)
else:
raise Exception, 'update not implemented'
raise Exception('update not implemented')
def update_record(
@@ -628,7 +627,7 @@ def update_record(
s,
id,
a,
):
):
item = s.get(id)
for (key, value) in a.items():
t[key] = value
@@ -650,7 +649,7 @@ class Rows(object):
db,
response,
*colnames
):
):
self._db = db
self.colnames = colnames
self.response = response
@@ -660,9 +659,9 @@ class Rows(object):
def __getitem__(self, i):
if i >= len(self.response) or i < 0:
raise SyntaxError, 'Rows: no such row: %i' % i
raise SyntaxError('Rows: no such row: %i' % i)
if len(self.response[0]) != len(self.colnames):
raise SyntaxError, 'Rows: internal error'
raise SyntaxError('Rows: internal error')
row = DALStorage()
for j in xrange(len(self.colnames)):
value = self.response[i][j]
@@ -684,7 +683,7 @@ class Rows(object):
referee = field.type[10:].strip()
rid = value
row[tablename][fieldname] = rid
elif field.type == 'boolean' and value != None:
elif field.type == 'boolean' and value is not None:
# row[tablename][fieldname]=Set(self._db[referee].id==rid)
@@ -692,13 +691,13 @@ class Rows(object):
row[tablename][fieldname] = True
else:
row[tablename][fieldname] = False
elif field.type == 'date' and value != None\
and not isinstance(value, datetime.date):
elif field.type == 'date' and value is not None\
and not isinstance(value, datetime.date):
(y, m, d) = [int(x) for x in
str(value).strip().split('-')]
row[tablename][fieldname] = datetime.date(y, m, d)
elif field.type == 'time' and value != None\
and not isinstance(value, datetime.time):
elif field.type == 'time' and value is not None\
and not isinstance(value, datetime.time):
time_items = [int(x) for x in
str(value).strip().split(':')[:3]]
if len(time_items) == 3:
@@ -706,8 +705,8 @@ class Rows(object):
else:
(h, mi, s) = time_items + [0]
row[tablename][fieldname] = datetime.time(h, mi, s)
elif field.type == 'datetime' and value != None\
and not isinstance(value, datetime.datetime):
elif field.type == 'datetime' and value is not None\
and not isinstance(value, datetime.datetime):
(y, m, d) = [int(x) for x in
str(value)[:10].strip().split('-')]
time_items = [int(x) for x in
@@ -723,19 +722,19 @@ class Rows(object):
h,
mi,
s,
)
)
else:
row[tablename][fieldname] = value
if fieldname == 'id':
id = row[tablename].id
row[tablename].update_record = lambda t = row[tablename], \
s = self._db[tablename], id = id, **a: update_record(t,
s, id, a)
s, id, a)
for (referee_table, referee_name) in \
table._referenced_by:
table._referenced_by:
s = self._db[referee_table][referee_name]
row[tablename][referee_table] = Set(self._db, s
== id)
== id)
if len(row.keys()) == 1:
return row[row.keys()[0]]
return row
+33 -32
View File
@@ -11,7 +11,7 @@ Modified for inclusion into web2py by: Ross Peoples <ross.peoples@gmail.com>
"""
from StringIO import StringIO # The pure-Python StringIO supports unicode.
from StringIO import StringIO # The pure-Python StringIO supports unicode.
import re
@@ -20,7 +20,7 @@ __version__ = '0.1.4'
def remove_comments(css):
"""Remove all CSS comment blocks."""
iemac = False
preserve = False
comment_start = css.find("/*")
@@ -28,7 +28,7 @@ def remove_comments(css):
# Preserve comments that look like `/*!...*/`.
# Slicing is used to make sure we don"t get an IndexError.
preserve = css[comment_start + 2:comment_start + 3] == "!"
comment_end = css.find("*/", comment_start + 2)
if comment_end < 0:
if not preserve:
@@ -48,22 +48,22 @@ def remove_comments(css):
else:
comment_start = comment_end + 2
comment_start = css.find("/*", comment_start)
return css
def remove_unnecessary_whitespace(css):
"""Remove unnecessary whitespace characters."""
def pseudoclasscolon(css):
"""
Prevents 'p :link' from becoming 'p:link'.
Translates 'p :link' into 'p ___PSEUDOCLASSCOLON___link'; this is
translated back again later.
"""
regex = re.compile(r"(^|\})(([^\{\:])+\:)+([^\{]*\{)")
match = regex.search(css)
while match:
@@ -73,43 +73,43 @@ def remove_unnecessary_whitespace(css):
css[match.end():]])
match = regex.search(css)
return css
css = pseudoclasscolon(css)
# Remove spaces from before things.
css = re.sub(r"\s+([!{};:>+\(\)\],])", r"\1", css)
# If there is a `@charset`, then only allow one, and move to the beginning.
css = re.sub(r"^(.*)(@charset \"[^\"]*\";)", r"\2\1", css)
css = re.sub(r"^(\s*@charset [^;]+;\s*)+", r"\1", css)
# Put the space back in for a few cases, such as `@media screen` and
# `(-webkit-min-device-pixel-ratio:0)`.
css = re.sub(r"\band\(", "and (", css)
# Put the colons back.
css = css.replace('___PSEUDOCLASSCOLON___', ':')
# Remove spaces from after things.
css = re.sub(r"([!{}:;>+\(\[,])\s+", r"\1", css)
return css
def remove_unnecessary_semicolons(css):
"""Remove unnecessary semicolons."""
return re.sub(r";+\}", "}", css)
def remove_empty_rules(css):
"""Remove empty rules."""
return re.sub(r"[^\}\{]+\{\}", "", css)
def normalize_rgb_colors_to_hex(css):
"""Convert `rgb(51,102,153)` to `#336699`."""
regex = re.compile(r"rgb\s*\(\s*([0-9,\s]+)\s*\)")
match = regex.search(css)
while match:
@@ -122,39 +122,40 @@ def normalize_rgb_colors_to_hex(css):
def condense_zero_units(css):
"""Replace `0(px, em, %, etc)` with `0`."""
return re.sub(r"([\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", r"\1\2", css)
def condense_multidimensional_zeros(css):
"""Replace `:0 0 0 0;`, `:0 0 0;` etc. with `:0;`."""
css = css.replace(":0 0 0 0;", ":0;")
css = css.replace(":0 0 0;", ":0;")
css = css.replace(":0 0;", ":0;")
# Revert `background-position:0;` to the valid `background-position:0 0;`.
css = css.replace("background-position:0;", "background-position:0 0;")
return css
def condense_floating_points(css):
"""Replace `0.6` with `.6` where possible."""
return re.sub(r"(:|\s)0+\.(\d+)", r"\1.\2", css)
def condense_hex_colors(css):
"""Shorten colors from #AABBCC to #ABC where possible."""
regex = re.compile(r"([^\"'=\s])(\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])")
match = regex.search(css)
while match:
first = match.group(3) + match.group(5) + match.group(7)
second = match.group(4) + match.group(6) + match.group(8)
if first.lower() == second.lower():
css = css.replace(match.group(), match.group(1) + match.group(2) + '#' + first)
css = css.replace(
match.group(), match.group(1) + match.group(2) + '#' + first)
match = regex.search(css, match.end() - 3)
else:
match = regex.search(css, match.end())
@@ -163,19 +164,19 @@ def condense_hex_colors(css):
def condense_whitespace(css):
"""Condense multiple adjacent whitespace characters into one."""
return re.sub(r"\s+", " ", css)
def condense_semicolons(css):
"""Condense multiple adjacent semicolon characters into one."""
return re.sub(r";;+", ";", css)
def wrap_css_lines(css, line_length):
"""Wrap the lines of the given CSS to an approximate length."""
lines = []
line_start = 0
for i, char in enumerate(css):
@@ -183,7 +184,7 @@ def wrap_css_lines(css, line_length):
if char == '}' and (i - line_start >= line_length):
lines.append(css[line_start:i + 1])
line_start = i + 1
if line_start < len(css):
lines.append(css[line_start:])
return '\n'.join(lines)
@@ -212,19 +213,19 @@ def cssmin(css, wrap=None):
def main():
import optparse
import sys
p = optparse.OptionParser(
prog="cssmin", version=__version__,
usage="%prog [--wrap N]",
description="""Reads raw CSS from stdin, and writes compressed CSS to stdout.""")
p.add_option(
'-w', '--wrap', type='int', default=None, metavar='N',
help="Wrap output to approximately N chars per line.")
options, args = p.parse_args()
sys.stdout.write(cssmin(sys.stdin.read(), wrap=options.wrap))
if __name__ == '__main__':
main()
main()
+10 -8
View File
@@ -2,12 +2,14 @@
import re
def minify(response):
def _replace(match):
match = match.group()
# save whole <pre>, <textarea> tags, and opening <!-- (so it doesn't break <script>)
# otherwise, replace all whitespace with a single space character
return match if match.startswith(('<pre', '<textarea', '<!--')) else ' '
cpat = re.compile(r'\s+|<pre(.*?)</pre>|<textarea(.*?)</textarea>|<!--\s', re.DOTALL)
return cpat.sub(_replace, response)
def _replace(match):
match = match.group()
# save whole <pre>, <textarea> tags, and opening <!-- (so it doesn't break <script>)
# otherwise, replace all whitespace with a single space character
return match if match.startswith(('<pre', '<textarea', '<!--')) else ' '
cpat = re.compile(
r'\s+|<pre(.*?)</pre>|<textarea(.*?)</textarea>|<!--\s', re.DOTALL)
return cpat.sub(_replace, response)
+44 -27
View File
@@ -49,7 +49,7 @@ Both python 2 and python 3 are supported.
.. _jsmin.c by Douglas Crockford:
http://www.crockford.com/javascript/jsmin.c
Original author of Python version: Andr\xe9 Malo
Home page: http://opensource.perlig.de/rjsmin/
Modified by Ross Peoples <ross.peoples@gmail.com> for inclusion into web2py.
@@ -96,7 +96,7 @@ def _make_jsmin(extended=True, python_only=True):
try:
xrange
except NameError:
xrange = range # pylint: disable = W0622
xrange = range # pylint: disable = W0622
space_chars = r'[\000-\011\013\014\016-\040]'
@@ -151,13 +151,13 @@ def _make_jsmin(extended=True, python_only=True):
last != first and chr(last) or ''
) for first, last in result])
return _re.sub(r'([\000-\040\047])', # for better portability
lambda m: '\\%03o' % ord(m.group(1)), (sequentize(result)
.replace('\\', '\\\\')
.replace('[', '\\[')
.replace(']', '\\]')
)
)
return _re.sub(r'([\000-\040\047])', # for better portability
lambda m: '\\%03o' % ord(m.group(1)), (sequentize(result)
.replace('\\', '\\\\')
.replace('[', '\\[')
.replace(']', '\\]')
)
)
def id_literal_(what):
""" Make id_literal like char class """
@@ -190,25 +190,33 @@ def _make_jsmin(extended=True, python_only=True):
r'|(?:(?<=%(preregex1)s)%(space)s*(%(regex)s[^\047"/\000-\040]*))'
r'|(?:(?<=%(preregex2)s)%(space)s*(%(regex)s[^\047"/\000-\040]*))'
r'|(?<=%(id_literal_close)s)'
r'%(space)s*(?:(%(newline)s)%(space)s*)+'
r'(?=%(id_literal_open)s)'
r'%(space)s*(?:(%(newline)s)%(space)s*)+'
r'(?=%(id_literal_open)s)'
r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)'
r'|%(space)s+'
r'|(?:%(newline)s%(space)s*)+'
) % locals()).sub
def space_subber(match):
""" Substitution callback """
# pylint: disable = C0321, R0911
groups = match.groups()
if groups[0]: return groups[0]
elif groups[1]: return groups[1]
elif groups[2]: return groups[2]
elif groups[3]: return groups[3]
elif groups[4]: return '\n'
elif groups[5]: return ' '
else: return ''
if groups[0]:
return groups[0]
elif groups[1]:
return groups[1]
elif groups[2]:
return groups[2]
elif groups[3]:
return groups[3]
elif groups[4]:
return '\n'
elif groups[5]:
return ' '
else:
return ''
def jsmin(script): # pylint: disable = W0621
def jsmin(script): # pylint: disable = W0621
r"""
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
@@ -239,20 +247,26 @@ def _make_jsmin(extended=True, python_only=True):
r'|(%(space)s)+'
r'|(?:(%(newline)s)%(space)s*)+'
) % locals()).sub
def space_norm_subber(match):
""" Substitution callback """
# pylint: disable = C0321
groups = match.groups()
if groups[0]: return groups[0]
elif groups[1]: return groups[1].replace('\r', '\n') + groups[2]
elif groups[3]: return ' '
elif groups[4]: return '\n'
if groups[0]:
return groups[0]
elif groups[1]:
return groups[1].replace('\r', '\n') + groups[2]
elif groups[3]:
return ' '
elif groups[4]:
return '\n'
space_sub1 = _re.compile((
r'[\040\n]?(%(strings)s|%(pre_regex)s%(regex)s)'
r'|\040(%(not_id_literal)s)'
r'|\n(%(not_id_literal_open)s)'
) % locals()).sub
def space_subber1(match):
""" Substitution callback """
groups = match.groups()
@@ -264,6 +278,7 @@ def _make_jsmin(extended=True, python_only=True):
r'|(%(not_id_literal)s)\040'
r'|(%(not_id_literal_close)s)\n'
) % locals()).sub
def space_subber2(match):
""" Substitution callback """
groups = match.groups()
@@ -295,10 +310,11 @@ def _make_jsmin(extended=True, python_only=True):
:Rtype: ``str``
"""
return space_sub2(space_subber2,
space_sub1(space_subber1,
space_norm_sub(space_norm_subber, '\n%s\n' % script)
)
).strip()
space_sub1(space_subber1,
space_norm_sub(space_norm_subber,
'\n%s\n' % script)
)
).strip()
return jsmin
jsmin = _make_jsmin()
@@ -311,6 +327,7 @@ jsmin = _make_jsmin()
# jsmin.jsmin(script)
#
def jsmin_for_posers(script):
r"""
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
+30 -28
View File
@@ -14,24 +14,27 @@ import os
import hashlib
import re
def read_binary_file(filename):
f = open(filename,'rb')
f = open(filename, 'rb')
data = f.read()
f.close()
return data
def write_binary_file(filename,data):
f =open(filename,'wb')
def write_binary_file(filename, data):
f = open(filename, 'wb')
f.write(data)
f.close()
def fix_links(css,static_path):
def fix_links(css, static_path):
return re.sub(r'url\((["\'])\.\./', 'url(\\1' + static_path, css)
def minify(files, path_info, folder, optimize_css, optimize_js,
ignore_concat = [],
ignore_minify = ['/jquery.js', '/anytime.js']):
ignore_concat=[],
ignore_minify=['/jquery.js', '/anytime.js']):
"""
Input:
@@ -45,7 +48,7 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
Returns a new list of:
- filename (absolute or relative, css or js, actual or temporary) or
- ('css:inline','...css..')
- ('js:inline','...js..')
- ('js:inline','...js..')
"""
optimize_css = optimize_css or ''
optimize_js = optimize_js or ''
@@ -55,27 +58,27 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
concat_js = 'concat' in optimize_js
minify_js = 'minify' in optimize_js
inline_js = 'inline' in optimize_js
static_path,temp = path_info.rsplit('/',1)
static_path, temp = path_info.rsplit('/', 1)
new_files = []
css = []
js = []
processed = []
for k,filename in enumerate(files):
for k, filename in enumerate(files):
if not filename.startswith('/') or \
any(filename.endswith(x) \
for x in ignore_concat):
any(filename.endswith(x)
for x in ignore_concat):
new_files.append(filename)
continue
abs_filename = os.path.join(
folder,'static', filename[len(static_path)+1:])
folder, 'static', filename[len(static_path) + 1:])
if filename.lower().endswith('.css'):
processed.append(filename)
spath_info, sfilename = \
path_info.split('/'), filename.split('/')
u = 0
for i,a in enumerate(sfilename):
for i, a in enumerate(sfilename):
try:
if a != spath_info[i]:
u = i
@@ -99,8 +102,8 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
if minify_js and \
not filename.endswith('.min.js') and \
not any(filename.endswith(x) \
for x in ignore_minify):
not any(filename.endswith(x)
for x in ignore_minify):
js.append(jsmin.jsmin(contents))
else:
js.append(contents)
@@ -110,32 +113,31 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
if css and concat_css:
css = '\n\n'.join(contents for contents in css)
if not inline_css:
temppath = os.path.join(folder,'static',temp)
temppath = os.path.join(folder, 'static', temp)
if not os.path.exists(temppath):
os.mkdir(temppath)
dest = "compressed_%s.css" % dest_key
tempfile = os.path.join(temppath, dest)
write_binary_file(tempfile,css)
css = path_info+'/%s' % dest
write_binary_file(tempfile, css)
css = path_info + '/%s' % dest
new_files.append(css)
else:
new_files.append(('css:inline',css))
new_files.append(('css:inline', css))
else:
new_files += css
if js and concat_js:
js = '\n'.join(contents for contents in js)
if inline_js:
js = ('js:inline',js)
js = ('js:inline', js)
else:
temppath = os.path.join(folder,'static',temp)
if not os.path.exists(temppath):
temppath = os.path.join(folder, 'static', temp)
if not os.path.exists(temppath):
os.mkdir(temppath)
dest = "compressed_%s.js" % dest_key
tempfile = os.path.join(folder,'static',temp,dest)
write_binary_file(tempfile,js)
js = path_info+'/%s' % dest
tempfile = os.path.join(folder, 'static', temp, dest)
write_binary_file(tempfile, js)
js = path_info + '/%s' % dest
new_files.append(js)
else:
new_files += js
return new_files
+20 -15
View File
@@ -24,7 +24,7 @@ CALLOC.argtypes = [c_uint, c_uint]
STRDUP = LIBC.strdup
STRDUP.argstypes = [c_char_p]
STRDUP.restype = POINTER(c_char) # NOT c_char_p !!!!
STRDUP.restype = POINTER(c_char) # NOT c_char_p !!!!
# Various constants
PAM_PROMPT_ECHO_OFF = 1
@@ -32,56 +32,61 @@ PAM_PROMPT_ECHO_ON = 2
PAM_ERROR_MSG = 3
PAM_TEXT_INFO = 4
class PamHandle(Structure):
"""wrapper class for pam_handle_t"""
_fields_ = [
("handle", c_void_p)
]
("handle", c_void_p)
]
def __init__(self):
Structure.__init__(self)
self.handle = 0
class PamMessage(Structure):
"""wrapper class for pam_message structure"""
_fields_ = [
("msg_style", c_int),
("msg", c_char_p),
]
("msg_style", c_int),
("msg", c_char_p),
]
def __repr__(self):
return "<PamMessage %i '%s'>" % (self.msg_style, self.msg)
class PamResponse(Structure):
"""wrapper class for pam_response structure"""
_fields_ = [
("resp", c_char_p),
("resp_retcode", c_int),
]
("resp", c_char_p),
("resp_retcode", c_int),
]
def __repr__(self):
return "<PamResponse %i '%s'>" % (self.resp_retcode, self.resp)
CONV_FUNC = CFUNCTYPE(c_int,
c_int, POINTER(POINTER(PamMessage)),
POINTER(POINTER(PamResponse)), c_void_p)
c_int, POINTER(POINTER(PamMessage)),
POINTER(POINTER(PamResponse)), c_void_p)
class PamConv(Structure):
"""wrapper class for pam_conv structure"""
_fields_ = [
("conv", CONV_FUNC),
("appdata_ptr", c_void_p)
]
("conv", CONV_FUNC),
("appdata_ptr", c_void_p)
]
PAM_START = LIBPAM.pam_start
PAM_START.restype = c_int
PAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv),
POINTER(PamHandle)]
POINTER(PamHandle)]
PAM_AUTHENTICATE = LIBPAM.pam_authenticate
PAM_AUTHENTICATE.restype = c_int
PAM_AUTHENTICATE.argtypes = [PamHandle, c_int]
def authenticate(username, password, service='login'):
"""Returns True if the given username and password authenticate for the
given service. Returns False otherwise
+28 -21
View File
@@ -24,6 +24,7 @@ import cmd
import pydoc
import threading
class Qdb(bdb.Bdb):
"Qdb Debugger Backend"
@@ -36,7 +37,7 @@ class Qdb(bdb.Bdb):
self.frame = None
self.i = 1 # sequential RPC call id
self.waiting = False
self.pipe = pipe # for communication
self.pipe = pipe # for communication
self._wait_for_mainpyfile = False
self._wait_for_breakpoint = False
self.mainpyfile = ""
@@ -67,7 +68,7 @@ class Qdb(bdb.Bdb):
# dispatch message (JSON RPC like)
method = getattr(self, request['method'])
response['result'] = method.__call__(*request['args'],
**request.get('kwargs', {}))
**request.get('kwargs', {}))
except Exception, e:
response['error'] = {'code': 0, 'message': str(e)}
# send the result for normal method calls, not for notifications
@@ -83,7 +84,7 @@ class Qdb(bdb.Bdb):
self.pull_actions()
# process the frame (see Bdb.trace_dispatch)
if self.quitting:
return # None
return # None
if event == 'line':
return self.dispatch_line(frame)
if event == 'call':
@@ -106,7 +107,7 @@ class Qdb(bdb.Bdb):
"""This function is called when we stop or break at this line."""
if self._wait_for_mainpyfile:
if (not self.canonic(frame.f_code.co_filename).startswith(self.mainpyfile)
or frame.f_lineno<= 0):
or frame.f_lineno <= 0):
return
self._wait_for_mainpyfile = 0
if self._wait_for_breakpoint:
@@ -150,11 +151,11 @@ class Qdb(bdb.Bdb):
import __main__
import imp
__main__.__dict__.clear()
__main__.__dict__.update({"__name__" : "__main__",
"__file__" : filename,
__main__.__dict__.update({"__name__": "__main__",
"__file__": filename,
"__builtins__": __builtins__,
"imp" : imp, # need for run
})
"imp": imp, # need for run
})
# avoid stopping before we reach the main script
self._wait_for_mainpyfile = 1
@@ -200,8 +201,8 @@ class Qdb(bdb.Bdb):
if self.params.get('environment'):
kwargs['environment'] = self.do_environment()
self.pipe.send({'method': 'interaction', 'id': None,
'args': (filename, self.frame.f_lineno, line),
'kwargs': kwargs})
'args': (filename, self.frame.f_lineno, line),
'kwargs': kwargs})
self.pull_actions()
@@ -278,7 +279,7 @@ class Qdb(bdb.Bdb):
filename = self.frame.f_code.co_filename
breaklist = self.get_file_breaks(filename)
lines = []
for lineno in range(first, last+1):
for lineno in range(first, last + 1):
line = linecache.getline(filename, lineno,
self.frame.f_globals)
if not line:
@@ -303,7 +304,7 @@ class Qdb(bdb.Bdb):
for bp in bdb.Breakpoint.bpbynumber:
if bp:
breaks.append((bp.number, bp.file, bp.line,
bp.temporary, bp.enabled, bp.hits, bp.cond, ))
bp.temporary, bp.enabled, bp.hits, bp.cond, ))
return breaks
def do_clear_breakpoint(self, filename, lineno):
@@ -320,7 +321,7 @@ class Qdb(bdb.Bdb):
def do_eval(self, arg, safe=True):
ret = eval(arg, self.frame.f_globals,
self.frame_locals)
self.frame_locals)
if safe:
ret = pydoc.cram(repr(ret), 255)
return ret
@@ -354,9 +355,11 @@ class Qdb(bdb.Bdb):
# converts the frame global and locals to a short text representation:
if self.frame:
for name, value in self.frame_locals.items():
env['locals'][name] = pydoc.cram(repr(value), 255), repr(type(value))
env['locals'][name] = pydoc.cram(repr(
value), 255), repr(type(value))
for name, value in self.frame.f_globals.items():
env['globals'][name] = pydoc.cram(repr(value), 20), repr(type(value))
env['globals'][name] = pydoc.cram(repr(
value), 20), repr(type(value))
return env
def get_autocomplete_list(self, expression):
@@ -589,8 +592,8 @@ class Frontend(object):
result = self.readline()
if result:
response = {'version': '1.1', 'id': request.get('id'),
'result': result,
'error': None}
'result': result,
'error': None}
self.send(response)
return True
@@ -609,7 +612,8 @@ class Frontend(object):
# nested request received (i.e. readline)! process it!
self.process_message(res)
elif long(req['id']) != long(res['id']):
print "DEBUGGER wrong packet received: expecting id", req['id'], res['id']
print "DEBUGGER wrong packet received: expecting id", req[
'id'], res['id']
# protocol state is unknown
elif 'error' in res and res['error']:
raise RPCError(res['error']['message'])
@@ -736,7 +740,7 @@ class Cli(Frontend, cmd.Cmd):
return raw_input()
def postcmd(self, stop, line):
return not line.startswith("h") # stop
return not line.startswith("h") # stop
do_h = cmd.Cmd.do_help
@@ -838,6 +842,7 @@ def test():
class Test(Frontend):
def interaction(self, *args):
print "interaction!", args
def exception(self, *args):
print "exception", args
#raise RuntimeError("exception %s" % repr(args))
@@ -877,7 +882,7 @@ def main(host='localhost', port=6000, authkey='secret password'):
print "usage: pdb.py scriptfile [arg] ..."
sys.exit(2)
mainpyfile = sys.argv[1] # Get script filename
mainpyfile = sys.argv[1] # Get script filename
if not os.path.exists(mainpyfile):
print 'Error:', mainpyfile, 'does not exist'
sys.exit(1)
@@ -913,6 +918,8 @@ def main(host='localhost', port=6000, authkey='secret password'):
qdb = None
def set_trace(host='localhost', port=6000, authkey='secret password'):
"Simplified interface to debug running programs"
global qdb, listener, conn
@@ -950,7 +957,7 @@ if __name__ == '__main__':
# Check environment for configuration parameters:
kwargs = {}
for param in 'host', 'port', 'authkey':
if 'QDB_%s' % param.upper() in os.environ:
if 'QDB_%s' % param.upper() in os.environ:
kwargs[param] = os.environ['QDB_%s' % param.upper()]
if not sys.argv[1:]:
+16 -11
View File
@@ -17,6 +17,7 @@ logger = logging.getLogger("web2py.cache.redis")
locker = thread.allocate_lock()
def RedisCache(*args, **vars):
"""
Usage example: put in models
@@ -47,10 +48,11 @@ class RedisClient(object):
meta_storage = {}
MAX_RETRIES = 5
RETRIES = 0
def __init__(self, server='localhost:6379', db=None, debug=False):
self.server = server
self.db = db or 0
host,port = (self.server.split(':')+['6379'])[:2]
host, port = (self.server.split(':') + ['6379'])[:2]
port = int(port)
self.request = current.request
self.debug = debug
@@ -64,7 +66,7 @@ class RedisClient(object):
CacheAbstract.cache_stats_name: {
'hit_total': 0,
'misses': 0,
}}
}}
else:
self.storage = self.meta_storage[app]
@@ -72,8 +74,8 @@ class RedisClient(object):
def __call__(self, key, f, time_expire=300):
try:
if time_expire == None:
time_expire = 24*60*60
if time_expire is None:
time_expire = 24 * 60 * 60
newKey = self.__keyFormat__(key)
value = None
obj = self.r_server.get(newKey)
@@ -100,13 +102,15 @@ class RedisClient(object):
def retry_call(self, key, f, time_expire):
self.RETRIES += 1
if self.RETRIES <= self.MAX_RETRIES:
logger.error("sleeping %s seconds before reconnecting" % (2 * self.RETRIES))
logger.error("sleeping %s seconds before reconnecting" %
(2 * self.RETRIES))
time.sleep(2 * self.RETRIES)
self.__init__(self.server, self.db, self.debug)
return self.__call__(key, f, time_expire)
else:
self.RETRIES = 0
raise ConnectionError , 'Redis instance is unavailable at %s' % (self.server)
raise ConnectionError('Redis instance is unavailable at %s' % (
self.server))
def increment(self, key, value=1, time_expire=300):
try:
@@ -129,7 +133,8 @@ class RedisClient(object):
return self.increment(key, value, time_expire)
else:
self.RETRIES = 0
raise ConnectionError , 'Redis instance is unavailable at %s' % (self.server)
raise ConnectionError('Redis instance is unavailable at %s' % (
self.server))
def clear(self, regex):
"""
@@ -139,8 +144,8 @@ class RedisClient(object):
r = re.compile(regex)
prefix = "w2p:%s:" % (self.request.application)
pipe = self.r_server.pipeline()
for a in self.r_server.keys("%s*" % \
(prefix)):
for a in self.r_server.keys("%s*" %
(prefix)):
if r.match(str(a).replace(prefix, '', 1)):
pipe.delete(a)
pipe.execute()
@@ -149,10 +154,10 @@ class RedisClient(object):
statscollector = self.r_server.info()
if self.debug:
statscollector['w2p_stats'] = dict(
hit_total = self.r_server.get(
hit_total=self.r_server.get(
'web2py_cache_statistics:hit_total'),
misses=self.r_server.get('web2py_cache_statistics:misses')
)
)
statscollector['w2p_keys'] = dict()
for a in self.r_server.keys("w2p:%s:*" % (
self.request.application)):
+13 -6
View File
@@ -16,6 +16,7 @@ logger = logging.getLogger("web2py.session.redis")
locker = thread.allocate_lock()
def RedisSession(*args, **vars):
"""
Usage example: put in models
@@ -48,7 +49,7 @@ class RedisClient(object):
"""
self.server = server
self.db = db or 0
host,port = (self.server.split(':')+['6379'])[:2]
host, port = (self.server.split(':') + ['6379'])[:2]
port = int(port)
self.debug = debug
if current and current.request:
@@ -63,12 +64,13 @@ class RedisClient(object):
return self.tablename
def Field(self, fieldname, type='string', length=None, default=None,
required=False,requires=None):
required=False, requires=None):
return None
def define_table(self,tablename,*fields,**args):
def define_table(self, tablename, *fields, **args):
if not self.tablename:
self.tablename = MockTable(self, self.r_server, tablename, self.session_expiry)
self.tablename = MockTable(
self, self.r_server, tablename, self.session_expiry)
return self.tablename
def __getitem__(self, key):
@@ -82,6 +84,7 @@ class RedisClient(object):
#this is only called by session2trash.py
pass
class MockTable(object):
def __init__(self, db, r_server, tablename, session_expiry):
@@ -89,7 +92,8 @@ class MockTable(object):
self.r_server = r_server
self.tablename = tablename
#set the namespace for sessions of this app
self.keyprefix = 'w2p:sess:%s' % tablename.replace('web2py_session_', '')
self.keyprefix = 'w2p:sess:%s' % tablename.replace(
'web2py_session_', '')
#fast auto-increment id (needed for session handling)
self.serial = "%s:serial" % self.keyprefix
#index of all the session keys of this app
@@ -125,6 +129,7 @@ class MockTable(object):
self.r_server.expire(key, self.session_expiry)
return newid
class MockQuery(object):
"""a fake Query object that supports querying by id
and listing all keys. No other operation is supported
@@ -171,7 +176,8 @@ class MockQuery(object):
continue
val = Storage(val)
#add a delete_record method (necessary for sessions2trash.py)
val.delete_record = RecordDeleter(self.db, sess, self.keyprefix)
val.delete_record = RecordDeleter(
self.db, sess, self.keyprefix)
rtn.append(val)
return rtn
else:
@@ -185,6 +191,7 @@ class MockQuery(object):
self.db.expire(key, self.session.expiry)
return rtn
class RecordDeleter(object):
"""Dumb record deleter to support sessions2trash.py"""
+10 -7
View File
@@ -54,14 +54,14 @@ UNPICKLABLE_TYPES = (
types.TypeType,
types.ClassType,
types.FunctionType,
)
)
# Unpicklable statements to seed new historys with.
INITIAL_UNPICKLABLES = [
'import logging',
'import os',
'import sys',
]
]
class History:
@@ -154,6 +154,7 @@ class History:
if name in self.unpicklable_names:
self.unpicklable_names.remove(name)
def represent(obj):
"""Returns a string representing the given object's value, which should allow the
code below to determine whether the object changes over time.
@@ -163,6 +164,7 @@ def represent(obj):
except:
return repr(obj)
def run(history, statement, env={}):
"""
Evaluates a python statement in a given history and returns the result.
@@ -180,7 +182,6 @@ def run(history, statement, env={}):
# single-line expressions such as 'class Foo: pass' evaluate happily.
statement += '\n\n'
# log and compile the statement up front
try:
logging.info('Compiling and evaluating:\n%s' % statement)
@@ -224,7 +225,8 @@ def run(history, statement, env={}):
history.remove_global(name)
# run!
old_globals = dict((key,represent(value)) for key,value in statement_module.__dict__.items())
old_globals = dict((key, represent(
value)) for key, value in statement_module.__dict__.items())
try:
old_stdout, old_stderr = sys.stdout, sys.stderr
try:
@@ -261,6 +263,7 @@ def run(history, statement, env={}):
sys.modules['__main__'] = old_main
return output.getvalue()
if __name__=='__main__':
history=History()
while True: print run(history, raw_input('>>> ')).rstrip()
if __name__ == '__main__':
history = History()
while True:
print run(history, raw_input('>>> ')).rstrip()
+6 -3
View File
@@ -46,8 +46,10 @@ class JSONDummyParser:
"json wrapper for xmlrpclib parser interfase"
def __init__(self):
self.buf = StringIO()
def feed(self, data):
self.buf.write(data)
def close(self):
return self.buf.getvalue()
@@ -72,6 +74,7 @@ class JSONTransportMixin:
class JSONTransport(JSONTransportMixin, Transport):
pass
class JSONSafeTransport(JSONTransportMixin, SafeTransport):
pass
@@ -79,7 +82,7 @@ class JSONSafeTransport(JSONTransportMixin, SafeTransport):
class ServerProxy(object):
"JSON RPC Simple Client Service Proxy"
def __init__(self, uri, transport=None, encoding=None, verbose=0):
def __init__(self, uri, transport=None, encoding=None, verbose=0):
self.location = uri # server location (url)
self.trace = verbose # show debug messages
self.exceptions = True # raise errors? (JSONRPCError)
@@ -88,7 +91,7 @@ class ServerProxy(object):
type, uri = urllib.splittype(uri)
if type not in ("http", "https"):
raise IOError, "unsupported JSON-RPC protocol"
raise IOError("unsupported JSON-RPC protocol")
self.__host, self.__handler = urllib.splithost(uri)
if transport is None:
@@ -118,7 +121,7 @@ class ServerProxy(object):
self.__handler,
request,
verbose=self.__verbose
)
)
# store plain request and response for further debugging
self.json_request = request
+108 -105
View File
@@ -1,112 +1,115 @@
SMSCODES = {
'Aliant':'@chat.wirefree.ca',
'Alltel':'@message.alltel.com',
'Ameritech':'@paging.acswireless.com',
'AT&T':'@txt.att.net',
'AU by KDDI':'@ezweb.ne.jp',
'BeeLine GSM':'@sms.beemail.ru',
'Bell Mobility Canada':'@txt.bellmobility.ca',
'Bellsouth':'@bellsouth.cl',
'BellSouth Mobility':'@blsdcs.net',
'Blue Sky Frog':'@blueskyfrog.com',
'Boost':'@myboostmobile.com',
'Cellular South':'@csouth1.com',
'CellularOne':'@mobile.celloneusa.com',
'CellularOne West':'@mycellone.com',
'Cincinnati Bell':'@gocbw.com',
'Claro':'@clarotorpedo.com.br',
'Comviq':'@sms.comviq.se',
'Dutchtone/Orange-NL':'@sms.orange.nl',
'Edge Wireless':'@sms.edgewireless.com',
'EinsteinPCS / Airadigm Communications':'@einsteinsms.com',
'EPlus':'@smsmail.eplus.de',
'Fido Canada':'@fido.ca',
'Golden Telecom':'@sms.goldentele.com',
'Idea Cellular':'@ideacellular.net',
'Kyivstar':'@sms.kyivstar.net',
'LMT':'@sms.lmt.lv',
'Manitoba Telecom Systems':'@text.mtsmobility.com',
'Meteor':'@sms.mymeteor.ie',
'Metro PCS':'@mymetropcs.com',
'Metrocall Pager':'@page.metrocall.com',
'MobileOne':'@m1.com.sg',
'Mobilfone':'@page.mobilfone.com',
'Mobility Bermuda':'@ml.bm',
'Netcom':'@sms.netcom.no',
'Nextel':'@messaging.nextel.com',
'NPI Wireless':'@npiwireless.com',
'O2':'@o2.co.uk',
'O2 M-mail':'@mmail.co.uk',
'Optus':'@optusmobile.com.au',
'Orange':'@orange.net',
'Oskar':'@mujoskar.cz',
'Pagenet':'@pagenet.net',
'PCS Rogers':'@pcs.rogers.com',
'Personal Communication':'@pcom.ru',
'Plus GSM Poland':'@text.plusgsm.pl',
'Powertel':'@ptel.net',
'Primtel':'@sms.primtel.ru',
'PSC Wireless':'@sms.pscel.com',
'Qualcomm':'@pager.qualcomm.com',
'Qwest':'@qwestmp.com',
'Safaricom':'@safaricomsms.com',
'Satelindo GSM':'@satelindogsm.com',
'SCS-900':'@scs-900.ru',
'Simple Freedom':'@text.simplefreedom.net',
'Skytel - Alphanumeric':'@skytel.com',
'Smart Telecom':'@mysmart.mymobile.ph',
'Southern Linc':'@page.southernlinc.com',
'Sprint PCS':'@messaging.sprintpcs.com',
'Sprint PCS - Short Mail':'@sprintpcs.com',
'SunCom':'@tms.suncom.com',
'SureWest Communications':'@mobile.surewest.com',
'SwissCom Mobile':'@bluewin.ch',
'T-Mobile Germany':'@T-D1-SMS.de',
'T-Mobile Netherlands':'@gin.nl',
'T-Mobile UK':'@t-mobile.uk.net',
'T-Mobile USA (tmail)':'@tmail.com',
'T-Mobile USA (tmomail)':'@tmomail.net',
'Tele2 Latvia':'@sms.tele2.lv',
'Telefonica Movistar':'@movistar.net',
'Telenor':'@mobilpost.no',
'Telia Denmark':'@gsm1800.telia.dk',
'Telus Mobility':'@msg.telus.com',
'The Phone House':'@sms.phonehouse.de',
'TIM':'@timnet.com',
'UMC':'@sms.umc.com.ua',
'Unicel':'@utext.com',
'US Cellular':'@email.uscc.net',
'Verizon Wireless (vtext)':'@vtext.com',
'Verizon Wireless (airtouchpaging)':'@airtouchpaging.com',
'Verizon Wireless (myairmail)':'@myairmail.com',
'Vessotel':'@pager.irkutsk.ru',
'Virgin Mobile Canada':'@vmobile.ca',
'Virgin Mobile USA':'@vmobl.com',
'Vodafone Italy':'@sms.vodafone.it',
'Vodafone Japan (n)':'@n.vodafone.ne.jp',
'Vodafone Japan (d)':'@d.vodafone.ne.jp',
'Vodafone Japan (r)':'@r.vodafone.ne.jp',
'Vodafone Japan (k)':'@k.vodafone.ne.jp',
'Vodafone Japan (t)':'@t.vodafone.ne.jp',
'Vodafone Japan (q)':'@q.vodafone.ne.jp',
'Vodafone Japan (s)':'@s.vodafone.ne.jp',
'Vodafone Japan (h)':'@h.vodafone.ne.jp',
'Vodafone Japan (c)':'@c.vodafone.ne.jp',
'Vodafone Spain':'@vodafone.es',
'Vodafone UK':'@vodafone.net',
'Weblink Wireless':'@airmessage.net',
'WellCom':'@sms.welcome2well.com',
'WyndTell':'@wyndtell.com',
}
'Aliant': '@chat.wirefree.ca',
'Alltel': '@message.alltel.com',
'Ameritech': '@paging.acswireless.com',
'AT&T': '@txt.att.net',
'AU by KDDI': '@ezweb.ne.jp',
'BeeLine GSM': '@sms.beemail.ru',
'Bell Mobility Canada': '@txt.bellmobility.ca',
'Bellsouth': '@bellsouth.cl',
'BellSouth Mobility': '@blsdcs.net',
'Blue Sky Frog': '@blueskyfrog.com',
'Boost': '@myboostmobile.com',
'Cellular South': '@csouth1.com',
'CellularOne': '@mobile.celloneusa.com',
'CellularOne West': '@mycellone.com',
'Cincinnati Bell': '@gocbw.com',
'Claro': '@clarotorpedo.com.br',
'Comviq': '@sms.comviq.se',
'Dutchtone/Orange-NL': '@sms.orange.nl',
'Edge Wireless': '@sms.edgewireless.com',
'EinsteinPCS / Airadigm Communications': '@einsteinsms.com',
'EPlus': '@smsmail.eplus.de',
'Fido Canada': '@fido.ca',
'Golden Telecom': '@sms.goldentele.com',
'Idea Cellular': '@ideacellular.net',
'Kyivstar': '@sms.kyivstar.net',
'LMT': '@sms.lmt.lv',
'Manitoba Telecom Systems': '@text.mtsmobility.com',
'Meteor': '@sms.mymeteor.ie',
'Metro PCS': '@mymetropcs.com',
'Metrocall Pager': '@page.metrocall.com',
'MobileOne': '@m1.com.sg',
'Mobilfone': '@page.mobilfone.com',
'Mobility Bermuda': '@ml.bm',
'Netcom': '@sms.netcom.no',
'Nextel': '@messaging.nextel.com',
'NPI Wireless': '@npiwireless.com',
'O2': '@o2.co.uk',
'O2 M-mail': '@mmail.co.uk',
'Optus': '@optusmobile.com.au',
'Orange': '@orange.net',
'Oskar': '@mujoskar.cz',
'Pagenet': '@pagenet.net',
'PCS Rogers': '@pcs.rogers.com',
'Personal Communication': '@pcom.ru',
'Plus GSM Poland': '@text.plusgsm.pl',
'Powertel': '@ptel.net',
'Primtel': '@sms.primtel.ru',
'PSC Wireless': '@sms.pscel.com',
'Qualcomm': '@pager.qualcomm.com',
'Qwest': '@qwestmp.com',
'Safaricom': '@safaricomsms.com',
'Satelindo GSM': '@satelindogsm.com',
'SCS-900': '@scs-900.ru',
'Simple Freedom': '@text.simplefreedom.net',
'Skytel - Alphanumeric': '@skytel.com',
'Smart Telecom': '@mysmart.mymobile.ph',
'Southern Linc': '@page.southernlinc.com',
'Sprint PCS': '@messaging.sprintpcs.com',
'Sprint PCS - Short Mail': '@sprintpcs.com',
'SunCom': '@tms.suncom.com',
'SureWest Communications': '@mobile.surewest.com',
'SwissCom Mobile': '@bluewin.ch',
'T-Mobile Germany': '@T-D1-SMS.de',
'T-Mobile Netherlands': '@gin.nl',
'T-Mobile UK': '@t-mobile.uk.net',
'T-Mobile USA (tmail)': '@tmail.com',
'T-Mobile USA (tmomail)': '@tmomail.net',
'Tele2 Latvia': '@sms.tele2.lv',
'Telefonica Movistar': '@movistar.net',
'Telenor': '@mobilpost.no',
'Telia Denmark': '@gsm1800.telia.dk',
'Telus Mobility': '@msg.telus.com',
'The Phone House': '@sms.phonehouse.de',
'TIM': '@timnet.com',
'UMC': '@sms.umc.com.ua',
'Unicel': '@utext.com',
'US Cellular': '@email.uscc.net',
'Verizon Wireless (vtext)': '@vtext.com',
'Verizon Wireless (airtouchpaging)': '@airtouchpaging.com',
'Verizon Wireless (myairmail)': '@myairmail.com',
'Vessotel': '@pager.irkutsk.ru',
'Virgin Mobile Canada': '@vmobile.ca',
'Virgin Mobile USA': '@vmobl.com',
'Vodafone Italy': '@sms.vodafone.it',
'Vodafone Japan (n)': '@n.vodafone.ne.jp',
'Vodafone Japan (d)': '@d.vodafone.ne.jp',
'Vodafone Japan (r)': '@r.vodafone.ne.jp',
'Vodafone Japan (k)': '@k.vodafone.ne.jp',
'Vodafone Japan (t)': '@t.vodafone.ne.jp',
'Vodafone Japan (q)': '@q.vodafone.ne.jp',
'Vodafone Japan (s)': '@s.vodafone.ne.jp',
'Vodafone Japan (h)': '@h.vodafone.ne.jp',
'Vodafone Japan (c)': '@c.vodafone.ne.jp',
'Vodafone Spain': '@vodafone.es',
'Vodafone UK': '@vodafone.net',
'Weblink Wireless': '@airmessage.net',
'WellCom': '@sms.welcome2well.com',
'WyndTell': '@wyndtell.com',
}
def sms_email(number,provider):
def sms_email(number, provider):
"""
>>> print sms_email('1 (312) 375-6536','T-Mobile USA (tmail)')
print 13123756536@tmail.com
"""
import re
if number[0]=='+1': number=number[1:]
elif number[0]=='+': number=number[3:]
elif number[:2]=='00': number=number[3:]
number=re.sub('[^\d]','',number)
return number+SMSCODES[provider]
if number[0] == '+1':
number = number[1:]
elif number[0] == '+':
number = number[3:]
elif number[:2] == '00': number = number[3:]
number = re.sub('[^\d]', '', number)
return number + SMSCODES[provider]
+64 -62
View File
@@ -9,13 +9,14 @@ import pickle
import copy
import simplejson
def quote(text):
return str(text).replace('\\', '\\\\').replace("'", "\\'")
class Node:
def __init__(self, name, value, url='.', readonly=False, active=True,
onchange=None, **kwarg):
onchange=None, **kwarg):
self.url = url
self.name = name
self.value = str(value)
@@ -246,8 +247,8 @@ class Sheet:
r, c = int(r), int(c)
except (ValueError, IndexError, TypeError), e:
error = "%s. %s" % \
("Unexpected position parameter",
"Must be a key of type 'rncn'")
("Unexpected position parameter",
"Must be a key of type 'rncn'")
raise ValueError(error)
return r, c
@@ -316,7 +317,6 @@ class Sheet:
return changes
def process(self, request, db=None, db_callback=None):
"""
call this in action that creates table, it will handle ajax callbacks
@@ -333,22 +333,22 @@ class Sheet:
if request.args(0) == 'focus':
return "jQuery('#%(cell)s').val('%(value)s');" % \
dict(cell=cell, value=quote(self[cell].value))
dict(cell=cell, value=quote(self[cell].value))
value = request.vars[cell]
self[cell] = value
if request.args(0) == 'blur':
return "jQuery('#%(cell)s').val('%(value)s');" % \
dict(cell=cell, value=quote(self[cell].computed_value))
dict(cell=cell, value=quote(self[cell].computed_value))
elif request.args(0) == 'keyup':
jquery = ''
for other_key in self.modified:
if other_key != cell:
jquery += "jQuery('#%(other_key)s').val('%(value)s');" % \
dict(other_key=other_key,
value=quote(self[other_key].computed_value))
dict(other_key=other_key,
value=quote(self[other_key].computed_value))
else:
# spreadsheet db update
@@ -390,8 +390,8 @@ class Sheet:
return "odd"
def __init__(self, rows, cols, url='.', readonly=False,
active=True, onchange=None, value=None, data=None,
headers=None, update_button="", **kwarg):
active=True, onchange=None, value=None, data=None,
headers=None, update_button="", **kwarg):
"""
Arguments:
@@ -418,7 +418,7 @@ class Sheet:
self.nodes = {}
self.error = 'ERROR: %(error)s'
self.allowed_keywords = ['for', 'in', 'if', 'else', 'and', 'or', 'not',
'i', 'j', 'k', 'x', 'y', 'z', 'sum']
'i', 'j', 'k', 'x', 'y', 'z', 'sum']
self.value = value
self.environment = {}
self.attributes = self.get_attributes(kwarg)
@@ -431,14 +431,14 @@ class Sheet:
self.update_button = update_button
self.client = {
"columns": {},
"colnames": {},
"id_columns": [],
"id_colnames": {},
"cells": {},
"modified": {},
"headers": headers
}
"columns": {},
"colnames": {},
"id_columns": [],
"id_colnames": {},
"cells": {},
"modified": {},
"headers": headers
}
# if db and query:
if self.data is not None:
@@ -467,10 +467,10 @@ class Sheet:
self.client["id_columns"].append(y)
self.client["id_colnames"][colname] = y
for k in xrange(self.rows*self.cols):
key = 'r%sc%s'%(k/self.cols, k%self.cols)
for k in xrange(self.rows * self.cols):
key = 'r%sc%s' % (k / self.cols, k % self.cols)
r, c = self.position(key)
if self.client["cells"].has_key(key):
if key in self.client["cells"]:
value = self.client["cells"][key]
# readonly id values
if c in self.client["id_columns"]:
@@ -485,7 +485,7 @@ class Sheet:
else:
value = '0.00'
self.cell(key, value,
readonly, active, onchange)
readonly, active, onchange)
exec('from math import *', {}, self.environment)
@@ -565,7 +565,7 @@ class Sheet:
for col, data in cells.iteritems():
key = "r%sc%s" % (row, col)
active, onchange, readonly, cell_value = \
self.get_cell_arguments(data, default=kwarg)
self.get_cell_arguments(data, default=kwarg)
if value is None:
v = cell_value
else:
@@ -575,7 +575,7 @@ class Sheet:
onchange=onchange, **attributes)
else:
active, onchange, readonly, all_value = \
self.get_cell_arguments(kwarg)
self.get_cell_arguments(kwarg)
for col, cell_value in enumerate(cells):
key = "r%sc%s" % (row, col)
if value is None:
@@ -601,7 +601,7 @@ class Sheet:
for row, data in cells.iteritems():
key = "r%sc%s" % (row, col)
active, onchange, readonly, cell_value = \
self.get_cell_arguments(data, default=kwarg)
self.get_cell_arguments(data, default=kwarg)
if value is None:
v = cell_value
else:
@@ -610,7 +610,7 @@ class Sheet:
onchange=onchange, **attributes)
else:
active, onchange, readonly, all_value = \
self.get_cell_arguments(kwarg)
self.get_cell_arguments(kwarg)
for row, cell_value in enumerate(cells):
key = "r%sc%s" % (row, col)
if value is None:
@@ -646,36 +646,36 @@ class Sheet:
if isinstance(cells, dict):
for key, data in cells.iteritems():
r, c = self.position(key)
key = "r%sc%s" % (r+starts_r, c+starts_c)
key = "r%sc%s" % (r + starts_r, c + starts_c)
active, onchange, readonly, cell_value = \
self.get_cell_arguments(data, default=kwarg)
self.get_cell_arguments(data, default=kwarg)
if value is None:
v = cell_value
else:
v = value
if (ends is None) or ((ends_r >= r+starts_r) and\
(ends_c >= c+starts_c)):
if (ends is None) or ((ends_r >= r + starts_r) and
(ends_c >= c + starts_c)):
self.cell(key, v, active=active,
readonly=readonly,
onchange=onchange, **attributes)
readonly=readonly,
onchange=onchange, **attributes)
else:
active, onchange, readonly, all_value = \
self.get_cell_arguments(kwarg)
self.get_cell_arguments(kwarg)
for r, row in enumerate(cells):
for c, cell_value in enumerate(row):
if value is None:
v = cell_value
else:
v = value
key = "r%sc%s" % (r+starts_r, c+starts_c)
key = "r%sc%s" % (r + starts_r, c + starts_c)
if (ends is None) or \
((ends_r >= r+starts_r) and\
(ends_c >= c+starts_c)):
((ends_r >= r + starts_r) and
(ends_c >= c + starts_c)):
self.cell(key, v,
active=active,
onchange=onchange,
readonly=readonly,
**attributes)
active=active,
onchange=onchange,
readonly=readonly,
**attributes)
def __setitem__(self, key, value):
key = str(key)
@@ -694,7 +694,7 @@ class Sheet:
other_key = match.group()
if other_key == key:
self.computed_value = self.error % dict(error='cycle')
self.modified={}
self.modified = {}
break
if other_key in self.nodes:
other_node = self.nodes[other_key]
@@ -704,7 +704,8 @@ class Sheet:
not other_key in self.environment:
node.locked = True
node.computed_value = \
self.error % dict(error='invalid keyword: ' + other_key)
self.error % dict(
error='invalid keyword: ' + other_key)
self.modified = {}
break
self.compute(node)
@@ -737,7 +738,7 @@ class Sheet:
output = {node.name: node.computed_value}
changed_nodes = self.changed(node)
while changed_nodes:
ok=False
ok = False
set_changed_nodes = set(changed_nodes)
for (k, other_node) in enumerate(changed_nodes):
#print other_node, changed_nodes
@@ -758,7 +759,7 @@ class Sheet:
return self.nodes[str(key)]
def get_computed_values(self):
d={}
d = {}
for key in self.nodes:
node = self.nodes[key]
if node.value[:1] != '=' or not node.active:
@@ -780,21 +781,21 @@ class Sheet:
gluon.html.TH, gluon.html.BR, gluon.html.SCRIPT)
regex = re.compile('r\d+c\d+')
header = TR(TH(), *[TH('c%s' % c) \
for c in range(self.cols)])
header = TR(TH(), *[TH('c%s' % c)
for c in range(self.cols)])
rows = []
for r in range(self.rows):
tds = [TH('r%s' % r),]
tds = [TH('r%s' % r), ]
for c in range(self.cols):
key = 'r%sc%s'%(r, c)
attributes = {"_class": "w2p_spreadsheet_col_%s" % \
self.even_or_odd(c)}
key = 'r%sc%s' % (r, c)
attributes = {"_class": "w2p_spreadsheet_col_%s" %
self.even_or_odd(c)}
if key in self.td_attributes:
attributes.update(self.td_attributes[key])
td = TD(self.nodes[key], **attributes)
tds.append(td)
attributes = {"_class": "w2p_spreadsheet_row_%s" % \
self.even_or_odd(r)}
attributes = {"_class": "w2p_spreadsheet_row_%s" %
self.even_or_odd(r)}
if str(r) in self.tr_attributes:
attributes.update(self.tr_attributes[str(r)])
rows.append(TR(*tds, **attributes))
@@ -805,8 +806,8 @@ class Sheet:
table = TABLE(header, *rows, **self.attributes)
if len(self.client["cells"]) >= 1:
data = SCRIPT(\
"""
data = SCRIPT(
"""
var w2p_spreadsheet_data = %(data)s;
var w2p_spreadsheet_update_button = "%(update_button)s";
var w2p_spreadsheet_update_result = null;
@@ -835,7 +836,8 @@ class Sheet:
if (w2p_spreadsheet_update_button != ""){
jQuery(function(){
jQuery("#" + w2p_spreadsheet_update_button).click(w2p_spreadsheet_update_db);
jQuery("#" + w2p_spreadsheet_update_button).click(
w2p_spreadsheet_update_db);
});
}
""" % dict(data=simplejson.dumps(self.client),
@@ -848,22 +850,22 @@ class Sheet:
if self.client["headers"] is not None:
for fieldname, name in self.client["headers"].iteritems():
unsorted_headers.append((self.client["columns"][fieldname],
name))
name))
else:
for fieldname, c in self.client["columns"].iteritems():
unsorted_headers.append((c, fieldname))
sorted_headers = [TH(),] + \
[TH(header[1]) for header in sorted(unsorted_headers)]
sorted_headers = [TH(), ] + \
[TH(header[1]) for header in sorted(unsorted_headers)]
table.insert(0, TR(*sorted_headers,
**{_class:"%s_fieldnames" % \
attributes["_class"]}))
**{_class: "%s_fieldnames" %
attributes["_class"]}))
else:
data = SCRIPT(""" // web2py Spreadsheets: no db data.""")
return DIV(table,
BR(),
TABLE(*[TR(TH(key), TD(self.nodes[key])) \
TABLE(*[TR(TH(key), TD(self.nodes[key]))
for key in self.nodes if not regex.match(key)]),
data, **attributes)
+23 -21
View File
@@ -1,6 +1,7 @@
import urllib
import simplejson
class Stripe:
"""
Usage:
@@ -22,8 +23,9 @@ class Stripe:
if paid is True than transaction was processed
"""
def __init__(self,key):
def __init__(self, key):
self.key = key
def charge(self,
amount,
currency='usd',
@@ -32,33 +34,33 @@ class Stripe:
card_exp_year='2012',
card_cvc_check='123',
description='test charge'):
params = urllib.urlencode({'amount':amount,
'currency':currency,
'card[number]':card_number,
'card[exp_month]':card_exp_month,
'card[exp_year]':card_exp_year,
'card[cvc_check]':card_cvc_check,
'description':description})
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges' % \
self.key,params)
params = urllib.urlencode({'amount': amount,
'currency': currency,
'card[number]': card_number,
'card[exp_month]': card_exp_month,
'card[exp_year]': card_exp_year,
'card[cvc_check]': card_cvc_check,
'description': description})
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges' %
self.key, params)
return simplejson.loads(u.read())
def check(self,charge_id):
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges/%s' % \
(self.key,charge_id))
def check(self, charge_id):
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges/%s' %
(self.key, charge_id))
return simplejson.loads(u.read())
def refund(self,charge_id):
def refund(self, charge_id):
params = urllib.urlencode({})
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges/%s/refund'%\
(self.key,charge_id),params)
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges/%s/refund' %
(self.key, charge_id), params)
return simplejson.loads(u.read())
if __name__=='__main__':
if __name__ == '__main__':
key = raw_input('user>')
d=Stripe(key).charge(100)
print 'charged',d['paid']
d = Stripe(key).charge(100)
print 'charged', d['paid']
s = Stripe(key).check(d[u'id'])
print 'paid',s['paid'],s['amount'] ,s['currency']
print 'paid', s['paid'], s['amount'], s['currency']
s = Stripe(key).refund(d[u'id'])
print 'refunded',s['refunded']
print 'refunded', s['refunded']
+13 -13
View File
@@ -28,7 +28,7 @@ class TaskBarIcon:
win32con.WM_DESTROY: self.OnDestroy,
win32con.WM_COMMAND: self.OnCommand,
win32con.WM_USER + 20: self.OnTaskbarNotify,
}
}
# Register the Window class.
@@ -56,7 +56,7 @@ class TaskBarIcon:
0,
hinst,
None,
)
)
win32gui.UpdateWindow(self.hwnd)
self.SetServerStopped()
@@ -82,7 +82,7 @@ class TaskBarIcon:
hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE\
| win32gui.NIF_TIP
| win32gui.NIF_TIP
nid = (
self.hwnd,
0,
@@ -90,7 +90,7 @@ class TaskBarIcon:
win32con.WM_USER + 20,
hicon,
'web2py Framework',
)
)
try:
win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, nid)
except:
@@ -111,7 +111,7 @@ class TaskBarIcon:
msg,
wparam,
lparam,
):
):
self._DoCreateIcons()
def OnDestroy(
@@ -120,7 +120,7 @@ class TaskBarIcon:
msg,
wparam,
lparam,
):
):
nid = (self.hwnd, 0)
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
@@ -130,7 +130,7 @@ class TaskBarIcon:
msg,
wparam,
lparam,
):
):
if lparam == win32con.WM_LBUTTONUP:
pass
elif lparam == win32con.WM_LBUTTONDBLCLK:
@@ -144,14 +144,14 @@ class TaskBarIcon:
win32gui.AppendMenu(menu, win32con.MF_STRING, 1024,
'Start Server')
win32gui.AppendMenu(menu, win32con.MF_STRING
| win32con.MF_GRAYED, 1025,
| win32con.MF_GRAYED, 1025,
'Restart Server')
win32gui.AppendMenu(menu, win32con.MF_STRING
| win32con.MF_GRAYED, 1026,
| win32con.MF_GRAYED, 1026,
'Stop Server')
else:
win32gui.AppendMenu(menu, win32con.MF_STRING
| win32con.MF_GRAYED, 1024,
| win32con.MF_GRAYED, 1024,
'Start Server')
win32gui.AppendMenu(menu, win32con.MF_STRING, 1025,
'Restart Server')
@@ -173,7 +173,7 @@ class TaskBarIcon:
0,
self.hwnd,
None,
)
)
win32api.PostMessage(self.hwnd, win32con.WM_NULL, 0, 0)
return 1
@@ -183,7 +183,7 @@ class TaskBarIcon:
msg,
wparam,
lparam,
):
):
id = win32api.LOWORD(wparam)
if id == 1023:
self.status.append(self.EnumStatus.TOGGLE)
@@ -226,7 +226,7 @@ class TaskBarIcon:
0,
0,
icon_flags,
)
)
return hicon
class EnumStatus:
+20 -13
View File
@@ -3,16 +3,20 @@ import time
from collections import namedtuple
Score = namedtuple('Score', ['tag', 'stamp'])
class TimeCollector(object):
def __init__(self):
'''The first time stamp is created here'''
self.scores = [Score(tag='start',stamp=time.clock())]
self.scores = [Score(tag='start', stamp=time.clock())]
def addStamp(self, description):
'''Adds a new time stamp, with a description.'''
self.scores.append(Score(tag=description, stamp=time.clock()))
def _stampDelta(self, index1, index2):
'''Private utility function to clean up this common calculation.'''
return self.scores[index1].stamp - self.scores[index2].stamp
def getReportItems(self, orderByCost=True):
'''Returns a list of dicts. Each dict has
start (ms),
@@ -25,23 +29,24 @@ class TimeCollector(object):
total_time = self._stampDelta(-1, 0)
data = []
for i in range(1, len(self.scores)):
delta = self._stampDelta(i, i-1)
delta = self._stampDelta(i, i - 1)
if abs(total_time) < 1e-6:
perc = 0
else:
perc = delta / total_time * 100
data.append(
dict(
start = self._stampDelta(i-1, 0) * 1000,
end = self._stampDelta(i, 0) * 1000,
delta = delta * 1000,
perc = perc,
tag = self.scores[i].tag
)
start=self._stampDelta(i - 1, 0) * 1000,
end=self._stampDelta(i, 0) * 1000,
delta=delta * 1000,
perc=perc,
tag=self.scores[i].tag
)
)
if orderByCost:
data.sort(key=lambda x: x['perc'], reverse=True)
return data
def getReportLines(self, orderByCost=True):
'''Produces a report of logged time-stamps as a list of strings.
if orderByCost is False, then the order of the stamps is
@@ -51,14 +56,16 @@ class TimeCollector(object):
headerData = ('Start(ms)', 'End(ms)', 'Delta(ms)', 'Time Cost',
'Description')
bodyTemplate = '%(start)10.0f | %(end)10.0f | %(delta)10.0f |' \
+ ' %(perc)10.0f%% | %(tag)-30s'
+ ' %(perc)10.0f%% | %(tag)-30s'
return [headerTemplate % headerData] + [bodyTemplate % d for d in data]
def getReportText(self, **kwargs):
return '\n'.join(self.getReportLines(**kwargs))
def restart(self):
self.scores = [Score(tag='start',stamp=time.clock())]
if __name__=='__main__':
def restart(self):
self.scores = [Score(tag='start', stamp=time.clock())]
if __name__ == '__main__':
print('')
print('Testing:')
print('')
@@ -87,7 +94,7 @@ if __name__=='__main__':
print(t.getReportText())
t.restart()
for y in range(1, 200, 20):
x = [i for i in range(10000)*y]
x = [i for i in range(10000) * y]
t.addStamp('Iteration when y = ' + str(y))
print('')
+117 -69
View File
@@ -15,6 +15,7 @@ Symbian.
"""
import sys
class DetectorsHub(dict):
_known_types = ['os', 'dist', 'flavor', 'browser']
@@ -38,6 +39,7 @@ class DetectorsHub(dict):
return detectors
else:
prefs.insert(0, '')
def key_name(d):
return d.name in prefs and prefs.index(d.name) or sys.maxint
return sorted(detectors, key=key_name)
@@ -46,24 +48,24 @@ class DetectorsHub(dict):
return iter(self._known_types)
def registerDetectors(self):
detectors = [v() for v in globals().values() \
if DetectorBase in getattr(v, '__mro__', [])]
detectors = [v() for v in globals().values()
if DetectorBase in getattr(v, '__mro__', [])]
for d in detectors:
if d.can_register:
self.register(d)
class DetectorBase(object):
name = "" # "to perform match in DetectorsHub object"
info_type = '' #override me
result_key = '' #override me
order = 10 # 0 is highest
look_for = [] # list of words to look for
skip_if_found = [] # strings if present stop processin
name = "" # "to perform match in DetectorsHub object"
info_type = '' # override me
result_key = '' # override me
order = 10 # 0 is highest
look_for = [] # list of words to look for
skip_if_found = [] # strings if present stop processin
can_register = False
is_mobile = False
is_tablet = False
prefs = dict() # dict(info_type = [name1, name2], ..)
prefs = dict() # dict(info_type = [name1, name2], ..)
version_splitters = ["/", " "]
_suggested_detectors = None
@@ -75,8 +77,8 @@ class DetectorBase(object):
def detect(self, agent, result):
if agent and self.checkWords(agent):
result[self.info_type] = dict(name=self.name)
is_mobile=self.is_mobile
is_tablet=self.is_tablet
is_mobile = self.is_mobile
is_tablet = self.is_tablet
if is_mobile:
result['is_mobile'] = is_mobile
if is_tablet:
@@ -131,6 +133,7 @@ class Browser(DetectorBase):
class Macintosh(OS):
look_for = ['Macintosh']
prefs = dict(dist=None)
def getVersion(self, agent):
pass
@@ -146,12 +149,15 @@ class Konqueror(Browser):
class Opera(Browser):
look_for = ["Opera"]
def getVersion(self, agent):
return agent.partition(self.look_for[0])[2][1:].partition(' ')[0]
class Netscape(Browser):
look_for = ["Netscape"]
class MSIE(Browser):
look_for = ["MSIE"]
skip_if_found = ["Opera"]
@@ -171,6 +177,7 @@ class Safari(Browser):
if "Version/" in agent:
return agent.partition('Version/')[2].partition(' ')[0].strip()
class SafariTablet(Browser):
name = "Safari"
look_for = ['Safari', 'Android']
@@ -182,6 +189,7 @@ class SafariTablet(Browser):
if "Version/" in agent:
return agent.partition('Version/')[2].partition(' ')[0].strip()
class SafariMobile(Browser):
name = "Safari"
look_for = ["Safari", "Mobile"]
@@ -191,6 +199,7 @@ class SafariMobile(Browser):
if "Version/" in agent:
return agent.partition('Version/')[2].partition(' ')[0].strip()
class SafariNokia(Browser):
name = "Safari"
look_for = ["Safari", "SymbianOS"]
@@ -199,6 +208,7 @@ class SafariNokia(Browser):
def getVersion(self, agent):
pass
class SafariiPad(Browser):
name = "Safari"
look_for = ["Safari", "iPad"]
@@ -218,9 +228,10 @@ class Linux(OS):
def getVersion(self, agent):
pass
class BlackBerry(OS):
look_for = ['BlackBerry']
prefs = dict( flavor=['PlayBook'])
prefs = dict(flavor=['PlayBook'])
is_mobile = True
# Manual check for tablet
@@ -232,14 +243,16 @@ class BlackBerry(OS):
def getVersion(self, agent):
pass
class PlayBook(Flavor):
look_for = ['PlayBook']
is_mobile=True
is_tablet=True
is_mobile = True
is_tablet = True
def getVersion(self, agent):
return agent.partition('Tablet OS')[2].partition(';')[0].strip()
class Macintosh(OS):
look_for = ['Macintosh']
prefs = dict(dist=None, flavor=['MacOS'])
@@ -250,7 +263,8 @@ class Macintosh(OS):
class MacOS(Flavor):
look_for = ['Mac OS']
prefs = dict(browser=['Safari','SafariMobile', 'SafariIpad', 'Firefox', 'Opera', "Microsoft Internet Explorer"])
prefs = dict(browser=['Safari', 'SafariMobile', 'SafariIpad',
'Firefox', 'Opera', "Microsoft Internet Explorer"])
def getVersion(self, agent):
version_end_chars = [';', ')']
@@ -269,7 +283,8 @@ class Windows(OS):
def getVersion(self, agent):
v = agent.partition('NT')
return v[1]+' '+v[2].replace(')',';').partition(';')[0].strip()
return v[1] + ' ' + v[2].replace(')', ';').partition(';')[0].strip()
class WindowsMobile(Dist):
name = 'Phone'
@@ -277,7 +292,8 @@ class WindowsMobile(Dist):
is_mobile = True
def getVersion(self, agent):
return agent.partition('Windows Phone')[2].replace(')','').partition(';')[0].strip()
return agent.partition('Windows Phone')[2].replace(')', '').partition(';')[0].strip()
class Ubuntu(Dist):
look_for = ['Ubuntu']
@@ -295,13 +311,16 @@ class Chrome(Browser):
look_for = ['Chrome']
version_splitters = ["/", " "]
class ChromeOS(OS):
look_for = ['CrOS']
version_splitters = [" ", ")"]
prefs = dict(browser=['Chrome'])
def getVersion(self, agent):
vs = self.version_splitters
return agent.partition(self.look_for[0]+vs[0])[2].partition(vs[1])[0].partition(" ")[2].strip()
return agent.partition(self.look_for[0] + vs[0])[2].partition(vs[1])[0].partition(" ")[2].strip()
class Android(Dist):
look_for = ['Android']
@@ -311,23 +330,27 @@ class Android(Dist):
def getVersion(self, agent):
return agent.partition('Android')[2].partition(';')[0].strip()
class SymbianOS(OS):
look_for = ['SymbianOS']
prefs = dict(dist = ['Series'], browser = ['Safari', 'Opera'])
prefs = dict(dist=['Series'], browser=['Safari', 'Opera'])
is_mobile = True
version_splitters = ['/', '; ']
class Series(Flavor):
look_for = ['SymbianOS', 'Series']
version_splitters = ['/', ';']
def getVersion(self, agent):
return agent.partition('Series')[2].partition(' ')[0].replace('/',' ')
return agent.partition('Series')[2].partition(' ')[0].replace('/', ' ')
class BrowserNG(Browser):
look_for = ['BrowserNG']
version_splitters = ['/', ';']
class iPhone(Dist):
look_for = ['iPhone']
is_mobile = True
@@ -344,6 +367,7 @@ class iPhone(Dist):
break
return version.replace('_', '.')
class iPad(Dist):
look_for = ['iPad']
is_mobile = True
@@ -362,6 +386,7 @@ class iPad(Dist):
detectorshub = DetectorsHub()
def detect(agent):
result = dict()
prefs = dict()
@@ -385,21 +410,26 @@ class Result(dict):
def __missing__(self, k):
return ""
def simple_detect(agent):
"""
-> (os, browser, is_mobile) # tuple of strings
"""
result = detect(agent)
os_list = []
if 'flavor' in result: os_list.append(result['flavor']['name'])
if 'dist' in result: os_list.append(result['dist']['name'])
if 'os' in result: os_list.append(result['os']['name'])
if 'flavor' in result:
os_list.append(result['flavor']['name'])
if 'dist' in result:
os_list.append(result['dist']['name'])
if 'os' in result:
os_list.append(result['os']['name'])
os = os_list and " ".join(os_list) or "Unknown OS"
os_version = os_list and ('flavor' in result and result['flavor'] and result['flavor'].get(
'version')) or ('dist' in result and result['dist'] and result['dist'].get('version')) \
or ('os' in result and result['os'] and result['os'].get('version')) or ""
browser = 'browser' in result and result['browser']['name'] or 'Unknown Browser'
'version')) or ('dist' in result and result['dist'] and result['dist'].get('version')) \
or ('os' in result and result['os'] and result['os'].get('version')) or ""
browser = 'browser' in result and result['browser'][
'name'] or 'Unknown Browser'
browser_version = 'browser' in result \
and result['browser'].get('version') or ""
if browser_version:
@@ -417,64 +447,82 @@ if __name__ == '__main__':
data = (
('Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 Nokia6120c/3.83; Profile/MIDP-2.0 Configuration/CLDC-1.1) AppleWebKit/413 (KHTML, like Gecko) Safari/413',
('Series SymbianOS 60 3.1', 'Safari', True),
{'is_mobile': True, 'is_tablet': False, 'flavor': {'name': 'Series', 'version': '60 3.1'}, 'os': {'name': 'SymbianOS', 'version': '9.2'}, 'browser': {'name': 'Safari'}},),
('Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/20.0.019; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.18124',
('Series SymbianOS 60 5.0', 'BrowserNG 7.1.18124', True),
(
'Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 Nokia6120c/3.83; Profile/MIDP-2.0 Configuration/CLDC-1.1) AppleWebKit/413 (KHTML, like Gecko) Safari/413',
('Series SymbianOS 60 3.1', 'Safari', True),
{'is_mobile': True, 'is_tablet': False, 'flavor': {'name': 'Series', 'version': '60 3.1'}, 'os': {'name': 'SymbianOS', 'version': '9.2'}, 'browser': {'name': 'Safari'}},),
(
'Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/20.0.019; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.18124',
('Series SymbianOS 60 5.0', 'BrowserNG 7.1.18124', True),
{'is_mobile': True, 'is_tablet': False, 'flavor': {'name': 'Series', 'version': '60 5.0'}, 'os': {'name': 'SymbianOS', 'version': '9.4'}, 'browser': {'name': 'BrowserNG', 'version': '7.1.18124'}},),
('Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Windows Phone 6.5.3.5)',
('Phone Windows 6.5.3.5', 'Microsoft Internet Explorer 6.0', True),
{'is_mobile': True, 'is_tablet': False, 'dist': {'name': 'Phone', 'version': '6.5.3.5'}, 'os': {'name': 'Windows', 'version': 'NT 5.1'}, 'browser': {'name': 'Microsoft Internet Explorer', 'version': '6.0'}},),
('Mozilla/5.0 (PlayBook; U; RIM Tablet OS 1.0.0; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/0.0.1 Safari/534.8+',
('PlayBook BlackBerry 1.0.0', 'Safari 0.0.1', True),
(
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Windows Phone 6.5.3.5)',
('Phone Windows 6.5.3.5', 'Microsoft Internet Explorer 6.0', True),
{'is_mobile': True, 'is_tablet': False, 'dist': {'name': 'Phone', 'version': '6.5.3.5'}, 'os': {'name': 'Windows', 'version': 'NT 5.1'}, 'browser': {'name': 'Microsoft Internet Explorer', 'version': '6.0'}},),
(
'Mozilla/5.0 (PlayBook; U; RIM Tablet OS 1.0.0; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/0.0.1 Safari/534.8+',
('PlayBook BlackBerry 1.0.0', 'Safari 0.0.1', True),
{'is_mobile': True, 'is_tablet': True, 'flavor': {'name': 'PlayBook', 'version': '1.0.0'}, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '0.0.1'}},),
('Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.246 Mobile Safari/534.1+',
('BlackBerry', 'Safari 6.0.0.246', True),
(
'Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.246 Mobile Safari/534.1+',
('BlackBerry', 'Safari 6.0.0.246', True),
{'is_mobile': True, 'is_tablet': False, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '6.0.0.246'}},),
('Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.600 Mobile Safari/534.8+',
('BlackBerry', 'Safari 6.0.0.600', True),
(
'Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.600 Mobile Safari/534.8+',
('BlackBerry', 'Safari 6.0.0.600', True),
{'is_mobile': True, 'is_tablet': False, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '6.0.0.600'}},),
('Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5',
('MacOS iPad X', 'Safari 5.0.2', True),
{'is_mobile': True, 'is_tablet': True, 'flavor': {'version': 'X', 'name': 'MacOS'}, 'dist': {'version': 'iOS 4.2.1', 'name': 'iPad'}, 'browser': {'name': 'Safari', 'version': '5.0.2'}},),
('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20060127 Netscape/8.1',
('Windows NT 5.1', 'Netscape 8.1', False),
(
'Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5',
('MacOS iPad X', 'Safari 5.0.2', True),
{'is_mobile': True, 'is_tablet': True, 'flavor': {'version': 'X', 'name': 'MacOS'}, 'dist': {'version': 'iOS 4.2.1', 'name': 'iPad'}, 'browser': {'name': 'Safari', 'version': '5.0.2'}},),
(
'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20060127 Netscape/8.1',
('Windows NT 5.1', 'Netscape 8.1', False),
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'Windows', 'version': 'NT 5.1'}, 'browser': {'name': 'Netscape', 'version': '8.1'}},),
('Mozilla/5.0 (Linux; U; Android 3.0.1; en-us; A500 Build/HRI66) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13',
('Android Linux 3.0.1', 'Safari 4.0', True),
(
'Mozilla/5.0 (Linux; U; Android 3.0.1; en-us; A500 Build/HRI66) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13',
('Android Linux 3.0.1', 'Safari 4.0', True),
{'is_mobile': True, 'is_tablet': True, 'dist': {'version': '3.0.1', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
('Mozilla/5.0 (Linux; U; Android 2.3.7; it-it; Dream/Sapphire Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',
('Android Linux 2.3.7', 'Safari 4.0', True),
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.3.7', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
('Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-GB; rv:1.9.0.10) Gecko/2009042315 Firefox/3.0.10',
('MacOS Macintosh X 10.5', 'Firefox 3.0.10', False),
{ 'is_mobile': False, 'is_tablet': False, 'flavor': {'version': 'X 10.5', 'name': 'MacOS'}, 'os': {'name': 'Macintosh'}, 'browser': {'version': '3.0.10', 'name': 'Firefox'}},),
('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_6) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24,gzip(gfe)',
('MacOS Macintosh X 10.6.6', 'Chrome 11.0.696.3', False),
(
'Mozilla/5.0 (Linux; U; Android 2.3.7; it-it; Dream/Sapphire Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',
('Android Linux 2.3.7', 'Safari 4.0', True),
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.3.7', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
(
'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-GB; rv:1.9.0.10) Gecko/2009042315 Firefox/3.0.10',
('MacOS Macintosh X 10.5', 'Firefox 3.0.10', False),
{'is_mobile': False, 'is_tablet': False, 'flavor': {'version': 'X 10.5', 'name': 'MacOS'}, 'os': {'name': 'Macintosh'}, 'browser': {'version': '3.0.10', 'name': 'Firefox'}},),
(
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_6) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24,gzip(gfe)',
('MacOS Macintosh X 10.6.6', 'Chrome 11.0.696.3', False),
{'is_mobile': False, 'is_tablet': False, 'flavor': {'version': 'X 10.6.6', 'name': 'MacOS'}, 'os': {'name': 'Macintosh'}, 'browser': {'version': '11.0.696.3', 'name': 'Chrome'}},),
('Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2) Gecko/20100308 Ubuntu/10.04 (lucid) Firefox/3.6 GTB7.1',
('Ubuntu Linux 10.04', 'Firefox 3.6', False),
(
'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2) Gecko/20100308 Ubuntu/10.04 (lucid) Firefox/3.6 GTB7.1',
('Ubuntu Linux 10.04', 'Firefox 3.6', False),
{'is_mobile': False, 'is_tablet': False, 'dist': {'version': '10.04', 'name': 'Ubuntu'}, 'os': {'name': 'Linux'}, 'browser': {'version': '3.6', 'name': 'Firefox'}},),
('Mozilla/5.0 (Linux; U; Android 2.2.1; fr-ch; A43 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',
('Android Linux 2.2.1', 'Safari 4.0', True),
(
'Mozilla/5.0 (Linux; U; Android 2.2.1; fr-ch; A43 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',
('Android Linux 2.2.1', 'Safari 4.0', True),
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.2.1', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
('Mozilla/5.0 (Linux; U; Android 2.3.4; it-it; LG-P990 Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 MMS/LG-Android-MMS-V1.0/1.2',
('Android Linux 2.3.4', 'Safari 4.0', True),
(
'Mozilla/5.0 (Linux; U; Android 2.3.4; it-it; LG-P990 Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 MMS/LG-Android-MMS-V1.0/1.2',
('Android Linux 2.3.4', 'Safari 4.0', True),
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.3.4', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
('Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3',
('MacOS iPhone X', 'Safari 3.0', True),
(
'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3',
('MacOS iPhone X', 'Safari 3.0', True),
{'is_mobile': True, 'is_tablet': False, 'flavor': {'version': 'X', 'name': 'MacOS'}, 'dist': {'version': 'X', 'name': 'iPhone'}, 'browser': {'version': '3.0', 'name': 'Safari'}},),
('Mozilla/5.0 (X11; CrOS i686 0.0.0) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.27 Safari/534.24,gzip(gfe)',
('ChromeOS 0.0.0', 'Chrome 11.0.696.27', False),
(
'Mozilla/5.0 (X11; CrOS i686 0.0.0) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.27 Safari/534.24,gzip(gfe)',
('ChromeOS 0.0.0', 'Chrome 11.0.696.27', False),
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'ChromeOS', 'version': '0.0.0'}, 'browser': {'name': 'Chrome', 'version': '11.0.696.27'}},),
('Mozilla/4.0 (compatible; MSIE 6.0; MSIE 5.5; Windows NT 5.1) Opera 7.02 [en]',
('Windows NT 5.1', 'Opera 7.02', False),
(
'Mozilla/4.0 (compatible; MSIE 6.0; MSIE 5.5; Windows NT 5.1) Opera 7.02 [en]',
('Windows NT 5.1', 'Opera 7.02', False),
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'Windows', 'version': 'NT 5.1'}, 'browser': {'name': 'Opera', 'version': '7.02'}},),
('Opera/9.80 (X11; Linux i686; U; en) Presto/2.9.168 Version/11.50',
('Linux', 'Opera 9.80', False),
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'Linux'}, 'browser': {'name': 'Opera', 'version': '9.80'}},),
)
)
class TestHAP(unittest.TestCase):
def setUp(self):
@@ -513,6 +561,6 @@ class mobilize(object):
user_agent = current.request.user_agent()
if user_agent.is_mobile:
items = current.response.view.split('.')
items.insert(-1,'mobile')
items.insert(-1, 'mobile')
current.response.view = '.'.join(items)
return self.func()
+48 -45
View File
@@ -22,21 +22,22 @@ import urllib
import urllib2
DEFAULT_HEADERS = {
'user-agent': 'Mozilla/4.0', # some servers are picky
'user-agent': 'Mozilla/4.0', # some servers are picky
'accept-language': 'en',
}
}
FORM_REGEX = re.compile('(\<input name\="_formkey" type\="hidden" value\="(?P<formkey>.+?)" \/\>)?\<input name\="_formname" type\="hidden" value\="(?P<formname>.+?)" \/\>')
SESSION_REGEX = 'session_id_(?P<name>.+)'
class WebClient(object):
def __init__(self,
app = '',
postbacks = True,
default_headers = DEFAULT_HEADERS,
session_regex = SESSION_REGEX):
app='',
postbacks=True,
default_headers=DEFAULT_HEADERS,
session_regex=SESSION_REGEX):
self.app = app
self.postbacks = postbacks
self.forms = {}
@@ -46,18 +47,18 @@ class WebClient(object):
self.sessions = {}
self.session_regex = session_regex and re.compile(session_regex)
def get(self,url,cookies=None,headers=None,auth=None):
return self.post(url,data=None,cookies=cookies,headers=headers)
def get(self, url, cookies=None, headers=None, auth=None):
return self.post(url, data=None, cookies=cookies, headers=headers)
def post(self,url,data=None,cookies=None,headers=None,auth=None):
self.url = self.app+url
def post(self, url, data=None, cookies=None, headers=None, auth=None):
self.url = self.app + url
# if this POST form requires a postback do it
if data and '_formname' in data and self.postbacks and \
self.history and self.history[-1][1]!=self.url:
self.history and self.history[-1][1] != self.url:
# to bypass the web2py CSRF need to get formkey
# before submitting the form
self.get(url,cookies=cookies,headers=headers,auth=auth)
self.get(url, cookies=cookies, headers=headers, auth=auth)
# unless cookies are specified, recycle cookies
if cookies is None:
@@ -75,22 +76,23 @@ class WebClient(object):
# copy headers from dict to list of key,value
headers_list = []
for key,value in self.default_headers.iteritems():
for key, value in self.default_headers.iteritems():
if not key in headers:
headers[key] = value
for key,value in headers.iteritems():
if isinstance(value,(list,tuple)):
for v in value: headers_list.append((key,v))
for key, value in headers.iteritems():
if isinstance(value, (list, tuple)):
for v in value:
headers_list.append((key, v))
else:
headers_list.append((key,value))
headers_list.append((key, value))
# move cookies to headers
for key,value in cookies.iteritems():
headers_list.append(('Cookie','%s=%s' % (key,value)))
for key, value in cookies.iteritems():
headers_list.append(('Cookie', '%s=%s' % (key, value)))
# add headers to request
for key,value in headers_list:
opener.addheaders.append((key,str(value)))
for key, value in headers_list:
opener.addheaders.append((key, str(value)))
# assume everything is ok and make http request
error = None
@@ -99,7 +101,7 @@ class WebClient(object):
self.method = 'POST'
# if there is only one form, set _formname automatically
if not '_formname' in data and len(self.forms)==1:
if not '_formname' in data and len(self.forms) == 1:
data['_formname'] = self.forms.keys()[0]
# if there is no formkey but it is known, set it
@@ -110,18 +112,18 @@ class WebClient(object):
# time the POST request
data = urllib.urlencode(data)
t0 = time.time()
self.response = opener.open(self.url,data)
self.time = time.time()-t0
self.response = opener.open(self.url, data)
self.time = time.time() - t0
else:
self.method = 'GET'
# time the GET request
t0 = time.time()
self.response = opener.open(self.url)
self.time = time.time()-t0
self.time = time.time() - t0
except urllib2.HTTPError, error:
# catch HTTP errors
self.time = time.time()-t0
self.time = time.time() - t0
self.response = error
self.status = self.response.getcode()
@@ -131,7 +133,7 @@ class WebClient(object):
# treat web2py tickets as special types of errors
if error is not None:
if 'web2py_error' in self.headers:
raise RuntimeError, self.headers['web2py_error']
raise RuntimeError(self.headers['web2py_error'])
else:
raise error
@@ -139,8 +141,8 @@ class WebClient(object):
self.cookies = {}
if 'set-cookie' in self.headers:
for item in self.headers['set-cookie'].split(','):
key,value = item[:item.find(';')].split('=')
self.cookies[key.strip()]=value.strip()
key, value = item[:item.find(';')].split('=')
self.cookies[key.strip()] = value.strip()
# check is a new session id has been issued, symptom of broken session
if self.session_regex is not None:
@@ -148,8 +150,8 @@ class WebClient(object):
match = self.session_regex.match(cookie)
if match:
name = match.group('name')
if name in self.sessions and self.sessions[name]!=value:
raise RuntimeError, 'Broken sessions %s' % name
if name in self.sessions and self.sessions[name] != value:
raise RuntimeError('Broken sessions %s' % name)
self.sessions[name] = value
# find all forms and formkeys in page
@@ -158,7 +160,8 @@ class WebClient(object):
self.forms[match.group('formname')] = match.group('formkey')
# log this request
self.history.append((self.method,self.url,self.status,self.time))
self.history.append((self.method, self.url, self.status, self.time))
def test_web2py_registration_and_login():
# from gluon.contrib.webclient import WebClient
@@ -168,13 +171,13 @@ def test_web2py_registration_and_login():
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')
@@ -182,18 +185,18 @@ def test_web2py_registration_and_login():
# login
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('user/profile')
assert 'Welcome Homer' in client.text
# print some variables
print '\nsessions:\n',client.sessions
print '\nheaders:\n',client.headers
print '\ncookies:\n',client.cookies
print '\nforms:\n',client.forms
print '\nsessions:\n', client.sessions
print '\nheaders:\n', client.headers
print '\ncookies:\n', client.cookies
print '\nforms:\n', client.forms
print
for method, url, status, t in client.history:
print method, url, status, t
+37 -20
View File
@@ -61,7 +61,8 @@ Here is a complete sample web2py action:
form=SQLFORM.factory(Field('message'))
if form.accepts(request,session):
from gluon.contrib.websocket_messaging import websocket_send
websocket_send('http://127.0.0.1:8888',form.vars.message,'mykey','mygroup')
websocket_send(
'http://127.0.0.1:8888',form.vars.message,'mykey','mygroup')
return form
Acknowledgements:
@@ -83,31 +84,38 @@ listeners = {}
names = {}
tokens = {}
def websocket_send(url,message,hmac_key=None,group='default'):
sig = hmac_key and hmac.new(hmac_key,message).hexdigest() or ''
params = urllib.urlencode({'message': message, 'signature': sig, 'group':group})
def websocket_send(url, message, hmac_key=None, group='default'):
sig = hmac_key and hmac.new(hmac_key, message).hexdigest() or ''
params = urllib.urlencode(
{'message': message, 'signature': sig, 'group': group})
f = urllib.urlopen(url, params)
data= f.read()
data = f.read()
f.close()
return data
class PostHandler(tornado.web.RequestHandler):
"""
only authorized parties can post messages
"""
def post(self):
if hmac_key and not 'signature' in self.request.arguments: return 'false'
if hmac_key and not 'signature' in self.request.arguments:
return 'false'
if 'message' in self.request.arguments:
message = self.request.arguments['message'][0]
group = self.request.arguments.get('group',['default'])[0]
group = self.request.arguments.get('group', ['default'])[0]
print '%s:MESSAGE to %s:%s' % (time.time(), group, message)
if hmac_key:
signature = self.request.arguments['signature'][0]
if not hmac.new(hmac_key,message).hexdigest()==signature: return 'false'
for client in listeners.get(group,[]): client.write_message(message)
if not hmac.new(hmac_key, message).hexdigest() == signature:
return 'false'
for client in listeners.get(group, []):
client.write_message(message)
return 'true'
return 'false'
class TokenHandler(tornado.web.RequestHandler):
"""
if running with -t post a token to allow a client to join using the token
@@ -115,46 +123,55 @@ class TokenHandler(tornado.web.RequestHandler):
allows only authorized parties to joins, for example, a chat
"""
def post(self):
if hmac_key and not 'message' in self.request.arguments: return 'false'
if hmac_key and not 'message' in self.request.arguments:
return 'false'
if 'message' in self.request.arguments:
message = self.request.arguments['message'][0]
if hmac_key:
signature = self.request.arguments['signature'][0]
if not hmac.new(hmac_key,message).hexdigest()==signature: return 'false'
if not hmac.new(hmac_key, message).hexdigest() == signature:
return 'false'
tokens[message] = None
return 'true'
return 'false'
class DistributeHandler(tornado.websocket.WebSocketHandler):
def open(self,params):
group,token,name = params.split('/')+[None,None]
def open(self, params):
group, token, name = params.split('/') + [None, None]
self.group = group or 'default'
self.token = token or 'none'
self.name = name or 'anonymous'
# only authorized parties can join
if DistributeHandler.tokens:
if not self.token in tokens or not token[self.token]==None:
if not self.token in tokens or not token[self.token] is None:
self.close()
else:
tokens[self.token] = self
if not self.group in listeners: listeners[self.group]=[]
if not self.group in listeners:
listeners[self.group] = []
# notify clients that a member has joined the groups
for client in listeners.get(self.group,[]): client.write_message('+'+self.name)
for client in listeners.get(self.group, []):
client.write_message('+' + self.name)
listeners[self.group].append(self)
names[self] = self.name
print '%s:CONNECT to %s' % (time.time(), self.group)
def on_message(self, message):
pass
def on_close(self):
if self.group in listeners: listeners[self.group].remove(self)
if self.group in listeners:
listeners[self.group].remove(self)
del names[self]
# notify clients that a member has left the groups
for client in listeners.get(self.group,[]): client.write_message('-'+self.name)
for client in listeners.get(self.group, []):
client.write_message('-' + self.name)
print '%s:DISCONNECT from %s' % (time.time(), self.group)
if __name__ == "__main__":
usage = __doc__
version= ""
version = ""
parser = optparse.OptionParser(usage, None, optparse.Option, version)
parser.add_option('-p',
'--port',
@@ -180,7 +197,7 @@ if __name__ == "__main__":
(options, args) = parser.parse_args()
hmac_key = options.hmac_key
DistributeHandler.tokens = options.tokens
urls=[
urls = [
(r'/', PostHandler),
(r'/token', TokenHandler),
(r'/realtime/(.*)', DistributeHandler)]
+30 -23
View File
@@ -10,24 +10,30 @@ import traceback
from gluon import current
NATIVE_IMPORTER = __builtin__.__import__
INVALID_MODULES = set(('','gluon','applications','custom_import'))
INVALID_MODULES = set(('', 'gluon', 'applications', 'custom_import'))
# backward compatibility API
def custom_import_install():
if __builtin__.__import__ != custom_importer:
INVALID_MODULES.update(sys.modules.keys())
__builtin__.__import__ = custom_importer
def track_changes(track=True):
assert track in (True,False), "must be True or False"
assert track in (True, False), "must be True or False"
current.request._custom_import_track_changes = track
def is_tracking_changes():
return current.request._custom_import_track_changes
class CustomImportException(ImportError):
pass
def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
"""
The web2py custom importer. Like the standard Python importer but it
@@ -45,25 +51,26 @@ def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
base_importer = TRACK_IMPORTER
else:
base_importer = NATIVE_IMPORTER
except: # there is no current.request (should never happen)
except: # there is no current.request (should never happen)
base_importer = NATIVE_IMPORTER
# if not relative and not from applications:
if hasattr(current,'request') \
and level<=0 \
if hasattr(current, 'request') \
and level <= 0 \
and not name.split('.')[0] in INVALID_MODULES \
and isinstance(globals, dict):
import_tb = None
try:
items = current.request.folder.split(os.path.sep)
if not items[-1]: items = items[:-1]
modules_prefix = '.'.join(items[-2:])+'.modules'
if not items[-1]:
items = items[:-1]
modules_prefix = '.'.join(items[-2:]) + '.modules'
if not fromlist:
# import like "import x" or "import x.y"
result = None
for itemname in name.split("."):
new_mod = base_importer(
modules_prefix, globals,locals, [itemname], level)
modules_prefix, globals, locals, [itemname], level)
try:
result = result or new_mod.__dict__[itemname]
except KeyError, e:
@@ -77,16 +84,16 @@ def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
except ImportError, e1:
import_tb = sys.exc_info()[2]
try:
return NATIVE_IMPORTER(name,globals,locals,fromlist,level)
return NATIVE_IMPORTER(name, globals, locals, fromlist, level)
except ImportError, e3:
raise ImportError, e1, import_tb # there an import error in the module
raise ImportError, e1, import_tb # there an import error in the module
except Exception, e2:
raise e2 # there is an error in the module
raise e2 # there is an error in the module
finally:
if import_tb:
import_tb = None
return NATIVE_IMPORTER(name,globals,locals,fromlist,level)
return NATIVE_IMPORTER(name, globals, locals, fromlist, level)
class TrackImporter(object):
@@ -96,19 +103,19 @@ class TrackImporter(object):
"""
THREAD_LOCAL = threading.local()
PACKAGE_PATH_SUFFIX = os.path.sep+"__init__.py"
PACKAGE_PATH_SUFFIX = os.path.sep + "__init__.py"
def __init__(self):
self._import_dates = {} # Import dates of the files of the modules
self._import_dates = {} # Import dates of the files of the modules
def __call__(self,name,globals=None,locals=None,fromlist=None,level=-1):
def __call__(self, name, globals=None, locals=None, fromlist=None, level=-1):
"""
The import method itself.
"""
globals = globals or {}
locals = locals or {}
fromlist = fromlist or []
if not hasattr(self.THREAD_LOCAL,'_modules_loaded'):
if not hasattr(self.THREAD_LOCAL, '_modules_loaded'):
self.THREAD_LOCAL._modules_loaded = set()
try:
# Check the date and reload if needed:
@@ -119,7 +126,7 @@ class TrackImporter(object):
self._update_dates(name, globals, locals, fromlist, level)
return result
except Exception, e:
raise # Don't hide something that went wrong
raise # Don't hide something that went wrong
def _update_dates(self, name, globals, locals, fromlist, level):
"""
@@ -143,7 +150,7 @@ class TrackImporter(object):
date = self._import_dates.get(file)
new_date = None
reload_mod = False
mod_to_pack = False # Module turning into a package? (special case)
mod_to_pack = False # Module turning into a package? (special case)
try:
new_date = os.path.getmtime(file)
except:
@@ -154,13 +161,13 @@ class TrackImporter(object):
# Get path without file ext:
file = os.path.splitext(file)[0]
reload_mod = os.path.isdir(file) \
and os.path.isfile(file+self.PACKAGE_PATH_SUFFIX)
and os.path.isfile(file + self.PACKAGE_PATH_SUFFIX)
mod_to_pack = reload_mod
else: # Package turning into module?
else: # Package turning into module?
file += ".py"
reload_mod = os.path.isfile(file)
if reload_mod:
new_date = os.path.getmtime(file) # Refresh file date
new_date = os.path.getmtime(file) # Refresh file date
if reload_mod or not date or new_date > date:
self._import_dates[file] = new_date
if reload_mod or (date and new_date > date):
@@ -168,7 +175,7 @@ class TrackImporter(object):
if mod_to_pack:
# Module turning into a package:
mod_name = module.__name__
del sys.modules[mod_name] # Delete the module
del sys.modules[mod_name] # Delete the module
# Reload the module:
NATIVE_IMPORTER(mod_name, globals, locals, [], level)
else:
@@ -182,7 +189,7 @@ class TrackImporter(object):
file = getattr(module, "__file__", None)
if file:
# Make path absolute if not:
file = os.path.splitext(file)[0]+".py" # Change .pyc for .py
file = os.path.splitext(file)[0] + ".py" # Change .pyc for .py
if file.endswith(self.PACKAGE_PATH_SUFFIX):
file = os.path.dirname(file) # Track dir for packages
return file
+2
View File
@@ -61,6 +61,7 @@ Example of usage:
### update one record
>>> james.update_record(name='Jim')
<Row {'id': 1, 'name': 'Jim'}>
### update multiple records by query
>>> db(person.name.like('J%')).update(name='James')
@@ -9390,6 +9391,7 @@ def test_all():
Update a single record
>>> me.update_record(name=\"Max\")
<Row {'name': 'Max', 'birth': datetime.date(1971, 12, 21), 'id': 2}>
>>> me.name
'Max'
+6 -1
View File
@@ -17,6 +17,7 @@ import sys
logger = logging.getLogger("web2py")
class Pipe(Queue.Queue):
def __init__(self, name, mode='r', *args, **kwargs):
self.__name = name
@@ -52,6 +53,7 @@ pipe_out = Pipe('out')
debugger = pdb.Pdb(completekey=None, stdin=pipe_in, stdout=pipe_out,)
def set_trace():
"breakpoint shortcut (like pdb)"
logger.info("DEBUG: set_trace!")
@@ -67,6 +69,7 @@ def stop_trace():
pipe_out.write(None)
#pipe_out.flush()
def communicate(command=None):
"send command to debbuger, wait result"
if command is not None:
@@ -91,6 +94,7 @@ from threading import RLock
interact_lock = RLock()
run_lock = RLock()
def check_interaction(fn):
"Decorator to clean and prevent interaction when not available"
def check_fn(self, *args, **kwargs):
@@ -181,7 +185,8 @@ front_conn = qdb.QueuePipe("parent", parent_queue, child_queue)
child_conn = qdb.QueuePipe("child", child_queue, parent_queue)
web_debugger = WebDebugger(front_conn) # frontend
qdb_debugger = qdb.Qdb(pipe=child_conn, redirect_stdio=False, skip=None) # backend
qdb_debugger = qdb.Qdb(
pipe=child_conn, redirect_stdio=False, skip=None) # backend
dbg = qdb_debugger
# enable getting context (stack, globals/locals) at interaction
+27 -24
View File
@@ -1,4 +1,5 @@
import codecs, encodings
import codecs
import encodings
"""Caller will hand this library a buffer and ask it to either convert
it or auto-detect the type.
@@ -9,16 +10,17 @@ Licensed under the PSF License
"""
# None represents a potentially variable byte. "##" in the XML spec...
autodetect_dict={ # bytepattern : ("name",
(0x00, 0x00, 0xFE, 0xFF) : ("ucs4_be"),
(0xFF, 0xFE, 0x00, 0x00) : ("ucs4_le"),
(0xFE, 0xFF, None, None) : ("utf_16_be"),
(0xFF, 0xFE, None, None) : ("utf_16_le"),
(0x00, 0x3C, 0x00, 0x3F) : ("utf_16_be"),
(0x3C, 0x00, 0x3F, 0x00) : ("utf_16_le"),
(0x3C, 0x3F, 0x78, 0x6D): ("utf_8"),
(0x4C, 0x6F, 0xA7, 0x94): ("EBCDIC")
}
autodetect_dict = { # bytepattern : ("name",
(0x00, 0x00, 0xFE, 0xFF): ("ucs4_be"),
(0xFF, 0xFE, 0x00, 0x00): ("ucs4_le"),
(0xFE, 0xFF, None, None): ("utf_16_be"),
(0xFF, 0xFE, None, None): ("utf_16_le"),
(0x00, 0x3C, 0x00, 0x3F): ("utf_16_be"),
(0x3C, 0x00, 0x3F, 0x00): ("utf_16_le"),
(0x3C, 0x3F, 0x78, 0x6D): ("utf_8"),
(0x4C, 0x6F, 0xA7, 0x94): ("EBCDIC")
}
def autoDetectXMLEncoding(buffer):
""" buffer -> encoding_name
@@ -31,14 +33,14 @@ def autoDetectXMLEncoding(buffer):
# buffer at once but otherwise we'd have to decode a character at
# a time looking for the quote character...that's a pain
encoding = "utf_8" # according to the XML spec, this is the default
encoding = "utf_8" # according to the XML spec, this is the default
# this code successively tries to refine the default
# whenever it fails to refine, it falls back to
# the last place encoding was set.
if len(buffer)>=4:
if len(buffer) >= 4:
bytes = (byte1, byte2, byte3, byte4) = tuple(map(ord, buffer[0:4]))
enc_info = autodetect_dict.get(bytes, None)
if not enc_info: # try autodetection again removing potentially
if not enc_info: # try autodetection again removing potentially
# variable bytes
bytes = (byte1, byte2, None, None)
enc_info = autodetect_dict.get(bytes)
@@ -46,29 +48,30 @@ def autoDetectXMLEncoding(buffer):
enc_info = None
if enc_info:
encoding = enc_info # we've got a guess... these are
encoding = enc_info # we've got a guess... these are
#the new defaults
# try to find a more precise encoding using xml declaration
secret_decoder_ring = codecs.lookup(encoding)[1]
(decoded,length) = secret_decoder_ring(buffer)
(decoded, length) = secret_decoder_ring(buffer)
first_line = decoded.split("\n")[0]
if first_line and first_line.startswith(u"<?xml"):
encoding_pos = first_line.find(u"encoding")
if encoding_pos!=-1:
if encoding_pos != -1:
# look for double quote
quote_pos=first_line.find('"', encoding_pos)
quote_pos = first_line.find('"', encoding_pos)
if quote_pos==-1: # look for single quote
quote_pos=first_line.find("'", encoding_pos)
if quote_pos == -1: # look for single quote
quote_pos = first_line.find("'", encoding_pos)
if quote_pos>-1:
quote_char,rest=(first_line[quote_pos],
first_line[quote_pos+1:])
encoding=rest[:rest.find(quote_char)]
if quote_pos > -1:
quote_char, rest = (first_line[quote_pos],
first_line[quote_pos + 1:])
encoding = rest[:rest.find(quote_char)]
return encoding
def decoder(buffer):
encoding = autoDetectXMLEncoding(buffer)
return buffer.decode(encoding).encode('utf8')
+40 -24
View File
@@ -40,15 +40,17 @@ __all__ = [
'w2p_unpack_plugin',
'fix_newlines',
'make_fake_file_like_object',
]
]
def parse_version(version = "Version 1.99.0 (2011-09-19 08:23:26)"):
def parse_version(version="Version 1.99.0 (2011-09-19 08:23:26)"):
re_version = re.compile('[^\d]+ (\d+)\.(\d+)\.(\d+)\s*\((?P<datetime>.+?)\)\s*(?P<type>[a-z]+)?')
m = re_version.match(version)
a,b,c = int(m.group(1)),int(m.group(2)),int(m.group(3)),
a, b, c = int(m.group(1)), int(m.group(2)), int(m.group(3)),
s = m.group('type') or 'dev'
d = datetime.datetime.strptime(m.group('datetime'),'%Y-%m-%d %H:%M:%S')
return (a,b,c,d,s)
d = datetime.datetime.strptime(m.group('datetime'), '%Y-%m-%d %H:%M:%S')
return (a, b, c, d, s)
def read_file(filename, mode='r'):
"returns content from filename, making sure to close the file explicitly on exit."
@@ -58,6 +60,7 @@ def read_file(filename, mode='r'):
finally:
f.close()
def write_file(filename, value, mode='w'):
"writes <value> to filename, making sure to close the file explicitly on exit."
f = open(filename, mode)
@@ -66,25 +69,28 @@ def write_file(filename, value, mode='w'):
finally:
f.close()
def readlines_file(filename, mode='r'):
"applies .split('\n') to the output of read_file()"
return read_file(filename, mode).split('\n')
def mktree(path):
head,tail =os.path.split(path)
head, tail = os.path.split(path)
if head:
if tail: mktree(head)
if tail:
mktree(head)
if not os.path.exists(head):
os.mkdir(head)
def listdir(
path,
expression='^.+$',
drop=True,
add_dirs=False,
sort=True,
):
):
"""
like os.listdir() but you can specify a regex pattern to filter files.
if add_dirs is True, the returned items will have the full path.
@@ -115,7 +121,7 @@ def listdir(
def recursive_unlink(f):
if os.path.isdir(f):
for s in os.listdir(f):
recursive_unlink(os.path.join(f,s))
recursive_unlink(os.path.join(f, s))
os.rmdir(f)
elif os.path.isfile(f):
os.unlink(f)
@@ -130,7 +136,7 @@ def cleanpath(path):
items = path.split('.')
if len(items) > 1:
path = re.sub('[^\w\.]+', '_', '_'.join(items[:-1]) + '.'
+ ''.join(items[-1:]))
+ ''.join(items[-1:]))
else:
path = re.sub('[^\w\.]+', '_', ''.join(items[-1:]))
return path
@@ -161,7 +167,7 @@ def _extractall(filename, path='.', members=None):
try:
os.makedirs(os.path.join(path,
tarinfo.name), 0777)
tarinfo.name), 0777)
except EnvironmentError:
pass
directories.append(tarinfo)
@@ -187,7 +193,6 @@ def _extractall(filename, path='.', members=None):
else:
self._dbg(1, 'tarfile: %s' % e)
_cls = TarFile
else:
_cls = tarfile.TarFile
@@ -197,6 +202,7 @@ def _extractall(filename, path='.', members=None):
tar.close()
return ret
def tar(file, dir, expression='^.+$'):
"""
tars dir into file, only tars file that match expression
@@ -209,6 +215,7 @@ def tar(file, dir, expression='^.+$'):
finally:
tar.close()
def untar(file, dir):
"""
untar file into dir
@@ -232,6 +239,7 @@ def w2p_pack(filename, path, compiled=False):
tarfp.close()
os.unlink(tarname)
def w2p_unpack(filename, path, delete_tar=True):
filename = abspath(filename)
path = abspath(path)
@@ -261,16 +269,18 @@ def w2p_pack_plugin(filename, path, plugin_name):
filename = abspath(filename)
path = abspath(path)
if not filename.endswith('web2py.plugin.%s.w2p' % plugin_name):
raise Exception, "Not a web2py plugin name"
raise Exception("Not a web2py plugin name")
plugin_tarball = tarfile.open(filename, 'w:gz')
try:
app_dir = path
while app_dir[-1]=='/':
while app_dir[-1] == '/':
app_dir = app_dir[:-1]
files1=glob.glob(os.path.join(app_dir,'*/plugin_%s.*' % plugin_name))
files2=glob.glob(os.path.join(app_dir,'*/plugin_%s/*' % plugin_name))
for file in files1+files2:
plugin_tarball.add(file, arcname=file[len(app_dir)+1:])
files1 = glob.glob(
os.path.join(app_dir, '*/plugin_%s.*' % plugin_name))
files2 = glob.glob(
os.path.join(app_dir, '*/plugin_%s/*' % plugin_name))
for file in files1 + files2:
plugin_tarball.add(file, arcname=file[len(app_dir) + 1:])
finally:
plugin_tarball.close()
@@ -279,8 +289,8 @@ def w2p_unpack_plugin(filename, path, delete_tar=True):
filename = abspath(filename)
path = abspath(path)
if not os.path.basename(filename).startswith('web2py.plugin.'):
raise Exception, "Not a web2py plugin"
w2p_unpack(filename,path,delete_tar)
raise Exception("Not a web2py plugin")
w2p_unpack(filename, path, delete_tar)
def tar_compiled(file, dir, expression='^.+$'):
@@ -306,6 +316,7 @@ def tar_compiled(file, dir, expression='^.+$'):
tar.add(filename, file, False)
tar.close()
def up(path):
return os.path.dirname(os.path.normpath(path))
@@ -317,13 +328,13 @@ def get_session(request, other_application='admin'):
try:
session_id = request.cookies['session_id_' + other_application].value
osession = storage.load_storage(os.path.join(
up(request.folder), other_application, 'sessions', session_id))
up(request.folder), other_application, 'sessions', session_id))
except Exception, e:
osession = storage.Storage()
return osession
def check_credentials(request, other_application='admin', expiration = 60*60):
def check_credentials(request, other_application='admin', expiration=60 * 60):
""" checks that user is authorized to access other_application"""
if request.env.web2py_runtime_gae:
from google.appengine.api import users
@@ -338,6 +349,7 @@ def check_credentials(request, other_application='admin', expiration = 60*60):
s = get_session(request, other_application)
return (s.authorized and s.last_time and s.last_time > dt)
def fix_newlines(path):
regex = re.compile(r'''(\r
|\r|
@@ -348,12 +360,13 @@ def fix_newlines(path):
if wdata != rdata:
write_file(filename, wdata, 'wb')
def copystream(
src,
dest,
size,
chunk_size=10 ** 5,
):
):
"""
this is here because I think there is a bug in shutil.copyfileobj
"""
@@ -379,13 +392,16 @@ def make_fake_file_like_object():
class LogFile(object):
def write(self, value):
pass
def close(self):
pass
return LogFile()
from settings import global_settings # we need to import settings here because
from settings import global_settings # we need to import settings here because
# settings imports fileutils too
def abspath(*relpath, **base):
"convert relative path to absolute path based (by default) on applications_parent"
path = os.path.join(*relpath)
+113 -87
View File
@@ -61,6 +61,7 @@ less_template = '<link href="%s" rel="stylesheet/less" type="text/css" />'
css_inline = '<style type="text/css">\n%s\n</style>'
js_inline = '<script type="text/javascript">\n%s\n</script>'
class Request(Storage):
"""
@@ -82,7 +83,7 @@ class Request(Storage):
def __init__(self):
Storage.__init__(self)
self.wsgi = Storage() # hooks to environ and start_response
self.wsgi = Storage() # hooks to environ and start_response
self.env = Storage()
self.cookies = Cookie.SimpleCookie()
self.get_vars = Storage()
@@ -116,8 +117,8 @@ class Request(Storage):
if session:
session._user_agent = user_agent
user_agent = Storage(user_agent)
for key,value in user_agent.items():
if isinstance(value,dict):
for key, value in user_agent.items():
if isinstance(value, dict):
user_agent[key] = Storage(value)
return user_agent
@@ -133,25 +134,26 @@ class Request(Storage):
current.session.secure()
def restful(self):
def wrapper(action,self=self):
def f(_action=action,_self=self,*a,**b):
def wrapper(action, self=self):
def f(_action=action, _self=self, *a, **b):
self.is_restful = True
method = _self.env.request_method
if len(_self.args) and '.' in _self.args[-1]:
_self.args[-1],_self.extension = _self.args[-1].rsplit('.',1)
_self.args[-
1], _self.extension = _self.args[-1].rsplit('.', 1)
current.response.headers['Content-Type'] = \
contenttype(_self.extension.lower())
if not method in ['GET','POST','DELETE','PUT']:
raise HTTP(400,"invalid method")
rest_action = _action().get(method,None)
if not method in ['GET', 'POST', 'DELETE', 'PUT']:
raise HTTP(400, "invalid method")
rest_action = _action().get(method, None)
if not rest_action:
raise HTTP(400,"method not supported")
raise HTTP(400, "method not supported")
try:
return rest_action(*_self.args,**_self.vars)
return rest_action(*_self.args, **_self.vars)
except TypeError, e:
exc_type, exc_value, exc_traceback = sys.exc_info()
if len(traceback.extract_tb(exc_traceback))==1:
raise HTTP(400,"invalid arguments")
if len(traceback.extract_tb(exc_traceback)) == 1:
raise HTTP(400, "invalid arguments")
else:
raise e
f.__doc__ = action.__doc__
@@ -180,8 +182,8 @@ class Response(Storage):
self.meta = Storage() # used by web2py_ajax.html
self.menu = [] # used by the default view layout
self.files = [] # used by web2py_ajax.html
self.generic_patterns = [] # patterns to allow generic views
self.delimiters = ('{{','}}')
self.generic_patterns = [] # patterns to allow generic views
self.delimiters = ('{{', '}}')
self._vars = None
self._caller = lambda f: f()
self._view_environment = None
@@ -197,7 +199,8 @@ class Response(Storage):
def render(self, *a, **b):
from compileapp import run_view_in
if len(a) > 2:
raise SyntaxError, 'Response.render can be called with two arguments, at most'
raise SyntaxError(
'Response.render can be called with two arguments, at most')
elif len(a) == 2:
(view, self._vars) = (a[0], a[1])
elif len(a) == 1 and isinstance(a[0], str):
@@ -225,9 +228,9 @@ class Response(Storage):
def include_meta(self):
s = '\n'.join(
'<meta name="%s" content="%s" />\n' % (k,xmlescape(v))
for k,v in (self.meta or {}).iteritems())
self.write(s,escape=False)
'<meta name="%s" content="%s" />\n' % (k, xmlescape(v))
for k, v in (self.meta or {}).iteritems())
self.write(s, escape=False)
def include_files(self, extensions=None):
@@ -256,44 +259,52 @@ class Response(Storage):
# cache for 5 minutes by default
key = hashlib.md5(repr(files)).hexdigest()
cache = self.cache_includes or (current.cache.ram, 60*5)
cache = self.cache_includes or (current.cache.ram, 60 * 5)
def call_minify(files=files):
return minify.minify(files,
URL('static','temp'),
URL('static', 'temp'),
current.request.folder,
self.optimize_css,
self.optimize_js)
if cache:
cache_model, time_expire = cache
files = cache_model('response.files.minified/'+key,
files = cache_model('response.files.minified/' + key,
call_minify,
time_expire)
else:
files = call_minify()
s = ''
for item in files:
if isinstance(item,str):
if isinstance(item, str):
f = item.lower().split('?')[0]
if self.static_version:
item = item.replace('/static/', '/static/_%s/' % self.static_version, 1)
if f.endswith('.css'): s += css_template % item
elif f.endswith('.js'): s += js_template % item
elif f.endswith('.coffee'): s += coffee_template % item
elif f.endswith('.less'): s += less_template % item
elif isinstance(item,(list,tuple)):
item = item.replace(
'/static/', '/static/_%s/' % self.static_version, 1)
if f.endswith('.css'):
s += css_template % item
elif f.endswith('.js'):
s += js_template % item
elif f.endswith('.coffee'):
s += coffee_template % item
elif f.endswith('.less'):
s += less_template % item
elif isinstance(item, (list, tuple)):
f = item[0]
if f=='css:inline': s += css_inline % item[1]
elif f=='js:inline': s += js_inline % item[1]
if f == 'css:inline':
s += css_inline % item[1]
elif f == 'js:inline':
s += js_inline % item[1]
self.write(s, escape=False)
def stream(
self,
stream,
chunk_size = DEFAULT_CHUNK_SIZE,
chunk_size=DEFAULT_CHUNK_SIZE,
request=None,
attachment=False,
filename=None,
):
):
"""
if a controller function::
@@ -347,7 +358,7 @@ class Response(Storage):
env = request.env
# Internet Explorer < 9.0 will not allow downloads over SSL unless caching is enabled
if request.is_https and isinstance(env.http_user_agent,str) and \
if request.is_https and isinstance(env.http_user_agent, str) and \
not re.search(r'Opera', env.http_user_agent) and \
re.search(r'MSIE [5-8][^0-9]', env.http_user_agent):
headers['Pragma'] = 'cache'
@@ -359,7 +370,7 @@ class Response(Storage):
wrapped = streamer(stream, chunk_size=chunk_size)
return wrapped
def download(self, request, db, chunk_size = DEFAULT_CHUNK_SIZE, attachment=True):
def download(self, request, db, chunk_size=DEFAULT_CHUNK_SIZE, attachment=True):
"""
example of usage in controller::
@@ -373,7 +384,7 @@ class Response(Storage):
raise HTTP(404)
name = request.args[-1]
items = re.compile('(?P<table>.*?)\.(?P<field>.*?)\..*')\
.match(name)
.match(name)
if not items:
raise HTTP(404)
(t, f) = (items.group('table'), items.group('field'))
@@ -389,9 +400,8 @@ class Response(Storage):
'attachment; filename=%s' % filename
return self.stream(stream, chunk_size=chunk_size, request=request)
def json(self, data, default=None):
return json(data, default = default or custom_json)
return json(data, default=default or custom_json)
def xmlrpc(self, request, methods):
"""
@@ -408,7 +418,8 @@ class Response(Storage):
the add function. Example::
import xmlrpclib
connection = xmlrpclib.ServerProxy('http://hostname/app/contr/func')
connection = xmlrpclib.ServerProxy(
'http://hostname/app/contr/func')
print connection.add(3, 4)
"""
@@ -418,14 +429,14 @@ class Response(Storage):
def toolbar(self):
from html import DIV, SCRIPT, BEAUTIFY, TAG, URL, A
BUTTON = TAG.button
admin = URL("admin","default","design",
admin = URL("admin", "default", "design",
args=current.request.application)
from gluon.dal import THREAD_LOCAL
if hasattr(THREAD_LOCAL,'instances'):
dbstats = [TABLE(*[TR(PRE(row[0]),'%.2fms' % (row[1]*1000)) \
for row in i.db._timings]) \
for i in THREAD_LOCAL.instances]
dbtables = dict([(regex_nopasswd.sub('******',i.uri),
if hasattr(THREAD_LOCAL, 'instances'):
dbstats = [TABLE(*[TR(PRE(row[0]), '%.2fms' % (row[1] * 1000))
for row in i.db._timings])
for i in THREAD_LOCAL.instances]
dbtables = dict([(regex_nopasswd.sub('******', i.uri),
{'defined':
sorted(list(set(i.db.tables) -
set(i.db._LAZY_TABLES.keys()))) or
@@ -434,25 +445,35 @@ class Response(Storage):
'[no lazy tables]'})
for i in THREAD_LOCAL.instances])
else:
dbstats = [] # if no db or on GAE
dbstats = [] # if no db or on GAE
dbtables = {}
u = web2py_uuid()
backtotop = A('Back to top', _href="#totop-%s" % u)
return DIV(
BUTTON('design',_onclick="document.location='%s'" % admin),
BUTTON('request',_onclick="jQuery('#request-%s').slideToggle()"%u),
BUTTON('response',_onclick="jQuery('#response-%s').slideToggle()"%u),
BUTTON('session',_onclick="jQuery('#session-%s').slideToggle()"%u),
BUTTON('db tables',_onclick="jQuery('#db-tables-%s').slideToggle()"%u),
BUTTON('db stats',_onclick="jQuery('#db-stats-%s').slideToggle()"%u),
DIV(BEAUTIFY(current.request), backtotop,_class="hidden",_id="request-%s"%u),
DIV(BEAUTIFY(current.session), backtotop, _class="hidden",_id="session-%s"%u),
DIV(BEAUTIFY(current.response), backtotop, _class="hidden",_id="response-%s"%u),
DIV(BEAUTIFY(dbtables), backtotop, _class="hidden",_id="db-tables-%s"%u),
DIV(BEAUTIFY(dbstats), backtotop, _class="hidden",_id="db-stats-%s"%u),
SCRIPT("jQuery('.hidden').hide()")
,_id="totop-%s" % u
)
BUTTON('design', _onclick="document.location='%s'" % admin),
BUTTON('request',
_onclick="jQuery('#request-%s').slideToggle()" % u),
BUTTON('response',
_onclick="jQuery('#response-%s').slideToggle()" % u),
BUTTON('session',
_onclick="jQuery('#session-%s').slideToggle()" % u),
BUTTON('db tables',
_onclick="jQuery('#db-tables-%s').slideToggle()" % u),
BUTTON('db stats',
_onclick="jQuery('#db-stats-%s').slideToggle()" % u),
DIV(BEAUTIFY(current.request), backtotop,
_class="hidden", _id="request-%s" % u),
DIV(BEAUTIFY(current.session), backtotop,
_class="hidden", _id="session-%s" % u),
DIV(BEAUTIFY(current.response), backtotop,
_class="hidden", _id="response-%s" % u),
DIV(BEAUTIFY(dbtables), backtotop, _class="hidden",
_id="db-tables-%s" % u),
DIV(BEAUTIFY(
dbstats), backtotop, _class="hidden", _id="db-stats-%s" % u),
SCRIPT("jQuery('.hidden').hide()"), _id="totop-%s" % u
)
class Session(Storage):
@@ -468,19 +489,21 @@ class Session(Storage):
tablename='web2py_session',
masterapp=None,
migrate=True,
separate = None,
separate=None,
check_client=False,
cookie_key=None,
cookie_expires=None,
compression_level=None
):
):
"""
separate can be separate=lambda(session_name): session_name[-2:]
and it is used to determine a session prefix.
separate can be True and it is set to session_name[-2:]
"""
if request is None: request = current.request
if response is None: response = current.response
if request is None:
request = current.request
if response is None:
response = current.response
if separate == True:
separate = lambda session_name: session_name[-2:]
self._unlock(response)
@@ -512,7 +535,8 @@ class Session(Storage):
response.session_cookie_key = cookie_key
response.session_cookie_compression_level = compression_level
if session_cookie_data:
data = secure_loads(session_cookie_data,cookie_key,compression_level=compression_level)
data = secure_loads(session_cookie_data, cookie_key,
compression_level=compression_level)
if data:
self.update(data)
# else if we are supposed to use file based sessions
@@ -527,7 +551,7 @@ class Session(Storage):
if regex_session_id.match(response.session_id):
response.session_filename = \
os.path.join(up(request.folder), masterapp,
'sessions', response.session_id)
'sessions', response.session_id)
else:
response.session_id = None
# do not try load the data from file is these was data in cookie
@@ -543,8 +567,8 @@ class Session(Storage):
response.session_file.seek(0)
oc = response.session_filename.split('/')[-1]\
.split('-')[0]
if check_client and client!=oc:
raise Exception, "cookie attack"
if check_client and client != oc:
raise Exception("cookie attack")
finally:
pass
#This causes admin login to break. Must find out why.
@@ -557,7 +581,7 @@ class Session(Storage):
if separate:
prefix = separate(response.session_id)
response.session_id = '%s/%s' % \
(prefix,response.session_id)
(prefix, response.session_id)
response.session_filename = \
os.path.join(up(request.folder), masterapp,
'sessions', response.session_id)
@@ -585,25 +609,25 @@ class Session(Storage):
Field('locked', 'boolean', default=False),
Field('client_ip', length=64),
Field('created_datetime', 'datetime',
default=request.now),
default=request.now),
Field('modified_datetime', 'datetime'),
Field('unique_key', length=64),
Field('session_data', 'blob'),
migrate=table_migrate,
)
table = db[tname] # to allow for lazy table
)
table = db[tname] # to allow for lazy table
try:
# Get session data out of the database
(record_id, unique_key) = response.session_id.split(':')
if record_id == '0':
raise Exception, 'record_id == 0'
raise Exception('record_id == 0')
# Select from database
if not session_cookie_data:
rows = db(table.id == record_id).select()
# Make sure the session data exists in the database
if len(rows) == 0 or rows[0].unique_key != unique_key:
raise Exception, 'No record'
raise Exception('No record')
# rows[0].update_record(locked=True)
# Unpickle the data
session_data = cPickle.loads(rows[0].session_data)
@@ -620,7 +644,8 @@ class Session(Storage):
rcookies[response.session_id_name] = response.session_id
rcookies[response.session_id_name]['path'] = '/'
if cookie_expires:
rcookies[response.session_id_name]['expires'] = cookie_expires.strftime(FMT)
rcookies[response.session_id_name][
'expires'] = cookie_expires.strftime(FMT)
# if not cookie_key, but session_data_name in cookies
# expire session_data_name from cookies
if session_cookie_data:
@@ -637,10 +662,10 @@ class Session(Storage):
self._start_timestamp = datetime.datetime.today()
return True
def is_expired(self, seconds = 3600):
def is_expired(self, seconds=3600):
now = datetime.datetime.today()
if not self._last_timestamp or \
self._last_timestamp + datetime.timedelta(seconds = seconds) > now:
self._last_timestamp + datetime.timedelta(seconds=seconds) > now:
self._last_timestamp = now
return False
else:
@@ -654,12 +679,13 @@ class Session(Storage):
self._forget = True
def _try_store_in_cookie(self, request, response):
if response.session_storage_type!='cookie': return False
if response.session_storage_type != 'cookie':
return False
name = response.session_data_name
value = secure_dumps(dict(self),response.session_cookie_key, compression_level=response.session_cookie_compression_level)
value = secure_dumps(dict(self), response.session_cookie_key, compression_level=response.session_cookie_compression_level)
expires = response.session_cookie_expires
rcookies = response.cookies
rcookies.pop(name,None)
rcookies.pop(name, None)
rcookies[name] = value
rcookies[name]['path'] = '/'
if expires:
@@ -667,7 +693,7 @@ class Session(Storage):
return True
def _unchanged(self):
previous_session_hash = self.pop('_session_hash',None)
previous_session_hash = self.pop('_session_hash', None)
if not previous_session_hash and not \
any(value is not None for value in self.itervalues()):
return True
@@ -683,7 +709,7 @@ class Session(Storage):
# don't save if file-based sessions,
# no session id, or session being forgotten
# or no changes to session
if response.session_storage_type!='db' or not response.session_id \
if response.session_storage_type != 'db' or not response.session_id \
or self._forget or self._unchanged():
return False
@@ -692,7 +718,7 @@ class Session(Storage):
unique_key = response.session_db_unique_key
dd = dict(locked=False,
client_ip=request.client.replace(':','.'),
client_ip=request.client.replace(':', '.'),
modified_datetime=request.now,
session_data=cPickle.dumps(dict(self)),
unique_key=unique_key)
@@ -708,11 +734,11 @@ class Session(Storage):
def _try_store_in_cookie_or_file(self, request, response):
return \
self._try_store_in_cookie(request,response) or \
self._try_store_in_file(request,response)
self._try_store_in_cookie(request, response) or \
self._try_store_in_file(request, response)
def _try_store_in_file(self, request, response):
if response.session_storage_type!='file':
if response.session_storage_type != 'file':
return False
try:
@@ -740,7 +766,7 @@ class Session(Storage):
try:
portalocker.unlock(response.session_file)
response.session_locked = False
except: ### this should never happen but happens in Windows
except: # this should never happen but happens in Windows
pass
def _close(self, response):
+41 -39
View File
@@ -24,7 +24,7 @@ class Highlighter(object):
mode,
link=None,
styles=None,
):
):
"""
Initialise highlighter:
mode = language (PYTHON, WEB2PY,C, CPP, HTML, HTML_PLAIN)
@@ -50,7 +50,7 @@ class Highlighter(object):
elif mode == 'HTML':
self.suppress_tokens = []
else:
raise SyntaxError, 'Unknown mode: %s' % mode
raise SyntaxError('Unknown mode: %s' % mode)
self.mode = mode
def c_tokenizer(
@@ -58,7 +58,7 @@ class Highlighter(object):
token,
match,
style,
):
):
"""
Callback for C specific highlighting.
"""
@@ -72,7 +72,7 @@ class Highlighter(object):
token,
match,
style,
):
):
"""
Callback for python specific highlighting.
"""
@@ -93,10 +93,10 @@ class Highlighter(object):
(url, style) = style[5:].split(';', 1)
if url == 'None' or url == '':
self.output.append('<span style="%s">%s</span>'
% (style, value))
% (style, value))
else:
self.output.append('<a href="%s%s" style="%s">%s</a>'
% (url, value, style, value))
% (url, value, style, value))
else:
self.change_style(token, style)
self.output.append(value)
@@ -109,7 +109,7 @@ class Highlighter(object):
token,
match,
style,
):
):
"""
Callback for HTML specific highlighting.
"""
@@ -135,51 +135,51 @@ class Highlighter(object):
re.compile(r'0x[0-9a-fA-F]+|[+-]?\d+(\.\d+)?([eE][+-]\d+)?|\d+'),
'color: red'),
('KEYWORD', re.compile(r'(sizeof|int|long|short|char|void|'
+ r'signed|unsigned|float|double|'
+ r'goto|break|return|continue|asm|'
+ r'case|default|if|else|switch|while|for|do|'
+ r'struct|union|enum|typedef|'
+ r'static|register|auto|volatile|extern|const)(?![a-zA-Z0-9_])'),
+ r'signed|unsigned|float|double|'
+ r'goto|break|return|continue|asm|'
+ r'case|default|if|else|switch|while|for|do|'
+ r'struct|union|enum|typedef|'
+ r'static|register|auto|volatile|extern|const)(?![a-zA-Z0-9_])'),
'color:#185369; font-weight: bold'),
('CPPKEYWORD',
re.compile(r'(class|private|protected|public|template|new|delete|'
+ r'this|friend|using|inline|export|bool|throw|try|catch|'
+ r'operator|typeid|virtual)(?![a-zA-Z0-9_])'),
+ r'this|friend|using|inline|export|bool|throw|try|catch|'
+ r'operator|typeid|virtual)(?![a-zA-Z0-9_])'),
'color: blue; font-weight: bold'),
('STRING', re.compile(r'r?u?\'(.*?)(?<!\\)\'|"(.*?)(?<!\\)"'),
'color: #FF9966'),
('IDENTIFIER', re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*'),
None),
('WHITESPACE', re.compile(r'[ \r\n]+'), 'Keep'),
)),
)),
'PYTHON': (python_tokenizer, (
('GOTOHTML', re.compile(r'\}\}'), 'color: red'),
('PUNC', re.compile(r'[-+*!|&^~/%\=<>\[\]{}(),.:]'),
'font-weight: bold'),
('NUMBER',
re.compile(r'0x[0-9a-fA-F]+|[+-]?\d+(\.\d+)?([eE][+-]\d+)?|\d+'
), 'color: red'),
), 'color: red'),
('KEYWORD',
re.compile(r'(def|class|break|continue|del|exec|finally|pass|'
+ r'print|raise|return|try|except|global|assert|lambda|'
+ r'yield|for|while|if|elif|else|and|in|is|not|or|import|'
+ r'from|True|False)(?![a-zA-Z0-9_])'),
+ r'print|raise|return|try|except|global|assert|lambda|'
+ r'yield|for|while|if|elif|else|and|in|is|not|or|import|'
+ r'from|True|False)(?![a-zA-Z0-9_])'),
'color:#185369; font-weight: bold'),
('WEB2PY',
re.compile(r'(request|response|session|cache|redirect|local_import|HTTP|TR|XML|URL|BEAUTIFY|A|BODY|BR|B|CAT|CENTER|CODE|COL|COLGROUP|DIV|EM|EMBED|FIELDSET|LEGEND|FORM|H1|H2|H3|H4|H5|H6|IFRAME|HEAD|HR|HTML|I|IMG|INPUT|LABEL|LI|LINK|MARKMIN|MENU|META|OBJECT|OL|ON|OPTION|P|PRE|SCRIPT|SELECT|SPAN|STYLE|TABLE|THEAD|TBODY|TFOOT|TAG|TD|TEXTAREA|TH|TITLE|TT|T|UL|XHTML|IS_SLUG|IS_STRONG|IS_LOWER|IS_UPPER|IS_ALPHANUMERIC|IS_DATETIME|IS_DATETIME_IN_RANGE|IS_DATE|IS_DATE_IN_RANGE|IS_DECIMAL_IN_RANGE|IS_EMAIL|IS_EXPR|IS_FLOAT_IN_RANGE|IS_IMAGE|IS_INT_IN_RANGE|IS_IN_SET|IS_IPV4|IS_LIST_OF|IS_LENGTH|IS_MATCH|IS_EQUAL_TO|IS_EMPTY_OR|IS_NULL_OR|IS_NOT_EMPTY|IS_TIME|IS_UPLOAD_FILENAME|IS_URL|CLEANUP|CRYPT|IS_IN_DB|IS_NOT_IN_DB|DAL|Field|SQLFORM|SQLTABLE|xmlescape|embed64)(?![a-zA-Z0-9_])'
), 'link:%(link)s;text-decoration:None;color:#FF5C1F;'),
), 'link:%(link)s;text-decoration:None;color:#FF5C1F;'),
('MAGIC', re.compile(r'self|None'),
'color:#185369; font-weight: bold'),
('MULTILINESTRING', re.compile(r'r?u?(\'\'\'|""")'),
'color: #FF9966'),
('STRING', re.compile(r'r?u?\'(.*?)(?<!\\)\'|"(.*?)(?<!\\)"'
), 'color: #FF9966'),
), 'color: #FF9966'),
('IDENTIFIER', re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*'),
None),
('COMMENT', re.compile(r'\#.*\r?\n'),
'color: green; font-style: italic'),
('WHITESPACE', re.compile(r'[ \r\n]+'), 'Keep'),
)),
)),
'PYTHONMultilineString': (python_tokenizer,
(('ENDMULTILINESTRING',
re.compile(r'.*?("""|\'\'\')',
@@ -191,13 +191,13 @@ class Highlighter(object):
('XMLCRAP', re.compile(r'<![^>]*>'),
'color: blue; font-style: italic'),
('SCRIPT', re.compile(r'<script .*?</script>', re.IGNORECASE
+ re.DOTALL), 'color: black'),
+ re.DOTALL), 'color: black'),
('TAG', re.compile(r'</?\s*[a-zA-Z0-9]+'),
'color: darkred; font-weight: bold'),
('ENDTAG', re.compile(r'/?>'),
'color: darkred; font-weight: bold'),
)),
}
)),
}
def highlight(self, data):
"""
@@ -215,12 +215,12 @@ class Highlighter(object):
if style:
new_mode = \
Highlighter.all_styles[mode][0](self,
token, match, style
% dict(link=self.link))
token, match, style
% dict(link=self.link))
else:
new_mode = \
Highlighter.all_styles[mode][0](self,
token, match, style)
token, match, style)
if not new_mode is None:
mode = new_mode
i += max(1, len(match.group()))
@@ -257,7 +257,7 @@ def highlight(
highlight_line=None,
context_lines=None,
attributes=None,
):
):
styles = styles or {}
attributes = attributes or {}
if not 'CODE' in styles:
@@ -289,7 +289,7 @@ def highlight(
linehighlight_style = styles['LINEHIGHLIGHT']
if language and language.upper() in ['PYTHON', 'C', 'CPP', 'HTML',
'WEB2PY']:
'WEB2PY']:
code = Highlighter(language, link, styles).highlight(code)
else:
code = cgi.escape(code)
@@ -301,22 +301,24 @@ def highlight(
linenumbers = [cgi.escape(counter)] * len(lines)
else:
linenumbers = [str(i + counter) + '.' for i in
xrange(len(lines))]
xrange(len(lines))]
if highlight_line:
if counter and not isinstance(counter, str):
lineno = highlight_line - counter
else:
lineno = highlight_line
if lineno<len(lines):
lines[lineno] = '<div style="%s">%s</div>' % (linehighlight_style, lines[lineno])
linenumbers[lineno] = '<div style="%s">%s</div>' % (linehighlight_style, linenumbers[lineno])
if lineno < len(lines):
lines[lineno] = '<div style="%s">%s</div>' % (
linehighlight_style, lines[lineno])
linenumbers[lineno] = '<div style="%s">%s</div>' % (
linehighlight_style, linenumbers[lineno])
if context_lines:
if lineno + context_lines < len(lines):
del lines[lineno + context_lines:]
del linenumbers[lineno + context_lines:]
if lineno -context_lines > 0:
if lineno - context_lines > 0:
del lines[0:lineno - context_lines]
del linenumbers[0:lineno - context_lines]
@@ -326,13 +328,13 @@ def highlight(
items = attributes.items()
fa = ' '.join([key[1:].lower() for (key, value) in items if key[:1]
== '_' and value is None] + ['%s="%s"'
% (key[1:].lower(), str(value).replace('"', "'"))
% (key[1:].lower(), str(value).replace('"', "'"))
for (key, value) in attributes.items() if key[:1]
== '_' and value])
== '_' and value])
if fa:
fa = ' ' + fa
return '<table%s><tr valign="top"><td style="width:40px; text-align: right;"><pre style="%s">%s</pre></td><td><pre style="%s">%s</pre></td></tr></table>'\
% (fa, linenumbers_style, numbers, code_style, code)
% (fa, linenumbers_style, numbers, code_style, code)
if __name__ == '__main__':
@@ -341,4 +343,4 @@ if __name__ == '__main__':
data = argfp.read()
argfp.close()
print '<html><body>' + highlight(data, sys.argv[2])\
+ '</body></html>'
+ '</body></html>'
+298 -223
View File
File diff suppressed because it is too large Load Diff
+10 -9
View File
@@ -49,7 +49,7 @@ defined_status = {
503: 'SERVICE UNAVAILABLE',
504: 'GATEWAY TIMEOUT',
505: 'HTTP VERSION NOT SUPPORTED',
}
}
# If web2py is executed with python2.4 we need
# to use Exception instead of BaseException
@@ -61,6 +61,7 @@ except NameError:
regex_status = re.compile('^\d{3} \w+$')
class HTTP(BaseException):
def __init__(
@@ -69,14 +70,14 @@ class HTTP(BaseException):
body='',
cookies=None,
**headers
):
):
self.status = status
self.body = body
self.headers = headers
self.cookies2headers(cookies)
def cookies2headers(self,cookies):
if cookies and len(cookies)>0:
def cookies2headers(self, cookies):
if cookies and len(cookies) > 0:
self.headers['Set-Cookie'] = [
str(cookie)[11:] for cookie in cookies.values()]
@@ -90,15 +91,15 @@ class HTTP(BaseException):
status = str(status)
if not regex_status.match(status):
status = '500 %s' % (defined_status[500])
headers.setdefault('Content-Type','text/html; charset=UTF-8')
headers.setdefault('Content-Type', 'text/html; charset=UTF-8')
body = self.body
if status[:1] == '4':
if not body:
body = status
if isinstance(body, str):
if len(body)<512 and \
if len(body) < 512 and \
headers['Content-Type'].startswith('text/html'):
body += '<!-- %s //-->' % ('x'*512) ### trick IE
body += '<!-- %s //-->' % ('x' * 512) # trick IE
headers['Content-Length'] = len(body)
rheaders = []
for k, v in headers.iteritems():
@@ -107,9 +108,9 @@ class HTTP(BaseException):
elif not v is None:
rheaders.append((k, str(v)))
responder(status, rheaders)
if env.get('request_method','')=='HEAD':
if env.get('request_method', '') == 'HEAD':
return ['']
elif isinstance(body,str):
elif isinstance(body, str):
return [body]
elif hasattr(body, '__iter__'):
return body
+7 -7
View File
@@ -69,10 +69,10 @@ base_modules = ['aifc', 'anydbm', 'array', 'asynchat', 'asyncore', 'atexit',
contributed_modules = []
for root, dirs, files in os.walk('gluon'):
for candidate in ['.'.join(
os.path.join(root, os.path.splitext(name)[0]).split(os.sep))
for name in files if name.endswith('.py')
and root.split(os.sep) != ['gluon', 'tests']
]:
os.path.join(root, os.path.splitext(name)[0]).split(os.sep))
for name in files if name.endswith('.py')
and root.split(os.sep) != ['gluon', 'tests']
]:
contributed_modules.append(candidate)
# Python base version
@@ -86,7 +86,7 @@ alert_dependency = ['hashlib', 'uuid']
#
# List of modules deprecated in Python 2.6 or 2.7 that are in the above set
py26_deprecated = ['mhlib', 'multifile', 'mimify', 'sets', 'MimeWriter']
py27_deprecated = [] # ['optparse'] but we need it for now
py27_deprecated = [] # ['optparse'] but we need it for now
if python_version >= '2.6':
base_modules += ['json', 'multiprocessing']
@@ -99,11 +99,11 @@ if python_version >= '2.7':
# Now iterate in the base_modules, trying to do the import
for module in base_modules + contributed_modules:
try:
__import__(module, globals(), locals(), [])
__import__(module, globals(), locals(), [])
except:
# Raise an exception if the current module is a dependency
if module in alert_dependency:
msg = "Missing dependency: %(module)s\n" % locals()
msg += "Try the following command: "
msg += "easy_install-%(python_version)s -U %(module)s" % locals()
raise ImportError, msg
raise ImportError(msg)
+141 -107
View File
@@ -48,16 +48,16 @@ DEFAULT_GET_PLURAL_ID = lambda n: 0
# word is unchangeable
DEFAULT_CONSTRUCT_PLURAL_FORM = lambda word, plural_id: word
NUMBERS = (int,long,float)
NUMBERS = (int, long, float)
# pattern to find T(blah blah blah) expressions
PY_STRING_LITERAL_RE = r'(?<=[^\w]T\()(?P<name>'\
+ r"[uU]?[rR]?(?:'''(?:[^']|'{1,2}(?!'))*''')|"\
+ r"(?:'(?:[^'\\]|\\.)*')|" + r'(?:"""(?:[^"]|"{1,2}(?!"))*""")|'\
+ r'(?:"(?:[^"\\]|\\.)*"))'
+ r"[uU]?[rR]?(?:'''(?:[^']|'{1,2}(?!'))*''')|"\
+ r"(?:'(?:[^'\\]|\\.)*')|" + r'(?:"""(?:[^"]|"{1,2}(?!"))*""")|'\
+ r'(?:"(?:[^"\\]|\\.)*"))'
regex_translate = re.compile(PY_STRING_LITERAL_RE, re.DOTALL)
regex_param=re.compile(r'{(?P<s>.+?)}')
regex_param = re.compile(r'{(?P<s>.+?)}')
# pattern for a valid accept_language
regex_language = \
@@ -66,33 +66,43 @@ regex_langfile = re.compile('^[a-z]{2}(-[a-z]{2})?\.py$')
regex_backslash = re.compile(r"\\([\\{}%])")
regex_plural = re.compile('%({.+?})')
regex_plural_dict = re.compile('^{(?P<w>[^()[\]][^()[\]]*?)\((?P<n>[^()\[\]]+)\)}$') # %%{word(varname or number)}
regex_plural_tuple = re.compile('^{(?P<w>[^[\]()]+)(?:\[(?P<i>\d+)\])?}$') # %%{word[index]} or %%{word}
regex_plural_tuple = re.compile(
'^{(?P<w>[^[\]()]+)(?:\[(?P<i>\d+)\])?}$') # %%{word[index]} or %%{word}
regex_plural_file = re.compile('^plural-[a-zA-Z]{2}(-[a-zA-Z]{2})?\.py$')
def safe_eval(text):
if text.strip():
try:
import ast
return ast.literal_eval(text)
except ImportError:
return eval(text,{},{})
return eval(text, {}, {})
return None
# used as default filter in translator.M()
def markmin(s):
def markmin_aux(m):
return '{%s}' % markmin_escape(m.group('s'))
return render(regex_param.sub(markmin_aux,s),
return render(regex_param.sub(markmin_aux, s),
sep='br', autolinks=None, id_prefix='')
# UTF8 helper functions
def upper_fun(s):
return unicode(s,'utf-8').upper().encode('utf-8')
return unicode(s, 'utf-8').upper().encode('utf-8')
def title_fun(s):
return unicode(s,'utf-8').title().encode('utf-8')
return unicode(s, 'utf-8').title().encode('utf-8')
def cap_fun(s):
return unicode(s,'utf-8').capitalize().encode('utf-8')
ttab_in = maketrans("\\%{}", '\x1c\x1d\x1e\x1f')
return unicode(s, 'utf-8').capitalize().encode('utf-8')
ttab_in = maketrans("\\%{}", '\x1c\x1d\x1e\x1f')
ttab_out = maketrans('\x1c\x1d\x1e\x1f', "\\%{}")
# cache of translated messages:
@@ -105,13 +115,14 @@ ttab_out = maketrans('\x1c\x1d\x1e\x1f', "\\%{}")
# ...
# }
global_language_cache={}
global_language_cache = {}
def get_from_cache(cache, val, fun):
lang_dict, lock = cache
lock.acquire()
try:
result = lang_dict.get(val);
result = lang_dict.get(val)
finally:
lock.release()
if result:
@@ -123,16 +134,18 @@ def get_from_cache(cache, val, fun):
lock.release()
return result
def clear_cache(filename):
cache = global_language_cache.setdefault(
filename, ({}, allocate_lock()))
lang_dict, lock = cache
lock.acquire()
try:
lang_dict.clear();
lang_dict.clear()
finally:
lock.release()
def read_dict_aux(filename):
lang_text = portalocker.read_locked(filename).replace('\r\n', '\n')
clear_cache(filename)
@@ -141,13 +154,14 @@ def read_dict_aux(filename):
except Exception, e:
status = 'Syntax error in %s (%s)' % (filename, e)
logging.error(status)
return {'__corrupted__':status}
return {'__corrupted__': status}
def read_dict(filename):
""" return dictionary with translation messages
"""
return getcfs('lang:'+filename, filename,
lambda: read_dict_aux(filename))
return getcfs('lang:' + filename, filename,
lambda: read_dict_aux(filename))
def read_possible_plural_rules():
@@ -159,17 +173,17 @@ def read_possible_plural_rules():
try:
import contrib.plural_rules as package
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
if len(modname)==2:
module = __import__(package.__name__+'.'+modname,
if len(modname) == 2:
module = __import__(package.__name__ + '.' + modname,
fromlist=[modname])
lang = modname
pname = modname+'.py'
nplurals = getattr(module,'nplurals', DEFAULT_NPLURALS)
pname = modname + '.py'
nplurals = getattr(module, 'nplurals', DEFAULT_NPLURALS)
get_plural_id = getattr(
module,'get_plural_id',
module, 'get_plural_id',
DEFAULT_GET_PLURAL_ID)
construct_plural_form = getattr(
module,'construct_plural_form',
module, 'construct_plural_form',
DEFAULT_CONSTRUCT_PLURAL_FORM)
plurals[lang] = (lang, nplurals, get_plural_id,
construct_plural_form)
@@ -179,6 +193,7 @@ def read_possible_plural_rules():
PLURAL_RULES = read_possible_plural_rules()
def read_possible_languages_aux(langdir):
def get_lang_struct(lang, langcode, langname, langfile_mtime):
if lang == 'default':
@@ -189,27 +204,28 @@ def read_possible_languages_aux(langdir):
nplurals,
get_plural_id,
construct_plural_form
) = PLURAL_RULES.get(real_lang[:2],('default',
DEFAULT_NPLURALS,
DEFAULT_GET_PLURAL_ID,
DEFAULT_CONSTRUCT_PLURAL_FORM))
) = PLURAL_RULES.get(real_lang[:2], ('default',
DEFAULT_NPLURALS,
DEFAULT_GET_PLURAL_ID,
DEFAULT_CONSTRUCT_PLURAL_FORM))
if prules_langcode != 'default':
(pluraldict_fname,
pluraldict_mtime) = plurals.get(real_lang,
plurals.get(real_lang[:2],
('plural-%s.py'%real_lang,0)))
plurals.get(real_lang[:2],
('plural-%s.py' % real_lang, 0)))
else:
pluraldict_fname = None
pluraldict_mtime = 0
return (langcode, # language code from !langcode!
langname, # language name in national spelling from !langname!
langname,
# language name in national spelling from !langname!
langfile_mtime, # m_time of language file
pluraldict_fname,# name of plural dictionary file or None (when default.py is not exist)
pluraldict_mtime,# m_time of plural dictionary file or 0 if file is not exist
prules_langcode, # code of plural rules language or 'default'
pluraldict_fname, # name of plural dictionary file or None (when default.py is not exist)
pluraldict_mtime, # m_time of plural dictionary file or 0 if file is not exist
prules_langcode, # code of plural rules language or 'default'
nplurals, # nplurals for current language
get_plural_id, # get_plural_id() for current language
construct_plural_form) # construct_plural_form() for current language
construct_plural_form) # construct_plural_form() for current language
plurals = {}
flist = oslistdir(langdir)
@@ -217,17 +233,17 @@ def read_possible_languages_aux(langdir):
for pname in flist:
if regex_plural_file.match(pname):
plurals[pname[7:-3]] = (pname,
ostat(pjoin(langdir,pname)).st_mtime)
ostat(pjoin(langdir, pname)).st_mtime)
langs = {}
# scan languages directory for langfiles:
for fname in flist:
if regex_langfile.match(fname) or fname == 'default.py':
fname_with_path = pjoin(langdir,fname)
fname_with_path = pjoin(langdir, fname)
d = read_dict(fname_with_path)
lang = fname[:-3]
langcode = d.get('!langcode!', lang if lang != 'default'
else DEFAULT_LANGUAGE)
langname = d.get('!langname!',langcode)
else DEFAULT_LANGUAGE)
langname = d.get('!langname!', langcode)
langfile_mtime = ostat(fname_with_path).st_mtime
langs[lang] = get_lang_struct(lang, langcode,
langname, langfile_mtime)
@@ -240,27 +256,31 @@ def read_possible_languages_aux(langdir):
deflangcode = deflang[0]
if deflangcode not in langs:
# create language from default.py:
langs[deflangcode] = deflang[:2]+(0,)+deflang[3:]
langs[deflangcode] = deflang[:2] + (0,) + deflang[3:]
return langs
def read_possible_languages(appdir):
langdir = pjoin(appdir,'languages')
return getcfs('langs:'+langdir, langdir,
lambda: read_possible_languages_aux(langdir))
langdir = pjoin(appdir, 'languages')
return getcfs('langs:' + langdir, langdir,
lambda: read_possible_languages_aux(langdir))
def read_plural_dict_aux(filename):
lang_text = portalocker.read_locked(filename).replace('\r\n', '\n')
try:
return eval(lang_text) or {}
except Exception, e:
status='Syntax error in %s (%s)' % (filename, e)
status = 'Syntax error in %s (%s)' % (filename, e)
logging.error(status)
return {'__corrupted__':status}
return {'__corrupted__': status}
def read_plural_dict(filename):
return getcfs('plurals:'+filename, filename,
lambda: read_plural_dict_aux(filename))
return getcfs('plurals:' + filename, filename,
lambda: read_plural_dict_aux(filename))
def write_plural_dict(filename, contents):
if '__corrupted__' in contents:
@@ -269,8 +289,9 @@ def write_plural_dict(filename, contents):
fp = portalocker.LockedFile(filename, 'w')
fp.write('#!/usr/bin/env python\n{\n# "singular form (0)": ["first plural form (1)", "second plural form (2)", ...],\n')
# coding: utf8\n{\n')
for key in sorted(contents,lambda x,y: cmp(unicode(x,'utf-8').lower(), unicode(y,'utf-8').lower())):
forms = '['+','.join([repr(Utf8(form)) for form in contents[key]])+']'
for key in sorted(contents, lambda x, y: cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())):
forms = '[' + ','.join([repr(Utf8(form))
for form in contents[key]]) + ']'
fp.write('%s: %s,\n' % (repr(Utf8(key)), forms))
fp.write('}\n')
except (IOError, OSError):
@@ -291,13 +312,12 @@ def write_dict(filename, contents):
logging.warning('Unable to write to file %s' % filename)
return
fp.write('# coding: utf8\n{\n')
for key in sorted(contents,lambda x,y: cmp(unicode(x,'utf-8').lower(), unicode(y,'utf-8').lower())):
for key in sorted(contents, lambda x, y: cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())):
fp.write('%s: %s,\n' % (repr(Utf8(key)), repr(Utf8(contents[key]))))
fp.write('}\n')
fp.close()
class lazyT(object):
"""
never to be called explicitly, returned by
@@ -309,12 +329,12 @@ class lazyT(object):
def __init__(
self,
message,
symbols = {},
T = None,
filter = None,
ftag = None,
M = False
):
symbols={},
T=None,
filter=None,
ftag=None,
M=False
):
if isinstance(message, lazyT):
self.m = message.m
self.s = message.s
@@ -354,7 +374,7 @@ class lazyT(object):
def __mul__(self, other):
return str(self) * other
def __cmp__(self,other):
def __cmp__(self, other):
return cmp(str(self), str(other))
def __hash__(self):
@@ -370,7 +390,8 @@ class lazyT(object):
return str(self)[i:j]
def __iter__(self):
for c in str(self): yield c
for c in str(self):
yield c
def __len__(self):
return len(str(self))
@@ -388,9 +409,11 @@ class lazyT(object):
return str(self)
def __mod__(self, symbols):
if self.is_copy: return lazyT(self)
if self.is_copy:
return lazyT(self)
return lazyT(self.m, symbols, self.T, self.f, self.t, self.M)
class translator(object):
"""
this class is instantiated by gluon.compileapp.build_environment
@@ -459,7 +482,8 @@ class translator(object):
returns dictionary with all possible languages:
{ langcode(from filename):
( langcode, # language code from !langcode!
langname, # language name in national spelling from !langname!
langname,
# language name in national spelling from !langname!
langfile_mtime, # m_time of language file
pluraldict_fname,# name of plural dictionary file or None (when default.py is not exist)
pluraldict_mtime,# m_time of plural dictionary file or 0 if file is not exist
@@ -470,14 +494,15 @@ class translator(object):
}
"""
info = read_possible_languages(self.folder)
if lang: info = info.get(lang)
if lang:
info = info.get(lang)
return info
def get_possible_languages(self):
""" get list of all possible languages for current applications """
return list(set(self.current_languages +
[lang for lang in read_possible_languages(self.folder).iterkeys()
if lang != 'default']))
[lang for lang in read_possible_languages(self.folder).iterkeys()
if lang != 'default']))
def set_current_languages(self, *languages):
"""
@@ -486,12 +511,12 @@ class translator(object):
turn translation off to use default language
"""
if len(languages) == 1 and isinstance(
languages[0], (tuple, list)):
languages[0], (tuple, list)):
languages = languages[0]
if not languages or languages[0] is None:
# set default language from default.py/DEFAULT_LANGUAGE
pl_info = self.get_possible_languages_info('default')
if pl_info[2]==0: # langfile_mtime
if pl_info[2] == 0: # langfile_mtime
# if languages/default.py is not found
self.default_language_file = self.langpath
self.default_t = {}
@@ -500,12 +525,11 @@ class translator(object):
self.default_language_file = pjoin(self.langpath,
'default.py')
self.default_t = read_dict(self.default_language_file)
self.current_languages = [pl_info[0]] # !langcode!
self.current_languages = [pl_info[0]] # !langcode!
else:
self.current_languages = list(languages)
self.force(self.http_accept_language)
def plural(self, word, n):
""" get plural form of word for number *n*
NOTE: *word" MUST be defined in current language
@@ -529,14 +553,14 @@ class translator(object):
# etc.
if id != 0:
forms = self.plural_dict.get(word, [])
if len(forms)>=id:
if len(forms) >= id:
# have this plural form:
return forms[id-1]
return forms[id - 1]
else:
# guessing this plural form
forms += ['']*(self.nplurals-len(forms)-1)
forms += [''] * (self.nplurals - len(forms) - 1)
form = self.construct_plural_form(word, id)
forms[id-1] = form
forms[id - 1] = form
self.plural_dict[word] = forms
if self.is_writable and self.plural_file:
write_plural_dict(self.plural_file,
@@ -558,6 +582,7 @@ class translator(object):
of them matches possible_languages.
"""
pl_info = read_possible_languages(self.folder)
def set_plural(language):
"""
initialize plural forms subsystem
@@ -570,7 +595,7 @@ class translator(object):
self.nplurals,
self.get_plural_id,
self.construct_plural_form
) = lang_info[3:]
) = lang_info[3:]
pdict = {}
if pname:
pname = pjoin(self.langpath, pname)
@@ -586,7 +611,7 @@ class translator(object):
self.plural_file = None
self.plural_dict = {}
language = ''
if len(languages)==1 and isinstance(languages[0],str):
if len(languages) == 1 and isinstance(languages[0], str):
languages = regex_language.findall(languages[0].lower())
elif not languages or languages[0] is None:
languages = []
@@ -594,7 +619,7 @@ class translator(object):
if languages:
all_languages = set(lang for lang in pl_info.iterkeys()
if lang != 'default') \
| set(self.current_languages)
| set(self.current_languages)
for lang in languages:
# compare "aa-bb" | "aa" from *language* parameter
# with strings from langlist using such alghorythm:
@@ -604,20 +629,20 @@ class translator(object):
language = lang5
else:
lang2 = lang[:2]
if len(lang5)>2 and lang2 in all_languages:
if len(lang5) > 2 and lang2 in all_languages:
language = lang2
else:
for l in all_languages:
if l[:2]==lang2:
if l[:2] == lang2:
language = l
if language:
if language in self.current_languages:
break
self.language_file = pjoin(self.langpath, language+'.py')
self.language_file = pjoin(self.langpath, language + '.py')
self.t = read_dict(self.language_file)
self.cache = global_language_cache.setdefault(
self.language_file,
({},allocate_lock()))
self.language_file,
({}, allocate_lock()))
set_plural(language)
self.accepted_language = language
return languages
@@ -637,7 +662,7 @@ class translator(object):
if lazy is None:
lazy = self.lazy
if not language:
if lazy :
if lazy:
return lazyT(message, symbols, self)
else:
return self.translate(message, symbols)
@@ -654,18 +679,18 @@ class translator(object):
s = self.get_t(message, prefix)
return filter(s) if filter else self.filter(s)
if filter:
prefix = '@'+(ftag or 'userdef')+'\x01'
prefix = '@' + (ftag or 'userdef') + '\x01'
else:
prefix = '@'+self.ftag+'\x01'
prefix = '@' + self.ftag + '\x01'
message = get_from_cache(
self.cache, prefix+message,
self.cache, prefix + message,
lambda: get_tr(message, prefix, filter))
if symbols or symbols == 0 or symbols == "":
if isinstance(symbols, dict):
symbols.update(
(key, xmlescape(value).translate(ttab_in))
for key, value in symbols.iteritems()
if not isinstance(value, NUMBERS) )
if not isinstance(value, NUMBERS))
else:
if not isinstance(symbols, tuple):
symbols = (symbols,)
@@ -686,7 +711,7 @@ class translator(object):
lazy = self.lazy
if not language:
if lazy:
return lazyT(message, symbols, self, filter, ftag, True)
return lazyT(message, symbols, self, filter, ftag, True)
else:
return self.apply_filter(message, symbols, filter, ftag)
else:
@@ -714,11 +739,12 @@ class translator(object):
message = message.encode('utf8')
if isinstance(prefix, unicode):
prefix = prefix.encode('utf8')
key = prefix+message
key = prefix + message
mt = self.t.get(key, None)
if mt is not None: return mt
if mt is not None:
return mt
# we did not find a translation
if message.find('##')>0 and not '\n' in message:
if message.find('##') > 0 and not '\n' in message:
# remove comments
message = message.rsplit('##', 1)[0]
# guess translation same as original
@@ -756,7 +782,7 @@ class translator(object):
word, !word, !!word, !!!word, ?word?number, ??number, ?number
?word?word[number], ?word?[number], ??word[number]
"""
w,i = m.group('w','i')
w, i = m.group('w', 'i')
c = w[0]
if c not in '!?':
return self.plural(w, symbols[int(i or 0)])
@@ -764,15 +790,17 @@ class translator(object):
(p1, sep, p2) = w[1:].partition("?")
part1 = p1 if sep else ""
(part2, sep, part3) = (p2 if sep else p1).partition("?")
if not sep: part3 = part2
if not sep:
part3 = part2
if i is None:
# ?[word]?number[?number] or ?number
if not part2: return m.group(0)
num = int(part2)
# ?[word]?number[?number] or ?number
if not part2:
return m.group(0)
num = int(part2)
else:
# ?[word]?word2[?word3][number]
num = int(symbols[int(i or 0)])
return part1 if num==1 else part3 if num==0 else part2
# ?[word]?word2[?word3][number]
num = int(symbols[int(i or 0)])
return part1 if num == 1 else part3 if num == 0 else part2
elif w.startswith('!!!'):
word = w[3:]
fun = upper_fun
@@ -783,7 +811,7 @@ class translator(object):
word = w[1:]
fun = cap_fun
if i is not None:
return fun(self.plural(word, symbols[int(i)]))
return fun(self.plural(word, symbols[int(i)]))
return fun(word)
def sub_dict(m):
@@ -792,7 +820,7 @@ class translator(object):
?word2(var), ?word1?word2(var), ?word1?word2?word0(var)
?word2(num), ?word1?word2(num), ?word1?word2?word0(num)
"""
w,n = m.group('w','n')
w, n = m.group('w', 'n')
c = w[0]
n = int(n) if n.isdigit() else symbols[n]
if c not in '!?':
@@ -802,9 +830,10 @@ class translator(object):
(p1, sep, p2) = w[1:].partition("?")
part1 = p1 if sep else ""
(part2, sep, part3) = (p2 if sep else p1).partition("?")
if not sep: part3 = part2
if not sep:
part3 = part2
num = int(n)
return part1 if num==1 else part3 if num==0 else part2
return part1 if num == 1 else part3 if num == 0 else part2
elif w.startswith('!!!'):
word = w[3:]
fun = upper_fun
@@ -824,7 +853,7 @@ class translator(object):
return m.group(0)
return part
message = message % symbols
message = regex_plural.sub(sub_plural, message )
message = regex_plural.sub(sub_plural, message)
return message
def translate(self, message, symbols):
@@ -838,7 +867,7 @@ class translator(object):
symbols.update(
(key, str(value).translate(ttab_in))
for key, value in symbols.iteritems()
if not isinstance(value, NUMBERS) )
if not isinstance(value, NUMBERS))
else:
if not isinstance(symbols, tuple):
symbols = (symbols,)
@@ -849,6 +878,7 @@ class translator(object):
message = self.params_substitution(message, symbols)
return message.translate(ttab_out)
def findT(path, language=DEFAULT_LANGUAGE):
"""
must be run by the admin app
@@ -860,22 +890,22 @@ def findT(path, language=DEFAULT_LANGUAGE):
vp = pjoin(path, 'views')
mop = pjoin(path, 'modules')
for filename in \
listdir(mp, '^.+\.py$', 0)+listdir(cp, '^.+\.py$', 0)\
+listdir(vp, '^.+\.html$', 0)+listdir(mop, '^.+\.py$', 0):
listdir(mp, '^.+\.py$', 0) + listdir(cp, '^.+\.py$', 0)\
+ listdir(vp, '^.+\.html$', 0) + listdir(mop, '^.+\.py$', 0):
data = portalocker.read_locked(filename)
items = regex_translate.findall(data)
for item in items:
try:
message = safe_eval(item)
except:
continue # silently ignore inproperly formatted strings
continue # silently ignore inproperly formatted strings
if not message.startswith('#') and not '\n' in message:
tokens = message.rsplit('##', 1)
else:
# this allows markmin syntax in translations
tokens = [message]
if len(tokens) == 2:
message = tokens[0].strip()+'##'+tokens[1].strip()
message = tokens[0].strip() + '##' + tokens[1].strip()
if message and not message in sentences:
sentences[message] = message
if not '!langcode!' in sentences:
@@ -888,8 +918,12 @@ def findT(path, language=DEFAULT_LANGUAGE):
write_dict(lang_file, sentences)
### important to allow safe session.flash=T(....)
def lazyT_unpickle(data):
return marshal.loads(data)
def lazyT_pickle(data):
return lazyT_unpickle, (marshal.dumps(str(data)),)
copy_reg.pickle(lazyT, lazyT_pickle, lazyT_unpickle)
+84 -75
View File
@@ -51,7 +51,7 @@ from globals import current
# The two are identical unless web2py_path is changed via the web2py.py -f folder option
# main.web2py_path is the same as applications_parent (for backward compatibility)
web2py_path = global_settings.applications_parent # backward compatibility
web2py_path = global_settings.applications_parent # backward compatibility
create_missing_folders()
@@ -107,7 +107,7 @@ try:
version_info.close()
global_settings.web2py_version = parse_version(raw_version_string)
except:
raise RuntimeError, "Cannot determine web2py version"
raise RuntimeError("Cannot determine web2py version")
web2py_version = global_settings.web2py_version
@@ -119,7 +119,8 @@ except:
load()
HTTPS_SCHEMES = set(('https','HTTPS'))
HTTPS_SCHEMES = set(('https', 'HTTPS'))
def get_client(env):
"""
@@ -138,10 +139,11 @@ def get_client(env):
else:
client = '127.0.0.1'
if not is_valid_ip_address(client):
raise HTTP(400,"Bad Request (request.client=%s)" % client)
raise HTTP(400, "Bad Request (request.client=%s)" % client)
return client
def copystream_progress(request, chunk_size= 10**5):
def copystream_progress(request, chunk_size=10 ** 5):
"""
copies request.env.wsgi_input into request.body
and stores progress upload status in cache_ram
@@ -154,22 +156,22 @@ def copystream_progress(request, chunk_size= 10**5):
try:
size = int(env.content_length)
except ValueError:
raise HTTP(400,"Invalid Content-Length header")
raise HTTP(400, "Invalid Content-Length header")
dest = tempfile.TemporaryFile()
if not 'X-Progress-ID' in request.vars:
copystream(source, dest, size, chunk_size)
return dest
cache_key = 'X-Progress-ID:'+request.vars['X-Progress-ID']
cache_key = 'X-Progress-ID:' + request.vars['X-Progress-ID']
cache_ram = CacheInRam(request) # same as cache.ram because meta_storage
cache_ram(cache_key+':length', lambda: size, 0)
cache_ram(cache_key+':uploaded', lambda: 0, 0)
cache_ram(cache_key + ':length', lambda: size, 0)
cache_ram(cache_key + ':uploaded', lambda: 0, 0)
while size > 0:
if size < chunk_size:
data = source.read(size)
cache_ram.increment(cache_key+':uploaded', size)
cache_ram.increment(cache_key + ':uploaded', size)
else:
data = source.read(chunk_size)
cache_ram.increment(cache_key+':uploaded', chunk_size)
cache_ram.increment(cache_key + ':uploaded', chunk_size)
length = len(data)
if length > size:
(data, length) = (data[:size], size)
@@ -180,8 +182,8 @@ def copystream_progress(request, chunk_size= 10**5):
if length < chunk_size:
break
dest.seek(0)
cache_ram(cache_key+':length', None)
cache_ram(cache_key+':uploaded', None)
cache_ram(cache_key + ':length', None)
cache_ram(cache_key + ':uploaded', None)
return dest
@@ -224,8 +226,9 @@ def serve_controller(request, response, session):
page = response.body.getvalue()
# logic to garbage collect after exec, not always, once every 100 requests
global requests
requests = ('requests' in globals()) and (requests+1) % 100 or 0
if not requests: gc.collect()
requests = ('requests' in globals()) and (requests + 1) % 100 or 0
if not requests:
gc.collect()
# end garbage collection logic
# ##################################################
@@ -233,13 +236,14 @@ def serve_controller(request, response, session):
# ##################################################
default_headers = [
('Content-Type', contenttype('.'+request.extension)),
('Cache-Control','no-store, no-cache, must-revalidate, post-check=0, pre-check=0'),
('Content-Type', contenttype('.' + request.extension)),
('Cache-Control',
'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'),
('Expires', time.strftime('%a, %d %b %Y %H:%M:%S GMT',
time.gmtime())),
('Pragma', 'no-cache')]
for key,value in default_headers:
response.headers.setdefault(key,value)
for key, value in default_headers:
response.headers.setdefault(key, value)
raise HTTP(response.status, page, **response.headers)
@@ -253,9 +257,9 @@ def start_response_aux(status, headers, exc_info, response=None):
to call third party WSGI applications
"""
response.status = str(status).split(' ',1)[0]
response.status = str(status).split(' ', 1)[0]
response.headers = dict(headers)
return lambda *args, **kargs: response.write(escape=False,*args,**kargs)
return lambda *args, **kargs: response.write(escape=False, *args, **kargs)
def middleware_aux(request, response, *middleware_apps):
@@ -270,24 +274,27 @@ def middleware_aux(request, response, *middleware_apps):
def middleware(f):
def app(environ, start_response):
data = f()
start_response(response.status,response.headers.items())
if isinstance(data,list):
start_response(response.status, response.headers.items())
if isinstance(data, list):
return data
return [data]
for item in middleware_apps:
app=item(app)
app = item(app)
def caller(app):
wsgi = request.wsgi
return app(wsgi.environ, wsgi.start_response)
return lambda caller=caller, app=app: caller(app)
return middleware
def environ_aux(environ,request):
def environ_aux(environ, request):
new_environ = copy.copy(environ)
new_environ['wsgi.input'] = request.body
new_environ['wsgi.version'] = 1
return new_environ
def parse_get_post_vars(request, environ):
# always parse variables in URL for GET, POST, PUT, DELETE, etc. in get_vars
@@ -307,22 +314,23 @@ def parse_get_post_vars(request, environ):
try:
request.body = body = copystream_progress(request)
except IOError:
raise HTTP(400,"Bad Request - HTTP body is incomplete")
raise HTTP(400, "Bad Request - HTTP body is incomplete")
if (body and env.request_method in ('POST', 'PUT', 'BOTH')):
dpost = cgi.FieldStorage(fp=body,environ=environ,keep_blank_values=1)
dpost = cgi.FieldStorage(fp=body, environ=environ, keep_blank_values=1)
# The same detection used by FieldStorage to detect multipart POSTs
is_multipart = dpost.type[:10] == 'multipart/'
body.seek(0)
isle25 = sys.version_info[1] <= 5
def listify(a):
return (not isinstance(a,list) and [a]) or a
return (not isinstance(a, list) and [a]) or a
try:
keys = sorted(dpost)
except TypeError:
keys = []
for key in keys:
if key is None: continue # not sure why cgi.FieldStorage returns None key
if key is None:
continue # not sure why cgi.FieldStorage returns None key
dpk = dpost[key]
# if en element is not a file replace it with its value else leave it alone
if isinstance(dpk, list):
@@ -347,7 +355,8 @@ def parse_get_post_vars(request, environ):
pvalue = pvalue[:-len(gvalue)]
request.vars[key] = value
if len(pvalue):
request.post_vars[key] = (len(pvalue)>1 and pvalue) or pvalue[0]
request.post_vars[key] = (len(pvalue) >
1 and pvalue) or pvalue[0]
def wsgibase(environ, responder):
@@ -407,22 +416,23 @@ def wsgibase(environ, responder):
response.status = env.web2py_status_code or response.status
if static_file:
if environ.get('QUERY_STRING','').startswith(
'attachment'):
if environ.get('QUERY_STRING', '').startswith(
'attachment'):
response.headers['Content-Disposition'] \
= 'attachment'
if version:
response.headers['Cache-Control'] = 'max-age=315360000'
response.headers['Expires'] = 'Thu, 31 Dec 2037 23:59:59 GMT'
response.headers[
'Expires'] = 'Thu, 31 Dec 2037 23:59:59 GMT'
response.stream(static_file, request=request)
# ##################################################
# fill in request items
# ##################################################
app = request.application ## must go after url_in!
app = request.application # must go after url_in!
if not global_settings.local_hosts:
local_hosts = ['127.0.0.1','::ffff:127.0.0.1']
local_hosts = ['127.0.0.1', '::ffff:127.0.0.1']
if not global_settings.web2py_runtime_gae:
try:
local_hosts.append(socket.gethostname())
@@ -433,7 +443,7 @@ def wsgibase(environ, responder):
local_hosts += [
env.server_name,
socket.gethostbyname(env.server_name)]
except (socket.gaierror,TypeError):
except (socket.gaierror, TypeError):
pass
global_settings.local_hosts = local_hosts
else:
@@ -442,15 +452,15 @@ def wsgibase(environ, responder):
x_req_with = str(env.http_x_requested_with).lower()
request.update(
client = client,
folder = abspath('applications',app) + os.sep,
ajax = x_req_with == 'xmlhttprequest',
cid = env.http_web2py_component_element,
is_local = env.remote_addr in local_hosts,
is_https = env.wsgi_url_scheme in HTTPS_SCHEMES \
or request.env.http_x_forwarded_proto in HTTPS_SCHEMES \
or env.https=='on')
request.uuid = request.compute_uuid() # requires client
client=client,
folder=abspath('applications', app) + os.sep,
ajax=x_req_with == 'xmlhttprequest',
cid=env.http_web2py_component_element,
is_local=env.remote_addr in local_hosts,
is_https=env.wsgi_url_scheme in HTTPS_SCHEMES
or request.env.http_x_forwarded_proto in HTTPS_SCHEMES
or env.https == 'on')
request.uuid = request.compute_uuid() # requires client
request.url = environ['PATH_INFO']
# ##################################################
@@ -460,7 +470,7 @@ def wsgibase(environ, responder):
if not exists(request.folder):
if app == rwthread.routes.default_application \
and app != 'welcome':
redirect(URL('welcome','default','index'))
redirect(URL('welcome', 'default', 'index'))
elif rwthread.routes.error_handler:
_handler = rwthread.routes.error_handler
redirect(URL(_handler['application'],
@@ -468,11 +478,11 @@ def wsgibase(environ, responder):
_handler['function'],
args=app))
else:
raise HTTP(404, rwthread.routes.error_message \
% 'invalid request',
raise HTTP(404, rwthread.routes.error_message
% 'invalid request',
web2py_error='invalid application')
elif not request.is_local and \
exists(pjoin(request.folder,'DISABLED')):
exists(pjoin(request.folder, 'DISABLED')):
raise HTTP(503, "<html><body><h1>Temporarily down for maintenance</h1></body></html>")
# ##################################################
@@ -491,13 +501,13 @@ def wsgibase(environ, responder):
# expose wsgi hooks for convenience
# ##################################################
request.wsgi.environ = environ_aux(environ,request)
request.wsgi.environ = environ_aux(environ, request)
request.wsgi.start_response = \
lambda status='200', headers=[], \
exec_info=None, response=response: \
start_response_aux(status, headers, exec_info, response)
request.wsgi.middleware = \
lambda *a: middleware_aux(request,response,*a)
lambda *a: middleware_aux(request, response, *a)
# ##################################################
# load cookies
@@ -507,7 +517,7 @@ def wsgibase(environ, responder):
try:
request.cookies.load(env.http_cookie)
except Cookie.CookieError, e:
pass # invalid cookies
pass # invalid cookies
# ##################################################
# try load session or create new session file
@@ -530,7 +540,7 @@ def wsgibase(environ, responder):
except HTTP, http_response:
if static_file:
return http_response.to(responder,env=env)
return http_response.to(responder, env=env)
if request.body:
request.body.close()
@@ -562,9 +572,9 @@ def wsgibase(environ, responder):
if request.cid:
if response.flash:
http_response.headers['web2py-component-flash'] = urllib2.quote(xmlescape(response.flash).replace('\n',''))
http_response.headers['web2py-component-flash'] = urllib2.quote(xmlescape(response.flash).replace('\n', ''))
if response.js:
http_response.headers['web2py-component-command'] = response.js.replace('\n','')
http_response.headers['web2py-component-command'] = response.js.replace('\n', '')
# ##################################################
# store cookies in headers
@@ -576,7 +586,7 @@ def wsgibase(environ, responder):
elif session._secure:
rcookies[response.session_id_name]['secure'] = True
http_response.cookies2headers(rcookies)
ticket=None
ticket = None
except RestrictedError, e:
@@ -594,8 +604,8 @@ def wsgibase(environ, responder):
BaseAdapter.close_all_instances('rollback')
http_response = \
HTTP(500, rwthread.routes.error_message_ticket % \
dict(ticket=ticket),
HTTP(500, rwthread.routes.error_message_ticket %
dict(ticket=ticket),
web2py_error='ticket %s' % ticket)
except:
@@ -617,8 +627,8 @@ def wsgibase(environ, responder):
e = RestrictedError('Framework', '', '', locals())
ticket = e.log(request) or 'unrecoverable'
http_response = \
HTTP(500, rwthread.routes.error_message_ticket \
% dict(ticket=ticket),
HTTP(500, rwthread.routes.error_message_ticket
% dict(ticket=ticket),
web2py_error='ticket %s' % ticket)
finally:
@@ -630,10 +640,10 @@ def wsgibase(environ, responder):
http_response, new_environ = try_rewrite_on_error(
http_response, request, environ, ticket)
if not http_response:
return wsgibase(new_environ,responder)
return wsgibase(new_environ, responder)
if global_settings.web2py_crontype == 'soft':
newcron.softcron(global_settings.applications_parent).start()
return http_response.to(responder,env=env)
return http_response.to(responder, env=env)
def save_password(password, port):
@@ -712,14 +722,14 @@ def appfactory(wsgiapp=wsgibase,
locker.acquire()
cProfile.runctx('ret[0] = wsgiapp(environ, responder2)',
globals(), locals(), profilerfilename+'.tmp')
stat = pstats.Stats(profilerfilename+'.tmp')
globals(), locals(), profilerfilename + '.tmp')
stat = pstats.Stats(profilerfilename + '.tmp')
stat.stream = cStringIO.StringIO()
stat.strip_dirs().sort_stats("time").print_stats(80)
profile_out = stat.stream.getvalue()
profile_file = open(profilerfilename, 'a')
profile_file.write('%s\n%s\n%s\n%s\n\n' % \
('='*60, environ['PATH_INFO'], '='*60, profile_out))
profile_file.write('%s\n%s\n%s\n%s\n\n' %
('=' * 60, environ['PATH_INFO'], '=' * 60, profile_out))
profile_file.close()
locker.release()
try:
@@ -731,7 +741,7 @@ def appfactory(wsgiapp=wsgibase,
environ['SERVER_PROTOCOL'],
(status_headers[0])[:3],
time.time() - time_in,
)
)
if not logfilename:
sys.stdout.write(line)
elif isinstance(logfilename, str):
@@ -766,11 +776,11 @@ class HttpServer(object):
server_name=None,
request_queue_size=5,
timeout=10,
socket_timeout = 1,
shutdown_timeout=None, # Rocket does not use a shutdown timeout
socket_timeout=1,
shutdown_timeout=None, # Rocket does not use a shutdown timeout
path=None,
interfaces=None # Rocket is able to use several interfaces - must be list of socket-tuples as string
):
interfaces=None # Rocket is able to use several interfaces - must be list of socket-tuples as string
):
"""
starts the web server.
"""
@@ -779,9 +789,9 @@ class HttpServer(object):
# if interfaces is specified, it must be tested for rocket parameter correctness
# not necessarily completely tested (e.g. content of tuples or ip-format)
import types
if isinstance(interfaces,types.ListType):
if isinstance(interfaces, types.ListType):
for i in interfaces:
if not isinstance(i,types.TupleType):
if not isinstance(i, types.TupleType):
raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/"
else:
raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/"
@@ -822,7 +832,7 @@ class HttpServer(object):
logger.info('SSL is ON')
app_info = {'wsgi_app': appfactory(wsgibase,
log_filename,
profiler_filename) }
profiler_filename)}
self.server = rocket.Rocket(interfaces or tuple(sock_list),
method='wsgi',
@@ -834,7 +844,6 @@ class HttpServer(object):
handle_signals=False,
)
def start(self):
"""
start the web server
+1
View File
@@ -5,6 +5,7 @@ try:
except:
tkMessageBox = None
class MessageBoxHandler(logging.Handler):
def __init__(self):
logging.Handler.__init__(self)
+5 -5
View File
@@ -11,18 +11,18 @@ import re
# pattern to find defined tables
regex_tables = re.compile(\
regex_tables = re.compile(
"""^[\w]+\.define_table\(\s*[\'\"](?P<name>\w+)[\'\"]""",
flags=re.M)
# pattern to find exposed functions in controller
regex_expose = re.compile(\
regex_expose = re.compile(
'^def\s+(?P<name>(?:[a-zA-Z0-9]\w*)|(?:_[a-zA-Z0-9]\w*))\(\)\s*:',
flags=re.M)
regex_include = re.compile(\
regex_include = re.compile(
'(?P<all>\{\{\s*include\s+[\'"](?P<name>[^\'"]*)[\'"]\s*\}\})')
regex_extend = re.compile(\
'^\s*(?P<all>\{\{\s*extend\s+[\'"](?P<name>[^\'"]+)[\'"]\s*\}\})',re.MULTILINE)
regex_extend = re.compile(
'^\s*(?P<all>\{\{\s*extend\s+[\'"](?P<name>[^\'"]+)[\'"]\s*\}\})', re.MULTILINE)
+60 -51
View File
@@ -23,6 +23,7 @@ from settings import global_settings
logger = logging.getLogger("web2py.cron")
_cron_stopping = False
def absolute_path_link(path):
"""
Return an absolute path for the destination of a symlink
@@ -36,11 +37,13 @@ def absolute_path_link(path):
link = os.path.abspath(path)
return link
def stopcron():
"graceful shutdown of cron"
global _cron_stopping
_cron_stopping = True
class extcron(threading.Thread):
def __init__(self, applications_parent, apps=None):
@@ -55,6 +58,7 @@ class extcron(threading.Thread):
logger.debug('external cron invocation')
crondance(self.path, 'external', startup=False, apps=self.apps)
class hardcron(threading.Thread):
def __init__(self, applications_parent):
@@ -66,7 +70,7 @@ class hardcron(threading.Thread):
def launch(self):
if not _cron_stopping:
logger.debug('hard cron invocation')
crondance(self.path, 'hard', startup = False)
crondance(self.path, 'hard', startup=False)
def run(self):
s = sched.scheduler(time.time, time.sleep)
@@ -76,6 +80,7 @@ class hardcron(threading.Thread):
s.enter(60 - now % 60, 1, self.launch, ())
s.run()
class softcron(threading.Thread):
def __init__(self, applications_parent):
@@ -88,16 +93,17 @@ class softcron(threading.Thread):
logger.debug('soft cron invocation')
crondance(self.path, 'soft', startup=False)
class Token(object):
def __init__(self,path):
def __init__(self, path):
self.path = os.path.join(path, 'cron.master')
if not os.path.exists(self.path):
fileutils.write_file(self.path, '', 'wb')
self.master = None
self.now = time.time()
def acquire(self,startup=False):
def acquire(self, startup=False):
"""
returns the time when the lock is acquired or
None if cron already running
@@ -112,12 +118,12 @@ class Token(object):
if portalocker.LOCK_EX is None:
logger.warning('WEB2PY CRON: Disabled because no file locking')
return None
self.master = open(self.path,'rb+')
self.master = open(self.path, 'rb+')
try:
ret = None
portalocker.lock(self.master,portalocker.LOCK_EX)
portalocker.lock(self.master, portalocker.LOCK_EX)
try:
(start, stop) = cPickle.load(self.master)
(start, stop) = cPickle.load(self.master)
except:
(start, stop) = (0, 1)
if startup or self.now - start > 59.99:
@@ -127,7 +133,7 @@ class Token(object):
logger.warning('WEB2PY CRON: Stale cron.master detected')
logger.debug('WEB2PY CRON: Acquiring lock')
self.master.seek(0)
cPickle.dump((self.now,0),self.master)
cPickle.dump((self.now, 0), self.master)
finally:
portalocker.unlock(self.master)
if not ret:
@@ -141,13 +147,13 @@ class Token(object):
was completed
"""
if not self.master.closed:
portalocker.lock(self.master,portalocker.LOCK_EX)
portalocker.lock(self.master, portalocker.LOCK_EX)
logger.debug('WEB2PY CRON: Releasing cron lock')
self.master.seek(0)
(start, stop) = cPickle.load(self.master)
if start == self.now: # if this is my lock
(start, stop) = cPickle.load(self.master)
if start == self.now: # if this is my lock
self.master.seek(0)
cPickle.dump((self.now,time.time()),self.master)
cPickle.dump((self.now, time.time()), self.master)
portalocker.unlock(self.master)
self.master.close()
@@ -177,25 +183,26 @@ def rangetolist(s, period='min'):
def parsecronline(line):
task = {}
if line.startswith('@reboot'):
line=line.replace('@reboot', '-1 * * * *')
line = line.replace('@reboot', '-1 * * * *')
elif line.startswith('@yearly'):
line=line.replace('@yearly', '0 0 1 1 *')
line = line.replace('@yearly', '0 0 1 1 *')
elif line.startswith('@annually'):
line=line.replace('@annually', '0 0 1 1 *')
line = line.replace('@annually', '0 0 1 1 *')
elif line.startswith('@monthly'):
line=line.replace('@monthly', '0 0 1 * *')
line = line.replace('@monthly', '0 0 1 * *')
elif line.startswith('@weekly'):
line=line.replace('@weekly', '0 0 * * 0')
line = line.replace('@weekly', '0 0 * * 0')
elif line.startswith('@daily'):
line=line.replace('@daily', '0 0 * * *')
line = line.replace('@daily', '0 0 * * *')
elif line.startswith('@midnight'):
line=line.replace('@midnight', '0 0 * * *')
line = line.replace('@midnight', '0 0 * * *')
elif line.startswith('@hourly'):
line=line.replace('@hourly', '0 * * * *')
line = line.replace('@hourly', '0 * * * *')
params = line.strip().split(None, 6)
if len(params) < 7:
return None
daysofweek={'sun':0,'mon':1,'tue':2,'wed':3,'thu':4,'fri':5,'sat':6}
daysofweek = {'sun': 0, 'mon': 1, 'tue': 2, 'wed': 3, 'thu': 4,
'fri': 5, 'sat': 6}
for (s, id) in zip(params[:5], ['min', 'hr', 'dom', 'mon', 'dow']):
if not s in [None, '*']:
task[id] = []
@@ -205,9 +212,9 @@ def parsecronline(line):
val = '%s/1' % val
if '/' in val:
task[id] += rangetolist(val, id)
elif val.isdigit() or val=='-1':
elif val.isdigit() or val == '-1':
task[id].append(int(val))
elif id=='dow' and val[:3].lower() in daysofweek:
elif id == 'dow' and val[:3].lower() in daysofweek:
task[id].append(daysofweek(val[:3].lower()))
task['user'] = params[5]
task['cmd'] = params[6]
@@ -225,7 +232,7 @@ class cronlauncher(threading.Thread):
def run(self):
import subprocess
if isinstance(self.cmd, (list,tuple)):
if isinstance(self.cmd, (list, tuple)):
cmd = self.cmd
else:
cmd = self.cmd.split()
@@ -234,28 +241,29 @@ class cronlauncher(threading.Thread):
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=self.shell)
(stdoutdata,stderrdata) = proc.communicate()
(stdoutdata, stderrdata) = proc.communicate()
if proc.returncode != 0:
logger.warning(
'WEB2PY CRON Call returned code %s:\n%s' % \
(proc.returncode, stdoutdata+stderrdata))
'WEB2PY CRON Call returned code %s:\n%s' %
(proc.returncode, stdoutdata + stderrdata))
else:
logger.debug('WEB2PY CRON Call returned success:\n%s' \
% stdoutdata)
logger.debug('WEB2PY CRON Call returned success:\n%s'
% stdoutdata)
def crondance(applications_parent, ctype='soft', startup=False, apps=None):
apppath = os.path.join(applications_parent,'applications')
apppath = os.path.join(applications_parent, 'applications')
cron_path = os.path.join(applications_parent)
token = Token(cron_path)
cronmaster = token.acquire(startup=startup)
if not cronmaster:
return
now_s = time.localtime()
checks=(('min',now_s.tm_min),
('hr',now_s.tm_hour),
('mon',now_s.tm_mon),
('dom',now_s.tm_mday),
('dow',(now_s.tm_wday+1)%7))
checks = (('min', now_s.tm_min),
('hr', now_s.tm_hour),
('mon', now_s.tm_mon),
('dom', now_s.tm_mday),
('dow', (now_s.tm_wday + 1) % 7))
if apps is None:
apps = [x for x in os.listdir(apppath)
@@ -265,8 +273,8 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
for app in apps:
if _cron_stopping:
break;
apath = os.path.join(apppath,app)
break
apath = os.path.join(apppath, app)
# if app is a symbolic link to other app, skip it
full_apath_link = absolute_path_link(apath)
@@ -281,7 +289,8 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
continue
try:
cronlines = fileutils.readlines_file(crontab, 'rt')
lines = [x.strip() for x in cronlines if x.strip() and not x.strip().startswith('#')]
lines = [x.strip() for x in cronlines if x.strip(
) and not x.strip().startswith('#')]
tasks = [parsecronline(cline) for cline in lines]
except Exception, e:
logger.error('WEB2PY CRON: crontab read error %s' % e)
@@ -289,42 +298,42 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
for task in tasks:
if _cron_stopping:
break;
break
commands = [sys.executable]
w2p_path = fileutils.abspath('web2py.py', gluon=True)
if os.path.exists(w2p_path):
commands.append(w2p_path)
if global_settings.applications_parent != global_settings.gluon_parent:
commands.extend(('-f', global_settings.applications_parent))
citems = [(k in task and not v in task[k]) for k,v in checks]
task_min= task.get('min',[])
citems = [(k in task and not v in task[k]) for k, v in checks]
task_min = task.get('min', [])
if not task:
continue
elif not startup and task_min == [-1]:
continue
elif task_min != [-1] and reduce(lambda a,b: a or b, citems):
elif task_min != [-1] and reduce(lambda a, b: a or b, citems):
continue
logger.info('WEB2PY CRON (%s): %s executing %s in %s at %s' \
% (ctype, app, task.get('cmd'),
os.getcwd(), datetime.datetime.now()))
logger.info('WEB2PY CRON (%s): %s executing %s in %s at %s'
% (ctype, app, task.get('cmd'),
os.getcwd(), datetime.datetime.now()))
action, command, models = False, task['cmd'], ''
if command.startswith('**'):
(action,models,command) = (True,'',command[2:])
(action, models, command) = (True, '', command[2:])
elif command.startswith('*'):
(action,models,command) = (True,'-M',command[1:])
(action, models, command) = (True, '-M', command[1:])
else:
action=False
action = False
if action and command.endswith('.py'):
commands.extend(('-J', # cron job
models, # import models?
'-S', app, # app name
'-a', '"<recycle>"', # password
'-a', '"<recycle>"', # password
'-R', command)) # command
elif action:
commands.extend(('-J', # cron job
models, # import models?
'-S', app+'/'+command, # app name
'-S', app + '/' + command, # app name
'-a', '"<recycle>"')) # password
else:
commands = command
@@ -338,6 +347,6 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
cronlauncher(commands, shell=shell).start()
except Exception, e:
logger.warning(
'WEB2PY CRON: Execution error for %s: %s' \
% (task.get('cmd'), e))
'WEB2PY CRON: Execution error for %s: %s'
% (task.get('cmd'), e))
token.release()
+20 -12
View File
@@ -110,54 +110,62 @@ else:
class LockedFile(object):
def __init__(self,filename, mode='rb'):
def __init__(self, filename, mode='rb'):
self.filename = filename
self.mode = mode
self.file = None
if 'r' in mode:
self.file = open(filename,mode)
lock(self.file,LOCK_SH)
self.file = open(filename, mode)
lock(self.file, LOCK_SH)
elif 'w' in mode or 'a' in mode:
self.file = open(filename,mode.replace('w','a'))
lock(self.file,LOCK_EX)
self.file = open(filename, mode.replace('w', 'a'))
lock(self.file, LOCK_EX)
if not 'a' in mode:
self.file.seek(0)
self.file.truncate()
else:
raise RuntimeError, "invalid LockedFile(...,mode)"
def read(self,size=None):
raise RuntimeError("invalid LockedFile(...,mode)")
def read(self, size=None):
return self.file.read() if size is None else self.file.read(size)
def readline(self):
return self.file.readline()
def readlines(self):
return self.file.readlines()
def write(self,data):
def write(self, data):
self.file.write(data)
self.file.flush()
def close(self):
if not self.file is None:
unlock(self.file)
self.file.close()
self.file = None
def __del__(self):
if not self.file is None:
self.close()
def read_locked(filename):
fp = LockedFile(filename, 'r')
data = fp.read()
fp.close()
return data
def write_locked(filename,data):
def write_locked(filename, data):
fp = LockedFile(filename, 'w')
data = fp.write(data)
fp.close()
if __name__=='__main__':
f = LockedFile('test.txt',mode='wb')
if __name__ == '__main__':
f = LockedFile('test.txt', mode='wb')
f.write('test ok')
f.close()
f = LockedFile('test.txt',mode='rb')
f = LockedFile('test.txt', mode='rb')
print f.read()
f.close()
+11 -10
View File
@@ -419,7 +419,7 @@ POSTGRESQL_NONRESERVED = set((
'HOLD',
'HOST',
'HOUR',
# 'ID',
# 'ID',
'IDENTITY',
'IF',
'IGNORE',
@@ -501,7 +501,7 @@ POSTGRESQL_NONRESERVED = set((
'MOVE',
'MULTISET',
'MUMPS',
# 'NAME',
# 'NAME',
'NAMES',
'NAMESPACE',
'NCLOB',
@@ -563,7 +563,7 @@ POSTGRESQL_NONRESERVED = set((
'PARTITION',
'PASCAL',
'PASSING',
# 'PASSWORD',
# 'PASSWORD',
'PATH',
'PERCENT_RANK',
'PERCENTILE_CONT',
@@ -622,7 +622,7 @@ POSTGRESQL_NONRESERVED = set((
'RETURNED_SQLSTATE',
'RETURNS',
'REVOKE',
# 'ROLE',
# 'ROLE',
'ROLLBACK',
'ROLLUP',
'ROUTINE',
@@ -697,7 +697,7 @@ POSTGRESQL_NONRESERVED = set((
'SYSTEM',
'SYSTEM_USER',
'T',
# 'TABLE_NAME',
# 'TABLE_NAME',
'TABLESAMPLE',
'TABLESPACE',
'TEMP',
@@ -785,7 +785,7 @@ POSTGRESQL_NONRESERVED = set((
'YEAR',
'YES',
'ZONE',
))
))
#Thanks villas
FIREBIRD = set((
@@ -887,7 +887,7 @@ FIREBIRD = set((
'PAGES',
'PAGE_SIZE',
'PARAMETER',
# 'PASSWORD',
# 'PASSWORD',
'PLAN',
'POST_EVENT',
'QUIT',
@@ -903,7 +903,7 @@ FIREBIRD = set((
'RETURN',
'RETURNING_VALUES',
'RETURNS',
# 'ROLE',
# 'ROLE',
'ROW_COUNT',
'ROWS',
'RUNTIME',
@@ -941,7 +941,7 @@ FIREBIRD = set((
'WEEKDAY',
'WHILE',
'YEARDAY',
))
))
FIREBIRD_NONRESERVED = set((
'BACKUP',
'BLOCK',
@@ -1710,4 +1710,5 @@ ADAPTERS = {
'common': COMMON,
}
ADAPTERS['all'] = reduce(lambda a,b:a.union(b),(x for x in ADAPTERS.values()))
ADAPTERS['all'] = reduce(lambda a, b: a.union(b), (
x for x in ADAPTERS.values()))
+46 -29
View File
@@ -22,6 +22,7 @@ logger = logging.getLogger("web2py")
__all__ = ['RestrictedError', 'restricted', 'TicketStorage', 'compile2']
class TicketStorage(Storage):
"""
@@ -32,7 +33,7 @@ class TicketStorage(Storage):
self,
db=None,
tablename='web2py_ticket'
):
):
Storage.__init__(self)
self.db = db
self.tablename = tablename
@@ -64,7 +65,8 @@ class TicketStorage(Storage):
root = request.folder
if app:
root = os.path.join(os.path.join(root, '..'), app)
errors_folder = os.path.abspath(os.path.join(root, 'errors'))#.replace('\\', '/')
errors_folder = os.path.abspath(
os.path.join(root, 'errors')) # .replace('\\', '/')
return open(os.path.join(errors_folder, ticket_id), mode)
def _get_table(self, db, tablename, app):
@@ -78,7 +80,7 @@ class TicketStorage(Storage):
db.Field('ticket_id', length=100),
db.Field('ticket_data', 'text'),
db.Field('created_datetime', 'datetime'),
)
)
return table
def load(
@@ -86,7 +88,7 @@ class TicketStorage(Storage):
request,
app,
ticket_id,
):
):
if not self.db:
try:
ef = self._error_file(request, ticket_id, 'rb', app)
@@ -102,7 +104,6 @@ class TicketStorage(Storage):
return cPickle.loads(rows[0].ticket_data) if rows else {}
class RestrictedError(Exception):
"""
class used to wrap an exception that occurs in the restricted environment
@@ -115,12 +116,13 @@ class RestrictedError(Exception):
code='',
output='',
environment=None,
):
):
"""
layer here is some description of where in the system the exception
occurred.
"""
if environment is None: environment = {}
if environment is None:
environment = {}
self.layer = layer
self.code = code
self.output = output
@@ -131,7 +133,7 @@ class RestrictedError(Exception):
except:
self.traceback = 'no traceback because template parting error'
try:
self.snapshot = snapshot(context=10,code=code,
self.snapshot = snapshot(context=10, code=code,
environment=self.environment)
except:
self.snapshot = {}
@@ -151,15 +153,14 @@ class RestrictedError(Exception):
'output': str(self.output),
'traceback': str(self.traceback),
'snapshot': self.snapshot,
}
}
ticket_storage = TicketStorage(db=request.tickets_db)
ticket_storage.store(request, request.uuid.split('/',1)[1], d)
ticket_storage.store(request, request.uuid.split('/', 1)[1], d)
return request.uuid
except:
logger.error(self.traceback)
return None
def load(self, request, app, ticket_id):
"""
loads a logged exception.
@@ -186,11 +187,12 @@ class RestrictedError(Exception):
return output
def compile2(code,layer):
def compile2(code, layer):
"""
The +'\n' is necessary else compile fails when code ends in a comment.
"""
return compile(code.rstrip().replace('\r\n','\n')+'\n', layer, 'exec')
return compile(code.rstrip().replace('\r\n', '\n') + '\n', layer, 'exec')
def restricted(code, environment=None, layer='Unknown'):
"""
@@ -198,14 +200,15 @@ def restricted(code, environment=None, layer='Unknown'):
in code it raises a RestrictedError containing the traceback. layer is
passed to RestrictedError to identify where the error occurred.
"""
if environment is None: environment = {}
if environment is None:
environment = {}
environment['__file__'] = layer
environment['__name__'] = '__restricted__'
try:
if type(code) == types.CodeType:
if isinstance(code, types.CodeType):
ccode = code
else:
ccode = compile2(code,layer)
ccode = compile2(code, layer)
exec ccode in environment
except HTTP:
raise
@@ -221,14 +224,21 @@ def restricted(code, environment=None, layer='Unknown'):
output = "%s %s" % (etype, evalue)
raise RestrictedError(layer, code, output, environment)
def snapshot(info=None, context=5, code=None, environment=None):
"""Return a dict describing a given traceback (based on cgitb.text)."""
import os, types, time, linecache, inspect, pydoc, cgitb
import os
import types
import time
import linecache
import inspect
import pydoc
import cgitb
# if no exception info given, get current:
etype, evalue, etb = info or sys.exc_info()
if type(etype) is types.ClassType:
if isinstance(etype, types.ClassType):
etype = etype.__name__
# create a snapshot dict with some basic information
@@ -245,22 +255,26 @@ def snapshot(info=None, context=5, code=None, environment=None):
call = ''
if func != '?':
call = inspect.formatargvalues(args, varargs, varkw, locals,
formatvalue=lambda value: '=' + pydoc.text.repr(value))
formatvalue=lambda value: '=' + pydoc.text.repr(value))
# basic frame information
f = {'file': file, 'func': func, 'call': call, 'lines': {}, 'lnum': lnum}
f = {'file': file, 'func': func, 'call': call, 'lines': {},
'lnum': lnum}
highlight = {}
def reader(lnum=[lnum]):
highlight[lnum[0]] = 1
try: return linecache.getline(file, lnum[0])
finally: lnum[0] += 1
try:
return linecache.getline(file, lnum[0])
finally:
lnum[0] += 1
vars = cgitb.scanvars(reader, frame, locals)
# if it is a view, replace with generated code
if file.endswith('html'):
lmin = lnum>context and (lnum-context) or 0
lmax = lnum+context
lmin = lnum > context and (lnum - context) or 0
lmax = lnum + context
lines = code.split("\n")[lmin:lmax]
index = min(context, lnum) - 1
@@ -273,10 +287,13 @@ def snapshot(info=None, context=5, code=None, environment=None):
# dump local variables (referenced in current line only)
f['dump'] = {}
for name, where, value in vars:
if name in f['dump']: continue
if name in f['dump']:
continue
if value is not cgitb.__UNDEF__:
if where == 'global': name = 'global ' + name
elif where != 'local': name = where + name.split('.')[-1]
if where == 'global':
name = 'global ' + name
elif where != 'local':
name = where + name.split('.')[-1]
f['dump'][name] = pydoc.text.repr(value)
else:
f['dump'][name] = 'undefined'
@@ -290,7 +307,7 @@ def snapshot(info=None, context=5, code=None, environment=None):
if isinstance(evalue, BaseException):
for name in dir(evalue):
# prevent py26 DeprecatedWarning:
if name!='message' or sys.version_info<(2.6):
if name != 'message' or sys.version_info < (2.6):
value = pydoc.text.repr(getattr(evalue, name))
s['exception'][name] = value
@@ -300,7 +317,7 @@ def snapshot(info=None, context=5, code=None, environment=None):
s['locals'][name] = pydoc.text.repr(value)
# add web2py environment variables
for k,v in environment.items():
for k, v in environment.items():
if k in ('request', 'response', 'session'):
s[k] = BEAUTIFY(v)
+172 -127
View File
@@ -38,7 +38,8 @@ THREAD_LOCAL = threading.local() # thread-local storage for routing params
regex_at = re.compile(r'(?<!\\)\$[a-zA-Z]\w*')
regex_anything = re.compile(r'(?<!\\)\$anything')
regex_redirect = re.compile(r'(\d+)->(.*)')
regex_full_url = re.compile(r'^(?P<scheme>http|https|HTTP|HTTPS)\://(?P<host>[^/]*)(?P<uri>.*)')
regex_full_url = re.compile(
r'^(?P<scheme>http|https|HTTP|HTTPS)\://(?P<host>[^/]*)(?P<uri>.*)')
regex_version = re.compile(r'^(_[\d]+\.[\d]+\.[\d]+)$')
# pattern to replace spaces with underscore in URL
# also the html escaped variants '+' and '%20' are covered
@@ -60,25 +61,26 @@ regex_url = re.compile('^/((?P<a>\w+)(/(?P<c>\w+)(/(?P<z>(?P<f>\w+)(\.(?P<e>[\w.
def _router_default():
"return new copy of default base router"
router = Storage(
default_application = 'init',
applications = 'ALL',
default_controller = 'default',
controllers = 'DEFAULT',
default_function = 'index',
functions = dict(),
default_language = None,
languages = None,
root_static = ['favicon.ico', 'robots.txt'],
map_static = None,
domains = None,
exclusive_domain = False,
map_hyphen = False,
acfe_match = r'\w+$', # legal app/ctlr/fcn/ext
file_match = r'([-+=@$%\w]+[./]?)+$', # legal static subpath
args_match = r'([\w@ -]+[=.]?)*$', # legal arg in args
default_application='init',
applications='ALL',
default_controller='default',
controllers='DEFAULT',
default_function='index',
functions=dict(),
default_language=None,
languages=None,
root_static=['favicon.ico', 'robots.txt'],
map_static=None,
domains=None,
exclusive_domain=False,
map_hyphen=False,
acfe_match=r'\w+$', # legal app/ctlr/fcn/ext
file_match=r'([-+=@$%\w]+[./]?)+$', # legal static subpath
args_match=r'([\w@ -]+[=.]?)*$', # legal arg in args
)
return router
def _params_default(app=None):
"return new copy of default parameters"
p = Storage()
@@ -94,7 +96,7 @@ def _params_default(app=None):
p.error_handler = None
p.error_message = '<html><body><h1>%s</h1></body></html>'
p.error_message_ticket = \
'<html><body><h1>Internal error</h1>Ticket issued: <a href="/admin/default/ticket/%(ticket)s" target="_blank">%(ticket)s</a></body><!-- this is junk text else IE does not display the page: '+('x'*512)+' //--></html>'
'<html><body><h1>Internal error</h1>Ticket issued: <a href="/admin/default/ticket/%(ticket)s" target="_blank">%(ticket)s</a></body><!-- this is junk text else IE does not display the page: ' + ('x' * 512) + ' //--></html>'
p.routers = None
p.logging = 'off'
return p
@@ -104,6 +106,7 @@ params = _params_default(app=None) # regex rewrite parameters
THREAD_LOCAL.routes = params # default to base regex rewrite parameters
routers = None
def log_rewrite(string):
"Log rewrite activity under control of routes.py"
if params.logging == 'debug': # catch common cases first
@@ -152,6 +155,7 @@ ROUTER_BASE_KEYS = set(
# filter_err: helper for doctest & unittest
# regex_filter_out: doctest
def fixup_missing_path_info(environ):
eget = environ.get
path_info = eget('PATH_INFO')
@@ -165,12 +169,12 @@ def fixup_missing_path_info(environ):
elif not request_uri:
query_string = eget('QUERY_STRING')
if query_string:
environ['REQUEST_URI'] = '%s?%s' % (path_info,query_string)
environ['REQUEST_URI'] = '%s?%s' % (path_info, query_string)
else:
environ['REQUEST_URI'] = path_info
if not eget('HTTP_HOST'):
environ['HTTP_HOST'] = \
'%s:%s' % (eget('SERVER_NAME'),eget('SERVER_PORT'))
'%s:%s' % (eget('SERVER_NAME'), eget('SERVER_PORT'))
def url_in(request, environ):
@@ -179,6 +183,7 @@ def url_in(request, environ):
return map_url_in(request, environ)
return regex_url_in(request, environ)
def url_out(request, environ, application, controller, function,
args, other, scheme, host, port):
"assemble and rewrite outgoing URL"
@@ -193,48 +198,51 @@ def url_out(request, environ, application, controller, function,
# fill in scheme and host if absolute URL is requested
# scheme can be a string, eg 'http', 'https', 'ws', 'wss'
#
if host is True or (host is None and (scheme or port!=None)):
if host is True or (host is None and (scheme or port is not None)):
host = request.env.http_host
if not scheme or scheme is True:
scheme = request.env.get('wsgi_url_scheme', 'http').lower() \
if request else 'http'
if host:
host_port = host if not port else host.split(':',1)[0]+':%s'%port
host_port = host if not port else host.split(':', 1)[0] + ':%s' % port
url = '%s://%s%s' % (scheme, host_port, url)
return url
def try_rewrite_on_error(http_response, request, environ, ticket=None):
"""
called from main.wsgibase to rewrite the http response.
"""
status = int(str(http_response.status).split()[0])
if status>=399 and THREAD_LOCAL.routes.routes_onerror:
keys=set(('%s/%s' % (request.application, status),
'%s/*' % (request.application),
'*/%s' % (status),
'*/*'))
for (key,uri) in THREAD_LOCAL.routes.routes_onerror:
if status >= 399 and THREAD_LOCAL.routes.routes_onerror:
keys = set(('%s/%s' % (request.application, status),
'%s/*' % (request.application),
'*/%s' % (status),
'*/*'))
for (key, uri) in THREAD_LOCAL.routes.routes_onerror:
if key in keys:
if uri == '!':
# do nothing!
return http_response, environ
elif '?' in uri:
path_info, query_string = uri.split('?',1)
path_info, query_string = uri.split('?', 1)
query_string += '&'
else:
path_info, query_string = uri, ''
query_string += \
'code=%s&ticket=%s&requested_uri=%s&request_url=%s' % \
(status,ticket,urllib.quote_plus(request.env.request_uri),request.url)
(status, ticket, urllib.quote_plus(
request.env.request_uri), request.url)
if uri.startswith('http://') or uri.startswith('https://'):
# make up a response
url = path_info+'?'+query_string
url = path_info + '?' + query_string
message = 'You are being redirected <a href="%s">here</a>'
return HTTP(303, message % url, Location=url), environ
else:
error_raising_path = environ['PATH_INFO']
# Rewrite routes_onerror path.
path_info = '/' + path_info.lstrip('/') # add leading '/' if missing
path_info = '/' + path_info.lstrip(
'/') # add leading '/' if missing
environ['PATH_INFO'] = path_info
error_handling_path = \
url_in(request, environ)[2]['PATH_INFO']
@@ -248,29 +256,30 @@ def try_rewrite_on_error(http_response, request, environ, ticket=None):
# do nothing!
return http_response, environ
def try_redirect_on_error(http_object, request, ticket=None):
"called from main.wsgibase to rewrite the http response"
status = int(str(http_object.status).split()[0])
if status>399 and THREAD_LOCAL.routes.routes_onerror:
keys=set(('%s/%s' % (request.application, status),
'%s/*' % (request.application),
'*/%s' % (status),
'*/*'))
for (key,redir) in THREAD_LOCAL.routes.routes_onerror:
if status > 399 and THREAD_LOCAL.routes.routes_onerror:
keys = set(('%s/%s' % (request.application, status),
'%s/*' % (request.application),
'*/%s' % (status),
'*/*'))
for (key, redir) in THREAD_LOCAL.routes.routes_onerror:
if key in keys:
if redir == '!':
break
elif '?' in redir:
url = '%s&code=%s&ticket=%s&requested_uri=%s&request_url=%s' % \
(redir,status,ticket,
(redir, status, ticket,
urllib.quote_plus(request.env.request_uri),
request.url)
else:
url = '%s?code=%s&ticket=%s&requested_uri=%s&request_url=%s' % \
(redir,status,ticket,
(redir, status, ticket,
urllib.quote_plus(request.env.request_uri),
request.url)
return HTTP(303,'You are being redirected <a href="%s">here</a>' % url,Location=url)
return HTTP(303, 'You are being redirected <a href="%s">here</a>' % url, Location=url)
return http_object
@@ -305,7 +314,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
path = abspath('applications', app, routes)
if not exists(path):
return
data = read_file(path).replace('\r\n','\n')
data = read_file(path).replace('\r\n', '\n')
symbols = dict(app=app)
try:
@@ -323,8 +332,8 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
for items in symbols[sym]:
p[sym].append(compile_regex(*items))
for sym in ('routes_onerror', 'routes_apps_raw',
'error_handler','error_message', 'error_message_ticket',
'default_application','default_controller', 'default_function',
'error_handler', 'error_message', 'error_message_ticket',
'default_application', 'default_controller', 'default_function',
'logging'):
if sym in symbols:
p[sym] = symbols[sym]
@@ -357,15 +366,15 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
apppath = abspath('applications')
for appname in os.listdir(apppath):
if not appname.startswith('.') and \
isdir(abspath(apppath,appname)) and \
isdir(abspath(apppath,appname,'controllers')):
isdir(abspath(apppath, appname)) and \
isdir(abspath(apppath, appname, 'controllers')):
all_apps.append(appname)
if routers:
router = Storage(routers.BASE) # new copy
if appname in routers:
for key in routers[appname].keys():
if key in ROUTER_BASE_KEYS:
raise SyntaxError, "BASE-only key '%s' in router '%s'" % (key, appname)
raise SyntaxError("BASE-only key '%s' in router '%s'" % (key, appname))
router.update(routers[appname])
routers[appname] = router
if exists(abspath('applications', appname, routes)):
@@ -374,7 +383,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
if routers:
load_routers(all_apps)
else: # app
else: # app
params_apps[app] = p
if routers and p.routers:
if app in p.routers:
@@ -406,8 +415,8 @@ def compile_regex(k, v, env=None):
if k.find('://') < 0:
i = k.find(':/')
if i < 0:
raise SyntaxError, "routes pattern syntax error: path needs leading '/' [%s]" % k0
k = r'%s:https?://[^:/]+:[a-z]+ %s' % (k[:i], k[i+1:])
raise SyntaxError("routes pattern syntax error: path needs leading '/' [%s]" % k0)
k = r'%s:https?://[^:/]+:[a-z]+ %s' % (k[:i], k[i + 1:])
# $anything -> ?P<anything>.*
for item in regex_anything.findall(k):
k = k.replace(item, '(?P<anything>.*)')
@@ -419,6 +428,7 @@ def compile_regex(k, v, env=None):
v = v.replace(item, r'\g<%s>' % item[1:])
return (re.compile(k, re.DOTALL), v, env or {})
def load_routers(all_apps):
"load-time post-processing of routers"
@@ -431,13 +441,15 @@ def load_routers(all_apps):
if app != 'BASE':
keys = set(routers[app]).intersection(ROUTER_BASE_KEYS)
if keys:
raise SyntaxError, "BASE-only key(s) %s in router '%s'" % (tuple(keys), app)
raise SyntaxError("BASE-only key(s) %s in router '%s'" % (
tuple(keys), app))
router.update(routers[app])
routers[app] = router
router = routers[app]
keys = set(router).difference(ROUTER_KEYS)
if keys:
raise SyntaxError, "unknown key(s) %s in router '%s'" % (tuple(keys), app)
raise SyntaxError("unknown key(s) %s in router '%s'" % (
tuple(keys), app))
if not router.controllers:
router.controllers = set()
elif not isinstance(router.controllers, str):
@@ -450,10 +462,12 @@ def load_routers(all_apps):
if isinstance(router.functions, (set, tuple, list)):
functions = set(router.functions)
if isinstance(router.default_function, str):
functions.add(router.default_function) # legacy compatibility
router.functions = { router.default_controller: functions }
functions.add(
router.default_function) # legacy compatibility
router.functions = {router.default_controller: functions}
for controller in router.functions:
router.functions[controller] = set(router.functions[controller])
router.functions[controller] = set(
router.functions[controller])
else:
router.functions = dict()
if app != 'BASE':
@@ -516,19 +530,20 @@ def load_routers(all_apps):
else:
fcn = None
if app not in all_apps and app not in routers:
raise SyntaxError, "unknown app '%s' in domains" % app
raise SyntaxError("unknown app '%s' in domains" % app)
domains[(domain, port)] = (app, ctlr, fcn)
routers.BASE.domains = domains
def regex_uri(e, regexes, tag, default=None):
"filter incoming URI against a list of regexes"
path = e['PATH_INFO']
host = e.get('http_host', e.get('SERVER_NAME','localhost')).lower()
host = e.get('http_host', e.get('SERVER_NAME', 'localhost')).lower()
i = host.find(':')
if i > 0:
host = host[:i]
key = '%s:%s://%s:%s %s' % \
(e.get('REMOTE_ADDR','localhost'),
(e.get('REMOTE_ADDR', 'localhost'),
e.get('wsgi.url_scheme', 'http').lower(), host,
e.get('REQUEST_METHOD', 'get').lower(), path)
for (regex, value, custom_env) in regexes:
@@ -540,6 +555,7 @@ def regex_uri(e, regexes, tag, default=None):
log_rewrite('%s: [%s] -> %s (not rewritten)' % (tag, key, default))
return default
def regex_select(env=None, app=None, request=None):
"""
select a set of regex rewrite params for the current request
@@ -553,10 +569,11 @@ def regex_select(env=None, app=None, request=None):
app = regex_uri(env, params.routes_app, "routes_app")
THREAD_LOCAL.routes = params_apps.get(app, params)
else:
THREAD_LOCAL.routes = params # default to base rewrite parameters
THREAD_LOCAL.routes = params # default to base rewrite parameters
log_rewrite("select routing parameters: %s" % THREAD_LOCAL.routes.name)
return app # for doctest
def regex_filter_in(e):
"regex rewrite incoming URL"
routes = THREAD_LOCAL.routes
@@ -567,7 +584,7 @@ def regex_filter_in(e):
"routes_in", e['PATH_INFO'])
rmatch = regex_redirect.match(path)
if rmatch:
raise HTTP(int(rmatch.group(1)),location=rmatch.group(2))
raise HTTP(int(rmatch.group(1)), location=rmatch.group(2))
items = path.split('?', 1)
e['PATH_INFO'] = items[0]
if len(items) > 1:
@@ -581,7 +598,8 @@ def regex_filter_in(e):
def sluggify(key):
return key.lower().replace('.','_')
return key.lower().replace('.', '_')
def regex_url_in(request, environ):
"rewrite and parse incoming URL"
@@ -596,26 +614,28 @@ def regex_url_in(request, environ):
routes = THREAD_LOCAL.routes
if routes.routes_in:
environ = regex_filter_in(environ)
request.env.update((k.lower().replace('.','_'),v) for k,v in environ.iteritems())
request.env.update(
(k.lower().replace('.', '_'), v) for k, v in environ.iteritems())
# ##################################################
# serve if a static file
# ##################################################
path = request.env.path_info.replace('\\', '/') or '/'
path = regex_space.sub('_',path)
if path.endswith('/') and len(path)>1: path = path[:-1]
path = regex_space.sub('_', path)
if path.endswith('/') and len(path) > 1:
path = path[:-1]
match = regex_url.match(path)
if not match:
raise HTTP(400,
routes.error_message % 'invalid request',
web2py_error='invalid path')
elif match.group('c')=='static':
elif match.group('c') == 'static':
application = match.group('a')
version, filename = None, match.group('z')
items = filename.split('/',1)
items = filename.split('/', 1)
if regex_version.match(items[0]):
version,filename = items
version, filename = items
static_file = pjoin(request.env.applications_parent,
'applications', application,
'static', filename)
@@ -655,9 +675,9 @@ def regex_filter_out(url, e=None):
if i > 0:
host = host[:i]
items[0] = '%s:%s://%s:%s %s' % \
(e.get('remote_addr', ''),
e.get('wsgi_url_scheme', 'http').lower(), host,
e.get('request_method', 'get').lower(), items[0])
(e.get('remote_addr', ''),
e.get('wsgi_url_scheme', 'http').lower(), host,
e.get('request_method', 'get').lower(), items[0])
else:
items[0] = ':http://localhost:get %s' % items[0]
for (regex, value, tmp) in routes.routes_out:
@@ -671,7 +691,7 @@ def regex_filter_out(url, e=None):
def filter_url(url, method='get', remote='0.0.0.0',
out=False, app=False, lang=None,
domain=(None,None), env=False, scheme=None,
domain=(None, None), env=False, scheme=None,
host=None, port=None):
"""
doctest/unittest interface to regex_filter_in() and regex_filter_out()
@@ -685,21 +705,21 @@ def filter_url(url, method='get', remote='0.0.0.0',
k = len(uri)
if isinstance(domain, str):
domain = (domain, None)
(path_info, query_string) = (uri[:k], uri[k+1:])
(path_info, query_string) = (uri[:k], uri[k + 1:])
path_info = urllib.unquote(path_info) # simulate server
e = {
'REMOTE_ADDR': remote,
'REQUEST_METHOD': method,
'wsgi.url_scheme': urlscheme,
'HTTP_HOST': urlhost,
'REQUEST_URI': uri,
'PATH_INFO': path_info,
'QUERY_STRING': query_string,
#for filter_out request.env use lowercase
'remote_addr': remote,
'request_method': method,
'wsgi_url_scheme': urlscheme,
'http_host': urlhost
'REMOTE_ADDR': remote,
'REQUEST_METHOD': method,
'wsgi.url_scheme': urlscheme,
'HTTP_HOST': urlhost,
'REQUEST_URI': uri,
'PATH_INFO': path_info,
'QUERY_STRING': query_string,
#for filter_out request.env use lowercase
'remote_addr': remote,
'request_method': method,
'wsgi_url_scheme': urlscheme,
'http_host': urlhost
}
request = Storage()
@@ -717,17 +737,19 @@ def filter_url(url, method='get', remote='0.0.0.0',
# rewrite outbound URL
#
if out:
(request.env.domain_application, request.env.domain_controller) = domain
(request.env.domain_application,
request.env.domain_controller) = domain
items = path_info.lstrip('/').split('/')
if items[-1] == '':
items.pop() # adjust trailing empty args
items.pop() # adjust trailing empty args
assert len(items) >= 3, "at least /a/c/f is required"
a = items.pop(0)
c = items.pop(0)
f = items.pop(0)
if not routers:
return regex_filter_out(uri, e)
acf = map_url_out(request, None, a, c, f, items, None, scheme, host, port)
acf = map_url_out(
request, None, a, c, f, items, None, scheme, host, port)
if items:
url = '%s/%s' % (acf, '/'.join(items))
if items[-1] == '':
@@ -743,7 +765,8 @@ def filter_url(url, method='get', remote='0.0.0.0',
(static, version, e) = url_in(request, e)
if static:
return static
result = "/%s/%s/%s" % (request.application, request.controller, request.function)
result = "/%s/%s/%s" % (
request.application, request.controller, request.function)
if request.extension and request.extension != 'html':
result += ".%s" % request.extension
if request.args:
@@ -762,22 +785,24 @@ def filter_err(status, application='app', ticket='tkt'):
routes = THREAD_LOCAL.routes
if status > 399 and routes.routes_onerror:
keys = set(('%s/%s' % (application, status),
'%s/*' % (application),
'*/%s' % (status),
'*/*'))
for (key,redir) in routes.routes_onerror:
'%s/*' % (application),
'*/%s' % (status),
'*/*'))
for (key, redir) in routes.routes_onerror:
if key in keys:
if redir == '!':
break
elif '?' in redir:
url = redir + '&' + 'code=%s&ticket=%s' % (status,ticket)
url = redir + '&' + 'code=%s&ticket=%s' % (status, ticket)
else:
url = redir + '?' + 'code=%s&ticket=%s' % (status,ticket)
return url # redirection
return status # no action
url = redir + '?' + 'code=%s&ticket=%s' % (status, ticket)
return url # redirection
return status # no action
# router support
#
class MapUrlIn(object):
"logic for mapping incoming URLs"
@@ -804,7 +829,8 @@ class MapUrlIn(object):
self.query = self.env.get('QUERY_STRING', None)
path = path.lstrip('/')
self.env['PATH_INFO'] = '/' + path
self.env['WEB2PY_ORIGINAL_URI'] = self.env['PATH_INFO'] + (self.query and ('?' + self.query) or '')
self.env['WEB2PY_ORIGINAL_URI'] = self.env['PATH_INFO'] + (
self.query and ('?' + self.query) or '')
# to handle empty args, strip exactly one trailing slash, if present
# .../arg1// represents one trailing empty arg
@@ -814,12 +840,13 @@ class MapUrlIn(object):
self.args = List(path and path.split('/') or [])
# see http://www.python.org/dev/peps/pep-3333/#url-reconstruction for URL composition
self.remote_addr = self.env.get('REMOTE_ADDR','localhost')
self.remote_addr = self.env.get('REMOTE_ADDR', 'localhost')
self.scheme = self.env.get('wsgi.url_scheme', 'http').lower()
self.method = self.env.get('REQUEST_METHOD', 'get').lower()
(self.host, self.port) = (self.env.get('HTTP_HOST'), None)
if not self.host:
(self.host, self.port) = (self.env.get('SERVER_NAME'), self.env.get('SERVER_PORT'))
(self.host, self.port) = (
self.env.get('SERVER_NAME'), self.env.get('SERVER_PORT'))
if not self.host:
(self.host, self.port) = ('localhost', '80')
if ':' in self.host:
@@ -837,7 +864,7 @@ class MapUrlIn(object):
for i in xrange(prefixlen):
if prefix[i] != self.args[i]:
return # prefix didn't match
self.args = List(self.args[prefixlen:]) # strip the prefix
self.args = List(self.args[prefixlen:]) # strip the prefix
def map_app(self):
"determine application name"
@@ -851,12 +878,14 @@ class MapUrlIn(object):
elif not base.exclusive_domain and arg0 and not base.applications:
self.application = arg0
elif (self.host, self.port) in base.domains:
(self.application, self.domain_controller, self.domain_function) = base.domains[(self.host, self.port)]
(self.application, self.domain_controller,
self.domain_function) = base.domains[(self.host, self.port)]
self.env['domain_application'] = self.application
self.env['domain_controller'] = self.domain_controller
self.env['domain_function'] = self.domain_function
elif (self.host, None) in base.domains:
(self.application, self.domain_controller, self.domain_function) = base.domains[(self.host, None)]
(self.application, self.domain_controller,
self.domain_function) = base.domains[(self.host, None)]
self.env['domain_application'] = self.application
self.env['domain_controller'] = self.domain_controller
self.env['domain_function'] = self.domain_function
@@ -869,12 +898,14 @@ class MapUrlIn(object):
self.pop_arg_if(self.application == arg0)
if not base._acfe_match.match(self.application):
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
web2py_error="invalid application: '%s'" % self.application)
raise HTTP(
400, THREAD_LOCAL.routes.error_message % 'invalid request',
web2py_error="invalid application: '%s'" % self.application)
if self.application not in routers and \
(self.application != THREAD_LOCAL.routes.default_application or self.application == 'welcome'):
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
(self.application != THREAD_LOCAL.routes.default_application or self.application == 'welcome'):
raise HTTP(
400, THREAD_LOCAL.routes.error_message % 'invalid request',
web2py_error="unknown application: '%s'" % self.application)
# set the application router
@@ -908,8 +939,8 @@ class MapUrlIn(object):
if len(self.args) == 1 and self.arg0 in self.router.root_static:
self.controller = self.request.controller = 'static'
root_static_file = pjoin(self.request.env.applications_parent,
'applications', self.application,
self.controller, self.arg0)
'applications', self.application,
self.controller, self.arg0)
log_rewrite("route: root static=%s" % root_static_file)
return root_static_file, None
return None, None
@@ -938,8 +969,9 @@ class MapUrlIn(object):
self.pop_arg_if(arg0 == self.controller)
log_rewrite("route: controller=%s" % self.controller)
if not self.router._acfe_match.match(self.controller):
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
web2py_error='invalid controller')
raise HTTP(
400, THREAD_LOCAL.routes.error_message % 'invalid request',
web2py_error='invalid controller')
def map_static(self):
'''
@@ -962,7 +994,8 @@ class MapUrlIn(object):
# match path elements
bad_static = False
for name in self.args:
bad_static = bad_static or name in ('', '.', '..') or not self.router._file_match.match(name)
bad_static = bad_static or name in (
'', '.', '..') or not self.router._file_match.match(name)
if bad_static:
log_rewrite('bad static path=%s' % file)
raise HTTP(400,
@@ -990,9 +1023,10 @@ class MapUrlIn(object):
arg0 = self.harg0 # map hyphens
functions = self.functions.get(self.controller, set())
if isinstance(self.router.default_function, dict):
default_function = self.router.default_function.get(self.controller, None)
default_function = self.router.default_function.get(
self.controller, None)
else:
default_function = self.router.default_function # str or None
default_function = self.router.default_function # str or None
default_function = self.domain_function or default_function
if not arg0 or functions and arg0 not in functions:
self.function = default_function or ""
@@ -1005,14 +1039,17 @@ class MapUrlIn(object):
else:
self.function = arg0
self.pop_arg_if(True)
log_rewrite("route: function.ext=%s.%s" % (self.function, self.extension))
log_rewrite(
"route: function.ext=%s.%s" % (self.function, self.extension))
if not self.router._acfe_match.match(self.function):
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
web2py_error='invalid function')
raise HTTP(
400, THREAD_LOCAL.routes.error_message % 'invalid request',
web2py_error='invalid function')
if self.extension and not self.router._acfe_match.match(self.extension):
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
web2py_error='invalid extension')
raise HTTP(
400, THREAD_LOCAL.routes.error_message % 'invalid request',
web2py_error='invalid extension')
def validate_args(self):
'''
@@ -1020,13 +1057,14 @@ class MapUrlIn(object):
'''
for arg in self.args:
if not self.router._args_match.match(arg):
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
web2py_error='invalid arg <%s>' % arg)
raise HTTP(
400, THREAD_LOCAL.routes.error_message % 'invalid request',
web2py_error='invalid arg <%s>' % arg)
def sluggify(self):
""
self.request.env.update(
(k.lower().replace('.','_'),v) for k,v in self.env.iteritems())
(k.lower().replace('.', '_'), v) for k, v in self.env.iteritems())
def update_request(self):
'''
@@ -1053,7 +1091,8 @@ class MapUrlIn(object):
uri = '/%s%s%s%s' % (
app,
uri,
urllib.quote('/'+'/'.join(str(x) for x in self.args)) if self.args else '',
urllib.quote('/' + '/'.join(
str(x) for x in self.args)) if self.args else '',
('?' + self.query) if self.query else '')
self.env['REQUEST_URI'] = uri
self.sluggify()
@@ -1075,6 +1114,7 @@ class MapUrlIn(object):
if dopop:
self.args.pop(0)
class MapUrlOut(object):
"logic for mapping outgoing URLs"
@@ -1090,7 +1130,8 @@ class MapUrlOut(object):
self.env = env
self.application = application
self.controller = controller
self.is_static = (controller == 'static' or controller.startswith('static/'))
self.is_static = (
controller == 'static' or controller.startswith('static/'))
self.function = function
self.args = args
self.other = other
@@ -1111,12 +1152,13 @@ class MapUrlOut(object):
self.domain_application = request and self.request.env.domain_application
self.domain_controller = request and self.request.env.domain_controller
if isinstance(self.router.default_function, dict):
self.default_function = self.router.default_function.get(self.controller, None)
self.default_function = self.router.default_function.get(
self.controller, None)
else:
self.default_function = self.router.default_function
if (self.router.exclusive_domain and self.domain_application and self.domain_application != self.application and not self.host):
raise SyntaxError, 'cross-domain conflict: must specify host'
raise SyntaxError('cross-domain conflict: must specify host')
lang = request and request.uri_language
if lang and self.languages and lang in self.languages:
@@ -1237,7 +1279,7 @@ class MapUrlOut(object):
return None # use regex filter
self.omit_lang() # try to omit language
self.omit_acf() # try to omit a/c/f
return self.build_acf() # build and return the /a/lang/c/f string
return self.build_acf() # build and return the /a/lang/c/f string
def map_url_in(request, env, app=False):
@@ -1259,7 +1301,8 @@ def map_url_in(request, env, app=False):
if app:
return map.application
root_static_file, version = map.map_root_static() # handle root-static files
root_static_file, version = map.map_root_static(
) # handle root-static files
if root_static_file:
map.update_request()
return (root_static_file, version, map.env)
@@ -1280,6 +1323,7 @@ def map_url_in(request, env, app=False):
map.update_request()
return (None, None, map.env)
def map_url_out(request, env, application, controller,
function, args, other, scheme, host, port):
'''
@@ -1311,6 +1355,7 @@ def map_url_out(request, env, application, controller,
function, args, other, scheme, host, port)
return map.acf()
def get_effective_router(appname):
"return a private copy of the effective router for the specified application"
if not routers or appname not in routers:
+121 -92
View File
@@ -16,21 +16,23 @@ import traceback
VERSION = '1.2.5'
SERVER_NAME = socket.gethostname()
SERVER_SOFTWARE = 'Rocket %s' % VERSION
HTTP_SERVER_SOFTWARE = '%s Python/%s' % (SERVER_SOFTWARE, sys.version.split(' ')[0])
HTTP_SERVER_SOFTWARE = '%s Python/%s' % (
SERVER_SOFTWARE, sys.version.split(' ')[0])
BUF_SIZE = 16384
SOCKET_TIMEOUT = 10 # in secs
THREAD_STOP_CHECK_INTERVAL = 1 # in secs, How often should threads check for a server stop message?
IS_JYTHON = platform.system() == 'Java' # Handle special cases for Jython
SOCKET_TIMEOUT = 10 # in secs
THREAD_STOP_CHECK_INTERVAL = 1 # in secs, How often should threads check for a server stop message?
IS_JYTHON = platform.system() == 'Java' # Handle special cases for Jython
IGNORE_ERRORS_ON_CLOSE = set([errno.ECONNABORTED, errno.ECONNRESET])
DEFAULT_LISTEN_QUEUE_SIZE = 5
DEFAULT_MIN_THREADS = 10
DEFAULT_MAX_THREADS = 0
DEFAULTS = dict(LISTEN_QUEUE_SIZE = DEFAULT_LISTEN_QUEUE_SIZE,
MIN_THREADS = DEFAULT_MIN_THREADS,
MAX_THREADS = DEFAULT_MAX_THREADS)
DEFAULTS = dict(LISTEN_QUEUE_SIZE=DEFAULT_LISTEN_QUEUE_SIZE,
MIN_THREADS=DEFAULT_MIN_THREADS,
MAX_THREADS=DEFAULT_MAX_THREADS)
PY3K = sys.version_info[0] > 2
class NullHandler(logging.Handler):
"A Logging handler to prevent library errors."
def emit(self, record):
@@ -94,6 +96,7 @@ except ImportError:
# TODO - This part is still very experimental.
#from .filelike import FileLikeSocket
class Connection(object):
__slots__ = [
'setblocking',
@@ -170,6 +173,7 @@ except ImportError:
# Import Package Modules
# package imports removed in monolithic build
class FileLikeSocket(object):
def __init__(self, conn, buf_size=BUF_SIZE):
self.conn = conn
@@ -317,13 +321,12 @@ class WSGIFuture(Future):
else:
return super(WSGIFuture, self).set_running_or_notify_cancel()
def remember(self, name, lifespan=None):
self._lifespan = lifespan or self._lifespan
if name in self._mem_dict:
raise NameError('Cannot remember future by name "%s". ' % name + \
'A future already exists with that name.' )
raise NameError('Cannot remember future by name "%s". ' % name +
'A future already exists with that name.')
self._name = name
self._mem_dict[name] = self
@@ -334,6 +337,7 @@ class WSGIFuture(Future):
del self._mem_dict[self._name]
self._name = None
class _WorkItem(object):
def __init__(self, future, fn, args, kwargs):
self.future = future
@@ -353,6 +357,7 @@ class _WorkItem(object):
else:
self.future.set_result(result)
class WSGIExecutor(ThreadPoolExecutor):
multithread = True
multiprocess = False
@@ -366,7 +371,8 @@ class WSGIExecutor(ThreadPoolExecutor):
if self._shutdown_lock.acquire():
if self._shutdown:
self._shutdown_lock.release()
raise RuntimeError('Cannot schedule new futures after shutdown')
raise RuntimeError(
'Cannot schedule new futures after shutdown')
f = WSGIFuture(self.futures)
w = _WorkItem(f, fn, args, kwargs)
@@ -378,6 +384,7 @@ class WSGIExecutor(ThreadPoolExecutor):
else:
return False
class FuturesMiddleware(object):
"Futures middleware that adds a Futures Executor to the environment"
def __init__(self, app, threads=5):
@@ -405,11 +412,13 @@ try:
has_ssl = True
except ImportError:
has_ssl = False
class SSLError(socket.error):
pass
# Import Package Modules
# package imports removed in monolithic build
class Listener(Thread):
"""The Listener class is a class responsible for accepting connections
and queuing them to be processed by a worker thread."""
@@ -446,18 +455,18 @@ class Listener(Thread):
elif not os.path.exists(interface[2]):
data = (interface[2], interface[0], interface[1])
self.err_log.error("Cannot find key file "
"'%s'. Cannot bind to %s:%s" % data)
"'%s'. Cannot bind to %s:%s" % data)
return
elif not os.path.exists(interface[3]):
data = (interface[3], interface[0], interface[1])
self.err_log.error("Cannot find certificate file "
"'%s'. Cannot bind to %s:%s" % data)
"'%s'. Cannot bind to %s:%s" % data)
return
if self.clientcert_req and not os.path.exists(interface[4]):
data = (interface[4], interface[0], interface[1])
self.err_log.error("Cannot find root ca certificate file "
"'%s'. Cannot bind to %s:%s" % data)
"'%s'. Cannot bind to %s:%s" % data)
return
# Set socket options
@@ -499,18 +508,18 @@ class Listener(Thread):
ca_certs = self.interface[4]
cert_reqs = ssl.CERT_OPTIONAL
sock = ssl.wrap_socket(sock,
keyfile = self.interface[2],
certfile = self.interface[3],
server_side = True,
cert_reqs = cert_reqs,
ca_certs = ca_certs,
ssl_version = ssl.PROTOCOL_SSLv23)
keyfile=self.interface[2],
certfile=self.interface[3],
server_side=True,
cert_reqs=cert_reqs,
ca_certs=ca_certs,
ssl_version=ssl.PROTOCOL_SSLv23)
else:
sock = ssl.wrap_socket(sock,
keyfile = self.interface[2],
certfile = self.interface[3],
server_side = True,
ssl_version = ssl.PROTOCOL_SSLv23)
keyfile=self.interface[2],
certfile=self.interface[3],
server_side=True,
ssl_version=ssl.PROTOCOL_SSLv23)
except SSLError:
# Generally this happens when an HTTP request is received on a
@@ -598,19 +607,17 @@ except ImportError:
# package imports removed in monolithic build
# Setup Logging
log = logging.getLogger('Rocket')
log.addHandler(NullHandler())
class Rocket(object):
"""The Rocket class is responsible for handling threads and accepting and
dispatching connections."""
def __init__(self,
interfaces = ('127.0.0.1', 8000),
interfaces=('127.0.0.1', 8000),
method = 'wsgi',
app_info = None,
min_threads = None,
@@ -650,15 +657,16 @@ class Rocket(object):
self.active_queue = Queue()
self._threadpool = ThreadPool(get_method(method),
app_info = app_info,
active_queue = self.active_queue,
monitor_queue = self.monitor_queue,
min_threads = min_threads,
max_threads = max_threads)
app_info=app_info,
active_queue=self.active_queue,
monitor_queue=self.monitor_queue,
min_threads=min_threads,
max_threads=max_threads)
# Build our socket listeners
self.listeners = [Listener(i, queue_size, self.active_queue) for i in self.interfaces]
for ndx in range(len(self.listeners)-1, 0, -1):
self.listeners = [Listener(
i, queue_size, self.active_queue) for i in self.interfaces]
for ndx in range(len(self.listeners) - 1, 0, -1):
if not self.listeners[ndx].ready:
del self.listeners[ndx]
@@ -705,7 +713,8 @@ class Rocket(object):
str_extract = lambda l: (l.addr, l.port, l.secure and '*' or '')
msg = 'Listening on sockets: '
msg += ', '.join(['%s:%i%s' % str_extract(l) for l in self.listeners])
msg += ', '.join(
['%s:%i%s' % str_extract(l) for l in self.listeners])
log.info(msg)
for l in self.listeners:
@@ -730,7 +739,7 @@ class Rocket(object):
return self.stop()
def stop(self, stoplogging = False):
def stop(self, stoplogging=False):
log.info('Stopping %s' % SERVER_SOFTWARE)
self.startstop_lock.acquire()
@@ -773,23 +782,24 @@ class Rocket(object):
self.stop()
self.start()
def CherryPyWSGIServer(bind_addr,
wsgi_app,
numthreads = 10,
server_name = None,
max = -1,
request_queue_size = 5,
timeout = 10,
shutdown_timeout = 5):
numthreads=10,
server_name=None,
max=-1,
request_queue_size=5,
timeout=10,
shutdown_timeout=5):
""" A Cherrypy wsgiserver-compatible wrapper. """
max_threads = max
if max_threads < 0:
max_threads = 0
return Rocket(bind_addr, 'wsgi', {'wsgi_app': wsgi_app},
min_threads = numthreads,
max_threads = max_threads,
queue_size = request_queue_size,
timeout = timeout)
min_threads=numthreads,
max_threads=max_threads,
queue_size=request_queue_size,
timeout=timeout)
# Monolithic build...end of module: rocket\main.py
# Monolithic build...start of module: rocket\monitor.py
@@ -803,6 +813,7 @@ from threading import Thread
# Import Package Modules
# package imports removed in monolithic build
class Monitor(Thread):
# Monitor worker class.
@@ -926,8 +937,10 @@ class Monitor(Thread):
for c in stale:
if __debug__:
# "EXPR and A or B" kept for Py2.4 compatibility
data = (c.client_addr, c.server_port, c.ssl and '*' or '')
self.log.debug('Flushing stale connection: %s:%i%s' % data)
data = (
c.client_addr, c.server_port, c.ssl and '*' or '')
self.log.debug(
'Flushing stale connection: %s:%i%s' % data)
self.connections.remove(c)
list_changed = True
@@ -940,7 +953,6 @@ class Monitor(Thread):
# Dynamically resize the threadpool to adapt to our changing needs.
self._threadpool.dynamic_resize()
def stop(self):
self.active = False
@@ -984,6 +996,7 @@ import logging
log = logging.getLogger('Rocket.Errors.ThreadPool')
log.addHandler(NullHandler())
class ThreadPool:
"""The ThreadPool class is a container class for all the worker threads. It
manages the number of actively running threads."""
@@ -1011,7 +1024,7 @@ class ThreadPool:
self.alive = False
# TODO - Optimize this based on some real-world usage data
self.grow_threshold = int(max_threads/10) + 2
self.grow_threshold = int(max_threads / 10) + 2
if not isinstance(app_info, dict):
app_info = dict()
@@ -1193,7 +1206,9 @@ Content-Type: %s
%s
'''
if IS_JYTHON:
HTTP_METHODS = set(['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT'])
HTTP_METHODS = set(['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT',
'DELETE', 'TRACE', 'CONNECT'])
class Worker(Thread):
"""The Worker class is a base class responsible for receiving connections
@@ -1223,7 +1238,7 @@ class Worker(Thread):
self.req_log.addHandler(NullHandler())
# Error Log
self.err_log = logging.getLogger('Rocket.Errors.'+self.getName())
self.err_log = logging.getLogger('Rocket.Errors.' + self.getName())
self.err_log.addHandler(NullHandler())
def _handleError(self, typ, val, tb):
@@ -1301,11 +1316,11 @@ class Worker(Thread):
self.err_log.debug('Serving a request')
try:
self.run_app(conn)
log_info = dict(client_ip = conn.client_addr,
time = datetime.now().strftime('%c'),
status = self.status.split(' ')[0],
size = self.size,
request_line = self.request_line)
log_info = dict(client_ip=conn.client_addr,
time=datetime.now().strftime('%c'),
status=self.status.split(' ')[0],
size=self.size,
request_line=self.request_line)
self.req_log.info(LOG_LINE % log_info)
except:
exc = sys.exc_info()
@@ -1314,11 +1329,12 @@ class Worker(Thread):
break
else:
if self.request_line:
log_info = dict(client_ip = conn.client_addr,
time = datetime.now().strftime('%c'),
status = self.status.split(' ')[0],
size = self.size,
request_line = self.request_line + ' - not stopping')
log_info = dict(client_ip=conn.client_addr,
time=datetime.now(
).strftime('%c'),
status=self.status.split(' ')[0],
size=self.size,
request_line=self.request_line + ' - not stopping')
self.req_log.info(LOG_LINE % log_info)
if self.closeConnection:
@@ -1382,13 +1398,15 @@ class Worker(Thread):
except socket.timeout:
raise SocketTimeout("Socket timed out before request.")
except TypeError:
raise SocketClosed("ssl bug caused closer of socket, upgrade to python 2.7")
raise SocketClosed(
"ssl bug caused closer of socket, upgrade to python 2.7")
d = d.strip()
if not d:
if __debug__:
self.err_log.debug('Client did not send a recognizable request.')
self.err_log.debug(
'Client did not send a recognizable request.')
raise SocketClosed('Client closed socket.')
self.request_line = d
@@ -1408,11 +1426,12 @@ class Worker(Thread):
raise BadRequest
req = match.groupdict()
for k,v in req.iteritems():
for k, v in req.iteritems():
if not v:
req[k] = ""
if k == 'path':
req['path'] = r'%2F'.join([unquote(x) for x in re_SLASH.split(v)])
req['path'] = r'%2F'.join(
[unquote(x) for x in re_SLASH.split(v)])
return req
@@ -1421,15 +1440,15 @@ class Worker(Thread):
try:
method, uri, proto = d.split(' ')
if not proto.startswith('HTTP') or \
proto[-3:] not in ('1.0', '1.1') or \
method not in HTTP_METHODS:
proto[-3:] not in ('1.0', '1.1') or \
method not in HTTP_METHODS:
self.send_response('400 Bad Request')
raise BadRequest
except ValueError:
self.send_response('400 Bad Request')
raise BadRequest
req = dict(method=method, protocol = proto)
req = dict(method=method, protocol=proto)
scheme = ''
host = ''
if uri == '*' or uri.startswith('/'):
@@ -1454,7 +1473,6 @@ class Worker(Thread):
host=host)
return req
def read_headers(self, sock_file, environ):
try:
lname = None
@@ -1464,9 +1482,10 @@ class Worker(Thread):
try:
l = str(l, 'ISO-8859-1')
except UnicodeDecodeError:
self.err_log.warning('Invalid request header: '+repr(l))
self.err_log.warning(
'Invalid request header: ' + repr(l))
if l.strip().replace('\0','') == '':
if l.strip().replace('\0', '') == '':
break
elif l[0] in ' \t' and lname:
# Some headers take more than one line
@@ -1476,25 +1495,30 @@ class Worker(Thread):
l = l.split(':', 1)
# HTTP header names are us-ascii encoded
lname = str('HTTP_'+l[0].strip().upper().replace('-', '_'))
lname = str(
'HTTP_' + l[0].strip().upper().replace('-', '_'))
lval = str(l[-1].strip())
environ[lname] = lval
except socket.timeout:
raise SocketTimeout("Socket timed out before request.")
class SocketTimeout(Exception):
"Exception for when a socket times out between requests."
pass
class BadRequest(Exception):
"Exception for when a client sends an incomprehensible request."
pass
class SocketClosed(Exception):
"Exception for when a socket is closed by the client."
pass
class ChunkedReader(object):
def __init__(self, sock_file):
self.stream = sock_file
@@ -1542,6 +1566,7 @@ class ChunkedReader(object):
def readlines(self):
yield self.readline()
def get_method(method):
methods = dict(wsgi=WSGIWorker)
return methods[method.lower()]
@@ -1559,7 +1584,6 @@ from wsgiref.util import FileWrapper
# package imports removed in monolithic build
if PY3K:
from email.utils import formatdate
else:
@@ -1578,6 +1602,7 @@ BASE_ENV = {'SERVER_NAME': SERVER_NAME,
'wsgi.file_wrapper': FileWrapper
}
class WSGIWorker(Worker):
def __init__(self, *args, **kwargs):
"""Builds some instance variables that will last the life of the
@@ -1588,9 +1613,10 @@ class WSGIWorker(Worker):
multithreaded = self.app_info.get('max_threads') != 1
else:
multithreaded = False
self.base_environ = dict({'SERVER_SOFTWARE': self.app_info['server_software'],
'wsgi.multithread': multithreaded,
})
self.base_environ = dict(
{'SERVER_SOFTWARE': self.app_info['server_software'],
'wsgi.multithread': multithreaded,
})
self.base_environ.update(BASE_ENV)
# Grab our application
@@ -1614,7 +1640,7 @@ class WSGIWorker(Worker):
environ = self.base_environ.copy()
# Grab the headers
self.read_headers(sock_file,environ)
self.read_headers(sock_file, environ)
# Add CGI Variables
environ['SERVER_PORT'] = str(conn.server_port)
@@ -1644,7 +1670,7 @@ class WSGIWorker(Worker):
peercert = conn.socket.getpeercert(binary_form=True)
environ['SSL_CLIENT_RAW_CERT'] = \
peercert and ssl.DER_cert_to_PEM_cert(peercert)
except Exception,e:
except Exception, e:
print e
if environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked':
@@ -1822,7 +1848,7 @@ class WSGIWorker(Worker):
if __debug__:
self.err_log.debug('Finally closing output and sock_file')
if hasattr(output,'close'):
if hasattr(output, 'close'):
output.close()
sock_file.close()
@@ -1833,19 +1859,21 @@ class WSGIWorker(Worker):
# the following code is not part of Rocket but was added in web2py for testing purposes
#
def demo_app(environ, start_response):
global static_folder
import os
types = {'htm': 'text/html','html': 'text/html','gif': 'image/gif',
'jpg': 'image/jpeg','png': 'image/png','pdf': 'applications/pdf'}
types = {'htm': 'text/html', 'html': 'text/html', 'gif': 'image/gif',
'jpg': 'image/jpeg', 'png': 'image/png', 'pdf': 'applications/pdf'}
if static_folder:
if not static_folder.startswith('/'):
static_folder = os.path.join(os.getcwd(),static_folder)
path = os.path.join(static_folder, environ['PATH_INFO'][1:] or 'index.html')
type = types.get(path.split('.')[-1],'text')
static_folder = os.path.join(os.getcwd(), static_folder)
path = os.path.join(
static_folder, environ['PATH_INFO'][1:] or 'index.html')
type = types.get(path.split('.')[-1], 'text')
if os.path.exists(path):
try:
data = open(path,'rb').read()
data = open(path, 'rb').read()
start_response('200 OK', [('Content-Type', type)])
except IOError:
start_response('404 NOT FOUND', [])
@@ -1858,21 +1886,22 @@ def demo_app(environ, start_response):
data = '<html><body><h1>Hello from Rocket Web Server</h1></body></html>'
return [data]
def demo():
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-i", "--ip", dest="ip",default="127.0.0.1",
parser.add_option("-i", "--ip", dest="ip", default="127.0.0.1",
help="ip address of the network interface")
parser.add_option("-p", "--port", dest="port",default="8000",
parser.add_option("-p", "--port", dest="port", default="8000",
help="post where to run web server")
parser.add_option("-s", "--static", dest="static",default=None,
parser.add_option("-s", "--static", dest="static", default=None,
help="folder containing static files")
(options, args) = parser.parse_args()
global static_folder
static_folder = options.static
print 'Rocket running on %s:%s' % (options.ip, options.port)
r=Rocket((options.ip,int(options.port)),'wsgi', {'wsgi_app':demo_app})
r = Rocket((options.ip, int(options.port)), 'wsgi', {'wsgi_app': demo_app})
r.start()
if __name__=='__main__':
if __name__ == '__main__':
demo()
+19 -17
View File
@@ -47,12 +47,12 @@ class XssCleaner(HTMLParser):
'code',
'pre',
'img/',
],
],
allowed_attributes={'a': ['href', 'title'], 'img': ['src', 'alt'
], 'blockquote': ['type']},
], 'blockquote': ['type']},
fmt=AbstractFormatter,
strip_disallowed = False
):
strip_disallowed=False
):
HTMLParser.__init__(self, fmt)
self.result = ''
@@ -103,7 +103,7 @@ class XssCleaner(HTMLParser):
tag,
method,
attrs,
):
):
if tag not in self.permitted_tags:
if self.strip_disallowed:
self.in_disallowed = True
@@ -114,16 +114,16 @@ class XssCleaner(HTMLParser):
if tag in self.allowed_attributes:
attrs = dict(attrs)
self.allowed_attributes_here = [x for x in
self.allowed_attributes[tag] if x in attrs
and len(attrs[x]) > 0]
self.allowed_attributes[tag] if x in attrs
and len(attrs[x]) > 0]
for attribute in self.allowed_attributes_here:
if attribute in ['href', 'src', 'background']:
if self.url_is_acceptable(attrs[attribute]):
bt += ' %s="%s"' % (attribute,
attrs[attribute])
attrs[attribute])
else:
bt += ' %s=%s' % (xssescape(attribute),
quoteattr(attrs[attribute]))
quoteattr(attrs[attribute]))
if bt == '<a' or bt == '<img':
return
if tag in self.requires_no_close:
@@ -168,7 +168,8 @@ class XssCleaner(HTMLParser):
content, otherwise remove it
"""
if not isinstance(rawstring, str): return str(rawstring)
if not isinstance(rawstring, str):
return str(rawstring)
for tag in self.requires_no_close:
rawstring = rawstring.replace("<%s/>" % tag, "<%s />" % tag)
if not escape:
@@ -209,16 +210,17 @@ def sanitize(text, permitted_tags=[
'code',
'pre',
'img/',
'h1','h2','h3','h4','h5','h6',
'table','tr','td','div',
],
allowed_attributes = {
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'table', 'tr', 'td', 'div',
],
allowed_attributes={
'a': ['href', 'title'],
'img': ['src', 'alt'],
'blockquote': ['type'],
'td': ['colspan'],
},
escape=True):
if not isinstance(text, str): return str(text)
},
escape=True):
if not isinstance(text, str):
return str(text)
return XssCleaner(permitted_tags=permitted_tags,
allowed_attributes=allowed_attributes).strip(text, escape)
+244 -211
View File
@@ -106,7 +106,7 @@ DISABLED = 'DISABLED'
KILL = 'KILL'
EXPIRED = 'EXPIRED'
SECONDS = 1
HEARTBEAT = 3*SECONDS
HEARTBEAT = 3 * SECONDS
MAXHIBERNATION = 10
CLEAROUT = '!clear!'
@@ -114,20 +114,23 @@ CALLABLETYPES = (types.LambdaType, types.FunctionType,
types.BuiltinFunctionType,
types.MethodType, types.BuiltinMethodType)
class Task(object):
def __init__(self,app,function,timeout,args='[]',vars='{}',**kwargs):
logger.debug(' new task allocated: %s.%s' % (app,function))
def __init__(self, app, function, timeout, args='[]', vars='{}', **kwargs):
logger.debug(' new task allocated: %s.%s' % (app, function))
self.app = app
self.function = function
self.timeout = timeout
self.args = args # json
self.vars = vars # json
self.args = args # json
self.vars = vars # json
self.__dict__.update(kwargs)
def __str__(self):
return '<Task: %s>' % self.function
class TaskReport(object):
def __init__(self,status,result=None,output=None,tb=None):
def __init__(self, status, result=None, output=None, tb=None):
logger.debug(' new task report: %s' % status)
if tb:
logger.debug(' traceback: %s' % tb)
@@ -137,19 +140,23 @@ class TaskReport(object):
self.result = result
self.output = output
self.tb = tb
def __str__(self):
return '<TaskReport: %s>' % self.status
def demo_function(*argv,**kwargs):
def demo_function(*argv, **kwargs):
""" test function """
for i in range(argv[0]):
print 'click',i
print 'click', i
time.sleep(1)
return 'done'
#the two functions below deal with simplejson decoding as unicode, esp for the dict decode
#and subsequent usage as function Keyword arguments unicode variable names won't work!
#borrowed from http://stackoverflow.com/questions/956867/how-to-get-string-objects-instead-unicode-ones-from-json-in-python
def _decode_list(lst):
newlist = []
for i in lst:
@@ -160,19 +167,21 @@ def _decode_list(lst):
newlist.append(i)
return newlist
def _decode_dict(dct):
newdict = {}
for k, v in dct.iteritems():
if isinstance(k, unicode):
k = k.encode('utf-8')
if isinstance(v, unicode):
v = v.encode('utf-8')
v = v.encode('utf-8')
elif isinstance(v, list):
v = _decode_list(v)
newdict[k] = v
return newdict
def executor(queue,task, out):
def executor(queue, task, out):
""" the background process """
logger.debug(' task started')
@@ -182,11 +191,14 @@ def executor(queue,task, out):
self.out_queue = out_queue
self.stdout = sys.stdout
sys.stdout = self
def __del__(self):
sys.stdout = self.stdout
def flush(self):
pass
def write(self,data):
def write(self, data):
self.out_queue.put(data)
stdout = LogOutput(out)
@@ -200,8 +212,8 @@ def executor(queue,task, out):
logging.getLogger().setLevel(logging.WARN)
# Get controller-specific subdirectory if task.app is of
# form 'app/controller'
(a,c,f) = parse_path_info(task.app)
_env = env(a=a,c=c,import_models=True)
(a, c, f) = parse_path_info(task.app)
_env = env(a=a, c=c, import_models=True)
logging.getLogger().setLevel(level)
scheduler = current._scheduler
f = task.function
@@ -212,22 +224,24 @@ def executor(queue,task, out):
else:
_function = functions.get(f)
if not isinstance(_function, CALLABLETYPES):
raise NameError("name '%s' not found in scheduler's environment" % f)
raise NameError(
"name '%s' not found in scheduler's environment" % f)
globals().update(_env)
args = loads(task.args)
vars = loads(task.vars, object_hook=_decode_dict)
result = dumps(_function(*args,**vars))
result = dumps(_function(*args, **vars))
else:
### for testing purpose only
result = eval(task.function)(
*loads(task.args, object_hook=_decode_dict),
**loads(task.vars, object_hook=_decode_dict))
**loads(task.vars, object_hook=_decode_dict))
queue.put(TaskReport(COMPLETED, result=result))
except BaseException,e:
except BaseException, e:
tb = traceback.format_exc()
queue.put(TaskReport(FAILED,tb=tb))
queue.put(TaskReport(FAILED, tb=tb))
del stdout
class MetaScheduler(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
@@ -235,7 +249,7 @@ class MetaScheduler(threading.Thread):
self.have_heartbeat = True # set to False to kill
self.empty_runs = 0
def async(self,task):
def async(self, task):
"""
starts the background process and returns:
('ok',result,output)
@@ -247,7 +261,7 @@ class MetaScheduler(threading.Thread):
sr = db.scheduler_run
out = multiprocessing.Queue()
queue = multiprocessing.Queue(maxsize=1)
p = multiprocessing.Process(target=executor,args=(queue,task,out))
p = multiprocessing.Process(target=executor, args=(queue, task, out))
self.process = p
logger.debug(' task starting')
p.start()
@@ -264,11 +278,11 @@ class MetaScheduler(threading.Thread):
start = time.time()
while p.is_alive() and (
not task.timeout or time.time()-start < task.timeout):
not task.timeout or time.time() - start < task.timeout):
if tout:
try:
logger.debug(' partial output saved')
db(sr.id==task.run_id).update(output = task_output)
db(sr.id == task.run_id).update(output=task_output)
db.commit()
except:
pass
@@ -279,7 +293,8 @@ class MetaScheduler(threading.Thread):
if tout:
logger.debug(' partial output: "%s"' % str(tout))
if CLEAROUT in tout:
task_output = tout[tout.rfind(CLEAROUT)+len(CLEAROUT):]
task_output = tout[
tout.rfind(CLEAROUT) + len(CLEAROUT):]
else:
task_output += tout
except:
@@ -322,7 +337,7 @@ class MetaScheduler(threading.Thread):
try:
self.process.terminate()
except:
pass # no process to terminate
pass # no process to terminate
def run(self):
""" the thread that sends heartbeat """
@@ -334,19 +349,19 @@ class MetaScheduler(threading.Thread):
def start_heartbeats(self):
self.start()
def send_heartbeat(self,counter):
def send_heartbeat(self, counter):
print 'thum'
time.sleep(1)
def pop_task(self):
return Task(
app = None,
function = 'demo_function',
timeout = 7,
args = '[2]',
vars = '{}')
app=None,
function='demo_function',
timeout=7,
args='[2]',
vars='{}')
def report_task(self,task,task_report):
def report_task(self, task, task_report):
print 'reporting task'
pass
@@ -361,14 +376,16 @@ class MetaScheduler(threading.Thread):
task = self.pop_task()
if task:
self.empty_runs = 0
self.report_task(task,self.async(task))
self.report_task(task, self.async(task))
else:
self.empty_runs += 1
logger.debug('sleeping...')
if self.max_empty_runs != 0:
logger.debug('empty runs %s/%s', self.empty_runs, self.max_empty_runs)
logger.debug('empty runs %s/%s',
self.empty_runs, self.max_empty_runs)
if self.empty_runs >= self.max_empty_runs:
logger.info('empty runs limit reached, killing myself')
logger.info(
'empty runs limit reached, killing myself')
self.die()
self.sleep()
except KeyboardInterrupt:
@@ -379,33 +396,35 @@ TASK_STATUS = (QUEUED, RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED, EXPIRED)
RUN_STATUS = (RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED)
WORKER_STATUS = (ACTIVE, DISABLED, TERMINATE, KILL)
class TYPE(object):
"""
validator that check whether field is valid json and validate its type
"""
def __init__(self,myclass=list,parse=False):
def __init__(self, myclass=list, parse=False):
self.myclass = myclass
self.parse = parse
def __call__(self,value):
def __call__(self, value):
from gluon import current
try:
obj = loads(value)
except:
return (value,current.T('invalid json'))
return (value, current.T('invalid json'))
else:
if isinstance(obj,self.myclass):
if isinstance(obj, self.myclass):
if self.parse:
return (obj,None)
return (obj, None)
else:
return (value,None)
return (value, None)
else:
return (value,current.T('Not of type: %s') % self.myclass)
return (value, current.T('Not of type: %s') % self.myclass)
class Scheduler(MetaScheduler):
def __init__(self,db,tasks=None,migrate=True,
worker_name=None,group_names=['main'],heartbeat=HEARTBEAT,
def __init__(self, db, tasks=None, migrate=True,
worker_name=None, group_names=['main'], heartbeat=HEARTBEAT,
max_empty_runs=0, discard_results=False, utc_time=False):
MetaScheduler.__init__(self)
@@ -415,8 +434,9 @@ class Scheduler(MetaScheduler):
self.tasks = tasks
self.group_names = group_names
self.heartbeat = heartbeat
self.worker_name = worker_name or socket.gethostname()+'#'+str(os.getpid())
self.worker_status = RUNNING, 1 #tuple containing status as recorded in
self.worker_name = worker_name or socket.gethostname(
) + '#' + str(os.getpid())
self.worker_status = RUNNING, 1 # tuple containing status as recorded in
#the table, plus a boost parameter for
#hibernation (i.e. when someone stop the
#worker acting on the scheduler_worker table)
@@ -429,86 +449,85 @@ class Scheduler(MetaScheduler):
from gluon import current
current._scheduler = self
self.define_tables(db,migrate=migrate)
self.define_tables(db, migrate=migrate)
def now(self):
return self.utc_time and datetime.datetime.utcnow() or datetime.datetime.now()
def set_requirements(self, scheduler_task):
from gluon import current
if hasattr(current,'request'):
scheduler_task.application_name.default= '%s/%s' % (
current.request.application, current.request.controller
if hasattr(current, 'request'):
scheduler_task.application_name.default = '%s/%s' % (
current.request.application, current.request.controller
)
def define_tables(self,db,migrate):
def define_tables(self, db, migrate):
from gluon.dal import DEFAULT
logger.debug('defining tables (migrate=%s)' % migrate)
now = self.now
db.define_table(
'scheduler_task',
Field('application_name',requires=IS_NOT_EMPTY(),
default=None,writable=False),
Field('task_name',default=None),
Field('group_name',default='main'),
Field('status',requires=IS_IN_SET(TASK_STATUS),
default=QUEUED,writable=False),
Field('application_name', requires=IS_NOT_EMPTY(),
default=None, writable=False),
Field('task_name', default=None),
Field('group_name', default='main'),
Field('status', requires=IS_IN_SET(TASK_STATUS),
default=QUEUED, writable=False),
Field('function_name',
requires=IS_IN_SET(sorted(self.tasks.keys()))
if self.tasks else DEFAULT),
Field('uuid', requires=IS_NOT_IN_DB(db, 'scheduler_task.uuid'),
unique=True, default=web2py_uuid),
Field('args','text',default='[]',requires=TYPE(list)),
Field('vars','text',default='{}',requires=TYPE(dict)),
Field('enabled','boolean',default=True),
Field('start_time','datetime',default=now,
requires = IS_DATETIME()),
Field('next_run_time','datetime',default=now),
Field('stop_time','datetime'),
Field('repeats','integer',default=1,comment="0=unlimited",
Field('args', 'text', default='[]', requires=TYPE(list)),
Field('vars', 'text', default='{}', requires=TYPE(dict)),
Field('enabled', 'boolean', default=True),
Field('start_time', 'datetime', default=now,
requires=IS_DATETIME()),
Field('next_run_time', 'datetime', default=now),
Field('stop_time', 'datetime'),
Field('repeats', 'integer', default=1, comment="0=unlimited",
requires=IS_INT_IN_RANGE(0, None)),
Field('retry_failed', 'integer', default=0, comment="-1=unlimited",
requires=IS_INT_IN_RANGE(-1, None)),
Field('period','integer',default=60,comment='seconds',
Field('period', 'integer', default=60, comment='seconds',
requires=IS_INT_IN_RANGE(0, None)),
Field('timeout','integer',default=60,comment='seconds',
Field('timeout', 'integer', default=60, comment='seconds',
requires=IS_INT_IN_RANGE(0, None)),
Field('sync_output', 'integer', default=0,
comment="update output every n sec: 0=never",
requires=IS_INT_IN_RANGE(0, None)),
Field('times_run','integer',default=0,writable=False),
Field('times_failed','integer',default=0,writable=False),
Field('last_run_time','datetime',writable=False,readable=False),
Field('assigned_worker_name',default='',writable=False),
Field('times_run', 'integer', default=0, writable=False),
Field('times_failed', 'integer', default=0, writable=False),
Field('last_run_time', 'datetime', writable=False, readable=False),
Field('assigned_worker_name', default='', writable=False),
on_define=self.set_requirements,
migrate=migrate,format='%(task_name)s')
migrate=migrate, format='%(task_name)s')
db.define_table(
'scheduler_run',
Field('scheduler_task','reference scheduler_task'),
Field('status',requires=IS_IN_SET(RUN_STATUS)),
Field('start_time','datetime'),
Field('stop_time','datetime'),
Field('output','text'),
Field('result','text'),
Field('traceback','text'),
Field('worker_name',default=self.worker_name),
Field('scheduler_task', 'reference scheduler_task'),
Field('status', requires=IS_IN_SET(RUN_STATUS)),
Field('start_time', 'datetime'),
Field('stop_time', 'datetime'),
Field('output', 'text'),
Field('result', 'text'),
Field('traceback', 'text'),
Field('worker_name', default=self.worker_name),
migrate=migrate)
db.define_table(
'scheduler_worker',
Field('worker_name', unique=True),
Field('first_heartbeat','datetime'),
Field('last_heartbeat','datetime'),
Field('status',requires=IS_IN_SET(WORKER_STATUS)),
Field('first_heartbeat', 'datetime'),
Field('last_heartbeat', 'datetime'),
Field('status', requires=IS_IN_SET(WORKER_STATUS)),
Field('is_ticker', 'boolean', default=False, writable=False),
Field('group_names', 'list:string', default=self.group_names),
migrate=migrate)
if migrate:
db.commit()
def loop(self,worker_name=None):
def loop(self, worker_name=None):
signal.signal(signal.SIGTERM, lambda signum, stack_frame: sys.exit(1))
try:
self.start_heartbeats()
@@ -521,14 +540,16 @@ class Scheduler(MetaScheduler):
task = self.pop_task()
if task:
self.empty_runs = 0
self.report_task(task,self.async(task))
self.report_task(task, self.async(task))
else:
self.empty_runs += 1
logger.debug('sleeping...')
if self.max_empty_runs != 0:
logger.debug('empty runs %s/%s', self.empty_runs, self.max_empty_runs)
logger.debug('empty runs %s/%s',
self.empty_runs, self.max_empty_runs)
if self.empty_runs >= self.max_empty_runs:
logger.info('empty runs limit reached, killing myself')
logger.info(
'empty runs limit reached, killing myself')
self.die()
self.sleep()
except (KeyboardInterrupt, SystemExit):
@@ -541,7 +562,7 @@ class Scheduler(MetaScheduler):
if self.is_a_ticker and self.do_assign_tasks:
#I'm a ticker, and 5 loops passed without reassigning tasks, let's do
#that and loop again
db.commit() #?don't know if it's useful, let's be completely sure
db.commit() # ?don't know if it's useful, let's be completely sure
while True:
try:
self.assign_tasks()
@@ -552,21 +573,22 @@ class Scheduler(MetaScheduler):
logger.error('TICKER: error assigning tasks')
return None
db.commit()
grabbed = db(ts.assigned_worker_name==self.worker_name)\
(ts.status==ASSIGNED)
grabbed = db(ts.assigned_worker_name == self.worker_name)(
ts.status == ASSIGNED)
task = grabbed.select(limitby=(0,1), orderby=ts.next_run_time).first()
task = grabbed.select(limitby=(0, 1), orderby=ts.next_run_time).first()
if task:
task.update_record(status=RUNNING,last_run_time=now)
task.update_record(status=RUNNING, last_run_time=now)
#noone will touch my task!
db.commit()
logger.debug(' work to do %s' % task.id)
else:
logger.debug('nothing to do')
return None
next_run_time = task.last_run_time + datetime.timedelta(seconds=task.period)
next_run_time = task.last_run_time + datetime.timedelta(
seconds=task.period)
times_run = task.times_run + 1
if times_run < task.repeats or task.repeats==0:
if times_run < task.repeats or task.repeats == 0:
run_again = True
else:
run_again = False
@@ -575,7 +597,7 @@ class Scheduler(MetaScheduler):
logger.debug(' new scheduler_run record')
try:
run_id = db.scheduler_run.insert(
scheduler_task = task.id,
scheduler_task=task.id,
status=RUNNING,
start_time=now,
worker_name=self.worker_name)
@@ -585,22 +607,22 @@ class Scheduler(MetaScheduler):
db.rollback()
logger.info('new task %(id)s "%(task_name)s" %(application_name)s.%(function_name)s' % task)
return Task(
app = task.application_name,
function = task.function_name,
timeout = task.timeout,
args = task.args, #in json
vars = task.vars, #in json
task_id = task.id,
run_id = run_id,
run_again = run_again,
app=task.application_name,
function=task.function_name,
timeout=task.timeout,
args=task.args, # in json
vars=task.vars, # in json
task_id=task.id,
run_id=run_id,
run_again=run_again,
next_run_time=next_run_time,
times_run = times_run,
stop_time = task.stop_time,
retry_failed = task.retry_failed,
times_failed = task.times_failed,
sync_output = task.sync_output)
times_run=times_run,
stop_time=task.stop_time,
retry_failed=task.retry_failed,
times_failed=task.times_failed,
sync_output=task.sync_output)
def report_task(self,task,task_report):
def report_task(self, task, task_report):
db = self.db
now = self.now()
while True:
@@ -610,16 +632,17 @@ class Scheduler(MetaScheduler):
#result is 'null' as a string if task completed
#if it's stopped it's None as NoneType, so we record
#the STOPPED "run" anyway
logger.debug(' recording task report in db (%s)' % task_report.status)
db(db.scheduler_run.id==task.run_id).update(
status = task_report.status,
stop_time = now,
result = task_report.result,
output = task_report.output,
traceback = task_report.tb)
logger.debug(' recording task report in db (%s)' %
task_report.status)
db(db.scheduler_run.id == task.run_id).update(
status=task_report.status,
stop_time=now,
result=task_report.result,
output=task_report.output,
traceback=task_report.tb)
else:
logger.debug(' deleting task report in db because of no result')
db(db.scheduler_run.id==task.run_id).delete()
db(db.scheduler_run.id == task.run_id).delete()
is_expired = (task.stop_time
and task.next_run_time > task.stop_time
and True or False)
@@ -627,26 +650,25 @@ class Scheduler(MetaScheduler):
or task.run_again and not is_expired
and QUEUED or COMPLETED)
if task_report.status == COMPLETED:
d = dict(status = status,
next_run_time = task.next_run_time,
times_run = task.times_run,
times_failed = 0
d = dict(status=status,
next_run_time=task.next_run_time,
times_run=task.times_run,
times_failed=0
)
db(db.scheduler_task.id==task.task_id)\
(db.scheduler_task.status==RUNNING).update(**d)
db(db.scheduler_task.id == task.task_id)(
db.scheduler_task.status == RUNNING).update(**d)
else:
st_mapping = {'FAILED':'FAILED',
'TIMEOUT':'TIMEOUT',
'STOPPED':'QUEUED'}[task_report.status]
st_mapping = {'FAILED': 'FAILED',
'TIMEOUT': 'TIMEOUT',
'STOPPED': 'QUEUED'}[task_report.status]
status = (task.retry_failed
and task.times_failed < task.retry_failed
and QUEUED or task.retry_failed==-1
and QUEUED or task.retry_failed == -1
and QUEUED or st_mapping)
db(db.scheduler_task.id==task.task_id)\
(db.scheduler_task.status==RUNNING).update(
times_failed=db.scheduler_task.times_failed+1,
next_run_time = task.next_run_time,
status=status)
db(db.scheduler_task.id == task.task_id)(db.scheduler_task.status == RUNNING).update(
times_failed=db.scheduler_task.times_failed + 1,
next_run_time=task.next_run_time,
status=status)
db.commit()
logger.info('task completed (%s)' % task_report.status)
break
@@ -655,34 +677,39 @@ class Scheduler(MetaScheduler):
def adj_hibernation(self):
if self.worker_status[0] == DISABLED:
hibernation = self.worker_status[1] + 1 if self.worker_status[1] < MAXHIBERNATION else MAXHIBERNATION
hibernation = self.worker_status[1] + 1 if self.worker_status[
1] < MAXHIBERNATION else MAXHIBERNATION
self.worker_status = DISABLED, hibernation
def send_heartbeat(self,counter):
def send_heartbeat(self, counter):
if not self.db_thread:
logger.debug('thread building own DAL object')
self.db_thread = DAL(self.db._uri,folder = self.db._adapter.folder)
self.define_tables(self.db_thread,migrate=False)
self.db_thread = DAL(
self.db._uri, folder=self.db._adapter.folder)
self.define_tables(self.db_thread, migrate=False)
try:
db = self.db_thread
sw, st = db.scheduler_worker, db.scheduler_task
now = self.now()
expiration = now-datetime.timedelta(seconds=self.heartbeat*3)
departure = now-datetime.timedelta(seconds=self.heartbeat*3*MAXHIBERNATION)
expiration = now - datetime.timedelta(seconds=self.heartbeat * 3)
departure = now - datetime.timedelta(
seconds=self.heartbeat * 3 * MAXHIBERNATION)
# record heartbeat
mybackedstatus = db(sw.worker_name==self.worker_name).select().first()
mybackedstatus = db(
sw.worker_name == self.worker_name).select().first()
if not mybackedstatus:
sw.insert(status = ACTIVE,worker_name = self.worker_name,
first_heartbeat = now,last_heartbeat = now,
group_names = self.group_names)
self.worker_status = ACTIVE, 1 #activating the process
sw.insert(status=ACTIVE, worker_name=self.worker_name,
first_heartbeat=now, last_heartbeat=now,
group_names=self.group_names)
self.worker_status = ACTIVE, 1 # activating the process
else:
if mybackedstatus.status == DISABLED:
self.worker_status = DISABLED, self.worker_status[1]#keep sleeping
self.worker_status = DISABLED, self.worker_status[
1] # keep sleeping
if self.worker_status[1] == MAXHIBERNATION:
logger.debug('........recording heartbeat')
db(sw.worker_name==self.worker_name).update(
last_heartbeat = now)
db(sw.worker_name == self.worker_name).update(
last_heartbeat=now)
elif mybackedstatus.status == TERMINATE:
self.worker_status = TERMINATE, self.worker_status[1]
@@ -695,23 +722,24 @@ class Scheduler(MetaScheduler):
else:
logger.debug('........recording heartbeat')
db(sw.worker_name==self.worker_name).update(
last_heartbeat = now, status = ACTIVE)
self.worker_status = ACTIVE, 1 #re-activating the process
db(sw.worker_name == self.worker_name).update(
last_heartbeat=now, status=ACTIVE)
self.worker_status = ACTIVE, 1 # re-activating the process
self.do_assign_tasks = False
if counter % 5 == 0:
try:
# delete inactive workers
logger.debug(' freeing workers that have not sent heartbeat')
logger.debug(
' freeing workers that have not sent heartbeat')
inactive_workers = db(
((sw.last_heartbeat<expiration) & (sw.status == ACTIVE)) |
((sw.last_heartbeat<departure) & (sw.status != ACTIVE))
)
((sw.last_heartbeat < expiration) & (sw.status == ACTIVE)) |
((sw.last_heartbeat <
departure) & (sw.status != ACTIVE))
)
db(st.assigned_worker_name.belongs(
inactive_workers._select(sw.worker_name)))\
(st.status == RUNNING)\
.update(assigned_worker_name='',status=QUEUED)
inactive_workers._select(sw.worker_name)))(st.status == RUNNING)\
.update(assigned_worker_name='', status=QUEUED)
inactive_workers.delete()
self.is_a_ticker = self.being_a_ticker()
if self.worker_status[0] == ACTIVE:
@@ -727,15 +755,17 @@ class Scheduler(MetaScheduler):
def being_a_ticker(self):
db = self.db_thread
sw = db.scheduler_worker
ticker = db((sw.worker_name != self.worker_name) & (sw.is_ticker == True) & (sw.status == ACTIVE)).select().first()
ticker = db((sw.worker_name != self.worker_name) & (
sw.is_ticker == True) & (sw.status == ACTIVE)).select().first()
if not ticker:
db(sw.worker_name == self.worker_name).update(is_ticker = True)
db(sw.worker_name != self.worker_name).update(is_ticker = False)
db(sw.worker_name == self.worker_name).update(is_ticker=True)
db(sw.worker_name != self.worker_name).update(is_ticker=False)
logger.info("TICKER: I'm a ticker (%s)" % self.worker_name)
db.commit()
return True
else:
logger.info("%s is a ticker, I'm a poor worker" % ticker.worker_name)
logger.info(
"%s is a ticker, I'm a poor worker" % ticker.worker_name)
return False
def assign_tasks(self):
@@ -749,19 +779,17 @@ class Scheduler(MetaScheduler):
group_names = w.group_names
for gname in group_names:
if gname not in wkgroups:
wkgroups[gname] = dict(workers=[{'name' : w.worker_name, 'c' : 0}])
wkgroups[gname] = dict(
workers=[{'name': w.worker_name, 'c': 0}])
else:
wkgroups[gname]['workers'].append({'name' : w.worker_name, 'c' : 0})
wkgroups[gname]['workers'].append(
{'name': w.worker_name, 'c': 0})
#set queued tasks that expired between "runs" (i.e., you turned off)
#the scheduler and then it wasn't expired, but now it is
db(ts.status.belongs((QUEUED,ASSIGNED)))(ts.stop_time<now).update(status=EXPIRED)
db(ts.status.belongs(
(QUEUED, ASSIGNED)))(ts.stop_time < now).update(status=EXPIRED)
all_available = db(ts.status.belongs((QUEUED,ASSIGNED)))\
((ts.times_run<ts.repeats)|(ts.repeats==0))\
(ts.start_time<=now)\
((ts.stop_time==None) | (ts.stop_time>now))\
(ts.next_run_time<=now)\
(ts.enabled==True)
all_available = db(ts.status.belongs((QUEUED, ASSIGNED)))((ts.times_run < ts.repeats) | (ts.repeats == 0))(ts.start_time <= now)((ts.stop_time is None) | (ts.stop_time > now))(ts.next_run_time <= now)(ts.enabled == True)
limit = len(all_workers) * 50
#if there are a moltitude of tasks, let's assign a maximum of 50 tasks per worker.
@@ -770,7 +798,8 @@ class Scheduler(MetaScheduler):
#50 is quite a sweet spot also for fast tasks, with sane heartbeat values
#NB: ticker reassign tasks every 5 cycles, so if a worker completes his 50 tasks in less
#than heartbeat*5 seconds, it won't pick new tasks until heartbeat*5 seconds pass.
tasks = all_available.select(limitby=(0,limit), orderby=ts.next_run_time)
tasks = all_available.select(
limitby=(0, limit), orderby=ts.next_run_time)
#everything until now is going fine. If a worker is currently elaborating a long task,
#all other tasks assigned to him needs to be reassigned "freely" to other workers, that may be free.
#this shuffles up things a bit, in order to maintain the idea of a semi-linear scalability
@@ -803,7 +832,8 @@ class Scheduler(MetaScheduler):
logger.info('TICKER: tasks are %s' % len(tasks))
def sleep(self):
time.sleep(self.heartbeat*self.worker_status[1]) # should only sleep until next available task
time.sleep(self.heartbeat * self.worker_status[1])
# should only sleep until next available task
def queue_task(self, function, pargs=[], pvars={}, **kwargs):
"""
@@ -829,12 +859,12 @@ class Scheduler(MetaScheduler):
tname = 'task_name' in kwargs and kwargs.pop('task_name') or function
print 'a', targs
rtn = self.db.scheduler_task.validate_and_insert(
function_name=function,
task_name=tname,
args=targs,
vars=tvars,
uuid=tuuid,
**kwargs)
function_name=function,
task_name=tname,
args=targs,
vars=tvars,
uuid=tuuid,
**kwargs)
if not rtn.errors:
rtn.uuid = tuuid
else:
@@ -866,25 +896,28 @@ class Scheduler(MetaScheduler):
elif isinstance(ref, Query):
q = ref
else:
raise SyntaxError, "You can retrieve results only by id, uuid or Query"
raise SyntaxError(
"You can retrieve results only by id, uuid or Query")
fields = st.ALL
left = False
orderby = ~st.id
if output:
fields = st.ALL, sr.ALL
left = sr.on(sr.scheduler_task == st.id)
orderby = ~st.id|~sr.id
orderby = ~st.id | ~sr.id
row = self.db(q).select(
*fields,
orderby=orderby,
left=left,
limitby=(0,1)
).first()
limitby=(0, 1)
).first()
if output:
row.result = row.scheduler_run.result and \
loads(row.scheduler_run.result, object_hook=_decode_dict) or None
loads(row.scheduler_run.result,
object_hook=_decode_dict) or None
return row
def main():
"""
allows to run worker without python web2py.py .... by simply python this.py
@@ -894,34 +927,34 @@ def main():
"-w", "--worker_name", dest="worker_name", default=None,
help="start a worker with name")
parser.add_option(
"-b", "--heartbeat",dest="heartbeat", default = 10,
"-b", "--heartbeat", dest="heartbeat", default=10,
type='int', help="heartbeat time in seconds (default 10)")
parser.add_option(
"-L", "--logger_level",dest="logger_level",
"-L", "--logger_level", dest="logger_level",
default=30,
type='int',
help="set debug output level (0-100, 0 means all, 100 means none;default is 30)")
parser.add_option("-E", "--empty-runs",
dest="max_empty_runs",
type='int',
default = 0,
help="max loops with no grabbed tasks permitted (0 for never check)")
dest="max_empty_runs",
type='int',
default=0,
help="max loops with no grabbed tasks permitted (0 for never check)")
parser.add_option(
"-g", "--group_names",dest="group_names",
default = 'main',
"-g", "--group_names", dest="group_names",
default='main',
help="comma separated list of groups to be picked by the worker")
parser.add_option(
"-f", "--db_folder",dest="db_folder",
default = '/Users/mdipierro/web2py/applications/scheduler/databases',
"-f", "--db_folder", dest="db_folder",
default='/Users/mdipierro/web2py/applications/scheduler/databases',
help="location of the dal database folder")
parser.add_option(
"-u", "--db_uri",dest="db_uri",
default = 'sqlite://storage.sqlite',
"-u", "--db_uri", dest="db_uri",
default='sqlite://storage.sqlite',
help="database URI string (web2py DAL syntax)")
parser.add_option(
"-t", "--tasks",dest="tasks",default=None,
help="file containing task files, must define" + \
"tasks = {'task_name':(lambda: 'output')} or similar set of tasks")
"-t", "--tasks", dest="tasks", default=None,
help="file containing task files, must define" +
"tasks = {'task_name':(lambda: 'output')} or similar set of tasks")
parser.add_option(
"-U", "--utc-time", dest="utc_time", default=False,
help="work with UTC timestamps"
@@ -930,35 +963,35 @@ def main():
if not options.tasks or not options.db_uri:
print USAGE
if options.tasks:
path,filename = os.path.split(options.tasks)
path, filename = os.path.split(options.tasks)
if filename.endswith('.py'):
filename = filename[:-3]
sys.path.append(path)
print 'importing tasks...'
tasks = __import__(filename, globals(), locals(), [], -1).tasks
print 'tasks found: '+', '.join(tasks.keys())
print 'tasks found: ' + ', '.join(tasks.keys())
else:
tasks = {}
group_names = [x.strip() for x in options.group_names.split(',')]
logging.getLogger().setLevel(options.logger_level)
print 'groups for this worker: '+', '.join(group_names)
print 'groups for this worker: ' + ', '.join(group_names)
print 'connecting to database in folder: ' + options.db_folder or './'
print 'using URI: '+options.db_uri
db = DAL(options.db_uri,folder=options.db_folder)
print 'using URI: ' + options.db_uri
db = DAL(options.db_uri, folder=options.db_folder)
print 'instantiating scheduler...'
scheduler=Scheduler(db = db,
worker_name = options.worker_name,
tasks = tasks,
migrate = True,
group_names = group_names,
heartbeat = options.heartbeat,
max_empty_runs = options.max_empty_runs,
utc_time = options.utc_time)
scheduler = Scheduler(db=db,
worker_name=options.worker_name,
tasks=tasks,
migrate=True,
group_names=group_names,
heartbeat=options.heartbeat,
max_empty_runs=options.max_empty_runs,
utc_time=options.utc_time)
signal.signal(signal.SIGTERM, lambda signum, stack_frame: sys.exit(1))
print 'starting main worker loop...'
scheduler.loop()
if __name__=='__main__':
if __name__ == '__main__':
main()
+36 -32
View File
@@ -19,61 +19,64 @@ except ImportError:
except:
import contrib.simplejson as json_parser # fallback to pure-Python module
def custom_json(o):
if hasattr(o,'custom_json') and callable(o.custom_json):
if hasattr(o, 'custom_json') and callable(o.custom_json):
return o.custom_json()
if isinstance(o, (datetime.date,
datetime.datetime,
datetime.time)):
return o.isoformat()[:19].replace('T',' ')
return o.isoformat()[:19].replace('T', ' ')
elif isinstance(o, (int, long)):
return int(o)
elif isinstance(o, decimal.Decimal):
return str(o)
elif isinstance(o, lazyT):
return str(o)
elif isinstance(o,XmlComponent):
elif isinstance(o, XmlComponent):
return str(o)
elif hasattr(o,'as_list') and callable(o.as_list):
elif hasattr(o, 'as_list') and callable(o.as_list):
return o.as_list()
elif hasattr(o,'as_dict') and callable(o.as_dict):
elif hasattr(o, 'as_dict') and callable(o.as_dict):
return o.as_dict()
else:
raise TypeError(repr(o) + " is not JSON serializable")
def xml_rec(value, key, quote=True):
if hasattr(value,'custom_xml') and callable(value.custom_xml):
if hasattr(value, 'custom_xml') and callable(value.custom_xml):
return value.custom_xml()
elif isinstance(value, (dict, Storage)):
return TAG[key](*[TAG[k](xml_rec(v, '',quote)) \
for k, v in value.items()])
return TAG[key](*[TAG[k](xml_rec(v, '', quote))
for k, v in value.items()])
elif isinstance(value, list):
return TAG[key](*[TAG.item(xml_rec(item, '',quote)) for item in value])
elif hasattr(value,'as_list') and callable(value.as_list):
return str(xml_rec(value.as_list(),'',quote))
elif hasattr(value,'as_dict') and callable(value.as_dict):
return str(xml_rec(value.as_dict(),'',quote))
return TAG[key](*[TAG.item(xml_rec(item, '', quote)) for item in value])
elif hasattr(value, 'as_list') and callable(value.as_list):
return str(xml_rec(value.as_list(), '', quote))
elif hasattr(value, 'as_dict') and callable(value.as_dict):
return str(xml_rec(value.as_dict(), '', quote))
else:
return xmlescape(value,quote)
return xmlescape(value, quote)
def xml(value, encoding='UTF-8', key='document', quote=True):
return ('<?xml version="1.0" encoding="%s"?>' % encoding) + str(xml_rec(value,key,quote))
return ('<?xml version="1.0" encoding="%s"?>' % encoding) + str(xml_rec(value, key, quote))
def json(value,default=custom_json):
return json_parser.dumps(value,default=default)
def json(value, default=custom_json):
return json_parser.dumps(value, default=default)
def csv(value):
return ''
def ics(events, title=None, link=None, timeshift=0, **ignored):
import datetime
title = title or '(unkown)'
if link and not callable(link):
link = lambda item,prefix=link: prefix.replace('[id]',str(item['id']))
link = lambda item, prefix=link: prefix.replace(
'[id]', str(item['id']))
s = 'BEGIN:VCALENDAR'
s += '\nVERSION:2.0'
s += '\nX-WR-CALNAME:%s' % title
@@ -86,9 +89,9 @@ def ics(events, title=None, link=None, timeshift=0, **ignored):
s += '\nUID:%s' % item['id']
if link:
s += '\nURL:%s' % link(item)
shift = datetime.timedelta(seconds=3600*timeshift)
start = item['start_datetime']+shift
stop = item['stop_datetime']+shift
shift = datetime.timedelta(seconds=3600 * timeshift)
start = item['start_datetime'] + shift
stop = item['stop_datetime'] + shift
s += '\nDTSTART:%s' % start.strftime('%Y%m%dT%H%M%S')
s += '\nDTEND:%s' % stop.strftime('%Y%m%dT%H%M%S')
s += '\nSUMMARY:%s' % item['title']
@@ -96,18 +99,19 @@ def ics(events, title=None, link=None, timeshift=0, **ignored):
s += '\nEND:VCALENDAR'
return s
def rss(feed):
if not 'entries' in feed and 'items' in feed:
feed['entries'] = feed['items']
now=datetime.datetime.now()
rss = rss2.RSS2(title = str(feed.get('title','(notitle)')),
link = str(feed.get('link',None)),
description = str(feed.get('description','')),
lastBuildDate = feed.get('created_on', now),
items = [rss2.RSSItem(
title=str(entry.get('title','(notitle)')),
link=str(entry.get('link',None)),
description=str(entry.get('description','')),
pubDate=entry.get('created_on', now)
) for entry in feed.get('entries',[])])
now = datetime.datetime.now()
rss = rss2.RSS2(title=str(feed.get('title', '(notitle)')),
link=str(feed.get('link', None)),
description=str(feed.get('description', '')),
lastBuildDate=feed.get('created_on', now),
items=[rss2.RSSItem(
title=str(entry.get('title', '(notitle)')),
link=str(entry.get('link', None)),
description=str(entry.get('description', '')),
pubDate=entry.get('created_on', now)
) for entry in feed.get('entries', [])])
return rss2.dumps(rss)
+1 -1
View File
@@ -29,7 +29,7 @@ global_settings.app_folders = set()
global_settings.debugging = False
global_settings.is_pypy = \
hasattr(platform,'python_implementation') and \
hasattr(platform, 'python_implementation') and \
platform.python_implementation() == 'PyPy'
global_settings.is_jython = \
+47 -35
View File
@@ -30,12 +30,13 @@ from dal import BaseAdapter
logger = logging.getLogger("web2py")
def exec_environment(
pyfile='',
request=None,
response=None,
session=None,
):
):
"""
.. function:: gluon.shell.exec_environment([pyfile=''[, request=Request()
[, response=Response[, session=Session()]]]])
@@ -50,9 +51,12 @@ def exec_environment(
"""
if request is None: request = Request()
if response is None: response = Response()
if session is None: session = Session()
if request is None:
request = Request()
if response is None:
response = Response()
if session is None:
session = Session()
if request.folder is None:
mo = re.match(r'(|.*/)applications/(?P<appname>[^/]+)', pyfile)
@@ -78,7 +82,7 @@ def env(
f=None,
dir='',
extra_request={},
):
):
"""
Return web2py execution environment for application (a), controller (c),
function (f).
@@ -113,7 +117,7 @@ def env(
request.env.remote_addr = '127.0.0.1'
request.env.web2py_runtime_gae = global_settings.web2py_runtime_gae
for k,v in extra_request.items():
for k, v in extra_request.items():
request[k] = v
# Monkey patch so credentials checks pass.
@@ -129,7 +133,7 @@ def env(
try:
run_models_in(environment)
except RestrictedError, e:
sys.stderr.write(e.traceback+'\n')
sys.stderr.write(e.traceback + '\n')
sys.exit(1)
environment['__name__'] = '__main__'
@@ -156,7 +160,7 @@ def run(
startfile=None,
bpython=False,
python_code=False
):
):
"""
Start interactive shell or run Python script (startfile) in web2py
controller environment. appname is formatted like:
@@ -172,7 +176,8 @@ def run(
adir = os.path.join('applications', a)
if not os.path.exists(adir):
if sys.stdin and not sys.stdin.name == '/dev/null':
confirm = raw_input('application %s does not exist, create (y/n)?' % a)
confirm = raw_input(
'application %s does not exist, create (y/n)?' % a)
else:
logging.warn('application does not exist and will not be created')
return
@@ -180,16 +185,17 @@ def run(
os.mkdir(adir)
w2p_unpack('welcome.w2p', adir)
for subfolder in ['models','views','controllers', 'databases',
'modules','cron','errors','sessions',
'languages','static','private','uploads']:
subpath = os.path.join(adir,subfolder)
for subfolder in ['models', 'views', 'controllers', 'databases',
'modules', 'cron', 'errors', 'sessions',
'languages', 'static', 'private', 'uploads']:
subpath = os.path.join(adir, subfolder)
if not os.path.exists(subpath):
os.mkdir(subpath)
db = os.path.join(adir,'models/db.py')
db = os.path.join(adir, 'models/db.py')
if os.path.exists(db):
data = fileutils.read_file(db)
data = data.replace('<your secret key>','sha512:'+web2py_uuid())
data = data.replace(
'<your secret key>', 'sha512:' + web2py_uuid())
fileutils.write_file(db, data)
if c:
@@ -198,7 +204,8 @@ def run(
if c:
cfile = os.path.join('applications', a, 'controllers', c + '.py')
if not os.path.isfile(cfile):
cfile = os.path.join('applications', a, 'compiled', "controllers_%s_%s.pyc" % (c,f))
cfile = os.path.join('applications', a, 'compiled',
"controllers_%s_%s.pyc" % (c, f))
if not os.path.isfile(cfile):
die(errmsg)
else:
@@ -214,17 +221,21 @@ def run(
if startfile:
try:
execfile(startfile, _env)
if import_models: BaseAdapter.close_all_instances('commit')
if import_models:
BaseAdapter.close_all_instances('commit')
except Exception, e:
print traceback.format_exc()
if import_models: BaseAdapter.close_all_instances('rollback')
if import_models:
BaseAdapter.close_all_instances('rollback')
elif python_code:
try:
exec(python_code, _env)
if import_models: BaseAdapter.close_all_instances('commit')
if import_models:
BaseAdapter.close_all_instances('commit')
except Exception, e:
print traceback.format_exc()
if import_models: BaseAdapter.close_all_instances('rollback')
if import_models:
BaseAdapter.close_all_instances('rollback')
else:
if not plain:
if bpython:
@@ -248,7 +259,7 @@ def run(
# IPython; thanks Michael Toomim
if '__builtins__' in _env:
del _env['__builtins__']
shell = IPython.Shell.IPShell(argv=[],user_ns=_env)
shell = IPython.Shell.IPShell(argv=[], user_ns=_env)
shell.mainloop()
return
except:
@@ -304,7 +315,7 @@ def test(testpath, import_models=True, verbose=False):
mo = re.match(r'(|.*/)applications/(?P<a>[^/]+)', testpath)
if not mo:
die('test file is not in application directory: %s'
% testpath)
% testpath)
a = mo.group('a')
c = f = None
files = [testpath]
@@ -340,8 +351,8 @@ def test(testpath, import_models=True, verbose=False):
globs = env(a, c=c, f=f, import_models=import_models)
execfile(testfile, globs)
doctest.run_docstring_examples(obj, globs=globs,
name='%s: %s' % (os.path.basename(testfile),
name), verbose=verbose)
name='%s: %s' % (os.path.basename(testfile),
name), verbose=verbose)
if type(obj) in (types.TypeType, types.ClassType):
for attr_name in dir(obj):
@@ -369,8 +380,8 @@ def execute_from_command_line(argv=None):
parser = optparse.OptionParser(usage=get_usage())
parser.add_option('-S', '--shell', dest='shell', metavar='APPNAME',
help='run web2py in interactive shell or IPython(if installed) ' + \
'with specified appname')
help='run web2py in interactive shell or IPython(if installed) ' +
'with specified appname')
msg = 'run web2py in interactive shell or bpython (if installed) with'
msg += ' specified appname (if app does not exist it will be created).'
msg += '\n Use combined with --shell'
@@ -381,7 +392,7 @@ def execute_from_command_line(argv=None):
default=False,
dest='bpython',
help=msg,
)
)
parser.add_option(
'-P',
'--plain',
@@ -389,25 +400,25 @@ def execute_from_command_line(argv=None):
default=False,
dest='plain',
help='only use plain python shell, should be used with --shell option',
)
)
parser.add_option(
'-M',
'--import_models',
action='store_true',
default=False,
dest='import_models',
help='auto import model files, default is False, ' + \
' should be used with --shell option',
)
help='auto import model files, default is False, ' +
' should be used with --shell option',
)
parser.add_option(
'-R',
'--run',
dest='run',
metavar='PYTHON_FILE',
default='',
help='run PYTHON_FILE in web2py environment, ' + \
'should be used with --shell option',
)
help='run PYTHON_FILE in web2py environment, ' +
'should be used with --shell option',
)
(options, args) = parser.parse_args(argv[1:])
@@ -419,7 +430,8 @@ def execute_from_command_line(argv=None):
startfile = args[0]
else:
startfile = ''
run(options.shell, options.plain, startfile=startfile, bpython=options.bpython)
run(options.shell, options.plain, startfile=startfile,
bpython=options.bpython)
if __name__ == '__main__':
+1 -1
View File
@@ -1,5 +1,5 @@
# this file exists for backward compatibility
__all__ = ['DAL','Field','DRIVERS']
__all__ = ['DAL', 'Field', 'DRIVERS']
from dal import DAL, Field, Table, Query, Set, Expression, Row, Rows, DRIVERS, BaseAdapter, SQLField, SQLTable, SQLXorable, SQLQuery, SQLSet, SQLRows, SQLStorage, SQLDB, GQLDB, SQLALL, SQLCustomType
+630 -564
View File
File diff suppressed because it is too large Load Diff
+43 -24
View File
@@ -18,6 +18,7 @@ import portalocker
__all__ = ['List', 'Storage', 'Settings', 'Messages',
'StorageList', 'load_storage', 'save_storage']
class Storage(dict):
"""
A Storage object is like a dictionary except `obj.foo` can be used
@@ -38,7 +39,7 @@ class Storage(dict):
>>> print o.a
None
"""
__slots__=()
__slots__ = ()
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
__getitem__ = dict.get
@@ -48,7 +49,7 @@ class Storage(dict):
__getstate__ = lambda self: None
__copy__ = lambda self: Storage(self)
def getlist(self,key):
def getlist(self, key):
"""
Return a Storage value as a list.
@@ -68,11 +69,11 @@ class Storage(dict):
>>> request.vars.getlist('z')
[]
"""
value = self.get(key,[])
value = self.get(key, [])
return value if not value else \
value if isinstance(value,(list,tuple)) else [value]
value if isinstance(value, (list, tuple)) else [value]
def getfirst(self,key,default=None):
def getfirst(self, key, default=None):
"""
Return the first or only value when given a request.vars-style key.
@@ -93,7 +94,7 @@ class Storage(dict):
values = self.getlist(key)
return values[0] if values else default
def getlast(self,key,default=None):
def getlast(self, key, default=None):
"""
Returns the last or only single value when
given a request.vars-style key.
@@ -115,56 +116,66 @@ class Storage(dict):
values = self.getlist(key)
return values[-1] if values else default
PICKABLE = (str,int,long,float,bool,list,dict,tuple,set)
PICKABLE = (str, int, long, float, bool, list, dict, tuple, set)
class StorageList(Storage):
"""
like Storage but missing elements default to [] instead of None
"""
def __getitem__(self,key):
def __getitem__(self, key):
return self.__getattr__(key)
def __getattr__(self, key):
if key in self:
return getattr(self,key)
return getattr(self, key)
else:
r = []
setattr(self,key,r)
setattr(self, key, r)
return r
def load_storage(filename):
fp = None
try:
fp = portalocker.LockedFile(filename, 'rb')
storage = cPickle.load(fp)
finally:
if fp: fp.close()
if fp:
fp.close()
return Storage(storage)
def save_storage(storage, filename):
fp = None
try:
fp = portalocker.LockedFile(filename, 'wb')
cPickle.dump(dict(storage), fp)
finally:
if fp: fp.close()
if fp:
fp.close()
class Settings(Storage):
def __setattr__(self, key, value):
if key != 'lock_keys' and self['lock_keys'] and key not in self:
raise SyntaxError, 'setting key \'%s\' does not exist' % key
raise SyntaxError('setting key \'%s\' does not exist' % key)
if key != 'lock_values' and self['lock_values']:
raise SyntaxError, 'setting value cannot be changed: %s' % key
raise SyntaxError('setting value cannot be changed: %s' % key)
self[key] = value
class Messages(Settings):
def __init__(self, T):
Storage.__init__(self,T=T)
Storage.__init__(self, T=T)
def __getattr__(self, key):
value = self[key]
if isinstance(value, str):
return str(self.T(value))
return value
class FastStorage(dict):
"""
Eventually this should replace class Storage but causes memory leak
@@ -203,25 +214,33 @@ class FastStorage(dict):
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self.__dict__ = self
def __getattr__(self,key):
return getattr(self,key) if key in self else None
def __getitem__(self,key):
return dict.get(self,key,None)
def __getattr__(self, key):
return getattr(self, key) if key in self else None
def __getitem__(self, key):
return dict.get(self, key, None)
def copy(self):
self.__dict__ = {}
s = FastStorage(self)
self.__dict__ = self
return s
def __repr__(self):
return '<Storage %s>' % dict.__repr__(self)
def __getstate__(self):
return dict(self)
def __setstate__(self, sdict):
dict.__init__(self, sdict)
self.__dict__=self
self.__dict__ = self
def update(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self.__dict__=self
self.__dict__ = self
class List(list):
"""
@@ -235,7 +254,7 @@ class List(list):
request.args(0,default=0,cast=int,otherwise=lambda:...)
"""
n = len(self)
if 0<=i<n or -n<=i<0:
if 0 <= i < n or -n <= i < 0:
value = self[i]
else:
value = default
@@ -246,12 +265,12 @@ class List(list):
from http import HTTP, redirect
if otherwise is None:
raise HTTP(404)
elif isinstance(otherwise,str):
elif isinstance(otherwise, str):
redirect(otherwise)
elif callable(otherwise):
return otherwise()
else:
raise RuntimeError, "invalid otherwise"
raise RuntimeError("invalid otherwise")
return value
+13 -11
View File
@@ -20,9 +20,10 @@ from contenttype import contenttype
regex_start_range = re.compile('\d+(?=\-)')
regex_stop_range = re.compile('(?<=\-)\d+')
DEFAULT_CHUNK_SIZE = 64*1024
DEFAULT_CHUNK_SIZE = 64 * 1024
def streamer(stream, chunk_size = DEFAULT_CHUNK_SIZE, bytes = None):
def streamer(stream, chunk_size=DEFAULT_CHUNK_SIZE, bytes=None):
offset = 0
while bytes is None or offset < bytes:
if not bytes is None and bytes - offset < chunk_size:
@@ -38,14 +39,15 @@ def streamer(stream, chunk_size = DEFAULT_CHUNK_SIZE, bytes = None):
offset += length
stream.close()
def stream_file_or_304_or_206(
static_file,
chunk_size = DEFAULT_CHUNK_SIZE,
request = None,
headers = {},
status = 200,
error_message = None,
):
chunk_size=DEFAULT_CHUNK_SIZE,
request=None,
headers={},
status=200,
error_message=None,
):
if error_message is None:
error_message = rewrite.THREAD_LOCAL.routes.error_message % 'invalid request'
try:
@@ -62,7 +64,7 @@ def stream_file_or_304_or_206(
stat_file = os.stat(static_file)
fsize = stat_file[stat.ST_SIZE]
modified = stat_file[stat.ST_MTIME]
mtime = time.strftime('%a, %d %b %Y %H:%M:%S GMT',time.gmtime(modified))
mtime = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(modified))
headers.setdefault('Content-Type', contenttype(static_file))
headers.setdefault('Last-Modified', mtime)
headers.setdefault('Pragma', 'cache')
@@ -95,10 +97,10 @@ def stream_file_or_304_or_206(
status = 206
# in all the other cases (not 304, not 206, but 200 or error page)
if status != 206:
enc = request.env.http_accept_encoding
enc = request.env.http_accept_encoding
if enc and 'gzip' in enc and not 'Content-Encoding' in headers:
gzipped = static_file + '.gz'
if os.path.isfile(gzipped) and os.path.getmtime(gzipped)>modified:
if os.path.isfile(gzipped) and os.path.getmtime(gzipped) > modified:
static_file = gzipped
fsize = os.path.getsize(gzipped)
headers['Content-Encoding'] = 'gzip'
+99 -77
View File
@@ -26,23 +26,26 @@ try:
except ImportError:
# do not have web2py
current = None
def RestrictedError(a,b,c):
logging.error(str(a)+':'+str(b)+':'+str(c))
def RestrictedError(a, b, c):
logging.error(str(a) + ':' + str(b) + ':' + str(c))
return RuntimeError
class Node(object):
"""
Basic Container Object
"""
def __init__(self, value = None, pre_extend = False):
def __init__(self, value=None, pre_extend=False):
self.value = value
self.pre_extend = pre_extend
def __str__(self):
return str(self.value)
class SuperNode(Node):
def __init__(self, name = '', pre_extend = False):
def __init__(self, name='', pre_extend=False):
self.name = name
self.value = None
self.pre_extend = pre_extend
@@ -57,7 +60,8 @@ class SuperNode(Node):
def __repr__(self):
return "%s->%s" % (self.name, self.value)
def output_aux(node,blocks):
def output_aux(node, blocks):
# If we have a block level
# If we can override this block.
# Override block from vars.
@@ -66,8 +70,9 @@ def output_aux(node,blocks):
return (blocks[node.name].output(blocks)
if node.name in blocks else
node.output(blocks)) \
if isinstance(node, BlockNode) \
else str(node)
if isinstance(node, BlockNode) \
else str(node)
class BlockNode(Node):
"""
@@ -82,7 +87,7 @@ class BlockNode(Node):
This is default block test
{{ end }}
"""
def __init__(self, name = '', pre_extend = False, delimiters = ('{{','}}')):
def __init__(self, name='', pre_extend=False, delimiters=('{{', '}}')):
"""
name - Name of this Node.
"""
@@ -92,7 +97,7 @@ class BlockNode(Node):
self.left, self.right = delimiters
def __repr__(self):
lines = ['%sblock %s%s' % (self.left,self.name,self.right)]
lines = ['%sblock %s%s' % (self.left, self.name, self.right)]
lines += [str(node) for node in self.nodes]
lines.append('%send%s' % (self.left, self.right))
return ''.join(lines)
@@ -101,8 +106,8 @@ class BlockNode(Node):
"""
Get this BlockNodes content, not including child Nodes
"""
return ''.join(str(node) for node in self.nodes \
if not isinstance(node, BlockNode))
return ''.join(str(node) for node in self.nodes
if not isinstance(node, BlockNode))
def append(self, node):
"""
@@ -128,8 +133,8 @@ class BlockNode(Node):
if isinstance(other, BlockNode):
self.nodes.extend(other.nodes)
else:
raise TypeError("Invalid type; must be instance of ``BlockNode``. %s" % other)
raise TypeError(
"Invalid type; must be instance of ``BlockNode``. %s" % other)
def output(self, blocks):
"""
@@ -137,7 +142,8 @@ class BlockNode(Node):
blocks -- Dictionary of blocks that are extending
from this template.
"""
return ''.join(output_aux(node,blocks) for node in self.nodes)
return ''.join(output_aux(node, blocks) for node in self.nodes)
class Content(BlockNode):
"""
@@ -145,7 +151,7 @@ class Content(BlockNode):
Contains functions that operate as such.
"""
def __init__(self, name = "ContentBlock", pre_extend = False):
def __init__(self, name="ContentBlock", pre_extend=False):
"""
Keyword Arguments
@@ -157,18 +163,19 @@ class Content(BlockNode):
self.pre_extend = pre_extend
def __str__(self):
return ''.join(output_aux(node,self.blocks) for node in self.nodes)
return ''.join(output_aux(node, self.blocks) for node in self.nodes)
def _insert(self, other, index = 0):
def _insert(self, other, index=0):
"""
Inserts object at index.
"""
if isinstance(other, (str, Node)):
self.nodes.insert(index, other)
else:
raise TypeError("Invalid type, must be instance of ``str`` or ``Node``.")
raise TypeError(
"Invalid type, must be instance of ``str`` or ``Node``.")
def insert(self, other, index = 0):
def insert(self, other, index=0):
"""
Inserts object at index.
@@ -201,21 +208,23 @@ class Content(BlockNode):
self.nodes.extend(other.nodes)
self.blocks.update(other.blocks)
else:
raise TypeError("Invalid type; must be instance of ``BlockNode``. %s" % other)
raise TypeError(
"Invalid type; must be instance of ``BlockNode``. %s" % other)
def clear_content(self):
self.nodes = []
class TemplateParser(object):
default_delimiters = ('{{','}}')
default_delimiters = ('{{', '}}')
r_tag = compile(r'(\{\{.*?\}\})', DOTALL)
r_multiline = compile(r'(""".*?""")|(\'\'\'.*?\'\'\')', DOTALL)
# These are used for re-indentation.
# Indent + 1
re_block = compile('^(elif |else:|except:|except |finally:).*$',DOTALL)
re_block = compile('^(elif |else:|except:|except |finally:).*$', DOTALL)
# Indent - 1
re_unblock = compile('^(return|continue|break|raise)( .*)?$', DOTALL)
@@ -223,12 +232,12 @@ class TemplateParser(object):
re_pass = compile('^pass( .*)?$', DOTALL)
def __init__(self, text,
name = "ParserContainer",
context = dict(),
path = 'views/',
writer = 'response.write',
lexers = {},
delimiters = ('{{','}}'),
name="ParserContainer",
context=dict(),
path='views/',
writer='response.write',
lexers={},
delimiters=('{{', '}}'),
_super_nodes = [],
):
"""
@@ -270,7 +279,7 @@ class TemplateParser(object):
escaped_delimiters = (escape(delimiters[0]),
escape(delimiters[1]))
self.r_tag = compile(r'(%s.*?%s)' % escaped_delimiters, DOTALL)
elif hasattr(context.get('response',None),'delimiters'):
elif hasattr(context.get('response', None), 'delimiters'):
if context['response'].delimiters != self.default_delimiters:
escaped_delimiters = (
escape(context['response'].delimiters[0]),
@@ -359,10 +368,10 @@ class TemplateParser(object):
k = k + credit - 1
# We obviously can't have a negative indentation
k = max(k,0)
k = max(k, 0)
# Add the indentation!
new_lines.append(' '*(4*k)+line)
new_lines.append(' ' * (4 * k) + line)
# Bank account back to 0 again :(
credit = 0
@@ -416,7 +425,7 @@ class TemplateParser(object):
# Allow Views to include other views dynamically
context = self.context
if current and not "response" in context:
context["response"] = getattr(current,'response',None)
context["response"] = getattr(current, 'response', None)
# Get the filename; filename looks like ``"template.html"``.
# We need to eval to remove the quotes and get the string type.
@@ -442,11 +451,11 @@ class TemplateParser(object):
text = self._get_file_text(filename)
t = TemplateParser(text,
name = filename,
context = self.context,
path = self.path,
writer = self.writer,
delimiters = self.delimiters)
name=filename,
context=self.context,
path=self.path,
writer=self.writer,
delimiters=self.delimiters)
content.append(t.content)
@@ -465,16 +474,17 @@ class TemplateParser(object):
super_nodes.extend(self.super_nodes)
t = TemplateParser(text,
name = filename,
context = self.context,
path = self.path,
writer = self.writer,
delimiters = self.delimiters,
_super_nodes = super_nodes)
name=filename,
context=self.context,
path=self.path,
writer=self.writer,
delimiters=self.delimiters,
_super_nodes=super_nodes)
# Make a temporary buffer that is unique for parent
# template.
buf = BlockNode(name='__include__' + filename, delimiters=self.delimiters)
buf = BlockNode(
name='__include__' + filename, delimiters=self.delimiters)
pre = []
# Iterate through each of our nodes
@@ -601,22 +611,22 @@ class TemplateParser(object):
# You can define custom names such as
# '{{<<variable}}' which could potentially
# write unescaped version of the variable.
self.lexers[name](parser = self,
value = value,
top = top,
stack = stack)
self.lexers[name](parser=self,
value=value,
top=top,
stack=stack)
elif name == '=':
# So we have a variable to insert into
# the template
buf = "\n%s(%s)" % (self.writer, value)
top.append(Node(buf, pre_extend = pre_extend))
top.append(Node(buf, pre_extend=pre_extend))
elif name == 'block' and not value.startswith('='):
# Make a new node with name.
node = BlockNode(name = value.strip(),
pre_extend = pre_extend,
delimiters = self.delimiters)
node = BlockNode(name=value.strip(),
pre_extend=pre_extend,
delimiters=self.delimiters)
# Append this node to our active node
top.append(node)
@@ -646,8 +656,8 @@ class TemplateParser(object):
target_node = top.name
# Create a SuperNode instance
node = SuperNode(name = target_node,
pre_extend = pre_extend)
node = SuperNode(name=target_node,
pre_extend=pre_extend)
# Add this to our list to be taken care of
self.super_nodes.append(node)
@@ -663,9 +673,10 @@ class TemplateParser(object):
# Otherwise, make a temporary include node
# That the child node will know to hook into.
else:
include_node = BlockNode(name = '__include__' + self.name,
pre_extend = pre_extend,
delimiters = self.delimiters)
include_node = BlockNode(
name='__include__' + self.name,
pre_extend=pre_extend,
delimiters=self.delimiters)
top.append(include_node)
elif name == 'extend' and not value.startswith('='):
@@ -697,21 +708,22 @@ class TemplateParser(object):
if token.startswith('='):
if token.endswith('\\'):
continuation = True
tokens[k] = "\n%s(%s" % (self.writer, token[1:].strip())
tokens[k] = "\n%s(%s" % (
self.writer, token[1:].strip())
else:
tokens[k] = "\n%s(%s)" % (self.writer, token[1:].strip())
tokens[k] = "\n%s(%s)" % (
self.writer, token[1:].strip())
elif continuation:
tokens[k] += ')'
continuation = False
buf = "\n%s" % '\n'.join(tokens)
top.append(Node(buf, pre_extend = pre_extend))
top.append(Node(buf, pre_extend=pre_extend))
else:
# It is HTML so just include it.
buf = "\n%s(%r, escape=False)" % (self.writer, i)
top.append(Node(buf, pre_extend = pre_extend))
top.append(Node(buf, pre_extend=pre_extend))
# Remember: tag, not tag, tag, not tag
in_tag = not in_tag
@@ -740,11 +752,13 @@ class TemplateParser(object):
self.extend(extend)
# We need this for integration with gluon
def parse_template(filename,
path = 'views/',
context = dict(),
lexers = {},
delimiters = ('{{','}}')
path='views/',
context=dict(),
lexers={},
delimiters=('{{', '}}')
):
"""
filename can be a view filename in the views folder or an input stream
@@ -766,6 +780,7 @@ def parse_template(filename,
# Use the file contents to get a parsed template and return it.
return str(TemplateParser(text, context=context, path=path, lexers=lexers, delimiters=delimiters))
def get_parsed(text):
"""
Returns the indented python code of text. Useful for unit testing.
@@ -773,13 +788,15 @@ def get_parsed(text):
"""
return str(TemplateParser(text))
class DummyResponse():
def __init__(self):
self.body = cStringIO.StringIO()
def write(self, data, escape=True):
if not escape:
self.body.write(str(data))
elif hasattr(data,'xml') and callable(data.xml):
elif hasattr(data, 'xml') and callable(data.xml):
self.body.write(data.xml())
else:
# make it a string
@@ -787,27 +804,31 @@ class DummyResponse():
data = str(data)
elif isinstance(data, unicode):
data = data.encode('utf8', 'xmlcharrefreplace')
data = cgi.escape(data, True).replace("'","&#x27;")
data = cgi.escape(data, True).replace("'", "&#x27;")
self.body.write(data)
class NOESCAPE():
"""
A little helper to avoid escaping.
"""
def __init__(self, text):
self.text = text
def xml(self):
return self.text
# And this is a generic render function.
# Here for integration with gluon.
def render(content = "hello world",
stream = None,
filename = None,
path = None,
context = {},
lexers = {},
delimiters = ('{{','}}')
def render(content="hello world",
stream=None,
filename=None,
path=None,
context={},
lexers={},
delimiters=('{{', '}}')
):
"""
>>> 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:
+3 -2
View File
@@ -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()
+3 -3
View File
@@ -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()
+5 -6
View File
@@ -130,7 +130,7 @@ class TestBareHelpers(unittest.TestCase):
def testOPTION(self):
self.assertEqual(OPTION('<>', _a='1', _b='2').xml(),
'<option a="1" b="2" value="&lt;&gt;">&lt;&gt;' + \
'<option a="1" b="2" value="&lt;&gt;">&lt;&gt;' +
'</option>')
def testP(self):
@@ -149,7 +149,7 @@ class TestBareHelpers(unittest.TestCase):
def testSELECT(self):
self.assertEqual(SELECT('<>', _a='1', _b='2').xml(),
'<select a="1" b="2">'+ \
'<select a="1" b="2">' +
'<option value="&lt;&gt;">&lt;&gt;</option></select>')
def testSPAN(self):
@@ -162,7 +162,7 @@ class TestBareHelpers(unittest.TestCase):
def testTABLE(self):
self.assertEqual(TABLE('<>', _a='1', _b='2').xml(),
'<table a="1" b="2"><tr><td>&lt;&gt;</td></tr>' + \
'<table a="1" b="2"><tr><td>&lt;&gt;</td></tr>' +
'</table>')
def testTBODY(self):
@@ -175,8 +175,8 @@ class TestBareHelpers(unittest.TestCase):
def testTEXTAREA(self):
self.assertEqual(TEXTAREA('<>', _a='1', _b='2').xml(),
'<textarea a="1" b="2" cols="40" rows="10">&lt;&gt;' + \
'</textarea>')
'<textarea a="1" b="2" cols="40" rows="10">&lt;&gt;' +
'</textarea>')
def testTFOOT(self):
self.assertEqual(TFOOT('<>', _a='1', _b='2').xml(),
@@ -209,4 +209,3 @@ class TestBareHelpers(unittest.TestCase):
if __name__ == '__main__':
unittest.main()
+93 -63
View File
@@ -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()
+3 -5
View File
@@ -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()
+1 -1
View File
@@ -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()
+868 -513
View File
File diff suppressed because it is too large Load Diff
+172 -91
View File
@@ -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()
-3
View File
@@ -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()
+20 -12
View File
@@ -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='{{="<&>"}}'), '&lt;&amp;&gt;')
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()
-1
View File
@@ -25,4 +25,3 @@ class TestUtils(unittest.TestCase):
if __name__ == '__main__':
unittest.main()
+13 -12
View File
@@ -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()

Some files were not shown because too many files have changed in this diff Show More