many pep8 improvements
This commit is contained in:
1 parent
71ec15190b
commit
6aa5edc7ff
105 files changed
+2999
-1509
No files matched your search
@@ -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 +0,0 @@
|
|||||||
|
|
||||||
+35
-15
@@ -20,6 +20,7 @@ sys.path = [path]+[p for p in sys.path if not p==path]
|
|||||||
import gluon.main
|
import gluon.main
|
||||||
from gluon.fileutils import read_file, write_file
|
from gluon.fileutils import read_file, write_file
|
||||||
|
|
||||||
|
|
||||||
class Servers:
|
class Servers:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def cgi(app, address=None, **options):
|
def cgi(app, address=None, **options):
|
||||||
@@ -34,8 +35,10 @@ class Servers:
|
|||||||
@staticmethod
|
@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
|
from wsgiref.simple_server import make_server, WSGIRequestHandler
|
||||||
|
|
||||||
class QuietHandler(WSGIRequestHandler):
|
class QuietHandler(WSGIRequestHandler):
|
||||||
def log_request(*args, **kw): pass
|
def log_request(*args, **kw):
|
||||||
|
pass
|
||||||
options['handler_class'] = QuietHandler
|
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()
|
srv.serve_forever()
|
||||||
@@ -80,19 +83,21 @@ class Servers:
|
|||||||
from fapws import base
|
from fapws import base
|
||||||
evwsgi.start(address[0], str(address[1]))
|
evwsgi.start(address[0], str(address[1]))
|
||||||
evwsgi.set_base_module(base)
|
evwsgi.set_base_module(base)
|
||||||
|
|
||||||
def app(environ, start_response):
|
def app(environ, start_response):
|
||||||
environ['wsgi.multiprocess'] = False
|
environ['wsgi.multiprocess'] = False
|
||||||
return app(environ, start_response)
|
return app(environ, start_response)
|
||||||
evwsgi.wsgi_cb(('', app))
|
evwsgi.wsgi_cb(('', app))
|
||||||
evwsgi.run()
|
evwsgi.run()
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def gevent(app, address, **options):
|
def gevent(app, address, **options):
|
||||||
from gevent import monkey; monkey.patch_all()
|
from gevent import monkey
|
||||||
|
monkey.patch_all()
|
||||||
from gevent import pywsgi
|
from gevent import pywsgi
|
||||||
from gevent.pool import Pool
|
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
|
@staticmethod
|
||||||
def bjoern(app, address, **options):
|
def bjoern(app, address, **options):
|
||||||
@@ -133,9 +138,11 @@ class Servers:
|
|||||||
config = {'bind': "%s:%d" % address}
|
config = {'bind': "%s:%d" % address}
|
||||||
config.update(options)
|
config.update(options)
|
||||||
sys.argv = ['anyserver.py']
|
sys.argv = ['anyserver.py']
|
||||||
|
|
||||||
class GunicornApplication(Application):
|
class GunicornApplication(Application):
|
||||||
def init(self, parser, opts, args):
|
def init(self, parser, opts, args):
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
return app
|
return app
|
||||||
g = GunicornApplication()
|
g = GunicornApplication()
|
||||||
@@ -169,6 +176,7 @@ def run(servername,ip,port,softcron=True,logging=False,profiler=None):
|
|||||||
global_settings.web2py_crontype = 'soft'
|
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 :
|
Based on :
|
||||||
@@ -194,14 +202,17 @@ def mongrel2_handler(application,conn,debug=False):
|
|||||||
# and responses. Unless I have missed something.
|
# and responses. Unless I have missed something.
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
if debug: print "WAITING FOR REQUEST"
|
if debug:
|
||||||
|
print "WAITING FOR REQUEST"
|
||||||
|
|
||||||
# receive a request
|
# receive a request
|
||||||
req = conn.recv()
|
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 req.is_disconnect():
|
||||||
if debug: print "DISCONNECT"
|
if debug:
|
||||||
|
print "DISCONNECT"
|
||||||
continue # effectively ignore the disconnect from the client
|
continue # effectively ignore the disconnect from the client
|
||||||
|
|
||||||
# Set a couple of environment attributes a.k.a. header attributes
|
# Set a couple of environment attributes a.k.a. header attributes
|
||||||
@@ -222,10 +233,12 @@ def mongrel2_handler(application,conn,debug=False):
|
|||||||
else:
|
else:
|
||||||
environ['QUERY_STRING'] = ''
|
environ['QUERY_STRING'] = ''
|
||||||
if 'Content-Length' in environ:
|
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
|
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
|
# SimpleHandler needs file-like stream objects for
|
||||||
# requests, errors and responses
|
# requests, errors and responses
|
||||||
@@ -234,7 +247,8 @@ def mongrel2_handler(application,conn,debug=False):
|
|||||||
respIO = StringIO.StringIO()
|
respIO = StringIO.StringIO()
|
||||||
|
|
||||||
# execute the application
|
# 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)
|
handler.run(application)
|
||||||
|
|
||||||
# Get the response and filter out the response (=data) itself,
|
# Get the response and filter out the response (=data) itself,
|
||||||
@@ -258,11 +272,15 @@ def mongrel2_handler(application,conn,debug=False):
|
|||||||
errors = errIO.getvalue()
|
errors = errIO.getvalue()
|
||||||
|
|
||||||
# return the response
|
# return the response
|
||||||
if debug: print "RESPONSE: %r\n" % response
|
if debug:
|
||||||
|
print "RESPONSE: %r\n" % response
|
||||||
if errors:
|
if errors:
|
||||||
if debug: print "ERRORS: %r" % errors
|
if debug:
|
||||||
|
print "ERRORS: %r" % errors
|
||||||
data = "%s\r\n\r\n%s" % (data, 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():
|
def main():
|
||||||
usage = "python anyserver.py -s tornado -i 127.0.0.1 -p 8000 -l -P"
|
usage = "python anyserver.py -s tornado -i 127.0.0.1 -p 8000 -l -P"
|
||||||
@@ -304,8 +322,10 @@ def main():
|
|||||||
dest='workers',
|
dest='workers',
|
||||||
help='number of workers number')
|
help='number of workers number')
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
print 'starting %s on %s:%s...' % (options.server,options.ip,options.port)
|
print 'starting %s on %s:%s...' % (
|
||||||
run(options.server,options.ip,options.port,logging=options.logging,profiler=options.profiler)
|
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()
|
main()
|
||||||
@@ -91,6 +91,7 @@ def wsgiapp(env, res):
|
|||||||
if LOG_STATS or DEBUG:
|
if LOG_STATS or DEBUG:
|
||||||
wsgiapp = log_stats(wsgiapp)
|
wsgiapp = log_stats(wsgiapp)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Run the wsgi app"""
|
"""Run the wsgi app"""
|
||||||
run_wsgi_app(wsgiapp)
|
run_wsgi_app(wsgiapp)
|
||||||
|
|||||||
+17
-4
@@ -23,6 +23,7 @@ from http import HTTP
|
|||||||
if not global_settings.web2py_runtime_gae:
|
if not global_settings.web2py_runtime_gae:
|
||||||
import site
|
import site
|
||||||
|
|
||||||
|
|
||||||
def apath(path='', r=None):
|
def apath(path='', r=None):
|
||||||
"""
|
"""
|
||||||
Builds a path inside an application folder
|
Builds a path inside an application folder
|
||||||
@@ -95,6 +96,7 @@ def app_pack_compiled(app, request, raise_ex=False):
|
|||||||
raise
|
raise
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def app_cleanup(app, request):
|
def app_cleanup(app, request):
|
||||||
"""
|
"""
|
||||||
Removes session, cache and error files
|
Removes session, cache and error files
|
||||||
@@ -158,6 +160,7 @@ def app_compile(app, request):
|
|||||||
remove_compiled_application(folder)
|
remove_compiled_application(folder)
|
||||||
return tb
|
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
|
Create a copy of welcome.w2p (scaffolding) app
|
||||||
@@ -283,6 +286,7 @@ def app_uninstall(app, request):
|
|||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def plugin_pack(app, plugin_name, request):
|
def plugin_pack(app, plugin_name, request):
|
||||||
"""
|
"""
|
||||||
Builds a w2p package for the application
|
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
|
filename of the w2p file or None on error
|
||||||
"""
|
"""
|
||||||
try:
|
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)
|
w2p_pack_plugin(filename, apath(app, request), plugin_name)
|
||||||
return filename
|
return filename
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def plugin_install(app, fobj, request, filename):
|
def plugin_install(app, fobj, request, filename):
|
||||||
"""
|
"""
|
||||||
Installs an application:
|
Installs an application:
|
||||||
@@ -345,6 +351,7 @@ def plugin_install(app, fobj, request, filename):
|
|||||||
os.unlink(upname)
|
os.unlink(upname)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def check_new_version(myversion, version_URL):
|
def check_new_version(myversion, version_URL):
|
||||||
"""
|
"""
|
||||||
Compares current web2py's version with the latest stable web2py version.
|
Compares current web2py's version with the latest stable web2py version.
|
||||||
@@ -375,6 +382,7 @@ def check_new_version(myversion, version_URL):
|
|||||||
else:
|
else:
|
||||||
return False, version
|
return False, version
|
||||||
|
|
||||||
|
|
||||||
def unzip(filename, dir, subfolder=''):
|
def unzip(filename, dir, subfolder=''):
|
||||||
"""
|
"""
|
||||||
Unzips filename into dir (.zip only, no .gz etc)
|
Unzips filename into dir (.zip only, no .gz etc)
|
||||||
@@ -382,7 +390,7 @@ def unzip(filename, dir, subfolder=''):
|
|||||||
"""
|
"""
|
||||||
filename = abspath(filename)
|
filename = abspath(filename)
|
||||||
if not zipfile.is_zipfile(filename):
|
if not zipfile.is_zipfile(filename):
|
||||||
raise RuntimeError, 'Not a valid zipfile'
|
raise RuntimeError('Not a valid zipfile')
|
||||||
zf = zipfile.ZipFile(filename)
|
zf = zipfile.ZipFile(filename)
|
||||||
if not subfolder.endswith('/'):
|
if not subfolder.endswith('/'):
|
||||||
subfolder = subfolder + '/'
|
subfolder = subfolder + '/'
|
||||||
@@ -450,20 +458,25 @@ def upgrade(request, url='http://web2py.com'):
|
|||||||
except Exception, e:
|
except Exception, e:
|
||||||
return False, e
|
return False, e
|
||||||
|
|
||||||
|
|
||||||
def add_path_first(path):
|
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:
|
if not global_settings.web2py_runtime_gae:
|
||||||
site.addsitedir(path)
|
site.addsitedir(path)
|
||||||
|
|
||||||
|
|
||||||
def create_missing_folders():
|
def create_missing_folders():
|
||||||
if not global_settings.web2py_runtime_gae:
|
if not global_settings.web2py_runtime_gae:
|
||||||
for path in ('applications', 'deposit', 'site-packages', 'logs'):
|
for path in ('applications', 'deposit', 'site-packages', 'logs'):
|
||||||
path = abspath(path, gluon=True)
|
path = abspath(path, gluon=True)
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
os.mkdir(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]
|
[add_path_first(path) for path in paths]
|
||||||
|
|
||||||
|
|
||||||
def create_missing_app_folders(request):
|
def create_missing_app_folders(request):
|
||||||
if not global_settings.web2py_runtime_gae:
|
if not global_settings.web2py_runtime_gae:
|
||||||
if request.folder not in global_settings.app_folders:
|
if request.folder not in global_settings.app_folders:
|
||||||
|
|||||||
+20
-8
@@ -40,6 +40,7 @@ __all__ = ['Cache', 'lazy_cache']
|
|||||||
|
|
||||||
DEFAULT_TIME_EXPIRE = 300
|
DEFAULT_TIME_EXPIRE = 300
|
||||||
|
|
||||||
|
|
||||||
class CacheAbstract(object):
|
class CacheAbstract(object):
|
||||||
"""
|
"""
|
||||||
Abstract class for cache implementations.
|
Abstract class for cache implementations.
|
||||||
@@ -130,6 +131,7 @@ class CacheAbstract(object):
|
|||||||
if r.match(str(key)):
|
if r.match(str(key)):
|
||||||
del storage[key]
|
del storage[key]
|
||||||
|
|
||||||
|
|
||||||
class CacheInRam(CacheAbstract):
|
class CacheInRam(CacheAbstract):
|
||||||
"""
|
"""
|
||||||
Ram based caching
|
Ram based caching
|
||||||
@@ -147,8 +149,10 @@ class CacheInRam(CacheAbstract):
|
|||||||
self.request = request
|
self.request = request
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
if self.initialized: return
|
if self.initialized:
|
||||||
else: self.initialized = True
|
return
|
||||||
|
else:
|
||||||
|
self.initialized = True
|
||||||
self.locker.acquire()
|
self.locker.acquire()
|
||||||
request = self.request
|
request = self.request
|
||||||
if request:
|
if request:
|
||||||
@@ -172,7 +176,8 @@ class CacheInRam(CacheAbstract):
|
|||||||
self._clear(storage, regex)
|
self._clear(storage, regex)
|
||||||
|
|
||||||
if not CacheAbstract.cache_stats_name in storage.keys():
|
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()
|
self.locker.release()
|
||||||
|
|
||||||
@@ -269,14 +274,15 @@ class CacheOnDisk(CacheAbstract):
|
|||||||
try:
|
try:
|
||||||
storage = shelve.open(self.shelve_name)
|
storage = shelve.open(self.shelve_name)
|
||||||
except:
|
except:
|
||||||
logger.error('corrupted cache file %s, will try rebuild it' \
|
logger.error('corrupted cache file %s, will try rebuild it'
|
||||||
% (self.shelve_name))
|
% (self.shelve_name))
|
||||||
storage = None
|
storage = None
|
||||||
if not storage and os.path.exists(self.shelve_name):
|
if not storage and os.path.exists(self.shelve_name):
|
||||||
os.unlink(self.shelve_name)
|
os.unlink(self.shelve_name)
|
||||||
storage = shelve.open(self.shelve_name)
|
storage = shelve.open(self.shelve_name)
|
||||||
if not CacheAbstract.cache_stats_name in storage.keys():
|
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()
|
storage.sync()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if storage:
|
if storage:
|
||||||
@@ -286,7 +292,8 @@ class CacheOnDisk(CacheAbstract):
|
|||||||
portalocker.unlock(locker)
|
portalocker.unlock(locker)
|
||||||
locker.close()
|
locker.close()
|
||||||
locked = False
|
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.locker = locker
|
||||||
self.locked = locked
|
self.locked = locked
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
@@ -298,8 +305,10 @@ class CacheOnDisk(CacheAbstract):
|
|||||||
self.folder = folder
|
self.folder = folder
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
if self.initialized: return
|
if self.initialized:
|
||||||
else: self.initialized = True
|
return
|
||||||
|
else:
|
||||||
|
self.initialized = True
|
||||||
folder = self.folder
|
folder = self.folder
|
||||||
request = self.request
|
request = self.request
|
||||||
|
|
||||||
@@ -365,6 +374,7 @@ class CacheOnDisk(CacheAbstract):
|
|||||||
self._close_shelve_and_unlock()
|
self._close_shelve_and_unlock()
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
class CacheAction(object):
|
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.__name__ = func.__name__
|
||||||
@@ -374,6 +384,7 @@ class CacheAction(object):
|
|||||||
self.time_expire = time_expire
|
self.time_expire = time_expire
|
||||||
self.cache = cache
|
self.cache = cache
|
||||||
self.cache_model = cache_model
|
self.cache_model = cache_model
|
||||||
|
|
||||||
def __call__(self, *a, **b):
|
def __call__(self, *a, **b):
|
||||||
if not self.key:
|
if not self.key:
|
||||||
key2 = self.__name__ + ':' + repr(a) + ':' + repr(b)
|
key2 = self.__name__ + ':' + repr(a) + ':' + repr(b)
|
||||||
@@ -483,6 +494,7 @@ def lazy_cache(key=None,time_expire=None,cache_model='ram'):
|
|||||||
"""
|
"""
|
||||||
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)
|
key = key or repr(f)
|
||||||
|
|
||||||
def g(*c, **d):
|
def g(*c, **d):
|
||||||
from gluon import current
|
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)
|
||||||
|
|||||||
+27
-13
@@ -95,6 +95,7 @@ _TEST()
|
|||||||
CACHED_REGEXES = {}
|
CACHED_REGEXES = {}
|
||||||
CACHED_REGEXES_MAX_SIZE = 1000
|
CACHED_REGEXES_MAX_SIZE = 1000
|
||||||
|
|
||||||
|
|
||||||
def re_compile(regex):
|
def re_compile(regex):
|
||||||
try:
|
try:
|
||||||
return CACHED_REGEXES[regex]
|
return CACHED_REGEXES[regex]
|
||||||
@@ -104,6 +105,7 @@ def re_compile(regex):
|
|||||||
compiled_regex = CACHED_REGEXES[regex] = re.compile(regex)
|
compiled_regex = CACHED_REGEXES[regex] = re.compile(regex)
|
||||||
return compiled_regex
|
return compiled_regex
|
||||||
|
|
||||||
|
|
||||||
class mybuiltin(object):
|
class mybuiltin(object):
|
||||||
"""
|
"""
|
||||||
NOTE could simple use a dict and populate it,
|
NOTE could simple use a dict and populate it,
|
||||||
@@ -114,10 +116,12 @@ class mybuiltin(object):
|
|||||||
try:
|
try:
|
||||||
return getattr(__builtin__, key)
|
return getattr(__builtin__, key)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise KeyError, key
|
raise KeyError(key)
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
|
||||||
def LOAD(c=None, f='index', args=None, vars=None,
|
def LOAD(c=None, f='index', args=None, vars=None,
|
||||||
extension=None, target=None, ajax=False, ajax_trap=False,
|
extension=None, target=None, ajax=False, ajax_trap=False,
|
||||||
url=None, user_signature=False, timeout=None, times=1,
|
url=None, user_signature=False, timeout=None, times=1,
|
||||||
@@ -134,7 +138,8 @@ def LOAD(c=None, f='index', args=None, vars=None,
|
|||||||
is added on page loading without delay.
|
is added on page loading without delay.
|
||||||
"""
|
"""
|
||||||
from html import TAG, DIV, URL, SCRIPT, XML
|
from html import TAG, DIV, URL, SCRIPT, XML
|
||||||
if args is None: args = []
|
if args is None:
|
||||||
|
args = []
|
||||||
vars = Storage(vars or {})
|
vars = Storage(vars or {})
|
||||||
target = target or 'c' + str(random.random())[2:]
|
target = target or 'c' + str(random.random())[2:]
|
||||||
attr['_id'] = target
|
attr['_id'] = target
|
||||||
@@ -160,7 +165,8 @@ def LOAD(c=None, f='index', args=None, vars=None,
|
|||||||
if not isinstance(timeout, (int, long)):
|
if not isinstance(timeout, (int, long)):
|
||||||
raise ValueError("Timeout argument must be an integer or None")
|
raise ValueError("Timeout argument must be an integer or None")
|
||||||
elif timeout <= 0:
|
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);" \
|
statement = "web2py_component('%s','%s', %s, %s);" \
|
||||||
% (url, target, timeout, times)
|
% (url, target, timeout, times)
|
||||||
else:
|
else:
|
||||||
@@ -186,7 +192,7 @@ def LOAD(c=None, f='index', args=None, vars=None,
|
|||||||
other_request.post_vars = Storage()
|
other_request.post_vars = Storage()
|
||||||
other_response = Response()
|
other_response = Response()
|
||||||
other_request.env.path_info = '/' + \
|
other_request.env.path_info = '/' + \
|
||||||
'/'.join([request.application,c,f] + \
|
'/'.join([request.application, c, f] +
|
||||||
map(str, other_request.args))
|
map(str, other_request.args))
|
||||||
other_request.env.query_string = \
|
other_request.env.query_string = \
|
||||||
vars and URL(vars=vars).split('?')[1] or ''
|
vars and URL(vars=vars).split('?')[1] or ''
|
||||||
@@ -196,7 +202,7 @@ def LOAD(c=None, f='index', args=None, vars=None,
|
|||||||
other_request.env.http_web2py_component_element = 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._view_environment = other_environment
|
||||||
other_response.generic_patterns = \
|
other_response.generic_patterns = \
|
||||||
@@ -225,17 +231,18 @@ def LOAD(c=None, f='index', args=None, vars=None,
|
|||||||
return TAG[''](DIV(XML(page), **attr), script)
|
return TAG[''](DIV(XML(page), **attr), script)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class LoadFactory(object):
|
class LoadFactory(object):
|
||||||
"""
|
"""
|
||||||
Attention: this helper is new and experimental
|
Attention: this helper is new and experimental
|
||||||
"""
|
"""
|
||||||
def __init__(self, environment):
|
def __init__(self, environment):
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
|
|
||||||
def __call__(self, c=None, f='index', args=None, vars=None,
|
def __call__(self, c=None, f='index', args=None, vars=None,
|
||||||
extension=None, target=None, ajax=False, ajax_trap=False,
|
extension=None, target=None, ajax=False, ajax_trap=False,
|
||||||
url=None, user_signature=False, content='loading...', **attr):
|
url=None, user_signature=False, content='loading...', **attr):
|
||||||
if args is None: args = []
|
if args is None:
|
||||||
|
args = []
|
||||||
vars = Storage(vars or {})
|
vars = Storage(vars or {})
|
||||||
import globals
|
import globals
|
||||||
target = target or 'c' + str(random.random())[2:]
|
target = target or 'c' + str(random.random())[2:]
|
||||||
@@ -266,7 +273,7 @@ class LoadFactory(object):
|
|||||||
other_request.post_vars = Storage()
|
other_request.post_vars = Storage()
|
||||||
other_response = globals.Response()
|
other_response = globals.Response()
|
||||||
other_request.env.path_info = '/' + \
|
other_request.env.path_info = '/' + \
|
||||||
'/'.join([request.application,c,f] + \
|
'/'.join([request.application, c, f] +
|
||||||
map(str, other_request.args))
|
map(str, other_request.args))
|
||||||
other_request.env.query_string = \
|
other_request.env.query_string = \
|
||||||
vars and html.URL(vars=vars).split('?')[1] or ''
|
vars and html.URL(vars=vars).split('?')[1] or ''
|
||||||
@@ -355,12 +362,14 @@ OLD IMPLEMENTATION:
|
|||||||
file.close()
|
file.close()
|
||||||
imp.release_lock()
|
imp.release_lock()
|
||||||
if not module:
|
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
|
return module
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_base_environment_ = dict((k, getattr(html, k)) for k in html.__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_.update(
|
||||||
|
(k, getattr(validators, k)) for k in validators.__all__)
|
||||||
_base_environment_['__builtins__'] = __builtins__
|
_base_environment_['__builtins__'] = __builtins__
|
||||||
_base_environment_['HTTP'] = HTTP
|
_base_environment_['HTTP'] = HTTP
|
||||||
_base_environment_['redirect'] = redirect
|
_base_environment_['redirect'] = redirect
|
||||||
@@ -372,6 +381,7 @@ _base_environment_['SQLFORM'] = SQLFORM
|
|||||||
_base_environment_['SQLTABLE'] = SQLTABLE
|
_base_environment_['SQLTABLE'] = SQLTABLE
|
||||||
_base_environment_['LOAD'] = LOAD
|
_base_environment_['LOAD'] = LOAD
|
||||||
|
|
||||||
|
|
||||||
def build_environment(request, response, session, store_current=True):
|
def build_environment(request, response, session, store_current=True):
|
||||||
"""
|
"""
|
||||||
Build the environment dictionary into which web2py files are executed.
|
Build the environment dictionary into which web2py files are executed.
|
||||||
@@ -403,7 +413,7 @@ def build_environment(request, response, session, store_current=True):
|
|||||||
elif is_pypy: # apply the same hack to pypy too
|
elif is_pypy: # apply the same hack to pypy too
|
||||||
__builtins__ = mybuiltin()
|
__builtins__ = mybuiltin()
|
||||||
else:
|
else:
|
||||||
__builtins__['__import__'] = __builtin__.__import__ ### WHY?
|
__builtins__['__import__'] = __builtin__.__import__ # WHY?
|
||||||
environment['request'] = request
|
environment['request'] = request
|
||||||
environment['response'] = response
|
environment['response'] = response
|
||||||
environment['session'] = session
|
environment['session'] = session
|
||||||
@@ -415,6 +425,7 @@ def build_environment(request, response, session, store_current=True):
|
|||||||
custom_import_install()
|
custom_import_install()
|
||||||
return environment
|
return environment
|
||||||
|
|
||||||
|
|
||||||
def save_pyc(filename):
|
def save_pyc(filename):
|
||||||
"""
|
"""
|
||||||
Bytecode compiles the file `filename`
|
Bytecode compiles the file `filename`
|
||||||
@@ -431,7 +442,7 @@ def read_pyc(filename):
|
|||||||
"""
|
"""
|
||||||
data = read_file(filename, 'rb')
|
data = read_file(filename, 'rb')
|
||||||
if not is_gae and data[:4] != imp.get_magic():
|
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:])
|
return marshal.loads(data[8:])
|
||||||
|
|
||||||
|
|
||||||
@@ -548,7 +559,8 @@ def run_controller_in(controller, function, environment):
|
|||||||
# TESTING: adjust the path to include site packages
|
# TESTING: adjust the path to include site packages
|
||||||
from settings import global_settings
|
from settings import global_settings
|
||||||
from admin import abspath, add_path_first
|
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]
|
[add_path_first(path) for path in paths]
|
||||||
# TESTING END
|
# TESTING END
|
||||||
|
|
||||||
@@ -590,6 +602,7 @@ def run_controller_in(controller, function, environment):
|
|||||||
vars = vars.xml()
|
vars = vars.xml()
|
||||||
return vars
|
return vars
|
||||||
|
|
||||||
|
|
||||||
def run_view_in(environment):
|
def run_view_in(environment):
|
||||||
"""
|
"""
|
||||||
Executes the view for the requested action.
|
Executes the view for the requested action.
|
||||||
@@ -655,6 +668,7 @@ def run_view_in(environment):
|
|||||||
context=environment)
|
context=environment)
|
||||||
restricted(ccode, environment, layer)
|
restricted(ccode, environment, layer)
|
||||||
|
|
||||||
|
|
||||||
def remove_compiled_application(folder):
|
def remove_compiled_application(folder):
|
||||||
"""
|
"""
|
||||||
Deletes the folder `compiled` containing the compiled application.
|
Deletes the folder `compiled` containing the compiled application.
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import urllib
|
|||||||
|
|
||||||
_known_tuple_types = {}
|
_known_tuple_types = {}
|
||||||
|
|
||||||
|
|
||||||
class NamedTupleBase(tuple):
|
class NamedTupleBase(tuple):
|
||||||
"""Base class for named tuples with the __new__ operator set, named tuples
|
"""Base class for named tuples with the __new__ operator set, named tuples
|
||||||
yielded by the namedtuple() function will subclass this and add
|
yielded by the namedtuple() function will subclass this and add
|
||||||
@@ -37,6 +38,7 @@ class NamedTupleBase(tuple):
|
|||||||
args = tuple(values)
|
args = tuple(values)
|
||||||
return tuple.__new__(cls, args)
|
return tuple.__new__(cls, args)
|
||||||
|
|
||||||
|
|
||||||
def namedtuple(typename, fieldnames):
|
def namedtuple(typename, fieldnames):
|
||||||
"""
|
"""
|
||||||
>>> import namedtuples
|
>>> import namedtuples
|
||||||
@@ -75,24 +77,26 @@ def namedtuple(typename, fieldnames):
|
|||||||
# Done
|
# Done
|
||||||
return new_tuple_type
|
return new_tuple_type
|
||||||
|
|
||||||
|
|
||||||
class AIM:
|
class AIM:
|
||||||
|
|
||||||
class AIMError(Exception):
|
class AIMError(Exception):
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
self.parameter = value
|
self.parameter = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.parameter)
|
return str(self.parameter)
|
||||||
|
|
||||||
def __init__(self, login, transkey, testmode=False):
|
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')
|
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')
|
raise AIM.AIMError('No transaction key provided')
|
||||||
if testmode != True and testmode != False:
|
if testmode != True and testmode != False:
|
||||||
raise AIM.AIMError('Invalid value for testmode. Must be True or False. "{0}" given.'.format(testmode))
|
raise AIM.AIMError('Invalid value for testmode. Must be True or False. "{0}" given.'.format(testmode))
|
||||||
|
|
||||||
self.testmode = testmode
|
self.testmode = testmode
|
||||||
self.proxy = None;
|
self.proxy = None
|
||||||
self.delimiter = '|'
|
self.delimiter = '|'
|
||||||
self.results = []
|
self.results = []
|
||||||
self.error = True
|
self.error = True
|
||||||
@@ -117,8 +121,9 @@ class AIM:
|
|||||||
else:
|
else:
|
||||||
url = 'https://secure.authorize.net/gateway/transact.dll'
|
url = 'https://secure.authorize.net/gateway/transact.dll'
|
||||||
|
|
||||||
if self.proxy == None:
|
if self.proxy is None:
|
||||||
self.results += str(urllib.urlopen(url, encoded_args).read()).split(self.delimiter)
|
self.results += str(urllib.urlopen(
|
||||||
|
url, encoded_args).read()).split(self.delimiter)
|
||||||
else:
|
else:
|
||||||
opener = urllib.FancyURLopener(self.proxy)
|
opener = urllib.FancyURLopener(self.proxy)
|
||||||
opened = opener.open(url, encoded_args)
|
opened = opener.open(url, encoded_args)
|
||||||
@@ -147,36 +152,37 @@ class AIM:
|
|||||||
raise AIM.AIMError(self.response.ResponseText)
|
raise AIM.AIMError(self.response.ResponseText)
|
||||||
|
|
||||||
def setTransaction(self, creditcard, expiration, total, cvv=None, tax=None, invoice=None):
|
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))
|
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))
|
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))
|
raise AIM.AIMError('No total amount passed to setTransaction(): {0}'.format(total))
|
||||||
|
|
||||||
self.setParameter('x_card_num', creditcard)
|
self.setParameter('x_card_num', creditcard)
|
||||||
self.setParameter('x_exp_date', expiration)
|
self.setParameter('x_exp_date', expiration)
|
||||||
self.setParameter('x_amount', total)
|
self.setParameter('x_amount', total)
|
||||||
if cvv != None:
|
if cvv is not None:
|
||||||
self.setParameter('x_card_code', cvv)
|
self.setParameter('x_card_code', cvv)
|
||||||
if tax != None:
|
if tax is not None:
|
||||||
self.setParameter('x_tax', tax)
|
self.setParameter('x_tax', tax)
|
||||||
if invoice != None:
|
if invoice is not None:
|
||||||
self.setParameter('x_invoice_num', invoice)
|
self.setParameter('x_invoice_num', invoice)
|
||||||
|
|
||||||
def setTransactionType(self, transtype=None):
|
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:
|
if transtype.upper() not in types:
|
||||||
raise AIM.AIMError('Incorrect Transaction Type passed to setTransactionType(): {0}'.format(transtype))
|
raise AIM.AIMError('Incorrect Transaction Type passed to setTransactionType(): {0}'.format(transtype))
|
||||||
self.setParameter('x_type', transtype.upper())
|
self.setParameter('x_type', transtype.upper())
|
||||||
|
|
||||||
def setProxy(self, proxy=None):
|
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()')
|
raise AIM.AIMError('No proxy passed to setProxy()')
|
||||||
self.proxy = {'http': str(proxy).strip()}
|
self.proxy = {'http': str(proxy).strip()}
|
||||||
|
|
||||||
def setParameter(self, key=None, value=None):
|
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()
|
self.parameters[key] = str(value).strip()
|
||||||
else:
|
else:
|
||||||
raise AIM.AIMError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value))
|
raise AIM.AIMError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value))
|
||||||
@@ -194,6 +200,7 @@ class AIM:
|
|||||||
responses = ['', 'Approved', 'Declined', 'Error']
|
responses = ['', 'Approved', 'Declined', 'Error']
|
||||||
return responses[int(self.results[0])]
|
return responses[int(self.results[0])]
|
||||||
|
|
||||||
|
|
||||||
def process(creditcard, expiration, total, cvv=None, tax=None, invoice=None,
|
def process(creditcard, expiration, total, cvv=None, tax=None, invoice=None,
|
||||||
login='cnpdev4289', transkey='SR2P8g4jdEn7vFLQ', testmode=True):
|
login='cnpdev4289', transkey='SR2P8g4jdEn7vFLQ', testmode=True):
|
||||||
payment = AIM(login, transkey, testmode)
|
payment = AIM(login, transkey, testmode)
|
||||||
@@ -205,6 +212,7 @@ def process(creditcard,expiration,total,cvv=None,tax=None,invoice=None,
|
|||||||
except AIM.AIMError:
|
except AIM.AIMError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
@@ -219,8 +227,10 @@ def test():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
payment = AIM('cnpdev4289', 'SR2P8g4jdEn7vFLQ', True)
|
payment = AIM('cnpdev4289', 'SR2P8g4jdEn7vFLQ', True)
|
||||||
payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice)
|
payment.setTransaction(
|
||||||
payment.setParameter('x_duplicate_window', 180) # three minutes duplicate windows
|
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_cust_id', '1324') # customer ID
|
||||||
payment.setParameter('x_first_name', 'John')
|
payment.setParameter('x_first_name', 'John')
|
||||||
payment.setParameter('x_last_name', 'Conde')
|
payment.setParameter('x_last_name', 'Conde')
|
||||||
@@ -232,7 +242,8 @@ def test():
|
|||||||
payment.setParameter('x_country', 'US')
|
payment.setParameter('x_country', 'US')
|
||||||
payment.setParameter('x_phone', '800-555-1234')
|
payment.setParameter('x_phone', '800-555-1234')
|
||||||
payment.setParameter('x_description', 'Test Transaction')
|
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', 'john@example.com')
|
||||||
payment.setParameter('x_email_customer', False)
|
payment.setParameter('x_email_customer', False)
|
||||||
payment.process()
|
payment.process()
|
||||||
|
|||||||
@@ -15,25 +15,27 @@ __all__ = ['DowCommerce']
|
|||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
|
|
||||||
class DowCommerce:
|
class DowCommerce:
|
||||||
|
|
||||||
class DowCommerceError(Exception):
|
class DowCommerceError(Exception):
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
self.parameter = value
|
self.parameter = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.parameter)
|
return str(self.parameter)
|
||||||
|
|
||||||
def __init__(self, username=None, password=None, demomode=False):
|
def __init__(self, username=None, password=None, demomode=False):
|
||||||
if not demomode:
|
if not demomode:
|
||||||
if str(username).strip() == '' or username == None:
|
if str(username).strip() == '' or username is None:
|
||||||
raise DowCommerce.DowCommerceError('No username provided')
|
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')
|
raise DowCommerce.DowCommerceError('No password provided')
|
||||||
else:
|
else:
|
||||||
username = 'demo'
|
username = 'demo'
|
||||||
password = 'password'
|
password = 'password'
|
||||||
|
|
||||||
self.proxy = None;
|
self.proxy = None
|
||||||
self.delimiter = '&'
|
self.delimiter = '&'
|
||||||
self.results = {}
|
self.results = {}
|
||||||
self.error = True
|
self.error = True
|
||||||
@@ -45,11 +47,11 @@ class DowCommerce:
|
|||||||
self.setParameter('username', username)
|
self.setParameter('username', username)
|
||||||
self.setParameter('password', password)
|
self.setParameter('password', password)
|
||||||
|
|
||||||
|
|
||||||
def process(self):
|
def process(self):
|
||||||
encoded_args = urllib.urlencode(self.parameters)
|
encoded_args = urllib.urlencode(self.parameters)
|
||||||
if self.proxy == None:
|
if self.proxy is None:
|
||||||
results = str(urllib.urlopen(self.url, encoded_args).read()).split(self.delimiter)
|
results = str(urllib.urlopen(
|
||||||
|
self.url, encoded_args).read()).split(self.delimiter)
|
||||||
else:
|
else:
|
||||||
opener = urllib.FancyURLopener(self.proxy)
|
opener = urllib.FancyURLopener(self.proxy)
|
||||||
opened = opener.open(self.url, encoded_args)
|
opened = opener.open(self.url, encoded_args)
|
||||||
@@ -80,17 +82,18 @@ class DowCommerce:
|
|||||||
self.declined = False
|
self.declined = False
|
||||||
raise DowCommerce.DowCommerceError(self.results)
|
raise DowCommerce.DowCommerceError(self.results)
|
||||||
|
|
||||||
def setTransaction(self, creditcard, expiration, total, cvv=None, orderid=None, orderdescription=None,
|
def setTransaction(
|
||||||
|
self, creditcard, expiration, total, cvv=None, orderid=None, orderdescription=None,
|
||||||
ipaddress=None, tax=None, shipping=None,
|
ipaddress=None, tax=None, shipping=None,
|
||||||
firstname=None, lastname=None, company=None, address1=None, address2=None, city=None, state=None, zipcode=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,
|
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_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):
|
shipping_city=None, shipping_state=None, shipping_zipcode=None, shipping_country=None, shipping_emailaddress=None):
|
||||||
if str(creditcard).strip() == '' or creditcard == None:
|
if str(creditcard).strip() == '' or creditcard is None:
|
||||||
raise DowCommerce.DowCommerceError('No credit card number passed to setTransaction(): {0}'.format(creditcard))
|
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))
|
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))
|
raise DowCommerce.DowCommerceError('No total amount passed to setTransaction(): {0}'.format(total))
|
||||||
|
|
||||||
self.setParameter('ccnumber', creditcard)
|
self.setParameter('ccnumber', creditcard)
|
||||||
@@ -165,12 +168,12 @@ class DowCommerce:
|
|||||||
self.setParameter('type', transtype.lower())
|
self.setParameter('type', transtype.lower())
|
||||||
|
|
||||||
def setProxy(self, proxy=None):
|
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()')
|
raise DowCommerce.DowCommerceError('No proxy passed to setProxy()')
|
||||||
self.proxy = {'http': str(proxy).strip()}
|
self.proxy = {'http': str(proxy).strip()}
|
||||||
|
|
||||||
def setParameter(self, key=None, value=None):
|
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()
|
self.parameters[key] = str(value).strip()
|
||||||
else:
|
else:
|
||||||
raise DowCommerce.DowCommerceError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value))
|
raise DowCommerce.DowCommerceError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value))
|
||||||
@@ -194,6 +197,7 @@ class DowCommerce:
|
|||||||
def getResponseText(self):
|
def getResponseText(self):
|
||||||
return self.results['responsetext']
|
return self.results['responsetext']
|
||||||
|
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
@@ -216,7 +220,8 @@ def test():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
payment = DowCommerce(demomode=True)
|
payment = DowCommerce(demomode=True)
|
||||||
payment.setTransaction(creditcard, expiration, total, cvv=cvv, tax=tax, orderid=orderid, orderdescription='Test Transaction',
|
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',
|
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')
|
zipcode='12345', country='US', phone='888-555-1212', emailaddress='john@noemail.local', ipaddress='192.168.1.1')
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -42,7 +42,9 @@ revision3.com
|
|||||||
viddler.com
|
viddler.com
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re, cgi, sys
|
import re
|
||||||
|
import cgi
|
||||||
|
import sys
|
||||||
from simplejson import loads
|
from simplejson import loads
|
||||||
import urllib
|
import urllib
|
||||||
import uuid
|
import uuid
|
||||||
@@ -77,18 +79,23 @@ EMBED_MAPS = [
|
|||||||
'http://lab.viddler.com/services/oembed/'),
|
'http://lab.viddler.com/services/oembed/'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def image(url):
|
def image(url):
|
||||||
return '<img src="%s" style="max-width:100%%"/>' % url
|
return '<img src="%s" style="max-width:100%%"/>' % url
|
||||||
|
|
||||||
|
|
||||||
def audio(url):
|
def audio(url):
|
||||||
return '<audio controls="controls" style="max-width:100%%"><source src="%s" /></audio>' % url
|
return '<audio controls="controls" style="max-width:100%%"><source src="%s" /></audio>' % url
|
||||||
|
|
||||||
|
|
||||||
def video(url):
|
def video(url):
|
||||||
return '<video controls="controls" style="max-width:100%%"><source src="%s" /></video>' % url
|
return '<video controls="controls" style="max-width:100%%"><source src="%s" /></video>' % url
|
||||||
|
|
||||||
|
|
||||||
def googledoc_viewer(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)
|
return '<iframe src="http://docs.google.com/viewer?url=%s&embedded=true" style="max-width:100%%"></iframe>' % urllib.quote(url)
|
||||||
|
|
||||||
|
|
||||||
def web2py_component(url):
|
def web2py_component(url):
|
||||||
code = str(uuid.uuid4())
|
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)
|
||||||
@@ -126,11 +133,13 @@ EXTENSION_MAPS = {
|
|||||||
'xps': googledoc_viewer,
|
'xps': googledoc_viewer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class VimeoURLOpener(urllib.FancyURLopener):
|
class VimeoURLOpener(urllib.FancyURLopener):
|
||||||
"Vimeo blocks the urllib user agent for some reason"
|
"Vimeo blocks the urllib user agent for some reason"
|
||||||
version = "Mozilla/4.0"
|
version = "Mozilla/4.0"
|
||||||
urllib._urlopener = VimeoURLOpener()
|
urllib._urlopener = VimeoURLOpener()
|
||||||
|
|
||||||
|
|
||||||
def oembed(url):
|
def oembed(url):
|
||||||
for k, v in EMBED_MAPS:
|
for k, v in EMBED_MAPS:
|
||||||
if k.match(url):
|
if k.match(url):
|
||||||
@@ -143,9 +152,11 @@ def oembed(url):
|
|||||||
pass
|
pass
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def extension(url):
|
def extension(url):
|
||||||
return url.split('?')[0].split('.')[-1].lower()
|
return url.split('?')[0].split('.')[-1].lower()
|
||||||
|
|
||||||
|
|
||||||
def expand_one(url, cdict):
|
def expand_one(url, cdict):
|
||||||
# try ombed but first check in cache
|
# try ombed but first check in cache
|
||||||
if cdict and url in cdict:
|
if cdict and url in cdict:
|
||||||
@@ -170,9 +181,10 @@ def expand_one(url,cdict):
|
|||||||
# else regular link
|
# else regular link
|
||||||
return '<a href="%(u)s">%(u)s</a>' % dict(u=url)
|
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:
|
if not have_soup:
|
||||||
raise RuntimeError, "Missing BeautifulSoup"
|
raise RuntimeError("Missing BeautifulSoup")
|
||||||
soup = BeautifulSoup(html)
|
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]
|
[comment.extract() for comment in comments]
|
||||||
@@ -183,6 +195,7 @@ def expand_html(html,cdict=None):
|
|||||||
txt.replaceWith(BeautifulSoup(ntxt))
|
txt.replaceWith(BeautifulSoup(ntxt))
|
||||||
return str(soup)
|
return str(soup)
|
||||||
|
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
example = """
|
example = """
|
||||||
<h3>Fringilla nisi parturient nullam</h3>
|
<h3>Fringilla nisi parturient nullam</h3>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ cache.ram=cache.disk=MemcacheClient(request)
|
|||||||
import time
|
import time
|
||||||
from google.appengine.api.memcache import Client
|
from google.appengine.api.memcache import Client
|
||||||
|
|
||||||
|
|
||||||
class MemcacheClient(object):
|
class MemcacheClient(object):
|
||||||
|
|
||||||
client = Client()
|
client = Client()
|
||||||
@@ -29,7 +30,7 @@ class MemcacheClient(object):
|
|||||||
dt = time_expire
|
dt = time_expire
|
||||||
value = None
|
value = None
|
||||||
obj = self.client.get(key)
|
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]
|
value = obj[1]
|
||||||
elif f is None:
|
elif f is None:
|
||||||
if obj:
|
if obj:
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0):
|
|||||||
:param exponent: rate of exponential back-off.
|
: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.api import apiproxy_stub_map
|
||||||
from google.appengine.runtime import apiproxy_errors
|
from google.appengine.runtime import apiproxy_errors
|
||||||
from google.appengine.datastore import datastore_pb
|
from google.appengine.datastore import datastore_pb
|
||||||
@@ -70,10 +71,12 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0):
|
|||||||
return wrapped(*args, **kwargs)
|
return wrapped(*args, **kwargs)
|
||||||
except apiproxy_errors.ApplicationError, err:
|
except apiproxy_errors.ApplicationError, err:
|
||||||
errno = err.application_error
|
errno = err.application_error
|
||||||
if errno not in errors: raise
|
if errno not in errors:
|
||||||
|
raise
|
||||||
sleep = (exponent ** count) * interval
|
sleep = (exponent ** count) * interval
|
||||||
count += 1.0
|
count += 1.0
|
||||||
if count > attempts: raise
|
if count > attempts:
|
||||||
|
raise
|
||||||
msg = "Datastore %s: retry #%d in %s seconds.\n%s"
|
msg = "Datastore %s: retry #%d in %s seconds.\n%s"
|
||||||
vals = ''
|
vals = ''
|
||||||
if count == 1.0:
|
if count == 1.0:
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from gluon.sanitizer import sanitize
|
|||||||
from gluon.contrib.markmin.markmin2latex import markmin2latex
|
from gluon.contrib.markmin.markmin2latex import markmin2latex
|
||||||
from gluon.contrib.markmin.markmin2pdf import markmin2pdf
|
from gluon.contrib.markmin.markmin2pdf import markmin2pdf
|
||||||
|
|
||||||
|
|
||||||
def wrapper(f):
|
def wrapper(f):
|
||||||
def g(data):
|
def g(data):
|
||||||
try:
|
try:
|
||||||
@@ -25,10 +26,12 @@ def wrapper(f):
|
|||||||
raise HTTP(405, '%s error' % e)
|
raise HTTP(405, '%s error' % e)
|
||||||
return g
|
return g
|
||||||
|
|
||||||
|
|
||||||
def latex_from_html(html):
|
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))
|
return XML(markmin2latex(markmin))
|
||||||
|
|
||||||
|
|
||||||
def pdflatex_from_html(html):
|
def pdflatex_from_html(html):
|
||||||
if os.system('which pdflatex > /dev/null') == 0:
|
if os.system('which pdflatex > /dev/null') == 0:
|
||||||
markmin = TAG(html).element('body').flatten(markmin_serializer)
|
markmin = TAG(html).element('body').flatten(markmin_serializer)
|
||||||
@@ -42,19 +45,25 @@ def pdflatex_from_html(html):
|
|||||||
else:
|
else:
|
||||||
return XML(out)
|
return XML(out)
|
||||||
|
|
||||||
|
|
||||||
def pyfpdf_from_html(html):
|
def pyfpdf_from_html(html):
|
||||||
request = current.request
|
request = current.request
|
||||||
|
|
||||||
def image_map(path):
|
def image_map(path):
|
||||||
if path.startswith('/%s/static/' % request.application):
|
if path.startswith('/%s/static/' % request.application):
|
||||||
return os.path.join(request.folder, path.split('/', 2)[2])
|
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)
|
return 'http%s://%s%s' % (request.is_https and 's' or '', request.env.http_host, path)
|
||||||
class MyFPDF(FPDF, HTMLMixin): pass
|
|
||||||
|
class MyFPDF(FPDF, HTMLMixin):
|
||||||
|
pass
|
||||||
pdf = MyFPDF()
|
pdf = MyFPDF()
|
||||||
pdf.add_page()
|
pdf.add_page()
|
||||||
html = sanitize(html, escape=False) #### should have better list of allowed tags
|
html = sanitize(
|
||||||
|
html, escape=False) # should have better list of allowed tags
|
||||||
pdf.write_html(html, image_map=image_map)
|
pdf.write_html(html, image_map=image_map)
|
||||||
return XML(pdf.output(dest='S'))
|
return XML(pdf.output(dest='S'))
|
||||||
|
|
||||||
|
|
||||||
def pdf_from_html(html):
|
def pdf_from_html(html):
|
||||||
# try use latex and pdflatex
|
# try use latex and pdflatex
|
||||||
if os.system('which pdflatex > /dev/null') == 0:
|
if os.system('which pdflatex > /dev/null') == 0:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from gluon import XML
|
from gluon import XML
|
||||||
|
|
||||||
|
|
||||||
def button(merchant_id="123456789012345",
|
def button(merchant_id="123456789012345",
|
||||||
products=[dict(name="shoes",
|
products=[dict(name="shoes",
|
||||||
quantity=1,
|
quantity=1,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#########################################################################
|
#########################################################################
|
||||||
from gluon import current
|
from gluon import current
|
||||||
|
|
||||||
|
|
||||||
class RESIZE(object):
|
class RESIZE(object):
|
||||||
def __init__(self, nx=160, ny=80, error_message=' image resize'):
|
def __init__(self, nx=160, ny=80, error_message=' image resize'):
|
||||||
(self.nx, self.ny, self.error_message) = (nx, ny, error_message)
|
(self.nx, self.ny, self.error_message) = (nx, ny, error_message)
|
||||||
@@ -43,6 +44,7 @@ class RESIZE(object):
|
|||||||
else:
|
else:
|
||||||
return (value, None)
|
return (value, None)
|
||||||
|
|
||||||
|
|
||||||
def THUMB(image, nx=120, ny=120, gae=False, name='thumb'):
|
def THUMB(image, nx=120, ny=120, gae=False, name='thumb'):
|
||||||
if image:
|
if image:
|
||||||
if not gae:
|
if not gae:
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -22,5 +22,3 @@ def basic_auth(server="http://127.0.0.1"):
|
|||||||
except (urllib2.URLError, urllib2.HTTPError):
|
except (urllib2.URLError, urllib2.HTTPError):
|
||||||
return False
|
return False
|
||||||
return basic_login_aux
|
return basic_login_aux
|
||||||
|
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@ from gluon.storage import Storage
|
|||||||
from gluon.tools import fetch
|
from gluon.tools import fetch
|
||||||
import gluon.contrib.simplejson as json
|
import gluon.contrib.simplejson as json
|
||||||
|
|
||||||
|
|
||||||
class BrowserID(object):
|
class BrowserID(object):
|
||||||
"""
|
"""
|
||||||
from gluon.contrib.login_methods.browserid_account import BrowserID
|
from gluon.contrib.login_methods.browserid_account import BrowserID
|
||||||
@@ -88,4 +89,3 @@ class BrowserID(object):
|
|||||||
A(IMG(_src=self.browserid_button, _alt=self.prompt), _href="#", _onclick=onclick, _class="browserid", _title="Login With BrowserID"),
|
A(IMG(_src=self.browserid_button, _alt=self.prompt), _href="#", _onclick=onclick, _class="browserid", _title="Login With BrowserID"),
|
||||||
SCRIPT(self.asertion_js))
|
SCRIPT(self.asertion_js))
|
||||||
return form
|
return form
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ Tinkered by Szabolcs Gyuris < szimszo n @ o regpreshaz dot eu>
|
|||||||
|
|
||||||
from gluon import current, redirect
|
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 will be done via Web2py's CAS application, instead of web2py's
|
||||||
@@ -39,7 +40,7 @@ class CasAuth( object ):
|
|||||||
user's username.
|
user's username.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, g=None, ### g for backward compatibility ###
|
def __init__(self, g=None, # g for backward compatibility ###
|
||||||
urlbase="https://web2py.com/cas/cas",
|
urlbase="https://web2py.com/cas/cas",
|
||||||
actions=['login', 'validate', 'logout'],
|
actions=['login', 'validate', 'logout'],
|
||||||
maps=dict(username=lambda v: v.get('username', v['user']),
|
maps=dict(username=lambda v: v.get('username', v['user']),
|
||||||
@@ -56,21 +57,25 @@ class CasAuth( object ):
|
|||||||
self.casversion = casversion
|
self.casversion = casversion
|
||||||
self.casusername = casusername
|
self.casusername = casusername
|
||||||
http_host = current.request.env.http_x_forwarded_host
|
http_host = current.request.env.http_x_forwarded_host
|
||||||
if not http_host: http_host=current.request.env.http_host
|
if not http_host:
|
||||||
|
http_host = current.request.env.http_host
|
||||||
if current.request.env.wsgi_url_scheme in ['https', 'HTTPS']:
|
if current.request.env.wsgi_url_scheme in ['https', 'HTTPS']:
|
||||||
scheme = 'https'
|
scheme = 'https'
|
||||||
else:
|
else:
|
||||||
scheme = 'http'
|
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="/"):
|
def login_url(self, next="/"):
|
||||||
current.session.token = self._CAS_login()
|
current.session.token = self._CAS_login()
|
||||||
return next
|
return next
|
||||||
|
|
||||||
def logout_url(self, next="/"):
|
def logout_url(self, next="/"):
|
||||||
current.session.token = None
|
current.session.token = None
|
||||||
current.session.auth = None
|
current.session.auth = None
|
||||||
self._CAS_logout()
|
self._CAS_logout()
|
||||||
return next
|
return next
|
||||||
|
|
||||||
def get_user(self):
|
def get_user(self):
|
||||||
user = current.session.token
|
user = current.session.token
|
||||||
if user:
|
if user:
|
||||||
@@ -79,6 +84,7 @@ class CasAuth( object ):
|
|||||||
d[key] = self.maps[key](user)
|
d[key] = self.maps[key](user)
|
||||||
return d
|
return d
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _CAS_login(self):
|
def _CAS_login(self):
|
||||||
"""
|
"""
|
||||||
exposed as CAS.login(request)
|
exposed as CAS.login(request)
|
||||||
@@ -110,7 +116,8 @@ class CasAuth( object ):
|
|||||||
import xml.parsers.expat as expat
|
import xml.parsers.expat as expat
|
||||||
try:
|
try:
|
||||||
dxml = dom.parseString(data)
|
dxml = dom.parseString(data)
|
||||||
envelop = dxml.getElementsByTagName("cas:authenticationSuccess")
|
envelop = dxml.getElementsByTagName(
|
||||||
|
"cas:authenticationSuccess")
|
||||||
if len(envelop) > 0:
|
if len(envelop) > 0:
|
||||||
res = dict()
|
res = dict()
|
||||||
for x in envelop[0].childNodes:
|
for x in envelop[0].childNodes:
|
||||||
@@ -124,10 +131,10 @@ class CasAuth( object ):
|
|||||||
res[key] = [res[key]]
|
res[key] = [res[key]]
|
||||||
res[key].append(value)
|
res[key].append(value)
|
||||||
return res
|
return res
|
||||||
except expat.ExpatError: pass
|
except expat.ExpatError:
|
||||||
|
pass
|
||||||
return None # fallback
|
return None # fallback
|
||||||
|
|
||||||
|
|
||||||
def _CAS_logout(self):
|
def _CAS_logout(self):
|
||||||
"""
|
"""
|
||||||
exposed CAS.logout()
|
exposed CAS.logout()
|
||||||
@@ -135,5 +142,3 @@ class CasAuth( object ):
|
|||||||
"""
|
"""
|
||||||
import urllib
|
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))
|
||||||
|
|
||||||
|
|
||||||
@@ -18,11 +18,13 @@ from gluon.tools import fetch
|
|||||||
from gluon.storage import Storage
|
from gluon.storage import Storage
|
||||||
import gluon.contrib.simplejson as json
|
import gluon.contrib.simplejson as json
|
||||||
|
|
||||||
|
|
||||||
class DropboxAccount(object):
|
class DropboxAccount(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from gluon.contrib.login_methods.dropbox_account import DropboxAccount
|
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,
|
auth.settings.login_form = DropboxAccount(request,
|
||||||
key="...",
|
key="...",
|
||||||
secret="...",
|
secret="...",
|
||||||
@@ -50,7 +52,6 @@ class DropboxAccount(object):
|
|||||||
self.sess = session.DropboxSession(
|
self.sess = session.DropboxSession(
|
||||||
self.key, self.secret, self.access_type)
|
self.key, self.secret, self.access_type)
|
||||||
|
|
||||||
|
|
||||||
def get_user(self):
|
def get_user(self):
|
||||||
request = self.request
|
request = self.request
|
||||||
if not current.session.dropbox_request_token:
|
if not current.session.dropbox_request_token:
|
||||||
@@ -66,7 +67,6 @@ class DropboxAccount(object):
|
|||||||
access_token = current.session.dropbox_access_token
|
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()
|
user = Storage()
|
||||||
self.client = client.DropboxClient(self.sess)
|
self.client = client.DropboxClient(self.sess)
|
||||||
data = self.client.account_info()
|
data = self.client.account_info()
|
||||||
@@ -98,13 +98,17 @@ class DropboxAccount(object):
|
|||||||
current.session.auth = None
|
current.session.auth = None
|
||||||
redirect('https://www.dropbox.com/logout')
|
redirect('https://www.dropbox.com/logout')
|
||||||
return next
|
return next
|
||||||
|
|
||||||
def put(self, filename, file):
|
def put(self, filename, file):
|
||||||
return json.loads(self.client.put_file(filename, file))['bytes']
|
return json.loads(self.client.put_file(filename, file))['bytes']
|
||||||
|
|
||||||
def get(self, filename, file):
|
def get(self, filename, file):
|
||||||
return self.client.get_file(filename)
|
return self.client.get_file(filename)
|
||||||
|
|
||||||
def dir(self, path):
|
def dir(self, path):
|
||||||
return json.loads(self.client.metadata(path))
|
return json.loads(self.client.metadata(path))
|
||||||
|
|
||||||
|
|
||||||
def use_dropbox(auth, filename='private/dropbox.key', **kwargs):
|
def use_dropbox(auth, filename='private/dropbox.key', **kwargs):
|
||||||
path = os.path.join(current.request.folder, filename)
|
path = os.path.join(current.request.folder, filename)
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
@@ -118,4 +122,3 @@ def use_dropbox(auth,filename='private/dropbox.key',**kwargs):
|
|||||||
auth.settings.login_form = DropboxAccount(
|
auth.settings.login_form = DropboxAccount(
|
||||||
request, key=key, secret=secret, access_type=access_type,
|
request, key=key, secret=secret, access_type=access_type,
|
||||||
login_url=login_url, **kwargs)
|
login_url=login_url, **kwargs)
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import smtplib
|
import smtplib
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
def email_auth(server="smtp.gmail.com:587",
|
def email_auth(server="smtp.gmail.com:587",
|
||||||
domain="@gmail.com",
|
domain="@gmail.com",
|
||||||
tls_mode=None):
|
tls_mode=None):
|
||||||
@@ -43,4 +44,3 @@ def email_auth(server="smtp.gmail.com:587",
|
|||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
return email_auth_aux
|
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
|
from gluon import current, DIV
|
||||||
|
|
||||||
|
|
||||||
class ExtendedLoginForm(object):
|
class ExtendedLoginForm(object):
|
||||||
"""
|
"""
|
||||||
Put extended_login_form under web2py/gluon/contrib/login_methods folder.
|
Put extended_login_form under web2py/gluon/contrib/login_methods folder.
|
||||||
@@ -22,7 +23,8 @@ class ExtendedLoginForm(object):
|
|||||||
api_key="...",
|
api_key="...",
|
||||||
domain="...",
|
domain="...",
|
||||||
url = "http://localhost:8000/%s/default/user/login" % request.application)
|
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
|
auth.settings.login_form = extended_login_form
|
||||||
|
|
||||||
@@ -91,7 +93,7 @@ class ExtendedLoginForm(object):
|
|||||||
args = request.args
|
args = request.args
|
||||||
|
|
||||||
if (self.signals and
|
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()
|
return self.alt_login_form.login_form()
|
||||||
|
|
||||||
@@ -101,5 +103,3 @@ class ExtendedLoginForm(object):
|
|||||||
|
|
||||||
form.components.append(self.alt_login_form.login_form())
|
form.components.append(self.alt_login_form.login_form())
|
||||||
return form
|
return form
|
||||||
|
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ Thanks to Hans Donner <hans.donner@pobox.com> for GaeGoogleAccount.
|
|||||||
|
|
||||||
from google.appengine.api import users
|
from google.appengine.api import users
|
||||||
|
|
||||||
|
|
||||||
class GaeGoogleAccount(object):
|
class GaeGoogleAccount(object):
|
||||||
"""
|
"""
|
||||||
Login will be done via Google's Appengine login object, instead of web2py's
|
Login will be done via Google's Appengine login object, instead of web2py's
|
||||||
@@ -35,5 +36,3 @@ class GaeGoogleAccount(object):
|
|||||||
if user:
|
if user:
|
||||||
return dict(nickname=user.nickname(), email=user.email(),
|
return dict(nickname=user.nickname(), email=user.email(),
|
||||||
user_id=user.user_id(), source="google account")
|
user_id=user.user_id(), source="google account")
|
||||||
|
|
||||||
|
|
||||||
@@ -188,18 +188,23 @@ def ldap_auth(server='ldap', port=None,
|
|||||||
str(custom_scope), str(manage_groups)))
|
str(custom_scope), str(manage_groups)))
|
||||||
if manage_user:
|
if manage_user:
|
||||||
if user_firstname_attrib.count(':') > 0:
|
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)
|
user_firstname_part = (int(user_firstname_part) - 1)
|
||||||
else:
|
else:
|
||||||
user_firstname_part = None
|
user_firstname_part = None
|
||||||
if user_lastname_attrib.count(':') > 0:
|
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)
|
user_lastname_part = (int(user_lastname_part) - 1)
|
||||||
else:
|
else:
|
||||||
user_lastname_part = None
|
user_lastname_part = None
|
||||||
user_firstname_attrib = ldap.filter.escape_filter_chars(user_firstname_attrib)
|
user_firstname_attrib = ldap.filter.escape_filter_chars(
|
||||||
user_lastname_attrib = ldap.filter.escape_filter_chars(user_lastname_attrib)
|
user_firstname_attrib)
|
||||||
user_mail_attrib = ldap.filter.escape_filter_chars(user_mail_attrib)
|
user_lastname_attrib = ldap.filter.escape_filter_chars(
|
||||||
|
user_lastname_attrib)
|
||||||
|
user_mail_attrib = ldap.filter.escape_filter_chars(
|
||||||
|
user_mail_attrib)
|
||||||
try:
|
try:
|
||||||
if allowed_groups:
|
if allowed_groups:
|
||||||
if not is_user_in_allowed_groups(username, password):
|
if not is_user_in_allowed_groups(username, password):
|
||||||
@@ -310,7 +315,8 @@ def ldap_auth(server='ldap', port=None,
|
|||||||
basedns = ldap_basedn
|
basedns = ldap_basedn
|
||||||
else:
|
else:
|
||||||
basedns = [ldap_basedn]
|
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
|
found = False
|
||||||
for basedn in basedns:
|
for basedn in basedns:
|
||||||
try:
|
try:
|
||||||
@@ -338,7 +344,8 @@ def ldap_auth(server='ldap', port=None,
|
|||||||
else:
|
else:
|
||||||
basedns = [ldap_basedn]
|
basedns = [ldap_basedn]
|
||||||
filter = '(&(%s=%s)(%s))' % (username_attrib,
|
filter = '(&(%s=%s)(%s))' % (username_attrib,
|
||||||
ldap.filter.escape_filter_chars(username),
|
ldap.filter.escape_filter_chars(
|
||||||
|
username),
|
||||||
filterstr)
|
filterstr)
|
||||||
if custom_scope == 'subtree':
|
if custom_scope == 'subtree':
|
||||||
ldap_scope = ldap.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))
|
logger.info('[%s] Manage user data' % str(username))
|
||||||
try:
|
try:
|
||||||
if user_firstname_part is not None:
|
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:
|
else:
|
||||||
store_user_firstname = result[user_firstname_attrib][0]
|
store_user_firstname = result[user_firstname_attrib][0]
|
||||||
except KeyError, e:
|
except KeyError, e:
|
||||||
store_user_firstname = None
|
store_user_firstname = None
|
||||||
try:
|
try:
|
||||||
if user_lastname_part is not None:
|
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:
|
else:
|
||||||
store_user_lastname = result[user_lastname_attrib][0]
|
store_user_lastname = result[user_lastname_attrib][0]
|
||||||
except KeyError, e:
|
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
|
# 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
|
# Get all group name where the user is in actually in local db
|
||||||
# #############################################################
|
# #############################################################
|
||||||
try:
|
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:
|
except:
|
||||||
try:
|
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:
|
except AttributeError, e:
|
||||||
#
|
#
|
||||||
# There is no user in local db
|
# 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,
|
db_user_id = db.auth_user.insert(email=username,
|
||||||
first_name=username)
|
first_name=username)
|
||||||
if not db_user_id:
|
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
|
raise
|
||||||
db_group_search = db((db.auth_membership.user_id == db_user_id) &
|
db_group_search = db((db.auth_membership.user_id == db_user_id) &
|
||||||
(db.auth_user.id == db.auth_membership.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,
|
gid = db.auth_group.insert(role=group_to_add,
|
||||||
description='Generated from LDAP')
|
description='Generated from LDAP')
|
||||||
else:
|
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,
|
db.auth_membership.insert(user_id=db_user_id,
|
||||||
group_id=gid)
|
group_id=gid)
|
||||||
except:
|
except:
|
||||||
@@ -634,4 +648,3 @@ def ldap_auth(server='ldap', port=None,
|
|||||||
if filterstr[0] == '(' and filterstr[-1] == ')': # rfc4515 syntax
|
if filterstr[0] == '(' and filterstr[-1] == ')': # rfc4515 syntax
|
||||||
filterstr = filterstr[1:-1] # parens added again where used
|
filterstr = filterstr[1:-1] # parens added again where used
|
||||||
return ldap_auth_aux
|
return ldap_auth_aux
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
raise HTTP(400, "linkedin module not found")
|
raise HTTP(400, "linkedin module not found")
|
||||||
|
|
||||||
|
|
||||||
class LinkedInAccount(object):
|
class LinkedInAccount(object):
|
||||||
"""
|
"""
|
||||||
Login will be done via Google's Appengine login object, instead of web2py's
|
Login will be done via Google's Appengine login object, instead of web2py's
|
||||||
@@ -40,13 +41,12 @@ class LinkedInAccount(object):
|
|||||||
return ''
|
return ''
|
||||||
|
|
||||||
def get_user(self):
|
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:
|
if result:
|
||||||
profile = self.api.GetProfile()
|
profile = self.api.GetProfile()
|
||||||
profile = self.api.GetProfile(profile).public_url = "http://www.linkedin.com/in/ozgurv"
|
profile = self.api.GetProfile(
|
||||||
|
profile).public_url = "http://www.linkedin.com/in/ozgurv"
|
||||||
return dict(first_name=profile.first_name,
|
return dict(first_name=profile.first_name,
|
||||||
last_name=profile.last_name,
|
last_name=profile.last_name,
|
||||||
username=profile.id)
|
username=profile.id)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ from gluon.tools import fetch
|
|||||||
from gluon.storage import Storage
|
from gluon.storage import Storage
|
||||||
import gluon.contrib.simplejson as json
|
import gluon.contrib.simplejson as json
|
||||||
|
|
||||||
|
|
||||||
class Loginza(object):
|
class Loginza(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -85,10 +86,11 @@ class Loginza(object):
|
|||||||
auth_info_json = fetch(self.auth_url + '?' + data)
|
auth_info_json = fetch(self.auth_url + '?' + data)
|
||||||
#print auth_info_json
|
#print auth_info_json
|
||||||
auth_info = json.loads(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
|
self.profile = auth_info
|
||||||
provider = self.profile["provider"]
|
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["password"] = ???
|
||||||
#user["avatar"] = ???
|
#user["avatar"] = ???
|
||||||
return user
|
return user
|
||||||
@@ -106,8 +108,8 @@ class Loginza(object):
|
|||||||
_frameborder="no",
|
_frameborder="no",
|
||||||
_style="width:359px;height:300px;")
|
_style="width:359px;height:300px;")
|
||||||
else:
|
else:
|
||||||
form = DIV(A(self.prompt, _href=LOGINZA_URL % (self.language, self.token_url), _class="loginza"),
|
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"))
|
SCRIPT(_src="https://s3-eu-west-1.amazonaws.com/s1.loginza.ru/js/widget.js", _type="text/javascript"))
|
||||||
return form
|
return form
|
||||||
|
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@ import time
|
|||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from gluon.dal import DAL
|
from gluon.dal import DAL
|
||||||
|
|
||||||
|
|
||||||
def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
||||||
time_offset=60):
|
time_offset=60):
|
||||||
|
|
||||||
@@ -44,7 +45,8 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
|||||||
writable=False, readable=False, default=''))
|
writable=False, readable=False, default=''))
|
||||||
|
|
||||||
##validators
|
##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 = \
|
custom_auth_table.first_name.requires = \
|
||||||
IS_NOT_EMPTY(error_message=auth.messages.is_empty)
|
IS_NOT_EMPTY(error_message=auth.messages.is_empty)
|
||||||
custom_auth_table.last_name.requires = \
|
custom_auth_table.last_name.requires = \
|
||||||
@@ -83,7 +85,8 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
|||||||
for t in range(time_start - 1, time_end + 1):
|
for t in range(time_start - 1, time_end + 1):
|
||||||
to_hash = str(t) + secret + pin
|
to_hash = str(t) + secret + pin
|
||||||
hash = md5(to_hash).hexdigest()[:6]
|
hash = md5(to_hash).hexdigest()[:6]
|
||||||
if otp == hash: return True
|
if otp == hash:
|
||||||
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def motp_auth_aux(email,
|
def motp_auth_aux(email,
|
||||||
@@ -96,10 +99,13 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
|||||||
if user_data['motp_secret'] and user_data['motp_pin']:
|
if user_data['motp_secret'] and user_data['motp_pin']:
|
||||||
motp_secret = user_data['motp_secret']
|
motp_secret = user_data['motp_secret']
|
||||||
motp_pin = user_data['motp_pin']
|
motp_pin = user_data['motp_pin']
|
||||||
otp_check = verify_otp(password,motp_pin,motp_secret,offset=offset)
|
otp_check = verify_otp(
|
||||||
if otp_check: return True
|
password, motp_pin, motp_secret, offset=offset)
|
||||||
else: return False
|
if otp_check:
|
||||||
else: return False
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
return False
|
return False
|
||||||
return motp_auth_aux
|
return motp_auth_aux
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ from urllib2 import urlopen
|
|||||||
import urllib2
|
import urllib2
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
|
|
||||||
|
|
||||||
class OAuthAccount(object):
|
class OAuthAccount(object):
|
||||||
"""
|
"""
|
||||||
Login will be done via OAuth Framework, instead of web2py's
|
Login will be done via OAuth Framework, instead of web2py's
|
||||||
@@ -51,7 +52,8 @@ class OAuthAccount(object):
|
|||||||
TOKEN_URL="..."
|
TOKEN_URL="..."
|
||||||
ACCESS_TOKEN_URL="..."
|
ACCESS_TOKEN_URL="..."
|
||||||
from gluon.contrib.login_methods.oauth10a_account import OAuthAccount
|
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)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -62,7 +64,8 @@ class OAuthAccount(object):
|
|||||||
"""
|
"""
|
||||||
r = self.request
|
r = self.request
|
||||||
http_host = r.env.http_x_forwarded_for
|
http_host = r.env.http_x_forwarded_for
|
||||||
if not http_host: http_host=r.env.http_host
|
if not http_host:
|
||||||
|
http_host = r.env.http_host
|
||||||
|
|
||||||
url_scheme = r.env.wsgi_url_scheme
|
url_scheme = r.env.wsgi_url_scheme
|
||||||
if next:
|
if next:
|
||||||
@@ -74,7 +77,6 @@ class OAuthAccount(object):
|
|||||||
uri += '?' + urlencode(r.get_vars)
|
uri += '?' + urlencode(r.get_vars)
|
||||||
return uri
|
return uri
|
||||||
|
|
||||||
|
|
||||||
def accessToken(self):
|
def accessToken(self):
|
||||||
"""Return the access token generated by the authenticating server.
|
"""Return the access token generated by the authenticating server.
|
||||||
|
|
||||||
@@ -97,12 +99,11 @@ class OAuthAccount(object):
|
|||||||
token.set_verifier(self.request.vars.oauth_verifier)
|
token.set_verifier(self.request.vars.oauth_verifier)
|
||||||
client = oauth.Client(self.consumer, token)
|
client = oauth.Client(self.consumer, token)
|
||||||
|
|
||||||
|
|
||||||
resp, content = client.request(self.access_token_url, "POST")
|
resp, content = client.request(self.access_token_url, "POST")
|
||||||
if str(resp['status']) != '200':
|
if str(resp['status']) != '200':
|
||||||
self.session.request_token = None
|
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)
|
self.session.access_token = oauth.Token.from_string(content)
|
||||||
|
|
||||||
@@ -125,7 +126,6 @@ class OAuthAccount(object):
|
|||||||
# consumer init
|
# consumer init
|
||||||
self.consumer = oauth.Consumer(self.client_id, self.client_secret)
|
self.consumer = oauth.Consumer(self.client_id, self.client_secret)
|
||||||
|
|
||||||
|
|
||||||
def login_url(self, next="/"):
|
def login_url(self, next="/"):
|
||||||
self.__oauth_login(next)
|
self.__oauth_login(next)
|
||||||
return next
|
return next
|
||||||
@@ -142,7 +142,7 @@ class OAuthAccount(object):
|
|||||||
is, this function must be implemented for the specific
|
is, this function must be implemented for the specific
|
||||||
provider.
|
provider.
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError, "Must override get_user()"
|
raise NotImplementedError("Must override get_user()")
|
||||||
|
|
||||||
def __oauth_login(self, next):
|
def __oauth_login(self, next):
|
||||||
'''This method redirects the user to the authenticating form
|
'''This method redirects the user to the authenticating form
|
||||||
@@ -166,7 +166,8 @@ class OAuthAccount(object):
|
|||||||
resp, content = client.request(self.token_url, "POST", body=data)
|
resp, content = client.request(self.token_url, "POST", body=data)
|
||||||
if resp['status'] != '200':
|
if resp['status'] != '200':
|
||||||
self.session.request_token = None
|
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.
|
# Store the request token in session.
|
||||||
request_token = self.session.request_token = oauth.Token.from_string(content)
|
request_token = self.session.request_token = oauth.Token.from_string(content)
|
||||||
@@ -176,16 +177,10 @@ class OAuthAccount(object):
|
|||||||
oauth_callback=callback_url))
|
oauth_callback=callback_url))
|
||||||
auth_request_url = self.auth_url + '?' + data
|
auth_request_url = self.auth_url + '?' + data
|
||||||
|
|
||||||
|
|
||||||
HTTP = self.globals['HTTP']
|
HTTP = self.globals['HTTP']
|
||||||
|
|
||||||
|
|
||||||
raise HTTP(307,
|
raise HTTP(307,
|
||||||
"You are not authenticated: you are being redirected to the <a href='" + auth_request_url + "'> authentication server</a>",
|
"You are not authenticated: you are being redirected to the <a href='" + auth_request_url + "'> authentication server</a>",
|
||||||
Location=auth_request_url)
|
Location=auth_request_url)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -17,6 +17,7 @@ import urllib2
|
|||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from gluon import current, redirect, HTTP
|
from gluon import current, redirect, HTTP
|
||||||
|
|
||||||
|
|
||||||
class OAuthAccount(object):
|
class OAuthAccount(object):
|
||||||
"""
|
"""
|
||||||
Login will be done via OAuth Framework, instead of web2py's
|
Login will be done via OAuth Framework, instead of web2py's
|
||||||
@@ -84,7 +85,8 @@ class OAuthAccount(object):
|
|||||||
username = user['id'])
|
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()
|
auth.settings.login_form=FaceBookAccount()
|
||||||
|
|
||||||
Any optional arg in the constructor will be passed asis to remote
|
Any optional arg in the constructor will be passed asis to remote
|
||||||
@@ -100,7 +102,8 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
|||||||
|
|
||||||
r = current.request
|
r = current.request
|
||||||
http_host = r.env.http_x_forwarded_for
|
http_host = r.env.http_x_forwarded_for
|
||||||
if not http_host: http_host=r.env.http_host
|
if not http_host:
|
||||||
|
http_host = r.env.http_host
|
||||||
|
|
||||||
url_scheme = r.env.wsgi_url_scheme
|
url_scheme = r.env.wsgi_url_scheme
|
||||||
if next:
|
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)
|
uri += '?' + urlencode(r.get_vars)
|
||||||
return uri
|
return uri
|
||||||
|
|
||||||
|
|
||||||
def __build_url_opener(self, uri):
|
def __build_url_opener(self, uri):
|
||||||
"""
|
"""
|
||||||
Build the url opener for managing HTTP Basic Athentication
|
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)
|
opener = urllib2.build_opener(auth_handler)
|
||||||
return opener
|
return opener
|
||||||
|
|
||||||
|
|
||||||
def accessToken(self):
|
def accessToken(self):
|
||||||
"""
|
"""
|
||||||
Return the access token generated by the authenticating server.
|
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.
|
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']
|
expires = current.session.token['expires']
|
||||||
# reuse token until expiration
|
# reuse token until expiration
|
||||||
if expires == 0 or expires > time.time():
|
if expires == 0 or expires > time.time():
|
||||||
@@ -169,9 +170,9 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
|||||||
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
|
# set expiration absolute time try to avoid broken
|
||||||
# implementations where "expires_in" becomes "expires"
|
# implementations where "expires_in" becomes "expires"
|
||||||
if current.session.token.has_key('expires_in'):
|
if 'expires_in' in current.session.token:
|
||||||
exps = 'expires_in'
|
exps = 'expires_in'
|
||||||
elif current.session.token.has_key('expires'):
|
elif 'expires' in current.session.token:
|
||||||
exps = 'expires'
|
exps = 'expires'
|
||||||
else:
|
else:
|
||||||
exps = None
|
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.
|
Override this method by sublcassing the class.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not current.session.token: return None
|
if not current.session.token:
|
||||||
|
return None
|
||||||
return dict(first_name='Pinco',
|
return dict(first_name='Pinco',
|
||||||
last_name='Pallino',
|
last_name='Pallino',
|
||||||
username='pincopallino')
|
username='pincopallino')
|
||||||
raise NotImplementedError, "Must override get_user()"
|
raise NotImplementedError("Must override get_user()")
|
||||||
|
|
||||||
# Following code is never executed. It can be used as example
|
# Following code is never executed. It can be used as example
|
||||||
# for overriding in subclasses.
|
# for overriding in subclasses.
|
||||||
@@ -243,7 +245,6 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
|||||||
last_name=user['last_name'],
|
last_name=user['last_name'],
|
||||||
username=user['id'])
|
username=user['id'])
|
||||||
|
|
||||||
|
|
||||||
def __oauth_login(self, next):
|
def __oauth_login(self, next):
|
||||||
"""
|
"""
|
||||||
This method redirects the user to the authenticating form
|
This method redirects the user to the authenticating form
|
||||||
@@ -273,5 +274,3 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
|||||||
self.accessToken()
|
self.accessToken()
|
||||||
return current.session.code
|
return current.session.code
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -49,6 +49,7 @@ except ImportError, err:
|
|||||||
|
|
||||||
DEFAULT = lambda: None
|
DEFAULT = lambda: None
|
||||||
|
|
||||||
|
|
||||||
class OpenIDAuth(object):
|
class OpenIDAuth(object):
|
||||||
"""
|
"""
|
||||||
OpenIDAuth
|
OpenIDAuth
|
||||||
@@ -158,7 +159,7 @@ class OpenIDAuth(object):
|
|||||||
and not processed yet. Else return the OpenID form for login.
|
and not processed yet. Else return the OpenID form for login.
|
||||||
"""
|
"""
|
||||||
request = current.request
|
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()
|
self._process_response()
|
||||||
return self.auth()
|
return self.auth()
|
||||||
return self._form()
|
return self._form()
|
||||||
@@ -196,11 +197,11 @@ class OpenIDAuth(object):
|
|||||||
if current.session.w2popenid:
|
if current.session.w2popenid:
|
||||||
del(current.session.w2popenid)
|
del(current.session.w2popenid)
|
||||||
current.session.flash = self.messages.flash_openid_associated
|
current.session.flash = self.messages.flash_openid_associated
|
||||||
if request.vars.has_key(nextvar):
|
if nextvar in request.vars:
|
||||||
redirect(request.vars[nextvar])
|
redirect(request.vars[nextvar])
|
||||||
redirect(self.auth.settings.login_next)
|
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
|
# no next var, add it and do login again
|
||||||
# so if user login or register can go back here to associate the OpenID
|
# so if user login or register can go back here to associate the OpenID
|
||||||
redirect(URL(r=request,
|
redirect(URL(r=request,
|
||||||
@@ -211,7 +212,8 @@ class OpenIDAuth(object):
|
|||||||
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
|
# 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 user:
|
||||||
if current.session.w2popenid:
|
if current.session.w2popenid:
|
||||||
del(current.session.w2popenid)
|
del(current.session.w2popenid)
|
||||||
@@ -227,7 +229,8 @@ class OpenIDAuth(object):
|
|||||||
"""
|
"""
|
||||||
Get the matched OpenID for given
|
Get the matched OpenID for given
|
||||||
"""
|
"""
|
||||||
query = ((db.alt_logins.username == oid) & (db.alt_logins.type == type_))
|
query = (
|
||||||
|
(db.alt_logins.username == oid) & (db.alt_logins.type == type_))
|
||||||
alt_login = db(query).select().first() # Get the OpenID record
|
alt_login = db(query).select().first() # Get the OpenID record
|
||||||
return alt_login
|
return alt_login
|
||||||
|
|
||||||
@@ -275,7 +278,6 @@ class OpenIDAuth(object):
|
|||||||
self.db)
|
self.db)
|
||||||
return self.consumerhelper
|
return self.consumerhelper
|
||||||
|
|
||||||
|
|
||||||
def _form(self, style=None):
|
def _form(self, style=None):
|
||||||
form = DIV(H3(self.messages.h_openid_login), self._login_form(style))
|
form = DIV(H3(self.messages.h_openid_login), self._login_form(style))
|
||||||
return form
|
return form
|
||||||
@@ -308,13 +310,17 @@ width: 400px;
|
|||||||
hidden_next_input = ""
|
hidden_next_input = ""
|
||||||
if _next == 'profile':
|
if _next == 'profile':
|
||||||
profile_url = URL(r=request, f='user', args=['profile'])
|
profile_url = URL(r=request, f='user', args=['profile'])
|
||||||
hidden_next_input = INPUT(_type="hidden", _name="_next", _value=profile_url)
|
hidden_next_input = INPUT(
|
||||||
form = FORM(openid_field_label or self.messages.label_alt_login_username,
|
_type="hidden", _name="_next", _value=profile_url)
|
||||||
|
form = FORM(
|
||||||
|
openid_field_label or self.messages.label_alt_login_username,
|
||||||
INPUT(_type="input", _name="oid",
|
INPUT(_type="input", _name="oid",
|
||||||
requires=IS_NOT_EMPTY(error_message=messages.openid_fail_discover),
|
requires=IS_NOT_EMPTY(
|
||||||
|
error_message=messages.openid_fail_discover),
|
||||||
_style=style),
|
_style=style),
|
||||||
hidden_next_input,
|
hidden_next_input,
|
||||||
INPUT(_type="submit", _value=submit_button or messages.submit_button),
|
INPUT(_type="submit",
|
||||||
|
_value=submit_button or messages.submit_button),
|
||||||
" ",
|
" ",
|
||||||
A(messages.comment_openid_signin,
|
A(messages.comment_openid_signin,
|
||||||
_href=messages.comment_openid_help_url,
|
_href=messages.comment_openid_help_url,
|
||||||
@@ -332,8 +338,9 @@ width: 400px;
|
|||||||
warning_openid_fail(session)
|
warning_openid_fail(session)
|
||||||
redirect(url)
|
redirect(url)
|
||||||
try:
|
try:
|
||||||
if request.vars.has_key('_next'):
|
if '_next' in request.vars:
|
||||||
return_to_url = self.return_to_url + '?_next=' + request.vars._next
|
return_to_url = self.return_to_url + \
|
||||||
|
'?_next=' + request.vars._next
|
||||||
url = consumerhelper.begin(oid, self.realm, return_to_url)
|
url = consumerhelper.begin(oid, self.realm, return_to_url)
|
||||||
except DiscoveryFailure:
|
except DiscoveryFailure:
|
||||||
warning_openid_fail(session)
|
warning_openid_fail(session)
|
||||||
@@ -353,7 +360,8 @@ width: 400px;
|
|||||||
"""
|
"""
|
||||||
Set expiration for OpenID authentication.
|
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):
|
def _w2popenid_expired(self, w2popenid):
|
||||||
"""
|
"""
|
||||||
@@ -369,7 +377,8 @@ width: 400px;
|
|||||||
request = current.request
|
request = current.request
|
||||||
request_vars = request.vars
|
request_vars = request.vars
|
||||||
consumerhelper = self._init_consumerhelper()
|
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":
|
if process_status == "success":
|
||||||
w2popenid = current.session.w2popenid
|
w2popenid = current.session.w2popenid
|
||||||
user_data = self.consumerhelper.sreg()
|
user_data = self.consumerhelper.sreg()
|
||||||
@@ -388,7 +397,7 @@ width: 400px;
|
|||||||
def list_user_openids(self):
|
def list_user_openids(self):
|
||||||
messages = self.messages
|
messages = self.messages
|
||||||
request = current.request
|
request = current.request
|
||||||
if request.vars.has_key('delete_openid'):
|
if 'delete_openid' in request.vars:
|
||||||
self.remove_openid(request.vars.delete_openid)
|
self.remove_openid(request.vars.delete_openid)
|
||||||
|
|
||||||
query = self.db.alt_logins.user == self.auth.user.id
|
query = self.db.alt_logins.user == self.auth.user.id
|
||||||
@@ -412,11 +421,11 @@ width: 400px;
|
|||||||
)
|
)
|
||||||
return openid_list
|
return openid_list
|
||||||
|
|
||||||
|
|
||||||
def remove_openid(self, openid):
|
def remove_openid(self, openid):
|
||||||
query = self.db.alt_logins.username == openid
|
query = self.db.alt_logins.username == openid
|
||||||
self.db(query).delete()
|
self.db(query).delete()
|
||||||
|
|
||||||
|
|
||||||
class ConsumerHelper(object):
|
class ConsumerHelper(object):
|
||||||
"""
|
"""
|
||||||
ConsumerHelper knows the python-openid and
|
ConsumerHelper knows the python-openid and
|
||||||
@@ -434,7 +443,7 @@ class ConsumerHelper(object):
|
|||||||
if not hasattr(self, "store"):
|
if not hasattr(self, "store"):
|
||||||
store = Web2pyStore(db)
|
store = Web2pyStore(db)
|
||||||
session = self.session
|
session = self.session
|
||||||
if not session.has_key('w2popenid'):
|
if 'w2popenid' not in session:
|
||||||
session.w2popenid = Storage()
|
session.w2popenid = Storage()
|
||||||
self.store = store
|
self.store = store
|
||||||
return self.store
|
return self.store
|
||||||
@@ -504,18 +513,26 @@ class Web2pyStore(OpenIDStore):
|
|||||||
|
|
||||||
if self.table_oid_associations_name not in self.database:
|
if self.table_oid_associations_name not in self.database:
|
||||||
self.database.define_table(self.table_oid_associations_name,
|
self.database.define_table(self.table_oid_associations_name,
|
||||||
Field('server_url', 'string', length=2047, required=True),
|
Field('server_url',
|
||||||
Field('handle', 'string', length=255, required=True),
|
'string', length=2047, required=True),
|
||||||
|
Field('handle',
|
||||||
|
'string', length=255, required=True),
|
||||||
Field('secret', 'blob', required=True),
|
Field('secret', 'blob', required=True),
|
||||||
Field('issued', 'integer', required=True),
|
Field('issued',
|
||||||
Field('lifetime', 'integer', required=True),
|
'integer', required=True),
|
||||||
Field('assoc_type', 'string', length=64, 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:
|
if self.table_oid_nonces_name not in self.database:
|
||||||
self.database.define_table(self.table_oid_nonces_name,
|
self.database.define_table(self.table_oid_nonces_name,
|
||||||
Field('server_url', 'string', length=2047, required=True),
|
Field('server_url',
|
||||||
Field('timestamp', 'integer', required=True),
|
'string', length=2047, required=True),
|
||||||
Field('salt', 'string', length=40, required=True)
|
Field('timestamp',
|
||||||
|
'integer', required=True),
|
||||||
|
Field('salt', 'string',
|
||||||
|
length=40, required=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
def storeAssociation(self, server_url, association):
|
def storeAssociation(self, server_url, association):
|
||||||
@@ -525,7 +542,8 @@ class Web2pyStore(OpenIDStore):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
db = self.database
|
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(query).delete()
|
||||||
db.oid_associations.insert(server_url=server_url,
|
db.oid_associations.insert(server_url=server_url,
|
||||||
handle=association.handle,
|
handle=association.handle,
|
||||||
@@ -550,7 +568,8 @@ class Web2pyStore(OpenIDStore):
|
|||||||
if len(keep_assoc) == 0:
|
if len(keep_assoc) == 0:
|
||||||
return None
|
return None
|
||||||
else:
|
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'],
|
return Association(assoc['handle'],
|
||||||
assoc['secret'],
|
assoc['secret'],
|
||||||
assoc['issued'],
|
assoc['issued'],
|
||||||
@@ -559,8 +578,9 @@ class Web2pyStore(OpenIDStore):
|
|||||||
|
|
||||||
def removeAssociation(self, server_url, handle):
|
def removeAssociation(self, server_url, handle):
|
||||||
db = self.database
|
db = self.database
|
||||||
query = (db.oid_associations.server_url == server_url) & (db.oid_associations.handle == handle)
|
query = (db.oid_associations.server_url == server_url) & (
|
||||||
return db(query).delete() != None
|
db.oid_associations.handle == handle)
|
||||||
|
return db(query).delete() is not None
|
||||||
|
|
||||||
def useNonce(self, server_url, timestamp, salt):
|
def useNonce(self, server_url, timestamp, salt):
|
||||||
"""
|
"""
|
||||||
@@ -628,6 +648,3 @@ class Web2pyStore(OpenIDStore):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
return self.cleanupNonces(), self.cleanupAssociations()
|
return self.cleanupNonces(), self.cleanupAssociations()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
from gluon.contrib.pam import authenticate
|
from gluon.contrib.pam import authenticate
|
||||||
|
|
||||||
|
|
||||||
def pam_auth():
|
def pam_auth():
|
||||||
"""
|
"""
|
||||||
to use pam_login:
|
to use pam_login:
|
||||||
@@ -19,5 +20,3 @@ def pam_auth():
|
|||||||
return authenticate(username, password)
|
return authenticate(username, password)
|
||||||
|
|
||||||
return pam_auth_aux
|
return pam_auth_aux
|
||||||
|
|
||||||
|
|
||||||
@@ -19,11 +19,13 @@ from gluon.tools import fetch
|
|||||||
from gluon.storage import Storage
|
from gluon.storage import Storage
|
||||||
import gluon.contrib.simplejson as json
|
import gluon.contrib.simplejson as json
|
||||||
|
|
||||||
|
|
||||||
class RPXAccount(object):
|
class RPXAccount(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from gluon.contrib.login_methods.rpx_account import RPXAccount
|
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,
|
auth.settings.login_form = RPXAccount(request,
|
||||||
api_key="...",
|
api_key="...",
|
||||||
domain="...",
|
domain="...",
|
||||||
@@ -78,14 +80,16 @@ class RPXAccount(object):
|
|||||||
request = self.request
|
request = self.request
|
||||||
if request.vars.token:
|
if request.vars.token:
|
||||||
user = Storage()
|
user = Storage()
|
||||||
data = urllib.urlencode(dict(apiKey = self.api_key, token=request.vars.token))
|
data = urllib.urlencode(
|
||||||
|
dict(apiKey=self.api_key, token=request.vars.token))
|
||||||
auth_info_json = fetch(self.auth_url + '?' + data)
|
auth_info_json = fetch(self.auth_url + '?' + data)
|
||||||
auth_info = json.loads(auth_info_json)
|
auth_info = json.loads(auth_info_json)
|
||||||
|
|
||||||
if auth_info['stat'] == 'ok':
|
if auth_info['stat'] == 'ok':
|
||||||
self.profile = auth_info['profile']
|
self.profile = auth_info['profile']
|
||||||
provider = re.sub('[^\w\-]', '', self.profile['providerName'])
|
provider = re.sub('[^\w\-]', '', self.profile['providerName'])
|
||||||
user = self.mappings.get(provider,self.mappings.default)(self.profile)
|
user = self.mappings.get(
|
||||||
|
provider, self.mappings.default)(self.profile)
|
||||||
return user
|
return user
|
||||||
elif self.on_login_failure:
|
elif self.on_login_failure:
|
||||||
redirect(self.on_login_failure)
|
redirect(self.on_login_failure)
|
||||||
@@ -97,7 +101,9 @@ class RPXAccount(object):
|
|||||||
if self.embed:
|
if self.embed:
|
||||||
JANRAIN_URL = \
|
JANRAIN_URL = \
|
||||||
"https://%s.rpxnow.com/openid/embed?token_url=%s&language_preference=%s"
|
"https://%s.rpxnow.com/openid/embed?token_url=%s&language_preference=%s"
|
||||||
rpxform = IFRAME(_src=JANRAIN_URL % (self.domain,self.token_url,self.language),
|
rpxform = IFRAME(
|
||||||
|
_src=JANRAIN_URL % (
|
||||||
|
self.domain, self.token_url, self.language),
|
||||||
_scrolling="no",
|
_scrolling="no",
|
||||||
_frameborder="no",
|
_frameborder="no",
|
||||||
_style="width:400px;height:240px;")
|
_style="width:400px;height:240px;")
|
||||||
@@ -114,6 +120,7 @@ class RPXAccount(object):
|
|||||||
_type="text/javascript"))
|
_type="text/javascript"))
|
||||||
return rpxform
|
return rpxform
|
||||||
|
|
||||||
|
|
||||||
def use_janrain(auth, filename='private/janrain.key', **kwargs):
|
def use_janrain(auth, filename='private/janrain.key', **kwargs):
|
||||||
path = os.path.join(current.request.folder, filename)
|
path = os.path.join(current.request.folder, filename)
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
@@ -125,4 +132,3 @@ def use_janrain(auth,filename='private/janrain.key',**kwargs):
|
|||||||
['register', 'change_password', 'request_reset_password']
|
['register', 'change_password', 'request_reset_password']
|
||||||
auth.settings.login_form = RPXAccount(
|
auth.settings.login_form = RPXAccount(
|
||||||
request, api_key=key, domain=domain, url=url, **kwargs)
|
request, api_key=key, domain=domain, url=url, **kwargs)
|
||||||
|
|
||||||
@@ -17,7 +17,6 @@ from gluon.http import HTTP,redirect
|
|||||||
from M2Crypto import X509
|
from M2Crypto import X509
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class X509Auth(object):
|
class X509Auth(object):
|
||||||
"""
|
"""
|
||||||
Login using x509 cert from client.
|
Login using x509 cert from client.
|
||||||
@@ -29,8 +28,6 @@ class X509Auth(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.request = current.request
|
self.request = current.request
|
||||||
self.ssl_client_raw_cert = self.request.env.ssl_client_raw_cert
|
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:
|
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
|
# 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()
|
subject = x509.get_subject()
|
||||||
|
|
||||||
@@ -54,23 +52,17 @@ class X509Auth(object):
|
|||||||
self.subject = Storage(filter(None,
|
self.subject = Storage(filter(None,
|
||||||
map(lambda x:
|
map(lambda x:
|
||||||
(x, map(lambda y:
|
(x, map(lambda y:
|
||||||
y.get_data().as_text(),
|
y.get_data(
|
||||||
|
).as_text(),
|
||||||
subject.get_entries_by_nid(subject.nid[x]))),
|
subject.get_entries_by_nid(subject.nid[x]))),
|
||||||
subject.nid.keys())))
|
subject.nid.keys())))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def login_form(self, **args):
|
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="/"):
|
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="/"):
|
def logout_url(self, next="/"):
|
||||||
return next
|
return next
|
||||||
|
|
||||||
@@ -86,10 +78,14 @@ class X509Auth(object):
|
|||||||
|
|
||||||
p = profile = dict()
|
p = profile = dict()
|
||||||
|
|
||||||
username = p['username'] = reduce(lambda a,b: '%s | %s' % (a,b), self.subject.CN or self.subject.commonName)
|
username = p['username'] = reduce(lambda a, b: '%s | %s' % (
|
||||||
p['first_name'] = reduce(lambda a,b: '%s | %s' % (a,b),self.subject.givenName or username)
|
a, b), self.subject.CN or self.subject.commonName)
|
||||||
p['last_name'] = reduce(lambda a,b: '%s | %s' % (a,b),self.subject.surname)
|
p['first_name'] = reduce(lambda a, b: '%s | %s' % (a, b),
|
||||||
p['email'] = reduce(lambda a,b: '%s | %s' % (a,b),self.subject.Email or self.subject.emailAddress)
|
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
|
# IMPORTANT WE USE THE CERT SERIAL AS UNIQUE KEY FOR THE USER
|
||||||
p['registration_id'] = self.serial
|
p['registration_id'] = self.serial
|
||||||
@@ -100,6 +96,3 @@ class X509Auth(object):
|
|||||||
p['certificate'] = self.ssl_client_raw_cert
|
p['certificate'] = self.ssl_client_raw_cert
|
||||||
|
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+35
-36
@@ -49,8 +49,7 @@ SQL_DIALECTS = {'memcache': {
|
|||||||
|
|
||||||
def cleanup(text):
|
def cleanup(text):
|
||||||
if re.compile('[^0-9a-zA-Z_]').findall(text):
|
if re.compile('[^0-9a-zA-Z_]').findall(text):
|
||||||
raise SyntaxError, \
|
raise SyntaxError('Can\'t cleanup \'%s\': only [0-9a-zA-Z_] allowed in table and field names' % text)
|
||||||
'Can\'t cleanup \'%s\': only [0-9a-zA-Z_] allowed in table and field names' % text
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
@@ -58,8 +57,8 @@ def assert_filter_fields(*fields):
|
|||||||
for field in fields:
|
for field in fields:
|
||||||
if isinstance(field, (Field, Expression)) and field.type\
|
if isinstance(field, (Field, Expression)) and field.type\
|
||||||
in ['text', 'blob']:
|
in ['text', 'blob']:
|
||||||
raise SyntaxError, 'AppEngine does not index by: %s'\
|
raise SyntaxError('AppEngine does not index by: %s'
|
||||||
% field.type
|
% field.type)
|
||||||
|
|
||||||
|
|
||||||
def dateobj_to_datetime(object):
|
def dateobj_to_datetime(object):
|
||||||
@@ -114,7 +113,8 @@ class DALStorage(dict):
|
|||||||
|
|
||||||
def __setattr__(self, key, value):
|
def __setattr__(self, key, value):
|
||||||
if key in self:
|
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
|
self[key] = value
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@@ -154,11 +154,11 @@ class MEMDB(DALStorage):
|
|||||||
):
|
):
|
||||||
tablename = cleanup(tablename)
|
tablename = cleanup(tablename)
|
||||||
if tablename in dir(self) or tablename[0] == '_':
|
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:
|
if not tablename in self.tables:
|
||||||
self.tables.append(tablename)
|
self.tables.append(tablename)
|
||||||
else:
|
else:
|
||||||
raise SyntaxError, 'table already defined: %s' % tablename
|
raise SyntaxError('table already defined: %s' % tablename)
|
||||||
t = self[tablename] = Table(self, tablename, *fields)
|
t = self[tablename] = Table(self, tablename, *fields)
|
||||||
t._create()
|
t._create()
|
||||||
return t
|
return t
|
||||||
@@ -219,24 +219,24 @@ class Table(DALStorage):
|
|||||||
if field.type[:9] == 'reference':
|
if field.type[:9] == 'reference':
|
||||||
referenced = field.type[10:].strip()
|
referenced = field.type[10:].strip()
|
||||||
if not referenced:
|
if not referenced:
|
||||||
raise SyntaxError, \
|
raise SyntaxError('Table %s: reference \'%s\' to nothing!' % (
|
||||||
'Table %s: reference \'%s\' to nothing!' % (self._tablename, k)
|
self._tablename, k))
|
||||||
if not referenced in self._db:
|
if not referenced in self._db:
|
||||||
raise SyntaxError, \
|
raise SyntaxError(
|
||||||
'Table: table %s does not exist' % referenced
|
'Table: table %s does not exist' % referenced)
|
||||||
referee = self._db[referenced]
|
referee = self._db[referenced]
|
||||||
ftype = \
|
ftype = \
|
||||||
self._db._translator[field.type[:9]](
|
self._db._translator[field.type[:9]](
|
||||||
self._db[referenced]._tableobj)
|
self._db[referenced]._tableobj)
|
||||||
if self._tablename in referee.fields: # ## THIS IS OK
|
if self._tablename in referee.fields: # ## THIS IS OK
|
||||||
raise SyntaxError, \
|
raise SyntaxError('Field: table \'%s\' has same name as a field '
|
||||||
'Field: table \'%s\' has same name as a field ' \
|
'in referenced table \'%s\'' % (
|
||||||
'in referenced table \'%s\'' % (self._tablename, referenced)
|
self._tablename, referenced))
|
||||||
self._db[referenced]._referenced_by.append((self._tablename,
|
self._db[referenced]._referenced_by.append((self._tablename,
|
||||||
field.name))
|
field.name))
|
||||||
elif not field.type in self._db._translator\
|
elif not field.type in self._db._translator\
|
||||||
or not self._db._translator[field.type]:
|
or not self._db._translator[field.type]:
|
||||||
raise SyntaxError, 'Field: unkown field type %s' % field.type
|
raise SyntaxError('Field: unkown field type %s' % field.type)
|
||||||
self._tableobj = self._db.client
|
self._tableobj = self._db.client
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -269,7 +269,7 @@ class Table(DALStorage):
|
|||||||
def update(self, id, **fields):
|
def update(self, id, **fields):
|
||||||
for field in fields:
|
for field in fields:
|
||||||
if not field in fields and self[field].default\
|
if not field in fields and self[field].default\
|
||||||
!= None:
|
is not None:
|
||||||
fields[field] = self[field].default
|
fields[field] = self[field].default
|
||||||
if field in fields:
|
if field in fields:
|
||||||
fields[field] = obj_represent(fields[field],
|
fields[field] = obj_represent(fields[field],
|
||||||
@@ -293,7 +293,7 @@ class Table(DALStorage):
|
|||||||
if self._tableobj.set(shard_id, '0'):
|
if self._tableobj.set(shard_id, '0'):
|
||||||
id = 0
|
id = 0
|
||||||
else:
|
else:
|
||||||
raise Exception, 'cannot set memcache'
|
raise Exception('cannot set memcache')
|
||||||
return long(str(shard) + str(id))
|
return long(str(shard) + str(id))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -397,7 +397,7 @@ class Field(Expression):
|
|||||||
|
|
||||||
self.name = cleanup(fieldname)
|
self.name = cleanup(fieldname)
|
||||||
if fieldname in dir(Table) or fieldname[0] == '_':
|
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):
|
if isinstance(type, Table):
|
||||||
type = 'reference ' + type._tablename
|
type = 'reference ' + type._tablename
|
||||||
if not length:
|
if not length:
|
||||||
@@ -437,7 +437,7 @@ MEMDB.Field = Field # ## required by gluon/globals.py session.connect
|
|||||||
|
|
||||||
|
|
||||||
def obj_represent(object, fieldtype, db):
|
def obj_represent(object, fieldtype, db):
|
||||||
if object != None:
|
if object is not None:
|
||||||
if fieldtype == 'date' and not isinstance(object,
|
if fieldtype == 'date' and not isinstance(object,
|
||||||
datetime.date):
|
datetime.date):
|
||||||
(y, m, d) = [int(x) for x in str(object).strip().split('-')]
|
(y, m, d) = [int(x) for x in str(object).strip().split('-')]
|
||||||
@@ -498,8 +498,8 @@ class Query(object):
|
|||||||
right=None,
|
right=None,
|
||||||
):
|
):
|
||||||
if isinstance(right, (Field, Expression)):
|
if isinstance(right, (Field, Expression)):
|
||||||
raise SyntaxError, \
|
raise SyntaxError(
|
||||||
'Query: right side of filter must be a value or entity'
|
'Query: right side of filter must be a value or entity')
|
||||||
if isinstance(left, Field) and left.name == 'id':
|
if isinstance(left, Field) and left.name == 'id':
|
||||||
if op == '=':
|
if op == '=':
|
||||||
self.get_one = \
|
self.get_one = \
|
||||||
@@ -507,8 +507,8 @@ class Query(object):
|
|||||||
id=long(right))
|
id=long(right))
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
raise SyntaxError, 'only equality by id is supported'
|
raise SyntaxError('only equality by id is supported')
|
||||||
raise SyntaxError, 'not supported'
|
raise SyntaxError('not supported')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.left)
|
return str(self.left)
|
||||||
@@ -554,8 +554,7 @@ class Set(object):
|
|||||||
def __call__(self, where):
|
def __call__(self, where):
|
||||||
if isinstance(self.where, QueryException) or isinstance(where,
|
if isinstance(self.where, QueryException) or isinstance(where,
|
||||||
QueryException):
|
QueryException):
|
||||||
raise SyntaxError, \
|
raise SyntaxError('neither self.where nor where can be a QueryException instance')
|
||||||
'neither self.where nor where can be a QueryException instance'
|
|
||||||
if self.where:
|
if self.where:
|
||||||
return Set(self._db, self.where & where)
|
return Set(self._db, self.where & where)
|
||||||
else:
|
else:
|
||||||
@@ -564,9 +563,9 @@ class Set(object):
|
|||||||
def _get_table_or_raise(self):
|
def _get_table_or_raise(self):
|
||||||
tablenames = list(set(self._tables)) # unique
|
tablenames = list(set(self._tables)) # unique
|
||||||
if len(tablenames) < 1:
|
if len(tablenames) < 1:
|
||||||
raise SyntaxError, 'Set: no tables selected'
|
raise SyntaxError('Set: no tables selected')
|
||||||
if len(tablenames) > 1:
|
if len(tablenames) > 1:
|
||||||
raise SyntaxError, 'Set: no join in appengine'
|
raise SyntaxError('Set: no join in appengine')
|
||||||
return self._db[tablenames[0]]._tableobj
|
return self._db[tablenames[0]]._tableobj
|
||||||
|
|
||||||
def _getitem_exception(self):
|
def _getitem_exception(self):
|
||||||
@@ -597,7 +596,7 @@ class Set(object):
|
|||||||
if isinstance(self.where, QueryException):
|
if isinstance(self.where, QueryException):
|
||||||
return self._select_except()
|
return self._select_except()
|
||||||
else:
|
else:
|
||||||
raise SyntaxError, 'select arguments not supported'
|
raise SyntaxError('select arguments not supported')
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
return len(self.select())
|
return len(self.select())
|
||||||
@@ -609,7 +608,7 @@ class Set(object):
|
|||||||
return
|
return
|
||||||
self._db[tablename].delete(id)
|
self._db[tablename].delete(id)
|
||||||
else:
|
else:
|
||||||
raise Exception, 'deletion not implemented'
|
raise Exception('deletion not implemented')
|
||||||
|
|
||||||
def update(self, **update_fields):
|
def update(self, **update_fields):
|
||||||
if isinstance(self.where, QueryException):
|
if isinstance(self.where, QueryException):
|
||||||
@@ -620,7 +619,7 @@ class Set(object):
|
|||||||
setattr(item, key, value)
|
setattr(item, key, value)
|
||||||
self._db[tablename].update(id, **item)
|
self._db[tablename].update(id, **item)
|
||||||
else:
|
else:
|
||||||
raise Exception, 'update not implemented'
|
raise Exception('update not implemented')
|
||||||
|
|
||||||
|
|
||||||
def update_record(
|
def update_record(
|
||||||
@@ -660,9 +659,9 @@ class Rows(object):
|
|||||||
|
|
||||||
def __getitem__(self, i):
|
def __getitem__(self, i):
|
||||||
if i >= len(self.response) or i < 0:
|
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):
|
if len(self.response[0]) != len(self.colnames):
|
||||||
raise SyntaxError, 'Rows: internal error'
|
raise SyntaxError('Rows: internal error')
|
||||||
row = DALStorage()
|
row = DALStorage()
|
||||||
for j in xrange(len(self.colnames)):
|
for j in xrange(len(self.colnames)):
|
||||||
value = self.response[i][j]
|
value = self.response[i][j]
|
||||||
@@ -684,7 +683,7 @@ class Rows(object):
|
|||||||
referee = field.type[10:].strip()
|
referee = field.type[10:].strip()
|
||||||
rid = value
|
rid = value
|
||||||
row[tablename][fieldname] = rid
|
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)
|
# row[tablename][fieldname]=Set(self._db[referee].id==rid)
|
||||||
|
|
||||||
@@ -692,12 +691,12 @@ class Rows(object):
|
|||||||
row[tablename][fieldname] = True
|
row[tablename][fieldname] = True
|
||||||
else:
|
else:
|
||||||
row[tablename][fieldname] = False
|
row[tablename][fieldname] = False
|
||||||
elif field.type == 'date' and value != None\
|
elif field.type == 'date' and value is not None\
|
||||||
and not isinstance(value, datetime.date):
|
and not isinstance(value, datetime.date):
|
||||||
(y, m, d) = [int(x) for x in
|
(y, m, d) = [int(x) for x in
|
||||||
str(value).strip().split('-')]
|
str(value).strip().split('-')]
|
||||||
row[tablename][fieldname] = datetime.date(y, m, d)
|
row[tablename][fieldname] = datetime.date(y, m, d)
|
||||||
elif field.type == 'time' and value != None\
|
elif field.type == 'time' and value is not None\
|
||||||
and not isinstance(value, datetime.time):
|
and not isinstance(value, datetime.time):
|
||||||
time_items = [int(x) for x in
|
time_items = [int(x) for x in
|
||||||
str(value).strip().split(':')[:3]]
|
str(value).strip().split(':')[:3]]
|
||||||
@@ -706,7 +705,7 @@ class Rows(object):
|
|||||||
else:
|
else:
|
||||||
(h, mi, s) = time_items + [0]
|
(h, mi, s) = time_items + [0]
|
||||||
row[tablename][fieldname] = datetime.time(h, mi, s)
|
row[tablename][fieldname] = datetime.time(h, mi, s)
|
||||||
elif field.type == 'datetime' and value != None\
|
elif field.type == 'datetime' and value is not None\
|
||||||
and not isinstance(value, datetime.datetime):
|
and not isinstance(value, datetime.datetime):
|
||||||
(y, m, d) = [int(x) for x in
|
(y, m, d) = [int(x) for x in
|
||||||
str(value)[:10].strip().split('-')]
|
str(value)[:10].strip().split('-')]
|
||||||
|
|||||||
@@ -154,7 +154,8 @@ def condense_hex_colors(css):
|
|||||||
first = match.group(3) + match.group(5) + match.group(7)
|
first = match.group(3) + match.group(5) + match.group(7)
|
||||||
second = match.group(4) + match.group(6) + match.group(8)
|
second = match.group(4) + match.group(6) + match.group(8)
|
||||||
if first.lower() == second.lower():
|
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)
|
match = regex.search(css, match.end() - 3)
|
||||||
else:
|
else:
|
||||||
match = regex.search(css, match.end())
|
match = regex.search(css, match.end())
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
def minify(response):
|
def minify(response):
|
||||||
def _replace(match):
|
def _replace(match):
|
||||||
match = match.group()
|
match = match.group()
|
||||||
@@ -9,5 +10,6 @@ def minify(response):
|
|||||||
# otherwise, replace all whitespace with a single space character
|
# otherwise, replace all whitespace with a single space character
|
||||||
return match if match.startswith(('<pre', '<textarea', '<!--')) else ' '
|
return match if match.startswith(('<pre', '<textarea', '<!--')) else ' '
|
||||||
|
|
||||||
cpat = re.compile(r'\s+|<pre(.*?)</pre>|<textarea(.*?)</textarea>|<!--\s', re.DOTALL)
|
cpat = re.compile(
|
||||||
|
r'\s+|<pre(.*?)</pre>|<textarea(.*?)</textarea>|<!--\s', re.DOTALL)
|
||||||
return cpat.sub(_replace, response)
|
return cpat.sub(_replace, response)
|
||||||
@@ -196,17 +196,25 @@ def _make_jsmin(extended=True, python_only=True):
|
|||||||
r'|%(space)s+'
|
r'|%(space)s+'
|
||||||
r'|(?:%(newline)s%(space)s*)+'
|
r'|(?:%(newline)s%(space)s*)+'
|
||||||
) % locals()).sub
|
) % locals()).sub
|
||||||
|
|
||||||
def space_subber(match):
|
def space_subber(match):
|
||||||
""" Substitution callback """
|
""" Substitution callback """
|
||||||
# pylint: disable = C0321, R0911
|
# pylint: disable = C0321, R0911
|
||||||
groups = match.groups()
|
groups = match.groups()
|
||||||
if groups[0]: return groups[0]
|
if groups[0]:
|
||||||
elif groups[1]: return groups[1]
|
return groups[0]
|
||||||
elif groups[2]: return groups[2]
|
elif groups[1]:
|
||||||
elif groups[3]: return groups[3]
|
return groups[1]
|
||||||
elif groups[4]: return '\n'
|
elif groups[2]:
|
||||||
elif groups[5]: return ' '
|
return groups[2]
|
||||||
else: return ''
|
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"""
|
r"""
|
||||||
@@ -239,20 +247,26 @@ def _make_jsmin(extended=True, python_only=True):
|
|||||||
r'|(%(space)s)+'
|
r'|(%(space)s)+'
|
||||||
r'|(?:(%(newline)s)%(space)s*)+'
|
r'|(?:(%(newline)s)%(space)s*)+'
|
||||||
) % locals()).sub
|
) % locals()).sub
|
||||||
|
|
||||||
def space_norm_subber(match):
|
def space_norm_subber(match):
|
||||||
""" Substitution callback """
|
""" Substitution callback """
|
||||||
# pylint: disable = C0321
|
# pylint: disable = C0321
|
||||||
groups = match.groups()
|
groups = match.groups()
|
||||||
if groups[0]: return groups[0]
|
if groups[0]:
|
||||||
elif groups[1]: return groups[1].replace('\r', '\n') + groups[2]
|
return groups[0]
|
||||||
elif groups[3]: return ' '
|
elif groups[1]:
|
||||||
elif groups[4]: return '\n'
|
return groups[1].replace('\r', '\n') + groups[2]
|
||||||
|
elif groups[3]:
|
||||||
|
return ' '
|
||||||
|
elif groups[4]:
|
||||||
|
return '\n'
|
||||||
|
|
||||||
space_sub1 = _re.compile((
|
space_sub1 = _re.compile((
|
||||||
r'[\040\n]?(%(strings)s|%(pre_regex)s%(regex)s)'
|
r'[\040\n]?(%(strings)s|%(pre_regex)s%(regex)s)'
|
||||||
r'|\040(%(not_id_literal)s)'
|
r'|\040(%(not_id_literal)s)'
|
||||||
r'|\n(%(not_id_literal_open)s)'
|
r'|\n(%(not_id_literal_open)s)'
|
||||||
) % locals()).sub
|
) % locals()).sub
|
||||||
|
|
||||||
def space_subber1(match):
|
def space_subber1(match):
|
||||||
""" Substitution callback """
|
""" Substitution callback """
|
||||||
groups = match.groups()
|
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)s)\040'
|
||||||
r'|(%(not_id_literal_close)s)\n'
|
r'|(%(not_id_literal_close)s)\n'
|
||||||
) % locals()).sub
|
) % locals()).sub
|
||||||
|
|
||||||
def space_subber2(match):
|
def space_subber2(match):
|
||||||
""" Substitution callback """
|
""" Substitution callback """
|
||||||
groups = match.groups()
|
groups = match.groups()
|
||||||
@@ -296,7 +311,8 @@ def _make_jsmin(extended=True, python_only=True):
|
|||||||
"""
|
"""
|
||||||
return space_sub2(space_subber2,
|
return space_sub2(space_subber2,
|
||||||
space_sub1(space_subber1,
|
space_sub1(space_subber1,
|
||||||
space_norm_sub(space_norm_subber, '\n%s\n' % script)
|
space_norm_sub(space_norm_subber,
|
||||||
|
'\n%s\n' % script)
|
||||||
)
|
)
|
||||||
).strip()
|
).strip()
|
||||||
return jsmin
|
return jsmin
|
||||||
@@ -311,6 +327,7 @@ jsmin = _make_jsmin()
|
|||||||
# jsmin.jsmin(script)
|
# jsmin.jsmin(script)
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
def jsmin_for_posers(script):
|
def jsmin_for_posers(script):
|
||||||
r"""
|
r"""
|
||||||
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
|
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
|
||||||
|
|||||||
@@ -14,17 +14,20 @@ import os
|
|||||||
import hashlib
|
import hashlib
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
def read_binary_file(filename):
|
def read_binary_file(filename):
|
||||||
f = open(filename, 'rb')
|
f = open(filename, 'rb')
|
||||||
data = f.read()
|
data = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def write_binary_file(filename, data):
|
def write_binary_file(filename, data):
|
||||||
f = open(filename, 'wb')
|
f = open(filename, 'wb')
|
||||||
f.write(data)
|
f.write(data)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def fix_links(css, static_path):
|
def fix_links(css, static_path):
|
||||||
return re.sub(r'url\((["\'])\.\./', 'url(\\1' + static_path, css)
|
return re.sub(r'url\((["\'])\.\./', 'url(\\1' + static_path, css)
|
||||||
|
|
||||||
@@ -62,7 +65,7 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
|||||||
processed = []
|
processed = []
|
||||||
for k, filename in enumerate(files):
|
for k, filename in enumerate(files):
|
||||||
if not filename.startswith('/') or \
|
if not filename.startswith('/') or \
|
||||||
any(filename.endswith(x) \
|
any(filename.endswith(x)
|
||||||
for x in ignore_concat):
|
for x in ignore_concat):
|
||||||
new_files.append(filename)
|
new_files.append(filename)
|
||||||
continue
|
continue
|
||||||
@@ -99,7 +102,7 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
|||||||
|
|
||||||
if minify_js and \
|
if minify_js and \
|
||||||
not filename.endswith('.min.js') and \
|
not filename.endswith('.min.js') and \
|
||||||
not any(filename.endswith(x) \
|
not any(filename.endswith(x)
|
||||||
for x in ignore_minify):
|
for x in ignore_minify):
|
||||||
js.append(jsmin.jsmin(contents))
|
js.append(jsmin.jsmin(contents))
|
||||||
else:
|
else:
|
||||||
@@ -138,4 +141,3 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
|||||||
else:
|
else:
|
||||||
new_files += js
|
new_files += js
|
||||||
return new_files
|
return new_files
|
||||||
|
|
||||||
@@ -32,6 +32,7 @@ PAM_PROMPT_ECHO_ON = 2
|
|||||||
PAM_ERROR_MSG = 3
|
PAM_ERROR_MSG = 3
|
||||||
PAM_TEXT_INFO = 4
|
PAM_TEXT_INFO = 4
|
||||||
|
|
||||||
|
|
||||||
class PamHandle(Structure):
|
class PamHandle(Structure):
|
||||||
"""wrapper class for pam_handle_t"""
|
"""wrapper class for pam_handle_t"""
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
@@ -42,6 +43,7 @@ class PamHandle(Structure):
|
|||||||
Structure.__init__(self)
|
Structure.__init__(self)
|
||||||
self.handle = 0
|
self.handle = 0
|
||||||
|
|
||||||
|
|
||||||
class PamMessage(Structure):
|
class PamMessage(Structure):
|
||||||
"""wrapper class for pam_message structure"""
|
"""wrapper class for pam_message structure"""
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
@@ -52,6 +54,7 @@ class PamMessage(Structure):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<PamMessage %i '%s'>" % (self.msg_style, self.msg)
|
return "<PamMessage %i '%s'>" % (self.msg_style, self.msg)
|
||||||
|
|
||||||
|
|
||||||
class PamResponse(Structure):
|
class PamResponse(Structure):
|
||||||
"""wrapper class for pam_response structure"""
|
"""wrapper class for pam_response structure"""
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
@@ -66,6 +69,7 @@ CONV_FUNC = CFUNCTYPE(c_int,
|
|||||||
c_int, POINTER(POINTER(PamMessage)),
|
c_int, POINTER(POINTER(PamMessage)),
|
||||||
POINTER(POINTER(PamResponse)), c_void_p)
|
POINTER(POINTER(PamResponse)), c_void_p)
|
||||||
|
|
||||||
|
|
||||||
class PamConv(Structure):
|
class PamConv(Structure):
|
||||||
"""wrapper class for pam_conv structure"""
|
"""wrapper class for pam_conv structure"""
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
@@ -82,6 +86,7 @@ PAM_AUTHENTICATE = LIBPAM.pam_authenticate
|
|||||||
PAM_AUTHENTICATE.restype = c_int
|
PAM_AUTHENTICATE.restype = c_int
|
||||||
PAM_AUTHENTICATE.argtypes = [PamHandle, c_int]
|
PAM_AUTHENTICATE.argtypes = [PamHandle, c_int]
|
||||||
|
|
||||||
|
|
||||||
def authenticate(username, password, service='login'):
|
def authenticate(username, password, service='login'):
|
||||||
"""Returns True if the given username and password authenticate for the
|
"""Returns True if the given username and password authenticate for the
|
||||||
given service. Returns False otherwise
|
given service. Returns False otherwise
|
||||||
|
|||||||
+10
-3
@@ -24,6 +24,7 @@ import cmd
|
|||||||
import pydoc
|
import pydoc
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
|
||||||
class Qdb(bdb.Bdb):
|
class Qdb(bdb.Bdb):
|
||||||
"Qdb Debugger Backend"
|
"Qdb Debugger Backend"
|
||||||
|
|
||||||
@@ -354,9 +355,11 @@ class Qdb(bdb.Bdb):
|
|||||||
# converts the frame global and locals to a short text representation:
|
# converts the frame global and locals to a short text representation:
|
||||||
if self.frame:
|
if self.frame:
|
||||||
for name, value in self.frame_locals.items():
|
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():
|
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
|
return env
|
||||||
|
|
||||||
def get_autocomplete_list(self, expression):
|
def get_autocomplete_list(self, expression):
|
||||||
@@ -609,7 +612,8 @@ class Frontend(object):
|
|||||||
# nested request received (i.e. readline)! process it!
|
# nested request received (i.e. readline)! process it!
|
||||||
self.process_message(res)
|
self.process_message(res)
|
||||||
elif long(req['id']) != long(res['id']):
|
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
|
# protocol state is unknown
|
||||||
elif 'error' in res and res['error']:
|
elif 'error' in res and res['error']:
|
||||||
raise RPCError(res['error']['message'])
|
raise RPCError(res['error']['message'])
|
||||||
@@ -838,6 +842,7 @@ def test():
|
|||||||
class Test(Frontend):
|
class Test(Frontend):
|
||||||
def interaction(self, *args):
|
def interaction(self, *args):
|
||||||
print "interaction!", args
|
print "interaction!", args
|
||||||
|
|
||||||
def exception(self, *args):
|
def exception(self, *args):
|
||||||
print "exception", args
|
print "exception", args
|
||||||
#raise RuntimeError("exception %s" % repr(args))
|
#raise RuntimeError("exception %s" % repr(args))
|
||||||
@@ -913,6 +918,8 @@ def main(host='localhost', port=6000, authkey='secret password'):
|
|||||||
|
|
||||||
|
|
||||||
qdb = None
|
qdb = None
|
||||||
|
|
||||||
|
|
||||||
def set_trace(host='localhost', port=6000, authkey='secret password'):
|
def set_trace(host='localhost', port=6000, authkey='secret password'):
|
||||||
"Simplified interface to debug running programs"
|
"Simplified interface to debug running programs"
|
||||||
global qdb, listener, conn
|
global qdb, listener, conn
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ logger = logging.getLogger("web2py.cache.redis")
|
|||||||
|
|
||||||
locker = thread.allocate_lock()
|
locker = thread.allocate_lock()
|
||||||
|
|
||||||
|
|
||||||
def RedisCache(*args, **vars):
|
def RedisCache(*args, **vars):
|
||||||
"""
|
"""
|
||||||
Usage example: put in models
|
Usage example: put in models
|
||||||
@@ -47,6 +48,7 @@ class RedisClient(object):
|
|||||||
meta_storage = {}
|
meta_storage = {}
|
||||||
MAX_RETRIES = 5
|
MAX_RETRIES = 5
|
||||||
RETRIES = 0
|
RETRIES = 0
|
||||||
|
|
||||||
def __init__(self, server='localhost:6379', db=None, debug=False):
|
def __init__(self, server='localhost:6379', db=None, debug=False):
|
||||||
self.server = server
|
self.server = server
|
||||||
self.db = db or 0
|
self.db = db or 0
|
||||||
@@ -72,7 +74,7 @@ class RedisClient(object):
|
|||||||
|
|
||||||
def __call__(self, key, f, time_expire=300):
|
def __call__(self, key, f, time_expire=300):
|
||||||
try:
|
try:
|
||||||
if time_expire == None:
|
if time_expire is None:
|
||||||
time_expire = 24 * 60 * 60
|
time_expire = 24 * 60 * 60
|
||||||
newKey = self.__keyFormat__(key)
|
newKey = self.__keyFormat__(key)
|
||||||
value = None
|
value = None
|
||||||
@@ -100,13 +102,15 @@ class RedisClient(object):
|
|||||||
def retry_call(self, key, f, time_expire):
|
def retry_call(self, key, f, time_expire):
|
||||||
self.RETRIES += 1
|
self.RETRIES += 1
|
||||||
if self.RETRIES <= self.MAX_RETRIES:
|
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)
|
time.sleep(2 * self.RETRIES)
|
||||||
self.__init__(self.server, self.db, self.debug)
|
self.__init__(self.server, self.db, self.debug)
|
||||||
return self.__call__(key, f, time_expire)
|
return self.__call__(key, f, time_expire)
|
||||||
else:
|
else:
|
||||||
self.RETRIES = 0
|
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):
|
def increment(self, key, value=1, time_expire=300):
|
||||||
try:
|
try:
|
||||||
@@ -129,7 +133,8 @@ class RedisClient(object):
|
|||||||
return self.increment(key, value, time_expire)
|
return self.increment(key, value, time_expire)
|
||||||
else:
|
else:
|
||||||
self.RETRIES = 0
|
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):
|
def clear(self, regex):
|
||||||
"""
|
"""
|
||||||
@@ -139,7 +144,7 @@ class RedisClient(object):
|
|||||||
r = re.compile(regex)
|
r = re.compile(regex)
|
||||||
prefix = "w2p:%s:" % (self.request.application)
|
prefix = "w2p:%s:" % (self.request.application)
|
||||||
pipe = self.r_server.pipeline()
|
pipe = self.r_server.pipeline()
|
||||||
for a in self.r_server.keys("%s*" % \
|
for a in self.r_server.keys("%s*" %
|
||||||
(prefix)):
|
(prefix)):
|
||||||
if r.match(str(a).replace(prefix, '', 1)):
|
if r.match(str(a).replace(prefix, '', 1)):
|
||||||
pipe.delete(a)
|
pipe.delete(a)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ logger = logging.getLogger("web2py.session.redis")
|
|||||||
|
|
||||||
locker = thread.allocate_lock()
|
locker = thread.allocate_lock()
|
||||||
|
|
||||||
|
|
||||||
def RedisSession(*args, **vars):
|
def RedisSession(*args, **vars):
|
||||||
"""
|
"""
|
||||||
Usage example: put in models
|
Usage example: put in models
|
||||||
@@ -68,7 +69,8 @@ class RedisClient(object):
|
|||||||
|
|
||||||
def define_table(self, tablename, *fields, **args):
|
def define_table(self, tablename, *fields, **args):
|
||||||
if not self.tablename:
|
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
|
return self.tablename
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
@@ -82,6 +84,7 @@ class RedisClient(object):
|
|||||||
#this is only called by session2trash.py
|
#this is only called by session2trash.py
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MockTable(object):
|
class MockTable(object):
|
||||||
|
|
||||||
def __init__(self, db, r_server, tablename, session_expiry):
|
def __init__(self, db, r_server, tablename, session_expiry):
|
||||||
@@ -89,7 +92,8 @@ class MockTable(object):
|
|||||||
self.r_server = r_server
|
self.r_server = r_server
|
||||||
self.tablename = tablename
|
self.tablename = tablename
|
||||||
#set the namespace for sessions of this app
|
#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)
|
#fast auto-increment id (needed for session handling)
|
||||||
self.serial = "%s:serial" % self.keyprefix
|
self.serial = "%s:serial" % self.keyprefix
|
||||||
#index of all the session keys of this app
|
#index of all the session keys of this app
|
||||||
@@ -125,6 +129,7 @@ class MockTable(object):
|
|||||||
self.r_server.expire(key, self.session_expiry)
|
self.r_server.expire(key, self.session_expiry)
|
||||||
return newid
|
return newid
|
||||||
|
|
||||||
|
|
||||||
class MockQuery(object):
|
class MockQuery(object):
|
||||||
"""a fake Query object that supports querying by id
|
"""a fake Query object that supports querying by id
|
||||||
and listing all keys. No other operation is supported
|
and listing all keys. No other operation is supported
|
||||||
@@ -171,7 +176,8 @@ class MockQuery(object):
|
|||||||
continue
|
continue
|
||||||
val = Storage(val)
|
val = Storage(val)
|
||||||
#add a delete_record method (necessary for sessions2trash.py)
|
#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)
|
rtn.append(val)
|
||||||
return rtn
|
return rtn
|
||||||
else:
|
else:
|
||||||
@@ -185,6 +191,7 @@ class MockQuery(object):
|
|||||||
self.db.expire(key, self.session.expiry)
|
self.db.expire(key, self.session.expiry)
|
||||||
return rtn
|
return rtn
|
||||||
|
|
||||||
|
|
||||||
class RecordDeleter(object):
|
class RecordDeleter(object):
|
||||||
"""Dumb record deleter to support sessions2trash.py"""
|
"""Dumb record deleter to support sessions2trash.py"""
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ class History:
|
|||||||
if name in self.unpicklable_names:
|
if name in self.unpicklable_names:
|
||||||
self.unpicklable_names.remove(name)
|
self.unpicklable_names.remove(name)
|
||||||
|
|
||||||
|
|
||||||
def represent(obj):
|
def represent(obj):
|
||||||
"""Returns a string representing the given object's value, which should allow the
|
"""Returns a string representing the given object's value, which should allow the
|
||||||
code below to determine whether the object changes over time.
|
code below to determine whether the object changes over time.
|
||||||
@@ -163,6 +164,7 @@ def represent(obj):
|
|||||||
except:
|
except:
|
||||||
return repr(obj)
|
return repr(obj)
|
||||||
|
|
||||||
|
|
||||||
def run(history, statement, env={}):
|
def run(history, statement, env={}):
|
||||||
"""
|
"""
|
||||||
Evaluates a python statement in a given history and returns the result.
|
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.
|
# single-line expressions such as 'class Foo: pass' evaluate happily.
|
||||||
statement += '\n\n'
|
statement += '\n\n'
|
||||||
|
|
||||||
|
|
||||||
# log and compile the statement up front
|
# log and compile the statement up front
|
||||||
try:
|
try:
|
||||||
logging.info('Compiling and evaluating:\n%s' % statement)
|
logging.info('Compiling and evaluating:\n%s' % statement)
|
||||||
@@ -224,7 +225,8 @@ def run(history, statement, env={}):
|
|||||||
history.remove_global(name)
|
history.remove_global(name)
|
||||||
|
|
||||||
# run!
|
# 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:
|
try:
|
||||||
old_stdout, old_stderr = sys.stdout, sys.stderr
|
old_stdout, old_stderr = sys.stdout, sys.stderr
|
||||||
try:
|
try:
|
||||||
@@ -263,4 +265,5 @@ def run(history, statement, env={}):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
history = History()
|
history = History()
|
||||||
while True: print run(history, raw_input('>>> ')).rstrip()
|
while True:
|
||||||
|
print run(history, raw_input('>>> ')).rstrip()
|
||||||
@@ -46,8 +46,10 @@ class JSONDummyParser:
|
|||||||
"json wrapper for xmlrpclib parser interfase"
|
"json wrapper for xmlrpclib parser interfase"
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.buf = StringIO()
|
self.buf = StringIO()
|
||||||
|
|
||||||
def feed(self, data):
|
def feed(self, data):
|
||||||
self.buf.write(data)
|
self.buf.write(data)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
return self.buf.getvalue()
|
return self.buf.getvalue()
|
||||||
|
|
||||||
@@ -72,6 +74,7 @@ class JSONTransportMixin:
|
|||||||
class JSONTransport(JSONTransportMixin, Transport):
|
class JSONTransport(JSONTransportMixin, Transport):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class JSONSafeTransport(JSONTransportMixin, SafeTransport):
|
class JSONSafeTransport(JSONTransportMixin, SafeTransport):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -88,7 +91,7 @@ class ServerProxy(object):
|
|||||||
|
|
||||||
type, uri = urllib.splittype(uri)
|
type, uri = urllib.splittype(uri)
|
||||||
if type not in ("http", "https"):
|
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)
|
self.__host, self.__handler = urllib.splithost(uri)
|
||||||
|
|
||||||
if transport is None:
|
if transport is None:
|
||||||
|
|||||||
@@ -99,14 +99,17 @@ SMSCODES = {
|
|||||||
'WyndTell': '@wyndtell.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 sms_email('1 (312) 375-6536','T-Mobile USA (tmail)')
|
||||||
print 13123756536@tmail.com
|
print 13123756536@tmail.com
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
if number[0]=='+1': number=number[1:]
|
if number[0] == '+1':
|
||||||
elif number[0]=='+': number=number[3:]
|
number = number[1:]
|
||||||
|
elif number[0] == '+':
|
||||||
|
number = number[3:]
|
||||||
elif number[:2] == '00': number = number[3:]
|
elif number[:2] == '00': number = number[3:]
|
||||||
number = re.sub('[^\d]', '', number)
|
number = re.sub('[^\d]', '', number)
|
||||||
return number + SMSCODES[provider]
|
return number + SMSCODES[provider]
|
||||||
@@ -9,6 +9,7 @@ import pickle
|
|||||||
import copy
|
import copy
|
||||||
import simplejson
|
import simplejson
|
||||||
|
|
||||||
|
|
||||||
def quote(text):
|
def quote(text):
|
||||||
return str(text).replace('\\', '\\\\').replace("'", "\\'")
|
return str(text).replace('\\', '\\\\').replace("'", "\\'")
|
||||||
|
|
||||||
@@ -316,7 +317,6 @@ class Sheet:
|
|||||||
|
|
||||||
return changes
|
return changes
|
||||||
|
|
||||||
|
|
||||||
def process(self, request, db=None, db_callback=None):
|
def process(self, request, db=None, db_callback=None):
|
||||||
"""
|
"""
|
||||||
call this in action that creates table, it will handle ajax callbacks
|
call this in action that creates table, it will handle ajax callbacks
|
||||||
@@ -470,7 +470,7 @@ class Sheet:
|
|||||||
for k in xrange(self.rows * self.cols):
|
for k in xrange(self.rows * self.cols):
|
||||||
key = 'r%sc%s' % (k / self.cols, k % self.cols)
|
key = 'r%sc%s' % (k / self.cols, k % self.cols)
|
||||||
r, c = self.position(key)
|
r, c = self.position(key)
|
||||||
if self.client["cells"].has_key(key):
|
if key in self.client["cells"]:
|
||||||
value = self.client["cells"][key]
|
value = self.client["cells"][key]
|
||||||
# readonly id values
|
# readonly id values
|
||||||
if c in self.client["id_columns"]:
|
if c in self.client["id_columns"]:
|
||||||
@@ -653,7 +653,7 @@ class Sheet:
|
|||||||
v = cell_value
|
v = cell_value
|
||||||
else:
|
else:
|
||||||
v = value
|
v = value
|
||||||
if (ends is None) or ((ends_r >= r+starts_r) and\
|
if (ends is None) or ((ends_r >= r + starts_r) and
|
||||||
(ends_c >= c + starts_c)):
|
(ends_c >= c + starts_c)):
|
||||||
self.cell(key, v, active=active,
|
self.cell(key, v, active=active,
|
||||||
readonly=readonly,
|
readonly=readonly,
|
||||||
@@ -669,7 +669,7 @@ class Sheet:
|
|||||||
v = value
|
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 \
|
if (ends is None) or \
|
||||||
((ends_r >= r+starts_r) and\
|
((ends_r >= r + starts_r) and
|
||||||
(ends_c >= c + starts_c)):
|
(ends_c >= c + starts_c)):
|
||||||
self.cell(key, v,
|
self.cell(key, v,
|
||||||
active=active,
|
active=active,
|
||||||
@@ -704,7 +704,8 @@ class Sheet:
|
|||||||
not other_key in self.environment:
|
not other_key in self.environment:
|
||||||
node.locked = True
|
node.locked = True
|
||||||
node.computed_value = \
|
node.computed_value = \
|
||||||
self.error % dict(error='invalid keyword: ' + other_key)
|
self.error % dict(
|
||||||
|
error='invalid keyword: ' + other_key)
|
||||||
self.modified = {}
|
self.modified = {}
|
||||||
break
|
break
|
||||||
self.compute(node)
|
self.compute(node)
|
||||||
@@ -780,20 +781,20 @@ class Sheet:
|
|||||||
gluon.html.TH, gluon.html.BR, gluon.html.SCRIPT)
|
gluon.html.TH, gluon.html.BR, gluon.html.SCRIPT)
|
||||||
regex = re.compile('r\d+c\d+')
|
regex = re.compile('r\d+c\d+')
|
||||||
|
|
||||||
header = TR(TH(), *[TH('c%s' % c) \
|
header = TR(TH(), *[TH('c%s' % c)
|
||||||
for c in range(self.cols)])
|
for c in range(self.cols)])
|
||||||
rows = []
|
rows = []
|
||||||
for r in range(self.rows):
|
for r in range(self.rows):
|
||||||
tds = [TH('r%s' % r), ]
|
tds = [TH('r%s' % r), ]
|
||||||
for c in range(self.cols):
|
for c in range(self.cols):
|
||||||
key = 'r%sc%s' % (r, c)
|
key = 'r%sc%s' % (r, c)
|
||||||
attributes = {"_class": "w2p_spreadsheet_col_%s" % \
|
attributes = {"_class": "w2p_spreadsheet_col_%s" %
|
||||||
self.even_or_odd(c)}
|
self.even_or_odd(c)}
|
||||||
if key in self.td_attributes:
|
if key in self.td_attributes:
|
||||||
attributes.update(self.td_attributes[key])
|
attributes.update(self.td_attributes[key])
|
||||||
td = TD(self.nodes[key], **attributes)
|
td = TD(self.nodes[key], **attributes)
|
||||||
tds.append(td)
|
tds.append(td)
|
||||||
attributes = {"_class": "w2p_spreadsheet_row_%s" % \
|
attributes = {"_class": "w2p_spreadsheet_row_%s" %
|
||||||
self.even_or_odd(r)}
|
self.even_or_odd(r)}
|
||||||
if str(r) in self.tr_attributes:
|
if str(r) in self.tr_attributes:
|
||||||
attributes.update(self.tr_attributes[str(r)])
|
attributes.update(self.tr_attributes[str(r)])
|
||||||
@@ -805,7 +806,7 @@ class Sheet:
|
|||||||
table = TABLE(header, *rows, **self.attributes)
|
table = TABLE(header, *rows, **self.attributes)
|
||||||
|
|
||||||
if len(self.client["cells"]) >= 1:
|
if len(self.client["cells"]) >= 1:
|
||||||
data = SCRIPT(\
|
data = SCRIPT(
|
||||||
"""
|
"""
|
||||||
var w2p_spreadsheet_data = %(data)s;
|
var w2p_spreadsheet_data = %(data)s;
|
||||||
var w2p_spreadsheet_update_button = "%(update_button)s";
|
var w2p_spreadsheet_update_button = "%(update_button)s";
|
||||||
@@ -835,7 +836,8 @@ class Sheet:
|
|||||||
|
|
||||||
if (w2p_spreadsheet_update_button != ""){
|
if (w2p_spreadsheet_update_button != ""){
|
||||||
jQuery(function(){
|
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),
|
""" % dict(data=simplejson.dumps(self.client),
|
||||||
@@ -856,14 +858,14 @@ class Sheet:
|
|||||||
sorted_headers = [TH(), ] + \
|
sorted_headers = [TH(), ] + \
|
||||||
[TH(header[1]) for header in sorted(unsorted_headers)]
|
[TH(header[1]) for header in sorted(unsorted_headers)]
|
||||||
table.insert(0, TR(*sorted_headers,
|
table.insert(0, TR(*sorted_headers,
|
||||||
**{_class:"%s_fieldnames" % \
|
**{_class: "%s_fieldnames" %
|
||||||
attributes["_class"]}))
|
attributes["_class"]}))
|
||||||
else:
|
else:
|
||||||
data = SCRIPT(""" // web2py Spreadsheets: no db data.""")
|
data = SCRIPT(""" // web2py Spreadsheets: no db data.""")
|
||||||
|
|
||||||
return DIV(table,
|
return DIV(table,
|
||||||
BR(),
|
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)]),
|
for key in self.nodes if not regex.match(key)]),
|
||||||
data, **attributes)
|
data, **attributes)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import urllib
|
import urllib
|
||||||
import simplejson
|
import simplejson
|
||||||
|
|
||||||
|
|
||||||
class Stripe:
|
class Stripe:
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
@@ -24,6 +25,7 @@ class Stripe:
|
|||||||
|
|
||||||
def __init__(self, key):
|
def __init__(self, key):
|
||||||
self.key = key
|
self.key = key
|
||||||
|
|
||||||
def charge(self,
|
def charge(self,
|
||||||
amount,
|
amount,
|
||||||
currency='usd',
|
currency='usd',
|
||||||
@@ -39,18 +41,18 @@ class Stripe:
|
|||||||
'card[exp_year]': card_exp_year,
|
'card[exp_year]': card_exp_year,
|
||||||
'card[cvc_check]': card_cvc_check,
|
'card[cvc_check]': card_cvc_check,
|
||||||
'description': description})
|
'description': description})
|
||||||
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges' % \
|
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges' %
|
||||||
self.key, params)
|
self.key, params)
|
||||||
return simplejson.loads(u.read())
|
return simplejson.loads(u.read())
|
||||||
|
|
||||||
def check(self, charge_id):
|
def check(self, charge_id):
|
||||||
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges/%s' % \
|
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges/%s' %
|
||||||
(self.key, charge_id))
|
(self.key, charge_id))
|
||||||
return simplejson.loads(u.read())
|
return simplejson.loads(u.read())
|
||||||
|
|
||||||
def refund(self, charge_id):
|
def refund(self, charge_id):
|
||||||
params = urllib.urlencode({})
|
params = urllib.urlencode({})
|
||||||
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges/%s/refund'%\
|
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges/%s/refund' %
|
||||||
(self.key, charge_id), params)
|
(self.key, charge_id), params)
|
||||||
return simplejson.loads(u.read())
|
return simplejson.loads(u.read())
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,20 @@ import time
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
Score = namedtuple('Score', ['tag', 'stamp'])
|
Score = namedtuple('Score', ['tag', 'stamp'])
|
||||||
|
|
||||||
|
|
||||||
class TimeCollector(object):
|
class TimeCollector(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
'''The first time stamp is created here'''
|
'''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):
|
def addStamp(self, description):
|
||||||
'''Adds a new time stamp, with a description.'''
|
'''Adds a new time stamp, with a description.'''
|
||||||
self.scores.append(Score(tag=description, stamp=time.clock()))
|
self.scores.append(Score(tag=description, stamp=time.clock()))
|
||||||
|
|
||||||
def _stampDelta(self, index1, index2):
|
def _stampDelta(self, index1, index2):
|
||||||
'''Private utility function to clean up this common calculation.'''
|
'''Private utility function to clean up this common calculation.'''
|
||||||
return self.scores[index1].stamp - self.scores[index2].stamp
|
return self.scores[index1].stamp - self.scores[index2].stamp
|
||||||
|
|
||||||
def getReportItems(self, orderByCost=True):
|
def getReportItems(self, orderByCost=True):
|
||||||
'''Returns a list of dicts. Each dict has
|
'''Returns a list of dicts. Each dict has
|
||||||
start (ms),
|
start (ms),
|
||||||
@@ -42,6 +46,7 @@ class TimeCollector(object):
|
|||||||
if orderByCost:
|
if orderByCost:
|
||||||
data.sort(key=lambda x: x['perc'], reverse=True)
|
data.sort(key=lambda x: x['perc'], reverse=True)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def getReportLines(self, orderByCost=True):
|
def getReportLines(self, orderByCost=True):
|
||||||
'''Produces a report of logged time-stamps as a list of strings.
|
'''Produces a report of logged time-stamps as a list of strings.
|
||||||
if orderByCost is False, then the order of the stamps is
|
if orderByCost is False, then the order of the stamps is
|
||||||
@@ -53,8 +58,10 @@ class TimeCollector(object):
|
|||||||
bodyTemplate = '%(start)10.0f | %(end)10.0f | %(delta)10.0f |' \
|
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]
|
return [headerTemplate % headerData] + [bodyTemplate % d for d in data]
|
||||||
|
|
||||||
def getReportText(self, **kwargs):
|
def getReportText(self, **kwargs):
|
||||||
return '\n'.join(self.getReportLines(**kwargs))
|
return '\n'.join(self.getReportLines(**kwargs))
|
||||||
|
|
||||||
def restart(self):
|
def restart(self):
|
||||||
self.scores = [Score(tag='start', stamp=time.clock())]
|
self.scores = [Score(tag='start', stamp=time.clock())]
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ Symbian.
|
|||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
class DetectorsHub(dict):
|
class DetectorsHub(dict):
|
||||||
_known_types = ['os', 'dist', 'flavor', 'browser']
|
_known_types = ['os', 'dist', 'flavor', 'browser']
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ class DetectorsHub(dict):
|
|||||||
return detectors
|
return detectors
|
||||||
else:
|
else:
|
||||||
prefs.insert(0, '')
|
prefs.insert(0, '')
|
||||||
|
|
||||||
def key_name(d):
|
def key_name(d):
|
||||||
return d.name in prefs and prefs.index(d.name) or sys.maxint
|
return d.name in prefs and prefs.index(d.name) or sys.maxint
|
||||||
return sorted(detectors, key=key_name)
|
return sorted(detectors, key=key_name)
|
||||||
@@ -46,7 +48,7 @@ class DetectorsHub(dict):
|
|||||||
return iter(self._known_types)
|
return iter(self._known_types)
|
||||||
|
|
||||||
def registerDetectors(self):
|
def registerDetectors(self):
|
||||||
detectors = [v() for v in globals().values() \
|
detectors = [v() for v in globals().values()
|
||||||
if DetectorBase in getattr(v, '__mro__', [])]
|
if DetectorBase in getattr(v, '__mro__', [])]
|
||||||
for d in detectors:
|
for d in detectors:
|
||||||
if d.can_register:
|
if d.can_register:
|
||||||
@@ -131,6 +133,7 @@ class Browser(DetectorBase):
|
|||||||
class Macintosh(OS):
|
class Macintosh(OS):
|
||||||
look_for = ['Macintosh']
|
look_for = ['Macintosh']
|
||||||
prefs = dict(dist=None)
|
prefs = dict(dist=None)
|
||||||
|
|
||||||
def getVersion(self, agent):
|
def getVersion(self, agent):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -146,12 +149,15 @@ class Konqueror(Browser):
|
|||||||
|
|
||||||
class Opera(Browser):
|
class Opera(Browser):
|
||||||
look_for = ["Opera"]
|
look_for = ["Opera"]
|
||||||
|
|
||||||
def getVersion(self, agent):
|
def getVersion(self, agent):
|
||||||
return agent.partition(self.look_for[0])[2][1:].partition(' ')[0]
|
return agent.partition(self.look_for[0])[2][1:].partition(' ')[0]
|
||||||
|
|
||||||
|
|
||||||
class Netscape(Browser):
|
class Netscape(Browser):
|
||||||
look_for = ["Netscape"]
|
look_for = ["Netscape"]
|
||||||
|
|
||||||
|
|
||||||
class MSIE(Browser):
|
class MSIE(Browser):
|
||||||
look_for = ["MSIE"]
|
look_for = ["MSIE"]
|
||||||
skip_if_found = ["Opera"]
|
skip_if_found = ["Opera"]
|
||||||
@@ -171,6 +177,7 @@ class Safari(Browser):
|
|||||||
if "Version/" in agent:
|
if "Version/" in agent:
|
||||||
return agent.partition('Version/')[2].partition(' ')[0].strip()
|
return agent.partition('Version/')[2].partition(' ')[0].strip()
|
||||||
|
|
||||||
|
|
||||||
class SafariTablet(Browser):
|
class SafariTablet(Browser):
|
||||||
name = "Safari"
|
name = "Safari"
|
||||||
look_for = ['Safari', 'Android']
|
look_for = ['Safari', 'Android']
|
||||||
@@ -182,6 +189,7 @@ class SafariTablet(Browser):
|
|||||||
if "Version/" in agent:
|
if "Version/" in agent:
|
||||||
return agent.partition('Version/')[2].partition(' ')[0].strip()
|
return agent.partition('Version/')[2].partition(' ')[0].strip()
|
||||||
|
|
||||||
|
|
||||||
class SafariMobile(Browser):
|
class SafariMobile(Browser):
|
||||||
name = "Safari"
|
name = "Safari"
|
||||||
look_for = ["Safari", "Mobile"]
|
look_for = ["Safari", "Mobile"]
|
||||||
@@ -191,6 +199,7 @@ class SafariMobile(Browser):
|
|||||||
if "Version/" in agent:
|
if "Version/" in agent:
|
||||||
return agent.partition('Version/')[2].partition(' ')[0].strip()
|
return agent.partition('Version/')[2].partition(' ')[0].strip()
|
||||||
|
|
||||||
|
|
||||||
class SafariNokia(Browser):
|
class SafariNokia(Browser):
|
||||||
name = "Safari"
|
name = "Safari"
|
||||||
look_for = ["Safari", "SymbianOS"]
|
look_for = ["Safari", "SymbianOS"]
|
||||||
@@ -199,6 +208,7 @@ class SafariNokia(Browser):
|
|||||||
def getVersion(self, agent):
|
def getVersion(self, agent):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SafariiPad(Browser):
|
class SafariiPad(Browser):
|
||||||
name = "Safari"
|
name = "Safari"
|
||||||
look_for = ["Safari", "iPad"]
|
look_for = ["Safari", "iPad"]
|
||||||
@@ -218,6 +228,7 @@ class Linux(OS):
|
|||||||
def getVersion(self, agent):
|
def getVersion(self, agent):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BlackBerry(OS):
|
class BlackBerry(OS):
|
||||||
look_for = ['BlackBerry']
|
look_for = ['BlackBerry']
|
||||||
prefs = dict(flavor=['PlayBook'])
|
prefs = dict(flavor=['PlayBook'])
|
||||||
@@ -232,6 +243,7 @@ class BlackBerry(OS):
|
|||||||
def getVersion(self, agent):
|
def getVersion(self, agent):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PlayBook(Flavor):
|
class PlayBook(Flavor):
|
||||||
look_for = ['PlayBook']
|
look_for = ['PlayBook']
|
||||||
is_mobile = True
|
is_mobile = True
|
||||||
@@ -240,6 +252,7 @@ class PlayBook(Flavor):
|
|||||||
def getVersion(self, agent):
|
def getVersion(self, agent):
|
||||||
return agent.partition('Tablet OS')[2].partition(';')[0].strip()
|
return agent.partition('Tablet OS')[2].partition(';')[0].strip()
|
||||||
|
|
||||||
|
|
||||||
class Macintosh(OS):
|
class Macintosh(OS):
|
||||||
look_for = ['Macintosh']
|
look_for = ['Macintosh']
|
||||||
prefs = dict(dist=None, flavor=['MacOS'])
|
prefs = dict(dist=None, flavor=['MacOS'])
|
||||||
@@ -250,7 +263,8 @@ class Macintosh(OS):
|
|||||||
|
|
||||||
class MacOS(Flavor):
|
class MacOS(Flavor):
|
||||||
look_for = ['Mac OS']
|
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):
|
def getVersion(self, agent):
|
||||||
version_end_chars = [';', ')']
|
version_end_chars = [';', ')']
|
||||||
@@ -271,6 +285,7 @@ class Windows(OS):
|
|||||||
v = agent.partition('NT')
|
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):
|
class WindowsMobile(Dist):
|
||||||
name = 'Phone'
|
name = 'Phone'
|
||||||
look_for = ['Windows', 'Phone']
|
look_for = ['Windows', 'Phone']
|
||||||
@@ -279,6 +294,7 @@ class WindowsMobile(Dist):
|
|||||||
def getVersion(self, agent):
|
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):
|
class Ubuntu(Dist):
|
||||||
look_for = ['Ubuntu']
|
look_for = ['Ubuntu']
|
||||||
version_splitters = ["/", " "]
|
version_splitters = ["/", " "]
|
||||||
@@ -295,14 +311,17 @@ class Chrome(Browser):
|
|||||||
look_for = ['Chrome']
|
look_for = ['Chrome']
|
||||||
version_splitters = ["/", " "]
|
version_splitters = ["/", " "]
|
||||||
|
|
||||||
|
|
||||||
class ChromeOS(OS):
|
class ChromeOS(OS):
|
||||||
look_for = ['CrOS']
|
look_for = ['CrOS']
|
||||||
version_splitters = [" ", ")"]
|
version_splitters = [" ", ")"]
|
||||||
prefs = dict(browser=['Chrome'])
|
prefs = dict(browser=['Chrome'])
|
||||||
|
|
||||||
def getVersion(self, agent):
|
def getVersion(self, agent):
|
||||||
vs = self.version_splitters
|
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):
|
class Android(Dist):
|
||||||
look_for = ['Android']
|
look_for = ['Android']
|
||||||
prefs = dict(browser=['SafariTablet', 'SafariMobile'])
|
prefs = dict(browser=['SafariTablet', 'SafariMobile'])
|
||||||
@@ -311,12 +330,14 @@ class Android(Dist):
|
|||||||
def getVersion(self, agent):
|
def getVersion(self, agent):
|
||||||
return agent.partition('Android')[2].partition(';')[0].strip()
|
return agent.partition('Android')[2].partition(';')[0].strip()
|
||||||
|
|
||||||
|
|
||||||
class SymbianOS(OS):
|
class SymbianOS(OS):
|
||||||
look_for = ['SymbianOS']
|
look_for = ['SymbianOS']
|
||||||
prefs = dict(dist=['Series'], browser=['Safari', 'Opera'])
|
prefs = dict(dist=['Series'], browser=['Safari', 'Opera'])
|
||||||
is_mobile = True
|
is_mobile = True
|
||||||
version_splitters = ['/', '; ']
|
version_splitters = ['/', '; ']
|
||||||
|
|
||||||
|
|
||||||
class Series(Flavor):
|
class Series(Flavor):
|
||||||
look_for = ['SymbianOS', 'Series']
|
look_for = ['SymbianOS', 'Series']
|
||||||
version_splitters = ['/', ';']
|
version_splitters = ['/', ';']
|
||||||
@@ -324,10 +345,12 @@ class Series(Flavor):
|
|||||||
def getVersion(self, agent):
|
def getVersion(self, agent):
|
||||||
return agent.partition('Series')[2].partition(' ')[0].replace('/', ' ')
|
return agent.partition('Series')[2].partition(' ')[0].replace('/', ' ')
|
||||||
|
|
||||||
|
|
||||||
class BrowserNG(Browser):
|
class BrowserNG(Browser):
|
||||||
look_for = ['BrowserNG']
|
look_for = ['BrowserNG']
|
||||||
version_splitters = ['/', ';']
|
version_splitters = ['/', ';']
|
||||||
|
|
||||||
|
|
||||||
class iPhone(Dist):
|
class iPhone(Dist):
|
||||||
look_for = ['iPhone']
|
look_for = ['iPhone']
|
||||||
is_mobile = True
|
is_mobile = True
|
||||||
@@ -344,6 +367,7 @@ class iPhone(Dist):
|
|||||||
break
|
break
|
||||||
return version.replace('_', '.')
|
return version.replace('_', '.')
|
||||||
|
|
||||||
|
|
||||||
class iPad(Dist):
|
class iPad(Dist):
|
||||||
look_for = ['iPad']
|
look_for = ['iPad']
|
||||||
is_mobile = True
|
is_mobile = True
|
||||||
@@ -362,6 +386,7 @@ class iPad(Dist):
|
|||||||
|
|
||||||
detectorshub = DetectorsHub()
|
detectorshub = DetectorsHub()
|
||||||
|
|
||||||
|
|
||||||
def detect(agent):
|
def detect(agent):
|
||||||
result = dict()
|
result = dict()
|
||||||
prefs = dict()
|
prefs = dict()
|
||||||
@@ -385,21 +410,26 @@ class Result(dict):
|
|||||||
def __missing__(self, k):
|
def __missing__(self, k):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def simple_detect(agent):
|
def simple_detect(agent):
|
||||||
"""
|
"""
|
||||||
-> (os, browser, is_mobile) # tuple of strings
|
-> (os, browser, is_mobile) # tuple of strings
|
||||||
"""
|
"""
|
||||||
result = detect(agent)
|
result = detect(agent)
|
||||||
os_list = []
|
os_list = []
|
||||||
if 'flavor' in result: os_list.append(result['flavor']['name'])
|
if 'flavor' in result:
|
||||||
if 'dist' in result: os_list.append(result['dist']['name'])
|
os_list.append(result['flavor']['name'])
|
||||||
if 'os' in result: os_list.append(result['os']['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 = os_list and " ".join(os_list) or "Unknown OS"
|
||||||
os_version = os_list and ('flavor' in result and result['flavor'] and result['flavor'].get(
|
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')) \
|
'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 ""
|
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 = 'browser' in result and result['browser'][
|
||||||
|
'name'] or 'Unknown Browser'
|
||||||
browser_version = 'browser' in result \
|
browser_version = 'browser' in result \
|
||||||
and result['browser'].get('version') or ""
|
and result['browser'].get('version') or ""
|
||||||
if browser_version:
|
if browser_version:
|
||||||
@@ -417,58 +447,76 @@ if __name__ == '__main__':
|
|||||||
data = (
|
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',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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)',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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+',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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+',
|
(
|
||||||
|
'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),
|
('BlackBerry', 'Safari 6.0.0.246', True),
|
||||||
{'is_mobile': True, 'is_tablet': False, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '6.0.0.246'}},),
|
{'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+',
|
(
|
||||||
|
'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),
|
('BlackBerry', 'Safari 6.0.0.600', True),
|
||||||
{'is_mobile': True, 'is_tablet': False, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '6.0.0.600'}},),
|
{'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',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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)',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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)',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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]',
|
(
|
||||||
|
'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),
|
('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'}},),
|
{'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',
|
('Opera/9.80 (X11; Linux i686; U; en) Presto/2.9.168 Version/11.50',
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ FORM_REGEX = re.compile('(\<input name\="_formkey" type\="hidden" value\="(?P<fo
|
|||||||
|
|
||||||
SESSION_REGEX = 'session_id_(?P<name>.+)'
|
SESSION_REGEX = 'session_id_(?P<name>.+)'
|
||||||
|
|
||||||
|
|
||||||
class WebClient(object):
|
class WebClient(object):
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
@@ -80,7 +81,8 @@ class WebClient(object):
|
|||||||
headers[key] = value
|
headers[key] = value
|
||||||
for key, value in headers.iteritems():
|
for key, value in headers.iteritems():
|
||||||
if isinstance(value, (list, tuple)):
|
if isinstance(value, (list, tuple)):
|
||||||
for v in value: headers_list.append((key,v))
|
for v in value:
|
||||||
|
headers_list.append((key, v))
|
||||||
else:
|
else:
|
||||||
headers_list.append((key, value))
|
headers_list.append((key, value))
|
||||||
|
|
||||||
@@ -131,7 +133,7 @@ class WebClient(object):
|
|||||||
# treat web2py tickets as special types of errors
|
# treat web2py tickets as special types of errors
|
||||||
if error is not None:
|
if error is not None:
|
||||||
if 'web2py_error' in self.headers:
|
if 'web2py_error' in self.headers:
|
||||||
raise RuntimeError, self.headers['web2py_error']
|
raise RuntimeError(self.headers['web2py_error'])
|
||||||
else:
|
else:
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
@@ -149,7 +151,7 @@ class WebClient(object):
|
|||||||
if match:
|
if match:
|
||||||
name = match.group('name')
|
name = match.group('name')
|
||||||
if name in self.sessions and self.sessions[name] != value:
|
if name in self.sessions and self.sessions[name] != value:
|
||||||
raise RuntimeError, 'Broken sessions %s' % name
|
raise RuntimeError('Broken sessions %s' % name)
|
||||||
self.sessions[name] = value
|
self.sessions[name] = value
|
||||||
|
|
||||||
# find all forms and formkeys in page
|
# find all forms and formkeys in page
|
||||||
@@ -160,6 +162,7 @@ class WebClient(object):
|
|||||||
# log this request
|
# 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():
|
def test_web2py_registration_and_login():
|
||||||
# from gluon.contrib.webclient import WebClient
|
# from gluon.contrib.webclient import WebClient
|
||||||
# start a web2py instance for testing
|
# start a web2py instance for testing
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ Here is a complete sample web2py action:
|
|||||||
form=SQLFORM.factory(Field('message'))
|
form=SQLFORM.factory(Field('message'))
|
||||||
if form.accepts(request,session):
|
if form.accepts(request,session):
|
||||||
from gluon.contrib.websocket_messaging import websocket_send
|
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
|
return form
|
||||||
|
|
||||||
Acknowledgements:
|
Acknowledgements:
|
||||||
@@ -83,31 +84,38 @@ listeners = {}
|
|||||||
names = {}
|
names = {}
|
||||||
tokens = {}
|
tokens = {}
|
||||||
|
|
||||||
|
|
||||||
def websocket_send(url, message, hmac_key=None, group='default'):
|
def websocket_send(url, message, hmac_key=None, group='default'):
|
||||||
sig = hmac_key and hmac.new(hmac_key, message).hexdigest() or ''
|
sig = hmac_key and hmac.new(hmac_key, message).hexdigest() or ''
|
||||||
params = urllib.urlencode({'message': message, 'signature': sig, 'group':group})
|
params = urllib.urlencode(
|
||||||
|
{'message': message, 'signature': sig, 'group': group})
|
||||||
f = urllib.urlopen(url, params)
|
f = urllib.urlopen(url, params)
|
||||||
data = f.read()
|
data = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
class PostHandler(tornado.web.RequestHandler):
|
class PostHandler(tornado.web.RequestHandler):
|
||||||
"""
|
"""
|
||||||
only authorized parties can post messages
|
only authorized parties can post messages
|
||||||
"""
|
"""
|
||||||
def post(self):
|
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:
|
if 'message' in self.request.arguments:
|
||||||
message = self.request.arguments['message'][0]
|
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)
|
print '%s:MESSAGE to %s:%s' % (time.time(), group, message)
|
||||||
if hmac_key:
|
if hmac_key:
|
||||||
signature = self.request.arguments['signature'][0]
|
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:
|
||||||
for client in listeners.get(group,[]): client.write_message(message)
|
return 'false'
|
||||||
|
for client in listeners.get(group, []):
|
||||||
|
client.write_message(message)
|
||||||
return 'true'
|
return 'true'
|
||||||
return 'false'
|
return 'false'
|
||||||
|
|
||||||
|
|
||||||
class TokenHandler(tornado.web.RequestHandler):
|
class TokenHandler(tornado.web.RequestHandler):
|
||||||
"""
|
"""
|
||||||
if running with -t post a token to allow a client to join using the token
|
if running with -t post a token to allow a client to join using the token
|
||||||
@@ -115,16 +123,19 @@ class TokenHandler(tornado.web.RequestHandler):
|
|||||||
allows only authorized parties to joins, for example, a chat
|
allows only authorized parties to joins, for example, a chat
|
||||||
"""
|
"""
|
||||||
def post(self):
|
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:
|
if 'message' in self.request.arguments:
|
||||||
message = self.request.arguments['message'][0]
|
message = self.request.arguments['message'][0]
|
||||||
if hmac_key:
|
if hmac_key:
|
||||||
signature = self.request.arguments['signature'][0]
|
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
|
tokens[message] = None
|
||||||
return 'true'
|
return 'true'
|
||||||
return 'false'
|
return 'false'
|
||||||
|
|
||||||
|
|
||||||
class DistributeHandler(tornado.websocket.WebSocketHandler):
|
class DistributeHandler(tornado.websocket.WebSocketHandler):
|
||||||
def open(self, params):
|
def open(self, params):
|
||||||
group, token, name = params.split('/') + [None, None]
|
group, token, name = params.split('/') + [None, None]
|
||||||
@@ -133,23 +144,29 @@ class DistributeHandler(tornado.websocket.WebSocketHandler):
|
|||||||
self.name = name or 'anonymous'
|
self.name = name or 'anonymous'
|
||||||
# only authorized parties can join
|
# only authorized parties can join
|
||||||
if DistributeHandler.tokens:
|
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()
|
self.close()
|
||||||
else:
|
else:
|
||||||
tokens[self.token] = self
|
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
|
# 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)
|
listeners[self.group].append(self)
|
||||||
names[self] = self.name
|
names[self] = self.name
|
||||||
print '%s:CONNECT to %s' % (time.time(), self.group)
|
print '%s:CONNECT to %s' % (time.time(), self.group)
|
||||||
|
|
||||||
def on_message(self, message):
|
def on_message(self, message):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_close(self):
|
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]
|
del names[self]
|
||||||
# notify clients that a member has left the groups
|
# 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)
|
print '%s:DISCONNECT from %s' % (time.time(), self.group)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -13,21 +13,27 @@ NATIVE_IMPORTER = __builtin__.__import__
|
|||||||
INVALID_MODULES = set(('', 'gluon', 'applications', 'custom_import'))
|
INVALID_MODULES = set(('', 'gluon', 'applications', 'custom_import'))
|
||||||
|
|
||||||
# backward compatibility API
|
# backward compatibility API
|
||||||
|
|
||||||
|
|
||||||
def custom_import_install():
|
def custom_import_install():
|
||||||
if __builtin__.__import__ != custom_importer:
|
if __builtin__.__import__ != custom_importer:
|
||||||
INVALID_MODULES.update(sys.modules.keys())
|
INVALID_MODULES.update(sys.modules.keys())
|
||||||
__builtin__.__import__ = custom_importer
|
__builtin__.__import__ = custom_importer
|
||||||
|
|
||||||
|
|
||||||
def track_changes(track=True):
|
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
|
current.request._custom_import_track_changes = track
|
||||||
|
|
||||||
|
|
||||||
def is_tracking_changes():
|
def is_tracking_changes():
|
||||||
return current.request._custom_import_track_changes
|
return current.request._custom_import_track_changes
|
||||||
|
|
||||||
|
|
||||||
class CustomImportException(ImportError):
|
class CustomImportException(ImportError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
|
def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
|
||||||
"""
|
"""
|
||||||
The web2py custom importer. Like the standard Python importer but it
|
The web2py custom importer. Like the standard Python importer but it
|
||||||
@@ -56,7 +62,8 @@ def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
|
|||||||
import_tb = None
|
import_tb = None
|
||||||
try:
|
try:
|
||||||
items = current.request.folder.split(os.path.sep)
|
items = current.request.folder.split(os.path.sep)
|
||||||
if not items[-1]: items = items[:-1]
|
if not items[-1]:
|
||||||
|
items = items[:-1]
|
||||||
modules_prefix = '.'.join(items[-2:]) + '.modules'
|
modules_prefix = '.'.join(items[-2:]) + '.modules'
|
||||||
if not fromlist:
|
if not fromlist:
|
||||||
# import like "import x" or "import x.y"
|
# import like "import x" or "import x.y"
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ Example of usage:
|
|||||||
|
|
||||||
### update one record
|
### update one record
|
||||||
>>> james.update_record(name='Jim')
|
>>> james.update_record(name='Jim')
|
||||||
|
<Row {'id': 1, 'name': 'Jim'}>
|
||||||
|
|
||||||
### update multiple records by query
|
### update multiple records by query
|
||||||
>>> db(person.name.like('J%')).update(name='James')
|
>>> db(person.name.like('J%')).update(name='James')
|
||||||
@@ -9390,6 +9391,7 @@ def test_all():
|
|||||||
Update a single record
|
Update a single record
|
||||||
|
|
||||||
>>> me.update_record(name=\"Max\")
|
>>> me.update_record(name=\"Max\")
|
||||||
|
<Row {'name': 'Max', 'birth': datetime.date(1971, 12, 21), 'id': 2}>
|
||||||
>>> me.name
|
>>> me.name
|
||||||
'Max'
|
'Max'
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -17,6 +17,7 @@ import sys
|
|||||||
|
|
||||||
logger = logging.getLogger("web2py")
|
logger = logging.getLogger("web2py")
|
||||||
|
|
||||||
|
|
||||||
class Pipe(Queue.Queue):
|
class Pipe(Queue.Queue):
|
||||||
def __init__(self, name, mode='r', *args, **kwargs):
|
def __init__(self, name, mode='r', *args, **kwargs):
|
||||||
self.__name = name
|
self.__name = name
|
||||||
@@ -52,6 +53,7 @@ pipe_out = Pipe('out')
|
|||||||
|
|
||||||
debugger = pdb.Pdb(completekey=None, stdin=pipe_in, stdout=pipe_out,)
|
debugger = pdb.Pdb(completekey=None, stdin=pipe_in, stdout=pipe_out,)
|
||||||
|
|
||||||
|
|
||||||
def set_trace():
|
def set_trace():
|
||||||
"breakpoint shortcut (like pdb)"
|
"breakpoint shortcut (like pdb)"
|
||||||
logger.info("DEBUG: set_trace!")
|
logger.info("DEBUG: set_trace!")
|
||||||
@@ -67,6 +69,7 @@ def stop_trace():
|
|||||||
pipe_out.write(None)
|
pipe_out.write(None)
|
||||||
#pipe_out.flush()
|
#pipe_out.flush()
|
||||||
|
|
||||||
|
|
||||||
def communicate(command=None):
|
def communicate(command=None):
|
||||||
"send command to debbuger, wait result"
|
"send command to debbuger, wait result"
|
||||||
if command is not None:
|
if command is not None:
|
||||||
@@ -91,6 +94,7 @@ from threading import RLock
|
|||||||
interact_lock = RLock()
|
interact_lock = RLock()
|
||||||
run_lock = RLock()
|
run_lock = RLock()
|
||||||
|
|
||||||
|
|
||||||
def check_interaction(fn):
|
def check_interaction(fn):
|
||||||
"Decorator to clean and prevent interaction when not available"
|
"Decorator to clean and prevent interaction when not available"
|
||||||
def check_fn(self, *args, **kwargs):
|
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)
|
child_conn = qdb.QueuePipe("child", child_queue, parent_queue)
|
||||||
|
|
||||||
web_debugger = WebDebugger(front_conn) # frontend
|
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
|
dbg = qdb_debugger
|
||||||
|
|
||||||
# enable getting context (stack, globals/locals) at interaction
|
# enable getting context (stack, globals/locals) at interaction
|
||||||
|
|||||||
+4
-1
@@ -1,4 +1,5 @@
|
|||||||
import codecs, encodings
|
import codecs
|
||||||
|
import encodings
|
||||||
|
|
||||||
"""Caller will hand this library a buffer and ask it to either convert
|
"""Caller will hand this library a buffer and ask it to either convert
|
||||||
it or auto-detect the type.
|
it or auto-detect the type.
|
||||||
@@ -20,6 +21,7 @@ autodetect_dict={ # bytepattern : ("name",
|
|||||||
(0x4C, 0x6F, 0xA7, 0x94): ("EBCDIC")
|
(0x4C, 0x6F, 0xA7, 0x94): ("EBCDIC")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def autoDetectXMLEncoding(buffer):
|
def autoDetectXMLEncoding(buffer):
|
||||||
""" buffer -> encoding_name
|
""" buffer -> encoding_name
|
||||||
The buffer should be at least 4 bytes long.
|
The buffer should be at least 4 bytes long.
|
||||||
@@ -69,6 +71,7 @@ def autoDetectXMLEncoding(buffer):
|
|||||||
|
|
||||||
return encoding
|
return encoding
|
||||||
|
|
||||||
|
|
||||||
def decoder(buffer):
|
def decoder(buffer):
|
||||||
encoding = autoDetectXMLEncoding(buffer)
|
encoding = autoDetectXMLEncoding(buffer)
|
||||||
return buffer.decode(encoding).encode('utf8')
|
return buffer.decode(encoding).encode('utf8')
|
||||||
+22
-6
@@ -42,6 +42,7 @@ __all__ = [
|
|||||||
'make_fake_file_like_object',
|
'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]+)?')
|
re_version = re.compile('[^\d]+ (\d+)\.(\d+)\.(\d+)\s*\((?P<datetime>.+?)\)\s*(?P<type>[a-z]+)?')
|
||||||
m = re_version.match(version)
|
m = re_version.match(version)
|
||||||
@@ -50,6 +51,7 @@ def parse_version(version = "Version 1.99.0 (2011-09-19 08:23:26)"):
|
|||||||
d = datetime.datetime.strptime(m.group('datetime'), '%Y-%m-%d %H:%M:%S')
|
d = datetime.datetime.strptime(m.group('datetime'), '%Y-%m-%d %H:%M:%S')
|
||||||
return (a, b, c, d, s)
|
return (a, b, c, d, s)
|
||||||
|
|
||||||
|
|
||||||
def read_file(filename, mode='r'):
|
def read_file(filename, mode='r'):
|
||||||
"returns content from filename, making sure to close the file explicitly on exit."
|
"returns content from filename, making sure to close the file explicitly on exit."
|
||||||
f = open(filename, mode)
|
f = open(filename, mode)
|
||||||
@@ -58,6 +60,7 @@ def read_file(filename, mode='r'):
|
|||||||
finally:
|
finally:
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def write_file(filename, value, mode='w'):
|
def write_file(filename, value, mode='w'):
|
||||||
"writes <value> to filename, making sure to close the file explicitly on exit."
|
"writes <value> to filename, making sure to close the file explicitly on exit."
|
||||||
f = open(filename, mode)
|
f = open(filename, mode)
|
||||||
@@ -66,6 +69,7 @@ def write_file(filename, value, mode='w'):
|
|||||||
finally:
|
finally:
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def readlines_file(filename, mode='r'):
|
def readlines_file(filename, mode='r'):
|
||||||
"applies .split('\n') to the output of read_file()"
|
"applies .split('\n') to the output of read_file()"
|
||||||
return read_file(filename, mode).split('\n')
|
return read_file(filename, mode).split('\n')
|
||||||
@@ -74,10 +78,12 @@ def readlines_file(filename, mode='r'):
|
|||||||
def mktree(path):
|
def mktree(path):
|
||||||
head, tail = os.path.split(path)
|
head, tail = os.path.split(path)
|
||||||
if head:
|
if head:
|
||||||
if tail: mktree(head)
|
if tail:
|
||||||
|
mktree(head)
|
||||||
if not os.path.exists(head):
|
if not os.path.exists(head):
|
||||||
os.mkdir(head)
|
os.mkdir(head)
|
||||||
|
|
||||||
|
|
||||||
def listdir(
|
def listdir(
|
||||||
path,
|
path,
|
||||||
expression='^.+$',
|
expression='^.+$',
|
||||||
@@ -187,7 +193,6 @@ def _extractall(filename, path='.', members=None):
|
|||||||
else:
|
else:
|
||||||
self._dbg(1, 'tarfile: %s' % e)
|
self._dbg(1, 'tarfile: %s' % e)
|
||||||
|
|
||||||
|
|
||||||
_cls = TarFile
|
_cls = TarFile
|
||||||
else:
|
else:
|
||||||
_cls = tarfile.TarFile
|
_cls = tarfile.TarFile
|
||||||
@@ -197,6 +202,7 @@ def _extractall(filename, path='.', members=None):
|
|||||||
tar.close()
|
tar.close()
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def tar(file, dir, expression='^.+$'):
|
def tar(file, dir, expression='^.+$'):
|
||||||
"""
|
"""
|
||||||
tars dir into file, only tars file that match expression
|
tars dir into file, only tars file that match expression
|
||||||
@@ -209,6 +215,7 @@ def tar(file, dir, expression='^.+$'):
|
|||||||
finally:
|
finally:
|
||||||
tar.close()
|
tar.close()
|
||||||
|
|
||||||
|
|
||||||
def untar(file, dir):
|
def untar(file, dir):
|
||||||
"""
|
"""
|
||||||
untar file into dir
|
untar file into dir
|
||||||
@@ -232,6 +239,7 @@ def w2p_pack(filename, path, compiled=False):
|
|||||||
tarfp.close()
|
tarfp.close()
|
||||||
os.unlink(tarname)
|
os.unlink(tarname)
|
||||||
|
|
||||||
|
|
||||||
def w2p_unpack(filename, path, delete_tar=True):
|
def w2p_unpack(filename, path, delete_tar=True):
|
||||||
filename = abspath(filename)
|
filename = abspath(filename)
|
||||||
path = abspath(path)
|
path = abspath(path)
|
||||||
@@ -261,14 +269,16 @@ def w2p_pack_plugin(filename, path, plugin_name):
|
|||||||
filename = abspath(filename)
|
filename = abspath(filename)
|
||||||
path = abspath(path)
|
path = abspath(path)
|
||||||
if not filename.endswith('web2py.plugin.%s.w2p' % plugin_name):
|
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')
|
plugin_tarball = tarfile.open(filename, 'w:gz')
|
||||||
try:
|
try:
|
||||||
app_dir = path
|
app_dir = path
|
||||||
while app_dir[-1] == '/':
|
while app_dir[-1] == '/':
|
||||||
app_dir = app_dir[:-1]
|
app_dir = app_dir[:-1]
|
||||||
files1=glob.glob(os.path.join(app_dir,'*/plugin_%s.*' % plugin_name))
|
files1 = glob.glob(
|
||||||
files2=glob.glob(os.path.join(app_dir,'*/plugin_%s/*' % plugin_name))
|
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:
|
for file in files1 + files2:
|
||||||
plugin_tarball.add(file, arcname=file[len(app_dir) + 1:])
|
plugin_tarball.add(file, arcname=file[len(app_dir) + 1:])
|
||||||
finally:
|
finally:
|
||||||
@@ -279,7 +289,7 @@ def w2p_unpack_plugin(filename, path, delete_tar=True):
|
|||||||
filename = abspath(filename)
|
filename = abspath(filename)
|
||||||
path = abspath(path)
|
path = abspath(path)
|
||||||
if not os.path.basename(filename).startswith('web2py.plugin.'):
|
if not os.path.basename(filename).startswith('web2py.plugin.'):
|
||||||
raise Exception, "Not a web2py plugin"
|
raise Exception("Not a web2py plugin")
|
||||||
w2p_unpack(filename, path, delete_tar)
|
w2p_unpack(filename, path, delete_tar)
|
||||||
|
|
||||||
|
|
||||||
@@ -306,6 +316,7 @@ def tar_compiled(file, dir, expression='^.+$'):
|
|||||||
tar.add(filename, file, False)
|
tar.add(filename, file, False)
|
||||||
tar.close()
|
tar.close()
|
||||||
|
|
||||||
|
|
||||||
def up(path):
|
def up(path):
|
||||||
return os.path.dirname(os.path.normpath(path))
|
return os.path.dirname(os.path.normpath(path))
|
||||||
|
|
||||||
@@ -338,6 +349,7 @@ def check_credentials(request, other_application='admin', expiration = 60*60):
|
|||||||
s = get_session(request, other_application)
|
s = get_session(request, other_application)
|
||||||
return (s.authorized and s.last_time and s.last_time > dt)
|
return (s.authorized and s.last_time and s.last_time > dt)
|
||||||
|
|
||||||
|
|
||||||
def fix_newlines(path):
|
def fix_newlines(path):
|
||||||
regex = re.compile(r'''(\r
|
regex = re.compile(r'''(\r
|
||||||
|\r|
|
|\r|
|
||||||
@@ -348,6 +360,7 @@ def fix_newlines(path):
|
|||||||
if wdata != rdata:
|
if wdata != rdata:
|
||||||
write_file(filename, wdata, 'wb')
|
write_file(filename, wdata, 'wb')
|
||||||
|
|
||||||
|
|
||||||
def copystream(
|
def copystream(
|
||||||
src,
|
src,
|
||||||
dest,
|
dest,
|
||||||
@@ -379,6 +392,7 @@ def make_fake_file_like_object():
|
|||||||
class LogFile(object):
|
class LogFile(object):
|
||||||
def write(self, value):
|
def write(self, value):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
pass
|
pass
|
||||||
return LogFile()
|
return LogFile()
|
||||||
@@ -386,6 +400,8 @@ def make_fake_file_like_object():
|
|||||||
|
|
||||||
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
|
# settings imports fileutils too
|
||||||
|
|
||||||
|
|
||||||
def abspath(*relpath, **base):
|
def abspath(*relpath, **base):
|
||||||
"convert relative path to absolute path based (by default) on applications_parent"
|
"convert relative path to absolute path based (by default) on applications_parent"
|
||||||
path = os.path.join(*relpath)
|
path = os.path.join(*relpath)
|
||||||
|
|||||||
+60
-34
@@ -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>'
|
css_inline = '<style type="text/css">\n%s\n</style>'
|
||||||
js_inline = '<script type="text/javascript">\n%s\n</script>'
|
js_inline = '<script type="text/javascript">\n%s\n</script>'
|
||||||
|
|
||||||
|
|
||||||
class Request(Storage):
|
class Request(Storage):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -138,7 +139,8 @@ class Request(Storage):
|
|||||||
self.is_restful = True
|
self.is_restful = True
|
||||||
method = _self.env.request_method
|
method = _self.env.request_method
|
||||||
if len(_self.args) and '.' in _self.args[-1]:
|
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'] = \
|
current.response.headers['Content-Type'] = \
|
||||||
contenttype(_self.extension.lower())
|
contenttype(_self.extension.lower())
|
||||||
if not method in ['GET', 'POST', 'DELETE', 'PUT']:
|
if not method in ['GET', 'POST', 'DELETE', 'PUT']:
|
||||||
@@ -197,7 +199,8 @@ class Response(Storage):
|
|||||||
def render(self, *a, **b):
|
def render(self, *a, **b):
|
||||||
from compileapp import run_view_in
|
from compileapp import run_view_in
|
||||||
if len(a) > 2:
|
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:
|
elif len(a) == 2:
|
||||||
(view, self._vars) = (a[0], a[1])
|
(view, self._vars) = (a[0], a[1])
|
||||||
elif len(a) == 1 and isinstance(a[0], str):
|
elif len(a) == 1 and isinstance(a[0], str):
|
||||||
@@ -257,6 +260,7 @@ class Response(Storage):
|
|||||||
key = hashlib.md5(repr(files)).hexdigest()
|
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):
|
def call_minify(files=files):
|
||||||
return minify.minify(files,
|
return minify.minify(files,
|
||||||
URL('static', 'temp'),
|
URL('static', 'temp'),
|
||||||
@@ -275,15 +279,22 @@ class Response(Storage):
|
|||||||
if isinstance(item, str):
|
if isinstance(item, str):
|
||||||
f = item.lower().split('?')[0]
|
f = item.lower().split('?')[0]
|
||||||
if self.static_version:
|
if self.static_version:
|
||||||
item = item.replace('/static/', '/static/_%s/' % self.static_version, 1)
|
item = item.replace(
|
||||||
if f.endswith('.css'): s += css_template % item
|
'/static/', '/static/_%s/' % self.static_version, 1)
|
||||||
elif f.endswith('.js'): s += js_template % item
|
if f.endswith('.css'):
|
||||||
elif f.endswith('.coffee'): s += coffee_template % item
|
s += css_template % item
|
||||||
elif f.endswith('.less'): s += less_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)):
|
elif isinstance(item, (list, tuple)):
|
||||||
f = item[0]
|
f = item[0]
|
||||||
if f=='css:inline': s += css_inline % item[1]
|
if f == 'css:inline':
|
||||||
elif f=='js:inline': s += js_inline % item[1]
|
s += css_inline % item[1]
|
||||||
|
elif f == 'js:inline':
|
||||||
|
s += js_inline % item[1]
|
||||||
self.write(s, escape=False)
|
self.write(s, escape=False)
|
||||||
|
|
||||||
def stream(
|
def stream(
|
||||||
@@ -389,7 +400,6 @@ class Response(Storage):
|
|||||||
'attachment; filename=%s' % filename
|
'attachment; filename=%s' % filename
|
||||||
return self.stream(stream, chunk_size=chunk_size, request=request)
|
return self.stream(stream, chunk_size=chunk_size, request=request)
|
||||||
|
|
||||||
|
|
||||||
def json(self, data, default=None):
|
def json(self, data, default=None):
|
||||||
return json(data, default=default or custom_json)
|
return json(data, default=default or custom_json)
|
||||||
|
|
||||||
@@ -408,7 +418,8 @@ class Response(Storage):
|
|||||||
the add function. Example::
|
the add function. Example::
|
||||||
|
|
||||||
import xmlrpclib
|
import xmlrpclib
|
||||||
connection = xmlrpclib.ServerProxy('http://hostname/app/contr/func')
|
connection = xmlrpclib.ServerProxy(
|
||||||
|
'http://hostname/app/contr/func')
|
||||||
print connection.add(3, 4)
|
print connection.add(3, 4)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -422,8 +433,8 @@ class Response(Storage):
|
|||||||
args=current.request.application)
|
args=current.request.application)
|
||||||
from gluon.dal import THREAD_LOCAL
|
from gluon.dal import THREAD_LOCAL
|
||||||
if hasattr(THREAD_LOCAL, 'instances'):
|
if hasattr(THREAD_LOCAL, 'instances'):
|
||||||
dbstats = [TABLE(*[TR(PRE(row[0]),'%.2fms' % (row[1]*1000)) \
|
dbstats = [TABLE(*[TR(PRE(row[0]), '%.2fms' % (row[1] * 1000))
|
||||||
for row in i.db._timings]) \
|
for row in i.db._timings])
|
||||||
for i in THREAD_LOCAL.instances]
|
for i in THREAD_LOCAL.instances]
|
||||||
dbtables = dict([(regex_nopasswd.sub('******', i.uri),
|
dbtables = dict([(regex_nopasswd.sub('******', i.uri),
|
||||||
{'defined':
|
{'defined':
|
||||||
@@ -440,20 +451,30 @@ class Response(Storage):
|
|||||||
backtotop = A('Back to top', _href="#totop-%s" % u)
|
backtotop = A('Back to top', _href="#totop-%s" % u)
|
||||||
return DIV(
|
return DIV(
|
||||||
BUTTON('design', _onclick="document.location='%s'" % admin),
|
BUTTON('design', _onclick="document.location='%s'" % admin),
|
||||||
BUTTON('request',_onclick="jQuery('#request-%s').slideToggle()"%u),
|
BUTTON('request',
|
||||||
BUTTON('response',_onclick="jQuery('#response-%s').slideToggle()"%u),
|
_onclick="jQuery('#request-%s').slideToggle()" % u),
|
||||||
BUTTON('session',_onclick="jQuery('#session-%s').slideToggle()"%u),
|
BUTTON('response',
|
||||||
BUTTON('db tables',_onclick="jQuery('#db-tables-%s').slideToggle()"%u),
|
_onclick="jQuery('#response-%s').slideToggle()" % u),
|
||||||
BUTTON('db stats',_onclick="jQuery('#db-stats-%s').slideToggle()"%u),
|
BUTTON('session',
|
||||||
DIV(BEAUTIFY(current.request), backtotop,_class="hidden",_id="request-%s"%u),
|
_onclick="jQuery('#session-%s').slideToggle()" % u),
|
||||||
DIV(BEAUTIFY(current.session), backtotop, _class="hidden",_id="session-%s"%u),
|
BUTTON('db tables',
|
||||||
DIV(BEAUTIFY(current.response), backtotop, _class="hidden",_id="response-%s"%u),
|
_onclick="jQuery('#db-tables-%s').slideToggle()" % u),
|
||||||
DIV(BEAUTIFY(dbtables), backtotop, _class="hidden",_id="db-tables-%s"%u),
|
BUTTON('db stats',
|
||||||
DIV(BEAUTIFY(dbstats), backtotop, _class="hidden",_id="db-stats-%s"%u),
|
_onclick="jQuery('#db-stats-%s').slideToggle()" % u),
|
||||||
SCRIPT("jQuery('.hidden').hide()")
|
DIV(BEAUTIFY(current.request), backtotop,
|
||||||
,_id="totop-%s" % u
|
_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):
|
class Session(Storage):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -479,8 +500,10 @@ class Session(Storage):
|
|||||||
and it is used to determine a session prefix.
|
and it is used to determine a session prefix.
|
||||||
separate can be True and it is set to session_name[-2:]
|
separate can be True and it is set to session_name[-2:]
|
||||||
"""
|
"""
|
||||||
if request is None: request = current.request
|
if request is None:
|
||||||
if response is None: response = current.response
|
request = current.request
|
||||||
|
if response is None:
|
||||||
|
response = current.response
|
||||||
if separate == True:
|
if separate == True:
|
||||||
separate = lambda session_name: session_name[-2:]
|
separate = lambda session_name: session_name[-2:]
|
||||||
self._unlock(response)
|
self._unlock(response)
|
||||||
@@ -512,7 +535,8 @@ class Session(Storage):
|
|||||||
response.session_cookie_key = cookie_key
|
response.session_cookie_key = cookie_key
|
||||||
response.session_cookie_compression_level = compression_level
|
response.session_cookie_compression_level = compression_level
|
||||||
if session_cookie_data:
|
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:
|
if data:
|
||||||
self.update(data)
|
self.update(data)
|
||||||
# else if we are supposed to use file based sessions
|
# else if we are supposed to use file based sessions
|
||||||
@@ -544,7 +568,7 @@ class Session(Storage):
|
|||||||
oc = response.session_filename.split('/')[-1]\
|
oc = response.session_filename.split('/')[-1]\
|
||||||
.split('-')[0]
|
.split('-')[0]
|
||||||
if check_client and client != oc:
|
if check_client and client != oc:
|
||||||
raise Exception, "cookie attack"
|
raise Exception("cookie attack")
|
||||||
finally:
|
finally:
|
||||||
pass
|
pass
|
||||||
#This causes admin login to break. Must find out why.
|
#This causes admin login to break. Must find out why.
|
||||||
@@ -597,13 +621,13 @@ class Session(Storage):
|
|||||||
# Get session data out of the database
|
# Get session data out of the database
|
||||||
(record_id, unique_key) = response.session_id.split(':')
|
(record_id, unique_key) = response.session_id.split(':')
|
||||||
if record_id == '0':
|
if record_id == '0':
|
||||||
raise Exception, 'record_id == 0'
|
raise Exception('record_id == 0')
|
||||||
# Select from database
|
# Select from database
|
||||||
if not session_cookie_data:
|
if not session_cookie_data:
|
||||||
rows = db(table.id == record_id).select()
|
rows = db(table.id == record_id).select()
|
||||||
# Make sure the session data exists in the database
|
# Make sure the session data exists in the database
|
||||||
if len(rows) == 0 or rows[0].unique_key != unique_key:
|
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)
|
# rows[0].update_record(locked=True)
|
||||||
# Unpickle the data
|
# Unpickle the data
|
||||||
session_data = cPickle.loads(rows[0].session_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] = response.session_id
|
||||||
rcookies[response.session_id_name]['path'] = '/'
|
rcookies[response.session_id_name]['path'] = '/'
|
||||||
if cookie_expires:
|
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
|
# if not cookie_key, but session_data_name in cookies
|
||||||
# expire session_data_name from cookies
|
# expire session_data_name from cookies
|
||||||
if session_cookie_data:
|
if session_cookie_data:
|
||||||
@@ -654,7 +679,8 @@ class Session(Storage):
|
|||||||
self._forget = True
|
self._forget = True
|
||||||
|
|
||||||
def _try_store_in_cookie(self, request, response):
|
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
|
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
|
expires = response.session_cookie_expires
|
||||||
@@ -740,7 +766,7 @@ class Session(Storage):
|
|||||||
try:
|
try:
|
||||||
portalocker.unlock(response.session_file)
|
portalocker.unlock(response.session_file)
|
||||||
response.session_locked = False
|
response.session_locked = False
|
||||||
except: ### this should never happen but happens in Windows
|
except: # this should never happen but happens in Windows
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _close(self, response):
|
def _close(self, response):
|
||||||
|
|||||||
+5
-3
@@ -50,7 +50,7 @@ class Highlighter(object):
|
|||||||
elif mode == 'HTML':
|
elif mode == 'HTML':
|
||||||
self.suppress_tokens = []
|
self.suppress_tokens = []
|
||||||
else:
|
else:
|
||||||
raise SyntaxError, 'Unknown mode: %s' % mode
|
raise SyntaxError('Unknown mode: %s' % mode)
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
|
||||||
def c_tokenizer(
|
def c_tokenizer(
|
||||||
@@ -309,8 +309,10 @@ def highlight(
|
|||||||
else:
|
else:
|
||||||
lineno = highlight_line
|
lineno = highlight_line
|
||||||
if lineno < len(lines):
|
if lineno < len(lines):
|
||||||
lines[lineno] = '<div style="%s">%s</div>' % (linehighlight_style, lines[lineno])
|
lines[lineno] = '<div style="%s">%s</div>' % (
|
||||||
linenumbers[lineno] = '<div style="%s">%s</div>' % (linehighlight_style, linenumbers[lineno])
|
linehighlight_style, lines[lineno])
|
||||||
|
linenumbers[lineno] = '<div style="%s">%s</div>' % (
|
||||||
|
linehighlight_style, linenumbers[lineno])
|
||||||
|
|
||||||
if context_lines:
|
if context_lines:
|
||||||
if lineno + context_lines < len(lines):
|
if lineno + context_lines < len(lines):
|
||||||
|
|||||||
+137
-62
@@ -33,7 +33,8 @@ regex_crlf = re.compile('\r|\n')
|
|||||||
join = ''.join
|
join = ''.join
|
||||||
|
|
||||||
# name2codepoint is incomplete respect to xhtml (and xml): 'apos' is missing.
|
# name2codepoint is incomplete respect to xhtml (and xml): 'apos' is missing.
|
||||||
entitydefs = dict(map(lambda (k,v): (k, unichr(v).encode('utf-8')), name2codepoint.iteritems()))
|
entitydefs = dict(map(lambda (
|
||||||
|
k, v): (k, unichr(v).encode('utf-8')), name2codepoint.iteritems()))
|
||||||
entitydefs.setdefault('apos', u"'".encode('utf-8'))
|
entitydefs.setdefault('apos', u"'".encode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
@@ -135,6 +136,7 @@ def truncate_string(text, length, dots='...'):
|
|||||||
text = text[:length - len(dots)].encode('utf-8') + dots
|
text = text[:length - len(dots)].encode('utf-8') + dots
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def URL(
|
def URL(
|
||||||
a=None,
|
a=None,
|
||||||
c=None,
|
c=None,
|
||||||
@@ -234,7 +236,8 @@ def URL(
|
|||||||
|
|
||||||
from rewrite import url_out # done here in case used not-in web2py
|
from rewrite import url_out # done here in case used not-in web2py
|
||||||
|
|
||||||
if args in (None,[]): args = []
|
if args in (None, []):
|
||||||
|
args = []
|
||||||
vars = vars or {}
|
vars = vars or {}
|
||||||
application = None
|
application = None
|
||||||
controller = None
|
controller = None
|
||||||
@@ -244,8 +247,10 @@ def URL(
|
|||||||
args = [args]
|
args = [args]
|
||||||
|
|
||||||
if not r:
|
if not r:
|
||||||
if a and not c and not f: (f,a,c)=(a,c,f)
|
if a and not c and not f:
|
||||||
elif a and c and not f: (c,f,a)=(a,c,f)
|
(f, a, c) = (a, c, f)
|
||||||
|
elif a and c and not f:
|
||||||
|
(c, f, a) = (a, c, f)
|
||||||
from globals import current
|
from globals import current
|
||||||
if hasattr(current, 'request'):
|
if hasattr(current, 'request'):
|
||||||
r = current.request
|
r = current.request
|
||||||
@@ -266,7 +271,8 @@ def URL(
|
|||||||
if hasattr(f, '__name__'):
|
if hasattr(f, '__name__'):
|
||||||
function = f.__name__
|
function = f.__name__
|
||||||
else:
|
else:
|
||||||
raise SyntaxError, 'when calling URL, function or function name required'
|
raise SyntaxError(
|
||||||
|
'when calling URL, function or function name required')
|
||||||
elif '/' in f:
|
elif '/' in f:
|
||||||
if f.startswith("/"):
|
if f.startswith("/"):
|
||||||
f = f[1:]
|
f = f[1:]
|
||||||
@@ -286,14 +292,16 @@ def URL(
|
|||||||
function2 = '%s.%s' % (function, extension or 'html')
|
function2 = '%s.%s' % (function, extension or 'html')
|
||||||
|
|
||||||
if not (application and controller and function):
|
if not (application and controller and function):
|
||||||
raise SyntaxError, 'not enough information to build the url (%s %s %s)' % (application, controller, function)
|
raise SyntaxError('not enough information to build the url (%s %s %s)' % (application, controller, function))
|
||||||
|
|
||||||
if args:
|
if args:
|
||||||
if url_encode:
|
if url_encode:
|
||||||
if encode_embedded_slash:
|
if encode_embedded_slash:
|
||||||
other = '/' + '/'.join([urllib.quote(str(x), '') for x in args])
|
other = '/' + '/'.join([urllib.quote(str(
|
||||||
|
x), '') for x in args])
|
||||||
else:
|
else:
|
||||||
other = args and urllib.quote('/' + '/'.join([str(x) for x in args]))
|
other = args and urllib.quote(
|
||||||
|
'/' + '/'.join([str(x) for x in args]))
|
||||||
else:
|
else:
|
||||||
other = args and ('/' + '/'.join([str(x) for x in args]))
|
other = args and ('/' + '/'.join([str(x) for x in args]))
|
||||||
else:
|
else:
|
||||||
@@ -334,7 +342,8 @@ def URL(
|
|||||||
|
|
||||||
# re-assembling the same way during hash authentication
|
# re-assembling the same way during hash authentication
|
||||||
message = h_args + '?' + urllib.urlencode(sorted(h_vars))
|
message = h_args + '?' + urllib.urlencode(sorted(h_vars))
|
||||||
sig = simple_hash(message, hmac_key or '',salt or '',digest_alg='sha1')
|
sig = simple_hash(
|
||||||
|
message, hmac_key or '', salt or '', digest_alg='sha1')
|
||||||
# add the signature into vars
|
# add the signature into vars
|
||||||
list_vars.append(('_signature', sig))
|
list_vars.append(('_signature', sig))
|
||||||
|
|
||||||
@@ -352,7 +361,7 @@ def URL(
|
|||||||
function += '.' + extension
|
function += '.' + extension
|
||||||
|
|
||||||
if regex_crlf.search(join([application, controller, function, other])):
|
if regex_crlf.search(join([application, controller, function, other])):
|
||||||
raise SyntaxError, 'CRLF Injection Detected'
|
raise SyntaxError('CRLF Injection Detected')
|
||||||
|
|
||||||
url = url_out(r, env, application, controller, function,
|
url = url_out(r, env, application, controller, function,
|
||||||
args, other, scheme, host, port)
|
args, other, scheme, host, port)
|
||||||
@@ -479,8 +488,10 @@ class XmlComponent(object):
|
|||||||
|
|
||||||
def xml(self):
|
def xml(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def __mul__(self, n):
|
def __mul__(self, n):
|
||||||
return CAT(*[self for i in range(n)])
|
return CAT(*[self for i in range(n)])
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
if isinstance(self, CAT):
|
if isinstance(self, CAT):
|
||||||
components = self.components
|
components = self.components
|
||||||
@@ -595,7 +606,8 @@ class XML(XmlComponent):
|
|||||||
return str(self)[i:j]
|
return str(self)[i:j]
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for c in str(self): yield c
|
for c in str(self):
|
||||||
|
yield c
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(str(self))
|
return len(str(self))
|
||||||
@@ -616,14 +628,17 @@ class XML(XmlComponent):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
### important to allow safe session.flash=T(....)
|
### important to allow safe session.flash=T(....)
|
||||||
|
|
||||||
|
|
||||||
def XML_unpickle(data):
|
def XML_unpickle(data):
|
||||||
return marshal.loads(data)
|
return marshal.loads(data)
|
||||||
|
|
||||||
|
|
||||||
def XML_pickle(data):
|
def XML_pickle(data):
|
||||||
return XML_unpickle, (marshal.dumps(str(data)),)
|
return XML_unpickle, (marshal.dumps(str(data)),)
|
||||||
copy_reg.pickle(XML, XML_pickle, XML_unpickle)
|
copy_reg.pickle(XML, XML_pickle, XML_unpickle)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DIV(XmlComponent):
|
class DIV(XmlComponent):
|
||||||
"""
|
"""
|
||||||
HTML helper, for easy generating and manipulating a DOM structure.
|
HTML helper, for easy generating and manipulating a DOM structure.
|
||||||
@@ -657,8 +672,8 @@ class DIV(XmlComponent):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if self.tag[-1:] == '/' and components:
|
if self.tag[-1:] == '/' and components:
|
||||||
raise SyntaxError, '<%s> tags cannot have components'\
|
raise SyntaxError('<%s> tags cannot have components'
|
||||||
% self.tag
|
% self.tag)
|
||||||
if len(components) == 1 and isinstance(components[0], (list, tuple)):
|
if len(components) == 1 and isinstance(components[0], (list, tuple)):
|
||||||
self.components = list(components[0])
|
self.components = list(components[0])
|
||||||
else:
|
else:
|
||||||
@@ -943,7 +958,6 @@ class DIV(XmlComponent):
|
|||||||
regex_class = re.compile('\.([\w\-]+)')
|
regex_class = re.compile('\.([\w\-]+)')
|
||||||
regex_attr = re.compile('\[([\w\-\:]+)=(.*?)\]')
|
regex_attr = re.compile('\[([\w\-\:]+)=(.*?)\]')
|
||||||
|
|
||||||
|
|
||||||
def elements(self, *args, **kargs):
|
def elements(self, *args, **kargs):
|
||||||
"""
|
"""
|
||||||
find all component that match the supplied attribute dictionary,
|
find all component that match the supplied attribute dictionary,
|
||||||
@@ -1025,7 +1039,8 @@ class DIV(XmlComponent):
|
|||||||
elif len(args) == 1:
|
elif len(args) == 1:
|
||||||
items = args[0].split()
|
items = args[0].split()
|
||||||
if len(items) > 1:
|
if len(items) > 1:
|
||||||
subset=[a.elements(' '.join(items[1:]),**kargs) for a in self.elements(items[0])]
|
subset = [a.elements(' '.join(
|
||||||
|
items[1:]), **kargs) for a in self.elements(items[0])]
|
||||||
return reduce(lambda a, b: a + b, subset, [])
|
return reduce(lambda a, b: a + b, subset, [])
|
||||||
else:
|
else:
|
||||||
item = items[0]
|
item = items[0]
|
||||||
@@ -1035,9 +1050,12 @@ class DIV(XmlComponent):
|
|||||||
match_class = self.regex_class.search(item)
|
match_class = self.regex_class.search(item)
|
||||||
match_attr = self.regex_attr.finditer(item)
|
match_attr = self.regex_attr.finditer(item)
|
||||||
args = []
|
args = []
|
||||||
if match_tag: args = [match_tag.group()]
|
if match_tag:
|
||||||
if match_id: kargs['_id'] = match_id.group(1)
|
args = [match_tag.group()]
|
||||||
if match_class: kargs['_class'] = re.compile('(?<!\w)%s(?!\w)' % \
|
if match_id:
|
||||||
|
kargs['_id'] = match_id.group(1)
|
||||||
|
if match_class:
|
||||||
|
kargs['_class'] = re.compile('(?<!\w)%s(?!\w)' %
|
||||||
match_class.group(1).replace('-', '\\-').replace(':', '\\:'))
|
match_class.group(1).replace('-', '\\-').replace(':', '\\:'))
|
||||||
for item in match_attr:
|
for item in match_attr:
|
||||||
kargs['_' + item.group(1)] = item.group(2)
|
kargs['_' + item.group(1)] = item.group(2)
|
||||||
@@ -1076,6 +1094,7 @@ class DIV(XmlComponent):
|
|||||||
find_text = replace is not False and kargs.get('find_text', False)
|
find_text = replace is not False and kargs.get('find_text', False)
|
||||||
is_regex = not isinstance(find_text, (str, int, bool))
|
is_regex = not isinstance(find_text, (str, int, bool))
|
||||||
find_components = not (check and first_only)
|
find_components = not (check and first_only)
|
||||||
|
|
||||||
def replace_component(i):
|
def replace_component(i):
|
||||||
if replace is None:
|
if replace is None:
|
||||||
del self[i]
|
del self[i]
|
||||||
@@ -1134,7 +1153,8 @@ class DIV(XmlComponent):
|
|||||||
check = False
|
check = False
|
||||||
if check:
|
if check:
|
||||||
matches.append(c)
|
matches.append(c)
|
||||||
if first_only: break
|
if first_only:
|
||||||
|
break
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
return matches
|
return matches
|
||||||
@@ -1150,19 +1170,23 @@ class DIV(XmlComponent):
|
|||||||
return None
|
return None
|
||||||
return sibs[0]
|
return sibs[0]
|
||||||
|
|
||||||
|
|
||||||
class CAT(DIV):
|
class CAT(DIV):
|
||||||
|
|
||||||
tag = ''
|
tag = ''
|
||||||
|
|
||||||
|
|
||||||
def TAG_unpickler(data):
|
def TAG_unpickler(data):
|
||||||
return cPickle.loads(data)
|
return cPickle.loads(data)
|
||||||
|
|
||||||
|
|
||||||
def TAG_pickler(data):
|
def TAG_pickler(data):
|
||||||
d = DIV()
|
d = DIV()
|
||||||
d.__dict__ = data.__dict__
|
d.__dict__ = data.__dict__
|
||||||
marshal_dump = cPickle.dumps(d)
|
marshal_dump = cPickle.dumps(d)
|
||||||
return (TAG_unpickler, (marshal_dump,))
|
return (TAG_unpickler, (marshal_dump,))
|
||||||
|
|
||||||
|
|
||||||
class __TAG__(XmlComponent):
|
class __TAG__(XmlComponent):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -1181,6 +1205,7 @@ class __TAG__(XmlComponent):
|
|||||||
name = name[:-1] + '/'
|
name = name[:-1] + '/'
|
||||||
if isinstance(name, unicode):
|
if isinstance(name, unicode):
|
||||||
name = name.encode('utf-8')
|
name = name.encode('utf-8')
|
||||||
|
|
||||||
class __tag__(DIV):
|
class __tag__(DIV):
|
||||||
tag = name
|
tag = name
|
||||||
copy_reg.pickle(__tag__, TAG_pickler, TAG_unpickler)
|
copy_reg.pickle(__tag__, TAG_pickler, TAG_unpickler)
|
||||||
@@ -1238,6 +1263,7 @@ class HTML(DIV):
|
|||||||
(fa, co) = self._xml()
|
(fa, co) = self._xml()
|
||||||
return '%s<%s%s>%s</%s>' % (doctype, self.tag, fa, co, self.tag)
|
return '%s<%s%s>%s</%s>' % (doctype, self.tag, fa, co, self.tag)
|
||||||
|
|
||||||
|
|
||||||
class XHTML(DIV):
|
class XHTML(DIV):
|
||||||
"""
|
"""
|
||||||
This is XHTML version of the HTML helper.
|
This is XHTML version of the HTML helper.
|
||||||
@@ -1297,6 +1323,7 @@ class HEAD(DIV):
|
|||||||
|
|
||||||
tag = 'head'
|
tag = 'head'
|
||||||
|
|
||||||
|
|
||||||
class TITLE(DIV):
|
class TITLE(DIV):
|
||||||
|
|
||||||
tag = 'title'
|
tag = 'title'
|
||||||
@@ -1592,6 +1619,7 @@ class TR(DIV):
|
|||||||
def _fixup(self):
|
def _fixup(self):
|
||||||
self._wrap_components((TD, TH), TD)
|
self._wrap_components((TD, TH), TD)
|
||||||
|
|
||||||
|
|
||||||
class THEAD(DIV):
|
class THEAD(DIV):
|
||||||
|
|
||||||
tag = 'thead'
|
tag = 'thead'
|
||||||
@@ -1641,10 +1669,12 @@ class TABLE(DIV):
|
|||||||
def _fixup(self):
|
def _fixup(self):
|
||||||
self._wrap_components((TR, TBODY, THEAD, TFOOT, COL, COLGROUP), TR)
|
self._wrap_components((TR, TBODY, THEAD, TFOOT, COL, COLGROUP), TR)
|
||||||
|
|
||||||
|
|
||||||
class I(DIV):
|
class I(DIV):
|
||||||
|
|
||||||
tag = 'i'
|
tag = 'i'
|
||||||
|
|
||||||
|
|
||||||
class IFRAME(DIV):
|
class IFRAME(DIV):
|
||||||
|
|
||||||
tag = 'iframe'
|
tag = 'iframe'
|
||||||
@@ -1758,7 +1788,8 @@ class INPUT(DIV):
|
|||||||
if name and hasattr(self, 'errors') \
|
if name and hasattr(self, 'errors') \
|
||||||
and self.errors.get(name, None) \
|
and self.errors.get(name, None) \
|
||||||
and self['hideerror'] != True:
|
and self['hideerror'] != True:
|
||||||
self['_class'] = (self['_class'] and self['_class']+' ' or '')+'invalidinput'
|
self['_class'] = (self['_class'] and self['_class']
|
||||||
|
+ ' ' or '') + 'invalidinput'
|
||||||
return DIV.xml(self) + DIV(
|
return DIV.xml(self) + DIV(
|
||||||
DIV(
|
DIV(
|
||||||
self.errors[name], _class='error',
|
self.errors[name], _class='error',
|
||||||
@@ -1808,6 +1839,7 @@ class OBJECT(DIV):
|
|||||||
|
|
||||||
tag = 'object'
|
tag = 'object'
|
||||||
|
|
||||||
|
|
||||||
class OPTGROUP(DIV):
|
class OPTGROUP(DIV):
|
||||||
|
|
||||||
tag = 'optgroup'
|
tag = 'optgroup'
|
||||||
@@ -2052,9 +2084,11 @@ class FORM(DIV):
|
|||||||
any other kwargs will be passed for form.accepts(...)
|
any other kwargs will be passed for form.accepts(...)
|
||||||
"""
|
"""
|
||||||
from gluon import current, redirect
|
from gluon import current, redirect
|
||||||
kwargs['request_vars'] = kwargs.get('request_vars',current.request.post_vars)
|
kwargs['request_vars'] = kwargs.get(
|
||||||
|
'request_vars', current.request.post_vars)
|
||||||
kwargs['session'] = kwargs.get('session', current.session)
|
kwargs['session'] = kwargs.get('session', current.session)
|
||||||
kwargs['dbio'] = kwargs.get('dbio',False) # necessary for SQLHTML forms
|
kwargs['dbio'] = kwargs.get('dbio', False)
|
||||||
|
# necessary for SQLHTML forms
|
||||||
|
|
||||||
onsuccess = kwargs.get('onsuccess', 'flash')
|
onsuccess = kwargs.get('onsuccess', 'flash')
|
||||||
onfailure = kwargs.get('onfailure', 'flash')
|
onfailure = kwargs.get('onfailure', 'flash')
|
||||||
@@ -2131,7 +2165,8 @@ class FORM(DIV):
|
|||||||
def action():
|
def action():
|
||||||
return dict(form=SQLFORM(db.table).process(onsuccess=my_callback)
|
return dict(form=SQLFORM(db.table).process(onsuccess=my_callback)
|
||||||
"""
|
"""
|
||||||
kwargs['dbio'] = kwargs.get('dbio',True) # necessary for SQLHTML forms
|
kwargs['dbio'] = kwargs.get('dbio', True)
|
||||||
|
# necessary for SQLHTML forms
|
||||||
self.validate(**kwargs)
|
self.validate(**kwargs)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@@ -2143,15 +2178,15 @@ class FORM(DIV):
|
|||||||
INPUT(_type="button", _value=value, _class=_class,
|
INPUT(_type="button", _value=value, _class=_class,
|
||||||
_onclick=self.REDIRECT_JS % url))
|
_onclick=self.REDIRECT_JS % url))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def confirm(text='OK', buttons=None, hidden=None):
|
def confirm(text='OK', buttons=None, hidden=None):
|
||||||
if not buttons: buttons = {}
|
if not buttons:
|
||||||
if not hidden: hidden={}
|
buttons = {}
|
||||||
|
if not hidden:
|
||||||
|
hidden = {}
|
||||||
inputs = [INPUT(_type='button',
|
inputs = [INPUT(_type='button',
|
||||||
_value=name,
|
_value=name,
|
||||||
_onclick=FORM.REDIRECT_JS % link) \
|
_onclick=FORM.REDIRECT_JS % link)
|
||||||
for name, link in buttons.iteritems()]
|
for name, link in buttons.iteritems()]
|
||||||
inputs += [INPUT(_type='hidden',
|
inputs += [INPUT(_type='hidden',
|
||||||
_name=name,
|
_name=name,
|
||||||
@@ -2161,6 +2196,7 @@ class FORM(DIV):
|
|||||||
form.process()
|
form.process()
|
||||||
return form
|
return form
|
||||||
|
|
||||||
|
|
||||||
class BEAUTIFY(DIV):
|
class BEAUTIFY(DIV):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -2215,9 +2251,11 @@ class BEAUTIFY(DIV):
|
|||||||
if filtered_key is None:
|
if filtered_key is None:
|
||||||
continue
|
continue
|
||||||
value = c[key]
|
value = c[key]
|
||||||
if type(value) == types.LambdaType:
|
if isinstance(value, types.LambdaType):
|
||||||
continue
|
continue
|
||||||
rows.append(TR(TD(filtered_key, _style='font-weight:bold;vertical-align:top'),
|
rows.append(
|
||||||
|
TR(
|
||||||
|
TD(filtered_key, _style='font-weight:bold;vertical-align:top'),
|
||||||
TD(':', _valign='top'),
|
TD(':', _valign='top'),
|
||||||
TD(BEAUTIFY(value, **attributes))))
|
TD(BEAUTIFY(value, **attributes))))
|
||||||
components.append(TABLE(*rows, **attributes))
|
components.append(TABLE(*rows, **attributes))
|
||||||
@@ -2405,19 +2443,23 @@ class web2pyHTMLParser(HTMLParser):
|
|||||||
self.tags = [x for x in __all__ if isinstance(eval(x), DIV)]
|
self.tags = [x for x in __all__ if isinstance(eval(x), DIV)]
|
||||||
self.last = None
|
self.last = None
|
||||||
self.feed(text)
|
self.feed(text)
|
||||||
|
|
||||||
def handle_starttag(self, tagname, attrs):
|
def handle_starttag(self, tagname, attrs):
|
||||||
if tagname.upper() in self.tags:
|
if tagname.upper() in self.tags:
|
||||||
tag = eval(tagname.upper())
|
tag = eval(tagname.upper())
|
||||||
else:
|
else:
|
||||||
if tagname in self.closed: tagname+='/'
|
if tagname in self.closed:
|
||||||
|
tagname += '/'
|
||||||
tag = TAG[tagname]()
|
tag = TAG[tagname]()
|
||||||
for key,value in attrs: tag['_'+key]=value
|
for key, value in attrs:
|
||||||
|
tag['_' + key] = value
|
||||||
tag.parent = self.parent
|
tag.parent = self.parent
|
||||||
self.parent.append(tag)
|
self.parent.append(tag)
|
||||||
if not tag.tag.endswith('/'):
|
if not tag.tag.endswith('/'):
|
||||||
self.parent = tag
|
self.parent = tag
|
||||||
else:
|
else:
|
||||||
self.last = tag.tag[:-1]
|
self.last = tag.tag[:-1]
|
||||||
|
|
||||||
def handle_data(self, data):
|
def handle_data(self, data):
|
||||||
if not isinstance(data, unicode):
|
if not isinstance(data, unicode):
|
||||||
try:
|
try:
|
||||||
@@ -2425,13 +2467,16 @@ class web2pyHTMLParser(HTMLParser):
|
|||||||
except:
|
except:
|
||||||
data = data.decode('latin1')
|
data = data.decode('latin1')
|
||||||
self.parent.append(data.encode('utf8', 'xmlcharref'))
|
self.parent.append(data.encode('utf8', 'xmlcharref'))
|
||||||
|
|
||||||
def handle_charref(self, name):
|
def handle_charref(self, name):
|
||||||
if name.startswith('x'):
|
if name.startswith('x'):
|
||||||
self.parent.append(unichr(int(name[1:], 16)).encode('utf8'))
|
self.parent.append(unichr(int(name[1:], 16)).encode('utf8'))
|
||||||
else:
|
else:
|
||||||
self.parent.append(unichr(int(name)).encode('utf8'))
|
self.parent.append(unichr(int(name)).encode('utf8'))
|
||||||
|
|
||||||
def handle_entityref(self, name):
|
def handle_entityref(self, name):
|
||||||
self.parent.append(entitydefs[name])
|
self.parent.append(entitydefs[name])
|
||||||
|
|
||||||
def handle_endtag(self, tagname):
|
def handle_endtag(self, tagname):
|
||||||
# this deals with unbalanced tags
|
# this deals with unbalanced tags
|
||||||
if tagname == self.last:
|
if tagname == self.last:
|
||||||
@@ -2441,44 +2486,74 @@ class web2pyHTMLParser(HTMLParser):
|
|||||||
parent_tagname = self.parent.tag
|
parent_tagname = self.parent.tag
|
||||||
self.parent = self.parent.parent
|
self.parent = self.parent.parent
|
||||||
except:
|
except:
|
||||||
raise RuntimeError, "unable to balance tag %s" % tagname
|
raise RuntimeError("unable to balance tag %s" % tagname)
|
||||||
if parent_tagname[:len(tagname)] == tagname: break
|
if parent_tagname[:len(tagname)] == tagname: break
|
||||||
|
|
||||||
|
|
||||||
def markdown_serializer(text, tag=None, attr=None):
|
def markdown_serializer(text, tag=None, attr=None):
|
||||||
attr = attr or {}
|
attr = attr or {}
|
||||||
if tag is None: return re.sub('\s+',' ',text)
|
if tag is None:
|
||||||
if tag=='br': return '\n\n'
|
return re.sub('\s+', ' ', text)
|
||||||
if tag=='h1': return '#'+text+'\n\n'
|
if tag == 'br':
|
||||||
if tag=='h2': return '#'*2+text+'\n\n'
|
return '\n\n'
|
||||||
if tag=='h3': return '#'*3+text+'\n\n'
|
if tag == 'h1':
|
||||||
if tag=='h4': return '#'*4+text+'\n\n'
|
return '#' + text + '\n\n'
|
||||||
if tag=='p': return text+'\n\n'
|
if tag == 'h2':
|
||||||
if tag=='b' or tag=='strong': return '**%s**' % text
|
return '#' * 2 + text + '\n\n'
|
||||||
if tag=='em' or tag=='i': return '*%s*' % text
|
if tag == 'h3':
|
||||||
if tag=='tt' or tag=='code': return '`%s`' % text
|
return '#' * 3 + text + '\n\n'
|
||||||
if tag=='a': return '[%s](%s)' % (text,attr.get('_href',''))
|
if tag == 'h4':
|
||||||
if tag=='img': return '' % (attr.get('_alt',''),attr.get('_src',''))
|
return '#' * 4 + text + '\n\n'
|
||||||
|
if tag == 'p':
|
||||||
|
return text + '\n\n'
|
||||||
|
if tag == 'b' or tag == 'strong':
|
||||||
|
return '**%s**' % text
|
||||||
|
if tag == 'em' or tag == 'i':
|
||||||
|
return '*%s*' % text
|
||||||
|
if tag == 'tt' or tag == 'code':
|
||||||
|
return '`%s`' % text
|
||||||
|
if tag == 'a':
|
||||||
|
return '[%s](%s)' % (text, attr.get('_href', ''))
|
||||||
|
if tag == 'img':
|
||||||
|
return '' % (attr.get('_alt', ''), attr.get('_src', ''))
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def markmin_serializer(text, tag=None, attr=None):
|
def markmin_serializer(text, tag=None, attr=None):
|
||||||
attr = attr or {}
|
attr = attr or {}
|
||||||
# if tag is None: return re.sub('\s+',' ',text)
|
# if tag is None: return re.sub('\s+',' ',text)
|
||||||
if tag=='br': return '\n\n'
|
if tag == 'br':
|
||||||
if tag=='h1': return '# '+text+'\n\n'
|
return '\n\n'
|
||||||
if tag=='h2': return '#'*2+' '+text+'\n\n'
|
if tag == 'h1':
|
||||||
if tag=='h3': return '#'*3+' '+text+'\n\n'
|
return '# ' + text + '\n\n'
|
||||||
if tag=='h4': return '#'*4+' '+text+'\n\n'
|
if tag == 'h2':
|
||||||
if tag=='p': return text+'\n\n'
|
return '#' * 2 + ' ' + text + '\n\n'
|
||||||
if tag=='li': return '\n- '+text.replace('\n',' ')
|
if tag == 'h3':
|
||||||
if tag=='tr': return text[3:].replace('\n',' ')+'\n'
|
return '#' * 3 + ' ' + text + '\n\n'
|
||||||
if tag in ['table','blockquote']: return '\n-----\n'+text+'\n------\n'
|
if tag == 'h4':
|
||||||
if tag in ['td','th']: return ' | '+text
|
return '#' * 4 + ' ' + text + '\n\n'
|
||||||
if tag in ['b','strong','label']: return '**%s**' % text
|
if tag == 'p':
|
||||||
if tag in ['em','i']: return "''%s''" % text
|
return text + '\n\n'
|
||||||
if tag in ['tt']: return '``%s``' % text.strip()
|
if tag == 'li':
|
||||||
if tag in ['code']: return '``\n%s``' % text
|
return '\n- ' + text.replace('\n', ' ')
|
||||||
if tag=='a': return '[[%s %s]]' % (text,attr.get('_href',''))
|
if tag == 'tr':
|
||||||
if tag=='img': return '[[%s %s left]]' % (attr.get('_alt','no title'),attr.get('_src',''))
|
return text[3:].replace('\n', ' ') + '\n'
|
||||||
|
if tag in ['table', 'blockquote']:
|
||||||
|
return '\n-----\n' + text + '\n------\n'
|
||||||
|
if tag in ['td', 'th']:
|
||||||
|
return ' | ' + text
|
||||||
|
if tag in ['b', 'strong', 'label']:
|
||||||
|
return '**%s**' % text
|
||||||
|
if tag in ['em', 'i']:
|
||||||
|
return "''%s''" % text
|
||||||
|
if tag in ['tt']:
|
||||||
|
return '``%s``' % text.strip()
|
||||||
|
if tag in ['code']:
|
||||||
|
return '``\n%s``' % text
|
||||||
|
if tag == 'a':
|
||||||
|
return '[[%s %s]]' % (text, attr.get('_href', ''))
|
||||||
|
if tag == 'img':
|
||||||
|
return '[[%s %s left]]' % (attr.get('_alt', 'no title'), attr.get('_src', ''))
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -61,6 +61,7 @@ except NameError:
|
|||||||
|
|
||||||
regex_status = re.compile('^\d{3} \w+$')
|
regex_status = re.compile('^\d{3} \w+$')
|
||||||
|
|
||||||
|
|
||||||
class HTTP(BaseException):
|
class HTTP(BaseException):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -98,7 +99,7 @@ class HTTP(BaseException):
|
|||||||
if isinstance(body, str):
|
if isinstance(body, str):
|
||||||
if len(body) < 512 and \
|
if len(body) < 512 and \
|
||||||
headers['Content-Type'].startswith('text/html'):
|
headers['Content-Type'].startswith('text/html'):
|
||||||
body += '<!-- %s //-->' % ('x'*512) ### trick IE
|
body += '<!-- %s //-->' % ('x' * 512) # trick IE
|
||||||
headers['Content-Length'] = len(body)
|
headers['Content-Length'] = len(body)
|
||||||
rheaders = []
|
rheaders = []
|
||||||
for k, v in headers.iteritems():
|
for k, v in headers.iteritems():
|
||||||
|
|||||||
+1
-1
@@ -106,4 +106,4 @@ for module in base_modules + contributed_modules:
|
|||||||
msg = "Missing dependency: %(module)s\n" % locals()
|
msg = "Missing dependency: %(module)s\n" % locals()
|
||||||
msg += "Try the following command: "
|
msg += "Try the following command: "
|
||||||
msg += "easy_install-%(python_version)s -U %(module)s" % locals()
|
msg += "easy_install-%(python_version)s -U %(module)s" % locals()
|
||||||
raise ImportError, msg
|
raise ImportError(msg)
|
||||||
+49
-15
@@ -66,9 +66,11 @@ regex_langfile = re.compile('^[a-z]{2}(-[a-z]{2})?\.py$')
|
|||||||
regex_backslash = re.compile(r"\\([\\{}%])")
|
regex_backslash = re.compile(r"\\([\\{}%])")
|
||||||
regex_plural = re.compile('%({.+?})')
|
regex_plural = re.compile('%({.+?})')
|
||||||
regex_plural_dict = re.compile('^{(?P<w>[^()[\]][^()[\]]*?)\((?P<n>[^()\[\]]+)\)}$') # %%{word(varname or number)}
|
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$')
|
regex_plural_file = re.compile('^plural-[a-zA-Z]{2}(-[a-zA-Z]{2})?\.py$')
|
||||||
|
|
||||||
|
|
||||||
def safe_eval(text):
|
def safe_eval(text):
|
||||||
if text.strip():
|
if text.strip():
|
||||||
try:
|
try:
|
||||||
@@ -79,6 +81,8 @@ def safe_eval(text):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
# used as default filter in translator.M()
|
# used as default filter in translator.M()
|
||||||
|
|
||||||
|
|
||||||
def markmin(s):
|
def markmin(s):
|
||||||
def markmin_aux(m):
|
def markmin_aux(m):
|
||||||
return '{%s}' % markmin_escape(m.group('s'))
|
return '{%s}' % markmin_escape(m.group('s'))
|
||||||
@@ -86,10 +90,16 @@ def markmin(s):
|
|||||||
sep='br', autolinks=None, id_prefix='')
|
sep='br', autolinks=None, id_prefix='')
|
||||||
|
|
||||||
# UTF8 helper functions
|
# UTF8 helper functions
|
||||||
|
|
||||||
|
|
||||||
def upper_fun(s):
|
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):
|
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):
|
def cap_fun(s):
|
||||||
return unicode(s, 'utf-8').capitalize().encode('utf-8')
|
return unicode(s, 'utf-8').capitalize().encode('utf-8')
|
||||||
ttab_in = maketrans("\\%{}", '\x1c\x1d\x1e\x1f')
|
ttab_in = maketrans("\\%{}", '\x1c\x1d\x1e\x1f')
|
||||||
@@ -107,11 +117,12 @@ ttab_out = maketrans('\x1c\x1d\x1e\x1f', "\\%{}")
|
|||||||
|
|
||||||
global_language_cache = {}
|
global_language_cache = {}
|
||||||
|
|
||||||
|
|
||||||
def get_from_cache(cache, val, fun):
|
def get_from_cache(cache, val, fun):
|
||||||
lang_dict, lock = cache
|
lang_dict, lock = cache
|
||||||
lock.acquire()
|
lock.acquire()
|
||||||
try:
|
try:
|
||||||
result = lang_dict.get(val);
|
result = lang_dict.get(val)
|
||||||
finally:
|
finally:
|
||||||
lock.release()
|
lock.release()
|
||||||
if result:
|
if result:
|
||||||
@@ -123,16 +134,18 @@ def get_from_cache(cache, val, fun):
|
|||||||
lock.release()
|
lock.release()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def clear_cache(filename):
|
def clear_cache(filename):
|
||||||
cache = global_language_cache.setdefault(
|
cache = global_language_cache.setdefault(
|
||||||
filename, ({}, allocate_lock()))
|
filename, ({}, allocate_lock()))
|
||||||
lang_dict, lock = cache
|
lang_dict, lock = cache
|
||||||
lock.acquire()
|
lock.acquire()
|
||||||
try:
|
try:
|
||||||
lang_dict.clear();
|
lang_dict.clear()
|
||||||
finally:
|
finally:
|
||||||
lock.release()
|
lock.release()
|
||||||
|
|
||||||
|
|
||||||
def read_dict_aux(filename):
|
def read_dict_aux(filename):
|
||||||
lang_text = portalocker.read_locked(filename).replace('\r\n', '\n')
|
lang_text = portalocker.read_locked(filename).replace('\r\n', '\n')
|
||||||
clear_cache(filename)
|
clear_cache(filename)
|
||||||
@@ -143,6 +156,7 @@ def read_dict_aux(filename):
|
|||||||
logging.error(status)
|
logging.error(status)
|
||||||
return {'__corrupted__': status}
|
return {'__corrupted__': status}
|
||||||
|
|
||||||
|
|
||||||
def read_dict(filename):
|
def read_dict(filename):
|
||||||
""" return dictionary with translation messages
|
""" return dictionary with translation messages
|
||||||
"""
|
"""
|
||||||
@@ -179,6 +193,7 @@ def read_possible_plural_rules():
|
|||||||
|
|
||||||
PLURAL_RULES = read_possible_plural_rules()
|
PLURAL_RULES = read_possible_plural_rules()
|
||||||
|
|
||||||
|
|
||||||
def read_possible_languages_aux(langdir):
|
def read_possible_languages_aux(langdir):
|
||||||
def get_lang_struct(lang, langcode, langname, langfile_mtime):
|
def get_lang_struct(lang, langcode, langname, langfile_mtime):
|
||||||
if lang == 'default':
|
if lang == 'default':
|
||||||
@@ -202,7 +217,8 @@ def read_possible_languages_aux(langdir):
|
|||||||
pluraldict_fname = None
|
pluraldict_fname = None
|
||||||
pluraldict_mtime = 0
|
pluraldict_mtime = 0
|
||||||
return (langcode, # language code from !langcode!
|
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
|
langfile_mtime, # m_time of language file
|
||||||
pluraldict_fname, # name of plural dictionary file or None (when default.py is not exist)
|
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
|
pluraldict_mtime, # m_time of plural dictionary file or 0 if file is not exist
|
||||||
@@ -244,11 +260,13 @@ def read_possible_languages_aux(langdir):
|
|||||||
|
|
||||||
return langs
|
return langs
|
||||||
|
|
||||||
|
|
||||||
def read_possible_languages(appdir):
|
def read_possible_languages(appdir):
|
||||||
langdir = pjoin(appdir, 'languages')
|
langdir = pjoin(appdir, 'languages')
|
||||||
return getcfs('langs:' + langdir, langdir,
|
return getcfs('langs:' + langdir, langdir,
|
||||||
lambda: read_possible_languages_aux(langdir))
|
lambda: read_possible_languages_aux(langdir))
|
||||||
|
|
||||||
|
|
||||||
def read_plural_dict_aux(filename):
|
def read_plural_dict_aux(filename):
|
||||||
lang_text = portalocker.read_locked(filename).replace('\r\n', '\n')
|
lang_text = portalocker.read_locked(filename).replace('\r\n', '\n')
|
||||||
try:
|
try:
|
||||||
@@ -258,10 +276,12 @@ def read_plural_dict_aux(filename):
|
|||||||
logging.error(status)
|
logging.error(status)
|
||||||
return {'__corrupted__': status}
|
return {'__corrupted__': status}
|
||||||
|
|
||||||
|
|
||||||
def read_plural_dict(filename):
|
def read_plural_dict(filename):
|
||||||
return getcfs('plurals:' + filename, filename,
|
return getcfs('plurals:' + filename, filename,
|
||||||
lambda: read_plural_dict_aux(filename))
|
lambda: read_plural_dict_aux(filename))
|
||||||
|
|
||||||
|
|
||||||
def write_plural_dict(filename, contents):
|
def write_plural_dict(filename, contents):
|
||||||
if '__corrupted__' in contents:
|
if '__corrupted__' in contents:
|
||||||
return
|
return
|
||||||
@@ -270,7 +290,8 @@ def write_plural_dict(filename, contents):
|
|||||||
fp.write('#!/usr/bin/env python\n{\n# "singular form (0)": ["first plural form (1)", "second plural form (2)", ...],\n')
|
fp.write('#!/usr/bin/env python\n{\n# "singular form (0)": ["first plural form (1)", "second plural form (2)", ...],\n')
|
||||||
# coding: utf8\n{\n')
|
# 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())):
|
||||||
forms = '['+','.join([repr(Utf8(form)) for form in contents[key]])+']'
|
forms = '[' + ','.join([repr(Utf8(form))
|
||||||
|
for form in contents[key]]) + ']'
|
||||||
fp.write('%s: %s,\n' % (repr(Utf8(key)), forms))
|
fp.write('%s: %s,\n' % (repr(Utf8(key)), forms))
|
||||||
fp.write('}\n')
|
fp.write('}\n')
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
@@ -297,7 +318,6 @@ def write_dict(filename, contents):
|
|||||||
fp.close()
|
fp.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class lazyT(object):
|
class lazyT(object):
|
||||||
"""
|
"""
|
||||||
never to be called explicitly, returned by
|
never to be called explicitly, returned by
|
||||||
@@ -370,7 +390,8 @@ class lazyT(object):
|
|||||||
return str(self)[i:j]
|
return str(self)[i:j]
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for c in str(self): yield c
|
for c in str(self):
|
||||||
|
yield c
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(str(self))
|
return len(str(self))
|
||||||
@@ -388,9 +409,11 @@ class lazyT(object):
|
|||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
def __mod__(self, symbols):
|
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)
|
return lazyT(self.m, symbols, self.T, self.f, self.t, self.M)
|
||||||
|
|
||||||
|
|
||||||
class translator(object):
|
class translator(object):
|
||||||
"""
|
"""
|
||||||
this class is instantiated by gluon.compileapp.build_environment
|
this class is instantiated by gluon.compileapp.build_environment
|
||||||
@@ -459,7 +482,8 @@ class translator(object):
|
|||||||
returns dictionary with all possible languages:
|
returns dictionary with all possible languages:
|
||||||
{ langcode(from filename):
|
{ langcode(from filename):
|
||||||
( langcode, # language code from !langcode!
|
( 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
|
langfile_mtime, # m_time of language file
|
||||||
pluraldict_fname,# name of plural dictionary file or None (when default.py is not exist)
|
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
|
pluraldict_mtime,# m_time of plural dictionary file or 0 if file is not exist
|
||||||
@@ -470,7 +494,8 @@ class translator(object):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
info = read_possible_languages(self.folder)
|
info = read_possible_languages(self.folder)
|
||||||
if lang: info = info.get(lang)
|
if lang:
|
||||||
|
info = info.get(lang)
|
||||||
return info
|
return info
|
||||||
|
|
||||||
def get_possible_languages(self):
|
def get_possible_languages(self):
|
||||||
@@ -505,7 +530,6 @@ class translator(object):
|
|||||||
self.current_languages = list(languages)
|
self.current_languages = list(languages)
|
||||||
self.force(self.http_accept_language)
|
self.force(self.http_accept_language)
|
||||||
|
|
||||||
|
|
||||||
def plural(self, word, n):
|
def plural(self, word, n):
|
||||||
""" get plural form of word for number *n*
|
""" get plural form of word for number *n*
|
||||||
NOTE: *word" MUST be defined in current language
|
NOTE: *word" MUST be defined in current language
|
||||||
@@ -558,6 +582,7 @@ class translator(object):
|
|||||||
of them matches possible_languages.
|
of them matches possible_languages.
|
||||||
"""
|
"""
|
||||||
pl_info = read_possible_languages(self.folder)
|
pl_info = read_possible_languages(self.folder)
|
||||||
|
|
||||||
def set_plural(language):
|
def set_plural(language):
|
||||||
"""
|
"""
|
||||||
initialize plural forms subsystem
|
initialize plural forms subsystem
|
||||||
@@ -716,7 +741,8 @@ class translator(object):
|
|||||||
prefix = prefix.encode('utf8')
|
prefix = prefix.encode('utf8')
|
||||||
key = prefix + message
|
key = prefix + message
|
||||||
mt = self.t.get(key, None)
|
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
|
# 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
|
# remove comments
|
||||||
@@ -764,10 +790,12 @@ class translator(object):
|
|||||||
(p1, sep, p2) = w[1:].partition("?")
|
(p1, sep, p2) = w[1:].partition("?")
|
||||||
part1 = p1 if sep else ""
|
part1 = p1 if sep else ""
|
||||||
(part2, sep, part3) = (p2 if sep else p1).partition("?")
|
(part2, sep, part3) = (p2 if sep else p1).partition("?")
|
||||||
if not sep: part3 = part2
|
if not sep:
|
||||||
|
part3 = part2
|
||||||
if i is None:
|
if i is None:
|
||||||
# ?[word]?number[?number] or ?number
|
# ?[word]?number[?number] or ?number
|
||||||
if not part2: return m.group(0)
|
if not part2:
|
||||||
|
return m.group(0)
|
||||||
num = int(part2)
|
num = int(part2)
|
||||||
else:
|
else:
|
||||||
# ?[word]?word2[?word3][number]
|
# ?[word]?word2[?word3][number]
|
||||||
@@ -802,7 +830,8 @@ class translator(object):
|
|||||||
(p1, sep, p2) = w[1:].partition("?")
|
(p1, sep, p2) = w[1:].partition("?")
|
||||||
part1 = p1 if sep else ""
|
part1 = p1 if sep else ""
|
||||||
(part2, sep, part3) = (p2 if sep else p1).partition("?")
|
(part2, sep, part3) = (p2 if sep else p1).partition("?")
|
||||||
if not sep: part3 = part2
|
if not sep:
|
||||||
|
part3 = part2
|
||||||
num = int(n)
|
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('!!!'):
|
elif w.startswith('!!!'):
|
||||||
@@ -849,6 +878,7 @@ class translator(object):
|
|||||||
message = self.params_substitution(message, symbols)
|
message = self.params_substitution(message, symbols)
|
||||||
return message.translate(ttab_out)
|
return message.translate(ttab_out)
|
||||||
|
|
||||||
|
|
||||||
def findT(path, language=DEFAULT_LANGUAGE):
|
def findT(path, language=DEFAULT_LANGUAGE):
|
||||||
"""
|
"""
|
||||||
must be run by the admin app
|
must be run by the admin app
|
||||||
@@ -888,8 +918,12 @@ def findT(path, language=DEFAULT_LANGUAGE):
|
|||||||
write_dict(lang_file, sentences)
|
write_dict(lang_file, sentences)
|
||||||
|
|
||||||
### important to allow safe session.flash=T(....)
|
### important to allow safe session.flash=T(....)
|
||||||
|
|
||||||
|
|
||||||
def lazyT_unpickle(data):
|
def lazyT_unpickle(data):
|
||||||
return marshal.loads(data)
|
return marshal.loads(data)
|
||||||
|
|
||||||
|
|
||||||
def lazyT_pickle(data):
|
def lazyT_pickle(data):
|
||||||
return lazyT_unpickle, (marshal.dumps(str(data)),)
|
return lazyT_unpickle, (marshal.dumps(str(data)),)
|
||||||
copy_reg.pickle(lazyT, lazyT_pickle, lazyT_unpickle)
|
copy_reg.pickle(lazyT, lazyT_pickle, lazyT_unpickle)
|
||||||
|
|||||||
+23
-14
@@ -107,7 +107,7 @@ try:
|
|||||||
version_info.close()
|
version_info.close()
|
||||||
global_settings.web2py_version = parse_version(raw_version_string)
|
global_settings.web2py_version = parse_version(raw_version_string)
|
||||||
except:
|
except:
|
||||||
raise RuntimeError, "Cannot determine web2py version"
|
raise RuntimeError("Cannot determine web2py version")
|
||||||
|
|
||||||
web2py_version = global_settings.web2py_version
|
web2py_version = global_settings.web2py_version
|
||||||
|
|
||||||
@@ -121,6 +121,7 @@ load()
|
|||||||
|
|
||||||
HTTPS_SCHEMES = set(('https', 'HTTPS'))
|
HTTPS_SCHEMES = set(('https', 'HTTPS'))
|
||||||
|
|
||||||
|
|
||||||
def get_client(env):
|
def get_client(env):
|
||||||
"""
|
"""
|
||||||
guess the client address from the environment variables
|
guess the client address from the environment variables
|
||||||
@@ -141,6 +142,7 @@ def get_client(env):
|
|||||||
raise HTTP(400, "Bad Request (request.client=%s)" % client)
|
raise HTTP(400, "Bad Request (request.client=%s)" % client)
|
||||||
return 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
|
copies request.env.wsgi_input into request.body
|
||||||
@@ -225,7 +227,8 @@ def serve_controller(request, response, session):
|
|||||||
# logic to garbage collect after exec, not always, once every 100 requests
|
# logic to garbage collect after exec, not always, once every 100 requests
|
||||||
global requests
|
global requests
|
||||||
requests = ('requests' in globals()) and (requests + 1) % 100 or 0
|
requests = ('requests' in globals()) and (requests + 1) % 100 or 0
|
||||||
if not requests: gc.collect()
|
if not requests:
|
||||||
|
gc.collect()
|
||||||
# end garbage collection logic
|
# end garbage collection logic
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
@@ -234,7 +237,8 @@ def serve_controller(request, response, session):
|
|||||||
|
|
||||||
default_headers = [
|
default_headers = [
|
||||||
('Content-Type', contenttype('.' + request.extension)),
|
('Content-Type', contenttype('.' + request.extension)),
|
||||||
('Cache-Control','no-store, no-cache, must-revalidate, post-check=0, pre-check=0'),
|
('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',
|
('Expires', time.strftime('%a, %d %b %Y %H:%M:%S GMT',
|
||||||
time.gmtime())),
|
time.gmtime())),
|
||||||
('Pragma', 'no-cache')]
|
('Pragma', 'no-cache')]
|
||||||
@@ -276,18 +280,21 @@ def middleware_aux(request, response, *middleware_apps):
|
|||||||
return [data]
|
return [data]
|
||||||
for item in middleware_apps:
|
for item in middleware_apps:
|
||||||
app = item(app)
|
app = item(app)
|
||||||
|
|
||||||
def caller(app):
|
def caller(app):
|
||||||
wsgi = request.wsgi
|
wsgi = request.wsgi
|
||||||
return app(wsgi.environ, wsgi.start_response)
|
return app(wsgi.environ, wsgi.start_response)
|
||||||
return lambda caller=caller, app=app: caller(app)
|
return lambda caller=caller, app=app: caller(app)
|
||||||
return middleware
|
return middleware
|
||||||
|
|
||||||
|
|
||||||
def environ_aux(environ, request):
|
def environ_aux(environ, request):
|
||||||
new_environ = copy.copy(environ)
|
new_environ = copy.copy(environ)
|
||||||
new_environ['wsgi.input'] = request.body
|
new_environ['wsgi.input'] = request.body
|
||||||
new_environ['wsgi.version'] = 1
|
new_environ['wsgi.version'] = 1
|
||||||
return new_environ
|
return new_environ
|
||||||
|
|
||||||
|
|
||||||
def parse_get_post_vars(request, environ):
|
def parse_get_post_vars(request, environ):
|
||||||
|
|
||||||
# always parse variables in URL for GET, POST, PUT, DELETE, etc. in get_vars
|
# always parse variables in URL for GET, POST, PUT, DELETE, etc. in get_vars
|
||||||
@@ -322,7 +329,8 @@ def parse_get_post_vars(request, environ):
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
keys = []
|
keys = []
|
||||||
for key in 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]
|
dpk = dpost[key]
|
||||||
# if en element is not a file replace it with its value else leave it alone
|
# if en element is not a file replace it with its value else leave it alone
|
||||||
if isinstance(dpk, list):
|
if isinstance(dpk, list):
|
||||||
@@ -347,7 +355,8 @@ def parse_get_post_vars(request, environ):
|
|||||||
pvalue = pvalue[:-len(gvalue)]
|
pvalue = pvalue[:-len(gvalue)]
|
||||||
request.vars[key] = value
|
request.vars[key] = value
|
||||||
if len(pvalue):
|
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):
|
def wsgibase(environ, responder):
|
||||||
@@ -413,13 +422,14 @@ def wsgibase(environ, responder):
|
|||||||
= 'attachment'
|
= 'attachment'
|
||||||
if version:
|
if version:
|
||||||
response.headers['Cache-Control'] = 'max-age=315360000'
|
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)
|
response.stream(static_file, request=request)
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
# fill in request items
|
# 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:
|
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']
|
||||||
@@ -447,8 +457,8 @@ def wsgibase(environ, responder):
|
|||||||
ajax=x_req_with == 'xmlhttprequest',
|
ajax=x_req_with == 'xmlhttprequest',
|
||||||
cid=env.http_web2py_component_element,
|
cid=env.http_web2py_component_element,
|
||||||
is_local=env.remote_addr in local_hosts,
|
is_local=env.remote_addr in local_hosts,
|
||||||
is_https = env.wsgi_url_scheme in HTTPS_SCHEMES \
|
is_https=env.wsgi_url_scheme in HTTPS_SCHEMES
|
||||||
or request.env.http_x_forwarded_proto in HTTPS_SCHEMES \
|
or request.env.http_x_forwarded_proto in HTTPS_SCHEMES
|
||||||
or env.https == 'on')
|
or env.https == 'on')
|
||||||
request.uuid = request.compute_uuid() # requires client
|
request.uuid = request.compute_uuid() # requires client
|
||||||
request.url = environ['PATH_INFO']
|
request.url = environ['PATH_INFO']
|
||||||
@@ -468,7 +478,7 @@ def wsgibase(environ, responder):
|
|||||||
_handler['function'],
|
_handler['function'],
|
||||||
args=app))
|
args=app))
|
||||||
else:
|
else:
|
||||||
raise HTTP(404, rwthread.routes.error_message \
|
raise HTTP(404, rwthread.routes.error_message
|
||||||
% 'invalid request',
|
% 'invalid request',
|
||||||
web2py_error='invalid application')
|
web2py_error='invalid application')
|
||||||
elif not request.is_local and \
|
elif not request.is_local and \
|
||||||
@@ -594,7 +604,7 @@ def wsgibase(environ, responder):
|
|||||||
BaseAdapter.close_all_instances('rollback')
|
BaseAdapter.close_all_instances('rollback')
|
||||||
|
|
||||||
http_response = \
|
http_response = \
|
||||||
HTTP(500, rwthread.routes.error_message_ticket % \
|
HTTP(500, rwthread.routes.error_message_ticket %
|
||||||
dict(ticket=ticket),
|
dict(ticket=ticket),
|
||||||
web2py_error='ticket %s' % ticket)
|
web2py_error='ticket %s' % ticket)
|
||||||
|
|
||||||
@@ -617,7 +627,7 @@ def wsgibase(environ, responder):
|
|||||||
e = RestrictedError('Framework', '', '', locals())
|
e = RestrictedError('Framework', '', '', locals())
|
||||||
ticket = e.log(request) or 'unrecoverable'
|
ticket = e.log(request) or 'unrecoverable'
|
||||||
http_response = \
|
http_response = \
|
||||||
HTTP(500, rwthread.routes.error_message_ticket \
|
HTTP(500, rwthread.routes.error_message_ticket
|
||||||
% dict(ticket=ticket),
|
% dict(ticket=ticket),
|
||||||
web2py_error='ticket %s' % ticket)
|
web2py_error='ticket %s' % ticket)
|
||||||
|
|
||||||
@@ -718,7 +728,7 @@ def appfactory(wsgiapp=wsgibase,
|
|||||||
stat.strip_dirs().sort_stats("time").print_stats(80)
|
stat.strip_dirs().sort_stats("time").print_stats(80)
|
||||||
profile_out = stat.stream.getvalue()
|
profile_out = stat.stream.getvalue()
|
||||||
profile_file = open(profilerfilename, 'a')
|
profile_file = open(profilerfilename, 'a')
|
||||||
profile_file.write('%s\n%s\n%s\n%s\n\n' % \
|
profile_file.write('%s\n%s\n%s\n%s\n\n' %
|
||||||
('=' * 60, environ['PATH_INFO'], '=' * 60, profile_out))
|
('=' * 60, environ['PATH_INFO'], '=' * 60, profile_out))
|
||||||
profile_file.close()
|
profile_file.close()
|
||||||
locker.release()
|
locker.release()
|
||||||
@@ -834,7 +844,6 @@ class HttpServer(object):
|
|||||||
handle_signals=False,
|
handle_signals=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""
|
"""
|
||||||
start the web server
|
start the web server
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ try:
|
|||||||
except:
|
except:
|
||||||
tkMessageBox = None
|
tkMessageBox = None
|
||||||
|
|
||||||
|
|
||||||
class MessageBoxHandler(logging.Handler):
|
class MessageBoxHandler(logging.Handler):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
logging.Handler.__init__(self)
|
logging.Handler.__init__(self)
|
||||||
|
|||||||
+4
-4
@@ -11,18 +11,18 @@ import re
|
|||||||
|
|
||||||
# pattern to find defined tables
|
# pattern to find defined tables
|
||||||
|
|
||||||
regex_tables = re.compile(\
|
regex_tables = re.compile(
|
||||||
"""^[\w]+\.define_table\(\s*[\'\"](?P<name>\w+)[\'\"]""",
|
"""^[\w]+\.define_table\(\s*[\'\"](?P<name>\w+)[\'\"]""",
|
||||||
flags=re.M)
|
flags=re.M)
|
||||||
|
|
||||||
# pattern to find exposed functions in controller
|
# 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*:',
|
'^def\s+(?P<name>(?:[a-zA-Z0-9]\w*)|(?:_[a-zA-Z0-9]\w*))\(\)\s*:',
|
||||||
flags=re.M)
|
flags=re.M)
|
||||||
|
|
||||||
regex_include = re.compile(\
|
regex_include = re.compile(
|
||||||
'(?P<all>\{\{\s*include\s+[\'"](?P<name>[^\'"]*)[\'"]\s*\}\})')
|
'(?P<all>\{\{\s*include\s+[\'"](?P<name>[^\'"]*)[\'"]\s*\}\})')
|
||||||
|
|
||||||
regex_extend = re.compile(\
|
regex_extend = re.compile(
|
||||||
'^\s*(?P<all>\{\{\s*extend\s+[\'"](?P<name>[^\'"]+)[\'"]\s*\}\})', re.MULTILINE)
|
'^\s*(?P<all>\{\{\s*extend\s+[\'"](?P<name>[^\'"]+)[\'"]\s*\}\})', re.MULTILINE)
|
||||||
+17
-8
@@ -23,6 +23,7 @@ from settings import global_settings
|
|||||||
logger = logging.getLogger("web2py.cron")
|
logger = logging.getLogger("web2py.cron")
|
||||||
_cron_stopping = False
|
_cron_stopping = False
|
||||||
|
|
||||||
|
|
||||||
def absolute_path_link(path):
|
def absolute_path_link(path):
|
||||||
"""
|
"""
|
||||||
Return an absolute path for the destination of a symlink
|
Return an absolute path for the destination of a symlink
|
||||||
@@ -36,11 +37,13 @@ def absolute_path_link(path):
|
|||||||
link = os.path.abspath(path)
|
link = os.path.abspath(path)
|
||||||
return link
|
return link
|
||||||
|
|
||||||
|
|
||||||
def stopcron():
|
def stopcron():
|
||||||
"graceful shutdown of cron"
|
"graceful shutdown of cron"
|
||||||
global _cron_stopping
|
global _cron_stopping
|
||||||
_cron_stopping = True
|
_cron_stopping = True
|
||||||
|
|
||||||
|
|
||||||
class extcron(threading.Thread):
|
class extcron(threading.Thread):
|
||||||
|
|
||||||
def __init__(self, applications_parent, apps=None):
|
def __init__(self, applications_parent, apps=None):
|
||||||
@@ -55,6 +58,7 @@ class extcron(threading.Thread):
|
|||||||
logger.debug('external cron invocation')
|
logger.debug('external cron invocation')
|
||||||
crondance(self.path, 'external', startup=False, apps=self.apps)
|
crondance(self.path, 'external', startup=False, apps=self.apps)
|
||||||
|
|
||||||
|
|
||||||
class hardcron(threading.Thread):
|
class hardcron(threading.Thread):
|
||||||
|
|
||||||
def __init__(self, applications_parent):
|
def __init__(self, applications_parent):
|
||||||
@@ -76,6 +80,7 @@ class hardcron(threading.Thread):
|
|||||||
s.enter(60 - now % 60, 1, self.launch, ())
|
s.enter(60 - now % 60, 1, self.launch, ())
|
||||||
s.run()
|
s.run()
|
||||||
|
|
||||||
|
|
||||||
class softcron(threading.Thread):
|
class softcron(threading.Thread):
|
||||||
|
|
||||||
def __init__(self, applications_parent):
|
def __init__(self, applications_parent):
|
||||||
@@ -88,6 +93,7 @@ class softcron(threading.Thread):
|
|||||||
logger.debug('soft cron invocation')
|
logger.debug('soft cron invocation')
|
||||||
crondance(self.path, 'soft', startup=False)
|
crondance(self.path, 'soft', startup=False)
|
||||||
|
|
||||||
|
|
||||||
class Token(object):
|
class Token(object):
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
@@ -195,7 +201,8 @@ def parsecronline(line):
|
|||||||
params = line.strip().split(None, 6)
|
params = line.strip().split(None, 6)
|
||||||
if len(params) < 7:
|
if len(params) < 7:
|
||||||
return None
|
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']):
|
for (s, id) in zip(params[:5], ['min', 'hr', 'dom', 'mon', 'dow']):
|
||||||
if not s in [None, '*']:
|
if not s in [None, '*']:
|
||||||
task[id] = []
|
task[id] = []
|
||||||
@@ -237,12 +244,13 @@ class cronlauncher(threading.Thread):
|
|||||||
(stdoutdata, stderrdata) = proc.communicate()
|
(stdoutdata, stderrdata) = proc.communicate()
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'WEB2PY CRON Call returned code %s:\n%s' % \
|
'WEB2PY CRON Call returned code %s:\n%s' %
|
||||||
(proc.returncode, stdoutdata + stderrdata))
|
(proc.returncode, stdoutdata + stderrdata))
|
||||||
else:
|
else:
|
||||||
logger.debug('WEB2PY CRON Call returned success:\n%s' \
|
logger.debug('WEB2PY CRON Call returned success:\n%s'
|
||||||
% stdoutdata)
|
% stdoutdata)
|
||||||
|
|
||||||
|
|
||||||
def crondance(applications_parent, ctype='soft', startup=False, apps=None):
|
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)
|
cron_path = os.path.join(applications_parent)
|
||||||
@@ -265,7 +273,7 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
|
|||||||
|
|
||||||
for app in apps:
|
for app in apps:
|
||||||
if _cron_stopping:
|
if _cron_stopping:
|
||||||
break;
|
break
|
||||||
apath = os.path.join(apppath, app)
|
apath = os.path.join(apppath, app)
|
||||||
|
|
||||||
# if app is a symbolic link to other app, skip it
|
# if app is a symbolic link to other app, skip it
|
||||||
@@ -281,7 +289,8 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
|
|||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
cronlines = fileutils.readlines_file(crontab, 'rt')
|
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]
|
tasks = [parsecronline(cline) for cline in lines]
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.error('WEB2PY CRON: crontab read error %s' % e)
|
logger.error('WEB2PY CRON: crontab read error %s' % e)
|
||||||
@@ -289,7 +298,7 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
|
|||||||
|
|
||||||
for task in tasks:
|
for task in tasks:
|
||||||
if _cron_stopping:
|
if _cron_stopping:
|
||||||
break;
|
break
|
||||||
commands = [sys.executable]
|
commands = [sys.executable]
|
||||||
w2p_path = fileutils.abspath('web2py.py', gluon=True)
|
w2p_path = fileutils.abspath('web2py.py', gluon=True)
|
||||||
if os.path.exists(w2p_path):
|
if os.path.exists(w2p_path):
|
||||||
@@ -304,7 +313,7 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
|
|||||||
continue
|
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
|
continue
|
||||||
logger.info('WEB2PY CRON (%s): %s executing %s in %s at %s' \
|
logger.info('WEB2PY CRON (%s): %s executing %s in %s at %s'
|
||||||
% (ctype, app, task.get('cmd'),
|
% (ctype, app, task.get('cmd'),
|
||||||
os.getcwd(), datetime.datetime.now()))
|
os.getcwd(), datetime.datetime.now()))
|
||||||
action, command, models = False, task['cmd'], ''
|
action, command, models = False, task['cmd'], ''
|
||||||
@@ -338,6 +347,6 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
|
|||||||
cronlauncher(commands, shell=shell).start()
|
cronlauncher(commands, shell=shell).start()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'WEB2PY CRON: Execution error for %s: %s' \
|
'WEB2PY CRON: Execution error for %s: %s'
|
||||||
% (task.get('cmd'), e))
|
% (task.get('cmd'), e))
|
||||||
token.release()
|
token.release()
|
||||||
@@ -124,31 +124,39 @@ class LockedFile(object):
|
|||||||
self.file.seek(0)
|
self.file.seek(0)
|
||||||
self.file.truncate()
|
self.file.truncate()
|
||||||
else:
|
else:
|
||||||
raise RuntimeError, "invalid LockedFile(...,mode)"
|
raise RuntimeError("invalid LockedFile(...,mode)")
|
||||||
|
|
||||||
def read(self, size=None):
|
def read(self, size=None):
|
||||||
return self.file.read() if size is None else self.file.read(size)
|
return self.file.read() if size is None else self.file.read(size)
|
||||||
|
|
||||||
def readline(self):
|
def readline(self):
|
||||||
return self.file.readline()
|
return self.file.readline()
|
||||||
|
|
||||||
def readlines(self):
|
def readlines(self):
|
||||||
return self.file.readlines()
|
return self.file.readlines()
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
self.file.write(data)
|
self.file.write(data)
|
||||||
self.file.flush()
|
self.file.flush()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if not self.file is None:
|
if not self.file is None:
|
||||||
unlock(self.file)
|
unlock(self.file)
|
||||||
self.file.close()
|
self.file.close()
|
||||||
self.file = None
|
self.file = None
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
if not self.file is None:
|
if not self.file is None:
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
def read_locked(filename):
|
def read_locked(filename):
|
||||||
fp = LockedFile(filename, 'r')
|
fp = LockedFile(filename, 'r')
|
||||||
data = fp.read()
|
data = fp.read()
|
||||||
fp.close()
|
fp.close()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def write_locked(filename, data):
|
def write_locked(filename, data):
|
||||||
fp = LockedFile(filename, 'w')
|
fp = LockedFile(filename, 'w')
|
||||||
data = fp.write(data)
|
data = fp.write(data)
|
||||||
|
|||||||
@@ -1710,4 +1710,5 @@ ADAPTERS = {
|
|||||||
'common': COMMON,
|
'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()))
|
||||||
+31
-14
@@ -22,6 +22,7 @@ logger = logging.getLogger("web2py")
|
|||||||
|
|
||||||
__all__ = ['RestrictedError', 'restricted', 'TicketStorage', 'compile2']
|
__all__ = ['RestrictedError', 'restricted', 'TicketStorage', 'compile2']
|
||||||
|
|
||||||
|
|
||||||
class TicketStorage(Storage):
|
class TicketStorage(Storage):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -64,7 +65,8 @@ class TicketStorage(Storage):
|
|||||||
root = request.folder
|
root = request.folder
|
||||||
if app:
|
if app:
|
||||||
root = os.path.join(os.path.join(root, '..'), 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)
|
return open(os.path.join(errors_folder, ticket_id), mode)
|
||||||
|
|
||||||
def _get_table(self, db, tablename, app):
|
def _get_table(self, db, tablename, app):
|
||||||
@@ -102,7 +104,6 @@ class TicketStorage(Storage):
|
|||||||
return cPickle.loads(rows[0].ticket_data) if rows else {}
|
return cPickle.loads(rows[0].ticket_data) if rows else {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RestrictedError(Exception):
|
class RestrictedError(Exception):
|
||||||
"""
|
"""
|
||||||
class used to wrap an exception that occurs in the restricted environment
|
class used to wrap an exception that occurs in the restricted environment
|
||||||
@@ -120,7 +121,8 @@ class RestrictedError(Exception):
|
|||||||
layer here is some description of where in the system the exception
|
layer here is some description of where in the system the exception
|
||||||
occurred.
|
occurred.
|
||||||
"""
|
"""
|
||||||
if environment is None: environment = {}
|
if environment is None:
|
||||||
|
environment = {}
|
||||||
self.layer = layer
|
self.layer = layer
|
||||||
self.code = code
|
self.code = code
|
||||||
self.output = output
|
self.output = output
|
||||||
@@ -159,7 +161,6 @@ class RestrictedError(Exception):
|
|||||||
logger.error(self.traceback)
|
logger.error(self.traceback)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def load(self, request, app, ticket_id):
|
def load(self, request, app, ticket_id):
|
||||||
"""
|
"""
|
||||||
loads a logged exception.
|
loads a logged exception.
|
||||||
@@ -192,17 +193,19 @@ def compile2(code,layer):
|
|||||||
"""
|
"""
|
||||||
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'):
|
def restricted(code, environment=None, layer='Unknown'):
|
||||||
"""
|
"""
|
||||||
runs code in environment and returns the output. if an exception occurs
|
runs code in environment and returns the output. if an exception occurs
|
||||||
in code it raises a RestrictedError containing the traceback. layer is
|
in code it raises a RestrictedError containing the traceback. layer is
|
||||||
passed to RestrictedError to identify where the error occurred.
|
passed to RestrictedError to identify where the error occurred.
|
||||||
"""
|
"""
|
||||||
if environment is None: environment = {}
|
if environment is None:
|
||||||
|
environment = {}
|
||||||
environment['__file__'] = layer
|
environment['__file__'] = layer
|
||||||
environment['__name__'] = '__restricted__'
|
environment['__name__'] = '__restricted__'
|
||||||
try:
|
try:
|
||||||
if type(code) == types.CodeType:
|
if isinstance(code, types.CodeType):
|
||||||
ccode = code
|
ccode = code
|
||||||
else:
|
else:
|
||||||
ccode = compile2(code, layer)
|
ccode = compile2(code, layer)
|
||||||
@@ -221,14 +224,21 @@ def restricted(code, environment=None, layer='Unknown'):
|
|||||||
output = "%s %s" % (etype, evalue)
|
output = "%s %s" % (etype, evalue)
|
||||||
raise RestrictedError(layer, code, output, environment)
|
raise RestrictedError(layer, code, output, environment)
|
||||||
|
|
||||||
|
|
||||||
def snapshot(info=None, context=5, code=None, environment=None):
|
def snapshot(info=None, context=5, code=None, environment=None):
|
||||||
"""Return a dict describing a given traceback (based on cgitb.text)."""
|
"""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:
|
# if no exception info given, get current:
|
||||||
etype, evalue, etb = info or sys.exc_info()
|
etype, evalue, etb = info or sys.exc_info()
|
||||||
|
|
||||||
if type(etype) is types.ClassType:
|
if isinstance(etype, types.ClassType):
|
||||||
etype = etype.__name__
|
etype = etype.__name__
|
||||||
|
|
||||||
# create a snapshot dict with some basic information
|
# create a snapshot dict with some basic information
|
||||||
@@ -248,13 +258,17 @@ def snapshot(info=None, context=5, code=None, environment=None):
|
|||||||
formatvalue=lambda value: '=' + pydoc.text.repr(value))
|
formatvalue=lambda value: '=' + pydoc.text.repr(value))
|
||||||
|
|
||||||
# basic frame information
|
# basic frame information
|
||||||
f = {'file': file, 'func': func, 'call': call, 'lines': {}, 'lnum': lnum}
|
f = {'file': file, 'func': func, 'call': call, 'lines': {},
|
||||||
|
'lnum': lnum}
|
||||||
|
|
||||||
highlight = {}
|
highlight = {}
|
||||||
|
|
||||||
def reader(lnum=[lnum]):
|
def reader(lnum=[lnum]):
|
||||||
highlight[lnum[0]] = 1
|
highlight[lnum[0]] = 1
|
||||||
try: return linecache.getline(file, lnum[0])
|
try:
|
||||||
finally: lnum[0] += 1
|
return linecache.getline(file, lnum[0])
|
||||||
|
finally:
|
||||||
|
lnum[0] += 1
|
||||||
vars = cgitb.scanvars(reader, frame, locals)
|
vars = cgitb.scanvars(reader, frame, locals)
|
||||||
|
|
||||||
# if it is a view, replace with generated code
|
# if it is a view, replace with generated code
|
||||||
@@ -273,10 +287,13 @@ def snapshot(info=None, context=5, code=None, environment=None):
|
|||||||
# dump local variables (referenced in current line only)
|
# dump local variables (referenced in current line only)
|
||||||
f['dump'] = {}
|
f['dump'] = {}
|
||||||
for name, where, value in vars:
|
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 value is not cgitb.__UNDEF__:
|
||||||
if where == 'global': name = 'global ' + name
|
if where == 'global':
|
||||||
elif where != 'local': name = where + name.split('.')[-1]
|
name = 'global ' + name
|
||||||
|
elif where != 'local':
|
||||||
|
name = where + name.split('.')[-1]
|
||||||
f['dump'][name] = pydoc.text.repr(value)
|
f['dump'][name] = pydoc.text.repr(value)
|
||||||
else:
|
else:
|
||||||
f['dump'][name] = 'undefined'
|
f['dump'][name] = 'undefined'
|
||||||
|
|||||||
+79
-34
@@ -38,7 +38,8 @@ THREAD_LOCAL = threading.local() # thread-local storage for routing params
|
|||||||
regex_at = re.compile(r'(?<!\\)\$[a-zA-Z]\w*')
|
regex_at = re.compile(r'(?<!\\)\$[a-zA-Z]\w*')
|
||||||
regex_anything = re.compile(r'(?<!\\)\$anything')
|
regex_anything = re.compile(r'(?<!\\)\$anything')
|
||||||
regex_redirect = re.compile(r'(\d+)->(.*)')
|
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]+)$')
|
regex_version = re.compile(r'^(_[\d]+\.[\d]+\.[\d]+)$')
|
||||||
# pattern to replace spaces with underscore in URL
|
# pattern to replace spaces with underscore in URL
|
||||||
# also the html escaped variants '+' and '%20' are covered
|
# also the html escaped variants '+' and '%20' are covered
|
||||||
@@ -79,6 +80,7 @@ def _router_default():
|
|||||||
)
|
)
|
||||||
return router
|
return router
|
||||||
|
|
||||||
|
|
||||||
def _params_default(app=None):
|
def _params_default(app=None):
|
||||||
"return new copy of default parameters"
|
"return new copy of default parameters"
|
||||||
p = Storage()
|
p = Storage()
|
||||||
@@ -104,6 +106,7 @@ params = _params_default(app=None) # regex rewrite parameters
|
|||||||
THREAD_LOCAL.routes = params # default to base regex rewrite parameters
|
THREAD_LOCAL.routes = params # default to base regex rewrite parameters
|
||||||
routers = None
|
routers = None
|
||||||
|
|
||||||
|
|
||||||
def log_rewrite(string):
|
def log_rewrite(string):
|
||||||
"Log rewrite activity under control of routes.py"
|
"Log rewrite activity under control of routes.py"
|
||||||
if params.logging == 'debug': # catch common cases first
|
if params.logging == 'debug': # catch common cases first
|
||||||
@@ -152,6 +155,7 @@ ROUTER_BASE_KEYS = set(
|
|||||||
# filter_err: helper for doctest & unittest
|
# filter_err: helper for doctest & unittest
|
||||||
# regex_filter_out: doctest
|
# regex_filter_out: doctest
|
||||||
|
|
||||||
|
|
||||||
def fixup_missing_path_info(environ):
|
def fixup_missing_path_info(environ):
|
||||||
eget = environ.get
|
eget = environ.get
|
||||||
path_info = eget('PATH_INFO')
|
path_info = eget('PATH_INFO')
|
||||||
@@ -179,6 +183,7 @@ def url_in(request, environ):
|
|||||||
return map_url_in(request, environ)
|
return map_url_in(request, environ)
|
||||||
return regex_url_in(request, environ)
|
return regex_url_in(request, environ)
|
||||||
|
|
||||||
|
|
||||||
def url_out(request, environ, application, controller, function,
|
def url_out(request, environ, application, controller, function,
|
||||||
args, other, scheme, host, port):
|
args, other, scheme, host, port):
|
||||||
"assemble and rewrite outgoing URL"
|
"assemble and rewrite outgoing URL"
|
||||||
@@ -193,7 +198,7 @@ def url_out(request, environ, application, controller, function,
|
|||||||
# fill in scheme and host if absolute URL is requested
|
# fill in scheme and host if absolute URL is requested
|
||||||
# scheme can be a string, eg 'http', 'https', 'ws', 'wss'
|
# 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
|
host = request.env.http_host
|
||||||
if not scheme or scheme is True:
|
if not scheme or scheme is True:
|
||||||
scheme = request.env.get('wsgi_url_scheme', 'http').lower() \
|
scheme = request.env.get('wsgi_url_scheme', 'http').lower() \
|
||||||
@@ -203,6 +208,7 @@ def url_out(request, environ, application, controller, function,
|
|||||||
url = '%s://%s%s' % (scheme, host_port, url)
|
url = '%s://%s%s' % (scheme, host_port, url)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
|
||||||
def try_rewrite_on_error(http_response, request, environ, ticket=None):
|
def try_rewrite_on_error(http_response, request, environ, ticket=None):
|
||||||
"""
|
"""
|
||||||
called from main.wsgibase to rewrite the http response.
|
called from main.wsgibase to rewrite the http response.
|
||||||
@@ -225,7 +231,8 @@ def try_rewrite_on_error(http_response, request, environ, ticket=None):
|
|||||||
path_info, query_string = uri, ''
|
path_info, query_string = uri, ''
|
||||||
query_string += \
|
query_string += \
|
||||||
'code=%s&ticket=%s&requested_uri=%s&request_url=%s' % \
|
'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://'):
|
if uri.startswith('http://') or uri.startswith('https://'):
|
||||||
# make up a response
|
# make up a response
|
||||||
url = path_info + '?' + query_string
|
url = path_info + '?' + query_string
|
||||||
@@ -234,7 +241,8 @@ def try_rewrite_on_error(http_response, request, environ, ticket=None):
|
|||||||
else:
|
else:
|
||||||
error_raising_path = environ['PATH_INFO']
|
error_raising_path = environ['PATH_INFO']
|
||||||
# Rewrite routes_onerror path.
|
# 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
|
environ['PATH_INFO'] = path_info
|
||||||
error_handling_path = \
|
error_handling_path = \
|
||||||
url_in(request, environ)[2]['PATH_INFO']
|
url_in(request, environ)[2]['PATH_INFO']
|
||||||
@@ -248,6 +256,7 @@ def try_rewrite_on_error(http_response, request, environ, ticket=None):
|
|||||||
# do nothing!
|
# do nothing!
|
||||||
return http_response, environ
|
return http_response, environ
|
||||||
|
|
||||||
|
|
||||||
def try_redirect_on_error(http_object, request, ticket=None):
|
def try_redirect_on_error(http_object, request, ticket=None):
|
||||||
"called from main.wsgibase to rewrite the http response"
|
"called from main.wsgibase to rewrite the http response"
|
||||||
status = int(str(http_object.status).split()[0])
|
status = int(str(http_object.status).split()[0])
|
||||||
@@ -365,7 +374,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
|
|||||||
if appname in routers:
|
if appname in routers:
|
||||||
for key in routers[appname].keys():
|
for key in routers[appname].keys():
|
||||||
if key in ROUTER_BASE_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])
|
router.update(routers[appname])
|
||||||
routers[appname] = router
|
routers[appname] = router
|
||||||
if exists(abspath('applications', appname, routes)):
|
if exists(abspath('applications', appname, routes)):
|
||||||
@@ -406,7 +415,7 @@ def compile_regex(k, v, env=None):
|
|||||||
if k.find('://') < 0:
|
if k.find('://') < 0:
|
||||||
i = k.find(':/')
|
i = k.find(':/')
|
||||||
if i < 0:
|
if i < 0:
|
||||||
raise SyntaxError, "routes pattern syntax error: path needs leading '/' [%s]" % k0
|
raise SyntaxError("routes pattern syntax error: path needs leading '/' [%s]" % k0)
|
||||||
k = r'%s:https?://[^:/]+:[a-z]+ %s' % (k[:i], k[i + 1:])
|
k = r'%s:https?://[^:/]+:[a-z]+ %s' % (k[:i], k[i + 1:])
|
||||||
# $anything -> ?P<anything>.*
|
# $anything -> ?P<anything>.*
|
||||||
for item in regex_anything.findall(k):
|
for item in regex_anything.findall(k):
|
||||||
@@ -419,6 +428,7 @@ def compile_regex(k, v, env=None):
|
|||||||
v = v.replace(item, r'\g<%s>' % item[1:])
|
v = v.replace(item, r'\g<%s>' % item[1:])
|
||||||
return (re.compile(k, re.DOTALL), v, env or {})
|
return (re.compile(k, re.DOTALL), v, env or {})
|
||||||
|
|
||||||
|
|
||||||
def load_routers(all_apps):
|
def load_routers(all_apps):
|
||||||
"load-time post-processing of routers"
|
"load-time post-processing of routers"
|
||||||
|
|
||||||
@@ -431,13 +441,15 @@ def load_routers(all_apps):
|
|||||||
if app != 'BASE':
|
if app != 'BASE':
|
||||||
keys = set(routers[app]).intersection(ROUTER_BASE_KEYS)
|
keys = set(routers[app]).intersection(ROUTER_BASE_KEYS)
|
||||||
if 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])
|
router.update(routers[app])
|
||||||
routers[app] = router
|
routers[app] = router
|
||||||
router = routers[app]
|
router = routers[app]
|
||||||
keys = set(router).difference(ROUTER_KEYS)
|
keys = set(router).difference(ROUTER_KEYS)
|
||||||
if 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:
|
if not router.controllers:
|
||||||
router.controllers = set()
|
router.controllers = set()
|
||||||
elif not isinstance(router.controllers, str):
|
elif not isinstance(router.controllers, str):
|
||||||
@@ -450,10 +462,12 @@ def load_routers(all_apps):
|
|||||||
if isinstance(router.functions, (set, tuple, list)):
|
if isinstance(router.functions, (set, tuple, list)):
|
||||||
functions = set(router.functions)
|
functions = set(router.functions)
|
||||||
if isinstance(router.default_function, str):
|
if isinstance(router.default_function, str):
|
||||||
functions.add(router.default_function) # legacy compatibility
|
functions.add(
|
||||||
|
router.default_function) # legacy compatibility
|
||||||
router.functions = {router.default_controller: functions}
|
router.functions = {router.default_controller: functions}
|
||||||
for controller in router.functions:
|
for controller in router.functions:
|
||||||
router.functions[controller] = set(router.functions[controller])
|
router.functions[controller] = set(
|
||||||
|
router.functions[controller])
|
||||||
else:
|
else:
|
||||||
router.functions = dict()
|
router.functions = dict()
|
||||||
if app != 'BASE':
|
if app != 'BASE':
|
||||||
@@ -516,10 +530,11 @@ def load_routers(all_apps):
|
|||||||
else:
|
else:
|
||||||
fcn = None
|
fcn = None
|
||||||
if app not in all_apps and app not in routers:
|
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)
|
domains[(domain, port)] = (app, ctlr, fcn)
|
||||||
routers.BASE.domains = domains
|
routers.BASE.domains = domains
|
||||||
|
|
||||||
|
|
||||||
def regex_uri(e, regexes, tag, default=None):
|
def regex_uri(e, regexes, tag, default=None):
|
||||||
"filter incoming URI against a list of regexes"
|
"filter incoming URI against a list of regexes"
|
||||||
path = e['PATH_INFO']
|
path = e['PATH_INFO']
|
||||||
@@ -540,6 +555,7 @@ def regex_uri(e, regexes, tag, default=None):
|
|||||||
log_rewrite('%s: [%s] -> %s (not rewritten)' % (tag, key, default))
|
log_rewrite('%s: [%s] -> %s (not rewritten)' % (tag, key, default))
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def regex_select(env=None, app=None, request=None):
|
def regex_select(env=None, app=None, request=None):
|
||||||
"""
|
"""
|
||||||
select a set of regex rewrite params for the current request
|
select a set of regex rewrite params for the current request
|
||||||
@@ -557,6 +573,7 @@ def regex_select(env=None, app=None, request=None):
|
|||||||
log_rewrite("select routing parameters: %s" % THREAD_LOCAL.routes.name)
|
log_rewrite("select routing parameters: %s" % THREAD_LOCAL.routes.name)
|
||||||
return app # for doctest
|
return app # for doctest
|
||||||
|
|
||||||
|
|
||||||
def regex_filter_in(e):
|
def regex_filter_in(e):
|
||||||
"regex rewrite incoming URL"
|
"regex rewrite incoming URL"
|
||||||
routes = THREAD_LOCAL.routes
|
routes = THREAD_LOCAL.routes
|
||||||
@@ -583,6 +600,7 @@ def regex_filter_in(e):
|
|||||||
def sluggify(key):
|
def sluggify(key):
|
||||||
return key.lower().replace('.', '_')
|
return key.lower().replace('.', '_')
|
||||||
|
|
||||||
|
|
||||||
def regex_url_in(request, environ):
|
def regex_url_in(request, environ):
|
||||||
"rewrite and parse incoming URL"
|
"rewrite and parse incoming URL"
|
||||||
|
|
||||||
@@ -596,7 +614,8 @@ def regex_url_in(request, environ):
|
|||||||
routes = THREAD_LOCAL.routes
|
routes = THREAD_LOCAL.routes
|
||||||
if routes.routes_in:
|
if routes.routes_in:
|
||||||
environ = regex_filter_in(environ)
|
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
|
# serve if a static file
|
||||||
@@ -604,7 +623,8 @@ def regex_url_in(request, environ):
|
|||||||
|
|
||||||
path = request.env.path_info.replace('\\', '/') or '/'
|
path = request.env.path_info.replace('\\', '/') or '/'
|
||||||
path = regex_space.sub('_', path)
|
path = regex_space.sub('_', path)
|
||||||
if path.endswith('/') and len(path)>1: path = path[:-1]
|
if path.endswith('/') and len(path) > 1:
|
||||||
|
path = path[:-1]
|
||||||
match = regex_url.match(path)
|
match = regex_url.match(path)
|
||||||
if not match:
|
if not match:
|
||||||
raise HTTP(400,
|
raise HTTP(400,
|
||||||
@@ -717,7 +737,8 @@ def filter_url(url, method='get', remote='0.0.0.0',
|
|||||||
# rewrite outbound URL
|
# rewrite outbound URL
|
||||||
#
|
#
|
||||||
if out:
|
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('/')
|
items = path_info.lstrip('/').split('/')
|
||||||
if items[-1] == '':
|
if items[-1] == '':
|
||||||
items.pop() # adjust trailing empty args
|
items.pop() # adjust trailing empty args
|
||||||
@@ -727,7 +748,8 @@ def filter_url(url, method='get', remote='0.0.0.0',
|
|||||||
f = items.pop(0)
|
f = items.pop(0)
|
||||||
if not routers:
|
if not routers:
|
||||||
return regex_filter_out(uri, e)
|
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:
|
if items:
|
||||||
url = '%s/%s' % (acf, '/'.join(items))
|
url = '%s/%s' % (acf, '/'.join(items))
|
||||||
if items[-1] == '':
|
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)
|
(static, version, e) = url_in(request, e)
|
||||||
if static:
|
if static:
|
||||||
return 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':
|
if request.extension and request.extension != 'html':
|
||||||
result += ".%s" % request.extension
|
result += ".%s" % request.extension
|
||||||
if request.args:
|
if request.args:
|
||||||
@@ -778,6 +801,8 @@ def filter_err(status, application='app', ticket='tkt'):
|
|||||||
|
|
||||||
# router support
|
# router support
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class MapUrlIn(object):
|
class MapUrlIn(object):
|
||||||
"logic for mapping incoming URLs"
|
"logic for mapping incoming URLs"
|
||||||
|
|
||||||
@@ -804,7 +829,8 @@ class MapUrlIn(object):
|
|||||||
self.query = self.env.get('QUERY_STRING', None)
|
self.query = self.env.get('QUERY_STRING', None)
|
||||||
path = path.lstrip('/')
|
path = path.lstrip('/')
|
||||||
self.env['PATH_INFO'] = '/' + path
|
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
|
# to handle empty args, strip exactly one trailing slash, if present
|
||||||
# .../arg1// represents one trailing empty arg
|
# .../arg1// represents one trailing empty arg
|
||||||
@@ -819,7 +845,8 @@ class MapUrlIn(object):
|
|||||||
self.method = self.env.get('REQUEST_METHOD', 'get').lower()
|
self.method = self.env.get('REQUEST_METHOD', 'get').lower()
|
||||||
(self.host, self.port) = (self.env.get('HTTP_HOST'), None)
|
(self.host, self.port) = (self.env.get('HTTP_HOST'), None)
|
||||||
if not self.host:
|
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:
|
if not self.host:
|
||||||
(self.host, self.port) = ('localhost', '80')
|
(self.host, self.port) = ('localhost', '80')
|
||||||
if ':' in self.host:
|
if ':' in self.host:
|
||||||
@@ -851,12 +878,14 @@ class MapUrlIn(object):
|
|||||||
elif not base.exclusive_domain and arg0 and not base.applications:
|
elif not base.exclusive_domain and arg0 and not base.applications:
|
||||||
self.application = arg0
|
self.application = arg0
|
||||||
elif (self.host, self.port) in base.domains:
|
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_application'] = self.application
|
||||||
self.env['domain_controller'] = self.domain_controller
|
self.env['domain_controller'] = self.domain_controller
|
||||||
self.env['domain_function'] = self.domain_function
|
self.env['domain_function'] = self.domain_function
|
||||||
elif (self.host, None) in base.domains:
|
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_application'] = self.application
|
||||||
self.env['domain_controller'] = self.domain_controller
|
self.env['domain_controller'] = self.domain_controller
|
||||||
self.env['domain_function'] = self.domain_function
|
self.env['domain_function'] = self.domain_function
|
||||||
@@ -869,12 +898,14 @@ class MapUrlIn(object):
|
|||||||
self.pop_arg_if(self.application == arg0)
|
self.pop_arg_if(self.application == arg0)
|
||||||
|
|
||||||
if not base._acfe_match.match(self.application):
|
if not base._acfe_match.match(self.application):
|
||||||
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
raise HTTP(
|
||||||
|
400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
web2py_error="invalid application: '%s'" % self.application)
|
web2py_error="invalid application: '%s'" % self.application)
|
||||||
|
|
||||||
if self.application not in routers and \
|
if self.application not in routers and \
|
||||||
(self.application != THREAD_LOCAL.routes.default_application or self.application == 'welcome'):
|
(self.application != THREAD_LOCAL.routes.default_application or self.application == 'welcome'):
|
||||||
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
raise HTTP(
|
||||||
|
400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
web2py_error="unknown application: '%s'" % self.application)
|
web2py_error="unknown application: '%s'" % self.application)
|
||||||
|
|
||||||
# set the application router
|
# set the application router
|
||||||
@@ -938,7 +969,8 @@ class MapUrlIn(object):
|
|||||||
self.pop_arg_if(arg0 == self.controller)
|
self.pop_arg_if(arg0 == self.controller)
|
||||||
log_rewrite("route: controller=%s" % self.controller)
|
log_rewrite("route: controller=%s" % self.controller)
|
||||||
if not self.router._acfe_match.match(self.controller):
|
if not self.router._acfe_match.match(self.controller):
|
||||||
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
raise HTTP(
|
||||||
|
400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
web2py_error='invalid controller')
|
web2py_error='invalid controller')
|
||||||
|
|
||||||
def map_static(self):
|
def map_static(self):
|
||||||
@@ -962,7 +994,8 @@ class MapUrlIn(object):
|
|||||||
# match path elements
|
# match path elements
|
||||||
bad_static = False
|
bad_static = False
|
||||||
for name in self.args:
|
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:
|
if bad_static:
|
||||||
log_rewrite('bad static path=%s' % file)
|
log_rewrite('bad static path=%s' % file)
|
||||||
raise HTTP(400,
|
raise HTTP(400,
|
||||||
@@ -990,7 +1023,8 @@ class MapUrlIn(object):
|
|||||||
arg0 = self.harg0 # map hyphens
|
arg0 = self.harg0 # map hyphens
|
||||||
functions = self.functions.get(self.controller, set())
|
functions = self.functions.get(self.controller, set())
|
||||||
if isinstance(self.router.default_function, dict):
|
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:
|
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
|
default_function = self.domain_function or default_function
|
||||||
@@ -1005,13 +1039,16 @@ class MapUrlIn(object):
|
|||||||
else:
|
else:
|
||||||
self.function = arg0
|
self.function = arg0
|
||||||
self.pop_arg_if(True)
|
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):
|
if not self.router._acfe_match.match(self.function):
|
||||||
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
raise HTTP(
|
||||||
|
400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
web2py_error='invalid function')
|
web2py_error='invalid function')
|
||||||
if self.extension and not self.router._acfe_match.match(self.extension):
|
if self.extension and not self.router._acfe_match.match(self.extension):
|
||||||
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
raise HTTP(
|
||||||
|
400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
web2py_error='invalid extension')
|
web2py_error='invalid extension')
|
||||||
|
|
||||||
def validate_args(self):
|
def validate_args(self):
|
||||||
@@ -1020,7 +1057,8 @@ class MapUrlIn(object):
|
|||||||
'''
|
'''
|
||||||
for arg in self.args:
|
for arg in self.args:
|
||||||
if not self.router._args_match.match(arg):
|
if not self.router._args_match.match(arg):
|
||||||
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
raise HTTP(
|
||||||
|
400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
web2py_error='invalid arg <%s>' % arg)
|
web2py_error='invalid arg <%s>' % arg)
|
||||||
|
|
||||||
def sluggify(self):
|
def sluggify(self):
|
||||||
@@ -1053,7 +1091,8 @@ class MapUrlIn(object):
|
|||||||
uri = '/%s%s%s%s' % (
|
uri = '/%s%s%s%s' % (
|
||||||
app,
|
app,
|
||||||
uri,
|
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.query) if self.query else '')
|
||||||
self.env['REQUEST_URI'] = uri
|
self.env['REQUEST_URI'] = uri
|
||||||
self.sluggify()
|
self.sluggify()
|
||||||
@@ -1075,6 +1114,7 @@ class MapUrlIn(object):
|
|||||||
if dopop:
|
if dopop:
|
||||||
self.args.pop(0)
|
self.args.pop(0)
|
||||||
|
|
||||||
|
|
||||||
class MapUrlOut(object):
|
class MapUrlOut(object):
|
||||||
"logic for mapping outgoing URLs"
|
"logic for mapping outgoing URLs"
|
||||||
|
|
||||||
@@ -1090,7 +1130,8 @@ class MapUrlOut(object):
|
|||||||
self.env = env
|
self.env = env
|
||||||
self.application = application
|
self.application = application
|
||||||
self.controller = controller
|
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.function = function
|
||||||
self.args = args
|
self.args = args
|
||||||
self.other = other
|
self.other = other
|
||||||
@@ -1111,12 +1152,13 @@ class MapUrlOut(object):
|
|||||||
self.domain_application = request and self.request.env.domain_application
|
self.domain_application = request and self.request.env.domain_application
|
||||||
self.domain_controller = request and self.request.env.domain_controller
|
self.domain_controller = request and self.request.env.domain_controller
|
||||||
if isinstance(self.router.default_function, dict):
|
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:
|
else:
|
||||||
self.default_function = self.router.default_function
|
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):
|
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
|
lang = request and request.uri_language
|
||||||
if lang and self.languages and lang in self.languages:
|
if lang and self.languages and lang in self.languages:
|
||||||
@@ -1259,7 +1301,8 @@ def map_url_in(request, env, app=False):
|
|||||||
if app:
|
if app:
|
||||||
return map.application
|
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:
|
if root_static_file:
|
||||||
map.update_request()
|
map.update_request()
|
||||||
return (root_static_file, version, map.env)
|
return (root_static_file, version, map.env)
|
||||||
@@ -1280,6 +1323,7 @@ def map_url_in(request, env, app=False):
|
|||||||
map.update_request()
|
map.update_request()
|
||||||
return (None, None, map.env)
|
return (None, None, map.env)
|
||||||
|
|
||||||
|
|
||||||
def map_url_out(request, env, application, controller,
|
def map_url_out(request, env, application, controller,
|
||||||
function, args, other, scheme, host, port):
|
function, args, other, scheme, host, port):
|
||||||
'''
|
'''
|
||||||
@@ -1311,6 +1355,7 @@ def map_url_out(request, env, application, controller,
|
|||||||
function, args, other, scheme, host, port)
|
function, args, other, scheme, host, port)
|
||||||
return map.acf()
|
return map.acf()
|
||||||
|
|
||||||
|
|
||||||
def get_effective_router(appname):
|
def get_effective_router(appname):
|
||||||
"return a private copy of the effective router for the specified application"
|
"return a private copy of the effective router for the specified application"
|
||||||
if not routers or appname not in routers:
|
if not routers or appname not in routers:
|
||||||
|
|||||||
+52
-23
@@ -16,7 +16,8 @@ import traceback
|
|||||||
VERSION = '1.2.5'
|
VERSION = '1.2.5'
|
||||||
SERVER_NAME = socket.gethostname()
|
SERVER_NAME = socket.gethostname()
|
||||||
SERVER_SOFTWARE = 'Rocket %s' % VERSION
|
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
|
BUF_SIZE = 16384
|
||||||
SOCKET_TIMEOUT = 10 # in secs
|
SOCKET_TIMEOUT = 10 # in secs
|
||||||
THREAD_STOP_CHECK_INTERVAL = 1 # in secs, How often should threads check for a server stop message?
|
THREAD_STOP_CHECK_INTERVAL = 1 # in secs, How often should threads check for a server stop message?
|
||||||
@@ -31,6 +32,7 @@ DEFAULTS = dict(LISTEN_QUEUE_SIZE = DEFAULT_LISTEN_QUEUE_SIZE,
|
|||||||
|
|
||||||
PY3K = sys.version_info[0] > 2
|
PY3K = sys.version_info[0] > 2
|
||||||
|
|
||||||
|
|
||||||
class NullHandler(logging.Handler):
|
class NullHandler(logging.Handler):
|
||||||
"A Logging handler to prevent library errors."
|
"A Logging handler to prevent library errors."
|
||||||
def emit(self, record):
|
def emit(self, record):
|
||||||
@@ -94,6 +96,7 @@ except ImportError:
|
|||||||
# TODO - This part is still very experimental.
|
# TODO - This part is still very experimental.
|
||||||
#from .filelike import FileLikeSocket
|
#from .filelike import FileLikeSocket
|
||||||
|
|
||||||
|
|
||||||
class Connection(object):
|
class Connection(object):
|
||||||
__slots__ = [
|
__slots__ = [
|
||||||
'setblocking',
|
'setblocking',
|
||||||
@@ -170,6 +173,7 @@ except ImportError:
|
|||||||
# Import Package Modules
|
# Import Package Modules
|
||||||
# package imports removed in monolithic build
|
# package imports removed in monolithic build
|
||||||
|
|
||||||
|
|
||||||
class FileLikeSocket(object):
|
class FileLikeSocket(object):
|
||||||
def __init__(self, conn, buf_size=BUF_SIZE):
|
def __init__(self, conn, buf_size=BUF_SIZE):
|
||||||
self.conn = conn
|
self.conn = conn
|
||||||
@@ -317,12 +321,11 @@ class WSGIFuture(Future):
|
|||||||
else:
|
else:
|
||||||
return super(WSGIFuture, self).set_running_or_notify_cancel()
|
return super(WSGIFuture, self).set_running_or_notify_cancel()
|
||||||
|
|
||||||
|
|
||||||
def remember(self, name, lifespan=None):
|
def remember(self, name, lifespan=None):
|
||||||
self._lifespan = lifespan or self._lifespan
|
self._lifespan = lifespan or self._lifespan
|
||||||
|
|
||||||
if name in self._mem_dict:
|
if name in self._mem_dict:
|
||||||
raise NameError('Cannot remember future by name "%s". ' % name + \
|
raise NameError('Cannot remember future by name "%s". ' % name +
|
||||||
'A future already exists with that name.')
|
'A future already exists with that name.')
|
||||||
self._name = name
|
self._name = name
|
||||||
self._mem_dict[name] = self
|
self._mem_dict[name] = self
|
||||||
@@ -334,6 +337,7 @@ class WSGIFuture(Future):
|
|||||||
del self._mem_dict[self._name]
|
del self._mem_dict[self._name]
|
||||||
self._name = None
|
self._name = None
|
||||||
|
|
||||||
|
|
||||||
class _WorkItem(object):
|
class _WorkItem(object):
|
||||||
def __init__(self, future, fn, args, kwargs):
|
def __init__(self, future, fn, args, kwargs):
|
||||||
self.future = future
|
self.future = future
|
||||||
@@ -353,6 +357,7 @@ class _WorkItem(object):
|
|||||||
else:
|
else:
|
||||||
self.future.set_result(result)
|
self.future.set_result(result)
|
||||||
|
|
||||||
|
|
||||||
class WSGIExecutor(ThreadPoolExecutor):
|
class WSGIExecutor(ThreadPoolExecutor):
|
||||||
multithread = True
|
multithread = True
|
||||||
multiprocess = False
|
multiprocess = False
|
||||||
@@ -366,7 +371,8 @@ class WSGIExecutor(ThreadPoolExecutor):
|
|||||||
if self._shutdown_lock.acquire():
|
if self._shutdown_lock.acquire():
|
||||||
if self._shutdown:
|
if self._shutdown:
|
||||||
self._shutdown_lock.release()
|
self._shutdown_lock.release()
|
||||||
raise RuntimeError('Cannot schedule new futures after shutdown')
|
raise RuntimeError(
|
||||||
|
'Cannot schedule new futures after shutdown')
|
||||||
|
|
||||||
f = WSGIFuture(self.futures)
|
f = WSGIFuture(self.futures)
|
||||||
w = _WorkItem(f, fn, args, kwargs)
|
w = _WorkItem(f, fn, args, kwargs)
|
||||||
@@ -378,6 +384,7 @@ class WSGIExecutor(ThreadPoolExecutor):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class FuturesMiddleware(object):
|
class FuturesMiddleware(object):
|
||||||
"Futures middleware that adds a Futures Executor to the environment"
|
"Futures middleware that adds a Futures Executor to the environment"
|
||||||
def __init__(self, app, threads=5):
|
def __init__(self, app, threads=5):
|
||||||
@@ -405,11 +412,13 @@ try:
|
|||||||
has_ssl = True
|
has_ssl = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
has_ssl = False
|
has_ssl = False
|
||||||
|
|
||||||
class SSLError(socket.error):
|
class SSLError(socket.error):
|
||||||
pass
|
pass
|
||||||
# Import Package Modules
|
# Import Package Modules
|
||||||
# package imports removed in monolithic build
|
# package imports removed in monolithic build
|
||||||
|
|
||||||
|
|
||||||
class Listener(Thread):
|
class Listener(Thread):
|
||||||
"""The Listener class is a class responsible for accepting connections
|
"""The Listener class is a class responsible for accepting connections
|
||||||
and queuing them to be processed by a worker thread."""
|
and queuing them to be processed by a worker thread."""
|
||||||
@@ -598,13 +607,11 @@ except ImportError:
|
|||||||
# package imports removed in monolithic build
|
# package imports removed in monolithic build
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Setup Logging
|
# Setup Logging
|
||||||
log = logging.getLogger('Rocket')
|
log = logging.getLogger('Rocket')
|
||||||
log.addHandler(NullHandler())
|
log.addHandler(NullHandler())
|
||||||
|
|
||||||
|
|
||||||
class Rocket(object):
|
class Rocket(object):
|
||||||
"""The Rocket class is responsible for handling threads and accepting and
|
"""The Rocket class is responsible for handling threads and accepting and
|
||||||
dispatching connections."""
|
dispatching connections."""
|
||||||
@@ -657,7 +664,8 @@ class Rocket(object):
|
|||||||
max_threads=max_threads)
|
max_threads=max_threads)
|
||||||
|
|
||||||
# Build our socket listeners
|
# Build our socket listeners
|
||||||
self.listeners = [Listener(i, queue_size, self.active_queue) for i in self.interfaces]
|
self.listeners = [Listener(
|
||||||
|
i, queue_size, self.active_queue) for i in self.interfaces]
|
||||||
for ndx in range(len(self.listeners) - 1, 0, -1):
|
for ndx in range(len(self.listeners) - 1, 0, -1):
|
||||||
if not self.listeners[ndx].ready:
|
if not self.listeners[ndx].ready:
|
||||||
del self.listeners[ndx]
|
del self.listeners[ndx]
|
||||||
@@ -705,7 +713,8 @@ class Rocket(object):
|
|||||||
str_extract = lambda l: (l.addr, l.port, l.secure and '*' or '')
|
str_extract = lambda l: (l.addr, l.port, l.secure and '*' or '')
|
||||||
|
|
||||||
msg = 'Listening on sockets: '
|
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)
|
log.info(msg)
|
||||||
|
|
||||||
for l in self.listeners:
|
for l in self.listeners:
|
||||||
@@ -773,6 +782,7 @@ class Rocket(object):
|
|||||||
self.stop()
|
self.stop()
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
|
|
||||||
def CherryPyWSGIServer(bind_addr,
|
def CherryPyWSGIServer(bind_addr,
|
||||||
wsgi_app,
|
wsgi_app,
|
||||||
numthreads=10,
|
numthreads=10,
|
||||||
@@ -803,6 +813,7 @@ from threading import Thread
|
|||||||
# Import Package Modules
|
# Import Package Modules
|
||||||
# package imports removed in monolithic build
|
# package imports removed in monolithic build
|
||||||
|
|
||||||
|
|
||||||
class Monitor(Thread):
|
class Monitor(Thread):
|
||||||
# Monitor worker class.
|
# Monitor worker class.
|
||||||
|
|
||||||
@@ -926,8 +937,10 @@ class Monitor(Thread):
|
|||||||
for c in stale:
|
for c in stale:
|
||||||
if __debug__:
|
if __debug__:
|
||||||
# "EXPR and A or B" kept for Py2.4 compatibility
|
# "EXPR and A or B" kept for Py2.4 compatibility
|
||||||
data = (c.client_addr, c.server_port, c.ssl and '*' or '')
|
data = (
|
||||||
self.log.debug('Flushing stale connection: %s:%i%s' % 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)
|
self.connections.remove(c)
|
||||||
list_changed = True
|
list_changed = True
|
||||||
@@ -940,7 +953,6 @@ class Monitor(Thread):
|
|||||||
# Dynamically resize the threadpool to adapt to our changing needs.
|
# Dynamically resize the threadpool to adapt to our changing needs.
|
||||||
self._threadpool.dynamic_resize()
|
self._threadpool.dynamic_resize()
|
||||||
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.active = False
|
self.active = False
|
||||||
|
|
||||||
@@ -984,6 +996,7 @@ import logging
|
|||||||
log = logging.getLogger('Rocket.Errors.ThreadPool')
|
log = logging.getLogger('Rocket.Errors.ThreadPool')
|
||||||
log.addHandler(NullHandler())
|
log.addHandler(NullHandler())
|
||||||
|
|
||||||
|
|
||||||
class ThreadPool:
|
class ThreadPool:
|
||||||
"""The ThreadPool class is a container class for all the worker threads. It
|
"""The ThreadPool class is a container class for all the worker threads. It
|
||||||
manages the number of actively running threads."""
|
manages the number of actively running threads."""
|
||||||
@@ -1193,7 +1206,9 @@ Content-Type: %s
|
|||||||
%s
|
%s
|
||||||
'''
|
'''
|
||||||
if IS_JYTHON:
|
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):
|
class Worker(Thread):
|
||||||
"""The Worker class is a base class responsible for receiving connections
|
"""The Worker class is a base class responsible for receiving connections
|
||||||
@@ -1315,7 +1330,8 @@ class Worker(Thread):
|
|||||||
else:
|
else:
|
||||||
if self.request_line:
|
if self.request_line:
|
||||||
log_info = dict(client_ip=conn.client_addr,
|
log_info = dict(client_ip=conn.client_addr,
|
||||||
time = datetime.now().strftime('%c'),
|
time=datetime.now(
|
||||||
|
).strftime('%c'),
|
||||||
status=self.status.split(' ')[0],
|
status=self.status.split(' ')[0],
|
||||||
size=self.size,
|
size=self.size,
|
||||||
request_line=self.request_line + ' - not stopping')
|
request_line=self.request_line + ' - not stopping')
|
||||||
@@ -1382,13 +1398,15 @@ class Worker(Thread):
|
|||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
raise SocketTimeout("Socket timed out before request.")
|
raise SocketTimeout("Socket timed out before request.")
|
||||||
except TypeError:
|
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()
|
d = d.strip()
|
||||||
|
|
||||||
if not d:
|
if not d:
|
||||||
if __debug__:
|
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.')
|
raise SocketClosed('Client closed socket.')
|
||||||
|
|
||||||
self.request_line = d
|
self.request_line = d
|
||||||
@@ -1412,7 +1430,8 @@ class Worker(Thread):
|
|||||||
if not v:
|
if not v:
|
||||||
req[k] = ""
|
req[k] = ""
|
||||||
if k == 'path':
|
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
|
return req
|
||||||
|
|
||||||
@@ -1454,7 +1473,6 @@ class Worker(Thread):
|
|||||||
host=host)
|
host=host)
|
||||||
return req
|
return req
|
||||||
|
|
||||||
|
|
||||||
def read_headers(self, sock_file, environ):
|
def read_headers(self, sock_file, environ):
|
||||||
try:
|
try:
|
||||||
lname = None
|
lname = None
|
||||||
@@ -1464,7 +1482,8 @@ class Worker(Thread):
|
|||||||
try:
|
try:
|
||||||
l = str(l, 'ISO-8859-1')
|
l = str(l, 'ISO-8859-1')
|
||||||
except UnicodeDecodeError:
|
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
|
break
|
||||||
@@ -1476,25 +1495,30 @@ class Worker(Thread):
|
|||||||
l = l.split(':', 1)
|
l = l.split(':', 1)
|
||||||
# HTTP header names are us-ascii encoded
|
# 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())
|
lval = str(l[-1].strip())
|
||||||
environ[lname] = lval
|
environ[lname] = lval
|
||||||
|
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
raise SocketTimeout("Socket timed out before request.")
|
raise SocketTimeout("Socket timed out before request.")
|
||||||
|
|
||||||
|
|
||||||
class SocketTimeout(Exception):
|
class SocketTimeout(Exception):
|
||||||
"Exception for when a socket times out between requests."
|
"Exception for when a socket times out between requests."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BadRequest(Exception):
|
class BadRequest(Exception):
|
||||||
"Exception for when a client sends an incomprehensible request."
|
"Exception for when a client sends an incomprehensible request."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SocketClosed(Exception):
|
class SocketClosed(Exception):
|
||||||
"Exception for when a socket is closed by the client."
|
"Exception for when a socket is closed by the client."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ChunkedReader(object):
|
class ChunkedReader(object):
|
||||||
def __init__(self, sock_file):
|
def __init__(self, sock_file):
|
||||||
self.stream = sock_file
|
self.stream = sock_file
|
||||||
@@ -1542,6 +1566,7 @@ class ChunkedReader(object):
|
|||||||
def readlines(self):
|
def readlines(self):
|
||||||
yield self.readline()
|
yield self.readline()
|
||||||
|
|
||||||
|
|
||||||
def get_method(method):
|
def get_method(method):
|
||||||
methods = dict(wsgi=WSGIWorker)
|
methods = dict(wsgi=WSGIWorker)
|
||||||
return methods[method.lower()]
|
return methods[method.lower()]
|
||||||
@@ -1559,7 +1584,6 @@ from wsgiref.util import FileWrapper
|
|||||||
# package imports removed in monolithic build
|
# package imports removed in monolithic build
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if PY3K:
|
if PY3K:
|
||||||
from email.utils import formatdate
|
from email.utils import formatdate
|
||||||
else:
|
else:
|
||||||
@@ -1578,6 +1602,7 @@ BASE_ENV = {'SERVER_NAME': SERVER_NAME,
|
|||||||
'wsgi.file_wrapper': FileWrapper
|
'wsgi.file_wrapper': FileWrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class WSGIWorker(Worker):
|
class WSGIWorker(Worker):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Builds some instance variables that will last the life of the
|
"""Builds some instance variables that will last the life of the
|
||||||
@@ -1588,7 +1613,8 @@ class WSGIWorker(Worker):
|
|||||||
multithreaded = self.app_info.get('max_threads') != 1
|
multithreaded = self.app_info.get('max_threads') != 1
|
||||||
else:
|
else:
|
||||||
multithreaded = False
|
multithreaded = False
|
||||||
self.base_environ = dict({'SERVER_SOFTWARE': self.app_info['server_software'],
|
self.base_environ = dict(
|
||||||
|
{'SERVER_SOFTWARE': self.app_info['server_software'],
|
||||||
'wsgi.multithread': multithreaded,
|
'wsgi.multithread': multithreaded,
|
||||||
})
|
})
|
||||||
self.base_environ.update(BASE_ENV)
|
self.base_environ.update(BASE_ENV)
|
||||||
@@ -1833,6 +1859,7 @@ class WSGIWorker(Worker):
|
|||||||
# the following code is not part of Rocket but was added in web2py for testing purposes
|
# the following code is not part of Rocket but was added in web2py for testing purposes
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
def demo_app(environ, start_response):
|
def demo_app(environ, start_response):
|
||||||
global static_folder
|
global static_folder
|
||||||
import os
|
import os
|
||||||
@@ -1841,7 +1868,8 @@ def demo_app(environ, start_response):
|
|||||||
if static_folder:
|
if static_folder:
|
||||||
if not static_folder.startswith('/'):
|
if not static_folder.startswith('/'):
|
||||||
static_folder = os.path.join(os.getcwd(), static_folder)
|
static_folder = os.path.join(os.getcwd(), static_folder)
|
||||||
path = os.path.join(static_folder, environ['PATH_INFO'][1:] or 'index.html')
|
path = os.path.join(
|
||||||
|
static_folder, environ['PATH_INFO'][1:] or 'index.html')
|
||||||
type = types.get(path.split('.')[-1], 'text')
|
type = types.get(path.split('.')[-1], 'text')
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
try:
|
try:
|
||||||
@@ -1858,6 +1886,7 @@ def demo_app(environ, start_response):
|
|||||||
data = '<html><body><h1>Hello from Rocket Web Server</h1></body></html>'
|
data = '<html><body><h1>Hello from Rocket Web Server</h1></body></html>'
|
||||||
return [data]
|
return [data]
|
||||||
|
|
||||||
|
|
||||||
def demo():
|
def demo():
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
parser = OptionParser()
|
parser = OptionParser()
|
||||||
|
|||||||
+4
-2
@@ -168,7 +168,8 @@ class XssCleaner(HTMLParser):
|
|||||||
content, otherwise remove it
|
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:
|
for tag in self.requires_no_close:
|
||||||
rawstring = rawstring.replace("<%s/>" % tag, "<%s />" % tag)
|
rawstring = rawstring.replace("<%s/>" % tag, "<%s />" % tag)
|
||||||
if not escape:
|
if not escape:
|
||||||
@@ -219,6 +220,7 @@ def sanitize(text, permitted_tags=[
|
|||||||
'td': ['colspan'],
|
'td': ['colspan'],
|
||||||
},
|
},
|
||||||
escape=True):
|
escape=True):
|
||||||
if not isinstance(text, str): return str(text)
|
if not isinstance(text, str):
|
||||||
|
return str(text)
|
||||||
return XssCleaner(permitted_tags=permitted_tags,
|
return XssCleaner(permitted_tags=permitted_tags,
|
||||||
allowed_attributes=allowed_attributes).strip(text, escape)
|
allowed_attributes=allowed_attributes).strip(text, escape)
|
||||||
+74
-41
@@ -114,6 +114,7 @@ CALLABLETYPES = (types.LambdaType, types.FunctionType,
|
|||||||
types.BuiltinFunctionType,
|
types.BuiltinFunctionType,
|
||||||
types.MethodType, types.BuiltinMethodType)
|
types.MethodType, types.BuiltinMethodType)
|
||||||
|
|
||||||
|
|
||||||
class Task(object):
|
class Task(object):
|
||||||
def __init__(self, app, function, timeout, args='[]', vars='{}', **kwargs):
|
def __init__(self, app, function, timeout, args='[]', vars='{}', **kwargs):
|
||||||
logger.debug(' new task allocated: %s.%s' % (app, function))
|
logger.debug(' new task allocated: %s.%s' % (app, function))
|
||||||
@@ -123,9 +124,11 @@ class Task(object):
|
|||||||
self.args = args # json
|
self.args = args # json
|
||||||
self.vars = vars # json
|
self.vars = vars # json
|
||||||
self.__dict__.update(kwargs)
|
self.__dict__.update(kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '<Task: %s>' % self.function
|
return '<Task: %s>' % self.function
|
||||||
|
|
||||||
|
|
||||||
class TaskReport(object):
|
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)
|
logger.debug(' new task report: %s' % status)
|
||||||
@@ -137,9 +140,11 @@ class TaskReport(object):
|
|||||||
self.result = result
|
self.result = result
|
||||||
self.output = output
|
self.output = output
|
||||||
self.tb = tb
|
self.tb = tb
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '<TaskReport: %s>' % self.status
|
return '<TaskReport: %s>' % self.status
|
||||||
|
|
||||||
|
|
||||||
def demo_function(*argv, **kwargs):
|
def demo_function(*argv, **kwargs):
|
||||||
""" test function """
|
""" test function """
|
||||||
for i in range(argv[0]):
|
for i in range(argv[0]):
|
||||||
@@ -150,6 +155,8 @@ def demo_function(*argv,**kwargs):
|
|||||||
#the two functions below deal with simplejson decoding as unicode, esp for the dict decode
|
#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!
|
#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
|
#borrowed from http://stackoverflow.com/questions/956867/how-to-get-string-objects-instead-unicode-ones-from-json-in-python
|
||||||
|
|
||||||
|
|
||||||
def _decode_list(lst):
|
def _decode_list(lst):
|
||||||
newlist = []
|
newlist = []
|
||||||
for i in lst:
|
for i in lst:
|
||||||
@@ -160,6 +167,7 @@ def _decode_list(lst):
|
|||||||
newlist.append(i)
|
newlist.append(i)
|
||||||
return newlist
|
return newlist
|
||||||
|
|
||||||
|
|
||||||
def _decode_dict(dct):
|
def _decode_dict(dct):
|
||||||
newdict = {}
|
newdict = {}
|
||||||
for k, v in dct.iteritems():
|
for k, v in dct.iteritems():
|
||||||
@@ -172,6 +180,7 @@ def _decode_dict(dct):
|
|||||||
newdict[k] = v
|
newdict[k] = v
|
||||||
return newdict
|
return newdict
|
||||||
|
|
||||||
|
|
||||||
def executor(queue, task, out):
|
def executor(queue, task, out):
|
||||||
""" the background process """
|
""" the background process """
|
||||||
logger.debug(' task started')
|
logger.debug(' task started')
|
||||||
@@ -182,10 +191,13 @@ def executor(queue,task, out):
|
|||||||
self.out_queue = out_queue
|
self.out_queue = out_queue
|
||||||
self.stdout = sys.stdout
|
self.stdout = sys.stdout
|
||||||
sys.stdout = self
|
sys.stdout = self
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
sys.stdout = self.stdout
|
sys.stdout = self.stdout
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
self.out_queue.put(data)
|
self.out_queue.put(data)
|
||||||
|
|
||||||
@@ -212,7 +224,8 @@ def executor(queue,task, out):
|
|||||||
else:
|
else:
|
||||||
_function = functions.get(f)
|
_function = functions.get(f)
|
||||||
if not isinstance(_function, CALLABLETYPES):
|
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)
|
globals().update(_env)
|
||||||
args = loads(task.args)
|
args = loads(task.args)
|
||||||
vars = loads(task.vars, object_hook=_decode_dict)
|
vars = loads(task.vars, object_hook=_decode_dict)
|
||||||
@@ -228,6 +241,7 @@ def executor(queue,task, out):
|
|||||||
queue.put(TaskReport(FAILED, tb=tb))
|
queue.put(TaskReport(FAILED, tb=tb))
|
||||||
del stdout
|
del stdout
|
||||||
|
|
||||||
|
|
||||||
class MetaScheduler(threading.Thread):
|
class MetaScheduler(threading.Thread):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
@@ -279,7 +293,8 @@ class MetaScheduler(threading.Thread):
|
|||||||
if tout:
|
if tout:
|
||||||
logger.debug(' partial output: "%s"' % str(tout))
|
logger.debug(' partial output: "%s"' % str(tout))
|
||||||
if CLEAROUT in tout:
|
if CLEAROUT in tout:
|
||||||
task_output = tout[tout.rfind(CLEAROUT)+len(CLEAROUT):]
|
task_output = tout[
|
||||||
|
tout.rfind(CLEAROUT) + len(CLEAROUT):]
|
||||||
else:
|
else:
|
||||||
task_output += tout
|
task_output += tout
|
||||||
except:
|
except:
|
||||||
@@ -366,9 +381,11 @@ class MetaScheduler(threading.Thread):
|
|||||||
self.empty_runs += 1
|
self.empty_runs += 1
|
||||||
logger.debug('sleeping...')
|
logger.debug('sleeping...')
|
||||||
if self.max_empty_runs != 0:
|
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:
|
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.die()
|
||||||
self.sleep()
|
self.sleep()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
@@ -379,6 +396,7 @@ TASK_STATUS = (QUEUED, RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED, EXPIRED)
|
|||||||
RUN_STATUS = (RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED)
|
RUN_STATUS = (RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED)
|
||||||
WORKER_STATUS = (ACTIVE, DISABLED, TERMINATE, KILL)
|
WORKER_STATUS = (ACTIVE, DISABLED, TERMINATE, KILL)
|
||||||
|
|
||||||
|
|
||||||
class TYPE(object):
|
class TYPE(object):
|
||||||
"""
|
"""
|
||||||
validator that check whether field is valid json and validate its type
|
validator that check whether field is valid json and validate its type
|
||||||
@@ -403,6 +421,7 @@ class TYPE(object):
|
|||||||
else:
|
else:
|
||||||
return (value, current.T('Not of type: %s') % self.myclass)
|
return (value, current.T('Not of type: %s') % self.myclass)
|
||||||
|
|
||||||
|
|
||||||
class Scheduler(MetaScheduler):
|
class Scheduler(MetaScheduler):
|
||||||
def __init__(self, db, tasks=None, migrate=True,
|
def __init__(self, db, tasks=None, migrate=True,
|
||||||
worker_name=None, group_names=['main'], heartbeat=HEARTBEAT,
|
worker_name=None, group_names=['main'], heartbeat=HEARTBEAT,
|
||||||
@@ -415,7 +434,8 @@ class Scheduler(MetaScheduler):
|
|||||||
self.tasks = tasks
|
self.tasks = tasks
|
||||||
self.group_names = group_names
|
self.group_names = group_names
|
||||||
self.heartbeat = heartbeat
|
self.heartbeat = heartbeat
|
||||||
self.worker_name = worker_name or socket.gethostname()+'#'+str(os.getpid())
|
self.worker_name = worker_name or socket.gethostname(
|
||||||
|
) + '#' + str(os.getpid())
|
||||||
self.worker_status = RUNNING, 1 # tuple containing status as recorded in
|
self.worker_status = RUNNING, 1 # tuple containing status as recorded in
|
||||||
#the table, plus a boost parameter for
|
#the table, plus a boost parameter for
|
||||||
#hibernation (i.e. when someone stop the
|
#hibernation (i.e. when someone stop the
|
||||||
@@ -483,7 +503,6 @@ class Scheduler(MetaScheduler):
|
|||||||
on_define=self.set_requirements,
|
on_define=self.set_requirements,
|
||||||
migrate=migrate, format='%(task_name)s')
|
migrate=migrate, format='%(task_name)s')
|
||||||
|
|
||||||
|
|
||||||
db.define_table(
|
db.define_table(
|
||||||
'scheduler_run',
|
'scheduler_run',
|
||||||
Field('scheduler_task', 'reference scheduler_task'),
|
Field('scheduler_task', 'reference scheduler_task'),
|
||||||
@@ -526,9 +545,11 @@ class Scheduler(MetaScheduler):
|
|||||||
self.empty_runs += 1
|
self.empty_runs += 1
|
||||||
logger.debug('sleeping...')
|
logger.debug('sleeping...')
|
||||||
if self.max_empty_runs != 0:
|
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:
|
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.die()
|
||||||
self.sleep()
|
self.sleep()
|
||||||
except (KeyboardInterrupt, SystemExit):
|
except (KeyboardInterrupt, SystemExit):
|
||||||
@@ -552,8 +573,8 @@ class Scheduler(MetaScheduler):
|
|||||||
logger.error('TICKER: error assigning tasks')
|
logger.error('TICKER: error assigning tasks')
|
||||||
return None
|
return None
|
||||||
db.commit()
|
db.commit()
|
||||||
grabbed = db(ts.assigned_worker_name==self.worker_name)\
|
grabbed = db(ts.assigned_worker_name == self.worker_name)(
|
||||||
(ts.status==ASSIGNED)
|
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:
|
if task:
|
||||||
@@ -564,7 +585,8 @@ class Scheduler(MetaScheduler):
|
|||||||
else:
|
else:
|
||||||
logger.debug('nothing to do')
|
logger.debug('nothing to do')
|
||||||
return None
|
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
|
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
|
run_again = True
|
||||||
@@ -610,7 +632,8 @@ class Scheduler(MetaScheduler):
|
|||||||
#result is 'null' as a string if task completed
|
#result is 'null' as a string if task completed
|
||||||
#if it's stopped it's None as NoneType, so we record
|
#if it's stopped it's None as NoneType, so we record
|
||||||
#the STOPPED "run" anyway
|
#the STOPPED "run" anyway
|
||||||
logger.debug(' recording task report in db (%s)' % task_report.status)
|
logger.debug(' recording task report in db (%s)' %
|
||||||
|
task_report.status)
|
||||||
db(db.scheduler_run.id == task.run_id).update(
|
db(db.scheduler_run.id == task.run_id).update(
|
||||||
status=task_report.status,
|
status=task_report.status,
|
||||||
stop_time=now,
|
stop_time=now,
|
||||||
@@ -632,8 +655,8 @@ class Scheduler(MetaScheduler):
|
|||||||
times_run=task.times_run,
|
times_run=task.times_run,
|
||||||
times_failed=0
|
times_failed=0
|
||||||
)
|
)
|
||||||
db(db.scheduler_task.id==task.task_id)\
|
db(db.scheduler_task.id == task.task_id)(
|
||||||
(db.scheduler_task.status==RUNNING).update(**d)
|
db.scheduler_task.status == RUNNING).update(**d)
|
||||||
else:
|
else:
|
||||||
st_mapping = {'FAILED': 'FAILED',
|
st_mapping = {'FAILED': 'FAILED',
|
||||||
'TIMEOUT': 'TIMEOUT',
|
'TIMEOUT': 'TIMEOUT',
|
||||||
@@ -642,8 +665,7 @@ class Scheduler(MetaScheduler):
|
|||||||
and task.times_failed < 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)
|
and QUEUED or st_mapping)
|
||||||
db(db.scheduler_task.id==task.task_id)\
|
db(db.scheduler_task.id == task.task_id)(db.scheduler_task.status == RUNNING).update(
|
||||||
(db.scheduler_task.status==RUNNING).update(
|
|
||||||
times_failed=db.scheduler_task.times_failed + 1,
|
times_failed=db.scheduler_task.times_failed + 1,
|
||||||
next_run_time=task.next_run_time,
|
next_run_time=task.next_run_time,
|
||||||
status=status)
|
status=status)
|
||||||
@@ -655,22 +677,26 @@ class Scheduler(MetaScheduler):
|
|||||||
|
|
||||||
def adj_hibernation(self):
|
def adj_hibernation(self):
|
||||||
if self.worker_status[0] == DISABLED:
|
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
|
self.worker_status = DISABLED, hibernation
|
||||||
|
|
||||||
def send_heartbeat(self, counter):
|
def send_heartbeat(self, counter):
|
||||||
if not self.db_thread:
|
if not self.db_thread:
|
||||||
logger.debug('thread building own DAL object')
|
logger.debug('thread building own DAL object')
|
||||||
self.db_thread = DAL(self.db._uri,folder = self.db._adapter.folder)
|
self.db_thread = DAL(
|
||||||
|
self.db._uri, folder=self.db._adapter.folder)
|
||||||
self.define_tables(self.db_thread, migrate=False)
|
self.define_tables(self.db_thread, migrate=False)
|
||||||
try:
|
try:
|
||||||
db = self.db_thread
|
db = self.db_thread
|
||||||
sw, st = db.scheduler_worker, db.scheduler_task
|
sw, st = db.scheduler_worker, db.scheduler_task
|
||||||
now = self.now()
|
now = self.now()
|
||||||
expiration = now - datetime.timedelta(seconds=self.heartbeat * 3)
|
expiration = now - datetime.timedelta(seconds=self.heartbeat * 3)
|
||||||
departure = now-datetime.timedelta(seconds=self.heartbeat*3*MAXHIBERNATION)
|
departure = now - datetime.timedelta(
|
||||||
|
seconds=self.heartbeat * 3 * MAXHIBERNATION)
|
||||||
# record heartbeat
|
# 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:
|
if not mybackedstatus:
|
||||||
sw.insert(status=ACTIVE, worker_name=self.worker_name,
|
sw.insert(status=ACTIVE, worker_name=self.worker_name,
|
||||||
first_heartbeat=now, last_heartbeat=now,
|
first_heartbeat=now, last_heartbeat=now,
|
||||||
@@ -678,7 +704,8 @@ class Scheduler(MetaScheduler):
|
|||||||
self.worker_status = ACTIVE, 1 # activating the process
|
self.worker_status = ACTIVE, 1 # activating the process
|
||||||
else:
|
else:
|
||||||
if mybackedstatus.status == DISABLED:
|
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:
|
if self.worker_status[1] == MAXHIBERNATION:
|
||||||
logger.debug('........recording heartbeat')
|
logger.debug('........recording heartbeat')
|
||||||
db(sw.worker_name == self.worker_name).update(
|
db(sw.worker_name == self.worker_name).update(
|
||||||
@@ -703,14 +730,15 @@ class Scheduler(MetaScheduler):
|
|||||||
if counter % 5 == 0:
|
if counter % 5 == 0:
|
||||||
try:
|
try:
|
||||||
# delete inactive workers
|
# delete inactive workers
|
||||||
logger.debug(' freeing workers that have not sent heartbeat')
|
logger.debug(
|
||||||
|
' freeing workers that have not sent heartbeat')
|
||||||
inactive_workers = db(
|
inactive_workers = db(
|
||||||
((sw.last_heartbeat < expiration) & (sw.status == ACTIVE)) |
|
((sw.last_heartbeat < expiration) & (sw.status == ACTIVE)) |
|
||||||
((sw.last_heartbeat<departure) & (sw.status != ACTIVE))
|
((sw.last_heartbeat <
|
||||||
|
departure) & (sw.status != ACTIVE))
|
||||||
)
|
)
|
||||||
db(st.assigned_worker_name.belongs(
|
db(st.assigned_worker_name.belongs(
|
||||||
inactive_workers._select(sw.worker_name)))\
|
inactive_workers._select(sw.worker_name)))(st.status == RUNNING)\
|
||||||
(st.status == RUNNING)\
|
|
||||||
.update(assigned_worker_name='', status=QUEUED)
|
.update(assigned_worker_name='', status=QUEUED)
|
||||||
inactive_workers.delete()
|
inactive_workers.delete()
|
||||||
self.is_a_ticker = self.being_a_ticker()
|
self.is_a_ticker = self.being_a_ticker()
|
||||||
@@ -727,7 +755,8 @@ class Scheduler(MetaScheduler):
|
|||||||
def being_a_ticker(self):
|
def being_a_ticker(self):
|
||||||
db = self.db_thread
|
db = self.db_thread
|
||||||
sw = db.scheduler_worker
|
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:
|
if not ticker:
|
||||||
db(sw.worker_name == self.worker_name).update(is_ticker=True)
|
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=False)
|
||||||
@@ -735,7 +764,8 @@ class Scheduler(MetaScheduler):
|
|||||||
db.commit()
|
db.commit()
|
||||||
return True
|
return True
|
||||||
else:
|
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
|
return False
|
||||||
|
|
||||||
def assign_tasks(self):
|
def assign_tasks(self):
|
||||||
@@ -749,19 +779,17 @@ class Scheduler(MetaScheduler):
|
|||||||
group_names = w.group_names
|
group_names = w.group_names
|
||||||
for gname in group_names:
|
for gname in group_names:
|
||||||
if gname not in wkgroups:
|
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:
|
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)
|
#set queued tasks that expired between "runs" (i.e., you turned off)
|
||||||
#the scheduler and then it wasn't expired, but now it is
|
#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)))\
|
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)
|
||||||
((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)
|
|
||||||
|
|
||||||
limit = len(all_workers) * 50
|
limit = len(all_workers) * 50
|
||||||
#if there are a moltitude of tasks, let's assign a maximum of 50 tasks per worker.
|
#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
|
#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
|
#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.
|
#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,
|
#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.
|
#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
|
#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))
|
logger.info('TICKER: tasks are %s' % len(tasks))
|
||||||
|
|
||||||
def sleep(self):
|
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):
|
def queue_task(self, function, pargs=[], pvars={}, **kwargs):
|
||||||
"""
|
"""
|
||||||
@@ -866,7 +896,8 @@ class Scheduler(MetaScheduler):
|
|||||||
elif isinstance(ref, Query):
|
elif isinstance(ref, Query):
|
||||||
q = ref
|
q = ref
|
||||||
else:
|
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
|
fields = st.ALL
|
||||||
left = False
|
left = False
|
||||||
orderby = ~st.id
|
orderby = ~st.id
|
||||||
@@ -882,9 +913,11 @@ class Scheduler(MetaScheduler):
|
|||||||
).first()
|
).first()
|
||||||
if output:
|
if output:
|
||||||
row.result = row.scheduler_run.result and \
|
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
|
return row
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""
|
"""
|
||||||
allows to run worker without python web2py.py .... by simply python this.py
|
allows to run worker without python web2py.py .... by simply python this.py
|
||||||
@@ -920,7 +953,7 @@ def main():
|
|||||||
help="database URI string (web2py DAL syntax)")
|
help="database URI string (web2py DAL syntax)")
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"-t", "--tasks", dest="tasks", default=None,
|
"-t", "--tasks", dest="tasks", default=None,
|
||||||
help="file containing task files, must define" + \
|
help="file containing task files, must define" +
|
||||||
"tasks = {'task_name':(lambda: 'output')} or similar set of tasks")
|
"tasks = {'task_name':(lambda: 'output')} or similar set of tasks")
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"-U", "--utc-time", dest="utc_time", default=False,
|
"-U", "--utc-time", dest="utc_time", default=False,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ except ImportError:
|
|||||||
except:
|
except:
|
||||||
import contrib.simplejson as json_parser # fallback to pure-Python module
|
import contrib.simplejson as json_parser # fallback to pure-Python module
|
||||||
|
|
||||||
|
|
||||||
def custom_json(o):
|
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()
|
return o.custom_json()
|
||||||
@@ -46,7 +47,7 @@ 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()
|
return value.custom_xml()
|
||||||
elif isinstance(value, (dict, Storage)):
|
elif isinstance(value, (dict, Storage)):
|
||||||
return TAG[key](*[TAG[k](xml_rec(v, '',quote)) \
|
return TAG[key](*[TAG[k](xml_rec(v, '', quote))
|
||||||
for k, v in value.items()])
|
for k, v in value.items()])
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
return TAG[key](*[TAG.item(xml_rec(item, '', quote)) for item in value])
|
return TAG[key](*[TAG.item(xml_rec(item, '', quote)) for item in value])
|
||||||
@@ -69,11 +70,13 @@ def json(value,default=custom_json):
|
|||||||
def csv(value):
|
def csv(value):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
def ics(events, title=None, link=None, timeshift=0, **ignored):
|
def ics(events, title=None, link=None, timeshift=0, **ignored):
|
||||||
import datetime
|
import datetime
|
||||||
title = title or '(unkown)'
|
title = title or '(unkown)'
|
||||||
if link and not callable(link):
|
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 = 'BEGIN:VCALENDAR'
|
||||||
s += '\nVERSION:2.0'
|
s += '\nVERSION:2.0'
|
||||||
s += '\nX-WR-CALNAME:%s' % title
|
s += '\nX-WR-CALNAME:%s' % title
|
||||||
@@ -96,6 +99,7 @@ def ics(events, title=None, link=None, timeshift=0, **ignored):
|
|||||||
s += '\nEND:VCALENDAR'
|
s += '\nEND:VCALENDAR'
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def rss(feed):
|
def rss(feed):
|
||||||
if not 'entries' in feed and 'items' in feed:
|
if not 'entries' in feed and 'items' in feed:
|
||||||
feed['entries'] = feed['items']
|
feed['entries'] = feed['items']
|
||||||
|
|||||||
+26
-14
@@ -30,6 +30,7 @@ from dal import BaseAdapter
|
|||||||
|
|
||||||
logger = logging.getLogger("web2py")
|
logger = logging.getLogger("web2py")
|
||||||
|
|
||||||
|
|
||||||
def exec_environment(
|
def exec_environment(
|
||||||
pyfile='',
|
pyfile='',
|
||||||
request=None,
|
request=None,
|
||||||
@@ -50,9 +51,12 @@ def exec_environment(
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if request is None: request = Request()
|
if request is None:
|
||||||
if response is None: response = Response()
|
request = Request()
|
||||||
if session is None: session = Session()
|
if response is None:
|
||||||
|
response = Response()
|
||||||
|
if session is None:
|
||||||
|
session = Session()
|
||||||
|
|
||||||
if request.folder is None:
|
if request.folder is None:
|
||||||
mo = re.match(r'(|.*/)applications/(?P<appname>[^/]+)', pyfile)
|
mo = re.match(r'(|.*/)applications/(?P<appname>[^/]+)', pyfile)
|
||||||
@@ -172,7 +176,8 @@ def run(
|
|||||||
adir = os.path.join('applications', a)
|
adir = os.path.join('applications', a)
|
||||||
if not os.path.exists(adir):
|
if not os.path.exists(adir):
|
||||||
if sys.stdin and not sys.stdin.name == '/dev/null':
|
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:
|
else:
|
||||||
logging.warn('application does not exist and will not be created')
|
logging.warn('application does not exist and will not be created')
|
||||||
return
|
return
|
||||||
@@ -189,7 +194,8 @@ def run(
|
|||||||
db = os.path.join(adir, 'models/db.py')
|
db = os.path.join(adir, 'models/db.py')
|
||||||
if os.path.exists(db):
|
if os.path.exists(db):
|
||||||
data = fileutils.read_file(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)
|
fileutils.write_file(db, data)
|
||||||
|
|
||||||
if c:
|
if c:
|
||||||
@@ -198,7 +204,8 @@ def run(
|
|||||||
if c:
|
if c:
|
||||||
cfile = os.path.join('applications', a, 'controllers', c + '.py')
|
cfile = os.path.join('applications', a, 'controllers', c + '.py')
|
||||||
if not os.path.isfile(cfile):
|
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):
|
if not os.path.isfile(cfile):
|
||||||
die(errmsg)
|
die(errmsg)
|
||||||
else:
|
else:
|
||||||
@@ -214,17 +221,21 @@ def run(
|
|||||||
if startfile:
|
if startfile:
|
||||||
try:
|
try:
|
||||||
execfile(startfile, _env)
|
execfile(startfile, _env)
|
||||||
if import_models: BaseAdapter.close_all_instances('commit')
|
if import_models:
|
||||||
|
BaseAdapter.close_all_instances('commit')
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
print traceback.format_exc()
|
print traceback.format_exc()
|
||||||
if import_models: BaseAdapter.close_all_instances('rollback')
|
if import_models:
|
||||||
|
BaseAdapter.close_all_instances('rollback')
|
||||||
elif python_code:
|
elif python_code:
|
||||||
try:
|
try:
|
||||||
exec(python_code, _env)
|
exec(python_code, _env)
|
||||||
if import_models: BaseAdapter.close_all_instances('commit')
|
if import_models:
|
||||||
|
BaseAdapter.close_all_instances('commit')
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
print traceback.format_exc()
|
print traceback.format_exc()
|
||||||
if import_models: BaseAdapter.close_all_instances('rollback')
|
if import_models:
|
||||||
|
BaseAdapter.close_all_instances('rollback')
|
||||||
else:
|
else:
|
||||||
if not plain:
|
if not plain:
|
||||||
if bpython:
|
if bpython:
|
||||||
@@ -369,7 +380,7 @@ def execute_from_command_line(argv=None):
|
|||||||
parser = optparse.OptionParser(usage=get_usage())
|
parser = optparse.OptionParser(usage=get_usage())
|
||||||
|
|
||||||
parser.add_option('-S', '--shell', dest='shell', metavar='APPNAME',
|
parser.add_option('-S', '--shell', dest='shell', metavar='APPNAME',
|
||||||
help='run web2py in interactive shell or IPython(if installed) ' + \
|
help='run web2py in interactive shell or IPython(if installed) ' +
|
||||||
'with specified appname')
|
'with specified appname')
|
||||||
msg = 'run web2py in interactive shell or bpython (if installed) with'
|
msg = 'run web2py in interactive shell or bpython (if installed) with'
|
||||||
msg += ' specified appname (if app does not exist it will be created).'
|
msg += ' specified appname (if app does not exist it will be created).'
|
||||||
@@ -396,7 +407,7 @@ def execute_from_command_line(argv=None):
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
dest='import_models',
|
dest='import_models',
|
||||||
help='auto import model files, default is False, ' + \
|
help='auto import model files, default is False, ' +
|
||||||
' should be used with --shell option',
|
' should be used with --shell option',
|
||||||
)
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
@@ -405,7 +416,7 @@ def execute_from_command_line(argv=None):
|
|||||||
dest='run',
|
dest='run',
|
||||||
metavar='PYTHON_FILE',
|
metavar='PYTHON_FILE',
|
||||||
default='',
|
default='',
|
||||||
help='run PYTHON_FILE in web2py environment, ' + \
|
help='run PYTHON_FILE in web2py environment, ' +
|
||||||
'should be used with --shell option',
|
'should be used with --shell option',
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -419,7 +430,8 @@ def execute_from_command_line(argv=None):
|
|||||||
startfile = args[0]
|
startfile = args[0]
|
||||||
else:
|
else:
|
||||||
startfile = ''
|
startfile = ''
|
||||||
run(options.shell, options.plain, startfile=startfile, bpython=options.bpython)
|
run(options.shell, options.plain, startfile=startfile,
|
||||||
|
bpython=options.bpython)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
+157
-91
@@ -42,21 +42,25 @@ is_gae = settings.global_settings.web2py_runtime_gae
|
|||||||
table_field = re.compile('[\w_]+\.[\w_]+')
|
table_field = re.compile('[\w_]+\.[\w_]+')
|
||||||
widget_class = re.compile('^\w*')
|
widget_class = re.compile('^\w*')
|
||||||
|
|
||||||
|
|
||||||
def trap_class(_class=None, trap=True):
|
def trap_class(_class=None, trap=True):
|
||||||
return (trap and 'w2p_trap' or '') + (_class and ' ' + _class or '')
|
return (trap and 'w2p_trap' or '') + (_class and ' ' + _class or '')
|
||||||
|
|
||||||
|
|
||||||
def represent(field, value, record):
|
def represent(field, value, record):
|
||||||
f = field.represent
|
f = field.represent
|
||||||
if not callable(f):
|
if not callable(f):
|
||||||
return str(value)
|
return str(value)
|
||||||
n = f.func_code.co_argcount - len(f.func_defaults or [])
|
n = f.func_code.co_argcount - len(f.func_defaults or [])
|
||||||
if getattr(f, 'im_self', None): n -= 1
|
if getattr(f, 'im_self', None):
|
||||||
|
n -= 1
|
||||||
if n == 1:
|
if n == 1:
|
||||||
return f(value)
|
return f(value)
|
||||||
elif n == 2:
|
elif n == 2:
|
||||||
return f(value, record)
|
return f(value, record)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError, "field representation must take 1 or 2 args"
|
raise RuntimeError("field representation must take 1 or 2 args")
|
||||||
|
|
||||||
|
|
||||||
def safe_int(x):
|
def safe_int(x):
|
||||||
try:
|
try:
|
||||||
@@ -64,12 +68,14 @@ def safe_int(x):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def safe_float(x):
|
def safe_float(x):
|
||||||
try:
|
try:
|
||||||
return float(x)
|
return float(x)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
class FormWidget(object):
|
class FormWidget(object):
|
||||||
"""
|
"""
|
||||||
helper for SQLFORM to generate form input fields
|
helper for SQLFORM to generate form input fields
|
||||||
@@ -91,7 +97,7 @@ class FormWidget(object):
|
|||||||
"""
|
"""
|
||||||
attr = dict(
|
attr = dict(
|
||||||
_id='%s_%s' % (field._tablename, field.name),
|
_id='%s_%s' % (field._tablename, field.name),
|
||||||
_class = cls._class or \
|
_class=cls._class or
|
||||||
widget_class.match(str(field.type)).group(),
|
widget_class.match(str(field.type)).group(),
|
||||||
_name=field.name,
|
_name=field.name,
|
||||||
requires=field.requires,
|
requires=field.requires,
|
||||||
@@ -118,6 +124,7 @@ class FormWidget(object):
|
|||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class StringWidget(FormWidget):
|
class StringWidget(FormWidget):
|
||||||
_class = 'string'
|
_class = 'string'
|
||||||
|
|
||||||
@@ -225,19 +232,23 @@ class OptionsWidget(FormWidget):
|
|||||||
if hasattr(requires[0], 'options'):
|
if hasattr(requires[0], 'options'):
|
||||||
options = requires[0].options()
|
options = requires[0].options()
|
||||||
else:
|
else:
|
||||||
raise SyntaxError, 'widget cannot determine options of %s' % field
|
raise SyntaxError(
|
||||||
|
'widget cannot determine options of %s' % field)
|
||||||
opts = [OPTION(v, _value=k) for (k, v) in options]
|
opts = [OPTION(v, _value=k) for (k, v) in options]
|
||||||
|
|
||||||
return SELECT(*opts, **attr)
|
return SELECT(*opts, **attr)
|
||||||
|
|
||||||
|
|
||||||
class ListWidget(StringWidget):
|
class ListWidget(StringWidget):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def widget(cls, field, value, **attributes):
|
def widget(cls, field, value, **attributes):
|
||||||
_id = '%s_%s' % (field._tablename, field.name)
|
_id = '%s_%s' % (field._tablename, field.name)
|
||||||
_name = field.name
|
_name = field.name
|
||||||
if field.type=='list:integer': _class = 'integer'
|
if field.type == 'list:integer':
|
||||||
else: _class = 'string'
|
_class = 'integer'
|
||||||
|
else:
|
||||||
|
_class = 'string'
|
||||||
requires = field.requires if isinstance(
|
requires = field.requires if isinstance(
|
||||||
field.requires, (IS_NOT_EMPTY, IS_LIST_OF)) else None
|
field.requires, (IS_NOT_EMPTY, IS_LIST_OF)) else None
|
||||||
attributes['_style'] = 'list-style:none'
|
attributes['_style'] = 'list-style:none'
|
||||||
@@ -319,8 +330,8 @@ class RadioWidget(OptionsWidget):
|
|||||||
if hasattr(requires[0], 'options'):
|
if hasattr(requires[0], 'options'):
|
||||||
options = requires[0].options()
|
options = requires[0].options()
|
||||||
else:
|
else:
|
||||||
raise SyntaxError, 'widget cannot determine options of %s' \
|
raise SyntaxError('widget cannot determine options of %s'
|
||||||
% field
|
% field)
|
||||||
options = [(k, v) for k, v in options if str(v)]
|
options = [(k, v) for k, v in options if str(v)]
|
||||||
opts = []
|
opts = []
|
||||||
cols = attributes.get('cols', 1)
|
cols = attributes.get('cols', 1)
|
||||||
@@ -382,8 +393,8 @@ class CheckboxesWidget(OptionsWidget):
|
|||||||
if requires and hasattr(requires[0], 'options'):
|
if requires and hasattr(requires[0], 'options'):
|
||||||
options = requires[0].options()
|
options = requires[0].options()
|
||||||
else:
|
else:
|
||||||
raise SyntaxError, 'widget cannot determine options of %s' \
|
raise SyntaxError('widget cannot determine options of %s'
|
||||||
% field
|
% field)
|
||||||
|
|
||||||
options = [(k, v) for k, v in options if k != '']
|
options = [(k, v) for k, v in options if k != '']
|
||||||
opts = []
|
opts = []
|
||||||
@@ -418,9 +429,9 @@ class CheckboxesWidget(OptionsWidget):
|
|||||||
LABEL(v, _for='%s%s' % (field.name, k))))
|
LABEL(v, _for='%s%s' % (field.name, k))))
|
||||||
opts.append(child(tds))
|
opts.append(child(tds))
|
||||||
|
|
||||||
|
|
||||||
if opts:
|
if opts:
|
||||||
opts.append(INPUT(_class="hidden", requires=attr.get('requires', None),
|
opts.append(
|
||||||
|
INPUT(_class="hidden", requires=attr.get('requires', None),
|
||||||
_disabled="disabled", _name=field.name,
|
_disabled="disabled", _name=field.name,
|
||||||
hideerror=False))
|
hideerror=False))
|
||||||
return parent(*opts, **attr)
|
return parent(*opts, **attr)
|
||||||
@@ -450,10 +461,11 @@ class PasswordWidget(FormWidget):
|
|||||||
|
|
||||||
# deal with entropy check!
|
# deal with entropy check!
|
||||||
requires = field.requires
|
requires = field.requires
|
||||||
if not isinstance(requires,(list,tuple)): requires = [requires]
|
if not isinstance(requires, (list, tuple)):
|
||||||
|
requires = [requires]
|
||||||
is_strong = [r for r in requires if isinstance(r, IS_STRONG)]
|
is_strong = [r for r in requires if isinstance(r, IS_STRONG)]
|
||||||
if is_strong:
|
if is_strong:
|
||||||
output.append(SCRIPT("web2py_validate_entropy(jQuery('#%s'),%s);" \
|
output.append(SCRIPT("web2py_validate_entropy(jQuery('#%s'),%s);"
|
||||||
% (attr['_id'], is_strong[0].entropy)))
|
% (attr['_id'], is_strong[0].entropy)))
|
||||||
# end entropy check
|
# end entropy check
|
||||||
return output
|
return output
|
||||||
@@ -499,7 +511,8 @@ class UploadWidget(FormWidget):
|
|||||||
requires = attr["requires"]
|
requires = attr["requires"]
|
||||||
if requires == [] or isinstance(requires, IS_EMPTY_OR):
|
if requires == [] or isinstance(requires, IS_EMPTY_OR):
|
||||||
inp = DIV(inp, '[',
|
inp = DIV(inp, '[',
|
||||||
A(current.T(UploadWidget.GENERIC_DESCRIPTION), _href = url),
|
A(current.T(
|
||||||
|
UploadWidget.GENERIC_DESCRIPTION), _href=url),
|
||||||
'|',
|
'|',
|
||||||
INPUT(_type='checkbox',
|
INPUT(_type='checkbox',
|
||||||
_name=field.name + cls.ID_DELETE_SUFFIX,
|
_name=field.name + cls.ID_DELETE_SUFFIX,
|
||||||
@@ -601,19 +614,23 @@ class AutocompleteWidget(object):
|
|||||||
if self.is_reference:
|
if self.is_reference:
|
||||||
id_field = self.fields[1]
|
id_field = self.fields[1]
|
||||||
if self.help_fields:
|
if self.help_fields:
|
||||||
options = [OPTION(self.help_string % dict([(h.name,s[h.name]) for h in self.fields[:1]+self.help_fields]),
|
options = [OPTION(
|
||||||
|
self.help_string % dict([(h.name, s[h.name]) for h in self.fields[:1] + self.help_fields]),
|
||||||
_value=s[id_field.name], _selected=(k == 0)) for k, s in enumerate(rows)]
|
_value=s[id_field.name], _selected=(k == 0)) for k, s in enumerate(rows)]
|
||||||
else:
|
else:
|
||||||
options = [OPTION(s[field.name],_value=s[id_field.name],
|
options = [OPTION(
|
||||||
|
s[field.name], _value=s[id_field.name],
|
||||||
_selected=(k == 0)) for k, s in enumerate(rows)]
|
_selected=(k == 0)) for k, s in enumerate(rows)]
|
||||||
raise HTTP(200,SELECT(_id=self.keyword,_class='autocomplete',
|
raise HTTP(
|
||||||
|
200, SELECT(_id=self.keyword, _class='autocomplete',
|
||||||
_size=len(rows), _multiple=(len(rows) == 1),
|
_size=len(rows), _multiple=(len(rows) == 1),
|
||||||
*options).xml())
|
*options).xml())
|
||||||
else:
|
else:
|
||||||
raise HTTP(200,SELECT(_id=self.keyword,_class='autocomplete',
|
raise HTTP(
|
||||||
|
200, SELECT(_id=self.keyword, _class='autocomplete',
|
||||||
_size=len(rows), _multiple=(len(rows) == 1),
|
_size=len(rows), _multiple=(len(rows) == 1),
|
||||||
*[OPTION(s[field.name],
|
*[OPTION(s[field.name],
|
||||||
_selected=(k==0)) \
|
_selected=(k == 0))
|
||||||
for k, s in enumerate(rows)]).xml())
|
for k, s in enumerate(rows)]).xml())
|
||||||
else:
|
else:
|
||||||
raise HTTP(200, '')
|
raise HTTP(200, '')
|
||||||
@@ -631,10 +648,12 @@ class AutocompleteWidget(object):
|
|||||||
key3 = self.keyword + '_auto'
|
key3 = self.keyword + '_auto'
|
||||||
attr['_class'] = 'string'
|
attr['_class'] = 'string'
|
||||||
name = attr['_name']
|
name = attr['_name']
|
||||||
if 'requires' in attr: del attr['requires']
|
if 'requires' in attr:
|
||||||
|
del attr['requires']
|
||||||
attr['_name'] = key2
|
attr['_name'] = key2
|
||||||
value = attr['value']
|
value = attr['value']
|
||||||
record = self.db(self.fields[1]==value).select(self.fields[0]).first()
|
record = self.db(
|
||||||
|
self.fields[1] == value).select(self.fields[0]).first()
|
||||||
attr['value'] = record and record[self.fields[0].name]
|
attr['value'] = record and record[self.fields[0].name]
|
||||||
attr['_onblur'] = "jQuery('#%(div_id)s').delay(1000).fadeOut('slow');" % \
|
attr['_onblur'] = "jQuery('#%(div_id)s').delay(1000).fadeOut('slow');" % \
|
||||||
dict(div_id=div_id, u='F' + self.keyword)
|
dict(div_id=div_id, u='F' + self.keyword)
|
||||||
@@ -658,6 +677,7 @@ class AutocompleteWidget(object):
|
|||||||
attr['_onfocus'] = attr['_onkeyup']
|
attr['_onfocus'] = attr['_onkeyup']
|
||||||
return TAG[''](INPUT(**attr), DIV(_id=div_id, _style='position:absolute;'))
|
return TAG[''](INPUT(**attr), DIV(_id=div_id, _style='position:absolute;'))
|
||||||
|
|
||||||
|
|
||||||
def formstyle_table3cols(form, fields):
|
def formstyle_table3cols(form, fields):
|
||||||
''' 3 column table - default '''
|
''' 3 column table - default '''
|
||||||
table = TABLE()
|
table = TABLE()
|
||||||
@@ -668,6 +688,7 @@ def formstyle_table3cols(form, fields):
|
|||||||
table.append(TR(_label, _controls, _help, _id=id))
|
table.append(TR(_label, _controls, _help, _id=id))
|
||||||
return table
|
return table
|
||||||
|
|
||||||
|
|
||||||
def formstyle_table2cols(form, fields):
|
def formstyle_table2cols(form, fields):
|
||||||
''' 2 column table '''
|
''' 2 column table '''
|
||||||
table = TABLE()
|
table = TABLE()
|
||||||
@@ -679,6 +700,7 @@ def formstyle_table2cols(form, fields):
|
|||||||
table.append(TR(_controls, _id=id + '2', _class='odd'))
|
table.append(TR(_controls, _id=id + '2', _class='odd'))
|
||||||
return table
|
return table
|
||||||
|
|
||||||
|
|
||||||
def formstyle_divs(form, fields):
|
def formstyle_divs(form, fields):
|
||||||
''' divs only '''
|
''' divs only '''
|
||||||
table = TAG['']()
|
table = TAG['']()
|
||||||
@@ -689,10 +711,11 @@ def formstyle_divs(form, fields):
|
|||||||
table.append(DIV(_label, _controls, _help, _id=id))
|
table.append(DIV(_label, _controls, _help, _id=id))
|
||||||
return table
|
return table
|
||||||
|
|
||||||
|
|
||||||
def formstyle_inline(form, fields):
|
def formstyle_inline(form, fields):
|
||||||
''' divs only '''
|
''' divs only '''
|
||||||
if len(fields) != 2:
|
if len(fields) != 2:
|
||||||
raise RuntimeError, "Not possible"
|
raise RuntimeError("Not possible")
|
||||||
id, label, controls, help = fields[0]
|
id, label, controls, help = fields[0]
|
||||||
submit_button = fields[1][2]
|
submit_button = fields[1][2]
|
||||||
return CAT(DIV(controls, _style='display:inline'),
|
return CAT(DIV(controls, _style='display:inline'),
|
||||||
@@ -709,6 +732,7 @@ def formstyle_ul(form, fields):
|
|||||||
table.append(LI(_label, _controls, _help, _id=id))
|
table.append(LI(_label, _controls, _help, _id=id))
|
||||||
return table
|
return table
|
||||||
|
|
||||||
|
|
||||||
def formstyle_bootstrap(form, fields):
|
def formstyle_bootstrap(form, fields):
|
||||||
''' bootstrap format form layout '''
|
''' bootstrap format form layout '''
|
||||||
form['_class'] = 'form-horizontal'
|
form['_class'] = 'form-horizontal'
|
||||||
@@ -747,6 +771,7 @@ def formstyle_bootstrap(form, fields):
|
|||||||
parent.append(DIV(label, _controls, _class='control-group'))
|
parent.append(DIV(label, _controls, _class='control-group'))
|
||||||
return parent
|
return parent
|
||||||
|
|
||||||
|
|
||||||
class SQLFORM(FORM):
|
class SQLFORM(FORM):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -817,7 +842,6 @@ class SQLFORM(FORM):
|
|||||||
list=ListWidget,
|
list=ListWidget,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
formstyles = Storage(dict(
|
formstyles = Storage(dict(
|
||||||
table3cols=formstyle_table3cols,
|
table3cols=formstyle_table3cols,
|
||||||
table2cols=formstyle_table2cols,
|
table2cols=formstyle_table2cols,
|
||||||
@@ -921,7 +945,7 @@ class SQLFORM(FORM):
|
|||||||
|
|
||||||
self.record_id = record_id
|
self.record_id = record_id
|
||||||
if keyed:
|
if keyed:
|
||||||
self.record_id = dict([(k,record and str(record[k]) or None) \
|
self.record_id = dict([(k, record and str(record[k]) or None)
|
||||||
for k in table._primarykey])
|
for k in table._primarykey])
|
||||||
self.field_parent = {}
|
self.field_parent = {}
|
||||||
xfields = []
|
xfields = []
|
||||||
@@ -938,7 +962,7 @@ class SQLFORM(FORM):
|
|||||||
if not keyed:
|
if not keyed:
|
||||||
self.id_field_name = table._id.name
|
self.id_field_name = table._id.name
|
||||||
else:
|
else:
|
||||||
self.id_field_name = table._primarykey[0] ### only works if one key
|
self.id_field_name = table._primarykey[0] # only works if one key
|
||||||
|
|
||||||
sep = separator or ''
|
sep = separator or ''
|
||||||
|
|
||||||
@@ -985,7 +1009,6 @@ class SQLFORM(FORM):
|
|||||||
self.custom.widget.id = widget
|
self.custom.widget.id = widget
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
if readonly and not ignore_rw and not field.readable:
|
if readonly and not ignore_rw and not field.readable:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -1017,7 +1040,8 @@ class SQLFORM(FORM):
|
|||||||
elif field.type == 'upload':
|
elif field.type == 'upload':
|
||||||
inp = UploadWidget.represent(field, default, upload)
|
inp = UploadWidget.represent(field, default, upload)
|
||||||
elif field.type == 'boolean':
|
elif field.type == 'boolean':
|
||||||
inp = self.widgets.boolean.widget(field, default, _disabled=True)
|
inp = self.widgets.boolean.widget(
|
||||||
|
field, default, _disabled=True)
|
||||||
else:
|
else:
|
||||||
inp = field.formatter(default)
|
inp = field.formatter(default)
|
||||||
elif field.type == 'upload':
|
elif field.type == 'upload':
|
||||||
@@ -1059,7 +1083,8 @@ class SQLFORM(FORM):
|
|||||||
|
|
||||||
xfields.append((row_id, label, inp, comment))
|
xfields.append((row_id, label, inp, comment))
|
||||||
self.custom.dspval[fieldname] = dspval or nbsp
|
self.custom.dspval[fieldname] = dspval or nbsp
|
||||||
self.custom.inpval[fieldname] = inpval if not inpval is None else ''
|
self.custom.inpval[
|
||||||
|
fieldname] = inpval if not inpval is None else ''
|
||||||
self.custom.widget[fieldname] = inp
|
self.custom.widget[fieldname] = inp
|
||||||
|
|
||||||
# if a record is provided and found, as is linkto
|
# if a record is provided and found, as is linkto
|
||||||
@@ -1068,9 +1093,11 @@ class SQLFORM(FORM):
|
|||||||
db = linkto.split('/')[-1]
|
db = linkto.split('/')[-1]
|
||||||
for rfld in table._referenced_by:
|
for rfld in table._referenced_by:
|
||||||
if keyed:
|
if keyed:
|
||||||
query = urllib.quote('%s.%s==%s' % (db,rfld,record[rfld.type[10:].split('.')[1]]))
|
query = urllib.quote('%s.%s==%s' % (
|
||||||
|
db, rfld, record[rfld.type[10:].split('.')[1]]))
|
||||||
else:
|
else:
|
||||||
query = urllib.quote('%s.%s==%s' % (db,rfld,record[self.id_field_name]))
|
query = urllib.quote(
|
||||||
|
'%s.%s==%s' % (db, rfld, record[self.id_field_name]))
|
||||||
lname = olname = '%s.%s' % (rfld.tablename, rfld.name)
|
lname = olname = '%s.%s' % (rfld.tablename, rfld.name)
|
||||||
if ofields and not olname in ofields:
|
if ofields and not olname in ofields:
|
||||||
continue
|
continue
|
||||||
@@ -1079,7 +1106,8 @@ class SQLFORM(FORM):
|
|||||||
widget = A(lname,
|
widget = A(lname,
|
||||||
_class='reference',
|
_class='reference',
|
||||||
_href='%s/%s?query=%s' % (linkto, rfld.tablename, query))
|
_href='%s/%s?query=%s' % (linkto, rfld.tablename, query))
|
||||||
xfields.append((olname.replace('.', '__')+SQLFORM.ID_ROW_SUFFIX,
|
xfields.append(
|
||||||
|
(olname.replace('.', '__') + SQLFORM.ID_ROW_SUFFIX,
|
||||||
'', widget, col3.get(olname, '')))
|
'', widget, col3.get(olname, '')))
|
||||||
self.custom.linkto[olname.replace('.', '__')] = widget
|
self.custom.linkto[olname.replace('.', '__')] = widget
|
||||||
# </block>
|
# </block>
|
||||||
@@ -1092,7 +1120,8 @@ class SQLFORM(FORM):
|
|||||||
_id=self.FIELDKEY_DELETE_RECORD,
|
_id=self.FIELDKEY_DELETE_RECORD,
|
||||||
_name=self.FIELDNAME_REQUEST_DELETE,
|
_name=self.FIELDNAME_REQUEST_DELETE,
|
||||||
)
|
)
|
||||||
xfields.append((self.FIELDKEY_DELETE_RECORD+SQLFORM.ID_ROW_SUFFIX,
|
xfields.append(
|
||||||
|
(self.FIELDKEY_DELETE_RECORD + SQLFORM.ID_ROW_SUFFIX,
|
||||||
LABEL(
|
LABEL(
|
||||||
delete_label, separator,
|
delete_label, separator,
|
||||||
_for=self.FIELDKEY_DELETE_RECORD,
|
_for=self.FIELDKEY_DELETE_RECORD,
|
||||||
@@ -1133,7 +1162,7 @@ class SQLFORM(FORM):
|
|||||||
if formstyle in SQLFORM.formstyles:
|
if formstyle in SQLFORM.formstyles:
|
||||||
formstyle = SQLFORM.formstyles[formstyle]
|
formstyle = SQLFORM.formstyles[formstyle]
|
||||||
else:
|
else:
|
||||||
raise RuntimeError, 'formstyle not found'
|
raise RuntimeError('formstyle not found')
|
||||||
|
|
||||||
if callable(formstyle):
|
if callable(formstyle):
|
||||||
# backward compatibility, 4 argument function is the old style
|
# backward compatibility, 4 argument function is the old style
|
||||||
@@ -1152,7 +1181,7 @@ class SQLFORM(FORM):
|
|||||||
for id, a, b, c in xfields:
|
for id, a, b, c in xfields:
|
||||||
self.field_parent[id] = getattr(b, 'parent', None)
|
self.field_parent[id] = getattr(b, 'parent', None)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError, 'formstyle not supported'
|
raise RuntimeError('formstyle not supported')
|
||||||
return table
|
return table
|
||||||
|
|
||||||
def accepts(
|
def accepts(
|
||||||
@@ -1180,7 +1209,8 @@ class SQLFORM(FORM):
|
|||||||
if keepvalues is None:
|
if keepvalues is None:
|
||||||
keepvalues = True if self.record else False
|
keepvalues = True if self.record else False
|
||||||
|
|
||||||
if self.readonly: return False
|
if self.readonly:
|
||||||
|
return False
|
||||||
|
|
||||||
if request_vars.__class__.__name__ == 'Request':
|
if request_vars.__class__.__name__ == 'Request':
|
||||||
request_vars = request_vars.post_vars
|
request_vars = request_vars.post_vars
|
||||||
@@ -1194,7 +1224,8 @@ class SQLFORM(FORM):
|
|||||||
if self.detect_record_change:
|
if self.detect_record_change:
|
||||||
if self.record:
|
if self.record:
|
||||||
self.record_changed = False
|
self.record_changed = False
|
||||||
serialized = '|'.join(str(self.record[k]) for k in self.table.fields())
|
serialized = '|'.join(
|
||||||
|
str(self.record[k]) for k in self.table.fields())
|
||||||
self.record_hash = md5_hash(serialized)
|
self.record_hash = md5_hash(serialized)
|
||||||
|
|
||||||
# logic to deal with record_id for keyed tables
|
# logic to deal with record_id for keyed tables
|
||||||
@@ -1203,7 +1234,7 @@ class SQLFORM(FORM):
|
|||||||
formname_id = '.'.join(str(self.record[k])
|
formname_id = '.'.join(str(self.record[k])
|
||||||
for k in self.table._primarykey
|
for k in self.table._primarykey
|
||||||
if hasattr(self.record, k))
|
if hasattr(self.record, k))
|
||||||
record_id = dict((k, request_vars.get(k,None)) \
|
record_id = dict((k, request_vars.get(k, None))
|
||||||
for k in self.table._primarykey)
|
for k in self.table._primarykey)
|
||||||
else:
|
else:
|
||||||
(formname_id, record_id) = (self.record[self.id_field_name],
|
(formname_id, record_id) = (self.record[self.id_field_name],
|
||||||
@@ -1285,7 +1316,8 @@ class SQLFORM(FORM):
|
|||||||
value = self.table[fieldname].default
|
value = self.table[fieldname].default
|
||||||
if field.type.startswith('list:') and isinstance(value, str):
|
if field.type.startswith('list:') and isinstance(value, str):
|
||||||
value = [value]
|
value = [value]
|
||||||
row_id = '%s_%s%s' % (self.table, fieldname, SQLFORM.ID_ROW_SUFFIX)
|
row_id = '%s_%s%s' % (
|
||||||
|
self.table, fieldname, SQLFORM.ID_ROW_SUFFIX)
|
||||||
widget = field.widget(field, value)
|
widget = field.widget(field, value)
|
||||||
parent = self.field_parent[row_id]
|
parent = self.field_parent[row_id]
|
||||||
if parent:
|
if parent:
|
||||||
@@ -1296,8 +1328,8 @@ class SQLFORM(FORM):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
if record_id and str(record_id) != str(self.record_id):
|
if record_id and str(record_id) != str(self.record_id):
|
||||||
raise SyntaxError, 'user is tampering with form\'s record_id: ' \
|
raise SyntaxError('user is tampering with form\'s record_id: '
|
||||||
'%s != %s' % (record_id, self.record_id)
|
'%s != %s' % (record_id, self.record_id))
|
||||||
|
|
||||||
if record_id and dbio and not keyed:
|
if record_id and dbio and not keyed:
|
||||||
self.vars.id = self.record[self.id_field_name]
|
self.vars.id = self.record[self.id_field_name]
|
||||||
@@ -1306,7 +1338,7 @@ class SQLFORM(FORM):
|
|||||||
if dbio:
|
if dbio:
|
||||||
if keyed:
|
if keyed:
|
||||||
qry = reduce(lambda x, y: x & y,
|
qry = reduce(lambda x, y: x & y,
|
||||||
[self.table[k] == record_id[k] \
|
[self.table[k] == record_id[k]
|
||||||
for k in self.table._primarykey])
|
for k in self.table._primarykey])
|
||||||
else:
|
else:
|
||||||
qry = self.table._id == self.record[self.id_field_name]
|
qry = self.table._id == self.record[self.id_field_name]
|
||||||
@@ -1392,7 +1424,8 @@ class SQLFORM(FORM):
|
|||||||
fields[fieldname] = value and [value] or []
|
fields[fieldname] = value and [value] or []
|
||||||
elif isinstance(field.type, str) and field.type.startswith('list:'):
|
elif isinstance(field.type, str) and field.type.startswith('list:'):
|
||||||
if not isinstance(value, list):
|
if not isinstance(value, list):
|
||||||
fields[fieldname] = [safe_int(x) for x in (value and [value] or [])]
|
fields[fieldname] = [safe_int(
|
||||||
|
x) for x in (value and [value] or [])]
|
||||||
elif field.type == 'integer':
|
elif field.type == 'integer':
|
||||||
if not value is None:
|
if not value is None:
|
||||||
fields[fieldname] = safe_int(value)
|
fields[fieldname] = safe_int(value)
|
||||||
@@ -1436,7 +1469,8 @@ class SQLFORM(FORM):
|
|||||||
if record_id:
|
if record_id:
|
||||||
self.vars.id = self.record[self.id_field_name]
|
self.vars.id = self.record[self.id_field_name]
|
||||||
if fields:
|
if fields:
|
||||||
self.table._db(self.table._id == self.record[self.id_field_name]).update(**fields)
|
self.table._db(self.table._id == self.record[
|
||||||
|
self.id_field_name]).update(**fields)
|
||||||
else:
|
else:
|
||||||
self.vars.id = self.table.insert(**fields)
|
self.vars.id = self.table.insert(**fields)
|
||||||
self.accepted = ret
|
self.accepted = ret
|
||||||
@@ -1509,7 +1543,8 @@ class SQLFORM(FORM):
|
|||||||
key = keywords.strip()
|
key = keywords.strip()
|
||||||
if key and not ' ' in key and not '"' in key and not "'" in key:
|
if key and not ' ' in key and not '"' in key and not "'" in key:
|
||||||
SEARCHABLE_TYPES = ('string', 'text', 'list:string')
|
SEARCHABLE_TYPES = ('string', 'text', 'list:string')
|
||||||
parts = [field.contains(key) for field in fields if field.type in SEARCHABLE_TYPES]
|
parts = [field.contains(
|
||||||
|
key) for field in fields if field.type in SEARCHABLE_TYPES]
|
||||||
else:
|
else:
|
||||||
parts = None
|
parts = None
|
||||||
if parts:
|
if parts:
|
||||||
@@ -1543,7 +1578,8 @@ class SQLFORM(FORM):
|
|||||||
name = str(field).replace('.', '-')
|
name = str(field).replace('.', '-')
|
||||||
options = search_options.get(field.type.split(' ')[0], None)
|
options = search_options.get(field.type.split(' ')[0], None)
|
||||||
if options:
|
if options:
|
||||||
label = isinstance(field.label,str) and T(field.label) or field.label
|
label = isinstance(
|
||||||
|
field.label, str) and T(field.label) or field.label
|
||||||
selectfields.append(OPTION(label, _value=str(field)))
|
selectfields.append(OPTION(label, _value=str(field)))
|
||||||
operators = SELECT(*[T(option) for option in options])
|
operators = SELECT(*[T(option) for option in options])
|
||||||
if field.type == 'boolean':
|
if field.type == 'boolean':
|
||||||
@@ -1582,7 +1618,8 @@ class SQLFORM(FORM):
|
|||||||
*selectfields))
|
*selectfields))
|
||||||
|
|
||||||
fadd = SCRIPT("""
|
fadd = SCRIPT("""
|
||||||
jQuery('#w2p_query_panel input,#w2p_query_panel select').css('width','auto');
|
jQuery('#w2p_query_panel input,#w2p_query_panel select').css(
|
||||||
|
'width','auto');
|
||||||
jQuery(function(){web2py_ajax_fields('#w2p_query_panel');});
|
jQuery(function(){web2py_ajax_fields('#w2p_query_panel');});
|
||||||
function w2p_build_query(aggregator,a) {
|
function w2p_build_query(aggregator,a) {
|
||||||
var b=a.replace('.','-');
|
var b=a.replace('.','-');
|
||||||
@@ -1597,8 +1634,6 @@ class SQLFORM(FORM):
|
|||||||
return CAT(
|
return CAT(
|
||||||
DIV(_id="w2p_query_panel", _class='hidden', *criteria), fadd)
|
DIV(_id="w2p_query_panel", _class='hidden', *criteria), fadd)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def grid(query,
|
def grid(query,
|
||||||
fields=None,
|
fields=None,
|
||||||
@@ -1680,7 +1715,7 @@ class SQLFORM(FORM):
|
|||||||
buttonview='icon magnifier icon-zoom-in',
|
buttonview='icon magnifier icon-zoom-in',
|
||||||
)
|
)
|
||||||
elif not isinstance(ui, dict):
|
elif not isinstance(ui, dict):
|
||||||
raise RuntimeError,'SQLFORM.grid ui argument must be a dictionary'
|
raise RuntimeError('SQLFORM.grid ui argument must be a dictionary')
|
||||||
|
|
||||||
db = query._db
|
db = query._db
|
||||||
T = current.T
|
T = current.T
|
||||||
@@ -1749,14 +1784,15 @@ class SQLFORM(FORM):
|
|||||||
_class=trap_class(ui.get('buttontext'), trap))
|
_class=trap_class(ui.get('buttontext'), trap))
|
||||||
dbset = db(query)
|
dbset = db(query)
|
||||||
tablenames = db._adapter.tables(dbset.query)
|
tablenames = db._adapter.tables(dbset.query)
|
||||||
if left!=None: tablenames+=db._adapter.tables(left)
|
if left is not None:
|
||||||
|
tablenames += db._adapter.tables(left)
|
||||||
tables = [db[tablename] for tablename in tablenames]
|
tables = [db[tablename] for tablename in tablenames]
|
||||||
if not fields:
|
if not fields:
|
||||||
fields = reduce(lambda a, b: a + b,
|
fields = reduce(lambda a, b: a + b,
|
||||||
[[field for field in table] for table in tables])
|
[[field for field in table] for table in tables])
|
||||||
if not field_id:
|
if not field_id:
|
||||||
field_id = tables[0]._id
|
field_id = tables[0]._id
|
||||||
columns = [str(field) for field in fields \
|
columns = [str(field) for field in fields
|
||||||
if field._tablename in tablenames]
|
if field._tablename in tablenames]
|
||||||
|
|
||||||
if not str(field_id) in [str(f) for f in fields]:
|
if not str(field_id) in [str(f) for f in fields]:
|
||||||
@@ -1816,7 +1852,8 @@ class SQLFORM(FORM):
|
|||||||
table = db[request.args[-2]]
|
table = db[request.args[-2]]
|
||||||
record = table(request.args[-1]) or redirect(URL('error'))
|
record = table(request.args[-1]) or redirect(URL('error'))
|
||||||
sqlformargs.update(viewargs)
|
sqlformargs.update(viewargs)
|
||||||
view_form = SQLFORM(table, record, upload=upload, ignore_rw=ignore_rw,
|
view_form = SQLFORM(
|
||||||
|
table, record, upload=upload, ignore_rw=ignore_rw,
|
||||||
formstyle=formstyle, readonly=True, _class='web2py_form',
|
formstyle=formstyle, readonly=True, _class='web2py_form',
|
||||||
**sqlformargs)
|
**sqlformargs)
|
||||||
res = DIV(buttons(edit=editable, record=record), view_form,
|
res = DIV(buttons(edit=editable, record=record), view_form,
|
||||||
@@ -1862,7 +1899,7 @@ class SQLFORM(FORM):
|
|||||||
csv=(ExporterCSV, 'CSV'),
|
csv=(ExporterCSV, 'CSV'),
|
||||||
xml=(ExporterXML, 'XML'),
|
xml=(ExporterXML, 'XML'),
|
||||||
html=(ExporterHTML, 'HTML'),
|
html=(ExporterHTML, 'HTML'),
|
||||||
tsv_with_hidden_cols=\
|
tsv_with_hidden_cols=
|
||||||
(ExporterTSV, 'TSV (Excel compatible, hidden cols)'),
|
(ExporterTSV, 'TSV (Excel compatible, hidden cols)'),
|
||||||
tsv=(ExporterTSV, 'TSV (Excel compatible)'))
|
tsv=(ExporterTSV, 'TSV (Excel compatible)'))
|
||||||
if not exportclasses is None:
|
if not exportclasses is None:
|
||||||
@@ -1984,8 +2021,10 @@ class SQLFORM(FORM):
|
|||||||
if selectable:
|
if selectable:
|
||||||
head.append(TH(_class=ui.get('default')))
|
head.append(TH(_class=ui.get('default')))
|
||||||
for field in fields:
|
for field in fields:
|
||||||
if columns and not str(field) in columns: continue
|
if columns and not str(field) in columns:
|
||||||
if not field.readable: continue
|
continue
|
||||||
|
if not field.readable:
|
||||||
|
continue
|
||||||
key = str(field)
|
key = str(field)
|
||||||
header = headers.get(str(field), field.label or key)
|
header = headers.get(str(field), field.label or key)
|
||||||
if sortable:
|
if sortable:
|
||||||
@@ -2015,14 +2054,20 @@ class SQLFORM(FORM):
|
|||||||
paginator = UL()
|
paginator = UL()
|
||||||
if paginate and paginate < nrows:
|
if paginate and paginate < nrows:
|
||||||
npages, reminder = divmod(nrows, paginate)
|
npages, reminder = divmod(nrows, paginate)
|
||||||
if reminder: npages+=1
|
if reminder:
|
||||||
try: page = int(request.vars.page or 1)-1
|
npages += 1
|
||||||
except ValueError: page = 0
|
try:
|
||||||
|
page = int(request.vars.page or 1) - 1
|
||||||
|
except ValueError:
|
||||||
|
page = 0
|
||||||
limitby = (paginate * page, paginate * (page + 1))
|
limitby = (paginate * page, paginate * (page + 1))
|
||||||
|
|
||||||
def self_link(name, p):
|
def self_link(name, p):
|
||||||
d = dict(page=p + 1)
|
d = dict(page=p + 1)
|
||||||
if order: d['order']=order
|
if order:
|
||||||
if request.vars.keywords: d['keywords']=request.vars.keywords
|
d['order'] = order
|
||||||
|
if request.vars.keywords:
|
||||||
|
d['keywords'] = request.vars.keywords
|
||||||
return A(name, _href=url(vars=d), _class=trap_class())
|
return A(name, _href=url(vars=d), _class=trap_class())
|
||||||
NPAGES = 5 # window is 2*NPAGES
|
NPAGES = 5 # window is 2*NPAGES
|
||||||
if page > NPAGES + 1:
|
if page > NPAGES + 1:
|
||||||
@@ -2068,18 +2113,22 @@ class SQLFORM(FORM):
|
|||||||
id = row[field_id]
|
id = row[field_id]
|
||||||
if id:
|
if id:
|
||||||
rid = id
|
rid = id
|
||||||
if callable(rid): ### can this ever be callable?
|
if callable(rid): # can this ever be callable?
|
||||||
rid = rid(row)
|
rid = rid(row)
|
||||||
tr = TR(_id=rid, _class='%s %s' % (classtr, 'with_id'))
|
tr = TR(_id=rid, _class='%s %s' % (classtr, 'with_id'))
|
||||||
else:
|
else:
|
||||||
tr = TR(_class=classtr)
|
tr = TR(_class=classtr)
|
||||||
if selectable:
|
if selectable:
|
||||||
tr.append(INPUT(_type="checkbox",_name="records",_value=id,
|
tr.append(
|
||||||
|
INPUT(_type="checkbox", _name="records", _value=id,
|
||||||
value=request.vars.records))
|
value=request.vars.records))
|
||||||
for field in fields:
|
for field in fields:
|
||||||
if not str(field) in columns: continue
|
if not str(field) in columns:
|
||||||
if not field.readable: continue
|
continue
|
||||||
if field.type=='blob': continue
|
if not field.readable:
|
||||||
|
continue
|
||||||
|
if field.type == 'blob':
|
||||||
|
continue
|
||||||
value = row[field]
|
value = row[field]
|
||||||
maxlength = maxtextlengths.get(str(field), maxtextlength)
|
maxlength = maxtextlengths.get(str(field), maxtextlength)
|
||||||
if field.represent:
|
if field.represent:
|
||||||
@@ -2087,7 +2136,8 @@ class SQLFORM(FORM):
|
|||||||
value = field.represent(value, row)
|
value = field.represent(value, row)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
try:
|
try:
|
||||||
value=field.represent(value,row[field._tablename])
|
value = field.represent(
|
||||||
|
value, row[field._tablename])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
elif field.type == 'boolean':
|
elif field.type == 'boolean':
|
||||||
@@ -2096,7 +2146,8 @@ class SQLFORM(FORM):
|
|||||||
elif field.type == 'upload':
|
elif field.type == 'upload':
|
||||||
if value:
|
if value:
|
||||||
if callable(upload):
|
if callable(upload):
|
||||||
value = A(current.T('file'), _href=upload(value))
|
value = A(
|
||||||
|
current.T('file'), _href=upload(value))
|
||||||
elif upload:
|
elif upload:
|
||||||
value = A(current.T('file'),
|
value = A(current.T('file'),
|
||||||
_href='%s/%s' % (upload, value))
|
_href='%s/%s' % (upload, value))
|
||||||
@@ -2135,7 +2186,7 @@ class SQLFORM(FORM):
|
|||||||
htmltable = DIV(htmltable, _style='width:100%;overflow-x:auto')
|
htmltable = DIV(htmltable, _style='width:100%;overflow-x:auto')
|
||||||
if selectable:
|
if selectable:
|
||||||
htmltable = FORM(htmltable, INPUT(_type="submit"))
|
htmltable = FORM(htmltable, INPUT(_type="submit"))
|
||||||
if htmltable.process(formname=formname).accepted:#
|
if htmltable.process(formname=formname).accepted:
|
||||||
htmltable.vars.records = htmltable.vars.records or []
|
htmltable.vars.records = htmltable.vars.records or []
|
||||||
htmltable.vars.records = htmltable.vars.records if type(htmltable.vars.records) == list else [htmltable.vars.records]
|
htmltable.vars.records = htmltable.vars.records if type(htmltable.vars.records) == list else [htmltable.vars.records]
|
||||||
records = [int(r) for r in htmltable.vars.records]
|
records = [int(r) for r in htmltable.vars.records]
|
||||||
@@ -2164,7 +2215,8 @@ class SQLFORM(FORM):
|
|||||||
res.append(
|
res.append(
|
||||||
DIV(paginator,
|
DIV(paginator,
|
||||||
_class="web2py_paginator %(header)s %(cornerbottom)s" % ui))
|
_class="web2py_paginator %(header)s %(cornerbottom)s" % ui))
|
||||||
if export_menu: res.append(export_menu)
|
if export_menu:
|
||||||
|
res.append(export_menu)
|
||||||
res.create_form = create_form
|
res.create_form = create_form
|
||||||
res.update_form = update_form
|
res.update_form = update_form
|
||||||
res.view_form = view_form
|
res.view_form = view_form
|
||||||
@@ -2207,7 +2259,8 @@ class SQLFORM(FORM):
|
|||||||
to be linked
|
to be linked
|
||||||
"""
|
"""
|
||||||
request, T = current.request, current.T
|
request, T = current.request, current.T
|
||||||
if args is None: args = []
|
if args is None:
|
||||||
|
args = []
|
||||||
|
|
||||||
def url(**b):
|
def url(**b):
|
||||||
b['args'] = request.args[:nargs] + b.get('args', [])
|
b['args'] = request.args[:nargs] + b.get('args', [])
|
||||||
@@ -2219,8 +2272,10 @@ class SQLFORM(FORM):
|
|||||||
breadcrumbs = []
|
breadcrumbs = []
|
||||||
if request.args(len(args)) != table._tablename:
|
if request.args(len(args)) != table._tablename:
|
||||||
request.args[:] = args + [table._tablename]
|
request.args[:] = args + [table._tablename]
|
||||||
if links is None: links = {}
|
if links is None:
|
||||||
if constraints is None: constraints = {}
|
links = {}
|
||||||
|
if constraints is None:
|
||||||
|
constraints = {}
|
||||||
field = None
|
field = None
|
||||||
try:
|
try:
|
||||||
nargs = len(args) + 1
|
nargs = len(args) + 1
|
||||||
@@ -2239,7 +2294,8 @@ class SQLFORM(FORM):
|
|||||||
raise HTTP(400)
|
raise HTTP(400)
|
||||||
cond = constraints.get(referee, None)
|
cond = constraints.get(referee, None)
|
||||||
if cond:
|
if cond:
|
||||||
record = db(db[referee].id==id)(cond).select().first()
|
record = db(
|
||||||
|
db[referee].id == id)(cond).select().first()
|
||||||
else:
|
else:
|
||||||
record = db[referee](id)
|
record = db[referee](id)
|
||||||
if previous_id:
|
if previous_id:
|
||||||
@@ -2249,8 +2305,10 @@ class SQLFORM(FORM):
|
|||||||
tablename, fieldname, id
|
tablename, fieldname, id
|
||||||
try:
|
try:
|
||||||
format = db[referee]._format
|
format = db[referee]._format
|
||||||
if callable(format): name = format(record)
|
if callable(format):
|
||||||
else: name = format % record
|
name = format(record)
|
||||||
|
else:
|
||||||
|
name = format % record
|
||||||
except TypeError:
|
except TypeError:
|
||||||
name = id
|
name = id
|
||||||
nameLink = 'view'
|
nameLink = 'view'
|
||||||
@@ -2309,7 +2367,7 @@ class SQLFORM(FORM):
|
|||||||
T(tb._plural + '(' + fieldname + ')')
|
T(tb._plural + '(' + fieldname + ')')
|
||||||
args0 = tablename + '.' + fieldname
|
args0 = tablename + '.' + fieldname
|
||||||
links.append(
|
links.append(
|
||||||
lambda row,t=t,nargs=nargs,args0=args0:\
|
lambda row, t=t, nargs=nargs, args0=args0:
|
||||||
A(SPAN(t), _class=trap_class(), _href=url(
|
A(SPAN(t), _class=trap_class(), _href=url(
|
||||||
args=[args0, row[id_field_name]])))
|
args=[args0, row[id_field_name]])))
|
||||||
|
|
||||||
@@ -2320,7 +2378,8 @@ class SQLFORM(FORM):
|
|||||||
header = table._plural + (field and ' for ' + field.name or '')
|
header = table._plural + (field and ' for ' + field.name or '')
|
||||||
breadcrumbs.append(LI(A(T(header), _class=trap_class(),
|
breadcrumbs.append(LI(A(T(header), _class=trap_class(),
|
||||||
_href=url()), _class='active w2p_grid_breadcrumb_elem'))
|
_href=url()), _class='active w2p_grid_breadcrumb_elem'))
|
||||||
grid.insert(0,DIV(UL(*breadcrumbs, **{'_class':breadcrumbs_class}),
|
grid.insert(
|
||||||
|
0, DIV(UL(*breadcrumbs, **{'_class': breadcrumbs_class}),
|
||||||
_class='web2py_breadcrumbs'))
|
_class='web2py_breadcrumbs'))
|
||||||
return grid
|
return grid
|
||||||
|
|
||||||
@@ -2467,7 +2526,6 @@ class SQLTABLE(TABLE):
|
|||||||
|
|
||||||
components.append(THEAD(TR(*row)))
|
components.append(THEAD(TR(*row)))
|
||||||
|
|
||||||
|
|
||||||
tbody = []
|
tbody = []
|
||||||
for (rc, record) in enumerate(sqlrows):
|
for (rc, record) in enumerate(sqlrows):
|
||||||
row = []
|
row = []
|
||||||
@@ -2487,7 +2545,8 @@ class SQLTABLE(TABLE):
|
|||||||
row.append(TD(r))
|
row.append(TD(r))
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
raise KeyError("Column %s not found (SQLTABLE)" % colname)
|
raise KeyError(
|
||||||
|
"Column %s not found (SQLTABLE)" % colname)
|
||||||
(tablename, fieldname) = colname.split('.')
|
(tablename, fieldname) = colname.split('.')
|
||||||
try:
|
try:
|
||||||
field = sqlrows.db[tablename][fieldname]
|
field = sqlrows.db[tablename][fieldname]
|
||||||
@@ -2500,7 +2559,7 @@ class SQLTABLE(TABLE):
|
|||||||
elif fieldname in record:
|
elif fieldname in record:
|
||||||
r = record[fieldname]
|
r = record[fieldname]
|
||||||
else:
|
else:
|
||||||
raise SyntaxError, 'something wrong in Rows object'
|
raise SyntaxError('something wrong in Rows object')
|
||||||
r_old = r
|
r_old = r
|
||||||
if not field:
|
if not field:
|
||||||
pass
|
pass
|
||||||
@@ -2527,11 +2586,11 @@ class SQLTABLE(TABLE):
|
|||||||
elif linkto and hasattr(field._table, '_primarykey')\
|
elif linkto and hasattr(field._table, '_primarykey')\
|
||||||
and fieldname in field._table._primarykey:
|
and fieldname in field._table._primarykey:
|
||||||
# have to test this with multi-key tables
|
# have to test this with multi-key tables
|
||||||
key = urllib.urlencode(dict( [ \
|
key = urllib.urlencode(dict([
|
||||||
((tablename in record \
|
((tablename in record
|
||||||
and isinstance(record, Row) \
|
and isinstance(record, Row)
|
||||||
and isinstance(record[tablename], Row)) and
|
and isinstance(record[tablename], Row)) and
|
||||||
(k, record[tablename][k])) or (k, record[k]) \
|
(k, record[tablename][k])) or (k, record[k])
|
||||||
for k in field._table._primarykey]))
|
for k in field._table._primarykey]))
|
||||||
r = A(r, _href='%s/%s?%s' % (linkto, tablename, key))
|
r = A(r, _href='%s/%s?%s' % (linkto, tablename, key))
|
||||||
elif isinstance(field.type, str) and field.type.startswith('list:'):
|
elif isinstance(field.type, str) and field.type.startswith('list:'):
|
||||||
@@ -2552,7 +2611,8 @@ class SQLTABLE(TABLE):
|
|||||||
if headers != {}: # new implement dict
|
if headers != {}: # new implement dict
|
||||||
if isinstance(headers[colname], dict):
|
if isinstance(headers[colname], dict):
|
||||||
if isinstance(headers[colname]['truncate'], int):
|
if isinstance(headers[colname]['truncate'], int):
|
||||||
r = truncate_string(r, headers[colname]['truncate'])
|
r = truncate_string(
|
||||||
|
r, headers[colname]['truncate'])
|
||||||
elif not truncate is None:
|
elif not truncate is None:
|
||||||
r = truncate_string(r, truncate)
|
r = truncate_string(r, truncate)
|
||||||
attrcol = dict() # new implement dict
|
attrcol = dict() # new implement dict
|
||||||
@@ -2560,7 +2620,8 @@ class SQLTABLE(TABLE):
|
|||||||
if isinstance(headers[colname], dict):
|
if isinstance(headers[colname], dict):
|
||||||
colclass = headers[colname]['class']
|
colclass = headers[colname]['class']
|
||||||
if headers[colname]['selected']:
|
if headers[colname]['selected']:
|
||||||
colclass= str(headers[colname]['class'] + " colselected").strip()
|
colclass = str(headers[colname]
|
||||||
|
['class'] + " colselected").strip()
|
||||||
if colclass != "":
|
if colclass != "":
|
||||||
attrcol.update(_class=colclass)
|
attrcol.update(_class=colclass)
|
||||||
|
|
||||||
@@ -2584,7 +2645,6 @@ class SQLTABLE(TABLE):
|
|||||||
|
|
||||||
components.append(TBODY(*tbody))
|
components.append(TBODY(*tbody))
|
||||||
|
|
||||||
|
|
||||||
def style(self):
|
def style(self):
|
||||||
|
|
||||||
css = '''
|
css = '''
|
||||||
@@ -2662,6 +2722,7 @@ class ExportClass(object):
|
|||||||
def export(self):
|
def export(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class ExporterTSV(ExportClass):
|
class ExporterTSV(ExportClass):
|
||||||
|
|
||||||
label = 'TSV'
|
label = 'TSV'
|
||||||
@@ -2680,7 +2741,8 @@ class ExporterTSV(ExportClass):
|
|||||||
import codecs
|
import codecs
|
||||||
final.write(codecs.BOM_UTF16)
|
final.write(codecs.BOM_UTF16)
|
||||||
colnames = [a.split('.') for a in self.rows.colnames]
|
colnames = [a.split('.') for a in self.rows.colnames]
|
||||||
writer.writerow([unicode(col).encode("utf8") for col in self.rows.colnames])
|
writer.writerow(
|
||||||
|
[unicode(col).encode("utf8") for col in self.rows.colnames])
|
||||||
data = out.getvalue().decode("utf8")
|
data = out.getvalue().decode("utf8")
|
||||||
data = data.encode("utf-16")
|
data = data.encode("utf-16")
|
||||||
data = data[2:]
|
data = data[2:]
|
||||||
@@ -2688,7 +2750,8 @@ class ExporterTSV(ExportClass):
|
|||||||
out.truncate(0)
|
out.truncate(0)
|
||||||
records = self.represented()
|
records = self.represented()
|
||||||
for row in records:
|
for row in records:
|
||||||
writer.writerow([str(col).decode('utf8').encode("utf-8") for col in row])
|
writer.writerow(
|
||||||
|
[str(col).decode('utf8').encode("utf-8") for col in row])
|
||||||
data = out.getvalue().decode("utf8")
|
data = out.getvalue().decode("utf8")
|
||||||
data = data.encode("utf-16")
|
data = data.encode("utf-16")
|
||||||
data = data[2:]
|
data = data[2:]
|
||||||
@@ -2696,6 +2759,7 @@ class ExporterTSV(ExportClass):
|
|||||||
out.truncate(0)
|
out.truncate(0)
|
||||||
return str(final.getvalue())
|
return str(final.getvalue())
|
||||||
|
|
||||||
|
|
||||||
class ExporterCSV(ExportClass):
|
class ExporterCSV(ExportClass):
|
||||||
label = 'CSV'
|
label = 'CSV'
|
||||||
file_ext = "csv"
|
file_ext = "csv"
|
||||||
@@ -2707,6 +2771,7 @@ class ExporterCSV(ExportClass):
|
|||||||
def export(self):
|
def export(self):
|
||||||
return str(self.rows)
|
return str(self.rows)
|
||||||
|
|
||||||
|
|
||||||
class ExporterHTML(ExportClass):
|
class ExporterHTML(ExportClass):
|
||||||
label = 'HTML'
|
label = 'HTML'
|
||||||
file_ext = "html"
|
file_ext = "html"
|
||||||
@@ -2743,7 +2808,8 @@ class ExporterXML(ExportClass):
|
|||||||
for row in self.rows.records:
|
for row in self.rows.records:
|
||||||
out.write('<row>\n')
|
out.write('<row>\n')
|
||||||
for col in colnames:
|
for col in colnames:
|
||||||
out.write('<%s>'%col+str(row[col[0]][col[1]])+'</%s>\n'%col)
|
out.write(
|
||||||
|
'<%s>' % col + str(row[col[0]][col[1]]) + '</%s>\n' % col)
|
||||||
out.write('</row>\n')
|
out.write('</row>\n')
|
||||||
out.write('</rows>')
|
out.write('</rows>')
|
||||||
return str(out.getvalue())
|
return str(out.getvalue())
|
||||||
+24
-5
@@ -18,6 +18,7 @@ import portalocker
|
|||||||
__all__ = ['List', 'Storage', 'Settings', 'Messages',
|
__all__ = ['List', 'Storage', 'Settings', 'Messages',
|
||||||
'StorageList', 'load_storage', 'save_storage']
|
'StorageList', 'load_storage', 'save_storage']
|
||||||
|
|
||||||
|
|
||||||
class Storage(dict):
|
class Storage(dict):
|
||||||
"""
|
"""
|
||||||
A Storage object is like a dictionary except `obj.foo` can be used
|
A Storage object is like a dictionary except `obj.foo` can be used
|
||||||
@@ -117,12 +118,14 @@ class Storage(dict):
|
|||||||
|
|
||||||
PICKABLE = (str, int, long, float, bool, list, dict, tuple, set)
|
PICKABLE = (str, int, long, float, bool, list, dict, tuple, set)
|
||||||
|
|
||||||
|
|
||||||
class StorageList(Storage):
|
class StorageList(Storage):
|
||||||
"""
|
"""
|
||||||
like Storage but missing elements default to [] instead of None
|
like Storage but missing elements default to [] instead of None
|
||||||
"""
|
"""
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
return self.__getattr__(key)
|
return self.__getattr__(key)
|
||||||
|
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
if key in self:
|
if key in self:
|
||||||
return getattr(self, key)
|
return getattr(self, key)
|
||||||
@@ -131,40 +134,48 @@ class StorageList(Storage):
|
|||||||
setattr(self, key, r)
|
setattr(self, key, r)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def load_storage(filename):
|
def load_storage(filename):
|
||||||
fp = None
|
fp = None
|
||||||
try:
|
try:
|
||||||
fp = portalocker.LockedFile(filename, 'rb')
|
fp = portalocker.LockedFile(filename, 'rb')
|
||||||
storage = cPickle.load(fp)
|
storage = cPickle.load(fp)
|
||||||
finally:
|
finally:
|
||||||
if fp: fp.close()
|
if fp:
|
||||||
|
fp.close()
|
||||||
return Storage(storage)
|
return Storage(storage)
|
||||||
|
|
||||||
|
|
||||||
def save_storage(storage, filename):
|
def save_storage(storage, filename):
|
||||||
fp = None
|
fp = None
|
||||||
try:
|
try:
|
||||||
fp = portalocker.LockedFile(filename, 'wb')
|
fp = portalocker.LockedFile(filename, 'wb')
|
||||||
cPickle.dump(dict(storage), fp)
|
cPickle.dump(dict(storage), fp)
|
||||||
finally:
|
finally:
|
||||||
if fp: fp.close()
|
if fp:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
|
||||||
class Settings(Storage):
|
class Settings(Storage):
|
||||||
def __setattr__(self, key, value):
|
def __setattr__(self, key, value):
|
||||||
if key != 'lock_keys' and self['lock_keys'] and key not in self:
|
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']:
|
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
|
self[key] = value
|
||||||
|
|
||||||
|
|
||||||
class Messages(Settings):
|
class Messages(Settings):
|
||||||
def __init__(self, T):
|
def __init__(self, T):
|
||||||
Storage.__init__(self, T=T)
|
Storage.__init__(self, T=T)
|
||||||
|
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
value = self[key]
|
value = self[key]
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
return str(self.T(value))
|
return str(self.T(value))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
class FastStorage(dict):
|
class FastStorage(dict):
|
||||||
"""
|
"""
|
||||||
Eventually this should replace class Storage but causes memory leak
|
Eventually this should replace class Storage but causes memory leak
|
||||||
@@ -203,26 +214,34 @@ class FastStorage(dict):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
dict.__init__(self, *args, **kwargs)
|
dict.__init__(self, *args, **kwargs)
|
||||||
self.__dict__ = self
|
self.__dict__ = self
|
||||||
|
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
return getattr(self, key) if key in self else None
|
return getattr(self, key) if key in self else None
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
return dict.get(self, key, None)
|
return dict.get(self, key, None)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
self.__dict__ = {}
|
self.__dict__ = {}
|
||||||
s = FastStorage(self)
|
s = FastStorage(self)
|
||||||
self.__dict__ = self
|
self.__dict__ = self
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Storage %s>' % dict.__repr__(self)
|
return '<Storage %s>' % dict.__repr__(self)
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
return dict(self)
|
return dict(self)
|
||||||
|
|
||||||
def __setstate__(self, sdict):
|
def __setstate__(self, sdict):
|
||||||
dict.__init__(self, sdict)
|
dict.__init__(self, sdict)
|
||||||
self.__dict__ = self
|
self.__dict__ = self
|
||||||
|
|
||||||
def update(self, *args, **kwargs):
|
def update(self, *args, **kwargs):
|
||||||
dict.__init__(self, *args, **kwargs)
|
dict.__init__(self, *args, **kwargs)
|
||||||
self.__dict__ = self
|
self.__dict__ = self
|
||||||
|
|
||||||
|
|
||||||
class List(list):
|
class List(list):
|
||||||
"""
|
"""
|
||||||
Like a regular python list but a[i] if i is out of bounds return None
|
Like a regular python list but a[i] if i is out of bounds return None
|
||||||
@@ -251,7 +270,7 @@ class List(list):
|
|||||||
elif callable(otherwise):
|
elif callable(otherwise):
|
||||||
return otherwise()
|
return otherwise()
|
||||||
else:
|
else:
|
||||||
raise RuntimeError, "invalid otherwise"
|
raise RuntimeError("invalid otherwise")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ 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
|
offset = 0
|
||||||
while bytes is None or offset < bytes:
|
while bytes is None or offset < bytes:
|
||||||
@@ -38,6 +39,7 @@ def streamer(stream, chunk_size = DEFAULT_CHUNK_SIZE, bytes = None):
|
|||||||
offset += length
|
offset += length
|
||||||
stream.close()
|
stream.close()
|
||||||
|
|
||||||
|
|
||||||
def stream_file_or_304_or_206(
|
def stream_file_or_304_or_206(
|
||||||
static_file,
|
static_file,
|
||||||
chunk_size=DEFAULT_CHUNK_SIZE,
|
chunk_size=DEFAULT_CHUNK_SIZE,
|
||||||
|
|||||||
+34
-12
@@ -26,10 +26,12 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
# do not have web2py
|
# do not have web2py
|
||||||
current = None
|
current = None
|
||||||
|
|
||||||
def RestrictedError(a, b, c):
|
def RestrictedError(a, b, c):
|
||||||
logging.error(str(a) + ':' + str(b) + ':' + str(c))
|
logging.error(str(a) + ':' + str(b) + ':' + str(c))
|
||||||
return RuntimeError
|
return RuntimeError
|
||||||
|
|
||||||
|
|
||||||
class Node(object):
|
class Node(object):
|
||||||
"""
|
"""
|
||||||
Basic Container Object
|
Basic Container Object
|
||||||
@@ -41,6 +43,7 @@ class Node(object):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.value)
|
return str(self.value)
|
||||||
|
|
||||||
|
|
||||||
class SuperNode(Node):
|
class SuperNode(Node):
|
||||||
def __init__(self, name='', pre_extend=False):
|
def __init__(self, name='', pre_extend=False):
|
||||||
self.name = name
|
self.name = name
|
||||||
@@ -57,6 +60,7 @@ class SuperNode(Node):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s->%s" % (self.name, self.value)
|
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 have a block level
|
||||||
# If we can override this block.
|
# If we can override this block.
|
||||||
@@ -69,6 +73,7 @@ def output_aux(node,blocks):
|
|||||||
if isinstance(node, BlockNode) \
|
if isinstance(node, BlockNode) \
|
||||||
else str(node)
|
else str(node)
|
||||||
|
|
||||||
|
|
||||||
class BlockNode(Node):
|
class BlockNode(Node):
|
||||||
"""
|
"""
|
||||||
Block Container.
|
Block Container.
|
||||||
@@ -101,7 +106,7 @@ class BlockNode(Node):
|
|||||||
"""
|
"""
|
||||||
Get this BlockNodes content, not including child Nodes
|
Get this BlockNodes content, not including child Nodes
|
||||||
"""
|
"""
|
||||||
return ''.join(str(node) for node in self.nodes \
|
return ''.join(str(node) for node in self.nodes
|
||||||
if not isinstance(node, BlockNode))
|
if not isinstance(node, BlockNode))
|
||||||
|
|
||||||
def append(self, node):
|
def append(self, node):
|
||||||
@@ -128,8 +133,8 @@ class BlockNode(Node):
|
|||||||
if isinstance(other, BlockNode):
|
if isinstance(other, BlockNode):
|
||||||
self.nodes.extend(other.nodes)
|
self.nodes.extend(other.nodes)
|
||||||
else:
|
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):
|
def output(self, blocks):
|
||||||
"""
|
"""
|
||||||
@@ -139,6 +144,7 @@ class BlockNode(Node):
|
|||||||
"""
|
"""
|
||||||
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):
|
class Content(BlockNode):
|
||||||
"""
|
"""
|
||||||
Parent Container -- Used as the root level BlockNode.
|
Parent Container -- Used as the root level BlockNode.
|
||||||
@@ -166,7 +172,8 @@ class Content(BlockNode):
|
|||||||
if isinstance(other, (str, Node)):
|
if isinstance(other, (str, Node)):
|
||||||
self.nodes.insert(index, other)
|
self.nodes.insert(index, other)
|
||||||
else:
|
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):
|
||||||
"""
|
"""
|
||||||
@@ -201,11 +208,13 @@ class Content(BlockNode):
|
|||||||
self.nodes.extend(other.nodes)
|
self.nodes.extend(other.nodes)
|
||||||
self.blocks.update(other.blocks)
|
self.blocks.update(other.blocks)
|
||||||
else:
|
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):
|
def clear_content(self):
|
||||||
self.nodes = []
|
self.nodes = []
|
||||||
|
|
||||||
|
|
||||||
class TemplateParser(object):
|
class TemplateParser(object):
|
||||||
|
|
||||||
default_delimiters = ('{{', '}}')
|
default_delimiters = ('{{', '}}')
|
||||||
@@ -474,7 +483,8 @@ class TemplateParser(object):
|
|||||||
|
|
||||||
# Make a temporary buffer that is unique for parent
|
# Make a temporary buffer that is unique for parent
|
||||||
# template.
|
# template.
|
||||||
buf = BlockNode(name='__include__' + filename, delimiters=self.delimiters)
|
buf = BlockNode(
|
||||||
|
name='__include__' + filename, delimiters=self.delimiters)
|
||||||
pre = []
|
pre = []
|
||||||
|
|
||||||
# Iterate through each of our nodes
|
# Iterate through each of our nodes
|
||||||
@@ -663,7 +673,8 @@ class TemplateParser(object):
|
|||||||
# Otherwise, make a temporary include node
|
# Otherwise, make a temporary include node
|
||||||
# That the child node will know to hook into.
|
# That the child node will know to hook into.
|
||||||
else:
|
else:
|
||||||
include_node = BlockNode(name = '__include__' + self.name,
|
include_node = BlockNode(
|
||||||
|
name='__include__' + self.name,
|
||||||
pre_extend=pre_extend,
|
pre_extend=pre_extend,
|
||||||
delimiters=self.delimiters)
|
delimiters=self.delimiters)
|
||||||
top.append(include_node)
|
top.append(include_node)
|
||||||
@@ -697,14 +708,15 @@ class TemplateParser(object):
|
|||||||
if token.startswith('='):
|
if token.startswith('='):
|
||||||
if token.endswith('\\'):
|
if token.endswith('\\'):
|
||||||
continuation = True
|
continuation = True
|
||||||
tokens[k] = "\n%s(%s" % (self.writer, token[1:].strip())
|
tokens[k] = "\n%s(%s" % (
|
||||||
|
self.writer, token[1:].strip())
|
||||||
else:
|
else:
|
||||||
tokens[k] = "\n%s(%s)" % (self.writer, token[1:].strip())
|
tokens[k] = "\n%s(%s)" % (
|
||||||
|
self.writer, token[1:].strip())
|
||||||
elif continuation:
|
elif continuation:
|
||||||
tokens[k] += ')'
|
tokens[k] += ')'
|
||||||
continuation = False
|
continuation = False
|
||||||
|
|
||||||
|
|
||||||
buf = "\n%s" % '\n'.join(tokens)
|
buf = "\n%s" % '\n'.join(tokens)
|
||||||
top.append(Node(buf, pre_extend=pre_extend))
|
top.append(Node(buf, pre_extend=pre_extend))
|
||||||
|
|
||||||
@@ -740,6 +752,8 @@ class TemplateParser(object):
|
|||||||
self.extend(extend)
|
self.extend(extend)
|
||||||
|
|
||||||
# We need this for integration with gluon
|
# We need this for integration with gluon
|
||||||
|
|
||||||
|
|
||||||
def parse_template(filename,
|
def parse_template(filename,
|
||||||
path='views/',
|
path='views/',
|
||||||
context=dict(),
|
context=dict(),
|
||||||
@@ -766,6 +780,7 @@ def parse_template(filename,
|
|||||||
# Use the file contents to get a parsed template and return it.
|
# Use the file contents to get a parsed template and return it.
|
||||||
return str(TemplateParser(text, context=context, path=path, lexers=lexers, delimiters=delimiters))
|
return str(TemplateParser(text, context=context, path=path, lexers=lexers, delimiters=delimiters))
|
||||||
|
|
||||||
|
|
||||||
def get_parsed(text):
|
def get_parsed(text):
|
||||||
"""
|
"""
|
||||||
Returns the indented python code of text. Useful for unit testing.
|
Returns the indented python code of text. Useful for unit testing.
|
||||||
@@ -773,9 +788,11 @@ def get_parsed(text):
|
|||||||
"""
|
"""
|
||||||
return str(TemplateParser(text))
|
return str(TemplateParser(text))
|
||||||
|
|
||||||
|
|
||||||
class DummyResponse():
|
class DummyResponse():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.body = cStringIO.StringIO()
|
self.body = cStringIO.StringIO()
|
||||||
|
|
||||||
def write(self, data, escape=True):
|
def write(self, data, escape=True):
|
||||||
if not escape:
|
if not escape:
|
||||||
self.body.write(str(data))
|
self.body.write(str(data))
|
||||||
@@ -790,17 +807,21 @@ class DummyResponse():
|
|||||||
data = cgi.escape(data, True).replace("'", "'")
|
data = cgi.escape(data, True).replace("'", "'")
|
||||||
self.body.write(data)
|
self.body.write(data)
|
||||||
|
|
||||||
|
|
||||||
class NOESCAPE():
|
class NOESCAPE():
|
||||||
"""
|
"""
|
||||||
A little helper to avoid escaping.
|
A little helper to avoid escaping.
|
||||||
"""
|
"""
|
||||||
def __init__(self, text):
|
def __init__(self, text):
|
||||||
self.text = text
|
self.text = text
|
||||||
|
|
||||||
def xml(self):
|
def xml(self):
|
||||||
return self.text
|
return self.text
|
||||||
|
|
||||||
# And this is a generic render function.
|
# And this is a generic render function.
|
||||||
# Here for integration with gluon.
|
# Here for integration with gluon.
|
||||||
|
|
||||||
|
|
||||||
def render(content="hello world",
|
def render(content="hello world",
|
||||||
stream=None,
|
stream=None,
|
||||||
filename=None,
|
filename=None,
|
||||||
@@ -856,7 +877,7 @@ def render(content = "hello world",
|
|||||||
|
|
||||||
# If we don't have anything to render, why bother?
|
# If we don't have anything to render, why bother?
|
||||||
if not content and not stream and not filename:
|
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
|
# Here for legacy purposes, probably can be reduced to
|
||||||
# something more simple.
|
# something more simple.
|
||||||
@@ -869,7 +890,8 @@ def render(content = "hello world",
|
|||||||
stream = cStringIO.StringIO(content)
|
stream = cStringIO.StringIO(content)
|
||||||
|
|
||||||
# Execute the template.
|
# 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:
|
try:
|
||||||
exec(code) in context
|
exec(code) in context
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from cache import CacheInRam, CacheOnDisk
|
|||||||
|
|
||||||
oldcwd = None
|
oldcwd = None
|
||||||
|
|
||||||
|
|
||||||
def setUpModule():
|
def setUpModule():
|
||||||
global oldcwd
|
global oldcwd
|
||||||
if oldcwd is None:
|
if oldcwd is None:
|
||||||
@@ -25,12 +26,14 @@ def setUpModule():
|
|||||||
if not os.path.isdir('gluon'):
|
if not os.path.isdir('gluon'):
|
||||||
os.chdir(os.path.realpath('../../'))
|
os.chdir(os.path.realpath('../../'))
|
||||||
|
|
||||||
|
|
||||||
def tearDownModule():
|
def tearDownModule():
|
||||||
global oldcwd
|
global oldcwd
|
||||||
if oldcwd:
|
if oldcwd:
|
||||||
os.chdir(oldcwd)
|
os.chdir(oldcwd)
|
||||||
oldcwd = None
|
oldcwd = None
|
||||||
|
|
||||||
|
|
||||||
class TestCache(unittest.TestCase):
|
class TestCache(unittest.TestCase):
|
||||||
|
|
||||||
def testCacheInRam(self):
|
def testCacheInRam(self):
|
||||||
@@ -70,5 +73,3 @@ if __name__ == '__main__':
|
|||||||
setUpModule() # pre-python-2.7
|
setUpModule() # pre-python-2.7
|
||||||
unittest.main()
|
unittest.main()
|
||||||
tearDownModule()
|
tearDownModule()
|
||||||
|
|
||||||
|
|
||||||
@@ -22,7 +22,8 @@ class TestContribs(unittest.TestCase):
|
|||||||
def test_fpdf(self):
|
def test_fpdf(self):
|
||||||
""" Basic PDF test and sanity checks """
|
""" 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')
|
self.assertEqual(fpdf.FPDF, pyfpdf.FPDF, 'class mistmatch')
|
||||||
|
|
||||||
pdf = fpdf.FPDF()
|
pdf = fpdf.FPDF()
|
||||||
@@ -39,4 +40,3 @@ class TestContribs(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ class TestBareHelpers(unittest.TestCase):
|
|||||||
|
|
||||||
def testOPTION(self):
|
def testOPTION(self):
|
||||||
self.assertEqual(OPTION('<>', _a='1', _b='2').xml(),
|
self.assertEqual(OPTION('<>', _a='1', _b='2').xml(),
|
||||||
'<option a="1" b="2" value="<>"><>' + \
|
'<option a="1" b="2" value="<>"><>' +
|
||||||
'</option>')
|
'</option>')
|
||||||
|
|
||||||
def testP(self):
|
def testP(self):
|
||||||
@@ -149,7 +149,7 @@ class TestBareHelpers(unittest.TestCase):
|
|||||||
|
|
||||||
def testSELECT(self):
|
def testSELECT(self):
|
||||||
self.assertEqual(SELECT('<>', _a='1', _b='2').xml(),
|
self.assertEqual(SELECT('<>', _a='1', _b='2').xml(),
|
||||||
'<select a="1" b="2">'+ \
|
'<select a="1" b="2">' +
|
||||||
'<option value="<>"><></option></select>')
|
'<option value="<>"><></option></select>')
|
||||||
|
|
||||||
def testSPAN(self):
|
def testSPAN(self):
|
||||||
@@ -162,7 +162,7 @@ class TestBareHelpers(unittest.TestCase):
|
|||||||
|
|
||||||
def testTABLE(self):
|
def testTABLE(self):
|
||||||
self.assertEqual(TABLE('<>', _a='1', _b='2').xml(),
|
self.assertEqual(TABLE('<>', _a='1', _b='2').xml(),
|
||||||
'<table a="1" b="2"><tr><td><></td></tr>' + \
|
'<table a="1" b="2"><tr><td><></td></tr>' +
|
||||||
'</table>')
|
'</table>')
|
||||||
|
|
||||||
def testTBODY(self):
|
def testTBODY(self):
|
||||||
@@ -175,7 +175,7 @@ class TestBareHelpers(unittest.TestCase):
|
|||||||
|
|
||||||
def testTEXTAREA(self):
|
def testTEXTAREA(self):
|
||||||
self.assertEqual(TEXTAREA('<>', _a='1', _b='2').xml(),
|
self.assertEqual(TEXTAREA('<>', _a='1', _b='2').xml(),
|
||||||
'<textarea a="1" b="2" cols="40" rows="10"><>' + \
|
'<textarea a="1" b="2" cols="40" rows="10"><>' +
|
||||||
'</textarea>')
|
'</textarea>')
|
||||||
|
|
||||||
def testTFOOT(self):
|
def testTFOOT(self):
|
||||||
@@ -209,4 +209,3 @@ class TestBareHelpers(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
+77
-47
@@ -31,7 +31,8 @@ class TestIsUrl(unittest.TestCase):
|
|||||||
self.assertEqual(x('unreal.blargg'), ('unreal.blargg',
|
self.assertEqual(x('unreal.blargg'), ('unreal.blargg',
|
||||||
'enter a valid URL'))
|
'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'))
|
self.assertEqual(
|
||||||
|
x('google.ca..'), ('google.ca..', 'enter a valid URL'))
|
||||||
|
|
||||||
# explicit use of 'http' mode
|
# explicit use of 'http' mode
|
||||||
|
|
||||||
@@ -194,7 +195,8 @@ class TestIsUrl(unittest.TestCase):
|
|||||||
!= "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))
|
self.fail('Wrong exception: ' + str(e))
|
||||||
else:
|
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'
|
# prepend_scheme's value must be in allowed_schemes (default for 'http'
|
||||||
# mode is [None, 'http', 'https'])
|
# mode is [None, 'http', 'https'])
|
||||||
@@ -251,7 +253,8 @@ class TestIsUrl(unittest.TestCase):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
x = IS_URL(mode='generic', prepend_scheme='blargg')
|
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:
|
except Exception, e:
|
||||||
if not str(e).startswith(
|
if not str(e).startswith(
|
||||||
"prepend_scheme='blargg' is not in allowed_schemes="):
|
"prepend_scheme='blargg' is not in allowed_schemes="):
|
||||||
@@ -340,7 +343,7 @@ class TestIsGenericUrl(unittest.TestCase):
|
|||||||
failures = []
|
failures = []
|
||||||
|
|
||||||
for url in urlsToCheckA + urlsToCheckB:
|
for url in urlsToCheckA + urlsToCheckB:
|
||||||
if self.x(url)[1] == None:
|
if self.x(url)[1] is None:
|
||||||
failures.append('Incorrectly accepted: ' + str(url))
|
failures.append('Incorrectly accepted: ' + str(url))
|
||||||
|
|
||||||
if len(failures) > 0:
|
if len(failures) > 0:
|
||||||
@@ -377,14 +380,13 @@ class TestIsGenericUrl(unittest.TestCase):
|
|||||||
'http://localhost:8080/',
|
'http://localhost:8080/',
|
||||||
'http://localhost:8080/hello',
|
'http://localhost:8080/hello',
|
||||||
'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 = []
|
failures = []
|
||||||
|
|
||||||
for url in urlsToCheck:
|
for url in urlsToCheck:
|
||||||
if self.x(url)[1] != None:
|
if self.x(url)[1] is not None:
|
||||||
failures.append('Incorrectly rejected: ' + str(url))
|
failures.append('Incorrectly rejected: ' + str(url))
|
||||||
|
|
||||||
if len(failures) > 0:
|
if len(failures) > 0:
|
||||||
@@ -464,7 +466,7 @@ class TestIsHttpUrl(unittest.TestCase):
|
|||||||
failures = []
|
failures = []
|
||||||
|
|
||||||
for url in urlsToCheck:
|
for url in urlsToCheck:
|
||||||
if self.x(url)[1] == None:
|
if self.x(url)[1] is None:
|
||||||
failures.append('Incorrectly accepted: ' + str(url))
|
failures.append('Incorrectly accepted: ' + str(url))
|
||||||
|
|
||||||
if len(failures) > 0:
|
if len(failures) > 0:
|
||||||
@@ -523,8 +525,7 @@ class TestIsHttpUrl(unittest.TestCase):
|
|||||||
'HTTPS://localhost.',
|
'HTTPS://localhost.',
|
||||||
'http://localhost#fragment',
|
'http://localhost#fragment',
|
||||||
'http://localhost/hello;param=value',
|
'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://localhost/hello?query=True',
|
||||||
'http://www.benn.ca/hello;param=value/hi;param2=value2;param3=value3/index.html?query=3',
|
'http://www.benn.ca/hello;param=value/hi;param2=value2;param3=value3/index.html?query=3',
|
||||||
'http://localhost/hello/?query=1500&five=6',
|
'http://localhost/hello/?query=1500&five=6',
|
||||||
@@ -539,7 +540,7 @@ class TestIsHttpUrl(unittest.TestCase):
|
|||||||
failures = []
|
failures = []
|
||||||
|
|
||||||
for url in urlsToCheck:
|
for url in urlsToCheck:
|
||||||
if self.x(url)[1] != None:
|
if self.x(url)[1] is not None:
|
||||||
failures.append('Incorrectly rejected: ' + str(url))
|
failures.append('Incorrectly rejected: ' + str(url))
|
||||||
|
|
||||||
if len(failures) > 0:
|
if len(failures) > 0:
|
||||||
@@ -557,11 +558,14 @@ class TestIsHttpUrl(unittest.TestCase):
|
|||||||
self.assertEqual(self.x('https://google.ca'),
|
self.assertEqual(self.x('https://google.ca'),
|
||||||
('https://google.ca', None))
|
('https://google.ca', None))
|
||||||
|
|
||||||
y = IS_HTTP_URL(prepend_scheme='https', allowed_schemes=[None, 'https'])
|
y = IS_HTTP_URL(
|
||||||
self.assertEqual(y('google.ca'), ('https://google.ca', None)) # prepends https if asked
|
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)
|
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:
|
try:
|
||||||
IS_HTTP_URL(prepend_scheme='mailto')
|
IS_HTTP_URL(prepend_scheme='mailto')
|
||||||
@@ -579,67 +583,93 @@ class TestIsHttpUrl(unittest.TestCase):
|
|||||||
self.assertEqual(a('google.ca:80'), ('google.ca:80',
|
self.assertEqual(a('google.ca:80'), ('google.ca:80',
|
||||||
'enter a valid URL'))
|
'enter a valid URL'))
|
||||||
|
|
||||||
|
|
||||||
class TestUnicode(unittest.TestCase):
|
class TestUnicode(unittest.TestCase):
|
||||||
x = IS_URL()
|
x = IS_URL()
|
||||||
y = IS_URL(allowed_schemes=['https'], prepend_scheme='https') #excludes the option for abbreviated URLs with no scheme
|
y = IS_URL(allowed_schemes=['https'], prepend_scheme='https')
|
||||||
z = IS_URL(prepend_scheme=None) # disables prepending the scheme in the return value
|
#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):
|
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.Alliancefran\xe7aise.nu'), 'www.xn--alliancefranaise-npb.nu')
|
||||||
self.assertEquals(unicode_to_ascii_authority(u'www.benn.ca'), 'www.benn.ca')
|
self.assertEquals(
|
||||||
self.assertRaises(UnicodeError, unicode_to_ascii_authority, u'\u4e2d'*1000) #label is too long
|
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):
|
def testValidUrls(self):
|
||||||
self.assertEquals(self.x(u'www.Alliancefrancaise.nu'), ('http://www.Alliancefrancaise.nu', None))
|
self.assertEquals(self.x(u'www.Alliancefrancaise.nu'), (
|
||||||
self.assertEquals(self.x(u'www.Alliancefran\xe7aise.nu'), ('http://www.xn--alliancefranaise-npb.nu', None))
|
'http://www.Alliancefrancaise.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'www.Alliancefran\xe7aise.nu'), (
|
||||||
self.assertEquals(self.x(u'http://www.Alliancefran\xe7aise.nu'), ('http://www.xn--alliancefranaise-npb.nu', None))
|
'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'), ('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#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/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'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'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(
|
||||||
self.assertEquals(self.x(u'http://\u4e2d\u4fd4.com/\u4e86'), ('http://xn--fiq13b.com/%4e%86', None))
|
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'), ('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/\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://\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.assertEquals(self.x(u'http://\xe4\xf6\xfc\xdf.com'), ('http://xn--ss-uia6e4a.com', None))
|
self.x(u'http://B\xfccher.ch'), ('http://xn--bcher-kva.ch', None))
|
||||||
self.assertEquals(self.x(u'http://visegr\xe1d.com'), ('http://xn--visegrd-mwa.com', None))
|
self.assertEquals(self.x(u'http://\xe4\xf6\xfc\xdf.com'), (
|
||||||
self.assertEquals(self.x(u'http://h\xe1zipatika.com'), ('http://xn--hzipatika-01a.com', None))
|
'http://xn--ss-uia6e4a.com', None))
|
||||||
self.assertEquals(self.x(u'http://www.\xe7ukurova.com'), ('http://www.xn--ukurova-txa.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'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.x(u'google.ca.'), ('http://google.ca.', None))
|
||||||
|
|
||||||
self.assertEquals(self.y(u'https://google.ca'), ('https://google.ca', None))
|
self.assertEquals(
|
||||||
self.assertEquals(self.y(u'https://\u4e2d\u4fd4.com'), ('https://xn--fiq13b.com', None))
|
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))
|
self.assertEquals(self.z(u'google.ca'), ('google.ca', None))
|
||||||
|
|
||||||
|
|
||||||
def testInvalidUrls(self):
|
def testInvalidUrls(self):
|
||||||
self.assertEquals(self.x(u'://ABC.com'), (u'://ABC.com', 'enter a valid URL'))
|
self.assertEquals(
|
||||||
self.assertEquals(self.x(u'http://\u4e2d\u4fd4.dne'), (u'http://\u4e2d\u4fd4.dne', 'enter a valid URL'))
|
self.x(u'://ABC.com'), (u'://ABC.com', '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'http://\u4e2d\u4fd4.dne'), (
|
||||||
self.assertEquals(self.x(u'https://google..ca'), (u'https://google..ca', 'enter a valid URL'))
|
u'http://\u4e2d\u4fd4.dne', 'enter a valid URL'))
|
||||||
self.assertEquals(self.x(u'google..ca'), (u'google..ca', 'enter a valid URL'))
|
self.assertEquals(self.x(u'https://google.dne'), (
|
||||||
self.assertEquals(self.x(u'http://' + u'\u4e2d'*1000 + u'.com'), (u'http://' + u'\u4e2d'*1000 + u'.com', 'enter a valid URL'))
|
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://google.com#fragment_\u4e86'), (
|
||||||
self.assertEquals(self.x(u'http\u4e86://google.com'), (u'http\u4e86://google.com', 'enter a valid URL'))
|
u'http://google.com#fragment_\u4e86', '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\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.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.domain..com'), (
|
||||||
self.assertEquals(self.z(u'invalid.\u4e2d\u4fd4.blargg'), (u'invalid.\u4e2d\u4fd4.blargg', 'enter a valid URL'))
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
@@ -54,7 +54,6 @@ try:
|
|||||||
for result in results:
|
for result in results:
|
||||||
self.assertTrue(result)
|
self.assertTrue(result)
|
||||||
|
|
||||||
|
|
||||||
class TestTranslations(unittest.TestCase):
|
class TestTranslations(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -62,11 +61,11 @@ try:
|
|||||||
if os.path.isdir('gluon'):
|
if os.path.isdir('gluon'):
|
||||||
self.request.folder = 'applications/welcome'
|
self.request.folder = 'applications/welcome'
|
||||||
else:
|
else:
|
||||||
self.request.folder = os.path.realpath('../../applications/welcome')
|
self.request.folder = os.path.realpath(
|
||||||
|
'../../applications/welcome')
|
||||||
self.request.env = Storage()
|
self.request.env = Storage()
|
||||||
self.request.env.http_accept_language = 'en'
|
self.request.env.http_accept_language = 'en'
|
||||||
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -99,4 +98,3 @@ except ImportError:
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
@@ -13,10 +13,10 @@ else:
|
|||||||
import unittest
|
import unittest
|
||||||
from contrib.markmin.markmin2html import run_doctests
|
from contrib.markmin.markmin2html import run_doctests
|
||||||
|
|
||||||
|
|
||||||
class TestMarkmin(unittest.TestCase):
|
class TestMarkmin(unittest.TestCase):
|
||||||
def testMarkmin(self):
|
def testMarkmin(self):
|
||||||
run_doctests()
|
run_doctests()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
+713
-358
File diff suppressed because it is too large.
Load diff
+159
-78
@@ -26,6 +26,7 @@ logger = None
|
|||||||
oldcwd = None
|
oldcwd = None
|
||||||
root = None
|
root = None
|
||||||
|
|
||||||
|
|
||||||
def setUpModule():
|
def setUpModule():
|
||||||
def make_apptree():
|
def make_apptree():
|
||||||
"build a temporary applications tree"
|
"build a temporary applications tree"
|
||||||
@@ -39,13 +40,16 @@ def setUpModule():
|
|||||||
os.mkdir(abspath('applications', app, subdir))
|
os.mkdir(abspath('applications', app, subdir))
|
||||||
# applications/admin/controllers/*.py
|
# applications/admin/controllers/*.py
|
||||||
for ctr in ('appadmin', 'default', 'gae', 'mercurial', 'shell', 'wizard'):
|
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
|
# applications/examples/controllers/*.py
|
||||||
for ctr in ('ajax_examples', 'appadmin', 'default', 'global', 'spreadsheet'):
|
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
|
# applications/welcome/controllers/*.py
|
||||||
for ctr in ('appadmin', 'default'):
|
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
|
# create an app-specific routes.py for examples app
|
||||||
routes = open(abspath('applications', 'examples', 'routes.py'), 'w')
|
routes = open(abspath('applications', 'examples', 'routes.py'), 'w')
|
||||||
routes.write("default_function='exdef'\n")
|
routes.write("default_function='exdef'\n")
|
||||||
@@ -55,7 +59,8 @@ def setUpModule():
|
|||||||
if oldcwd is None: # do this only once
|
if oldcwd is None: # do this only once
|
||||||
oldcwd = os.getcwd()
|
oldcwd = os.getcwd()
|
||||||
if not os.path.isdir('gluon'):
|
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
|
import main # for initialization after chdir
|
||||||
global logger
|
global logger
|
||||||
logger = logging.getLogger('web2py.rewrite')
|
logger = logging.getLogger('web2py.rewrite')
|
||||||
@@ -64,6 +69,7 @@ def setUpModule():
|
|||||||
root = global_settings.applications_parent
|
root = global_settings.applications_parent
|
||||||
make_apptree()
|
make_apptree()
|
||||||
|
|
||||||
|
|
||||||
def tearDownModule():
|
def tearDownModule():
|
||||||
global oldcwd
|
global oldcwd
|
||||||
if oldcwd is not None:
|
if oldcwd is not None:
|
||||||
@@ -78,16 +84,26 @@ class TestRoutes(unittest.TestCase):
|
|||||||
""" Tests a null routes table """
|
""" Tests a null routes table """
|
||||||
load(data='')
|
load(data='')
|
||||||
# incoming
|
# incoming
|
||||||
self.assertEqual(filter_url('http://domain.com'), '/init/default/index')
|
self.assertEqual(
|
||||||
self.assertEqual(filter_url('http://domain.com/'), '/init/default/index')
|
filter_url('http://domain.com'), '/init/default/index')
|
||||||
self.assertEqual(filter_url('http://domain.com/abc'), '/abc/default/index')
|
self.assertEqual(
|
||||||
self.assertEqual(filter_url('http://domain.com/abc/'), '/abc/default/index')
|
filter_url('http://domain.com/'), '/init/default/index')
|
||||||
self.assertEqual(filter_url('http://domain.com/abc/def'), "/abc/def/index")
|
self.assertEqual(
|
||||||
self.assertEqual(filter_url('http://domain.com/abc/def/'), "/abc/def/index")
|
filter_url('http://domain.com/abc'), '/abc/default/index')
|
||||||
self.assertEqual(filter_url('http://domain.com/abc/def/ghi'), "/abc/def/ghi")
|
self.assertEqual(
|
||||||
self.assertEqual(filter_url('http://domain.com/abc/def/ghi/'), "/abc/def/ghi")
|
filter_url('http://domain.com/abc/'), '/abc/default/index')
|
||||||
self.assertEqual(filter_url('http://domain.com/abc/def/ghi/jkl'), "/abc/def/ghi ['jkl']")
|
self.assertEqual(
|
||||||
self.assertEqual(filter_url('http://domain.com/abc/def/ghi/j%20kl'), "/abc/def/ghi ['j_kl']")
|
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.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')
|
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/st~tic')
|
||||||
try:
|
try:
|
||||||
@@ -96,9 +112,11 @@ class TestRoutes(unittest.TestCase):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
# outgoing
|
# 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/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):
|
def test_routes_query(self):
|
||||||
""" Test query appending """
|
""" Test query appending """
|
||||||
@@ -125,8 +143,10 @@ routes_app = [
|
|||||||
]
|
]
|
||||||
'''
|
'''
|
||||||
load(data=data)
|
load(data=data)
|
||||||
self.assertEqual(filter_url('http://domain.com/welcome'), '/welcome/default/index')
|
self.assertEqual(
|
||||||
self.assertEqual(filter_url('http://domain.com/examples'), '/examples/default/exdef')
|
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):
|
def test_routes_defapp(self):
|
||||||
""" Test the default-application function """
|
""" Test the default-application function """
|
||||||
@@ -135,12 +155,17 @@ default_application = 'defapp'
|
|||||||
'''
|
'''
|
||||||
load(data=data)
|
load(data=data)
|
||||||
# incoming
|
# incoming
|
||||||
self.assertEqual(filter_url('http://domain.com'), '/defapp/default/index')
|
self.assertEqual(
|
||||||
self.assertEqual(filter_url('http://domain.com/'), '/defapp/default/index')
|
filter_url('http://domain.com'), '/defapp/default/index')
|
||||||
self.assertEqual(filter_url('http://domain.com/welcome'), '/welcome/default/index')
|
self.assertEqual(
|
||||||
self.assertEqual(filter_url('http://domain.com/app'), '/app/default/index')
|
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/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)
|
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):
|
def test_routes_raise(self):
|
||||||
@@ -149,15 +174,21 @@ default_application = 'defapp'
|
|||||||
'''
|
'''
|
||||||
# test non-exception variants
|
# test non-exception variants
|
||||||
load(data='')
|
load(data='')
|
||||||
self.assertEqual(filter_url('http://domain.com/init'), "/init/default/index")
|
self.assertEqual(
|
||||||
self.assertEqual(filter_url('http://domain.com/init/default'), "/init/default/index")
|
filter_url('http://domain.com/init'), "/init/default/index")
|
||||||
self.assertEqual(filter_url('http://domain.com/init/default/fcn.ext'), "/init/default/fcn.ext")
|
self.assertEqual(filter_url(
|
||||||
self.assertEqual(filter_url('http://domain.com/init/default/fcn/arg'), "/init/default/fcn ['arg']")
|
'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
|
# now raise-HTTP variants
|
||||||
self.assertRaises(HTTP, filter_url, 'http://domain.com/bad!ctl')
|
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/bad!fcn')
|
||||||
self.assertRaises(HTTP, filter_url, 'http://domain.com/ctl/fcn.bad!ext')
|
self.assertRaises(
|
||||||
self.assertRaises(HTTP, filter_url, 'http://domain.com/ctl/fcn/bad!arg')
|
HTTP, filter_url, 'http://domain.com/ctl/fcn.bad!ext')
|
||||||
|
self.assertRaises(
|
||||||
|
HTTP, filter_url, 'http://domain.com/ctl/fcn/bad!arg')
|
||||||
try:
|
try:
|
||||||
# 2.7+ only
|
# 2.7+ only
|
||||||
self.assertRaisesRegexp(HTTP, '400 BAD REQUEST \[invalid path\]', filter_url, 'http://domain.com/init/bad!ctl')
|
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:
|
except AttributeError:
|
||||||
pass
|
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')
|
#self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/default/fcn-1')
|
||||||
#try:
|
#try:
|
||||||
# # 2.7+ only
|
# # 2.7+ only
|
||||||
@@ -194,17 +226,26 @@ default_application = 'defapp'
|
|||||||
('/favicon.ico', '/welcome/static/favicon.ico'),
|
('/favicon.ico', '/welcome/static/favicon.ico'),
|
||||||
('/admin$anything', '/admin$anything'),
|
('/admin$anything', '/admin$anything'),
|
||||||
('.*:https?://(.*\\.)?domain1.com:$method /', '/app1/default'),
|
('.*:https?://(.*\\.)?domain1.com:$method /', '/app1/default'),
|
||||||
('.*:https?://(.*\\.)?domain1.com:$method /static/$anything', '/app1/static/$anything'),
|
('.*:https?://(.*\\.)?domain1.com:$method /static/$anything',
|
||||||
('.*:https?://(.*\\.)?domain1.com:$method /appadmin/$anything', '/app1/appadmin/$anything'),
|
'/app1/static/$anything'),
|
||||||
('.*:https?://(.*\\.)?domain1.com:$method /$anything', '/app1/default/$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 /', '/app2/default'),
|
||||||
('.*:https?://(.*\\.)?domain2.com:$method /static/$anything', '/app2/static/$anything'),
|
('.*:https?://(.*\\.)?domain2.com:$method /static/$anything',
|
||||||
('.*:https?://(.*\\.)?domain2.com:$method /appadmin/$anything', '/app2/appadmin/$anything'),
|
'/app2/static/$anything'),
|
||||||
('.*:https?://(.*\\.)?domain2.com:$method /$anything', '/app2/default/$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 /', '/app3/defcon3'),
|
||||||
('.*:https?://(.*\\.)?domain3.com:$method /static/$anything', '/app3/static/$anything'),
|
('.*:https?://(.*\\.)?domain3.com:$method /static/$anything',
|
||||||
('.*:https?://(.*\\.)?domain3.com:$method /appadmin/$anything', '/app3/appadmin/$anything'),
|
'/app3/static/$anything'),
|
||||||
('.*:https?://(.*\\.)?domain3.com:$method /$anything', '/app3/defcon3/$anything'),
|
('.*:https?://(.*\\.)?domain3.com:$method /appadmin/$anything',
|
||||||
|
'/app3/appadmin/$anything'),
|
||||||
|
('.*:https?://(.*\\.)?domain3.com:$method /$anything',
|
||||||
|
'/app3/defcon3/$anything'),
|
||||||
('/', '/welcome/default'),
|
('/', '/welcome/default'),
|
||||||
('/welcome/default/$anything', '/welcome/default/$anything'),
|
('/welcome/default/$anything', '/welcome/default/$anything'),
|
||||||
('/welcome/$anything', '/welcome/default/$anything'),
|
('/welcome/$anything', '/welcome/default/$anything'),
|
||||||
@@ -228,47 +269,69 @@ routes_out = [
|
|||||||
]
|
]
|
||||||
'''
|
'''
|
||||||
load(data=data)
|
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']")
|
"/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']")
|
"/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', '']")
|
"/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']")
|
"/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']")
|
"/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']")
|
"/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']")
|
"/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', '']")
|
"/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(regex_filter_out('/welcome/default/f'), "/f")
|
||||||
self.assertEqual(str(URL(a='welcome', c='default', f='f', args=None)), "/f")
|
self.assertEqual(
|
||||||
self.assertEqual(str(URL(a='welcome', c='default', f='f', args=['arg1'])), "/f/arg1")
|
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(
|
||||||
self.assertEqual(str(URL(a='welcome', c='default', f='f', args=['arg1', '', 'arg3'])), "/f/arg1//arg3")
|
URL(a='welcome', c='default', f='f', args=['arg1'])), "/f/arg1")
|
||||||
self.assertEqual(str(URL(a='welcome', c='default', f='f', args=['ar g'])), "/f/ar%20g")
|
self.assertEqual(str(URL(
|
||||||
self.assertEqual(str(URL(a='welcome', c='default', f='f', args=['årg'])), "/f/%C3%A5rg")
|
a='welcome', c='default', f='f', args=['arg1', ''])), "/f/arg1//")
|
||||||
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=['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):
|
def test_routes_anchor(self):
|
||||||
'''
|
'''
|
||||||
Test URL with anchor
|
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='')
|
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']
|
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")
|
"/a/c/f/a1/a2#anchor")
|
||||||
vars = dict(v1=1, v2=2)
|
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")
|
"/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")
|
"/a/c/f/a1/a2?v1=1&v2=2#anchor")
|
||||||
|
|
||||||
data = r'''routes_out = [
|
data = r'''routes_out = [
|
||||||
@@ -277,7 +340,8 @@ routes_out = [
|
|||||||
load(data=data)
|
load(data=data)
|
||||||
self.assertEqual(str(URL(a='init', c='default', f='index')),
|
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")
|
"/init/default/index#anchor")
|
||||||
|
|
||||||
data = r'''routes_out = [
|
data = r'''routes_out = [
|
||||||
@@ -286,7 +350,8 @@ routes_out = [
|
|||||||
load(data=data)
|
load(data=data)
|
||||||
self.assertEqual(str(URL(a='init', c='default', f='index')),
|
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")
|
"/#anchor")
|
||||||
|
|
||||||
data = r'''routes_out = [
|
data = r'''routes_out = [
|
||||||
@@ -295,12 +360,16 @@ routes_out = [
|
|||||||
load(data=data)
|
load(data=data)
|
||||||
self.assertEqual(str(URL(a='init', c='default', f='index')),
|
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")
|
"/#anchor")
|
||||||
query = dict(var='abc')
|
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")
|
"/?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")
|
"/?var=abc#anchor")
|
||||||
|
|
||||||
def test_routes_absolute(self):
|
def test_routes_absolute(self):
|
||||||
@@ -325,23 +394,32 @@ routes_out = [
|
|||||||
"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')),
|
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme='wss')),
|
||||||
"wss://domain.com/a/c/f")
|
"wss://domain.com/a/c/f")
|
||||||
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=True, host=True)),
|
self.assertEqual(
|
||||||
|
str(URL(r=r, a='a', c='c', f='f', scheme=True, 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', 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")
|
"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")
|
"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")
|
"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")
|
"httpx://host.com/a/c/f")
|
||||||
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', port=1234)),
|
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', port=1234)),
|
||||||
"httpx://domain.com:1234/a/c/f")
|
"httpx://domain.com:1234/a/c/f")
|
||||||
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=True, port=1234)),
|
self.assertEqual(
|
||||||
|
str(URL(r=r, a='a', c='c', f='f', scheme=True, port=1234)),
|
||||||
"httpx://domain.com:1234/a/c/f")
|
"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")
|
"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")
|
"wss://host.com:1234/a/c/f")
|
||||||
|
|
||||||
def test_request_uri(self):
|
def test_request_uri(self):
|
||||||
@@ -354,13 +432,17 @@ routes_out = [
|
|||||||
]
|
]
|
||||||
'''
|
'''
|
||||||
load(data=data)
|
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')
|
'/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')
|
'/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")
|
"/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")
|
"/init/default/index/a bc")
|
||||||
|
|
||||||
|
|
||||||
@@ -368,4 +450,3 @@ if __name__ == '__main__':
|
|||||||
setUpModule() # pre-2.7
|
setUpModule() # pre-2.7
|
||||||
unittest.main()
|
unittest.main()
|
||||||
tearDownModule()
|
tearDownModule()
|
||||||
|
|
||||||
@@ -39,7 +39,6 @@ class TestStorage(unittest.TestCase):
|
|||||||
s.d = list()
|
s.d = list()
|
||||||
self.assertTrue(s.d is s['d'])
|
self.assertTrue(s.d is s['d'])
|
||||||
|
|
||||||
|
|
||||||
def test_store_none(self):
|
def test_store_none(self):
|
||||||
""" Test Storage store-None handling
|
""" Test Storage store-None handling
|
||||||
s.key = None deletes an item
|
s.key = None deletes an item
|
||||||
@@ -59,7 +58,6 @@ class TestStorage(unittest.TestCase):
|
|||||||
self.assertTrue('a' in s)
|
self.assertTrue('a' in s)
|
||||||
self.assertTrue(s.a is None)
|
self.assertTrue(s.a is None)
|
||||||
|
|
||||||
|
|
||||||
def test_item(self):
|
def test_item(self):
|
||||||
""" Tests Storage item handling """
|
""" Tests Storage item handling """
|
||||||
|
|
||||||
@@ -76,4 +74,3 @@ class TestStorage(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ else:
|
|||||||
import unittest
|
import unittest
|
||||||
from template import render
|
from template import render
|
||||||
|
|
||||||
|
|
||||||
class TestVirtualFields(unittest.TestCase):
|
class TestVirtualFields(unittest.TestCase):
|
||||||
|
|
||||||
def testRun(self):
|
def testRun(self):
|
||||||
@@ -21,7 +22,8 @@ class TestVirtualFields(unittest.TestCase):
|
|||||||
context=dict(n=3)), '012')
|
context=dict(n=3)), '012')
|
||||||
self.assertEqual(render(content='{{if n>2:}}ok{{pass}}',
|
self.assertEqual(render(content='{{if n>2:}}ok{{pass}}',
|
||||||
context=dict(n=3)), 'ok')
|
context=dict(n=3)), 'ok')
|
||||||
self.assertEqual(render(content='{{try:}}{{n/0}}{{except:}}fail{{pass}}',
|
self.assertEqual(
|
||||||
|
render(content='{{try:}}{{n/0}}{{except:}}fail{{pass}}',
|
||||||
context=dict(n=3)), 'fail')
|
context=dict(n=3)), 'fail')
|
||||||
self.assertEqual(render(content='{{="<&>"}}'), '<&>')
|
self.assertEqual(render(content='{{="<&>"}}'), '<&>')
|
||||||
self.assertEqual(render(content='"abc"'), '"abc"')
|
self.assertEqual(render(content='"abc"'), '"abc"')
|
||||||
@@ -38,13 +40,18 @@ class TestVirtualFields(unittest.TestCase):
|
|||||||
self.assertEqual(render(content='{{ ="abc" }}'), 'abc')
|
self.assertEqual(render(content='{{ ="abc" }}'), 'abc')
|
||||||
self.assertEqual(render(content='{{pass\n="abc" }}'), 'abc')
|
self.assertEqual(render(content='{{pass\n="abc" }}'), 'abc')
|
||||||
# = recognized only at the beginning of a physical line
|
# = 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
|
# = in python blocks
|
||||||
self.assertEqual(render(content='{{if True:\n="abc"\npass }}'), 'abc')
|
self.assertEqual(render(content='{{if True:\n="abc"\npass }}'), 'abc')
|
||||||
self.assertEqual(render(content='{{if True:\n="abc"\npass\n="def" }}'), 'abcdef')
|
self.assertEqual(
|
||||||
self.assertEqual(render(content='{{if False:\n="abc"\npass\n="def" }}'), 'def')
|
render(content='{{if True:\n="abc"\npass\n="def" }}'), 'abcdef')
|
||||||
self.assertEqual(render(content='{{if True:\n="abc"\nelse:\n="def"\npass }}'), 'abc')
|
self.assertEqual(
|
||||||
self.assertEqual(render(content='{{if False:\n="abc"\nelse:\n="def"\npass }}'), 'def')
|
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
|
# 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,3))}}'), '[1, 2, 3]')
|
||||||
self.assertEqual(render(content='{{=list((1,2,\\\n3))}}'), '[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
|
# ...but that means no more = operators in the codeblock
|
||||||
self.assertRaises(SyntaxError, render, content='{{="abc"\n="def" }}')
|
self.assertRaises(SyntaxError, render, content='{{="abc"\n="def" }}')
|
||||||
# = embedded in codeblock won't handle newlines in its argument
|
# = embedded in codeblock won't handle newlines in its argument
|
||||||
self.assertEqual(render(content='{{pass\n=list((1,2,\\\n3))}}'), '[1, 2, 3]')
|
self.assertEqual(
|
||||||
self.assertRaises(SyntaxError, render, content='{{pass\n=list((1,2,\n3))}}')
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
@@ -25,4 +25,3 @@ class TestUtils(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ else:
|
|||||||
import unittest
|
import unittest
|
||||||
from contrib.webclient import WebClient
|
from contrib.webclient import WebClient
|
||||||
|
|
||||||
|
|
||||||
class TestWeb(unittest.TestCase):
|
class TestWeb(unittest.TestCase):
|
||||||
def testWebClient(self):
|
def testWebClient(self):
|
||||||
client = WebClient('http://127.0.0.1:8000/welcome/default/')
|
client = WebClient('http://127.0.0.1:8000/welcome/default/')
|
||||||
@@ -46,6 +47,7 @@ class TestWeb(unittest.TestCase):
|
|||||||
client.get('site')
|
client.get('site')
|
||||||
client.get('design/welcome')
|
client.get('design/welcome')
|
||||||
|
|
||||||
|
|
||||||
class TestStaticCacheControl(unittest.TestCase):
|
class TestStaticCacheControl(unittest.TestCase):
|
||||||
def testWebClient(self):
|
def testWebClient(self):
|
||||||
s = WebClient('http://127.0.0.1:8000/welcome/')
|
s = WebClient('http://127.0.0.1:8000/welcome/')
|
||||||
@@ -60,4 +62,3 @@ class TestStaticCacheControl(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
+260
-126
File diff suppressed because it is too large.
Load diff
+160
-55
@@ -15,7 +15,8 @@ import __builtin__
|
|||||||
__all__ = ['Utf8']
|
__all__ = ['Utf8']
|
||||||
|
|
||||||
repr_escape_tab = {}
|
repr_escape_tab = {}
|
||||||
for i in range(1,32): repr_escape_tab[i]=ur'\x%02x'%i
|
for i in range(1, 32):
|
||||||
|
repr_escape_tab[i] = ur'\x%02x' % i
|
||||||
repr_escape_tab[7] = u'\\a'
|
repr_escape_tab[7] = u'\\a'
|
||||||
repr_escape_tab[8] = u'\\b'
|
repr_escape_tab[8] = u'\\b'
|
||||||
repr_escape_tab[9] = u'\\t'
|
repr_escape_tab[9] = u'\\t'
|
||||||
@@ -27,6 +28,7 @@ repr_escape_tab[ord('\\')]=u'\\\\'
|
|||||||
repr_escape_tab2 = repr_escape_tab.copy()
|
repr_escape_tab2 = repr_escape_tab.copy()
|
||||||
repr_escape_tab2[ord('\'')] = u"\\'"
|
repr_escape_tab2[ord('\'')] = u"\\'"
|
||||||
|
|
||||||
|
|
||||||
def sort_key(s):
|
def sort_key(s):
|
||||||
""" Unicode Collation Algorithm (UCA) (http://www.unicode.org/reports/tr10/)
|
""" Unicode Collation Algorithm (UCA) (http://www.unicode.org/reports/tr10/)
|
||||||
is used for utf-8 and unicode strings sorting and for utf-8 strings
|
is used for utf-8 and unicode strings sorting and for utf-8 strings
|
||||||
@@ -48,7 +50,8 @@ def sort_key(s):
|
|||||||
sort_key = lambda s: unicode_sort_key(
|
sort_key = lambda s: unicode_sort_key(
|
||||||
unicode(s, 'utf-8') if isinstance(s, str) else s)
|
unicode(s, 'utf-8') if isinstance(s, str) else s)
|
||||||
except:
|
except:
|
||||||
sort_key=lambda s: (unicode(s, 'utf-8') if isinstance(s, str) else s).lower()
|
sort_key = lambda s: (
|
||||||
|
unicode(s, 'utf-8') if isinstance(s, str) else s).lower()
|
||||||
return sort_key(s)
|
return sort_key(s)
|
||||||
|
|
||||||
|
|
||||||
@@ -57,13 +60,16 @@ def ord(char):
|
|||||||
|
|
||||||
SUPPOSE that *char* is an utf-8 or unicode character only
|
SUPPOSE that *char* is an utf-8 or unicode character only
|
||||||
"""
|
"""
|
||||||
if isinstance(char, unicode): return __builtin__.ord(char)
|
if isinstance(char, unicode):
|
||||||
|
return __builtin__.ord(char)
|
||||||
return __builtin__.ord(unicode(char, 'utf-8'))
|
return __builtin__.ord(unicode(char, 'utf-8'))
|
||||||
|
|
||||||
|
|
||||||
def chr(code):
|
def chr(code):
|
||||||
""" return utf8-character with *code* unicode id """
|
""" return utf8-character with *code* unicode id """
|
||||||
return Utf8(unichr(code))
|
return Utf8(unichr(code))
|
||||||
|
|
||||||
|
|
||||||
def size(string):
|
def size(string):
|
||||||
""" return length of utf-8 string in bytes
|
""" return length of utf-8 string in bytes
|
||||||
NOTE! The length of correspondent utf-8
|
NOTE! The length of correspondent utf-8
|
||||||
@@ -71,6 +77,7 @@ def size(string):
|
|||||||
"""
|
"""
|
||||||
return Utf8(string).__size__()
|
return Utf8(string).__size__()
|
||||||
|
|
||||||
|
|
||||||
def truncate(string, length, dots='...'):
|
def truncate(string, length, dots='...'):
|
||||||
""" returns string of length < *length* or truncate
|
""" returns string of length < *length* or truncate
|
||||||
string with adding *dots* suffix to the string's end
|
string with adding *dots* suffix to the string's end
|
||||||
@@ -153,100 +160,185 @@ class Utf8(str):
|
|||||||
""" length of utf-8 string in bytes """
|
""" length of utf-8 string in bytes """
|
||||||
return str.__len__(self)
|
return str.__len__(self)
|
||||||
|
|
||||||
def __contains__(self, other): return str.__contains__(self, Utf8(other))
|
def __contains__(self, other):
|
||||||
def __getitem__(self, index): return str.__new__(Utf8, unicode(self, 'utf-8')[index].encode('utf-8'))
|
return str.__contains__(self, Utf8(other))
|
||||||
def __getslice__(self, begin, end): return str.__new__(Utf8, unicode(self, 'utf-8')[begin:end].encode('utf-8'))
|
|
||||||
|
|
||||||
def __add__(self, other): return str.__new__(Utf8, str.__add__(self, unicode.encode(other, 'utf-8')
|
def __getitem__(self, index):
|
||||||
|
return str.__new__(Utf8, unicode(self, 'utf-8')[index].encode('utf-8'))
|
||||||
|
|
||||||
|
def __getslice__(self, begin, end):
|
||||||
|
return str.__new__(Utf8, unicode(self, 'utf-8')[begin:end].encode('utf-8'))
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
return str.__new__(Utf8, str.__add__(self, unicode.encode(other, 'utf-8')
|
||||||
if isinstance(other, unicode) else other))
|
if isinstance(other, unicode) else other))
|
||||||
def __len__(self): return len(unicode(self, 'utf-8'))
|
|
||||||
def __mul__(self, integer):return str.__new__(Utf8, str.__mul__(self, integer))
|
def __len__(self):
|
||||||
def __eq__(self, string): return str.__eq__(self, Utf8(string))
|
return len(unicode(self, 'utf-8'))
|
||||||
def __ne__(self, string): return str.__ne__(self, Utf8(string))
|
|
||||||
def capitalize(self): return str.__new__(Utf8, unicode(self, 'utf-8').capitalize().encode('utf-8'))
|
def __mul__(self, integer):
|
||||||
def center(self, length): return str.__new__(Utf8, unicode(self, 'utf-8').center(length).encode('utf-8'))
|
return str.__new__(Utf8, str.__mul__(self, integer))
|
||||||
def upper(self): return str.__new__(Utf8, unicode(self, 'utf-8').upper().encode('utf-8'))
|
|
||||||
def lower(self): return str.__new__(Utf8, unicode(self, 'utf-8').lower().encode('utf-8'))
|
def __eq__(self, string):
|
||||||
def title(self): return str.__new__(Utf8, unicode(self, 'utf-8').title().encode('utf-8'))
|
return str.__eq__(self, Utf8(string))
|
||||||
def index(self, string): return unicode(self, 'utf-8').index(string if isinstance(string,unicode) else unicode(string, 'utf-8'))
|
|
||||||
def isalnum(self): return unicode(self, 'utf-8').isalnum()
|
def __ne__(self, string):
|
||||||
def isalpha(self): return unicode(self, 'utf-8').isalpha()
|
return str.__ne__(self, Utf8(string))
|
||||||
def isdigit(self): return unicode(self, 'utf-8').isdigit()
|
|
||||||
def islower(self): return unicode(self, 'utf-8').islower()
|
def capitalize(self):
|
||||||
def isspace(self): return unicode(self, 'utf-8').isspace()
|
return str.__new__(Utf8, unicode(self, 'utf-8').capitalize().encode('utf-8'))
|
||||||
def istitle(self): return unicode(self, 'utf-8').istitle()
|
|
||||||
def isupper(self): return unicode(self, 'utf-8').isupper()
|
def center(self, length):
|
||||||
def zfill(self, length): return str.__new__(Utf8, unicode(self, 'utf-8').zfill(length).encode('utf-8'))
|
return str.__new__(Utf8, unicode(self, 'utf-8').center(length).encode('utf-8'))
|
||||||
def join(self, iter): return str.__new__(Utf8, str.join(self, [Utf8(c) for c in
|
|
||||||
|
def upper(self):
|
||||||
|
return str.__new__(Utf8, unicode(self, 'utf-8').upper().encode('utf-8'))
|
||||||
|
|
||||||
|
def lower(self):
|
||||||
|
return str.__new__(Utf8, unicode(self, 'utf-8').lower().encode('utf-8'))
|
||||||
|
|
||||||
|
def title(self):
|
||||||
|
return str.__new__(Utf8, unicode(self, 'utf-8').title().encode('utf-8'))
|
||||||
|
|
||||||
|
def index(self, string):
|
||||||
|
return unicode(self, 'utf-8').index(string if isinstance(string, unicode) else unicode(string, 'utf-8'))
|
||||||
|
|
||||||
|
def isalnum(self):
|
||||||
|
return unicode(self, 'utf-8').isalnum()
|
||||||
|
|
||||||
|
def isalpha(self):
|
||||||
|
return unicode(self, 'utf-8').isalpha()
|
||||||
|
|
||||||
|
def isdigit(self):
|
||||||
|
return unicode(self, 'utf-8').isdigit()
|
||||||
|
|
||||||
|
def islower(self):
|
||||||
|
return unicode(self, 'utf-8').islower()
|
||||||
|
|
||||||
|
def isspace(self):
|
||||||
|
return unicode(self, 'utf-8').isspace()
|
||||||
|
|
||||||
|
def istitle(self):
|
||||||
|
return unicode(self, 'utf-8').istitle()
|
||||||
|
|
||||||
|
def isupper(self):
|
||||||
|
return unicode(self, 'utf-8').isupper()
|
||||||
|
|
||||||
|
def zfill(self, length):
|
||||||
|
return str.__new__(Utf8, unicode(self, 'utf-8').zfill(length).encode('utf-8'))
|
||||||
|
|
||||||
|
def join(self, iter):
|
||||||
|
return str.__new__(Utf8, str.join(self, [Utf8(c) for c in
|
||||||
list(unicode(iter, 'utf-8') if
|
list(unicode(iter, 'utf-8') if
|
||||||
isinstance(iter, str) else
|
isinstance(iter, str) else
|
||||||
iter)]))
|
iter)]))
|
||||||
def lstrip(self, chars=None): return str.__new__(Utf8, str.lstrip(self, None if chars is None else Utf8(chars)))
|
|
||||||
def rstrip(self, chars=None ): return str.__new__(Utf8, str.rstrip(self, None if chars is None else Utf8(chars)))
|
def lstrip(self, chars=None):
|
||||||
def strip(self, chars=None): return str.__new__(Utf8, str.strip(self, None if chars is None else Utf8(chars)))
|
return str.__new__(Utf8, str.lstrip(self, None if chars is None else Utf8(chars)))
|
||||||
def swapcase(self): return str.__new__(Utf8, unicode(self, 'utf-8').swapcase().encode('utf-8'))
|
|
||||||
|
def rstrip(self, chars=None):
|
||||||
|
return str.__new__(Utf8, str.rstrip(self, None if chars is None else Utf8(chars)))
|
||||||
|
|
||||||
|
def strip(self, chars=None):
|
||||||
|
return str.__new__(Utf8, str.strip(self, None if chars is None else Utf8(chars)))
|
||||||
|
|
||||||
|
def swapcase(self):
|
||||||
|
return str.__new__(Utf8, unicode(self, 'utf-8').swapcase().encode('utf-8'))
|
||||||
|
|
||||||
def count(self, sub, start=0, end=None):
|
def count(self, sub, start=0, end=None):
|
||||||
unistr = unicode(self, 'utf-8')
|
unistr = unicode(self, 'utf-8')
|
||||||
return unistr.count(unicode(sub, 'utf-8') if isinstance(sub, str) else sub,
|
return unistr.count(
|
||||||
|
unicode(sub, 'utf-8') if isinstance(sub, str) else sub,
|
||||||
start, len(unistr) if end is None else end)
|
start, len(unistr) if end is None else end)
|
||||||
def decode(self, encoding='utf-8', errors='strict'): return str.decode(self, encoding, errors)
|
|
||||||
def encode(self, encoding, errors='strict'): return unicode(self, 'utf-8').encode(encoding, errors)
|
def decode(self, encoding='utf-8', errors='strict'):
|
||||||
def expandtabs(self, tabsize=8): return str.__new__(Utf8, unicode(self, 'utf-8').expandtabs(tabsize).encode('utf-8'))
|
return str.decode(self, encoding, errors)
|
||||||
def find(self, sub, start=None, end=None): return unicode(self, 'utf-8').find(unicode(sub, 'utf-8')
|
|
||||||
|
def encode(self, encoding, errors='strict'):
|
||||||
|
return unicode(self, 'utf-8').encode(encoding, errors)
|
||||||
|
|
||||||
|
def expandtabs(self, tabsize=8):
|
||||||
|
return str.__new__(Utf8, unicode(self, 'utf-8').expandtabs(tabsize).encode('utf-8'))
|
||||||
|
|
||||||
|
def find(self, sub, start=None, end=None):
|
||||||
|
return unicode(self, 'utf-8').find(unicode(sub, 'utf-8')
|
||||||
if isinstance(sub, str) else sub, start, end)
|
if isinstance(sub, str) else sub, start, end)
|
||||||
def ljust(self, width, fillchar=' '): return str.__new__(Utf8, unicode(self, 'utf-8').ljust(width, unicode(fillchar, 'utf-8')
|
|
||||||
|
def ljust(self, width, fillchar=' '):
|
||||||
|
return str.__new__(Utf8, unicode(self, 'utf-8').ljust(width, unicode(fillchar, 'utf-8')
|
||||||
if isinstance(fillchar, str) else fillchar).encode('utf-8'))
|
if isinstance(fillchar, str) else fillchar).encode('utf-8'))
|
||||||
|
|
||||||
def partition(self, sep):
|
def partition(self, sep):
|
||||||
(head, sep, tail) = str.partition(self, Utf8(sep))
|
(head, sep, tail) = str.partition(self, Utf8(sep))
|
||||||
return (str.__new__(Utf8, head),
|
return (str.__new__(Utf8, head),
|
||||||
str.__new__(Utf8, sep),
|
str.__new__(Utf8, sep),
|
||||||
str.__new__(Utf8, tail))
|
str.__new__(Utf8, tail))
|
||||||
|
|
||||||
def replace(self, old, new, count=-1): return str.__new__(Utf8, str.replace(self, Utf8(old), Utf8(new), count))
|
def replace(self, old, new, count=-1):
|
||||||
def rfind(self, sub, start=None, end=None): return unicode(self, 'utf-8').rfind(unicode(sub, 'utf-8')
|
return str.__new__(Utf8, str.replace(self, Utf8(old), Utf8(new), count))
|
||||||
|
|
||||||
|
def rfind(self, sub, start=None, end=None):
|
||||||
|
return unicode(self, 'utf-8').rfind(unicode(sub, 'utf-8')
|
||||||
if isinstance(sub, str) else sub, start, end)
|
if isinstance(sub, str) else sub, start, end)
|
||||||
def rindex(self, string): return unicode(self, 'utf-8').rindex(string if isinstance(string,unicode)
|
|
||||||
|
def rindex(self, string):
|
||||||
|
return unicode(self, 'utf-8').rindex(string if isinstance(string, unicode)
|
||||||
else unicode(string, 'utf-8'))
|
else unicode(string, 'utf-8'))
|
||||||
def rjust(self, width, fillchar=' '): return str.__new__(Utf8, unicode(self, 'utf-8').rjust(width, unicode(fillchar, 'utf-8')
|
|
||||||
|
def rjust(self, width, fillchar=' '):
|
||||||
|
return str.__new__(Utf8, unicode(self, 'utf-8').rjust(width, unicode(fillchar, 'utf-8')
|
||||||
if isinstance(fillchar, str) else fillchar).encode('utf-8'))
|
if isinstance(fillchar, str) else fillchar).encode('utf-8'))
|
||||||
|
|
||||||
def rpartition(self, sep):
|
def rpartition(self, sep):
|
||||||
(head, sep, tail) = str.rpartition(self, Utf8(sep))
|
(head, sep, tail) = str.rpartition(self, Utf8(sep))
|
||||||
return (str.__new__(Utf8, head),
|
return (str.__new__(Utf8, head),
|
||||||
str.__new__(Utf8, sep),
|
str.__new__(Utf8, sep),
|
||||||
str.__new__(Utf8, tail))
|
str.__new__(Utf8, tail))
|
||||||
def rsplit(self, sep=None, maxsplit=-1): return [str.__new__(Utf8, part) for part in str.rsplit(self,
|
|
||||||
|
def rsplit(self, sep=None, maxsplit=-1):
|
||||||
|
return [str.__new__(Utf8, part) for part in str.rsplit(self,
|
||||||
None if sep is None else Utf8(sep), maxsplit)]
|
None if sep is None else Utf8(sep), maxsplit)]
|
||||||
def split(self, sep=None, maxsplit=-1): return [str.__new__(Utf8, part) for part in str.split(self,
|
|
||||||
|
def split(self, sep=None, maxsplit=-1):
|
||||||
|
return [str.__new__(Utf8, part) for part in str.split(self,
|
||||||
None if sep is None else Utf8(sep), maxsplit)]
|
None if sep is None else Utf8(sep), maxsplit)]
|
||||||
def splitlines(self,keepends=False): return [str.__new__(Utf8, part) for part in str.splitlines(self,keepends)]
|
|
||||||
|
def splitlines(self, keepends=False):
|
||||||
|
return [str.__new__(Utf8, part) for part in str.splitlines(self, keepends)]
|
||||||
|
|
||||||
def startswith(self, prefix, start=0, end=None):
|
def startswith(self, prefix, start=0, end=None):
|
||||||
unistr = unicode(self, 'utf-8')
|
unistr = unicode(self, 'utf-8')
|
||||||
if isinstance(prefix, tuple):
|
if isinstance(prefix, tuple):
|
||||||
prefix = tuple(unicode(s,'utf-8') if isinstance(s, str) else s for s in prefix)
|
prefix = tuple(unicode(
|
||||||
|
s, 'utf-8') if isinstance(s, str) else s for s in prefix)
|
||||||
elif isinstance(prefix, str):
|
elif isinstance(prefix, str):
|
||||||
prefix = unicode(prefix, 'utf-8')
|
prefix = unicode(prefix, 'utf-8')
|
||||||
return unistr.startswith(prefix, start, len(unistr) if end is None else end)
|
return unistr.startswith(prefix, start, len(unistr) if end is None else end)
|
||||||
|
|
||||||
def translate(self, table, deletechars=''):
|
def translate(self, table, deletechars=''):
|
||||||
if isinstance(table, dict):
|
if isinstance(table, dict):
|
||||||
return str.__new__(Utf8, unicode(self, 'utf-8').translate(table).encode('utf-8'))
|
return str.__new__(Utf8, unicode(self, 'utf-8').translate(table).encode('utf-8'))
|
||||||
else:
|
else:
|
||||||
return str.__new__(Utf8, str.translate(self, table, deletechars))
|
return str.__new__(Utf8, str.translate(self, table, deletechars))
|
||||||
|
|
||||||
def endswith(self, prefix, start=0, end=None):
|
def endswith(self, prefix, start=0, end=None):
|
||||||
unistr = unicode(self, 'utf-8')
|
unistr = unicode(self, 'utf-8')
|
||||||
if isinstance(prefix, tuple):
|
if isinstance(prefix, tuple):
|
||||||
prefix = tuple(unicode(s,'utf-8') if isinstance(s, str) else s for s in prefix)
|
prefix = tuple(unicode(
|
||||||
|
s, 'utf-8') if isinstance(s, str) else s for s in prefix)
|
||||||
elif isinstance(prefix, str):
|
elif isinstance(prefix, str):
|
||||||
prefix = unicode(prefix, 'utf-8')
|
prefix = unicode(prefix, 'utf-8')
|
||||||
return unistr.endswith(prefix, start, len(unistr) if end is None else end)
|
return unistr.endswith(prefix, start, len(unistr) if end is None else end)
|
||||||
if hasattr(str, 'format'): # Python 2.5 hasn't got str.format() method
|
if hasattr(str, 'format'): # Python 2.5 hasn't got str.format() method
|
||||||
def format(self, *args, **kwargs):
|
def format(self, *args, **kwargs):
|
||||||
args = [unicode(s, 'utf-8') if isinstance(s, str) else s for s in args]
|
args = [unicode(
|
||||||
|
s, 'utf-8') if isinstance(s, str) else s for s in args]
|
||||||
kwargs = dict((unicode(k, 'utf-8') if isinstance(k, str) else k,
|
kwargs = dict((unicode(k, 'utf-8') if isinstance(k, str) else k,
|
||||||
unicode(v, 'utf-8') if isinstance(v, str) else v)
|
unicode(v, 'utf-8') if isinstance(v, str) else v)
|
||||||
for k, v in kwargs.iteritems())
|
for k, v in kwargs.iteritems())
|
||||||
return str.__new__(Utf8, unicode(self, 'utf-8').
|
return str.__new__(Utf8, unicode(self, 'utf-8').
|
||||||
format(*args, **kwargs).encode('utf-8'))
|
format(*args, **kwargs).encode('utf-8'))
|
||||||
|
|
||||||
def __mod__(self, right):
|
def __mod__(self, right):
|
||||||
if isinstance(right, tuple):
|
if isinstance(right, tuple):
|
||||||
right = tuple(unicode(v, 'utf-8') if isinstance(v, str) else v
|
right = tuple(unicode(v, 'utf-8') if isinstance(v, str) else v
|
||||||
@@ -258,10 +350,18 @@ class Utf8(str):
|
|||||||
elif isinstance(right, str):
|
elif isinstance(right, str):
|
||||||
right = unicode(right, 'utf-8')
|
right = unicode(right, 'utf-8')
|
||||||
return str.__new__(Utf8, unicode(self, 'utf-8').__mod__(right).encode('utf-8'))
|
return str.__new__(Utf8, unicode(self, 'utf-8').__mod__(right).encode('utf-8'))
|
||||||
def __ge__(self, string): return sort_key(self) >= sort_key(string)
|
|
||||||
def __gt__(self, string): return sort_key(self) > sort_key(string)
|
def __ge__(self, string):
|
||||||
def __le__(self, string): return sort_key(self) <= sort_key(string)
|
return sort_key(self) >= sort_key(string)
|
||||||
def __lt__(self, string): return sort_key(self) < sort_key(string)
|
|
||||||
|
def __gt__(self, string):
|
||||||
|
return sort_key(self) > sort_key(string)
|
||||||
|
|
||||||
|
def __le__(self, string):
|
||||||
|
return sort_key(self) <= sort_key(string)
|
||||||
|
|
||||||
|
def __lt__(self, string):
|
||||||
|
return sort_key(self) < sort_key(string)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@@ -478,9 +578,11 @@ if __name__ == '__main__':
|
|||||||
7
|
7
|
||||||
>>> a=Utf8('а б ц д е а б ц д е а\\tб ц д е')
|
>>> a=Utf8('а б ц д е а б ц д е а\\tб ц д е')
|
||||||
>>> a.split()
|
>>> a.split()
|
||||||
['а', 'б', 'ц', 'д', 'е', 'а', 'б', 'ц', 'д', 'е', 'а', 'б', 'ц', 'д', 'е']
|
['а', 'б', 'ц', 'д', 'е', 'а', 'б', 'ц', 'д',
|
||||||
|
'е', 'а', 'б', 'ц', 'д', 'е']
|
||||||
>>> a.rsplit()
|
>>> a.rsplit()
|
||||||
['а', 'б', 'ц', 'д', 'е', 'а', 'б', 'ц', 'д', 'е', 'а', 'б', 'ц', 'д', 'е']
|
['а', 'б', 'ц', 'д', 'е', 'а', 'б', 'ц', 'д',
|
||||||
|
'е', 'а', 'б', 'ц', 'д', 'е']
|
||||||
>>> a.expandtabs().split('б')
|
>>> a.expandtabs().split('б')
|
||||||
['а ', ' ц д е а ', ' ц д е а ', ' ц д е']
|
['а ', ' ц д е а ', ' ц д е а ', ' ц д е']
|
||||||
>>> a.expandtabs().rsplit('б')
|
>>> a.expandtabs().rsplit('б')
|
||||||
@@ -528,7 +630,8 @@ if __name__ == '__main__':
|
|||||||
1
|
1
|
||||||
>>> s.count('Є', 0, 5)
|
>>> s.count('Є', 0, 5)
|
||||||
0
|
0
|
||||||
>>> Utf8("Parameters: '%(проба)s', %(probe)04d, %(проба2)s") % { u"проба": s,
|
>>> Utf8(
|
||||||
|
"Parameters: '%(проба)s', %(probe)04d, %(проба2)s") % { u"проба": s,
|
||||||
... "not used": "???", "probe": 2, "проба2": u"ПРоба Probe" }
|
... "not used": "???", "probe": 2, "проба2": u"ПРоба Probe" }
|
||||||
"Parameters: 'ПРоба Є PRobe', 0002, ПРоба Probe"
|
"Parameters: 'ПРоба Є PRobe', 0002, ПРоба Probe"
|
||||||
>>> a=Utf8(u"Параметр: (%s)-(%s)-[%s]")
|
>>> a=Utf8(u"Параметр: (%s)-(%s)-[%s]")
|
||||||
@@ -591,7 +694,8 @@ if __name__ == '__main__':
|
|||||||
аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ
|
аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ
|
||||||
>>> Utf8().join(sorted(c.decode(), key=sort_key)) # convert to unicode for better performance
|
>>> Utf8().join(sorted(c.decode(), key=sort_key)) # convert to unicode for better performance
|
||||||
'аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ'
|
'аАбБвВгГґҐдДеЕєЄжЖзЗиИіІїЇйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩьЬюЮяЯ'
|
||||||
>>> for result in sorted(["Іа", "Астро", u"гала", Utf8("Гоша"), "Єва", "шовк", "аякс", "Їжа",
|
>>> for result in sorted(
|
||||||
|
["Іа", "Астро", u"гала", Utf8("Гоша"), "Єва", "шовк", "аякс", "Їжа",
|
||||||
... "ґанок", Utf8("Дар'я"), "білінг", "веб", u"Жужа", "проба", u"тест",
|
... "ґанок", Utf8("Дар'я"), "білінг", "веб", u"Жужа", "проба", u"тест",
|
||||||
... "абетка", "яблуко", "Юляся", "Київ", "лимонад", "ложка", "Матриця",
|
... "абетка", "яблуко", "Юляся", "Київ", "лимонад", "ложка", "Матриця",
|
||||||
... ], key=sort_key):
|
... ], key=sort_key):
|
||||||
@@ -630,7 +734,8 @@ if __name__ == '__main__':
|
|||||||
>>> a="中文字" # standard str type
|
>>> a="中文字" # standard str type
|
||||||
>>> L=list(a)
|
>>> L=list(a)
|
||||||
>>> L
|
>>> L
|
||||||
['\\xe4', '\\xb8', '\\xad', '\\xe6', '\\x96', '\\x87', '\\xe5', '\\xad', '\\x97']
|
['\\xe4', '\\xb8', '\\xad', '\\xe6', '\\x96', '\\x87',
|
||||||
|
'\\xe5', '\\xad', '\\x97']
|
||||||
>>> from string import maketrans
|
>>> from string import maketrans
|
||||||
>>> str_tab=maketrans('PRobe','12345')
|
>>> str_tab=maketrans('PRobe','12345')
|
||||||
>>> unicode_tab={ord(u'П'):ord(u'Ж'),
|
>>> unicode_tab={ord(u'П'):ord(u'Ж'),
|
||||||
|
|||||||
+15
-2
@@ -38,6 +38,7 @@ except ImportError:
|
|||||||
|
|
||||||
logger = logging.getLogger("web2py")
|
logger = logging.getLogger("web2py")
|
||||||
|
|
||||||
|
|
||||||
def compare(a, b):
|
def compare(a, b):
|
||||||
""" compares two strings and not vulnerable to timing attacks """
|
""" compares two strings and not vulnerable to timing attacks """
|
||||||
if len(a) != len(b):
|
if len(a) != len(b):
|
||||||
@@ -47,17 +48,19 @@ def compare(a,b):
|
|||||||
result |= ord(x) ^ ord(y)
|
result |= ord(x) ^ ord(y)
|
||||||
return result == 0
|
return result == 0
|
||||||
|
|
||||||
|
|
||||||
def md5_hash(text):
|
def md5_hash(text):
|
||||||
""" Generate a md5 hash with the given text """
|
""" Generate a md5 hash with the given text """
|
||||||
return hashlib.md5(text).hexdigest()
|
return hashlib.md5(text).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def simple_hash(text, key='', salt='', digest_alg='md5'):
|
def simple_hash(text, key='', salt='', digest_alg='md5'):
|
||||||
"""
|
"""
|
||||||
Generates hash with the given text using the specified
|
Generates hash with the given text using the specified
|
||||||
digest hashing algorithm
|
digest hashing algorithm
|
||||||
"""
|
"""
|
||||||
if not digest_alg:
|
if not digest_alg:
|
||||||
raise RuntimeError, "simple_hash with digest_alg=None"
|
raise RuntimeError("simple_hash with digest_alg=None")
|
||||||
elif not isinstance(digest_alg, str): # manual approach
|
elif not isinstance(digest_alg, str): # manual approach
|
||||||
h = digest_alg(text + key + salt)
|
h = digest_alg(text + key + salt)
|
||||||
elif digest_alg.startswith('pbkdf2'): # latest and coolest!
|
elif digest_alg.startswith('pbkdf2'): # latest and coolest!
|
||||||
@@ -72,6 +75,7 @@ def simple_hash(text, key='', salt = '', digest_alg = 'md5'):
|
|||||||
h.update(text + salt)
|
h.update(text + salt)
|
||||||
return h.hexdigest()
|
return h.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def get_digest(value):
|
def get_digest(value):
|
||||||
"""
|
"""
|
||||||
Returns a hashlib digest algorithm from a string
|
Returns a hashlib digest algorithm from a string
|
||||||
@@ -103,9 +107,11 @@ DIGEST_ALG_BY_SIZE = {
|
|||||||
512 / 4: 'sha512',
|
512 / 4: 'sha512',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def pad(s, n=32, padchar='.'):
|
def pad(s, n=32, padchar='.'):
|
||||||
return s + (32 - len(s) % 32) * padchar
|
return s + (32 - len(s) % 32) * padchar
|
||||||
|
|
||||||
|
|
||||||
def secure_dumps(data, encryption_key, hash_key=None, compression_level=None):
|
def secure_dumps(data, encryption_key, hash_key=None, compression_level=None):
|
||||||
if not hash_key:
|
if not hash_key:
|
||||||
hash_key = hashlib.sha1(encryption_key).hexdigest()
|
hash_key = hashlib.sha1(encryption_key).hexdigest()
|
||||||
@@ -118,6 +124,7 @@ def secure_dumps(data,encryption_key,hash_key=None,compression_level=None):
|
|||||||
signature = hmac.new(hash_key, encrypted_data).hexdigest()
|
signature = hmac.new(hash_key, encrypted_data).hexdigest()
|
||||||
return signature + ':' + encrypted_data
|
return signature + ':' + encrypted_data
|
||||||
|
|
||||||
|
|
||||||
def secure_loads(data, encryption_key, hash_key=None, compression_level=None):
|
def secure_loads(data, encryption_key, hash_key=None, compression_level=None):
|
||||||
if not ':' in data:
|
if not ':' in data:
|
||||||
return None
|
return None
|
||||||
@@ -139,6 +146,8 @@ def secure_loads(data,encryption_key,hash_key=None, compression_level=None):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
### compute constant CTOKENS
|
### compute constant CTOKENS
|
||||||
|
|
||||||
|
|
||||||
def initialize_urandom():
|
def initialize_urandom():
|
||||||
"""
|
"""
|
||||||
This function and the web2py_uuid follow from the following discussion:
|
This function and the web2py_uuid follow from the following discussion:
|
||||||
@@ -154,7 +163,8 @@ def initialize_urandom():
|
|||||||
"""
|
"""
|
||||||
node_id = uuid.getnode()
|
node_id = uuid.getnode()
|
||||||
microseconds = int(time.time() * 1e6)
|
microseconds = int(time.time() * 1e6)
|
||||||
ctokens = [((node_id + microseconds) >> ((i%6)*8)) % 256 for i in range(16)]
|
ctokens = [((node_id + microseconds) >> ((i % 6) * 8)) %
|
||||||
|
256 for i in range(16)]
|
||||||
random.seed(node_id + microseconds)
|
random.seed(node_id + microseconds)
|
||||||
try:
|
try:
|
||||||
os.urandom(1)
|
os.urandom(1)
|
||||||
@@ -180,6 +190,7 @@ This is not specific to web2py; consider deploying on a different operating syst
|
|||||||
return unpacked_ctokens, have_urandom
|
return unpacked_ctokens, have_urandom
|
||||||
UNPACKED_CTOKENS, HAVE_URANDOM = initialize_urandom()
|
UNPACKED_CTOKENS, HAVE_URANDOM = initialize_urandom()
|
||||||
|
|
||||||
|
|
||||||
def fast_urandom16(urandom=[], locker=threading.RLock()):
|
def fast_urandom16(urandom=[], locker=threading.RLock()):
|
||||||
"""
|
"""
|
||||||
this is 4x faster than calling os.urandom(16) and prevents
|
this is 4x faster than calling os.urandom(16) and prevents
|
||||||
@@ -196,6 +207,7 @@ def fast_urandom16(urandom=[], locker = threading.RLock()):
|
|||||||
finally:
|
finally:
|
||||||
locker.release()
|
locker.release()
|
||||||
|
|
||||||
|
|
||||||
def web2py_uuid(ctokens=UNPACKED_CTOKENS):
|
def web2py_uuid(ctokens=UNPACKED_CTOKENS):
|
||||||
"""
|
"""
|
||||||
This function follows from the following discussion:
|
This function follows from the following discussion:
|
||||||
@@ -218,6 +230,7 @@ def web2py_uuid(ctokens=UNPACKED_CTOKENS):
|
|||||||
|
|
||||||
REGEX_IPv4 = re.compile('(\d+)\.(\d+)\.(\d+)\.(\d+)')
|
REGEX_IPv4 = re.compile('(\d+)\.(\d+)\.(\d+)\.(\d+)')
|
||||||
|
|
||||||
|
|
||||||
def is_valid_ip_address(address):
|
def is_valid_ip_address(address):
|
||||||
"""
|
"""
|
||||||
>>> is_valid_ip_address('127.0')
|
>>> is_valid_ip_address('127.0')
|
||||||
|
|||||||
+71
-47
@@ -61,6 +61,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
have_current = False
|
have_current = False
|
||||||
|
|
||||||
|
|
||||||
def translate(text):
|
def translate(text):
|
||||||
if text is None:
|
if text is None:
|
||||||
return None
|
return None
|
||||||
@@ -69,9 +70,11 @@ def translate(text):
|
|||||||
return str(current.T(text))
|
return str(current.T(text))
|
||||||
return str(text)
|
return str(text)
|
||||||
|
|
||||||
|
|
||||||
def options_sorter(x, y):
|
def options_sorter(x, y):
|
||||||
return (str(x[1]).upper() > str(y[1]).upper() and 1) or -1
|
return (str(x[1]).upper() > str(y[1]).upper() and 1) or -1
|
||||||
|
|
||||||
|
|
||||||
class Validator(object):
|
class Validator(object):
|
||||||
"""
|
"""
|
||||||
Root for all validators, mainly for documentation purposes.
|
Root for all validators, mainly for documentation purposes.
|
||||||
@@ -294,7 +297,7 @@ class IS_LENGTH(Validator):
|
|||||||
return (value, None)
|
return (value, None)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
return (value, translate(self.error_message) \
|
return (value, translate(self.error_message)
|
||||||
% dict(min=self.minsize, max=self.maxsize))
|
% dict(min=self.minsize, max=self.maxsize))
|
||||||
|
|
||||||
|
|
||||||
@@ -416,7 +419,8 @@ class IS_IN_DB(Validator):
|
|||||||
_and=None,
|
_and=None,
|
||||||
):
|
):
|
||||||
from dal import Table
|
from dal import Table
|
||||||
if isinstance(field,Table): field = field._id
|
if isinstance(field, Table):
|
||||||
|
field = field._id
|
||||||
|
|
||||||
if hasattr(dbset, 'define_table'):
|
if hasattr(dbset, 'define_table'):
|
||||||
self.dbset = dbset()
|
self.dbset = dbset()
|
||||||
@@ -471,7 +475,8 @@ class IS_IN_DB(Validator):
|
|||||||
records = self.dbset(table).select(*fields, **dd)
|
records = self.dbset(table).select(*fields, **dd)
|
||||||
else:
|
else:
|
||||||
orderby = self.orderby or \
|
orderby = self.orderby or \
|
||||||
reduce(lambda a,b:a|b,(f for f in fields if not f.name=='id'))
|
reduce(lambda a, b: a | b, (
|
||||||
|
f for f in fields if not f.name == 'id'))
|
||||||
dd = dict(orderby=orderby, cache=self.cache, cacheable=True)
|
dd = dict(orderby=orderby, cache=self.cache, cacheable=True)
|
||||||
records = self.dbset(table).select(table.ALL, **dd)
|
records = self.dbset(table).select(table.ALL, **dd)
|
||||||
self.theset = [str(r[self.kfield]) for r in records]
|
self.theset = [str(r[self.kfield]) for r in records]
|
||||||
@@ -509,6 +514,7 @@ class IS_IN_DB(Validator):
|
|||||||
return (values, None)
|
return (values, None)
|
||||||
else:
|
else:
|
||||||
from dal import GoogleDatastoreAdapter
|
from dal import GoogleDatastoreAdapter
|
||||||
|
|
||||||
def count(values, s=self.dbset, f=field):
|
def count(values, s=self.dbset, f=field):
|
||||||
return s(f.belongs(map(int, values))).count()
|
return s(f.belongs(map(int, values))).count()
|
||||||
if isinstance(self.dbset.db._adapter, GoogleDatastoreAdapter):
|
if isinstance(self.dbset.db._adapter, GoogleDatastoreAdapter):
|
||||||
@@ -552,7 +558,8 @@ class IS_NOT_IN_DB(Validator):
|
|||||||
):
|
):
|
||||||
|
|
||||||
from dal import Table
|
from dal import Table
|
||||||
if isinstance(field,Table): field = field._id
|
if isinstance(field, Table):
|
||||||
|
field = field._id
|
||||||
|
|
||||||
if hasattr(dbset, 'define_table'):
|
if hasattr(dbset, 'define_table'):
|
||||||
self.dbset = dbset()
|
self.dbset = dbset()
|
||||||
@@ -640,12 +647,14 @@ class IS_INT_IN_RANGE(Validator):
|
|||||||
self.maximum = int(maximum)
|
self.maximum = int(maximum)
|
||||||
if error_message is None:
|
if error_message is None:
|
||||||
error_message = 'enter an integer less than or equal to %(max)g'
|
error_message = 'enter an integer less than or equal to %(max)g'
|
||||||
self.error_message = translate(error_message) % dict(max=self.maximum-1)
|
self.error_message = translate(
|
||||||
|
error_message) % dict(max=self.maximum - 1)
|
||||||
elif maximum is None:
|
elif maximum is None:
|
||||||
self.minimum = int(minimum)
|
self.minimum = int(minimum)
|
||||||
if error_message is None:
|
if error_message is None:
|
||||||
error_message = 'enter an integer greater than or equal to %(min)g'
|
error_message = 'enter an integer greater than or equal to %(min)g'
|
||||||
self.error_message = translate(error_message) % dict(min=self.minimum)
|
self.error_message = translate(
|
||||||
|
error_message) % dict(min=self.minimum)
|
||||||
else:
|
else:
|
||||||
self.minimum = int(minimum)
|
self.minimum = int(minimum)
|
||||||
self.maximum = int(maximum)
|
self.maximum = int(maximum)
|
||||||
@@ -672,12 +681,16 @@ class IS_INT_IN_RANGE(Validator):
|
|||||||
pass
|
pass
|
||||||
return (value, self.error_message)
|
return (value, self.error_message)
|
||||||
|
|
||||||
|
|
||||||
def str2dec(number):
|
def str2dec(number):
|
||||||
s = str(number)
|
s = str(number)
|
||||||
if not '.' in s: s+='.00'
|
if not '.' in s:
|
||||||
else: s+='0'*(2-len(s.split('.')[1]))
|
s += '.00'
|
||||||
|
else:
|
||||||
|
s += '0' * (2 - len(s.split('.')[1]))
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
class IS_FLOAT_IN_RANGE(Validator):
|
class IS_FLOAT_IN_RANGE(Validator):
|
||||||
"""
|
"""
|
||||||
Determine that the argument is (or can be represented as) a float,
|
Determine that the argument is (or can be represented as) a float,
|
||||||
@@ -871,6 +884,7 @@ class IS_DECIMAL_IN_RANGE(Validator):
|
|||||||
def formatter(self, value):
|
def formatter(self, value):
|
||||||
return str2dec(value).replace('.', self.dot)
|
return str2dec(value).replace('.', self.dot)
|
||||||
|
|
||||||
|
|
||||||
def is_empty(value, empty_regex=None):
|
def is_empty(value, empty_regex=None):
|
||||||
"test empty field"
|
"test empty field"
|
||||||
if isinstance(value, (str, unicode)):
|
if isinstance(value, (str, unicode)):
|
||||||
@@ -881,6 +895,7 @@ def is_empty(value, empty_regex=None):
|
|||||||
return (value, True)
|
return (value, True)
|
||||||
return (value, False)
|
return (value, False)
|
||||||
|
|
||||||
|
|
||||||
class IS_NOT_EMPTY(Validator):
|
class IS_NOT_EMPTY(Validator):
|
||||||
"""
|
"""
|
||||||
example::
|
example::
|
||||||
@@ -1048,7 +1063,8 @@ class IS_EMAIL(Validator):
|
|||||||
localhost
|
localhost
|
||||||
|
|
|
|
||||||
(
|
(
|
||||||
[a-z0-9] # [sub]domain begins with alphanumeric
|
[a-z0-9]
|
||||||
|
# [sub]domain begins with alphanumeric
|
||||||
(
|
(
|
||||||
[-\w]* # alphanumeric, underscore, dot, hyphen
|
[-\w]* # alphanumeric, underscore, dot, hyphen
|
||||||
[a-z0-9] # ending alphanumeric
|
[a-z0-9] # ending alphanumeric
|
||||||
@@ -1345,7 +1361,7 @@ def unicode_to_ascii_url(url, prepend_scheme):
|
|||||||
unicode(scheme_to_prepend) + u'://' + url).groups()
|
unicode(scheme_to_prepend) + u'://' + url).groups()
|
||||||
#if we still can't find the authority
|
#if we still can't find the authority
|
||||||
if not groups[3]:
|
if not groups[3]:
|
||||||
raise Exception('No authority component found, '+ \
|
raise Exception('No authority component found, ' +
|
||||||
'could not decode unicode to US-ASCII')
|
'could not decode unicode to US-ASCII')
|
||||||
|
|
||||||
#We're here if we found an authority, let's rebuild the URL
|
#We're here if we found an authority, let's rebuild the URL
|
||||||
@@ -1395,7 +1411,6 @@ class IS_GENERIC_URL(Validator):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
error_message='enter a valid URL',
|
error_message='enter a valid URL',
|
||||||
@@ -1418,9 +1433,8 @@ class IS_GENERIC_URL(Validator):
|
|||||||
self.allowed_schemes = allowed_schemes
|
self.allowed_schemes = allowed_schemes
|
||||||
self.prepend_scheme = prepend_scheme
|
self.prepend_scheme = prepend_scheme
|
||||||
if self.prepend_scheme not in self.allowed_schemes:
|
if self.prepend_scheme not in self.allowed_schemes:
|
||||||
raise SyntaxError, \
|
raise SyntaxError("prepend_scheme='%s' is not in allowed_schemes=%s"
|
||||||
"prepend_scheme='%s' is not in allowed_schemes=%s" \
|
% (self.prepend_scheme, self.allowed_schemes))
|
||||||
% (self.prepend_scheme, self.allowed_schemes)
|
|
||||||
|
|
||||||
GENERIC_URL = re.compile(r"%[^0-9A-Fa-f]{2}|%[^0-9A-Fa-f][0-9A-Fa-f]|%[0-9A-Fa-f][^0-9A-Fa-f]|%$|%[0-9A-Fa-f]$|%[^0-9A-Fa-f]$")
|
GENERIC_URL = re.compile(r"%[^0-9A-Fa-f]{2}|%[^0-9A-Fa-f][0-9A-Fa-f]|%[0-9A-Fa-f][^0-9A-Fa-f]|%$|%[0-9A-Fa-f]$|%[^0-9A-Fa-f]$")
|
||||||
GENERIC_URL_VALID = re.compile(r"[A-Za-z0-9;/?:@&=+$,\-_\.!~*'\(\)%#]+$")
|
GENERIC_URL_VALID = re.compile(r"[A-Za-z0-9;/?:@&=+$,\-_\.!~*'\(\)%#]+$")
|
||||||
@@ -1810,7 +1824,8 @@ class IS_HTTP_URL(Validator):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
GENERIC_VALID_IP = re.compile("([\w.!~*'|;:&=+$,-]+@)?\d+\.\d+\.\d+\.\d+(:\d*)*$")
|
GENERIC_VALID_IP = re.compile(
|
||||||
|
"([\w.!~*'|;:&=+$,-]+@)?\d+\.\d+\.\d+\.\d+(:\d*)*$")
|
||||||
GENERIC_VALID_DOMAIN = re.compile("([\w.!~*'|;:&=+$,-]+@)?(([A-Za-z0-9]+[A-Za-z0-9\-]*[A-Za-z0-9]+\.)*([A-Za-z0-9]+\.)*)*([A-Za-z]+[A-Za-z0-9\-]*[A-Za-z0-9]+)\.?(:\d*)*$")
|
GENERIC_VALID_DOMAIN = re.compile("([\w.!~*'|;:&=+$,-]+@)?(([A-Za-z0-9]+[A-Za-z0-9\-]*[A-Za-z0-9]+\.)*([A-Za-z0-9]+\.)*)*([A-Za-z]+[A-Za-z0-9\-]*[A-Za-z0-9]+)\.?(:\d*)*$")
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -1837,14 +1852,12 @@ class IS_HTTP_URL(Validator):
|
|||||||
|
|
||||||
for i in self.allowed_schemes:
|
for i in self.allowed_schemes:
|
||||||
if i not in http_schemes:
|
if i not in http_schemes:
|
||||||
raise SyntaxError, \
|
raise SyntaxError("allowed_scheme value '%s' is not in %s" %
|
||||||
"allowed_scheme value '%s' is not in %s" % \
|
(i, http_schemes))
|
||||||
(i, http_schemes)
|
|
||||||
|
|
||||||
if self.prepend_scheme not in self.allowed_schemes:
|
if self.prepend_scheme not in self.allowed_schemes:
|
||||||
raise SyntaxError, \
|
raise SyntaxError("prepend_scheme='%s' is not in allowed_schemes=%s" %
|
||||||
"prepend_scheme='%s' is not in allowed_schemes=%s" % \
|
(self.prepend_scheme, self.allowed_schemes))
|
||||||
(self.prepend_scheme, self.allowed_schemes)
|
|
||||||
|
|
||||||
def __call__(self, value):
|
def __call__(self, value):
|
||||||
"""
|
"""
|
||||||
@@ -1870,7 +1883,8 @@ class IS_HTTP_URL(Validator):
|
|||||||
return (value, None)
|
return (value, None)
|
||||||
else:
|
else:
|
||||||
# else if authority is a valid domain name
|
# else if authority is a valid domain name
|
||||||
domainMatch = self.GENERIC_VALID_DOMAIN.match(authority)
|
domainMatch = self.GENERIC_VALID_DOMAIN.match(
|
||||||
|
authority)
|
||||||
if domainMatch:
|
if domainMatch:
|
||||||
# if the top-level domain really exists
|
# if the top-level domain really exists
|
||||||
if domainMatch.group(5).lower()\
|
if domainMatch.group(5).lower()\
|
||||||
@@ -2002,14 +2016,13 @@ class IS_URL(Validator):
|
|||||||
self.error_message = error_message
|
self.error_message = error_message
|
||||||
self.mode = mode.lower()
|
self.mode = mode.lower()
|
||||||
if not self.mode in ['generic', 'http']:
|
if not self.mode in ['generic', 'http']:
|
||||||
raise SyntaxError, "invalid mode '%s' in IS_URL" % self.mode
|
raise SyntaxError("invalid mode '%s' in IS_URL" % self.mode)
|
||||||
self.allowed_schemes = allowed_schemes
|
self.allowed_schemes = allowed_schemes
|
||||||
|
|
||||||
if self.allowed_schemes:
|
if self.allowed_schemes:
|
||||||
if prepend_scheme not in self.allowed_schemes:
|
if prepend_scheme not in self.allowed_schemes:
|
||||||
raise SyntaxError, \
|
raise SyntaxError("prepend_scheme='%s' is not in allowed_schemes=%s"
|
||||||
"prepend_scheme='%s' is not in allowed_schemes=%s" \
|
% (prepend_scheme, self.allowed_schemes))
|
||||||
% (prepend_scheme, self.allowed_schemes)
|
|
||||||
|
|
||||||
# if allowed_schemes is None, then we will defer testing
|
# if allowed_schemes is None, then we will defer testing
|
||||||
# prepend_scheme's validity to a sub-method
|
# prepend_scheme's validity to a sub-method
|
||||||
@@ -2036,7 +2049,7 @@ class IS_URL(Validator):
|
|||||||
allowed_schemes=self.allowed_schemes,
|
allowed_schemes=self.allowed_schemes,
|
||||||
prepend_scheme=self.prepend_scheme)
|
prepend_scheme=self.prepend_scheme)
|
||||||
else:
|
else:
|
||||||
raise SyntaxError, "invalid mode '%s' in IS_URL" % self.mode
|
raise SyntaxError("invalid mode '%s' in IS_URL" % self.mode)
|
||||||
|
|
||||||
if type(value) != unicode:
|
if type(value) != unicode:
|
||||||
return subMethod(value)
|
return subMethod(value)
|
||||||
@@ -2118,8 +2131,7 @@ class IS_TIME(Validator):
|
|||||||
h = h + 12
|
h = h + 12
|
||||||
if not (h in range(24) and m in range(60) and s
|
if not (h in range(24) and m in range(60) and s
|
||||||
in range(60)):
|
in range(60)):
|
||||||
raise ValueError\
|
raise ValueError('Hours or minutes or seconds are outside of allowed range')
|
||||||
('Hours or minutes or seconds are outside of allowed range')
|
|
||||||
value = datetime.time(h, m, s)
|
value = datetime.time(h, m, s)
|
||||||
return (value, None)
|
return (value, None)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@@ -2214,7 +2226,6 @@ class IS_DATETIME(Validator):
|
|||||||
self.extremes.update(IS_DATETIME.nice(self.format))
|
self.extremes.update(IS_DATETIME.nice(self.format))
|
||||||
return (value, translate(self.error_message) % self.extremes)
|
return (value, translate(self.error_message) % self.extremes)
|
||||||
|
|
||||||
|
|
||||||
def formatter(self, value):
|
def formatter(self, value):
|
||||||
format = self.format
|
format = self.format
|
||||||
year = value.year
|
year = value.year
|
||||||
@@ -2227,6 +2238,7 @@ class IS_DATETIME(Validator):
|
|||||||
value.hour, value.minute, value.second)
|
value.hour, value.minute, value.second)
|
||||||
return d.strftime(format)
|
return d.strftime(format)
|
||||||
|
|
||||||
|
|
||||||
class IS_DATE_IN_RANGE(IS_DATE):
|
class IS_DATE_IN_RANGE(IS_DATE):
|
||||||
"""
|
"""
|
||||||
example::
|
example::
|
||||||
@@ -2399,7 +2411,8 @@ def urlify(value, maxlen=80, keep_underscores=False):
|
|||||||
s = re.sub('&\w+;', '', s) # strip html entities
|
s = re.sub('&\w+;', '', s) # strip html entities
|
||||||
if keep_underscores:
|
if keep_underscores:
|
||||||
s = re.sub('\s+', '-', s) # whitespace to hyphens
|
s = re.sub('\s+', '-', s) # whitespace to hyphens
|
||||||
s = re.sub('[^\w\-]', '', s) # strip all but alphanumeric/underscore/hyphen
|
s = re.sub('[^\w\-]', '', s)
|
||||||
|
# strip all but alphanumeric/underscore/hyphen
|
||||||
else:
|
else:
|
||||||
s = re.sub('[\s_]+', '-', s) # whitespace & underscores to hyphens
|
s = re.sub('[\s_]+', '-', s) # whitespace & underscores to hyphens
|
||||||
s = re.sub('[^a-z0-9\-]', '', s) # strip all but alphanumeric/hyphen
|
s = re.sub('[^a-z0-9\-]', '', s) # strip all but alphanumeric/hyphen
|
||||||
@@ -2467,6 +2480,7 @@ class IS_SLUG(Validator):
|
|||||||
return (value, translate(self.error_message))
|
return (value, translate(self.error_message))
|
||||||
return (urlify(value, self.maxlen, self.keep_underscores), None)
|
return (urlify(value, self.maxlen, self.keep_underscores), None)
|
||||||
|
|
||||||
|
|
||||||
class IS_EMPTY_OR(Validator):
|
class IS_EMPTY_OR(Validator):
|
||||||
"""
|
"""
|
||||||
dummy class for testing IS_EMPTY_OR
|
dummy class for testing IS_EMPTY_OR
|
||||||
@@ -2519,7 +2533,8 @@ class IS_EMPTY_OR(Validator):
|
|||||||
error = None
|
error = None
|
||||||
for item in self.other:
|
for item in self.other:
|
||||||
value, error = item(value)
|
value, error = item(value)
|
||||||
if error: break
|
if error:
|
||||||
|
break
|
||||||
return value, error
|
return value, error
|
||||||
else:
|
else:
|
||||||
return self.other(value)
|
return self.other(value)
|
||||||
@@ -2550,6 +2565,7 @@ class CLEANUP(Validator):
|
|||||||
v = self.regex.sub('', str(value).strip())
|
v = self.regex.sub('', str(value).strip())
|
||||||
return (v, None)
|
return (v, None)
|
||||||
|
|
||||||
|
|
||||||
class LazyCrypt(object):
|
class LazyCrypt(object):
|
||||||
"""
|
"""
|
||||||
Stores a lazy password hash
|
Stores a lazy password hash
|
||||||
@@ -2736,7 +2752,9 @@ upperset = frozenset(unicode('ABCDEFGHIJKLMNOPQRSTUVWXYZ'))
|
|||||||
numberset = frozenset(unicode('0123456789'))
|
numberset = frozenset(unicode('0123456789'))
|
||||||
sym1set = frozenset(unicode('!@#$%^&*()'))
|
sym1set = frozenset(unicode('!@#$%^&*()'))
|
||||||
sym2set = frozenset(unicode('~`-_=+[]{}\\|;:\'",.<>?/'))
|
sym2set = frozenset(unicode('~`-_=+[]{}\\|;:\'",.<>?/'))
|
||||||
otherset = frozenset(unicode('0123456789abcdefghijklmnopqrstuvwxyz')) # anything else
|
otherset = frozenset(
|
||||||
|
unicode('0123456789abcdefghijklmnopqrstuvwxyz')) # anything else
|
||||||
|
|
||||||
|
|
||||||
def calc_entropy(string):
|
def calc_entropy(string):
|
||||||
" calculate a simple entropy for a given string "
|
" calculate a simple entropy for a given string "
|
||||||
@@ -2764,9 +2782,11 @@ def calc_entropy(string):
|
|||||||
if inset is not lastset:
|
if inset is not lastset:
|
||||||
alphabet += 1 # credit for set transitions
|
alphabet += 1 # credit for set transitions
|
||||||
lastset = cset
|
lastset = cset
|
||||||
entropy = len(string) * math.log(alphabet) / 0.6931471805599453 # math.log(2)
|
entropy = len(
|
||||||
|
string) * math.log(alphabet) / 0.6931471805599453 # math.log(2)
|
||||||
return round(entropy, 2)
|
return round(entropy, 2)
|
||||||
|
|
||||||
|
|
||||||
class IS_STRONG(object):
|
class IS_STRONG(object):
|
||||||
"""
|
"""
|
||||||
example::
|
example::
|
||||||
@@ -2777,7 +2797,8 @@ class IS_STRONG(object):
|
|||||||
enforces complexity requirements on a field
|
enforces complexity requirements on a field
|
||||||
|
|
||||||
>>> IS_STRONG(es=True)('Abcd1234')
|
>>> IS_STRONG(es=True)('Abcd1234')
|
||||||
('Abcd1234', 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|')
|
('Abcd1234',
|
||||||
|
'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|')
|
||||||
>>> IS_STRONG(es=True)('Abcd1234!')
|
>>> IS_STRONG(es=True)('Abcd1234!')
|
||||||
('Abcd1234!', None)
|
('Abcd1234!', None)
|
||||||
>>> IS_STRONG(es=True, entropy=1)('a')
|
>>> IS_STRONG(es=True, entropy=1)('a')
|
||||||
@@ -2828,7 +2849,7 @@ class IS_STRONG(object):
|
|||||||
if self.entropy is not None:
|
if self.entropy is not None:
|
||||||
entropy = calc_entropy(value)
|
entropy = calc_entropy(value)
|
||||||
if entropy < self.entropy:
|
if entropy < self.entropy:
|
||||||
failures.append(translate("Entropy (%(have)s) less than required (%(need)s)") \
|
failures.append(translate("Entropy (%(have)s) less than required (%(need)s)")
|
||||||
% dict(have=entropy, need=self.entropy))
|
% dict(have=entropy, need=self.entropy))
|
||||||
if type(self.min) == int and self.min > 0:
|
if type(self.min) == int and self.min > 0:
|
||||||
if not len(value) >= self.min:
|
if not len(value) >= self.min:
|
||||||
@@ -2840,31 +2861,33 @@ class IS_STRONG(object):
|
|||||||
all_special = [ch in value for ch in self.specials]
|
all_special = [ch in value for ch in self.specials]
|
||||||
if self.special > 0:
|
if self.special > 0:
|
||||||
if not all_special.count(True) >= self.special:
|
if not all_special.count(True) >= self.special:
|
||||||
failures.append(translate("Must include at least %s of the following: %s") \
|
failures.append(translate("Must include at least %s of the following: %s")
|
||||||
% (self.special, self.specials))
|
% (self.special, self.specials))
|
||||||
if self.invalid:
|
if self.invalid:
|
||||||
all_invalid = [ch in value for ch in self.invalid]
|
all_invalid = [ch in value for ch in self.invalid]
|
||||||
if all_invalid.count(True) > 0:
|
if all_invalid.count(True) > 0:
|
||||||
failures.append(translate("May not contain any of the following: %s") \
|
failures.append(translate("May not contain any of the following: %s")
|
||||||
% self.invalid)
|
% self.invalid)
|
||||||
if type(self.upper) == int:
|
if type(self.upper) == int:
|
||||||
all_upper = re.findall("[A-Z]", value)
|
all_upper = re.findall("[A-Z]", value)
|
||||||
if self.upper > 0:
|
if self.upper > 0:
|
||||||
if not len(all_upper) >= self.upper:
|
if not len(all_upper) >= self.upper:
|
||||||
failures.append(translate("Must include at least %s upper case") \
|
failures.append(translate("Must include at least %s upper case")
|
||||||
% str(self.upper))
|
% str(self.upper))
|
||||||
else:
|
else:
|
||||||
if len(all_upper) > 0:
|
if len(all_upper) > 0:
|
||||||
failures.append(translate("May not include any upper case letters"))
|
failures.append(
|
||||||
|
translate("May not include any upper case letters"))
|
||||||
if type(self.lower) == int:
|
if type(self.lower) == int:
|
||||||
all_lower = re.findall("[a-z]", value)
|
all_lower = re.findall("[a-z]", value)
|
||||||
if self.lower > 0:
|
if self.lower > 0:
|
||||||
if not len(all_lower) >= self.lower:
|
if not len(all_lower) >= self.lower:
|
||||||
failures.append(translate("Must include at least %s lower case") \
|
failures.append(translate("Must include at least %s lower case")
|
||||||
% str(self.lower))
|
% str(self.lower))
|
||||||
else:
|
else:
|
||||||
if len(all_lower) > 0:
|
if len(all_lower) > 0:
|
||||||
failures.append(translate("May not include any lower case letters"))
|
failures.append(
|
||||||
|
translate("May not include any lower case letters"))
|
||||||
if type(self.number) == int:
|
if type(self.number) == int:
|
||||||
all_number = re.findall("[0-9]", value)
|
all_number = re.findall("[0-9]", value)
|
||||||
if self.number > 0:
|
if self.number > 0:
|
||||||
@@ -2872,7 +2895,7 @@ class IS_STRONG(object):
|
|||||||
if self.number > 1:
|
if self.number > 1:
|
||||||
numbers = "numbers"
|
numbers = "numbers"
|
||||||
if not len(all_number) >= self.number:
|
if not len(all_number) >= self.number:
|
||||||
failures.append(translate("Must include at least %s %s") \
|
failures.append(translate("Must include at least %s %s")
|
||||||
% (str(self.number), numbers))
|
% (str(self.number), numbers))
|
||||||
else:
|
else:
|
||||||
if len(all_number) > 0:
|
if len(all_number) > 0:
|
||||||
@@ -3232,13 +3255,13 @@ class IS_IPV4(Validator):
|
|||||||
for bottom, top in zip(self.minip, self.maxip):
|
for bottom, top in zip(self.minip, self.maxip):
|
||||||
if self.invert != (bottom <= number <= top):
|
if self.invert != (bottom <= number <= top):
|
||||||
ok = True
|
ok = True
|
||||||
if not (self.is_localhost is None or self.is_localhost == \
|
if not (self.is_localhost is None or self.is_localhost ==
|
||||||
(number == self.localhost)):
|
(number == self.localhost)):
|
||||||
ok = False
|
ok = False
|
||||||
if not (self.is_private is None or self.is_private == \
|
if not (self.is_private is None or self.is_private ==
|
||||||
(sum([number[0] <= number <= number[1] for number in self.private]) > 0)):
|
(sum([number[0] <= number <= number[1] for number in self.private]) > 0)):
|
||||||
ok = False
|
ok = False
|
||||||
if not (self.is_automatic is None or self.is_automatic == \
|
if not (self.is_automatic is None or self.is_automatic ==
|
||||||
(self.automatic[0] <= number <= self.automatic[1])):
|
(self.automatic[0] <= number <= self.automatic[1])):
|
||||||
ok = False
|
ok = False
|
||||||
if ok:
|
if ok:
|
||||||
@@ -3247,4 +3270,5 @@ class IS_IPV4(Validator):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import doctest
|
import doctest
|
||||||
doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS)
|
doctest.testmod(
|
||||||
|
optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS)
|
||||||
+26
-16
@@ -30,7 +30,8 @@ from settings import global_settings
|
|||||||
from shell import run, test
|
from shell import run, test
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import Tkinter, tkMessageBox
|
import Tkinter
|
||||||
|
import tkMessageBox
|
||||||
import contrib.taskbar_widget
|
import contrib.taskbar_widget
|
||||||
from winservice import web2py_windows_service_handler
|
from winservice import web2py_windows_service_handler
|
||||||
have_winservice = True
|
have_winservice = True
|
||||||
@@ -44,7 +45,8 @@ except NameError:
|
|||||||
BaseException = Exception
|
BaseException = Exception
|
||||||
|
|
||||||
ProgramName = 'web2py Web Framework'
|
ProgramName = 'web2py Web Framework'
|
||||||
ProgramAuthor = 'Created by Massimo Di Pierro, Copyright 2007-' + str(datetime.datetime.now().year)
|
ProgramAuthor = 'Created by Massimo Di Pierro, Copyright 2007-' + str(
|
||||||
|
datetime.datetime.now().year)
|
||||||
ProgramVersion = read_file('VERSION').strip()
|
ProgramVersion = read_file('VERSION').strip()
|
||||||
|
|
||||||
ProgramInfo = '''%s
|
ProgramInfo = '''%s
|
||||||
@@ -58,6 +60,7 @@ if not sys.version[:3] in ['2.4', '2.5', '2.6', '2.7']:
|
|||||||
|
|
||||||
logger = logging.getLogger("web2py")
|
logger = logging.getLogger("web2py")
|
||||||
|
|
||||||
|
|
||||||
def run_system_tests():
|
def run_system_tests():
|
||||||
major_version = sys.version_info[0]
|
major_version = sys.version_info[0]
|
||||||
minor_version = sys.version_info[1]
|
minor_version = sys.version_info[1]
|
||||||
@@ -77,6 +80,7 @@ def run_system_tests():
|
|||||||
ret = 256
|
ret = 256
|
||||||
sys.exit(ret and 1)
|
sys.exit(ret and 1)
|
||||||
|
|
||||||
|
|
||||||
class IO(object):
|
class IO(object):
|
||||||
""" """
|
""" """
|
||||||
|
|
||||||
@@ -240,7 +244,8 @@ class web2pyDialog(object):
|
|||||||
self.root, text='%s (%s)' % (legend, ip),
|
self.root, text='%s (%s)' % (legend, ip),
|
||||||
variable=self.selected_ip, value=ip)
|
variable=self.selected_ip, value=ip)
|
||||||
self.ips[ip].grid(row=row, column=1, sticky=sticky)
|
self.ips[ip].grid(row=row, column=1, sticky=sticky)
|
||||||
if row==0: self.ips[ip].select()
|
if row == 0:
|
||||||
|
self.ips[ip].select()
|
||||||
row += 1
|
row += 1
|
||||||
shift = row
|
shift = row
|
||||||
# Port
|
# Port
|
||||||
@@ -343,7 +348,8 @@ class web2pyDialog(object):
|
|||||||
p = Process(target=run, args=args)
|
p = Process(target=run, args=args)
|
||||||
self.scheduler_processes[app] = p
|
self.scheduler_processes[app] = p
|
||||||
self.update_schedulers()
|
self.update_schedulers()
|
||||||
print "Currently running %s scheduler processes" % (len(self.scheduler_processes))
|
print "Currently running %s scheduler processes" % (
|
||||||
|
len(self.scheduler_processes))
|
||||||
p.start()
|
p.start()
|
||||||
print "Processes started"
|
print "Processes started"
|
||||||
|
|
||||||
@@ -360,7 +366,6 @@ class web2pyDialog(object):
|
|||||||
t = threading.Thread(target=self.start_schedulers, args=(app,))
|
t = threading.Thread(target=self.start_schedulers, args=(app,))
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
|
|
||||||
def checkTaskBar(self):
|
def checkTaskBar(self):
|
||||||
""" Check taskbar status """
|
""" Check taskbar status """
|
||||||
|
|
||||||
@@ -573,7 +578,8 @@ def console():
|
|||||||
|
|
||||||
description = textwrap.dedent(description)
|
description = textwrap.dedent(description)
|
||||||
|
|
||||||
parser = optparse.OptionParser(usage, None, optparse.Option, ProgramVersion)
|
parser = optparse.OptionParser(
|
||||||
|
usage, None, optparse.Option, ProgramVersion)
|
||||||
|
|
||||||
parser.description = description
|
parser.description = description
|
||||||
|
|
||||||
@@ -857,7 +863,6 @@ def console():
|
|||||||
dest='nobanner',
|
dest='nobanner',
|
||||||
help='Do not print header banner')
|
help='Do not print header banner')
|
||||||
|
|
||||||
|
|
||||||
msg = 'listen on multiple addresses: "ip:port:cert:key:ca_cert;ip2:port2:cert2:key2:ca_cert2;..." (:cert:key optional; no spaces)'
|
msg = 'listen on multiple addresses: "ip:port:cert:key:ca_cert;ip2:port2:cert2:key2:ca_cert2;..." (:cert:key optional; no spaces)'
|
||||||
parser.add_option('--interfaces',
|
parser.add_option('--interfaces',
|
||||||
action='store',
|
action='store',
|
||||||
@@ -865,7 +870,6 @@ def console():
|
|||||||
default=None,
|
default=None,
|
||||||
help=msg)
|
help=msg)
|
||||||
|
|
||||||
|
|
||||||
msg = 'runs web2py tests'
|
msg = 'runs web2py tests'
|
||||||
parser.add_option('--run_system_tests',
|
parser.add_option('--run_system_tests',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
@@ -873,9 +877,12 @@ def console():
|
|||||||
default=False,
|
default=False,
|
||||||
help=msg)
|
help=msg)
|
||||||
|
|
||||||
if '-A' in sys.argv: k = sys.argv.index('-A')
|
if '-A' in sys.argv:
|
||||||
elif '--args' in sys.argv: k = sys.argv.index('--args')
|
k = sys.argv.index('-A')
|
||||||
else: k=len(sys.argv)
|
elif '--args' in sys.argv:
|
||||||
|
k = sys.argv.index('--args')
|
||||||
|
else:
|
||||||
|
k = len(sys.argv)
|
||||||
sys.argv, other_args = sys.argv[:k], sys.argv[k + 1:]
|
sys.argv, other_args = sys.argv[:k], sys.argv[k + 1:]
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
options.args = [options.run] + other_args
|
options.args = [options.run] + other_args
|
||||||
@@ -951,12 +958,14 @@ def console():
|
|||||||
|
|
||||||
return (options, args)
|
return (options, args)
|
||||||
|
|
||||||
|
|
||||||
def check_existent_app(options, appname):
|
def check_existent_app(options, appname):
|
||||||
if os.path.isdir(os.path.join(options.folder, 'applications', appname)):
|
if os.path.isdir(os.path.join(options.folder, 'applications', appname)):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_code_for_scheduler(app, options):
|
def get_code_for_scheduler(app, options):
|
||||||
if len(app) == 1 or app[1] == None:
|
if len(app) == 1 or app[1] is None:
|
||||||
code = "from gluon import current;current._scheduler.loop()"
|
code = "from gluon import current;current._scheduler.loop()"
|
||||||
else:
|
else:
|
||||||
code = "from gluon import current;current._scheduler.group_names = ['%s'];"
|
code = "from gluon import current;current._scheduler.group_names = ['%s'];"
|
||||||
@@ -968,6 +977,7 @@ def get_code_for_scheduler(app, options):
|
|||||||
return None, None
|
return None, None
|
||||||
return app_, code
|
return app_, code
|
||||||
|
|
||||||
|
|
||||||
def start_schedulers(options):
|
def start_schedulers(options):
|
||||||
try:
|
try:
|
||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
@@ -1024,7 +1034,6 @@ def start(cron=True):
|
|||||||
if not options.nobanner:
|
if not options.nobanner:
|
||||||
print 'Database drivers available: %s' % ', '.join(DRIVERS)
|
print 'Database drivers available: %s' % ', '.join(DRIVERS)
|
||||||
|
|
||||||
|
|
||||||
# ## if -L load options from options.config file
|
# ## if -L load options from options.config file
|
||||||
if options.config:
|
if options.config:
|
||||||
try:
|
try:
|
||||||
@@ -1066,7 +1075,8 @@ def start(cron=True):
|
|||||||
logger.debug('Starting extcron...')
|
logger.debug('Starting extcron...')
|
||||||
global_settings.web2py_crontype = 'external'
|
global_settings.web2py_crontype = 'external'
|
||||||
if options.scheduler: # -K
|
if options.scheduler: # -K
|
||||||
apps = [app.strip() for app in options.scheduler.split(',') if check_existent_app(options, app.strip())]
|
apps = [app.strip() for app in options.scheduler.split(
|
||||||
|
',') if check_existent_app(options, app.strip())]
|
||||||
else:
|
else:
|
||||||
apps = None
|
apps = None
|
||||||
extcron = newcron.extcron(options.folder, apps=apps)
|
extcron = newcron.extcron(options.folder, apps=apps)
|
||||||
@@ -1082,7 +1092,6 @@ def start(cron=True):
|
|||||||
pass
|
pass
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
# ## if -H cron is enabled in this *process*
|
# ## if -H cron is enabled in this *process*
|
||||||
# ## if --softcron use softcron
|
# ## if --softcron use softcron
|
||||||
# ## use hardcron in all other cases
|
# ## use hardcron in all other cases
|
||||||
@@ -1127,7 +1136,8 @@ def start(cron=True):
|
|||||||
import Tkinter
|
import Tkinter
|
||||||
havetk = True
|
havetk = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.warn('GUI not available because Tk library is not installed')
|
logger.warn(
|
||||||
|
'GUI not available because Tk library is not installed')
|
||||||
havetk = False
|
havetk = False
|
||||||
|
|
||||||
if options.password == '<ask>' and havetk or options.taskbar and havetk:
|
if options.password == '<ask>' and havetk or options.taskbar and havetk:
|
||||||
|
|||||||
+5
-2
@@ -30,6 +30,7 @@ from fileutils import up
|
|||||||
|
|
||||||
__all__ = ['web2py_windows_service_handler']
|
__all__ = ['web2py_windows_service_handler']
|
||||||
|
|
||||||
|
|
||||||
class Service(win32serviceutil.ServiceFramework):
|
class Service(win32serviceutil.ServiceFramework):
|
||||||
|
|
||||||
_svc_name_ = '_unNamed'
|
_svc_name_ = '_unNamed'
|
||||||
@@ -111,8 +112,10 @@ class Web2pyService(Service):
|
|||||||
if True: # legacy support for old options files, which have only (deprecated) numthreads
|
if True: # legacy support for old options files, which have only (deprecated) numthreads
|
||||||
if hasattr(options, 'numthreads') and not hasattr(options, 'minthreads'):
|
if hasattr(options, 'numthreads') and not hasattr(options, 'minthreads'):
|
||||||
options.minthreads = options.numthreads
|
options.minthreads = options.numthreads
|
||||||
if not hasattr(options, 'minthreads'): options.minthreads = None
|
if not hasattr(options, 'minthreads'):
|
||||||
if not hasattr(options, 'maxthreads'): options.maxthreads = None
|
options.minthreads = None
|
||||||
|
if not hasattr(options, 'maxthreads'):
|
||||||
|
options.maxthreads = None
|
||||||
import main
|
import main
|
||||||
self.server = main.HttpServer(
|
self.server = main.HttpServer(
|
||||||
ip=options.ip,
|
ip=options.ip,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ web2py handler for isapi-wsgi for IIS. Requires:
|
|||||||
http://code.google.com/p/isapi-wsgi/
|
http://code.google.com/p/isapi-wsgi/
|
||||||
"""
|
"""
|
||||||
# The entry point for the ISAPI extension.
|
# The entry point for the ISAPI extension.
|
||||||
|
|
||||||
|
|
||||||
def __ExtensionFactory__():
|
def __ExtensionFactory__():
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
+2
-1
@@ -14,7 +14,8 @@ import os
|
|||||||
|
|
||||||
ip = '0.0.0.0'
|
ip = '0.0.0.0'
|
||||||
port = 80
|
port = 80
|
||||||
interfaces=[('0.0.0.0',80)] #,('0.0.0.0',443,'ssl_private_key.pem','ssl_certificate.pem')]
|
interfaces = [('0.0.0.0', 80)]
|
||||||
|
#,('0.0.0.0',443,'ssl_private_key.pem','ssl_certificate.pem')]
|
||||||
password = '<recycle>' # ## <recycle> means use the previous password
|
password = '<recycle>' # ## <recycle> means use the previous password
|
||||||
pid_filename = 'httpserver.pid'
|
pid_filename = 'httpserver.pid'
|
||||||
log_filename = 'httpserver.log'
|
log_filename = 'httpserver.log'
|
||||||
|
|||||||
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user