many pep8 improvements

This commit is contained in:
mdipierro committed 2012-10-19 12:33:53 -05:00
1 parent 71ec15190b
commit 6aa5edc7ff
105 files changed
+2999 -1509

No files matched your search

+1 -1
View File
@@ -1 +1 @@
Version 2.1.1 (2012-10-19 10:37:01) dev Version 2.1.1 (2012-10-19 12:33:48) dev
-1
View File
@@ -1 +0,0 @@
+35 -15
View File
@@ -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()
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+28 -17
View File
@@ -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()
+18 -13
View File
@@ -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
View File
@@ -1 +0,0 @@
+15 -2
View File
@@ -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>
+2 -1
View File
@@ -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:
+6 -3
View File
@@ -51,7 +51,8 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0):
:param exponent: rate of exponential back-off. :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:
+11 -2
View File
@@ -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
View File
@@ -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,
+2
View File
@@ -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:
-3
View File
@@ -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
+13 -8
View File
@@ -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 -1
View File
@@ -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")
+28 -15
View File
@@ -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)
+7 -5
View File
@@ -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
+13 -7
View File
@@ -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
+10 -15
View File
@@ -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
+11 -12
View File
@@ -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 -32
View File
@@ -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 -2
View File
@@ -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
+11 -5
View File
@@ -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)
+14 -21
View File
@@ -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
View File
@@ -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('-')]
+2 -1
View File
@@ -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())
+3 -1
View File
@@ -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)
+29 -12
View File
@@ -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`_\.
+5 -3
View File
@@ -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
+5
View File
@@ -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
View File
@@ -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
+10 -5
View File
@@ -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)
+10 -3
View File
@@ -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"""
+6 -3
View File
@@ -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()
+4 -1
View File
@@ -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:
+5 -2
View File
@@ -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]
+14 -12
View File
@@ -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)
+5 -3
View File
@@ -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())
+7
View File
@@ -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())]
+72 -24
View File
@@ -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',
+6 -3
View File
@@ -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
+29 -12
View File
@@ -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__":
+8 -1
View File
@@ -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"
+2
View File
@@ -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
View File
@@ -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
View File
@@ -1,4 +1,5 @@
import codecs, encodings import codecs
import encodings
"""Caller will hand this library a buffer and ask it to either convert """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
View File
@@ -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
View File
@@ -61,6 +61,7 @@ less_template = '<link href="%s" rel="stylesheet/less" type="text/css" />'
css_inline = '<style type="text/css">\n%s\n</style>' 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
View File
@@ -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
View File
@@ -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 '![%s](%s)' % (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 '![%s](%s)' % (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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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()
+9 -1
View File
@@ -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)
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -38,7 +38,8 @@ THREAD_LOCAL = threading.local() # thread-local storage for routing params
regex_at = re.compile(r'(?<!\\)\$[a-zA-Z]\w*') regex_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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+6 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2
View File
@@ -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
View File
@@ -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("'", "&#x27;") data = cgi.escape(data, True).replace("'", "&#x27;")
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:
+3 -2
View File
@@ -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()
+2 -2
View File
@@ -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()
+4 -5
View File
@@ -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="&lt;&gt;">&lt;&gt;' + \ '<option a="1" b="2" value="&lt;&gt;">&lt;&gt;' +
'</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="&lt;&gt;">&lt;&gt;</option></select>') '<option value="&lt;&gt;">&lt;&gt;</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>&lt;&gt;</td></tr>' + \ '<table a="1" b="2"><tr><td>&lt;&gt;</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">&lt;&gt;' + \ '<textarea a="1" b="2" cols="40" rows="10">&lt;&gt;' +
'</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
View File
@@ -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()
+2 -4
View File
@@ -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()
+1 -1
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+159 -78
View File
@@ -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()
-3
View File
@@ -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()
+17 -9
View File
@@ -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='{{="<&>"}}'), '&lt;&amp;&gt;') self.assertEqual(render(content='{{="<&>"}}'), '&lt;&amp;&gt;')
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()
-1
View File
@@ -25,4 +25,3 @@ class TestUtils(unittest.TestCase):
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
+2 -1
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+160 -55
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+2
View File
@@ -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
View File
@@ -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