many pep8 improvements
This commit is contained in:
1 parent
71ec15190b
commit
6aa5edc7ff
117 files changed
+7160
-5670
No files matched your search
@@ -1 +1 @@
|
|||||||
Version 2.1.1 (2012-10-19 10:37:01) dev
|
Version 2.1.1 (2012-10-19 12:33:48) dev
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
+66
-46
@@ -16,44 +16,47 @@ import urllib
|
|||||||
|
|
||||||
path = os.path.dirname(os.path.abspath(__file__))
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
os.chdir(path)
|
os.chdir(path)
|
||||||
sys.path = [path]+[p for p in sys.path if not p==path]
|
sys.path = [path] + [p for p in sys.path if not p == path]
|
||||||
import gluon.main
|
import gluon.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):
|
||||||
from wsgiref.handlers import CGIHandler
|
from wsgiref.handlers import CGIHandler
|
||||||
CGIHandler().run(app) # Just ignore host and port here
|
CGIHandler().run(app) # Just ignore host and port here
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def flup(app,address, **options):
|
def flup(app, address, **options):
|
||||||
import flup.server.fcgi
|
import flup.server.fcgi
|
||||||
flup.server.fcgi.WSGIServer(app, bindAddress=address).run()
|
flup.server.fcgi.WSGIServer(app, bindAddress=address).run()
|
||||||
|
|
||||||
@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()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def cherrypy(app,address, **options):
|
def cherrypy(app, address, **options):
|
||||||
from cherrypy import wsgiserver
|
from cherrypy import wsgiserver
|
||||||
server = wsgiserver.CherryPyWSGIServer(address, app)
|
server = wsgiserver.CherryPyWSGIServer(address, app)
|
||||||
server.start()
|
server.start()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def rocket(app,address, **options):
|
def rocket(app, address, **options):
|
||||||
from gluon.rocket import CherryPyWSGIServer
|
from gluon.rocket import CherryPyWSGIServer
|
||||||
server = CherryPyWSGIServer(address, app)
|
server = CherryPyWSGIServer(address, app)
|
||||||
server.start()
|
server.start()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def rocket_with_repoze_profiler(app,address, **options):
|
def rocket_with_repoze_profiler(app, address, **options):
|
||||||
from gluon.rocket import CherryPyWSGIServer
|
from gluon.rocket import CherryPyWSGIServer
|
||||||
from repoze.profile.profiler import AccumulatingProfileMiddleware
|
from repoze.profile.profiler import AccumulatingProfileMiddleware
|
||||||
from gluon.settings import global_settings
|
from gluon.settings import global_settings
|
||||||
@@ -63,44 +66,46 @@ class Servers:
|
|||||||
log_filename='wsgi.prof',
|
log_filename='wsgi.prof',
|
||||||
discard_first_request=True,
|
discard_first_request=True,
|
||||||
flush_at_shutdown=True,
|
flush_at_shutdown=True,
|
||||||
path = '/__profile__'
|
path='/__profile__'
|
||||||
)
|
)
|
||||||
server = CherryPyWSGIServer(address, wrapped)
|
server = CherryPyWSGIServer(address, wrapped)
|
||||||
server.start()
|
server.start()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def paste(app,address,**options):
|
def paste(app, address, **options):
|
||||||
from paste import httpserver
|
from paste import httpserver
|
||||||
from paste.translogger import TransLogger
|
from paste.translogger import TransLogger
|
||||||
httpserver.serve(app, host=address[0], port=address[1], **options)
|
httpserver.serve(app, host=address[0], port=address[1], **options)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def fapws(app,address, **options):
|
def fapws(app, address, **options):
|
||||||
import fapws._evwsgi as evwsgi
|
import fapws._evwsgi as evwsgi
|
||||||
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):
|
||||||
import bjoern
|
import bjoern
|
||||||
bjoern.run(app, *address)
|
bjoern.run(app, *address)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tornado(app,address, **options):
|
def tornado(app, address, **options):
|
||||||
import tornado.wsgi
|
import tornado.wsgi
|
||||||
import tornado.httpserver
|
import tornado.httpserver
|
||||||
import tornado.ioloop
|
import tornado.ioloop
|
||||||
@@ -110,7 +115,7 @@ class Servers:
|
|||||||
tornado.ioloop.IOLoop.instance().start()
|
tornado.ioloop.IOLoop.instance().start()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def twisted(app,address, **options):
|
def twisted(app, address, **options):
|
||||||
from twisted.web import server, wsgi
|
from twisted.web import server, wsgi
|
||||||
from twisted.python.threadpool import ThreadPool
|
from twisted.python.threadpool import ThreadPool
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
@@ -122,42 +127,44 @@ class Servers:
|
|||||||
reactor.run()
|
reactor.run()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def diesel(app,address, **options):
|
def diesel(app, address, **options):
|
||||||
from diesel.protocols.wsgi import WSGIApplication
|
from diesel.protocols.wsgi import WSGIApplication
|
||||||
app = WSGIApplication(app, port=address[1])
|
app = WSGIApplication(app, port=address[1])
|
||||||
app.run()
|
app.run()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def gunicorn(app,address, **options):
|
def gunicorn(app, address, **options):
|
||||||
from gunicorn.app.base import Application
|
from gunicorn.app.base import Application
|
||||||
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()
|
||||||
g.run()
|
g.run()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def eventlet(app,address, **options):
|
def eventlet(app, address, **options):
|
||||||
from eventlet import wsgi, listen
|
from eventlet import wsgi, listen
|
||||||
wsgi.server(listen(address), app)
|
wsgi.server(listen(address), app)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mongrel2(app,address,**options):
|
def mongrel2(app, address, **options):
|
||||||
import uuid
|
import uuid
|
||||||
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
|
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
|
||||||
from mongrel2 import handler
|
from mongrel2 import handler
|
||||||
conn = handler.Connection(str(uuid.uuid4()),
|
conn = handler.Connection(str(uuid.uuid4()),
|
||||||
"tcp://127.0.0.1:9997",
|
"tcp://127.0.0.1:9997",
|
||||||
"tcp://127.0.0.1:9996")
|
"tcp://127.0.0.1:9996")
|
||||||
mongrel2_handler(app,conn,debug=False)
|
mongrel2_handler(app, conn, debug=False)
|
||||||
|
|
||||||
|
|
||||||
def run(servername,ip,port,softcron=True,logging=False,profiler=None):
|
def run(servername, ip, port, softcron=True, logging=False, profiler=None):
|
||||||
if logging:
|
if logging:
|
||||||
application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase,
|
application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase,
|
||||||
logfilename='httpserver.log',
|
logfilename='httpserver.log',
|
||||||
@@ -167,9 +174,10 @@ def run(servername,ip,port,softcron=True,logging=False,profiler=None):
|
|||||||
if softcron:
|
if softcron:
|
||||||
from gluon.settings import global_settings
|
from gluon.settings import global_settings
|
||||||
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 :
|
||||||
https://github.com/berry/Mongrel2-WSGI-Handler/blob/master/wsgi-handler.py
|
https://github.com/berry/Mongrel2-WSGI-Handler/blob/master/wsgi-handler.py
|
||||||
@@ -194,20 +202,23 @@ def mongrel2_handler(application,conn,debug=False):
|
|||||||
# and responses. Unless I have missed something.
|
# 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:
|
||||||
continue #effectively ignore the disconnect from the client
|
print "DISCONNECT"
|
||||||
|
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
|
||||||
# that are a must according to PEP 333
|
# that are a must according to PEP 333
|
||||||
environ = req.headers
|
environ = req.headers
|
||||||
environ['SERVER_PROTOCOL'] = 'HTTP/1.1' # SimpleHandler expects a server_protocol, lets assume it is HTTP 1.1
|
environ['SERVER_PROTOCOL'] = 'HTTP/1.1' # SimpleHandler expects a server_protocol, lets assume it is HTTP 1.1
|
||||||
environ['REQUEST_METHOD'] = environ['METHOD']
|
environ['REQUEST_METHOD'] = environ['METHOD']
|
||||||
if ':' in environ['Host']:
|
if ':' in environ['Host']:
|
||||||
environ['SERVER_NAME'] = environ['Host'].split(':')[0]
|
environ['SERVER_NAME'] = environ['Host'].split(':')[0]
|
||||||
@@ -215,17 +226,19 @@ def mongrel2_handler(application,conn,debug=False):
|
|||||||
else:
|
else:
|
||||||
environ['SERVER_NAME'] = environ['Host']
|
environ['SERVER_NAME'] = environ['Host']
|
||||||
environ['SERVER_PORT'] = ''
|
environ['SERVER_PORT'] = ''
|
||||||
environ['SCRIPT_NAME'] = '' # empty for now
|
environ['SCRIPT_NAME'] = '' # empty for now
|
||||||
environ['PATH_INFO'] = urllib.unquote(environ['PATH'])
|
environ['PATH_INFO'] = urllib.unquote(environ['PATH'])
|
||||||
if '?' in environ['URI']:
|
if '?' in environ['URI']:
|
||||||
environ['QUERY_STRING'] = environ['URI'].split('?')[1]
|
environ['QUERY_STRING'] = environ['URI'].split('?')[1]
|
||||||
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"
|
||||||
@@ -282,7 +300,7 @@ def main():
|
|||||||
default=False,
|
default=False,
|
||||||
dest='profiler',
|
dest='profiler',
|
||||||
help='profiler filename')
|
help='profiler filename')
|
||||||
servers = ', '.join(x for x in dir(Servers) if not x[0]=='_')
|
servers = ', '.join(x for x in dir(Servers) if not x[0] == '_')
|
||||||
parser.add_option('-s',
|
parser.add_option('-s',
|
||||||
'--server',
|
'--server',
|
||||||
default='rocket',
|
default='rocket',
|
||||||
@@ -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
-1
@@ -56,7 +56,7 @@ import wsgiref.handlers
|
|||||||
|
|
||||||
path = os.path.dirname(os.path.abspath(__file__))
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
os.chdir(path)
|
os.chdir(path)
|
||||||
sys.path = [path]+[p for p in sys.path if not p==path]
|
sys.path = [path] + [p for p in sys.path if not p == path]
|
||||||
|
|
||||||
import gluon.main
|
import gluon.main
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ import os
|
|||||||
|
|
||||||
path = os.path.dirname(os.path.abspath(__file__))
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
os.chdir(path)
|
os.chdir(path)
|
||||||
sys.path = [path]+[p for p in sys.path if not p==path]
|
sys.path = [path] + [p for p in sys.path if not p == path]
|
||||||
|
|
||||||
import gluon.main
|
import gluon.main
|
||||||
import gluon.contrib.gateways.fcgi as fcgi
|
import gluon.contrib.gateways.fcgi as fcgi
|
||||||
|
|||||||
+3
-2
@@ -33,7 +33,7 @@ import wsgiref.handlers
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
path = os.path.dirname(os.path.abspath(__file__))
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
sys.path = [path]+[p for p in sys.path if not p==path]
|
sys.path = [path] + [p for p in sys.path if not p == path]
|
||||||
|
|
||||||
sys.modules['cPickle'] = sys.modules['pickle']
|
sys.modules['cPickle'] = sys.modules['pickle']
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ def wsgiapp(env, res):
|
|||||||
if global_settings.web2py_runtime == 'gae:development':
|
if global_settings.web2py_runtime == 'gae:development':
|
||||||
gluon.admin.create_missing_folders()
|
gluon.admin.create_missing_folders()
|
||||||
|
|
||||||
web2py_path = global_settings.applications_parent # backward compatibility
|
web2py_path = global_settings.applications_parent # backward compatibility
|
||||||
|
|
||||||
return gluon.main.wsgibase(env, res)
|
return gluon.main.wsgibase(env, res)
|
||||||
|
|
||||||
@@ -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)
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ Web2Py framework modules
|
|||||||
========================
|
========================
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML','redirect','current','embed64']
|
__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML', 'redirect', 'current', 'embed64']
|
||||||
|
|
||||||
from globals import current
|
from globals import current
|
||||||
from html import *
|
from html import *
|
||||||
|
|||||||
+31
-18
@@ -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
|
||||||
@@ -113,7 +115,7 @@ def app_cleanup(app, request):
|
|||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
for f in os.listdir(path):
|
for f in os.listdir(path):
|
||||||
try:
|
try:
|
||||||
if f[:1]!='.': os.unlink(os.path.join(path,f))
|
if f[:1] != '.': os.unlink(os.path.join(path, f))
|
||||||
except IOError:
|
except IOError:
|
||||||
r = False
|
r = False
|
||||||
|
|
||||||
@@ -122,7 +124,7 @@ def app_cleanup(app, request):
|
|||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
for f in os.listdir(path):
|
for f in os.listdir(path):
|
||||||
try:
|
try:
|
||||||
if f[:1]!='.': recursive_unlink(os.path.join(path,f))
|
if f[:1] != '.': recursive_unlink(os.path.join(path, f))
|
||||||
except IOError:
|
except IOError:
|
||||||
r = False
|
r = False
|
||||||
|
|
||||||
@@ -131,7 +133,7 @@ def app_cleanup(app, request):
|
|||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
for f in os.listdir(path):
|
for f in os.listdir(path):
|
||||||
try:
|
try:
|
||||||
if f[:1]!='.': os.unlink(os.path.join(path,f))
|
if f[:1] != '.': os.unlink(os.path.join(path, f))
|
||||||
except IOError:
|
except IOError:
|
||||||
r = False
|
r = False
|
||||||
return r
|
return r
|
||||||
@@ -158,7 +160,8 @@ 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
|
||||||
|
|
||||||
@@ -186,17 +189,17 @@ def app_create(app, request,force=False,key=None,info=False):
|
|||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
w2p_unpack('welcome.w2p', path)
|
w2p_unpack('welcome.w2p', path)
|
||||||
for subfolder in ['models','views','controllers', 'databases',
|
for subfolder in ['models', 'views', 'controllers', 'databases',
|
||||||
'modules','cron','errors','sessions',
|
'modules', 'cron', 'errors', 'sessions',
|
||||||
'languages','static','private','uploads']:
|
'languages', 'static', 'private', 'uploads']:
|
||||||
subpath = os.path.join(path,subfolder)
|
subpath = os.path.join(path, subfolder)
|
||||||
if not os.path.exists(subpath):
|
if not os.path.exists(subpath):
|
||||||
os.mkdir(subpath)
|
os.mkdir(subpath)
|
||||||
db = os.path.join(path, 'models', 'db.py')
|
db = os.path.join(path, 'models', 'db.py')
|
||||||
if os.path.exists(db):
|
if os.path.exists(db):
|
||||||
data = read_file(db)
|
data = read_file(db)
|
||||||
data = data.replace('<your secret key>',
|
data = data.replace('<your secret key>',
|
||||||
'sha512:'+(key or web2py_uuid()))
|
'sha512:' + (key or web2py_uuid()))
|
||||||
write_file(db, data)
|
write_file(db, data)
|
||||||
if info:
|
if info:
|
||||||
return True, None
|
return True, None
|
||||||
@@ -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 + '/'
|
||||||
@@ -392,7 +400,7 @@ def unzip(filename, dir, subfolder=''):
|
|||||||
continue
|
continue
|
||||||
#print name[n:]
|
#print name[n:]
|
||||||
if name.endswith('/'):
|
if name.endswith('/'):
|
||||||
folder = os.path.join(dir,name[n:])
|
folder = os.path.join(dir, name[n:])
|
||||||
if not os.path.exists(folder):
|
if not os.path.exists(folder):
|
||||||
os.mkdir(folder)
|
os.mkdir(folder)
|
||||||
else:
|
else:
|
||||||
@@ -421,7 +429,7 @@ def upgrade(request, url='http://web2py.com'):
|
|||||||
if not gluon_parent.endswith('/'):
|
if not gluon_parent.endswith('/'):
|
||||||
gluon_parent = gluon_parent + '/'
|
gluon_parent = gluon_parent + '/'
|
||||||
(check, version) = check_new_version(web2py_version,
|
(check, version) = check_new_version(web2py_version,
|
||||||
url+'/examples/default/version')
|
url + '/examples/default/version')
|
||||||
if not check:
|
if not check:
|
||||||
return (False, 'Already latest version')
|
return (False, 'Already latest version')
|
||||||
if os.path.exists(os.path.join(gluon_parent, 'web2py.exe')):
|
if os.path.exists(os.path.join(gluon_parent, 'web2py.exe')):
|
||||||
@@ -442,35 +450,40 @@ def upgrade(request, url='http://web2py.com'):
|
|||||||
file = None
|
file = None
|
||||||
try:
|
try:
|
||||||
write_file(filename, urllib.urlopen(full_url).read(), 'wb')
|
write_file(filename, urllib.urlopen(full_url).read(), 'wb')
|
||||||
except Exception,e:
|
except Exception, e:
|
||||||
return False, e
|
return False, e
|
||||||
try:
|
try:
|
||||||
unzip(filename, destination, subfolder)
|
unzip(filename, destination, subfolder)
|
||||||
return True, None
|
return True, None
|
||||||
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:
|
||||||
for subfolder in ('models', 'views', 'controllers', 'databases',
|
for subfolder in ('models', 'views', 'controllers', 'databases',
|
||||||
'modules', 'cron', 'errors', 'sessions',
|
'modules', 'cron', 'errors', 'sessions',
|
||||||
'languages', 'static', 'private', 'uploads'):
|
'languages', 'static', 'private', 'uploads'):
|
||||||
path = os.path.join(request.folder, subfolder)
|
path = os.path.join(request.folder, subfolder)
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
os.mkdir(path)
|
os.mkdir(path)
|
||||||
global_settings.app_folders.add(request.folder)
|
global_settings.app_folders.add(request.folder)
|
||||||
+45
-33
@@ -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.
|
||||||
@@ -73,7 +74,7 @@ class CacheAbstract(object):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def __call__(self, key, f,
|
def __call__(self, key, f,
|
||||||
time_expire = DEFAULT_TIME_EXPIRE):
|
time_expire=DEFAULT_TIME_EXPIRE):
|
||||||
"""
|
"""
|
||||||
Tries retrieve the value corresponding to `key` from the cache of the
|
Tries retrieve the value corresponding to `key` from the cache of the
|
||||||
object exists and if it did not expire, else it called the function `f`
|
object exists and if it did not expire, else it called the function `f`
|
||||||
@@ -130,6 +131,7 @@ class CacheAbstract(object):
|
|||||||
if r.match(str(key)):
|
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,13 +176,14 @@ 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()
|
||||||
|
|
||||||
def __call__(self, key, f,
|
def __call__(self, key, f,
|
||||||
time_expire = DEFAULT_TIME_EXPIRE,
|
time_expire=DEFAULT_TIME_EXPIRE,
|
||||||
destroyer = None):
|
destroyer=None):
|
||||||
"""
|
"""
|
||||||
Attention! cache.ram does not copy the cached object. It just stores a reference to it.
|
Attention! cache.ram does not copy the cached object. It just stores a reference to it.
|
||||||
Turns out the deepcopying the object has some problems:
|
Turns out the deepcopying the object has some problems:
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -312,8 +321,8 @@ class CacheOnDisk(CacheAbstract):
|
|||||||
|
|
||||||
### we need this because of a possible bug in shelve that may
|
### we need this because of a possible bug in shelve that may
|
||||||
### or may not lock
|
### or may not lock
|
||||||
self.locker_name = os.path.join(folder,'cache.lock')
|
self.locker_name = os.path.join(folder, 'cache.lock')
|
||||||
self.shelve_name = os.path.join(folder,'cache.shelve')
|
self.shelve_name = os.path.join(folder, 'cache.shelve')
|
||||||
|
|
||||||
def clear(self, regex=None):
|
def clear(self, regex=None):
|
||||||
self.initialize()
|
self.initialize()
|
||||||
@@ -328,7 +337,7 @@ class CacheOnDisk(CacheAbstract):
|
|||||||
self._close_shelve_and_unlock()
|
self._close_shelve_and_unlock()
|
||||||
|
|
||||||
def __call__(self, key, f,
|
def __call__(self, key, f,
|
||||||
time_expire = DEFAULT_TIME_EXPIRE):
|
time_expire=DEFAULT_TIME_EXPIRE):
|
||||||
self.initialize()
|
self.initialize()
|
||||||
dt = time_expire
|
dt = time_expire
|
||||||
storage = self._open_shelve_and_lock()
|
storage = self._open_shelve_and_lock()
|
||||||
@@ -346,7 +355,7 @@ class CacheOnDisk(CacheAbstract):
|
|||||||
else:
|
else:
|
||||||
value = f()
|
value = f()
|
||||||
storage[key] = (now, value)
|
storage[key] = (now, value)
|
||||||
storage[CacheAbstract.cache_stats_name]['misses']+=1
|
storage[CacheAbstract.cache_stats_name]['misses'] += 1
|
||||||
storage.sync()
|
storage.sync()
|
||||||
finally:
|
finally:
|
||||||
self._close_shelve_and_unlock()
|
self._close_shelve_and_unlock()
|
||||||
@@ -365,8 +374,9 @@ 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__
|
||||||
self.__doc__ = func.__doc__
|
self.__doc__ = func.__doc__
|
||||||
self.func = func
|
self.func = func
|
||||||
@@ -374,17 +384,18 @@ 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)
|
||||||
else:
|
else:
|
||||||
key2 = self.key.replace('%(name)s',self.__name__)\
|
key2 = self.key.replace('%(name)s', self.__name__)\
|
||||||
.replace('%(args)s',str(a)).replace('%(vars)s',str(b))
|
.replace('%(args)s', str(a)).replace('%(vars)s', str(b))
|
||||||
cache_model = self.cache_model
|
cache_model = self.cache_model
|
||||||
if not cache_model or isinstance(cache_model,str):
|
if not cache_model or isinstance(cache_model, str):
|
||||||
cache_model = getattr(self.cache,cache_model or 'ram')
|
cache_model = getattr(self.cache, cache_model or 'ram')
|
||||||
return cache_model(key2,
|
return cache_model(key2,
|
||||||
lambda a=a,b=b:self.func(*a,**b),
|
lambda a=a, b=b: self.func(*a, **b),
|
||||||
self.time_expire)
|
self.time_expire)
|
||||||
|
|
||||||
|
|
||||||
@@ -424,9 +435,9 @@ class Cache(object):
|
|||||||
logger.warning('no cache.disk (AttributeError)')
|
logger.warning('no cache.disk (AttributeError)')
|
||||||
|
|
||||||
def __call__(self,
|
def __call__(self,
|
||||||
key = None,
|
key=None,
|
||||||
time_expire = DEFAULT_TIME_EXPIRE,
|
time_expire=DEFAULT_TIME_EXPIRE,
|
||||||
cache_model = None):
|
cache_model=None):
|
||||||
"""
|
"""
|
||||||
Decorator function that can be used to cache any function/method.
|
Decorator function that can be used to cache any function/method.
|
||||||
|
|
||||||
@@ -459,8 +470,8 @@ class Cache(object):
|
|||||||
`request.env.path_info` as key.
|
`request.env.path_info` as key.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def tmp(func,cache=self,cache_model=cache_model):
|
def tmp(func, cache=self, cache_model=cache_model):
|
||||||
return CacheAction(func,key,time_expire,self,cache_model)
|
return CacheAction(func, key, time_expire, self, cache_model)
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -473,7 +484,7 @@ class Cache(object):
|
|||||||
cache_model(prefix + key, f, time_expire)
|
cache_model(prefix + key, f, time_expire)
|
||||||
|
|
||||||
|
|
||||||
def lazy_cache(key=None,time_expire=None,cache_model='ram'):
|
def lazy_cache(key=None, time_expire=None, cache_model='ram'):
|
||||||
"""
|
"""
|
||||||
can be used to cache any function including in modules,
|
can be used to cache any function including in modules,
|
||||||
as long as the cached function is only called within a web2py request
|
as long as the cached function is only called within a web2py request
|
||||||
@@ -481,11 +492,12 @@ def lazy_cache(key=None,time_expire=None,cache_model='ram'):
|
|||||||
the time_expire defaults to None (no cache expiration)
|
the time_expire defaults to None (no cache expiration)
|
||||||
if cache_model is "ram" then the model is current.cache.ram, etc.
|
if cache_model is "ram" then the model is current.cache.ram, etc.
|
||||||
"""
|
"""
|
||||||
def decorator(f,key=key,time_expire=time_expire,cache_model=cache_model):
|
def decorator(f, key=key, time_expire=time_expire, cache_model=cache_model):
|
||||||
key = key or repr(f)
|
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)
|
||||||
g.__name__ = f.__name__
|
g.__name__ = f.__name__
|
||||||
return g
|
return g
|
||||||
return decorator
|
return decorator
|
||||||
+79
-65
@@ -48,7 +48,7 @@ except:
|
|||||||
logger.warning('unable to import py_compile')
|
logger.warning('unable to import py_compile')
|
||||||
|
|
||||||
is_pypy = settings.global_settings.is_pypy
|
is_pypy = settings.global_settings.is_pypy
|
||||||
is_gae = settings.global_settings.web2py_runtime_gae
|
is_gae = settings.global_settings.web2py_runtime_gae
|
||||||
is_jython = settings.global_settings.is_jython
|
is_jython = settings.global_settings.is_jython
|
||||||
|
|
||||||
pjoin = os.path.join
|
pjoin = os.path.join
|
||||||
@@ -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,14 +116,16 @@ 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,
|
||||||
content='loading...',**attr):
|
content='loading...', **attr):
|
||||||
""" LOAD a component into the action's document
|
""" LOAD a component into the action's document
|
||||||
|
|
||||||
Timing options:
|
Timing options:
|
||||||
@@ -134,13 +138,14 @@ 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
|
||||||
request = current.request
|
request = current.request
|
||||||
if '.' in f:
|
if '.' in f:
|
||||||
f, extension = f.rsplit('.',1)
|
f, extension = f.rsplit('.', 1)
|
||||||
if url or ajax:
|
if url or ajax:
|
||||||
url = url or URL(request.application, c, f, r=request,
|
url = url or URL(request.application, c, f, r=request,
|
||||||
args=args, vars=vars, extension=extension,
|
args=args, vars=vars, extension=extension,
|
||||||
@@ -160,19 +165,20 @@ def LOAD(c=None, f='index', args=None, vars=None,
|
|||||||
if not isinstance(timeout, (int, long)):
|
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:
|
||||||
statement = "web2py_component('%s','%s');" % (url, target)
|
statement = "web2py_component('%s','%s');" % (url, target)
|
||||||
script = SCRIPT(statement, _type="text/javascript")
|
script = SCRIPT(statement, _type="text/javascript")
|
||||||
if not content is None:
|
if not content is None:
|
||||||
return TAG[''](script, DIV(content,**attr))
|
return TAG[''](script, DIV(content, **attr))
|
||||||
else:
|
else:
|
||||||
return TAG[''](script)
|
return TAG[''](script)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if not isinstance(args,(list,tuple)):
|
if not isinstance(args, (list, tuple)):
|
||||||
args = [args]
|
args = [args]
|
||||||
c = c or request.controller
|
c = c or request.controller
|
||||||
other_request = Storage(request)
|
other_request = Storage(request)
|
||||||
@@ -186,17 +192,17 @@ 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 ''
|
||||||
other_request.env.http_web2py_component_location = \
|
other_request.env.http_web2py_component_location = \
|
||||||
request.env.path_info
|
request.env.path_info
|
||||||
other_request.cid = target
|
other_request.cid = target
|
||||||
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 = \
|
||||||
@@ -218,40 +224,41 @@ def LOAD(c=None, f='index', args=None, vars=None,
|
|||||||
js = None
|
js = None
|
||||||
if ajax_trap:
|
if ajax_trap:
|
||||||
link = URL(request.application, c, f, r=request,
|
link = URL(request.application, c, f, r=request,
|
||||||
args=args, vars=vars, extension=extension,
|
args=args, vars=vars, extension=extension,
|
||||||
user_signature=user_signature)
|
user_signature=user_signature)
|
||||||
js = "web2py_trap_form('%s','%s');" % (link, target)
|
js = "web2py_trap_form('%s','%s');" % (link, target)
|
||||||
script = js and SCRIPT(js,_type="text/javascript") or ''
|
script = js and SCRIPT(js, _type="text/javascript") or ''
|
||||||
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:]
|
||||||
attr['_id']=target
|
attr['_id'] = target
|
||||||
request = self.environment['request']
|
request = self.environment['request']
|
||||||
if '.' in f:
|
if '.' in f:
|
||||||
f, extension = f.rsplit('.',1)
|
f, extension = f.rsplit('.', 1)
|
||||||
if url or ajax:
|
if url or ajax:
|
||||||
url = url or html.URL(request.application, c, f, r=request,
|
url = url or html.URL(request.application, c, f, r=request,
|
||||||
args=args, vars=vars, extension=extension,
|
args=args, vars=vars, extension=extension,
|
||||||
user_signature=user_signature)
|
user_signature=user_signature)
|
||||||
script = html.SCRIPT('web2py_component("%s","%s")' % (url, target),
|
script = html.SCRIPT('web2py_component("%s","%s")' % (url, target),
|
||||||
_type="text/javascript")
|
_type="text/javascript")
|
||||||
return html.TAG[''](script, html.DIV(content,**attr))
|
return html.TAG[''](script, html.DIV(content, **attr))
|
||||||
else:
|
else:
|
||||||
if not isinstance(args,(list,tuple)):
|
if not isinstance(args, (list, tuple)):
|
||||||
args = [args]
|
args = [args]
|
||||||
c = c or request.controller
|
c = c or request.controller
|
||||||
|
|
||||||
@@ -266,15 +273,15 @@ 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 ''
|
||||||
other_request.env.http_web2py_component_location = \
|
other_request.env.http_web2py_component_location = \
|
||||||
request.env.path_info
|
request.env.path_info
|
||||||
other_request.cid = target
|
other_request.cid = target
|
||||||
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(self.environment)
|
other_environment = copy.copy(self.environment)
|
||||||
other_response._view_environment = other_environment
|
other_response._view_environment = other_environment
|
||||||
other_response.generic_patterns = \
|
other_response.generic_patterns = \
|
||||||
@@ -299,8 +306,8 @@ class LoadFactory(object):
|
|||||||
args=args, vars=vars, extension=extension,
|
args=args, vars=vars, extension=extension,
|
||||||
user_signature=user_signature)
|
user_signature=user_signature)
|
||||||
js = "web2py_trap_form('%s','%s');" % (link, target)
|
js = "web2py_trap_form('%s','%s');" % (link, target)
|
||||||
script = js and html.SCRIPT(js,_type="text/javascript") or ''
|
script = js and html.SCRIPT(js, _type="text/javascript") or ''
|
||||||
return html.TAG[''](html.DIV(html.XML(page),**attr),script)
|
return html.TAG[''](html.DIV(html.XML(page), **attr), script)
|
||||||
|
|
||||||
|
|
||||||
def local_import_aux(name, reload_force=False, app='welcome'):
|
def local_import_aux(name, reload_force=False, app='welcome'):
|
||||||
@@ -321,7 +328,7 @@ def local_import_aux(name, reload_force=False, app='welcome'):
|
|||||||
This prevents conflict between applications and un-necessary execs.
|
This prevents conflict between applications and un-necessary execs.
|
||||||
It can be used to import any module, including regular Python modules.
|
It can be used to import any module, including regular Python modules.
|
||||||
"""
|
"""
|
||||||
items = name.replace('/','.')
|
items = name.replace('/', '.')
|
||||||
name = "applications.%s.modules.%s" % (app, items)
|
name = "applications.%s.modules.%s" % (app, items)
|
||||||
module = __import__(name)
|
module = __import__(name)
|
||||||
for item in name.split(".")[1:]:
|
for item in name.split(".")[1:]:
|
||||||
@@ -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.
|
||||||
@@ -384,7 +394,7 @@ def build_environment(request, response, session, store_current=True):
|
|||||||
# Enable standard conditional models (i.e., /*.py, /[controller]/*.py, and
|
# Enable standard conditional models (i.e., /*.py, /[controller]/*.py, and
|
||||||
# /[controller]/[function]/*.py)
|
# /[controller]/[function]/*.py)
|
||||||
response.models_to_run = [r'^\w+\.py$', r'^%s/\w+\.py$' % request.controller,
|
response.models_to_run = [r'^\w+\.py$', r'^%s/\w+\.py$' % request.controller,
|
||||||
r'^%s/%s/\w+\.py$' % (request.controller, request.function)]
|
r'^%s/%s/\w+\.py$' % (request.controller, request.function)]
|
||||||
|
|
||||||
t = environment['T'] = translator(request)
|
t = environment['T'] = translator(request)
|
||||||
c = environment['cache'] = Cache(request)
|
c = environment['cache'] = Cache(request)
|
||||||
@@ -398,23 +408,24 @@ def build_environment(request, response, session, store_current=True):
|
|||||||
current.cache = c
|
current.cache = c
|
||||||
|
|
||||||
global __builtins__
|
global __builtins__
|
||||||
if is_jython: # jython hack
|
if is_jython: # jython hack
|
||||||
__builtins__ = mybuiltin()
|
__builtins__ = mybuiltin()
|
||||||
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
|
||||||
environment['local_import'] = \
|
environment['local_import'] = \
|
||||||
lambda name, reload=False, app=request.application:\
|
lambda name, reload=False, app=request.application:\
|
||||||
local_import_aux(name,reload,app)
|
local_import_aux(name, reload, app)
|
||||||
BaseAdapter.set_folder(pjoin(request.folder, 'databases'))
|
BaseAdapter.set_folder(pjoin(request.folder, 'databases'))
|
||||||
response._view_environment = copy.copy(environment)
|
response._view_environment = copy.copy(environment)
|
||||||
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:])
|
||||||
|
|
||||||
|
|
||||||
@@ -458,7 +469,7 @@ def compile_models(folder):
|
|||||||
path = pjoin(folder, 'models')
|
path = pjoin(folder, 'models')
|
||||||
for file in listdir(path, '.+\.py$'):
|
for file in listdir(path, '.+\.py$'):
|
||||||
data = read_file(pjoin(path, file))
|
data = read_file(pjoin(path, file))
|
||||||
filename = pjoin(folder, 'compiled','models',file)
|
filename = pjoin(folder, 'compiled', 'models', file)
|
||||||
mktree(filename)
|
mktree(filename)
|
||||||
write_file(filename, data)
|
write_file(filename, data)
|
||||||
save_pyc(filename)
|
save_pyc(filename)
|
||||||
@@ -473,14 +484,14 @@ def compile_controllers(folder):
|
|||||||
path = pjoin(folder, 'controllers')
|
path = pjoin(folder, 'controllers')
|
||||||
for file in listdir(path, '.+\.py$'):
|
for file in listdir(path, '.+\.py$'):
|
||||||
### why is this here? save_pyc(pjoin(path, file))
|
### why is this here? save_pyc(pjoin(path, file))
|
||||||
data = read_file(pjoin(path,file))
|
data = read_file(pjoin(path, file))
|
||||||
exposed = regex_expose.findall(data)
|
exposed = regex_expose.findall(data)
|
||||||
for function in exposed:
|
for function in exposed:
|
||||||
command = data + "\nresponse._vars=response._caller(%s)\n" % \
|
command = data + "\nresponse._vars=response._caller(%s)\n" % \
|
||||||
function
|
function
|
||||||
filename = pjoin(folder, 'compiled', ('controllers/'
|
filename = pjoin(folder, 'compiled', ('controllers/'
|
||||||
+ file[:-3]).replace('/', '_')
|
+ file[:-3]).replace('/', '_')
|
||||||
+ '_' + function + '.py')
|
+ '_' + function + '.py')
|
||||||
write_file(filename, command)
|
write_file(filename, command)
|
||||||
save_pyc(filename)
|
save_pyc(filename)
|
||||||
os.unlink(filename)
|
os.unlink(filename)
|
||||||
@@ -500,19 +511,19 @@ def run_models_in(environment):
|
|||||||
for model in listdir(cpath, '^models_\w+\.pyc$', 0):
|
for model in listdir(cpath, '^models_\w+\.pyc$', 0):
|
||||||
restricted(read_pyc(model), environment, layer=model)
|
restricted(read_pyc(model), environment, layer=model)
|
||||||
path = pjoin(cpath, 'models')
|
path = pjoin(cpath, 'models')
|
||||||
models = listdir(path, '^\w+\.pyc$',0,sort=False)
|
models = listdir(path, '^\w+\.pyc$', 0, sort=False)
|
||||||
compiled=True
|
compiled = True
|
||||||
else:
|
else:
|
||||||
path = pjoin(folder, 'models')
|
path = pjoin(folder, 'models')
|
||||||
models = listdir(path, '^\w+\.py$',0,sort=False)
|
models = listdir(path, '^\w+\.py$', 0, sort=False)
|
||||||
compiled=False
|
compiled = False
|
||||||
n = len(path) + 1
|
n = len(path) + 1
|
||||||
for model in models:
|
for model in models:
|
||||||
regex = environment['response'].models_to_run
|
regex = environment['response'].models_to_run
|
||||||
if isinstance(regex, list):
|
if isinstance(regex, list):
|
||||||
regex = re_compile('|'.join(regex))
|
regex = re_compile('|'.join(regex))
|
||||||
file = model[n:].replace(os.path.sep, '/').replace('.pyc', '.py')
|
file = model[n:].replace(os.path.sep, '/').replace('.pyc', '.py')
|
||||||
if not regex.search(file) and c!= 'appadmin':
|
if not regex.search(file) and c != 'appadmin':
|
||||||
continue
|
continue
|
||||||
elif compiled:
|
elif compiled:
|
||||||
code = read_pyc(model)
|
code = read_pyc(model)
|
||||||
@@ -538,7 +549,7 @@ def run_controller_in(controller, function, environment):
|
|||||||
badf = 'invalid function (%s/%s)' % (controller, function)
|
badf = 'invalid function (%s/%s)' % (controller, function)
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
filename = pjoin(path, 'controllers_%s_%s.pyc'
|
filename = pjoin(path, 'controllers_%s_%s.pyc'
|
||||||
% (controller, function))
|
% (controller, function))
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
raise HTTP(404,
|
raise HTTP(404,
|
||||||
rewrite.THREAD_LOCAL.routes.error_message % badf,
|
rewrite.THREAD_LOCAL.routes.error_message % badf,
|
||||||
@@ -548,7 +559,8 @@ def run_controller_in(controller, function, environment):
|
|||||||
# TESTING: adjust the path to include site packages
|
# 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
|
||||||
|
|
||||||
@@ -578,18 +590,19 @@ def run_controller_in(controller, function, environment):
|
|||||||
code = "%s\nresponse._vars=response._caller(%s)\n" % (code, function)
|
code = "%s\nresponse._vars=response._caller(%s)\n" % (code, function)
|
||||||
if is_gae:
|
if is_gae:
|
||||||
layer = filename + ':' + function
|
layer = filename + ':' + function
|
||||||
code = getcfs(layer, filename, lambda: compile2(code,layer))
|
code = getcfs(layer, filename, lambda: compile2(code, layer))
|
||||||
restricted(code, environment, filename)
|
restricted(code, environment, filename)
|
||||||
response = environment['response']
|
response = environment['response']
|
||||||
vars=response._vars
|
vars = response._vars
|
||||||
if response.postprocessing:
|
if response.postprocessing:
|
||||||
vars = reduce(lambda vars, p: p(vars), response.postprocessing, vars)
|
vars = reduce(lambda vars, p: p(vars), response.postprocessing, vars)
|
||||||
if isinstance(vars,unicode):
|
if isinstance(vars, unicode):
|
||||||
vars = vars.encode('utf8')
|
vars = vars.encode('utf8')
|
||||||
elif hasattr(vars,'xml') and callable(vars.xml):
|
elif hasattr(vars, 'xml') and callable(vars.xml):
|
||||||
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.
|
||||||
@@ -606,7 +619,7 @@ def run_view_in(environment):
|
|||||||
if response.generic_patterns:
|
if response.generic_patterns:
|
||||||
patterns = response.generic_patterns
|
patterns = response.generic_patterns
|
||||||
regex = re_compile('|'.join(map(fnmatch.translate, patterns)))
|
regex = re_compile('|'.join(map(fnmatch.translate, patterns)))
|
||||||
short_action = '%(controller)s/%(function)s.%(extension)s' % request
|
short_action = '%(controller)s/%(function)s.%(extension)s' % request
|
||||||
allow_generic = regex.search(short_action)
|
allow_generic = regex.search(short_action)
|
||||||
else:
|
else:
|
||||||
allow_generic = False
|
allow_generic = False
|
||||||
@@ -626,7 +639,7 @@ def run_view_in(environment):
|
|||||||
files.append('views_generic.pyc')
|
files.append('views_generic.pyc')
|
||||||
# end backward compatibility code
|
# end backward compatibility code
|
||||||
for f in files:
|
for f in files:
|
||||||
filename = pjoin(path,f)
|
filename = pjoin(path, f)
|
||||||
if os.path.exists(filename):
|
if os.path.exists(filename):
|
||||||
code = read_pyc(filename)
|
code = read_pyc(filename)
|
||||||
restricted(code, environment, layer=filename)
|
restricted(code, environment, layer=filename)
|
||||||
@@ -648,13 +661,14 @@ def run_view_in(environment):
|
|||||||
ccode = getcfs(layer, filename,
|
ccode = getcfs(layer, filename,
|
||||||
lambda: compile2(parse_template(view,
|
lambda: compile2(parse_template(view,
|
||||||
pjoin(folder, 'views'),
|
pjoin(folder, 'views'),
|
||||||
context=environment),layer))
|
context=environment), layer))
|
||||||
else:
|
else:
|
||||||
ccode = parse_template(view,
|
ccode = parse_template(view,
|
||||||
pjoin(folder, 'views'),
|
pjoin(folder, 'views'),
|
||||||
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.
|
||||||
@@ -662,7 +676,7 @@ def remove_compiled_application(folder):
|
|||||||
try:
|
try:
|
||||||
shutil.rmtree(pjoin(folder, 'compiled'))
|
shutil.rmtree(pjoin(folder, 'compiled'))
|
||||||
path = pjoin(folder, 'controllers')
|
path = pjoin(folder, 'controllers')
|
||||||
for file in listdir(path,'.*\.pyc$',drop=False):
|
for file in listdir(path, '.*\.pyc$', drop=False):
|
||||||
os.unlink(file)
|
os.unlink(file)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -700,7 +700,7 @@ CONTENT_TYPE = {
|
|||||||
'.zabw': 'application/x-abiword',
|
'.zabw': 'application/x-abiword',
|
||||||
'.zip': 'application/zip',
|
'.zip': 'application/zip',
|
||||||
'.zoo': 'application/x-zoo',
|
'.zoo': 'application/x-zoo',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def contenttype(filename, default='text/plain'):
|
def contenttype(filename, default='text/plain'):
|
||||||
@@ -709,11 +709,11 @@ def contenttype(filename, default='text/plain'):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
i = filename.rfind('.')
|
i = filename.rfind('.')
|
||||||
if i>=0:
|
if i >= 0:
|
||||||
default = CONTENT_TYPE.get(filename[i:].lower(),default)
|
default = CONTENT_TYPE.get(filename[i:].lower(), default)
|
||||||
j = filename.rfind('.', 0, i)
|
j = filename.rfind('.', 0, i)
|
||||||
if j>=0:
|
if j >= 0:
|
||||||
default = CONTENT_TYPE.get(filename[j:].lower(),default)
|
default = CONTENT_TYPE.get(filename[j:].lower(), default)
|
||||||
if default.startswith('text/'):
|
if default.startswith('text/'):
|
||||||
default += '; charset=utf-8'
|
default += '; charset=utf-8'
|
||||||
return default
|
return default
|
||||||
@@ -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
|
||||||
@@ -29,7 +30,7 @@ class NamedTupleBase(tuple):
|
|||||||
"""Create a new instance of this fielded tuple"""
|
"""Create a new instance of this fielded tuple"""
|
||||||
# May need to unpack named field values here
|
# May need to unpack named field values here
|
||||||
if kws:
|
if kws:
|
||||||
values = list(args) + [None]*(len(cls._fields) - len(args))
|
values = list(args) + [None] * (len(cls._fields) - len(args))
|
||||||
fields = dict((val, idx) for idx, val in enumerate(cls._fields))
|
fields = dict((val, idx) for idx, val in enumerate(cls._fields))
|
||||||
for kw, val in kws.iteritems():
|
for kw, val in kws.iteritems():
|
||||||
assert kw in kws, "%r not in field list" % kw
|
assert kw in kws, "%r not in field list" % kw
|
||||||
@@ -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,10 +200,11 @@ 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,
|
|
||||||
login='cnpdev4289', transkey='SR2P8g4jdEn7vFLQ',testmode=True):
|
def process(creditcard, expiration, total, cvv=None, tax=None, invoice=None,
|
||||||
payment = AIM(login,transkey,testmode)
|
login='cnpdev4289', transkey='SR2P8g4jdEn7vFLQ', testmode=True):
|
||||||
expiration = expiration.replace('/','')
|
payment = AIM(login, transkey, testmode)
|
||||||
|
expiration = expiration.replace('/', '')
|
||||||
payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice)
|
payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice)
|
||||||
try:
|
try:
|
||||||
payment.process()
|
payment.process()
|
||||||
@@ -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
|
||||||
@@ -215,12 +223,14 @@ def test():
|
|||||||
total = '1.00'
|
total = '1.00'
|
||||||
cvv = '123'
|
cvv = '123'
|
||||||
tax = '0.00'
|
tax = '0.00'
|
||||||
invoice = str(time())[4:10] # get a random invoice number
|
invoice = str(time())[4:10] # get a random invoice number
|
||||||
|
|
||||||
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()
|
||||||
@@ -251,9 +262,9 @@ def test():
|
|||||||
except AIM.AIMError, e:
|
except AIM.AIMError, e:
|
||||||
print "Exception thrown:", e
|
print "Exception thrown:", e
|
||||||
print 'An error occured'
|
print 'An error occured'
|
||||||
print 'approved',payment.isApproved()
|
print 'approved', payment.isApproved()
|
||||||
print 'declined',payment.isDeclined()
|
print 'declined', payment.isDeclined()
|
||||||
print 'error',payment.isError()
|
print 'error', payment.isError()
|
||||||
|
|
||||||
if __name__=='__main__':
|
if __name__ == '__main__':
|
||||||
test()
|
test()
|
||||||
@@ -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)
|
||||||
@@ -59,7 +61,7 @@ class DowCommerce:
|
|||||||
opened.close()
|
opened.close()
|
||||||
|
|
||||||
for result in results:
|
for result in results:
|
||||||
(key,val) = result.split('=')
|
(key, val) = result.split('=')
|
||||||
self.results[key] = val
|
self.results[key] = val
|
||||||
|
|
||||||
if self.results['response'] == '1':
|
if self.results['response'] == '1':
|
||||||
@@ -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(
|
||||||
ipaddress=None, tax=None, shipping=None,
|
self, creditcard, expiration, total, cvv=None, orderid=None, orderdescription=None,
|
||||||
firstname=None, lastname=None, company=None, address1=None, address2=None, city=None, state=None, zipcode=None,
|
ipaddress=None, tax=None, shipping=None,
|
||||||
country=None, phone=None, fax=None, emailaddress=None, website=None,
|
firstname=None, lastname=None, company=None, address1=None, address2=None, city=None, state=None, zipcode=None,
|
||||||
shipping_firstname=None, shipping_lastname=None, shipping_company=None, shipping_address1=None, shipping_address2=None,
|
country=None, phone=None, fax=None, emailaddress=None, website=None,
|
||||||
shipping_city=None, shipping_state=None, shipping_zipcode = None, shipping_country=None, shipping_emailaddress=None):
|
shipping_firstname=None, shipping_lastname=None, shipping_company=None, shipping_address1=None, shipping_address2=None,
|
||||||
if str(creditcard).strip() == '' or creditcard == None:
|
shipping_city=None, shipping_state=None, shipping_zipcode=None, shipping_country=None, shipping_emailaddress=None):
|
||||||
|
if str(creditcard).strip() == '' or creditcard is None:
|
||||||
raise DowCommerce.DowCommerceError('No credit card number passed to setTransaction(): {0}'.format(creditcard))
|
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
|
||||||
@@ -212,13 +216,14 @@ def test():
|
|||||||
total = '1.00'
|
total = '1.00'
|
||||||
cvv = '999'
|
cvv = '999'
|
||||||
tax = '0.00'
|
tax = '0.00'
|
||||||
orderid = str(time())[4:10] # get a random invoice number
|
orderid = str(time())[4:10] # get a random invoice number
|
||||||
|
|
||||||
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(
|
||||||
firstname='John', lastname='Doe', company='Acme', address1='123 Min Street', city='Hometown', state='VA',
|
creditcard, expiration, total, cvv=cvv, tax=tax, orderid=orderid, orderdescription='Test Transaction',
|
||||||
zipcode='12345', country='US', phone='888-555-1212', emailaddress='john@noemail.local', ipaddress='192.168.1.1')
|
firstname='John', lastname='Doe', company='Acme', address1='123 Min Street', city='Hometown', state='VA',
|
||||||
|
zipcode='12345', country='US', phone='888-555-1212', emailaddress='john@noemail.local', ipaddress='192.168.1.1')
|
||||||
|
|
||||||
payment.process()
|
payment.process()
|
||||||
if payment.isApproved():
|
if payment.isApproved():
|
||||||
@@ -231,9 +236,9 @@ def test():
|
|||||||
except DowCommerce.DowCommerceError, e:
|
except DowCommerce.DowCommerceError, e:
|
||||||
print "Exception thrown:", e
|
print "Exception thrown:", e
|
||||||
print 'An error occured'
|
print 'An error occured'
|
||||||
print 'approved',payment.isApproved()
|
print 'approved', payment.isApproved()
|
||||||
print 'declined',payment.isDeclined()
|
print 'declined', payment.isDeclined()
|
||||||
print 'error',payment.isError()
|
print 'error', payment.isError()
|
||||||
|
|
||||||
if __name__=='__main__':
|
if __name__ == '__main__':
|
||||||
test()
|
test()
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
+29
-16
@@ -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
|
||||||
@@ -75,23 +77,28 @@ EMBED_MAPS = [
|
|||||||
'http://revision3.com/api/oembed/'),
|
'http://revision3.com/api/oembed/'),
|
||||||
(re.compile('http://\S+.viddler.com/\S+'),
|
(re.compile('http://\S+.viddler.com/\S+'),
|
||||||
'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)
|
||||||
|
|
||||||
EXTENSION_MAPS = {
|
EXTENSION_MAPS = {
|
||||||
'png': image,
|
'png': image,
|
||||||
@@ -126,33 +133,37 @@ 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):
|
||||||
oembed = v+'?format=json&url='+cgi.escape(url)
|
oembed = v + '?format=json&url=' + cgi.escape(url)
|
||||||
try:
|
try:
|
||||||
data = urllib.urlopen(oembed).read()
|
data = urllib.urlopen(oembed).read()
|
||||||
print data
|
print data
|
||||||
return loads(data) # json!
|
return loads(data) # json!
|
||||||
except:
|
except:
|
||||||
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:
|
||||||
r = cdict[url]
|
r = cdict[url]
|
||||||
else:
|
else:
|
||||||
r = oembed(url)
|
r = oembed(url)
|
||||||
if isinstance(cdict,dict):
|
if isinstance(cdict, dict):
|
||||||
cdict[url] = r
|
cdict[url] = r
|
||||||
# if oembed service
|
# if oembed service
|
||||||
if 'html' in r:
|
if 'html' in r:
|
||||||
@@ -170,21 +181,23 @@ 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]
|
||||||
for txt in soup.findAll(text=True):
|
for txt in soup.findAll(text=True):
|
||||||
if not txt.parent.name in ('a','script','pre','code','embed','object','audio','video'):
|
if not txt.parent.name in ('a', 'script', 'pre', 'code', 'embed', 'object', 'audio', 'video'):
|
||||||
ntxt = regex_link.sub(
|
ntxt = regex_link.sub(
|
||||||
lambda match: expand_one(match.group(0),cdict), txt)
|
lambda match: expand_one(match.group(0), cdict), txt)
|
||||||
txt.replaceWith(BeautifulSoup(ntxt))
|
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>
|
||||||
<p>http://www.youtube.com/watch?v=IWBFiI5RrA0</p>
|
<p>http://www.youtube.com/watch?v=IWBFiI5RrA0</p>
|
||||||
<p>http://www.web2py.com/examples/static/images/logo_bw.png</p>
|
<p>http://www.web2py.com/examples/static/images/logo_bw.png</p>
|
||||||
@@ -198,8 +211,8 @@ laoreet tortor.</p>
|
|||||||
"""
|
"""
|
||||||
return expand_html(example)
|
return expand_html(example)
|
||||||
|
|
||||||
if __name__=="__main__":
|
if __name__ == "__main__":
|
||||||
if len(sys.argv)>1:
|
if len(sys.argv) > 1:
|
||||||
print expand_html(open(sys.argv[1]).read())
|
print expand_html(open(sys.argv[1]).read())
|
||||||
else:
|
else:
|
||||||
print test()
|
print test()
|
||||||
@@ -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()
|
||||||
@@ -24,12 +25,12 @@ class MemcacheClient(object):
|
|||||||
key,
|
key,
|
||||||
f,
|
f,
|
||||||
time_expire=300,
|
time_expire=300,
|
||||||
):
|
):
|
||||||
key = '%s/%s' % (self.request.application, key)
|
key = '%s/%s' % (self.request.application, key)
|
||||||
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:
|
||||||
@@ -47,21 +48,21 @@ class MemcacheClient(object):
|
|||||||
self.client.set(key, (time.time(), value))
|
self.client.set(key, (time.time(), value))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def clear(self, key = None):
|
def clear(self, key=None):
|
||||||
if key:
|
if key:
|
||||||
key = '%s/%s' % (self.request.application, key)
|
key = '%s/%s' % (self.request.application, key)
|
||||||
self.client.delete(key)
|
self.client.delete(key)
|
||||||
else:
|
else:
|
||||||
self.client.flush_all()
|
self.client.flush_all()
|
||||||
|
|
||||||
def delete(self,*a,**b):
|
def delete(self, *a, **b):
|
||||||
return self.client.delete(*a,**b)
|
return self.client.delete(*a, **b)
|
||||||
|
|
||||||
def get(self,*a,**b):
|
def get(self, *a, **b):
|
||||||
return self.client.delete(*a,**b)
|
return self.client.delete(*a, **b)
|
||||||
|
|
||||||
def set(self,*a,**b):
|
def set(self, *a, **b):
|
||||||
return self.client.delete(*a,**b)
|
return self.client.delete(*a, **b)
|
||||||
|
|
||||||
def flush_all(self,*a,**b):
|
def flush_all(self, *a, **b):
|
||||||
return self.client.delete(*a,**b)
|
return self.client.delete(*a, **b)
|
||||||
@@ -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
|
||||||
@@ -60,8 +61,8 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0):
|
|||||||
interval = float(interval)
|
interval = float(interval)
|
||||||
exponent = float(exponent)
|
exponent = float(exponent)
|
||||||
wrapped = apiproxy_stub_map.MakeSyncCall
|
wrapped = apiproxy_stub_map.MakeSyncCall
|
||||||
errors = {datastore_pb.Error.TIMEOUT:'Timeout',
|
errors = {datastore_pb.Error.TIMEOUT: 'Timeout',
|
||||||
datastore_pb.Error.CONCURRENT_TRANSACTION:'TransactionFailedError'}
|
datastore_pb.Error.CONCURRENT_TRANSACTION: 'TransactionFailedError'}
|
||||||
|
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
count = 0.0
|
count = 0.0
|
||||||
@@ -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:
|
||||||
|
|||||||
+25
-16
@@ -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,39 +26,47 @@ 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)
|
||||||
out,warnings,errors=markmin2pdf(markmin)
|
out, warnings, errors = markmin2pdf(markmin)
|
||||||
if errors:
|
if errors:
|
||||||
current.response.headers['Content-Type']='text/html'
|
current.response.headers['Content-Type'] = 'text/html'
|
||||||
raise HTTP(405,HTML(BODY(H1('errors'),
|
raise HTTP(405, HTML(BODY(H1('errors'),
|
||||||
UL(*errors),
|
UL(*errors),
|
||||||
H1('warnings'),
|
H1('warnings'),
|
||||||
UL(*warnings))).xml())
|
UL(*warnings))).xml())
|
||||||
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
|
|
||||||
pdf=MyFPDF()
|
class MyFPDF(FPDF, HTMLMixin):
|
||||||
|
pass
|
||||||
|
pdf = MyFPDF()
|
||||||
pdf.add_page()
|
pdf.add_page()
|
||||||
html = sanitize(html, escape=False) #### should have better list of allowed tags
|
html = sanitize(
|
||||||
pdf.write_html(html,image_map=image_map)
|
html, escape=False) # should have better list of allowed tags
|
||||||
|
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:
|
||||||
return pdflatex_from_html(html)
|
return pdflatex_from_html(html)
|
||||||
else:
|
else:
|
||||||
return pyfpdf_from_html(html)
|
return pyfpdf_from_html(html)
|
||||||
@@ -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,
|
||||||
@@ -8,8 +9,8 @@ def button(merchant_id="123456789012345",
|
|||||||
description="running shoes black")]):
|
description="running shoes black")]):
|
||||||
t = '<input name="item_%(key)s_%(k)s" type="hidden" value="%(value)s"/>'
|
t = '<input name="item_%(key)s_%(k)s" type="hidden" value="%(value)s"/>'
|
||||||
list_products = ''
|
list_products = ''
|
||||||
for k,product in enumerate(products):
|
for k, product in enumerate(products):
|
||||||
for key,value in product.items():
|
for key, value in product.items():
|
||||||
list_products += t % dict(k=k+1,key=key,value=value)
|
list_products += t % dict(k=k + 1, key=key, value=value)
|
||||||
button = '<form action="https://checkout.google.com/api/checkout/v2/checkoutForm/Merchant/%s" id="BB_BuyButtonForm" method="post" name="BB_BuyButtonForm" target="_top">%s<input name="_charset_" type="hidden" value="utf-8"/><input alt="" src="https://checkout.google.com/buttons/buy.gif?merchant_id=%s&w=117&h=48&style=white&variant=text&loc=en_US" type="image"/></form>' % (merchant_id, list_products, merchant_id)
|
button = '<form action="https://checkout.google.com/api/checkout/v2/checkoutForm/Merchant/%s" id="BB_BuyButtonForm" method="post" name="BB_BuyButtonForm" target="_top">%s<input name="_charset_" type="hidden" value="utf-8"/><input alt="" src="https://checkout.google.com/buttons/buy.gif?merchant_id=%s&w=117&h=48&style=white&variant=text&loc=en_US" type="image"/></form>' % (merchant_id, list_products, merchant_id)
|
||||||
return XML(button)
|
return XML(button)
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# this file exists for backward compatibility
|
# this file exists for backward compatibility
|
||||||
|
|
||||||
__all__ = ['DAL','Field','drivers','gae']
|
__all__ = ['DAL', 'Field', 'drivers', 'gae']
|
||||||
|
|
||||||
from gluon.dal import DAL, Field, Table, Query, Set, Expression, Row, Rows, drivers, BaseAdapter, SQLField, SQLTable, SQLXorable, SQLQuery, SQLSet, SQLRows, SQLStorage, SQLDB, GQLDB, SQLALL, SQLCustomType, gae
|
from gluon.dal import DAL, Field, Table, Query, Set, Expression, Row, Rows, drivers, BaseAdapter, SQLField, SQLTable, SQLXorable, SQLQuery, SQLSet, SQLRows, SQLStorage, SQLDB, GQLDB, SQLALL, SQLCustomType, gae
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
#########################################################################
|
#########################################################################
|
||||||
from gluon import current
|
from gluon import current
|
||||||
|
|
||||||
|
|
||||||
class RESIZE(object):
|
class RESIZE(object):
|
||||||
def __init__(self, nx=160, ny=80, error_message=' image resize'):
|
def __init__(self, nx=160, ny=80, error_message=' image resize'):
|
||||||
(self.nx, self.ny, self.error_message) = (nx, ny, error_message)
|
(self.nx, self.ny, self.error_message) = (nx, ny, error_message)
|
||||||
@@ -43,6 +44,7 @@ class RESIZE(object):
|
|||||||
else:
|
else:
|
||||||
return (value, None)
|
return (value, None)
|
||||||
|
|
||||||
|
|
||||||
def THUMB(image, nx=120, ny=120, gae=False, name='thumb'):
|
def THUMB(image, nx=120, ny=120, gae=False, name='thumb'):
|
||||||
if image:
|
if image:
|
||||||
if not gae:
|
if not gae:
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -11,9 +11,9 @@ def basic_auth(server="http://127.0.0.1"):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def basic_login_aux(username,
|
def basic_login_aux(username,
|
||||||
password,
|
password,
|
||||||
server=server):
|
server=server):
|
||||||
key = base64.b64encode(username+':'+password)
|
key = base64.b64encode(username + ':' + password)
|
||||||
headers = {'Authorization': 'Basic ' + key}
|
headers = {'Authorization': 'Basic ' + key}
|
||||||
request = urllib2.Request(server, None, headers)
|
request = urllib2.Request(server, None, headers)
|
||||||
try:
|
try:
|
||||||
@@ -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
|
||||||
@@ -34,17 +35,17 @@ class BrowserID(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
request,
|
request,
|
||||||
audience = "",
|
audience="",
|
||||||
assertion_post_url = "",
|
assertion_post_url="",
|
||||||
prompt = "BrowserID Login",
|
prompt="BrowserID Login",
|
||||||
issuer = "browserid.org",
|
issuer="browserid.org",
|
||||||
verify_url = "https://browserid.org/verify",
|
verify_url="https://browserid.org/verify",
|
||||||
browserid_js = "https://browserid.org/include.js",
|
browserid_js="https://browserid.org/include.js",
|
||||||
browserid_button = "https://browserid.org/i/sign_in_red.png",
|
browserid_button="https://browserid.org/i/sign_in_red.png",
|
||||||
crypto_js = "https://crypto-js.googlecode.com/files/2.2.0-crypto-md5.js",
|
crypto_js="https://crypto-js.googlecode.com/files/2.2.0-crypto-md5.js",
|
||||||
on_login_failure = None,
|
on_login_failure=None,
|
||||||
):
|
):
|
||||||
|
|
||||||
self.request = request
|
self.request = request
|
||||||
self.audience = audience
|
self.audience = audience
|
||||||
@@ -67,13 +68,13 @@ class BrowserID(object):
|
|||||||
if request.vars.assertion:
|
if request.vars.assertion:
|
||||||
audience = self.audience
|
audience = self.audience
|
||||||
issuer = self.issuer
|
issuer = self.issuer
|
||||||
assertion = XML(request.vars.assertion,sanitize=True)
|
assertion = XML(request.vars.assertion, sanitize=True)
|
||||||
verify_data = {'assertion':assertion,'audience':audience}
|
verify_data = {'assertion': assertion, 'audience': audience}
|
||||||
auth_info_json = fetch(self.verify_url,data=verify_data)
|
auth_info_json = fetch(self.verify_url, data=verify_data)
|
||||||
j = json.loads(auth_info_json)
|
j = json.loads(auth_info_json)
|
||||||
epoch_time = int(time.time()*1000) # we need 13 digit epoch time
|
epoch_time = int(time.time() * 1000) # we need 13 digit epoch time
|
||||||
if j["status"] == "okay" and j["audience"] == audience and j['issuer'] == issuer and j['expires'] >= epoch_time:
|
if j["status"] == "okay" and j["audience"] == audience and j['issuer'] == issuer and j['expires'] >= epoch_time:
|
||||||
return dict(email = j['email'])
|
return dict(email=j['email'])
|
||||||
elif self.on_login_failure:
|
elif self.on_login_failure:
|
||||||
redirect('http://google.com')
|
redirect('http://google.com')
|
||||||
else:
|
else:
|
||||||
@@ -83,9 +84,8 @@ class BrowserID(object):
|
|||||||
def login_form(self):
|
def login_form(self):
|
||||||
request = self.request
|
request = self.request
|
||||||
onclick = "javascript:navigator.id.getVerifiedEmail(gotVerifiedEmail) ; return false"
|
onclick = "javascript:navigator.id.getVerifiedEmail(gotVerifiedEmail) ; return false"
|
||||||
form = DIV(SCRIPT(_src=self.browserid_js,_type="text/javascript"),
|
form = DIV(SCRIPT(_src=self.browserid_js, _type="text/javascript"),
|
||||||
SCRIPT(_src=self.crypto_js,_type="text/javascript"),
|
SCRIPT(_src=self.crypto_js, _type="text/javascript"),
|
||||||
A(IMG(_src=self.browserid_button,_alt=self.prompt),_href="#",_onclick=onclick,_class="browserid",_title="Login With BrowserID"),
|
A(IMG(_src=self.browserid_button, _alt=self.prompt), _href="#", _onclick=onclick, _class="browserid", _title="Login With BrowserID"),
|
||||||
SCRIPT(self.asertion_js))
|
SCRIPT(self.asertion_js))
|
||||||
return form
|
return form
|
||||||
|
|
||||||
@@ -11,7 +11,8 @@ 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
|
||||||
login form.
|
login form.
|
||||||
@@ -39,101 +40,105 @@ 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']),
|
||||||
email=lambda v:v.get('email',None),
|
email=lambda v: v.get('email', None),
|
||||||
user_id=lambda v:v['user']),
|
user_id=lambda v: v['user']),
|
||||||
casversion = 1,
|
casversion=1,
|
||||||
casusername = 'cas:user'
|
casusername='cas:user'
|
||||||
):
|
):
|
||||||
self.urlbase=urlbase
|
self.urlbase = urlbase
|
||||||
self.cas_login_url="%s/%s"%(self.urlbase,actions[0])
|
self.cas_login_url = "%s/%s" % (self.urlbase, actions[0])
|
||||||
self.cas_check_url="%s/%s"%(self.urlbase,actions[1])
|
self.cas_check_url = "%s/%s" % (self.urlbase, actions[1])
|
||||||
self.cas_logout_url="%s/%s"%(self.urlbase,actions[2])
|
self.cas_logout_url = "%s/%s" % (self.urlbase, actions[2])
|
||||||
self.maps=maps
|
self.maps = maps
|
||||||
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:
|
||||||
if current.request.env.wsgi_url_scheme in [ 'https', 'HTTPS' ]:
|
http_host = current.request.env.http_host
|
||||||
|
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 = "/" ):
|
|
||||||
current.session.token=None
|
def logout_url(self, next="/"):
|
||||||
current.session.auth=None
|
current.session.token = None
|
||||||
|
current.session.auth = None
|
||||||
self._CAS_logout()
|
self._CAS_logout()
|
||||||
return next
|
return next
|
||||||
def get_user( self ):
|
|
||||||
user=current.session.token
|
def get_user(self):
|
||||||
|
user = current.session.token
|
||||||
if user:
|
if user:
|
||||||
d = {'source':'web2py cas'}
|
d = {'source': 'web2py cas'}
|
||||||
for key in self.maps:
|
for key in self.maps:
|
||||||
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)
|
||||||
returns a token on success, None on failed authentication
|
returns a token on success, None on failed authentication
|
||||||
"""
|
"""
|
||||||
import urllib
|
import urllib
|
||||||
self.ticket=current.request.vars.ticket
|
self.ticket = current.request.vars.ticket
|
||||||
if not current.request.vars.ticket:
|
if not current.request.vars.ticket:
|
||||||
redirect( "%s?service=%s"% (self.cas_login_url,
|
redirect("%s?service=%s" % (self.cas_login_url,
|
||||||
self.cas_my_url))
|
self.cas_my_url))
|
||||||
else:
|
else:
|
||||||
url="%s?service=%s&ticket=%s" % (self.cas_check_url,
|
url = "%s?service=%s&ticket=%s" % (self.cas_check_url,
|
||||||
self.cas_my_url,
|
self.cas_my_url,
|
||||||
self.ticket )
|
self.ticket)
|
||||||
data=urllib.urlopen( url ).read()
|
data = urllib.urlopen(url).read()
|
||||||
if data.startswith('yes') or data.startswith('no'):
|
if data.startswith('yes') or data.startswith('no'):
|
||||||
data = data.split('\n')
|
data = data.split('\n')
|
||||||
if data[0]=='yes':
|
if data[0] == 'yes':
|
||||||
if ':' in data[1]: # for Compatibility with Custom CAS
|
if ':' in data[1]: # for Compatibility with Custom CAS
|
||||||
items = data[1].split(':')
|
items = data[1].split(':')
|
||||||
a = items[0]
|
a = items[0]
|
||||||
b = len(items)>1 and items[1] or a
|
b = len(items) > 1 and items[1] or a
|
||||||
c = len(items)>2 and items[2] or b
|
c = len(items) > 2 and items[2] or b
|
||||||
else:
|
else:
|
||||||
a = b = c = data[1]
|
a = b = c = data[1]
|
||||||
return dict(user=a,email=b,username=c)
|
return dict(user=a, email=b, username=c)
|
||||||
return None
|
return None
|
||||||
import xml.dom.minidom as dom
|
import xml.dom.minidom as dom
|
||||||
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(
|
||||||
if len(envelop)>0:
|
"cas:authenticationSuccess")
|
||||||
|
if len(envelop) > 0:
|
||||||
res = dict()
|
res = dict()
|
||||||
for x in envelop[0].childNodes:
|
for x in envelop[0].childNodes:
|
||||||
if x.nodeName.startswith('cas:') and len(x.childNodes):
|
if x.nodeName.startswith('cas:') and len(x.childNodes):
|
||||||
key = x.nodeName[4:].encode('utf8')
|
key = x.nodeName[4:].encode('utf8')
|
||||||
value = x.childNodes[0].nodeValue.encode('utf8')
|
value = x.childNodes[0].nodeValue.encode('utf8')
|
||||||
if not key in res:
|
if not key in res:
|
||||||
res[key]=value
|
res[key] = value
|
||||||
else:
|
else:
|
||||||
if not isinstance(res[key],list):
|
if not isinstance(res[key], list):
|
||||||
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:
|
||||||
return None # fallback
|
pass
|
||||||
|
return None # fallback
|
||||||
|
|
||||||
|
def _CAS_logout(self):
|
||||||
def _CAS_logout( self ):
|
|
||||||
"""
|
"""
|
||||||
exposed CAS.logout()
|
exposed CAS.logout()
|
||||||
redirects to the CAS logout page
|
redirects to the CAS logout page
|
||||||
"""
|
"""
|
||||||
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="...",
|
||||||
@@ -34,47 +36,45 @@ class DropboxAccount(object):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
request,
|
request,
|
||||||
key = "",
|
key="",
|
||||||
secret = "",
|
secret="",
|
||||||
access_type="app_folder",
|
access_type="app_folder",
|
||||||
login_url = "",
|
login_url="",
|
||||||
on_login_failure=None,
|
on_login_failure=None,
|
||||||
):
|
):
|
||||||
|
|
||||||
self.request=request
|
self.request = request
|
||||||
self.key=key
|
self.key = key
|
||||||
self.secret=secret
|
self.secret = secret
|
||||||
self.access_type=access_type
|
self.access_type = access_type
|
||||||
self.login_url = login_url
|
self.login_url = login_url
|
||||||
self.on_login_failure = on_login_failure
|
self.on_login_failure = on_login_failure
|
||||||
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:
|
||||||
return None
|
return None
|
||||||
elif not current.session.dropbox_access_token:
|
elif not current.session.dropbox_access_token:
|
||||||
|
|
||||||
request_token = current.session.dropbox_request_token
|
request_token = current.session.dropbox_request_token
|
||||||
self.sess.set_request_token(request_token[0],request_token[1])
|
self.sess.set_request_token(request_token[0], request_token[1])
|
||||||
access_token = self.sess.obtain_access_token(self.sess.token)
|
access_token = self.sess.obtain_access_token(self.sess.token)
|
||||||
current.session.dropbox_access_token = \
|
current.session.dropbox_access_token = \
|
||||||
(access_token.key,access_token.secret)
|
(access_token.key, access_token.secret)
|
||||||
else:
|
else:
|
||||||
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()
|
||||||
display_name = data.get('display_name','').split(' ',1)
|
display_name = data.get('display_name', '').split(' ', 1)
|
||||||
user = dict(email = data.get('email',None),
|
user = dict(email=data.get('email', None),
|
||||||
first_name = display_name[0],
|
first_name=display_name[0],
|
||||||
last_name = display_name[-1],
|
last_name=display_name[-1],
|
||||||
registration_id = data.get('uid',None))
|
registration_id=data.get('uid', None))
|
||||||
if not user['registration_id'] and self.on_login_failure:
|
if not user['registration_id'] and self.on_login_failure:
|
||||||
redirect(self.on_login_failure)
|
redirect(self.on_login_failure)
|
||||||
return user
|
return user
|
||||||
@@ -83,7 +83,7 @@ class DropboxAccount(object):
|
|||||||
|
|
||||||
request_token = self.sess.obtain_request_token()
|
request_token = self.sess.obtain_request_token()
|
||||||
current.session.dropbox_request_token = \
|
current.session.dropbox_request_token = \
|
||||||
(request_token.key,request_token.secret)
|
(request_token.key, request_token.secret)
|
||||||
dropbox_url = self.sess.build_authorize_url(request_token,
|
dropbox_url = self.sess.build_authorize_url(request_token,
|
||||||
self.login_url)
|
self.login_url)
|
||||||
redirect(dropbox_url)
|
redirect(dropbox_url)
|
||||||
@@ -93,29 +93,32 @@ class DropboxAccount(object):
|
|||||||
_style="width:400px;height:240px;")
|
_style="width:400px;height:240px;")
|
||||||
return form
|
return form
|
||||||
|
|
||||||
def logout_url(self, next = "/"):
|
def logout_url(self, next="/"):
|
||||||
current.session.dropbox_request_token=None
|
current.session.dropbox_request_token = None
|
||||||
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):
|
|
||||||
return json.loads(self.client.put_file(filename,file))['bytes']
|
def put(self, filename, file):
|
||||||
def get(self,filename,file):
|
return json.loads(self.client.put_file(filename, file))['bytes']
|
||||||
|
|
||||||
|
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):
|
|
||||||
path = os.path.join(current.request.folder,filename)
|
def use_dropbox(auth, filename='private/dropbox.key', **kwargs):
|
||||||
|
path = os.path.join(current.request.folder, filename)
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
request = current.request
|
request = current.request
|
||||||
key,secret,access_type = open(path,'r').read().strip().split(':')
|
key, secret, access_type = open(path, 'r').read().strip().split(':')
|
||||||
host = current.request.env.http_host
|
host = current.request.env.http_host
|
||||||
login_url = "http://%s/%s/default/user/login" % \
|
login_url = "http://%s/%s/default/user/login" % \
|
||||||
(host,request.application)
|
(host, request.application)
|
||||||
auth.settings.actions_disabled = \
|
auth.settings.actions_disabled = \
|
||||||
['register','change_password','request_reset_password']
|
['register', 'change_password', 'request_reset_password']
|
||||||
auth.settings.login_form = DropboxAccount(
|
auth.settings.login_form = DropboxAccount(
|
||||||
request,key=key,secret=secret,access_type=access_type,
|
request, key=key, secret=secret, access_type=access_type,
|
||||||
login_url = login_url,**kwargs)
|
login_url=login_url, **kwargs)
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import smtplib
|
import smtplib
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
def email_auth(server="smtp.gmail.com:587",
|
def email_auth(server="smtp.gmail.com:587",
|
||||||
domain="@gmail.com",
|
domain="@gmail.com",
|
||||||
tls_mode=None):
|
tls_mode=None):
|
||||||
@@ -17,9 +18,9 @@ def email_auth(server="smtp.gmail.com:587",
|
|||||||
domain=domain,
|
domain=domain,
|
||||||
tls_mode=tls_mode):
|
tls_mode=tls_mode):
|
||||||
if domain:
|
if domain:
|
||||||
if not isinstance(domain,(list,tuple)):
|
if not isinstance(domain, (list, tuple)):
|
||||||
domain=[str(domain)]
|
domain = [str(domain)]
|
||||||
if not [d for d in domain if email[-len(d):]==d]:
|
if not [d for d in domain if email[-len(d):] == d]:
|
||||||
return False
|
return False
|
||||||
(host, port) = server.split(':')
|
(host, port) = server.split(':')
|
||||||
if tls_mode is None: # then auto detect
|
if tls_mode is None: # then auto detect
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -37,7 +39,7 @@ class ExtendedLoginForm(object):
|
|||||||
auth,
|
auth,
|
||||||
alt_login_form,
|
alt_login_form,
|
||||||
signals=[],
|
signals=[],
|
||||||
login_arg = 'login'
|
login_arg='login'
|
||||||
):
|
):
|
||||||
self.auth = auth
|
self.auth = auth
|
||||||
self.alt_login_form = alt_login_form
|
self.alt_login_form = alt_login_form
|
||||||
@@ -50,7 +52,7 @@ class ExtendedLoginForm(object):
|
|||||||
"""
|
"""
|
||||||
if hasattr(self.alt_login_form, 'get_user'):
|
if hasattr(self.alt_login_form, 'get_user'):
|
||||||
return self.alt_login_form.get_user()
|
return self.alt_login_form.get_user()
|
||||||
return None # let gluon.tools.Auth.get_or_create_user do the rest
|
return None # let gluon.tools.Auth.get_or_create_user do the rest
|
||||||
|
|
||||||
def login_url(self, next):
|
def login_url(self, next):
|
||||||
"""
|
"""
|
||||||
@@ -91,8 +93,8 @@ 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()
|
||||||
|
|
||||||
self.auth.settings.login_form = self.auth
|
self.auth.settings.login_form = self.auth
|
||||||
@@ -101,5 +103,3 @@ class ExtendedLoginForm(object):
|
|||||||
|
|
||||||
form.components.append(self.alt_login_form.login_form())
|
form.components.append(self.alt_login_form.login_form())
|
||||||
return form
|
return form
|
||||||
|
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ Thanks to Hans Donner <hans.donner@pobox.com> for GaeGoogleAccount.
|
|||||||
|
|
||||||
from google.appengine.api import users
|
from google.appengine.api import users
|
||||||
|
|
||||||
|
|
||||||
class GaeGoogleAccount(object):
|
class GaeGoogleAccount(object):
|
||||||
"""
|
"""
|
||||||
Login will be done via Google's Appengine login object, instead of web2py's
|
Login will be done via Google's Appengine login object, instead of web2py's
|
||||||
@@ -35,5 +36,3 @@ class GaeGoogleAccount(object):
|
|||||||
if user:
|
if user:
|
||||||
return dict(nickname=user.nickname(), email=user.email(),
|
return dict(nickname=user.nickname(), email=user.email(),
|
||||||
user_id=user.user_id(), source="google account")
|
user_id=user.user_id(), source="google account")
|
||||||
|
|
||||||
|
|
||||||
@@ -188,18 +188,23 @@ def ldap_auth(server='ldap', port=None,
|
|||||||
str(custom_scope), str(manage_groups)))
|
str(custom_scope), str(manage_groups)))
|
||||||
if manage_user:
|
if manage_user:
|
||||||
if user_firstname_attrib.count(':') > 0:
|
if user_firstname_attrib.count(':') > 0:
|
||||||
(user_firstname_attrib, user_firstname_part) = user_firstname_attrib.split(':', 1)
|
(user_firstname_attrib,
|
||||||
|
user_firstname_part) = user_firstname_attrib.split(':', 1)
|
||||||
user_firstname_part = (int(user_firstname_part) - 1)
|
user_firstname_part = (int(user_firstname_part) - 1)
|
||||||
else:
|
else:
|
||||||
user_firstname_part = None
|
user_firstname_part = None
|
||||||
if user_lastname_attrib.count(':') > 0:
|
if user_lastname_attrib.count(':') > 0:
|
||||||
(user_lastname_attrib, user_lastname_part) = user_lastname_attrib.split(':', 1)
|
(user_lastname_attrib,
|
||||||
|
user_lastname_part) = user_lastname_attrib.split(':', 1)
|
||||||
user_lastname_part = (int(user_lastname_part) - 1)
|
user_lastname_part = (int(user_lastname_part) - 1)
|
||||||
else:
|
else:
|
||||||
user_lastname_part = None
|
user_lastname_part = None
|
||||||
user_firstname_attrib = ldap.filter.escape_filter_chars(user_firstname_attrib)
|
user_firstname_attrib = ldap.filter.escape_filter_chars(
|
||||||
user_lastname_attrib = ldap.filter.escape_filter_chars(user_lastname_attrib)
|
user_firstname_attrib)
|
||||||
user_mail_attrib = ldap.filter.escape_filter_chars(user_mail_attrib)
|
user_lastname_attrib = ldap.filter.escape_filter_chars(
|
||||||
|
user_lastname_attrib)
|
||||||
|
user_mail_attrib = ldap.filter.escape_filter_chars(
|
||||||
|
user_mail_attrib)
|
||||||
try:
|
try:
|
||||||
if allowed_groups:
|
if allowed_groups:
|
||||||
if not is_user_in_allowed_groups(username, password):
|
if not is_user_in_allowed_groups(username, password):
|
||||||
@@ -310,7 +315,8 @@ def ldap_auth(server='ldap', port=None,
|
|||||||
basedns = ldap_basedn
|
basedns = ldap_basedn
|
||||||
else:
|
else:
|
||||||
basedns = [ldap_basedn]
|
basedns = [ldap_basedn]
|
||||||
filter = '(&(uid=%s)(%s))' % (ldap.filter.escape_filter_chars(username), filterstr)
|
filter = '(&(uid=%s)(%s))' % (
|
||||||
|
ldap.filter.escape_filter_chars(username), filterstr)
|
||||||
found = False
|
found = False
|
||||||
for basedn in basedns:
|
for basedn in basedns:
|
||||||
try:
|
try:
|
||||||
@@ -338,7 +344,8 @@ def ldap_auth(server='ldap', port=None,
|
|||||||
else:
|
else:
|
||||||
basedns = [ldap_basedn]
|
basedns = [ldap_basedn]
|
||||||
filter = '(&(%s=%s)(%s))' % (username_attrib,
|
filter = '(&(%s=%s)(%s))' % (username_attrib,
|
||||||
ldap.filter.escape_filter_chars(username),
|
ldap.filter.escape_filter_chars(
|
||||||
|
username),
|
||||||
filterstr)
|
filterstr)
|
||||||
if custom_scope == 'subtree':
|
if custom_scope == 'subtree':
|
||||||
ldap_scope = ldap.SCOPE_SUBTREE
|
ldap_scope = ldap.SCOPE_SUBTREE
|
||||||
@@ -368,14 +375,16 @@ def ldap_auth(server='ldap', port=None,
|
|||||||
logger.info('[%s] Manage user data' % str(username))
|
logger.info('[%s] Manage user data' % str(username))
|
||||||
try:
|
try:
|
||||||
if user_firstname_part is not None:
|
if user_firstname_part is not None:
|
||||||
store_user_firstname = result[user_firstname_attrib][0].split(' ', 1)[user_firstname_part]
|
store_user_firstname = result[user_firstname_attrib][
|
||||||
|
0].split(' ', 1)[user_firstname_part]
|
||||||
else:
|
else:
|
||||||
store_user_firstname = result[user_firstname_attrib][0]
|
store_user_firstname = result[user_firstname_attrib][0]
|
||||||
except KeyError, e:
|
except KeyError, e:
|
||||||
store_user_firstname = None
|
store_user_firstname = None
|
||||||
try:
|
try:
|
||||||
if user_lastname_part is not None:
|
if user_lastname_part is not None:
|
||||||
store_user_lastname = result[user_lastname_attrib][0].split(' ', 1)[user_lastname_part]
|
store_user_lastname = result[user_lastname_attrib][
|
||||||
|
0].split(' ', 1)[user_lastname_part]
|
||||||
else:
|
else:
|
||||||
store_user_lastname = result[user_lastname_attrib][0]
|
store_user_lastname = result[user_lastname_attrib][0]
|
||||||
except KeyError, e:
|
except KeyError, e:
|
||||||
@@ -464,16 +473,19 @@ def ldap_auth(server='ldap', port=None,
|
|||||||
#
|
#
|
||||||
# Get all group name where the user is in actually in ldap
|
# Get all group name where the user is in actually in ldap
|
||||||
# #########################################################
|
# #########################################################
|
||||||
ldap_groups_of_the_user = get_user_groups_from_ldap(username, password)
|
ldap_groups_of_the_user = get_user_groups_from_ldap(
|
||||||
|
username, password)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Get all group name where the user is in actually in local db
|
# Get all group name where the user is in actually in local db
|
||||||
# #############################################################
|
# #############################################################
|
||||||
try:
|
try:
|
||||||
db_user_id = db(db.auth_user.username == username).select(db.auth_user.id).first().id
|
db_user_id = db(db.auth_user.username == username).select(
|
||||||
|
db.auth_user.id).first().id
|
||||||
except:
|
except:
|
||||||
try:
|
try:
|
||||||
db_user_id = db(db.auth_user.email == username).select(db.auth_user.id).first().id
|
db_user_id = db(db.auth_user.email == username).select(
|
||||||
|
db.auth_user.id).first().id
|
||||||
except AttributeError, e:
|
except AttributeError, e:
|
||||||
#
|
#
|
||||||
# There is no user in local db
|
# There is no user in local db
|
||||||
@@ -486,7 +498,8 @@ def ldap_auth(server='ldap', port=None,
|
|||||||
db_user_id = db.auth_user.insert(email=username,
|
db_user_id = db.auth_user.insert(email=username,
|
||||||
first_name=username)
|
first_name=username)
|
||||||
if not db_user_id:
|
if not db_user_id:
|
||||||
logging.error('There is no username or email for %s!' % username)
|
logging.error(
|
||||||
|
'There is no username or email for %s!' % username)
|
||||||
raise
|
raise
|
||||||
db_group_search = db((db.auth_membership.user_id == db_user_id) &
|
db_group_search = db((db.auth_membership.user_id == db_user_id) &
|
||||||
(db.auth_user.id == db.auth_membership.user_id) &
|
(db.auth_user.id == db.auth_membership.user_id) &
|
||||||
@@ -520,7 +533,8 @@ def ldap_auth(server='ldap', port=None,
|
|||||||
gid = db.auth_group.insert(role=group_to_add,
|
gid = db.auth_group.insert(role=group_to_add,
|
||||||
description='Generated from LDAP')
|
description='Generated from LDAP')
|
||||||
else:
|
else:
|
||||||
gid = db(db.auth_group.role == group_to_add).select(db.auth_group.id).first().id
|
gid = db(db.auth_group.role == group_to_add).select(
|
||||||
|
db.auth_group.id).first().id
|
||||||
db.auth_membership.insert(user_id=db_user_id,
|
db.auth_membership.insert(user_id=db_user_id,
|
||||||
group_id=gid)
|
group_id=gid)
|
||||||
except:
|
except:
|
||||||
@@ -634,4 +648,3 @@ def ldap_auth(server='ldap', port=None,
|
|||||||
if filterstr[0] == '(' and filterstr[-1] == ')': # rfc4515 syntax
|
if filterstr[0] == '(' and filterstr[-1] == ')': # rfc4515 syntax
|
||||||
filterstr = filterstr[1:-1] # parens added again where used
|
filterstr = filterstr[1:-1] # parens added again where used
|
||||||
return ldap_auth_aux
|
return ldap_auth_aux
|
||||||
|
|
||||||
@@ -14,7 +14,8 @@ from gluon.http import HTTP
|
|||||||
try:
|
try:
|
||||||
import linkedin
|
import linkedin
|
||||||
except ImportError:
|
except ImportError:
|
||||||
raise HTTP(400,"linkedin module not found")
|
raise HTTP(400, "linkedin module not found")
|
||||||
|
|
||||||
|
|
||||||
class LinkedInAccount(object):
|
class LinkedInAccount(object):
|
||||||
"""
|
"""
|
||||||
@@ -28,9 +29,9 @@ class LinkedInAccount(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,request,key,secret,return_url):
|
def __init__(self, request, key, secret, return_url):
|
||||||
self.request = request
|
self.request = request
|
||||||
self.api = linkedin.LinkedIn(key,secret,return_url)
|
self.api = linkedin.LinkedIn(key, secret, return_url)
|
||||||
self.token = result = self.api.requestToken()
|
self.token = result = self.api.requestToken()
|
||||||
|
|
||||||
def login_url(self, next="/"):
|
def login_url(self, next="/"):
|
||||||
@@ -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(
|
||||||
return dict(first_name = profile.first_name,
|
profile).public_url = "http://www.linkedin.com/in/ozgurv"
|
||||||
last_name = profile.last_name,
|
return dict(first_name=profile.first_name,
|
||||||
username = profile.id)
|
last_name=profile.last_name,
|
||||||
|
username=profile.id)
|
||||||
|
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ from gluon.tools import fetch
|
|||||||
from gluon.storage import Storage
|
from gluon.storage import Storage
|
||||||
import gluon.contrib.simplejson as json
|
import gluon.contrib.simplejson as json
|
||||||
|
|
||||||
|
|
||||||
class Loginza(object):
|
class Loginza(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -23,13 +24,13 @@ class Loginza(object):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
request,
|
request,
|
||||||
url = "",
|
url="",
|
||||||
embed = True,
|
embed=True,
|
||||||
auth_url = "http://loginza.ru/api/authinfo",
|
auth_url="http://loginza.ru/api/authinfo",
|
||||||
language = "en",
|
language="en",
|
||||||
prompt = "loginza",
|
prompt="loginza",
|
||||||
on_login_failure = None,
|
on_login_failure=None,
|
||||||
):
|
):
|
||||||
|
|
||||||
self.request = request
|
self.request = request
|
||||||
self.token_url = url
|
self.token_url = url
|
||||||
@@ -46,49 +47,50 @@ class Loginza(object):
|
|||||||
# FIXME: what if email is unique=True
|
# FIXME: what if email is unique=True
|
||||||
|
|
||||||
self.mappings["http://twitter.com/"] = lambda profile:\
|
self.mappings["http://twitter.com/"] = lambda profile:\
|
||||||
dict(registration_id = profile.get("identity",""),
|
dict(registration_id=profile.get("identity", ""),
|
||||||
username = profile.get("nickname",""),
|
username=profile.get("nickname", ""),
|
||||||
email = profile.get("email",""),
|
email=profile.get("email", ""),
|
||||||
last_name = profile.get("name","").get("full_name",""),
|
last_name=profile.get("name", "").get("full_name", ""),
|
||||||
#avatar = profile.get("photo",""),
|
#avatar = profile.get("photo",""),
|
||||||
)
|
)
|
||||||
self.mappings["https://www.google.com/accounts/o8/ud"] = lambda profile:\
|
self.mappings["https://www.google.com/accounts/o8/ud"] = lambda profile:\
|
||||||
dict(registration_id = profile.get("identity",""),
|
dict(registration_id=profile.get("identity", ""),
|
||||||
username = profile.get("name","").get("full_name",""),
|
username=profile.get("name", "").get("full_name", ""),
|
||||||
email = profile.get("email",""),
|
email=profile.get("email", ""),
|
||||||
first_name = profile.get("name","").get("first_name",""),
|
first_name=profile.get("name", "").get("first_name", ""),
|
||||||
last_name = profile.get("name","").get("last_name",""),
|
last_name=profile.get("name", "").get("last_name", ""),
|
||||||
#avatar = profile.get("photo",""),
|
#avatar = profile.get("photo",""),
|
||||||
)
|
)
|
||||||
self.mappings["http://vkontakte.ru/"] = lambda profile:\
|
self.mappings["http://vkontakte.ru/"] = lambda profile:\
|
||||||
dict(registration_id=profile.get("identity",""),
|
dict(registration_id=profile.get("identity", ""),
|
||||||
username = profile.get("name","").get("full_name",""),
|
username=profile.get("name", "").get("full_name", ""),
|
||||||
email = profile.get("email",""),
|
email=profile.get("email", ""),
|
||||||
first_name = profile.get("name","").get("first_name",""),
|
first_name=profile.get("name", "").get("first_name", ""),
|
||||||
last_name = profile.get("name","").get("last_name",""),
|
last_name=profile.get("name", "").get("last_name", ""),
|
||||||
#avatar = profile.get("photo",""),
|
#avatar = profile.get("photo",""),
|
||||||
)
|
)
|
||||||
self.mappings.default = lambda profile:\
|
self.mappings.default = lambda profile:\
|
||||||
dict(registration_id = profile.get("identity",""),
|
dict(registration_id=profile.get("identity", ""),
|
||||||
username = profile.get("name","").get("full_name"),
|
username=profile.get("name", "").get("full_name"),
|
||||||
email = profile.get("email",""),
|
email=profile.get("email", ""),
|
||||||
first_name = profile.get("name","").get("first_name",""),
|
first_name=profile.get("name", "").get("first_name", ""),
|
||||||
last_name = profile.get("name","").get("last_name",""),
|
last_name=profile.get("name", "").get("last_name", ""),
|
||||||
#avatar = profile.get("photo",""),
|
#avatar = profile.get("photo",""),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_user(self):
|
def get_user(self):
|
||||||
request = self.request
|
request = self.request
|
||||||
if request.vars.token:
|
if request.vars.token:
|
||||||
user = Storage()
|
user = Storage()
|
||||||
data = urllib.urlencode(dict(token = request.vars.token))
|
data = urllib.urlencode(dict(token=request.vars.token))
|
||||||
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(
|
||||||
SCRIPT(_src="https://s3-eu-west-1.amazonaws.com/s1.loginza.ru/js/widget.js", _type="text/javascript"))
|
A(self.prompt, _href=LOGINZA_URL % (
|
||||||
|
self.language, self.token_url), _class="loginza"),
|
||||||
|
SCRIPT(_src="https://s3-eu-west-1.amazonaws.com/s1.loginza.ru/js/widget.js", _type="text/javascript"))
|
||||||
return form
|
return form
|
||||||
|
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@ import time
|
|||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from gluon.dal import DAL
|
from gluon.dal import DAL
|
||||||
|
|
||||||
|
|
||||||
def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
||||||
time_offset=60):
|
time_offset=60):
|
||||||
|
|
||||||
@@ -44,7 +45,8 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
|||||||
writable=False, readable=False, default=''))
|
writable=False, readable=False, default=''))
|
||||||
|
|
||||||
##validators
|
##validators
|
||||||
custom_auth_table = db[auth.settings.table_user_name] # get the custom_auth_table
|
custom_auth_table = db[auth.settings.table_user_name]
|
||||||
|
# get the custom_auth_table
|
||||||
custom_auth_table.first_name.requires = \
|
custom_auth_table.first_name.requires = \
|
||||||
IS_NOT_EMPTY(error_message=auth.messages.is_empty)
|
IS_NOT_EMPTY(error_message=auth.messages.is_empty)
|
||||||
custom_auth_table.last_name.requires = \
|
custom_auth_table.last_name.requires = \
|
||||||
@@ -76,14 +78,15 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
|||||||
- as of now user field is hardcoded to email. Some way of selecting user table and user field.
|
- as of now user field is hardcoded to email. Some way of selecting user table and user field.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def verify_otp(otp,pin,secret,offset=60):
|
def verify_otp(otp, pin, secret, offset=60):
|
||||||
epoch_time = int(time.time())
|
epoch_time = int(time.time())
|
||||||
time_start = int(str(epoch_time - offset)[:-1])
|
time_start = int(str(epoch_time - offset)[:-1])
|
||||||
time_end = int(str(epoch_time + offset)[:-1])
|
time_end = int(str(epoch_time + offset)[:-1])
|
||||||
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,
|
||||||
@@ -91,15 +94,18 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
|||||||
db=db,
|
db=db,
|
||||||
offset=time_offset):
|
offset=time_offset):
|
||||||
if db:
|
if db:
|
||||||
user_data = db(db.auth_user.email == email ).select().first()
|
user_data = db(db.auth_user.email == email).select().first()
|
||||||
if user_data:
|
if user_data:
|
||||||
if user_data['motp_secret'] and user_data['motp_pin']:
|
if user_data['motp_secret'] and user_data['motp_pin']:
|
||||||
motp_secret = user_data['motp_secret']
|
motp_secret = user_data['motp_secret']
|
||||||
motp_pin = user_data['motp_pin']
|
motp_pin = user_data['motp_pin']
|
||||||
otp_check = verify_otp(password,motp_pin,motp_secret,offset=offset)
|
otp_check = verify_otp(
|
||||||
if otp_check: return True
|
password, motp_pin, motp_secret, offset=offset)
|
||||||
else: return False
|
if otp_check:
|
||||||
else: return False
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
return False
|
return False
|
||||||
return motp_auth_aux
|
return motp_auth_aux
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ from urllib2 import urlopen
|
|||||||
import urllib2
|
import urllib2
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
|
|
||||||
|
|
||||||
class OAuthAccount(object):
|
class OAuthAccount(object):
|
||||||
"""
|
"""
|
||||||
Login will be done via OAuth Framework, instead of web2py's
|
Login will be done via OAuth Framework, instead of web2py's
|
||||||
@@ -51,7 +52,8 @@ class OAuthAccount(object):
|
|||||||
TOKEN_URL="..."
|
TOKEN_URL="..."
|
||||||
ACCESS_TOKEN_URL="..."
|
ACCESS_TOKEN_URL="..."
|
||||||
from gluon.contrib.login_methods.oauth10a_account import OAuthAccount
|
from gluon.contrib.login_methods.oauth10a_account import OAuthAccount
|
||||||
auth.settings.login_form=OAuthAccount(globals(),CLIENT_ID,CLIENT_SECRET, AUTH_URL, TOKEN_URL, ACCESS_TOKEN_URL)
|
auth.settings.login_form=OAuthAccount(globals(
|
||||||
|
),CLIENT_ID,CLIENT_SECRET, AUTH_URL, TOKEN_URL, ACCESS_TOKEN_URL)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -61,20 +63,20 @@ class OAuthAccount(object):
|
|||||||
Appends the _next action to the generated url so the flows continues.
|
Appends the _next action to the generated url so the flows continues.
|
||||||
"""
|
"""
|
||||||
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:
|
||||||
path_info = next
|
path_info = next
|
||||||
else:
|
else:
|
||||||
path_info = r.env.path_info
|
path_info = r.env.path_info
|
||||||
uri = '%s://%s%s' %(url_scheme, http_host, path_info)
|
uri = '%s://%s%s' % (url_scheme, http_host, path_info)
|
||||||
if r.get_vars and not next:
|
if r.get_vars and not next:
|
||||||
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)
|
||||||
|
|
||||||
@@ -111,7 +112,7 @@ class OAuthAccount(object):
|
|||||||
self.session.access_token = None
|
self.session.access_token = None
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __init__(self, g, client_id, client_secret, auth_url, token_url, access_token_url):
|
def __init__(self, g, client_id, client_secret, auth_url, token_url, access_token_url):
|
||||||
self.globals = g
|
self.globals = g
|
||||||
self.client_id = client_id
|
self.client_id = client_id
|
||||||
self.client_secret = client_secret
|
self.client_secret = client_secret
|
||||||
@@ -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
|
||||||
@@ -163,10 +163,11 @@ class OAuthAccount(object):
|
|||||||
# putting it in the body seems to work.
|
# putting it in the body seems to work.
|
||||||
callback_url = self.__redirect_uri(next)
|
callback_url = self.__redirect_uri(next)
|
||||||
data = urlencode(dict(oauth_callback=callback_url))
|
data = urlencode(dict(oauth_callback=callback_url))
|
||||||
resp, content = client.request(self.token_url, "POST", body=data)
|
resp, content = client.request(self.token_url, "POST", body=data)
|
||||||
if resp['status'] != '200':
|
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)
|
||||||
@@ -174,18 +175,12 @@ class OAuthAccount(object):
|
|||||||
# Redirect the user to the authentication URL and pass the callback url.
|
# Redirect the user to the authentication URL and pass the callback url.
|
||||||
data = urlencode(dict(oauth_token=request_token.key,
|
data = urlencode(dict(oauth_token=request_token.key,
|
||||||
oauth_callback=callback_url))
|
oauth_callback=callback_url))
|
||||||
auth_request_url = self.auth_url + '?' +data
|
auth_request_url = self.auth_url + '?' + data
|
||||||
|
|
||||||
|
|
||||||
HTTP = self.globals['HTTP']
|
HTTP = self.globals['HTTP']
|
||||||
|
|
||||||
|
|
||||||
raise HTTP(307,
|
raise HTTP(307,
|
||||||
"You are not authenticated: you are being redirected to the <a href='" + auth_request_url + "'> authentication server</a>",
|
"You are not authenticated: you are being redirected to the <a href='" + auth_request_url + "'> authentication server</a>",
|
||||||
Location=auth_request_url)
|
Location=auth_request_url)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -17,6 +17,7 @@ import urllib2
|
|||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from gluon import current, redirect, HTTP
|
from gluon import current, redirect, HTTP
|
||||||
|
|
||||||
|
|
||||||
class OAuthAccount(object):
|
class OAuthAccount(object):
|
||||||
"""
|
"""
|
||||||
Login will be done via OAuth Framework, instead of web2py's
|
Login will be done via OAuth Framework, instead of web2py's
|
||||||
@@ -84,7 +85,8 @@ class OAuthAccount(object):
|
|||||||
username = user['id'])
|
username = user['id'])
|
||||||
|
|
||||||
|
|
||||||
auth.settings.actions_disabled=['register','change_password','request_reset_password','profile']
|
auth.settings.actions_disabled=['register',
|
||||||
|
'change_password','request_reset_password','profile']
|
||||||
auth.settings.login_form=FaceBookAccount()
|
auth.settings.login_form=FaceBookAccount()
|
||||||
|
|
||||||
Any optional arg in the constructor will be passed asis to remote
|
Any optional arg in the constructor will be passed asis to remote
|
||||||
@@ -99,8 +101,9 @@ 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():
|
||||||
@@ -159,19 +160,19 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
|||||||
print tmp
|
print tmp
|
||||||
raise Exception(tmp)
|
raise Exception(tmp)
|
||||||
finally:
|
finally:
|
||||||
del current.session.code # throw it away
|
del current.session.code # throw it away
|
||||||
|
|
||||||
if open_url:
|
if open_url:
|
||||||
try:
|
try:
|
||||||
data = open_url.read()
|
data = open_url.read()
|
||||||
tokendata = cgi.parse_qs(data)
|
tokendata = cgi.parse_qs(data)
|
||||||
current.session.token = \
|
current.session.token = \
|
||||||
dict([(k,v[-1]) for k,v in tokendata.items()])
|
dict([(k, v[-1]) for k, v in tokendata.items()])
|
||||||
# set expiration absolute time try to avoid broken
|
# 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 dict(first_name = 'Pinco',
|
return None
|
||||||
last_name = 'Pallino',
|
return dict(first_name='Pinco',
|
||||||
username = 'pincopallino')
|
last_name='Pallino',
|
||||||
raise NotImplementedError, "Must override get_user()"
|
username='pincopallino')
|
||||||
|
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.
|
||||||
@@ -239,10 +241,9 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
|||||||
self.graph = None
|
self.graph = None
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
return dict(first_name = user['first_name'],
|
return dict(first_name=user['first_name'],
|
||||||
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):
|
||||||
"""
|
"""
|
||||||
@@ -258,13 +259,13 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
|||||||
|
|
||||||
if not self.accessToken():
|
if not self.accessToken():
|
||||||
if not current.request.vars.code:
|
if not current.request.vars.code:
|
||||||
current.session.redirect_uri=self.__redirect_uri(next)
|
current.session.redirect_uri = self.__redirect_uri(next)
|
||||||
data = dict(redirect_uri=current.session.redirect_uri,
|
data = dict(redirect_uri=current.session.redirect_uri,
|
||||||
response_type='code',
|
response_type='code',
|
||||||
client_id=self.client_id)
|
client_id=self.client_id)
|
||||||
if self.args:
|
if self.args:
|
||||||
data.update(self.args)
|
data.update(self.args)
|
||||||
auth_request_url = self.auth_url + "?" +urlencode(data)
|
auth_request_url = self.auth_url + "?" + urlencode(data)
|
||||||
raise HTTP(307,
|
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)
|
||||||
@@ -273,5 +274,3 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
|||||||
self.accessToken()
|
self.accessToken()
|
||||||
return current.session.code
|
return current.session.code
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -49,6 +49,7 @@ except ImportError, err:
|
|||||||
|
|
||||||
DEFAULT = lambda: None
|
DEFAULT = lambda: None
|
||||||
|
|
||||||
|
|
||||||
class OpenIDAuth(object):
|
class OpenIDAuth(object):
|
||||||
"""
|
"""
|
||||||
OpenIDAuth
|
OpenIDAuth
|
||||||
@@ -94,7 +95,7 @@ class OpenIDAuth(object):
|
|||||||
if not auth.settings.table_user:
|
if not auth.settings.table_user:
|
||||||
raise
|
raise
|
||||||
self.table_user = self.auth.settings.table_user
|
self.table_user = self.auth.settings.table_user
|
||||||
self.openid_expiration = 15 #minutes
|
self.openid_expiration = 15 # minutes
|
||||||
|
|
||||||
self.messages = self._define_messages()
|
self.messages = self._define_messages()
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ class OpenIDAuth(object):
|
|||||||
messages.flash_openid_associated = 'OpenID associated'
|
messages.flash_openid_associated = 'OpenID associated'
|
||||||
messages.flash_associate_openid = 'Please login or register an account for this OpenID.'
|
messages.flash_associate_openid = 'Please login or register an account for this OpenID.'
|
||||||
messages.p_openid_not_registered = "This Open ID haven't be registered. " \
|
messages.p_openid_not_registered = "This Open ID haven't be registered. " \
|
||||||
+ "Please login to associate with it or register an account for it."
|
+ "Please login to associate with it or register an account for it."
|
||||||
messages.flash_openid_authenticated = 'OpenID authenticated successfully.'
|
messages.flash_openid_authenticated = 'OpenID authenticated successfully.'
|
||||||
messages.flash_openid_fail_authentication = 'OpenID authentication failed. (Error message: %s)'
|
messages.flash_openid_fail_authentication = 'OpenID authentication failed. (Error message: %s)'
|
||||||
messages.flash_openid_canceled = 'OpenID authentication canceled by user.'
|
messages.flash_openid_canceled = 'OpenID authentication canceled by user.'
|
||||||
@@ -158,7 +159,7 @@ class OpenIDAuth(object):
|
|||||||
and not processed yet. Else return the OpenID form for login.
|
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()
|
||||||
@@ -172,12 +173,12 @@ class OpenIDAuth(object):
|
|||||||
args = request.args
|
args = request.args
|
||||||
|
|
||||||
if args[0] == 'logout':
|
if args[0] == 'logout':
|
||||||
return True # Let logout_url got called
|
return True # Let logout_url got called
|
||||||
|
|
||||||
if current.session.w2popenid:
|
if current.session.w2popenid:
|
||||||
w2popenid = current.session.w2popenid
|
w2popenid = current.session.w2popenid
|
||||||
db = self.db
|
db = self.db
|
||||||
if (w2popenid.ok is True and w2popenid.oid): # OpenID authenticated
|
if (w2popenid.ok is True and w2popenid.oid): # OpenID authenticated
|
||||||
if self._w2popenid_expired(w2popenid):
|
if self._w2popenid_expired(w2popenid):
|
||||||
del(current.session.w2popenid)
|
del(current.session.w2popenid)
|
||||||
flash = self.messages.flash_openid_expired
|
flash = self.messages.flash_openid_expired
|
||||||
@@ -196,22 +197,23 @@ 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,
|
||||||
args=['login'],
|
args=['login'],
|
||||||
vars={nextvar:self.login_url}))
|
vars={nextvar: self.login_url}))
|
||||||
self.login_form = self._form_with_notification()
|
self.login_form = self._form_with_notification()
|
||||||
current.session.flash = self.messages.flash_associate_openid
|
current.session.flash = self.messages.flash_associate_openid
|
||||||
return None # need to login or register to associate this openid
|
return None # need to login or register to associate this openid
|
||||||
|
|
||||||
# Get existed OpenID user
|
# 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)
|
||||||
@@ -219,16 +221,17 @@ class OpenIDAuth(object):
|
|||||||
username = 'username'
|
username = 'username'
|
||||||
elif 'email' in self.table_user.fields():
|
elif 'email' in self.table_user.fields():
|
||||||
username = 'email'
|
username = 'email'
|
||||||
return {username: user[username]} if user else None # login success (almost)
|
return {username: user[username]} if user else None # login success (almost)
|
||||||
|
|
||||||
return None # just start to login
|
return None # just start to login
|
||||||
|
|
||||||
def _find_matched_openid(self, db, oid, type_='openid'):
|
def _find_matched_openid(self, db, oid, type_='openid'):
|
||||||
"""
|
"""
|
||||||
Get the matched OpenID for given
|
Get the matched OpenID for given
|
||||||
"""
|
"""
|
||||||
query = ((db.alt_logins.username == oid) & (db.alt_logins.type == type_))
|
query = (
|
||||||
alt_login = db(query).select().first() # Get the OpenID record
|
(db.alt_logins.username == oid) & (db.alt_logins.type == type_))
|
||||||
|
alt_login = db(query).select().first() # Get the OpenID record
|
||||||
return alt_login
|
return alt_login
|
||||||
|
|
||||||
def _associate_user_openid(self, user, oid):
|
def _associate_user_openid(self, user, oid):
|
||||||
@@ -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
|
||||||
@@ -300,7 +302,7 @@ background-color: transparent;
|
|||||||
padding-left: 18px;
|
padding-left: 18px;
|
||||||
width: 400px;
|
width: 400px;
|
||||||
"""
|
"""
|
||||||
style = style.replace("\n","")
|
style = style.replace("\n", "")
|
||||||
|
|
||||||
request = current.request
|
request = current.request
|
||||||
session = current.session
|
session = current.session
|
||||||
@@ -308,21 +310,25 @@ 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)
|
||||||
INPUT(_type="input", _name="oid",
|
form = FORM(
|
||||||
requires=IS_NOT_EMPTY(error_message=messages.openid_fail_discover),
|
openid_field_label or self.messages.label_alt_login_username,
|
||||||
_style=style),
|
INPUT(_type="input", _name="oid",
|
||||||
hidden_next_input,
|
requires=IS_NOT_EMPTY(
|
||||||
INPUT(_type="submit", _value=submit_button or messages.submit_button),
|
error_message=messages.openid_fail_discover),
|
||||||
" ",
|
_style=style),
|
||||||
A(messages.comment_openid_signin,
|
hidden_next_input,
|
||||||
_href=messages.comment_openid_help_url,
|
INPUT(_type="submit",
|
||||||
_title=messages.comment_openid_help_title,
|
_value=submit_button or messages.submit_button),
|
||||||
_class='openid-identifier',
|
" ",
|
||||||
_target="_blank"),
|
A(messages.comment_openid_signin,
|
||||||
_action=self.login_url
|
_href=messages.comment_openid_help_url,
|
||||||
)
|
_title=messages.comment_openid_help_title,
|
||||||
|
_class='openid-identifier',
|
||||||
|
_target="_blank"),
|
||||||
|
_action=self.login_url
|
||||||
|
)
|
||||||
if form.accepts(request.vars, session):
|
if form.accepts(request.vars, session):
|
||||||
oid = request.vars.oid
|
oid = request.vars.oid
|
||||||
consumerhelper = self._init_consumerhelper()
|
consumerhelper = self._init_consumerhelper()
|
||||||
@@ -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
|
||||||
@@ -397,8 +406,8 @@ width: 400px;
|
|||||||
for alt_login in alt_logins:
|
for alt_login in alt_logins:
|
||||||
username = alt_login.username
|
username = alt_login.username
|
||||||
delete_href = URL(r=request, f='user',
|
delete_href = URL(r=request, f='user',
|
||||||
args=['profile'],
|
args=['profile'],
|
||||||
vars={'delete_openid': username})
|
vars={'delete_openid': username})
|
||||||
delete_link = A(messages.a_delete, _href=delete_href)
|
delete_link = A(messages.a_delete, _href=delete_href)
|
||||||
l.append(LI(username, " ", delete_link))
|
l.append(LI(username, " ", delete_link))
|
||||||
|
|
||||||
@@ -409,23 +418,23 @@ width: 400px;
|
|||||||
_next='profile',
|
_next='profile',
|
||||||
submit_button=messages.submit_button_add,
|
submit_button=messages.submit_button_add,
|
||||||
openid_field_label=messages.label_add_alt_login_username)
|
openid_field_label=messages.label_add_alt_login_username)
|
||||||
)
|
)
|
||||||
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
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, session, db):
|
def __init__(self, session, db):
|
||||||
self.session = session
|
self.session = session
|
||||||
store = self._init_store(db)
|
store = self._init_store(db)
|
||||||
self.consumer = openid.consumer.consumer.Consumer(session, store)
|
self.consumer = openid.consumer.consumer.Consumer(session, store)
|
||||||
|
|
||||||
def _init_store(self, db):
|
def _init_store(self, db):
|
||||||
"""
|
"""
|
||||||
@@ -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
|
||||||
@@ -446,7 +455,7 @@ class ConsumerHelper(object):
|
|||||||
w2popenid = self.session.w2popenid
|
w2popenid = self.session.w2popenid
|
||||||
w2popenid.oid = oid
|
w2popenid.oid = oid
|
||||||
auth_req = self.consumer.begin(oid)
|
auth_req = self.consumer.begin(oid)
|
||||||
auth_req.addExtension(SRegRequest(required=['email','nickname']))
|
auth_req.addExtension(SRegRequest(required=['email', 'nickname']))
|
||||||
url = auth_req.redirectURL(return_to=return_to_url, realm=realm)
|
url = auth_req.redirectURL(return_to=return_to_url, realm=realm)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
@@ -504,19 +513,27 @@ 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('secret', 'blob', required=True),
|
Field('handle',
|
||||||
Field('issued', 'integer', required=True),
|
'string', length=255, required=True),
|
||||||
Field('lifetime', 'integer', required=True),
|
Field('secret', 'blob', required=True),
|
||||||
Field('assoc_type', 'string', length=64, required=True)
|
Field('issued',
|
||||||
)
|
'integer', required=True),
|
||||||
|
Field('lifetime',
|
||||||
|
'integer', required=True),
|
||||||
|
Field('assoc_type',
|
||||||
|
'string', length=64, required=True)
|
||||||
|
)
|
||||||
if self.table_oid_nonces_name not in self.database:
|
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,14 +542,15 @@ 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,
|
||||||
secret = association.secret,
|
secret=association.secret,
|
||||||
issued = association.issued,
|
issued=association.issued,
|
||||||
lifetime = association.lifetime,
|
lifetime=association.lifetime,
|
||||||
assoc_type = association.assoc_type), 'insert '*10
|
assoc_type=association.assoc_type), 'insert ' * 10
|
||||||
|
|
||||||
def getAssociation(self, server_url, handle=None):
|
def getAssociation(self, server_url, handle=None):
|
||||||
"""
|
"""
|
||||||
@@ -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):
|
||||||
"""
|
"""
|
||||||
@@ -575,10 +595,10 @@ class Web2pyStore(OpenIDStore):
|
|||||||
if db(query).count() > 0:
|
if db(query).count() > 0:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
db.oid_nonces.insert(server_url = server_url,
|
db.oid_nonces.insert(server_url=server_url,
|
||||||
timestamp = timestamp,
|
timestamp=timestamp,
|
||||||
salt = salt)
|
salt=salt)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _removeExpiredAssocations(self, rows):
|
def _removeExpiredAssocations(self, rows):
|
||||||
"""
|
"""
|
||||||
@@ -599,7 +619,7 @@ class Web2pyStore(OpenIDStore):
|
|||||||
keep_assoc.append(r)
|
keep_assoc.append(r)
|
||||||
for r in remove_assoc:
|
for r in remove_assoc:
|
||||||
del db.oid_associations[r['id']]
|
del db.oid_associations[r['id']]
|
||||||
return (keep_assoc, len(remove_assoc)) # return tuple (list of valid associations, number of deleted associations)
|
return (keep_assoc, len(remove_assoc)) # return tuple (list of valid associations, number of deleted associations)
|
||||||
|
|
||||||
def cleanupNonces(self):
|
def cleanupNonces(self):
|
||||||
"""
|
"""
|
||||||
@@ -619,7 +639,7 @@ class Web2pyStore(OpenIDStore):
|
|||||||
|
|
||||||
db = self.database
|
db = self.database
|
||||||
query = (db.oid_associations.id > 0)
|
query = (db.oid_associations.id > 0)
|
||||||
return self._removeExpiredAssocations(db(query).select())[1] #return number of assoc removed
|
return self._removeExpiredAssocations(db(query).select())[1] # return number of assoc removed
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
"""
|
"""
|
||||||
@@ -628,6 +648,3 @@ class Web2pyStore(OpenIDStore):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
return self.cleanupNonces(), self.cleanupAssociations()
|
return self.cleanupNonces(), self.cleanupAssociations()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
from gluon.contrib.pam import authenticate
|
from gluon.contrib.pam import authenticate
|
||||||
|
|
||||||
|
|
||||||
def pam_auth():
|
def pam_auth():
|
||||||
"""
|
"""
|
||||||
to use pam_login:
|
to use pam_login:
|
||||||
@@ -19,5 +20,3 @@ def pam_auth():
|
|||||||
return authenticate(username, password)
|
return authenticate(username, password)
|
||||||
|
|
||||||
return pam_auth_aux
|
return pam_auth_aux
|
||||||
|
|
||||||
|
|
||||||
@@ -19,11 +19,13 @@ from gluon.tools import fetch
|
|||||||
from gluon.storage import Storage
|
from gluon.storage import Storage
|
||||||
import gluon.contrib.simplejson as json
|
import gluon.contrib.simplejson as json
|
||||||
|
|
||||||
|
|
||||||
class RPXAccount(object):
|
class RPXAccount(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from gluon.contrib.login_methods.rpx_account import RPXAccount
|
from gluon.contrib.login_methods.rpx_account import RPXAccount
|
||||||
auth.settings.actions_disabled=['register','change_password','request_reset_password']
|
auth.settings.actions_disabled=['register','change_password',
|
||||||
|
'request_reset_password']
|
||||||
auth.settings.login_form = RPXAccount(request,
|
auth.settings.login_form = RPXAccount(request,
|
||||||
api_key="...",
|
api_key="...",
|
||||||
domain="...",
|
domain="...",
|
||||||
@@ -32,18 +34,18 @@ class RPXAccount(object):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
request,
|
request,
|
||||||
api_key = "",
|
api_key="",
|
||||||
domain = "",
|
domain="",
|
||||||
url = "",
|
url="",
|
||||||
embed = True,
|
embed=True,
|
||||||
auth_url = "https://rpxnow.com/api/v2/auth_info",
|
auth_url="https://rpxnow.com/api/v2/auth_info",
|
||||||
language= "en",
|
language="en",
|
||||||
prompt='rpx',
|
prompt='rpx',
|
||||||
on_login_failure = None,
|
on_login_failure=None,
|
||||||
):
|
):
|
||||||
|
|
||||||
self.request=request
|
self.request = request
|
||||||
self.api_key=api_key
|
self.api_key = api_key
|
||||||
self.embed = embed
|
self.embed = embed
|
||||||
self.auth_url = auth_url
|
self.auth_url = auth_url
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
@@ -54,38 +56,40 @@ class RPXAccount(object):
|
|||||||
self.on_login_failure = on_login_failure
|
self.on_login_failure = on_login_failure
|
||||||
self.mappings = Storage()
|
self.mappings = Storage()
|
||||||
|
|
||||||
dn = {'givenName':'','familyName':''}
|
dn = {'givenName': '', 'familyName': ''}
|
||||||
self.mappings.Facebook = lambda profile, dn=dn:\
|
self.mappings.Facebook = lambda profile, dn=dn:\
|
||||||
dict(registration_id = profile.get("identifier",""),
|
dict(registration_id=profile.get("identifier", ""),
|
||||||
username = profile.get("preferredUsername",""),
|
username=profile.get("preferredUsername", ""),
|
||||||
email = profile.get("email",""),
|
email=profile.get("email", ""),
|
||||||
first_name = profile.get("name",dn).get("givenName",""),
|
first_name=profile.get("name", dn).get("givenName", ""),
|
||||||
last_name = profile.get("name",dn).get("familyName",""))
|
last_name=profile.get("name", dn).get("familyName", ""))
|
||||||
self.mappings.Google = lambda profile, dn=dn:\
|
self.mappings.Google = lambda profile, dn=dn:\
|
||||||
dict(registration_id=profile.get("identifier",""),
|
dict(registration_id=profile.get("identifier", ""),
|
||||||
username=profile.get("preferredUsername",""),
|
username=profile.get("preferredUsername", ""),
|
||||||
email=profile.get("email",""),
|
email=profile.get("email", ""),
|
||||||
first_name=profile.get("name",dn).get("givenName",""),
|
first_name=profile.get("name", dn).get("givenName", ""),
|
||||||
last_name=profile.get("name",dn).get("familyName",""))
|
last_name=profile.get("name", dn).get("familyName", ""))
|
||||||
self.mappings.default = lambda profile:\
|
self.mappings.default = lambda profile:\
|
||||||
dict(registration_id=profile.get("identifier",""),
|
dict(registration_id=profile.get("identifier", ""),
|
||||||
username=profile.get("preferredUsername",""),
|
username=profile.get("preferredUsername", ""),
|
||||||
email=profile.get("email",""),
|
email=profile.get("email", ""),
|
||||||
first_name=profile.get("preferredUsername",""),
|
first_name=profile.get("preferredUsername", ""),
|
||||||
last_name='')
|
last_name='')
|
||||||
|
|
||||||
def get_user(self):
|
def get_user(self):
|
||||||
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(
|
||||||
auth_info_json = fetch(self.auth_url+'?'+data)
|
dict(apiKey=self.api_key, token=request.vars.token))
|
||||||
|
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)
|
||||||
@@ -95,12 +99,14 @@ class RPXAccount(object):
|
|||||||
request = self.request
|
request = self.request
|
||||||
args = request.args
|
args = request.args
|
||||||
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(
|
||||||
_scrolling="no",
|
_src=JANRAIN_URL % (
|
||||||
_frameborder="no",
|
self.domain, self.token_url, self.language),
|
||||||
_style="width:400px;height:240px;")
|
_scrolling="no",
|
||||||
|
_frameborder="no",
|
||||||
|
_style="width:400px;height:240px;")
|
||||||
else:
|
else:
|
||||||
JANRAIN_URL = \
|
JANRAIN_URL = \
|
||||||
"https://%s.rpxnow.com/openid/v2/signin?token_url=%s"
|
"https://%s.rpxnow.com/openid/v2/signin?token_url=%s"
|
||||||
@@ -114,15 +120,15 @@ class RPXAccount(object):
|
|||||||
_type="text/javascript"))
|
_type="text/javascript"))
|
||||||
return rpxform
|
return rpxform
|
||||||
|
|
||||||
def use_janrain(auth,filename='private/janrain.key',**kwargs):
|
|
||||||
path = os.path.join(current.request.folder,filename)
|
def use_janrain(auth, filename='private/janrain.key', **kwargs):
|
||||||
|
path = os.path.join(current.request.folder, filename)
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
request = current.request
|
request = current.request
|
||||||
domain,key = open(path,'r').read().strip().split(':')
|
domain, key = open(path, 'r').read().strip().split(':')
|
||||||
host = current.request.env.http_host
|
host = current.request.env.http_host
|
||||||
url = URL('default', 'user', args='login', scheme=True)
|
url = URL('default', 'user', args='login', scheme=True)
|
||||||
auth.settings.actions_disabled = \
|
auth.settings.actions_disabled = \
|
||||||
['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)
|
||||||
|
|
||||||
@@ -11,13 +11,12 @@ Adds support for x509 authentication.
|
|||||||
|
|
||||||
from gluon.globals import current
|
from gluon.globals import current
|
||||||
from gluon.storage import Storage
|
from gluon.storage import Storage
|
||||||
from gluon.http import HTTP,redirect
|
from gluon.http import HTTP, redirect
|
||||||
|
|
||||||
#requires M2Crypto
|
#requires M2Crypto
|
||||||
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()
|
||||||
|
|
||||||
@@ -53,23 +51,17 @@ class X509Auth(object):
|
|||||||
# cn = self.subject.cn
|
# cn = self.subject.cn
|
||||||
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(
|
||||||
subject.get_entries_by_nid(subject.nid[x]))),
|
).as_text(),
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+61
-62
@@ -44,22 +44,21 @@ SQL_DIALECTS = {'memcache': {
|
|||||||
'is not null': 'IS NOT NULL',
|
'is not null': 'IS NOT NULL',
|
||||||
'extract': None,
|
'extract': None,
|
||||||
'left join': None,
|
'left join': None,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
def assert_filter_fields(*fields):
|
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):
|
||||||
@@ -78,7 +77,7 @@ def dateobj_to_datetime(object):
|
|||||||
object.minute,
|
object.minute,
|
||||||
object.second,
|
object.second,
|
||||||
object.microsecond,
|
object.microsecond,
|
||||||
)
|
)
|
||||||
return object
|
return object
|
||||||
|
|
||||||
|
|
||||||
@@ -96,7 +95,7 @@ def sqlhtml_validators(field_type, length):
|
|||||||
'time': validators.IS_TIME(),
|
'time': validators.IS_TIME(),
|
||||||
'datetime': validators.IS_DATETIME(),
|
'datetime': validators.IS_DATETIME(),
|
||||||
'reference': validators.IS_INT_IN_RANGE(0, 1e100),
|
'reference': validators.IS_INT_IN_RANGE(0, 1e100),
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
return v[field_type[:9]]
|
return v[field_type[:9]]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -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):
|
||||||
@@ -151,14 +151,14 @@ class MEMDB(DALStorage):
|
|||||||
tablename,
|
tablename,
|
||||||
*fields,
|
*fields,
|
||||||
**args
|
**args
|
||||||
):
|
):
|
||||||
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
|
||||||
@@ -190,7 +190,7 @@ class Table(DALStorage):
|
|||||||
db,
|
db,
|
||||||
tablename,
|
tablename,
|
||||||
*fields
|
*fields
|
||||||
):
|
):
|
||||||
self._db = db
|
self._db = db
|
||||||
self._tablename = tablename
|
self._tablename = tablename
|
||||||
self.fields = SQLCallableList()
|
self.fields = SQLCallableList()
|
||||||
@@ -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,11 +269,11 @@ 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],
|
||||||
self[field].type, self._db)
|
self[field].type, self._db)
|
||||||
return self._tableobj.set(self._id_to_key(id), fields)
|
return self._tableobj.set(self._id_to_key(id), fields)
|
||||||
|
|
||||||
def delete(self, id):
|
def delete(self, id):
|
||||||
@@ -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):
|
||||||
@@ -307,7 +307,7 @@ class Expression(object):
|
|||||||
name,
|
name,
|
||||||
type='string',
|
type='string',
|
||||||
db=None,
|
db=None,
|
||||||
):
|
):
|
||||||
(self.name, self.type, self._db) = (name, type, db)
|
(self.name, self.type, self._db) = (name, type, db)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -393,11 +393,11 @@ class Field(Expression):
|
|||||||
notnull=False,
|
notnull=False,
|
||||||
unique=False,
|
unique=False,
|
||||||
uploadfield=True,
|
uploadfield=True,
|
||||||
):
|
):
|
||||||
|
|
||||||
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,9 +437,9 @@ 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('-')]
|
||||||
object = datetime.date(y, m, d)
|
object = datetime.date(y, m, d)
|
||||||
elif fieldtype == 'time' and not isinstance(object, datetime.time):
|
elif fieldtype == 'time' and not isinstance(object, datetime.time):
|
||||||
@@ -450,7 +450,7 @@ def obj_represent(object, fieldtype, db):
|
|||||||
(h, mi, s) = time_items + [0]
|
(h, mi, s) = time_items + [0]
|
||||||
object = datetime.time(h, mi, s)
|
object = datetime.time(h, mi, s)
|
||||||
elif fieldtype == 'datetime' and not isinstance(object,
|
elif fieldtype == 'datetime' and not isinstance(object,
|
||||||
datetime.datetime):
|
datetime.datetime):
|
||||||
(y, m, d) = [int(x) for x in
|
(y, m, d) = [int(x) for x in
|
||||||
str(object)[:10].strip().split('-')]
|
str(object)[:10].strip().split('-')]
|
||||||
time_items = [int(x) for x in
|
time_items = [int(x) for x in
|
||||||
@@ -466,7 +466,7 @@ def obj_represent(object, fieldtype, db):
|
|||||||
h,
|
h,
|
||||||
mi,
|
mi,
|
||||||
s,
|
s,
|
||||||
)
|
)
|
||||||
elif fieldtype == 'integer' and not isinstance(object, long):
|
elif fieldtype == 'integer' and not isinstance(object, long):
|
||||||
object = long(object)
|
object = long(object)
|
||||||
|
|
||||||
@@ -496,10 +496,10 @@ class Query(object):
|
|||||||
left,
|
left,
|
||||||
op=None,
|
op=None,
|
||||||
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)
|
||||||
@@ -539,7 +539,7 @@ class Set(object):
|
|||||||
self.where = where
|
self.where = where
|
||||||
self._tables.insert(0, where.get_all)
|
self._tables.insert(0, where.get_all)
|
||||||
elif hasattr(where, 'get_one') and isinstance(where.get_one,
|
elif hasattr(where, 'get_one') and isinstance(where.get_one,
|
||||||
QueryException):
|
QueryException):
|
||||||
self.where = where.get_one
|
self.where = where.get_one
|
||||||
else:
|
else:
|
||||||
|
|
||||||
@@ -553,9 +553,8 @@ 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(
|
||||||
@@ -628,7 +627,7 @@ def update_record(
|
|||||||
s,
|
s,
|
||||||
id,
|
id,
|
||||||
a,
|
a,
|
||||||
):
|
):
|
||||||
item = s.get(id)
|
item = s.get(id)
|
||||||
for (key, value) in a.items():
|
for (key, value) in a.items():
|
||||||
t[key] = value
|
t[key] = value
|
||||||
@@ -650,7 +649,7 @@ class Rows(object):
|
|||||||
db,
|
db,
|
||||||
response,
|
response,
|
||||||
*colnames
|
*colnames
|
||||||
):
|
):
|
||||||
self._db = db
|
self._db = db
|
||||||
self.colnames = colnames
|
self.colnames = colnames
|
||||||
self.response = response
|
self.response = response
|
||||||
@@ -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,13 +691,13 @@ 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]]
|
||||||
if len(time_items) == 3:
|
if len(time_items) == 3:
|
||||||
@@ -706,8 +705,8 @@ 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('-')]
|
||||||
time_items = [int(x) for x in
|
time_items = [int(x) for x in
|
||||||
@@ -723,19 +722,19 @@ class Rows(object):
|
|||||||
h,
|
h,
|
||||||
mi,
|
mi,
|
||||||
s,
|
s,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
row[tablename][fieldname] = value
|
row[tablename][fieldname] = value
|
||||||
if fieldname == 'id':
|
if fieldname == 'id':
|
||||||
id = row[tablename].id
|
id = row[tablename].id
|
||||||
row[tablename].update_record = lambda t = row[tablename], \
|
row[tablename].update_record = lambda t = row[tablename], \
|
||||||
s = self._db[tablename], id = id, **a: update_record(t,
|
s = self._db[tablename], id = id, **a: update_record(t,
|
||||||
s, id, a)
|
s, id, a)
|
||||||
for (referee_table, referee_name) in \
|
for (referee_table, referee_name) in \
|
||||||
table._referenced_by:
|
table._referenced_by:
|
||||||
s = self._db[referee_table][referee_name]
|
s = self._db[referee_table][referee_name]
|
||||||
row[tablename][referee_table] = Set(self._db, s
|
row[tablename][referee_table] = Set(self._db, s
|
||||||
== id)
|
== id)
|
||||||
if len(row.keys()) == 1:
|
if len(row.keys()) == 1:
|
||||||
return row[row.keys()[0]]
|
return row[row.keys()[0]]
|
||||||
return row
|
return row
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Modified for inclusion into web2py by: Ross Peoples <ross.peoples@gmail.com>
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
from StringIO import StringIO # The pure-Python StringIO supports unicode.
|
from StringIO import StringIO # The pure-Python StringIO supports unicode.
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ __version__ = '0.1.4'
|
|||||||
|
|
||||||
def remove_comments(css):
|
def remove_comments(css):
|
||||||
"""Remove all CSS comment blocks."""
|
"""Remove all CSS comment blocks."""
|
||||||
|
|
||||||
iemac = False
|
iemac = False
|
||||||
preserve = False
|
preserve = False
|
||||||
comment_start = css.find("/*")
|
comment_start = css.find("/*")
|
||||||
@@ -28,7 +28,7 @@ def remove_comments(css):
|
|||||||
# Preserve comments that look like `/*!...*/`.
|
# Preserve comments that look like `/*!...*/`.
|
||||||
# Slicing is used to make sure we don"t get an IndexError.
|
# Slicing is used to make sure we don"t get an IndexError.
|
||||||
preserve = css[comment_start + 2:comment_start + 3] == "!"
|
preserve = css[comment_start + 2:comment_start + 3] == "!"
|
||||||
|
|
||||||
comment_end = css.find("*/", comment_start + 2)
|
comment_end = css.find("*/", comment_start + 2)
|
||||||
if comment_end < 0:
|
if comment_end < 0:
|
||||||
if not preserve:
|
if not preserve:
|
||||||
@@ -48,22 +48,22 @@ def remove_comments(css):
|
|||||||
else:
|
else:
|
||||||
comment_start = comment_end + 2
|
comment_start = comment_end + 2
|
||||||
comment_start = css.find("/*", comment_start)
|
comment_start = css.find("/*", comment_start)
|
||||||
|
|
||||||
return css
|
return css
|
||||||
|
|
||||||
|
|
||||||
def remove_unnecessary_whitespace(css):
|
def remove_unnecessary_whitespace(css):
|
||||||
"""Remove unnecessary whitespace characters."""
|
"""Remove unnecessary whitespace characters."""
|
||||||
|
|
||||||
def pseudoclasscolon(css):
|
def pseudoclasscolon(css):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Prevents 'p :link' from becoming 'p:link'.
|
Prevents 'p :link' from becoming 'p:link'.
|
||||||
|
|
||||||
Translates 'p :link' into 'p ___PSEUDOCLASSCOLON___link'; this is
|
Translates 'p :link' into 'p ___PSEUDOCLASSCOLON___link'; this is
|
||||||
translated back again later.
|
translated back again later.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
regex = re.compile(r"(^|\})(([^\{\:])+\:)+([^\{]*\{)")
|
regex = re.compile(r"(^|\})(([^\{\:])+\:)+([^\{]*\{)")
|
||||||
match = regex.search(css)
|
match = regex.search(css)
|
||||||
while match:
|
while match:
|
||||||
@@ -73,43 +73,43 @@ def remove_unnecessary_whitespace(css):
|
|||||||
css[match.end():]])
|
css[match.end():]])
|
||||||
match = regex.search(css)
|
match = regex.search(css)
|
||||||
return css
|
return css
|
||||||
|
|
||||||
css = pseudoclasscolon(css)
|
css = pseudoclasscolon(css)
|
||||||
# Remove spaces from before things.
|
# Remove spaces from before things.
|
||||||
css = re.sub(r"\s+([!{};:>+\(\)\],])", r"\1", css)
|
css = re.sub(r"\s+([!{};:>+\(\)\],])", r"\1", css)
|
||||||
|
|
||||||
# If there is a `@charset`, then only allow one, and move to the beginning.
|
# If there is a `@charset`, then only allow one, and move to the beginning.
|
||||||
css = re.sub(r"^(.*)(@charset \"[^\"]*\";)", r"\2\1", css)
|
css = re.sub(r"^(.*)(@charset \"[^\"]*\";)", r"\2\1", css)
|
||||||
css = re.sub(r"^(\s*@charset [^;]+;\s*)+", r"\1", css)
|
css = re.sub(r"^(\s*@charset [^;]+;\s*)+", r"\1", css)
|
||||||
|
|
||||||
# Put the space back in for a few cases, such as `@media screen` and
|
# Put the space back in for a few cases, such as `@media screen` and
|
||||||
# `(-webkit-min-device-pixel-ratio:0)`.
|
# `(-webkit-min-device-pixel-ratio:0)`.
|
||||||
css = re.sub(r"\band\(", "and (", css)
|
css = re.sub(r"\band\(", "and (", css)
|
||||||
|
|
||||||
# Put the colons back.
|
# Put the colons back.
|
||||||
css = css.replace('___PSEUDOCLASSCOLON___', ':')
|
css = css.replace('___PSEUDOCLASSCOLON___', ':')
|
||||||
|
|
||||||
# Remove spaces from after things.
|
# Remove spaces from after things.
|
||||||
css = re.sub(r"([!{}:;>+\(\[,])\s+", r"\1", css)
|
css = re.sub(r"([!{}:;>+\(\[,])\s+", r"\1", css)
|
||||||
|
|
||||||
return css
|
return css
|
||||||
|
|
||||||
|
|
||||||
def remove_unnecessary_semicolons(css):
|
def remove_unnecessary_semicolons(css):
|
||||||
"""Remove unnecessary semicolons."""
|
"""Remove unnecessary semicolons."""
|
||||||
|
|
||||||
return re.sub(r";+\}", "}", css)
|
return re.sub(r";+\}", "}", css)
|
||||||
|
|
||||||
|
|
||||||
def remove_empty_rules(css):
|
def remove_empty_rules(css):
|
||||||
"""Remove empty rules."""
|
"""Remove empty rules."""
|
||||||
|
|
||||||
return re.sub(r"[^\}\{]+\{\}", "", css)
|
return re.sub(r"[^\}\{]+\{\}", "", css)
|
||||||
|
|
||||||
|
|
||||||
def normalize_rgb_colors_to_hex(css):
|
def normalize_rgb_colors_to_hex(css):
|
||||||
"""Convert `rgb(51,102,153)` to `#336699`."""
|
"""Convert `rgb(51,102,153)` to `#336699`."""
|
||||||
|
|
||||||
regex = re.compile(r"rgb\s*\(\s*([0-9,\s]+)\s*\)")
|
regex = re.compile(r"rgb\s*\(\s*([0-9,\s]+)\s*\)")
|
||||||
match = regex.search(css)
|
match = regex.search(css)
|
||||||
while match:
|
while match:
|
||||||
@@ -122,39 +122,40 @@ def normalize_rgb_colors_to_hex(css):
|
|||||||
|
|
||||||
def condense_zero_units(css):
|
def condense_zero_units(css):
|
||||||
"""Replace `0(px, em, %, etc)` with `0`."""
|
"""Replace `0(px, em, %, etc)` with `0`."""
|
||||||
|
|
||||||
return re.sub(r"([\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", r"\1\2", css)
|
return re.sub(r"([\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", r"\1\2", css)
|
||||||
|
|
||||||
|
|
||||||
def condense_multidimensional_zeros(css):
|
def condense_multidimensional_zeros(css):
|
||||||
"""Replace `:0 0 0 0;`, `:0 0 0;` etc. with `:0;`."""
|
"""Replace `:0 0 0 0;`, `:0 0 0;` etc. with `:0;`."""
|
||||||
|
|
||||||
css = css.replace(":0 0 0 0;", ":0;")
|
css = css.replace(":0 0 0 0;", ":0;")
|
||||||
css = css.replace(":0 0 0;", ":0;")
|
css = css.replace(":0 0 0;", ":0;")
|
||||||
css = css.replace(":0 0;", ":0;")
|
css = css.replace(":0 0;", ":0;")
|
||||||
|
|
||||||
# Revert `background-position:0;` to the valid `background-position:0 0;`.
|
# Revert `background-position:0;` to the valid `background-position:0 0;`.
|
||||||
css = css.replace("background-position:0;", "background-position:0 0;")
|
css = css.replace("background-position:0;", "background-position:0 0;")
|
||||||
|
|
||||||
return css
|
return css
|
||||||
|
|
||||||
|
|
||||||
def condense_floating_points(css):
|
def condense_floating_points(css):
|
||||||
"""Replace `0.6` with `.6` where possible."""
|
"""Replace `0.6` with `.6` where possible."""
|
||||||
|
|
||||||
return re.sub(r"(:|\s)0+\.(\d+)", r"\1.\2", css)
|
return re.sub(r"(:|\s)0+\.(\d+)", r"\1.\2", css)
|
||||||
|
|
||||||
|
|
||||||
def condense_hex_colors(css):
|
def condense_hex_colors(css):
|
||||||
"""Shorten colors from #AABBCC to #ABC where possible."""
|
"""Shorten colors from #AABBCC to #ABC where possible."""
|
||||||
|
|
||||||
regex = re.compile(r"([^\"'=\s])(\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])")
|
regex = re.compile(r"([^\"'=\s])(\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])")
|
||||||
match = regex.search(css)
|
match = regex.search(css)
|
||||||
while match:
|
while match:
|
||||||
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())
|
||||||
@@ -163,19 +164,19 @@ def condense_hex_colors(css):
|
|||||||
|
|
||||||
def condense_whitespace(css):
|
def condense_whitespace(css):
|
||||||
"""Condense multiple adjacent whitespace characters into one."""
|
"""Condense multiple adjacent whitespace characters into one."""
|
||||||
|
|
||||||
return re.sub(r"\s+", " ", css)
|
return re.sub(r"\s+", " ", css)
|
||||||
|
|
||||||
|
|
||||||
def condense_semicolons(css):
|
def condense_semicolons(css):
|
||||||
"""Condense multiple adjacent semicolon characters into one."""
|
"""Condense multiple adjacent semicolon characters into one."""
|
||||||
|
|
||||||
return re.sub(r";;+", ";", css)
|
return re.sub(r";;+", ";", css)
|
||||||
|
|
||||||
|
|
||||||
def wrap_css_lines(css, line_length):
|
def wrap_css_lines(css, line_length):
|
||||||
"""Wrap the lines of the given CSS to an approximate length."""
|
"""Wrap the lines of the given CSS to an approximate length."""
|
||||||
|
|
||||||
lines = []
|
lines = []
|
||||||
line_start = 0
|
line_start = 0
|
||||||
for i, char in enumerate(css):
|
for i, char in enumerate(css):
|
||||||
@@ -183,7 +184,7 @@ def wrap_css_lines(css, line_length):
|
|||||||
if char == '}' and (i - line_start >= line_length):
|
if char == '}' and (i - line_start >= line_length):
|
||||||
lines.append(css[line_start:i + 1])
|
lines.append(css[line_start:i + 1])
|
||||||
line_start = i + 1
|
line_start = i + 1
|
||||||
|
|
||||||
if line_start < len(css):
|
if line_start < len(css):
|
||||||
lines.append(css[line_start:])
|
lines.append(css[line_start:])
|
||||||
return '\n'.join(lines)
|
return '\n'.join(lines)
|
||||||
@@ -212,19 +213,19 @@ def cssmin(css, wrap=None):
|
|||||||
def main():
|
def main():
|
||||||
import optparse
|
import optparse
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
p = optparse.OptionParser(
|
p = optparse.OptionParser(
|
||||||
prog="cssmin", version=__version__,
|
prog="cssmin", version=__version__,
|
||||||
usage="%prog [--wrap N]",
|
usage="%prog [--wrap N]",
|
||||||
description="""Reads raw CSS from stdin, and writes compressed CSS to stdout.""")
|
description="""Reads raw CSS from stdin, and writes compressed CSS to stdout.""")
|
||||||
|
|
||||||
p.add_option(
|
p.add_option(
|
||||||
'-w', '--wrap', type='int', default=None, metavar='N',
|
'-w', '--wrap', type='int', default=None, metavar='N',
|
||||||
help="Wrap output to approximately N chars per line.")
|
help="Wrap output to approximately N chars per line.")
|
||||||
|
|
||||||
options, args = p.parse_args()
|
options, args = p.parse_args()
|
||||||
sys.stdout.write(cssmin(sys.stdin.read(), wrap=options.wrap))
|
sys.stdout.write(cssmin(sys.stdin.read(), wrap=options.wrap))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
@@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
def minify(response):
|
def minify(response):
|
||||||
def _replace(match):
|
def _replace(match):
|
||||||
match = match.group()
|
match = match.group()
|
||||||
# save whole <pre>, <textarea> tags, and opening <!-- (so it doesn't break <script>)
|
# save whole <pre>, <textarea> tags, and opening <!-- (so it doesn't break <script>)
|
||||||
# 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(
|
||||||
return cpat.sub(_replace, response)
|
r'\s+|<pre(.*?)</pre>|<textarea(.*?)</textarea>|<!--\s', re.DOTALL)
|
||||||
|
return cpat.sub(_replace, response)
|
||||||
@@ -49,7 +49,7 @@ Both python 2 and python 3 are supported.
|
|||||||
|
|
||||||
.. _jsmin.c by Douglas Crockford:
|
.. _jsmin.c by Douglas Crockford:
|
||||||
http://www.crockford.com/javascript/jsmin.c
|
http://www.crockford.com/javascript/jsmin.c
|
||||||
|
|
||||||
Original author of Python version: Andr\xe9 Malo
|
Original author of Python version: Andr\xe9 Malo
|
||||||
Home page: http://opensource.perlig.de/rjsmin/
|
Home page: http://opensource.perlig.de/rjsmin/
|
||||||
Modified by Ross Peoples <ross.peoples@gmail.com> for inclusion into web2py.
|
Modified by Ross Peoples <ross.peoples@gmail.com> for inclusion into web2py.
|
||||||
@@ -96,7 +96,7 @@ def _make_jsmin(extended=True, python_only=True):
|
|||||||
try:
|
try:
|
||||||
xrange
|
xrange
|
||||||
except NameError:
|
except NameError:
|
||||||
xrange = range # pylint: disable = W0622
|
xrange = range # pylint: disable = W0622
|
||||||
|
|
||||||
space_chars = r'[\000-\011\013\014\016-\040]'
|
space_chars = r'[\000-\011\013\014\016-\040]'
|
||||||
|
|
||||||
@@ -151,13 +151,13 @@ def _make_jsmin(extended=True, python_only=True):
|
|||||||
last != first and chr(last) or ''
|
last != first and chr(last) or ''
|
||||||
) for first, last in result])
|
) for first, last in result])
|
||||||
|
|
||||||
return _re.sub(r'([\000-\040\047])', # for better portability
|
return _re.sub(r'([\000-\040\047])', # for better portability
|
||||||
lambda m: '\\%03o' % ord(m.group(1)), (sequentize(result)
|
lambda m: '\\%03o' % ord(m.group(1)), (sequentize(result)
|
||||||
.replace('\\', '\\\\')
|
.replace('\\', '\\\\')
|
||||||
.replace('[', '\\[')
|
.replace('[', '\\[')
|
||||||
.replace(']', '\\]')
|
.replace(']', '\\]')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def id_literal_(what):
|
def id_literal_(what):
|
||||||
""" Make id_literal like char class """
|
""" Make id_literal like char class """
|
||||||
@@ -190,25 +190,33 @@ def _make_jsmin(extended=True, python_only=True):
|
|||||||
r'|(?:(?<=%(preregex1)s)%(space)s*(%(regex)s[^\047"/\000-\040]*))'
|
r'|(?:(?<=%(preregex1)s)%(space)s*(%(regex)s[^\047"/\000-\040]*))'
|
||||||
r'|(?:(?<=%(preregex2)s)%(space)s*(%(regex)s[^\047"/\000-\040]*))'
|
r'|(?:(?<=%(preregex2)s)%(space)s*(%(regex)s[^\047"/\000-\040]*))'
|
||||||
r'|(?<=%(id_literal_close)s)'
|
r'|(?<=%(id_literal_close)s)'
|
||||||
r'%(space)s*(?:(%(newline)s)%(space)s*)+'
|
r'%(space)s*(?:(%(newline)s)%(space)s*)+'
|
||||||
r'(?=%(id_literal_open)s)'
|
r'(?=%(id_literal_open)s)'
|
||||||
r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)'
|
r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)'
|
||||||
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"""
|
||||||
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
|
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
|
||||||
|
|
||||||
@@ -239,20 +247,26 @@ def _make_jsmin(extended=True, python_only=True):
|
|||||||
r'|(%(space)s)+'
|
r'|(%(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()
|
||||||
@@ -295,10 +310,11 @@ def _make_jsmin(extended=True, python_only=True):
|
|||||||
:Rtype: ``str``
|
:Rtype: ``str``
|
||||||
"""
|
"""
|
||||||
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
|
||||||
|
|
||||||
jsmin = _make_jsmin()
|
jsmin = _make_jsmin()
|
||||||
@@ -311,6 +327,7 @@ jsmin = _make_jsmin()
|
|||||||
# jsmin.jsmin(script)
|
# jsmin.jsmin(script)
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
def jsmin_for_posers(script):
|
def jsmin_for_posers(script):
|
||||||
r"""
|
r"""
|
||||||
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
|
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
|
||||||
|
|||||||
@@ -14,24 +14,27 @@ 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):
|
|
||||||
f =open(filename,'wb')
|
def write_binary_file(filename, data):
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
def minify(files, path_info, folder, optimize_css, optimize_js,
|
def minify(files, path_info, folder, optimize_css, optimize_js,
|
||||||
ignore_concat = [],
|
ignore_concat=[],
|
||||||
ignore_minify = ['/jquery.js', '/anytime.js']):
|
ignore_minify=['/jquery.js', '/anytime.js']):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Input:
|
Input:
|
||||||
@@ -45,7 +48,7 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
|||||||
Returns a new list of:
|
Returns a new list of:
|
||||||
- filename (absolute or relative, css or js, actual or temporary) or
|
- filename (absolute or relative, css or js, actual or temporary) or
|
||||||
- ('css:inline','...css..')
|
- ('css:inline','...css..')
|
||||||
- ('js:inline','...js..')
|
- ('js:inline','...js..')
|
||||||
"""
|
"""
|
||||||
optimize_css = optimize_css or ''
|
optimize_css = optimize_css or ''
|
||||||
optimize_js = optimize_js or ''
|
optimize_js = optimize_js or ''
|
||||||
@@ -55,27 +58,27 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
|||||||
concat_js = 'concat' in optimize_js
|
concat_js = 'concat' in optimize_js
|
||||||
minify_js = 'minify' in optimize_js
|
minify_js = 'minify' in optimize_js
|
||||||
inline_js = 'inline' in optimize_js
|
inline_js = 'inline' in optimize_js
|
||||||
static_path,temp = path_info.rsplit('/',1)
|
static_path, temp = path_info.rsplit('/', 1)
|
||||||
new_files = []
|
new_files = []
|
||||||
css = []
|
css = []
|
||||||
js = []
|
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
|
||||||
|
|
||||||
abs_filename = os.path.join(
|
abs_filename = os.path.join(
|
||||||
folder,'static', filename[len(static_path)+1:])
|
folder, 'static', filename[len(static_path) + 1:])
|
||||||
|
|
||||||
if filename.lower().endswith('.css'):
|
if filename.lower().endswith('.css'):
|
||||||
processed.append(filename)
|
processed.append(filename)
|
||||||
spath_info, sfilename = \
|
spath_info, sfilename = \
|
||||||
path_info.split('/'), filename.split('/')
|
path_info.split('/'), filename.split('/')
|
||||||
u = 0
|
u = 0
|
||||||
for i,a in enumerate(sfilename):
|
for i, a in enumerate(sfilename):
|
||||||
try:
|
try:
|
||||||
if a != spath_info[i]:
|
if a != spath_info[i]:
|
||||||
u = i
|
u = i
|
||||||
@@ -99,8 +102,8 @@ 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:
|
||||||
js.append(contents)
|
js.append(contents)
|
||||||
@@ -110,32 +113,31 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
|||||||
if css and concat_css:
|
if css and concat_css:
|
||||||
css = '\n\n'.join(contents for contents in css)
|
css = '\n\n'.join(contents for contents in css)
|
||||||
if not inline_css:
|
if not inline_css:
|
||||||
temppath = os.path.join(folder,'static',temp)
|
temppath = os.path.join(folder, 'static', temp)
|
||||||
if not os.path.exists(temppath):
|
if not os.path.exists(temppath):
|
||||||
os.mkdir(temppath)
|
os.mkdir(temppath)
|
||||||
dest = "compressed_%s.css" % dest_key
|
dest = "compressed_%s.css" % dest_key
|
||||||
tempfile = os.path.join(temppath, dest)
|
tempfile = os.path.join(temppath, dest)
|
||||||
write_binary_file(tempfile,css)
|
write_binary_file(tempfile, css)
|
||||||
css = path_info+'/%s' % dest
|
css = path_info + '/%s' % dest
|
||||||
new_files.append(css)
|
new_files.append(css)
|
||||||
else:
|
else:
|
||||||
new_files.append(('css:inline',css))
|
new_files.append(('css:inline', css))
|
||||||
else:
|
else:
|
||||||
new_files += css
|
new_files += css
|
||||||
if js and concat_js:
|
if js and concat_js:
|
||||||
js = '\n'.join(contents for contents in js)
|
js = '\n'.join(contents for contents in js)
|
||||||
if inline_js:
|
if inline_js:
|
||||||
js = ('js:inline',js)
|
js = ('js:inline', js)
|
||||||
else:
|
else:
|
||||||
temppath = os.path.join(folder,'static',temp)
|
temppath = os.path.join(folder, 'static', temp)
|
||||||
if not os.path.exists(temppath):
|
if not os.path.exists(temppath):
|
||||||
os.mkdir(temppath)
|
os.mkdir(temppath)
|
||||||
dest = "compressed_%s.js" % dest_key
|
dest = "compressed_%s.js" % dest_key
|
||||||
tempfile = os.path.join(folder,'static',temp,dest)
|
tempfile = os.path.join(folder, 'static', temp, dest)
|
||||||
write_binary_file(tempfile,js)
|
write_binary_file(tempfile, js)
|
||||||
js = path_info+'/%s' % dest
|
js = path_info + '/%s' % dest
|
||||||
new_files.append(js)
|
new_files.append(js)
|
||||||
else:
|
else:
|
||||||
new_files += js
|
new_files += js
|
||||||
return new_files
|
return new_files
|
||||||
|
|
||||||
+20
-15
@@ -24,7 +24,7 @@ CALLOC.argtypes = [c_uint, c_uint]
|
|||||||
|
|
||||||
STRDUP = LIBC.strdup
|
STRDUP = LIBC.strdup
|
||||||
STRDUP.argstypes = [c_char_p]
|
STRDUP.argstypes = [c_char_p]
|
||||||
STRDUP.restype = POINTER(c_char) # NOT c_char_p !!!!
|
STRDUP.restype = POINTER(c_char) # NOT c_char_p !!!!
|
||||||
|
|
||||||
# Various constants
|
# Various constants
|
||||||
PAM_PROMPT_ECHO_OFF = 1
|
PAM_PROMPT_ECHO_OFF = 1
|
||||||
@@ -32,56 +32,61 @@ 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_ = [
|
||||||
("handle", c_void_p)
|
("handle", c_void_p)
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
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_ = [
|
||||||
("msg_style", c_int),
|
("msg_style", c_int),
|
||||||
("msg", c_char_p),
|
("msg", c_char_p),
|
||||||
]
|
]
|
||||||
|
|
||||||
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_ = [
|
||||||
("resp", c_char_p),
|
("resp", c_char_p),
|
||||||
("resp_retcode", c_int),
|
("resp_retcode", c_int),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<PamResponse %i '%s'>" % (self.resp_retcode, self.resp)
|
return "<PamResponse %i '%s'>" % (self.resp_retcode, self.resp)
|
||||||
|
|
||||||
CONV_FUNC = CFUNCTYPE(c_int,
|
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_ = [
|
||||||
("conv", CONV_FUNC),
|
("conv", CONV_FUNC),
|
||||||
("appdata_ptr", c_void_p)
|
("appdata_ptr", c_void_p)
|
||||||
]
|
]
|
||||||
|
|
||||||
PAM_START = LIBPAM.pam_start
|
PAM_START = LIBPAM.pam_start
|
||||||
PAM_START.restype = c_int
|
PAM_START.restype = c_int
|
||||||
PAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv),
|
PAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv),
|
||||||
POINTER(PamHandle)]
|
POINTER(PamHandle)]
|
||||||
|
|
||||||
PAM_AUTHENTICATE = LIBPAM.pam_authenticate
|
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
|
||||||
|
|||||||
+28
-21
@@ -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"
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ class Qdb(bdb.Bdb):
|
|||||||
self.frame = None
|
self.frame = None
|
||||||
self.i = 1 # sequential RPC call id
|
self.i = 1 # sequential RPC call id
|
||||||
self.waiting = False
|
self.waiting = False
|
||||||
self.pipe = pipe # for communication
|
self.pipe = pipe # for communication
|
||||||
self._wait_for_mainpyfile = False
|
self._wait_for_mainpyfile = False
|
||||||
self._wait_for_breakpoint = False
|
self._wait_for_breakpoint = False
|
||||||
self.mainpyfile = ""
|
self.mainpyfile = ""
|
||||||
@@ -67,7 +68,7 @@ class Qdb(bdb.Bdb):
|
|||||||
# dispatch message (JSON RPC like)
|
# dispatch message (JSON RPC like)
|
||||||
method = getattr(self, request['method'])
|
method = getattr(self, request['method'])
|
||||||
response['result'] = method.__call__(*request['args'],
|
response['result'] = method.__call__(*request['args'],
|
||||||
**request.get('kwargs', {}))
|
**request.get('kwargs', {}))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
response['error'] = {'code': 0, 'message': str(e)}
|
response['error'] = {'code': 0, 'message': str(e)}
|
||||||
# send the result for normal method calls, not for notifications
|
# send the result for normal method calls, not for notifications
|
||||||
@@ -83,7 +84,7 @@ class Qdb(bdb.Bdb):
|
|||||||
self.pull_actions()
|
self.pull_actions()
|
||||||
# process the frame (see Bdb.trace_dispatch)
|
# process the frame (see Bdb.trace_dispatch)
|
||||||
if self.quitting:
|
if self.quitting:
|
||||||
return # None
|
return # None
|
||||||
if event == 'line':
|
if event == 'line':
|
||||||
return self.dispatch_line(frame)
|
return self.dispatch_line(frame)
|
||||||
if event == 'call':
|
if event == 'call':
|
||||||
@@ -106,7 +107,7 @@ class Qdb(bdb.Bdb):
|
|||||||
"""This function is called when we stop or break at this line."""
|
"""This function is called when we stop or break at this line."""
|
||||||
if self._wait_for_mainpyfile:
|
if self._wait_for_mainpyfile:
|
||||||
if (not self.canonic(frame.f_code.co_filename).startswith(self.mainpyfile)
|
if (not self.canonic(frame.f_code.co_filename).startswith(self.mainpyfile)
|
||||||
or frame.f_lineno<= 0):
|
or frame.f_lineno <= 0):
|
||||||
return
|
return
|
||||||
self._wait_for_mainpyfile = 0
|
self._wait_for_mainpyfile = 0
|
||||||
if self._wait_for_breakpoint:
|
if self._wait_for_breakpoint:
|
||||||
@@ -150,11 +151,11 @@ class Qdb(bdb.Bdb):
|
|||||||
import __main__
|
import __main__
|
||||||
import imp
|
import imp
|
||||||
__main__.__dict__.clear()
|
__main__.__dict__.clear()
|
||||||
__main__.__dict__.update({"__name__" : "__main__",
|
__main__.__dict__.update({"__name__": "__main__",
|
||||||
"__file__" : filename,
|
"__file__": filename,
|
||||||
"__builtins__": __builtins__,
|
"__builtins__": __builtins__,
|
||||||
"imp" : imp, # need for run
|
"imp": imp, # need for run
|
||||||
})
|
})
|
||||||
|
|
||||||
# avoid stopping before we reach the main script
|
# avoid stopping before we reach the main script
|
||||||
self._wait_for_mainpyfile = 1
|
self._wait_for_mainpyfile = 1
|
||||||
@@ -200,8 +201,8 @@ class Qdb(bdb.Bdb):
|
|||||||
if self.params.get('environment'):
|
if self.params.get('environment'):
|
||||||
kwargs['environment'] = self.do_environment()
|
kwargs['environment'] = self.do_environment()
|
||||||
self.pipe.send({'method': 'interaction', 'id': None,
|
self.pipe.send({'method': 'interaction', 'id': None,
|
||||||
'args': (filename, self.frame.f_lineno, line),
|
'args': (filename, self.frame.f_lineno, line),
|
||||||
'kwargs': kwargs})
|
'kwargs': kwargs})
|
||||||
|
|
||||||
self.pull_actions()
|
self.pull_actions()
|
||||||
|
|
||||||
@@ -278,7 +279,7 @@ class Qdb(bdb.Bdb):
|
|||||||
filename = self.frame.f_code.co_filename
|
filename = self.frame.f_code.co_filename
|
||||||
breaklist = self.get_file_breaks(filename)
|
breaklist = self.get_file_breaks(filename)
|
||||||
lines = []
|
lines = []
|
||||||
for lineno in range(first, last+1):
|
for lineno in range(first, last + 1):
|
||||||
line = linecache.getline(filename, lineno,
|
line = linecache.getline(filename, lineno,
|
||||||
self.frame.f_globals)
|
self.frame.f_globals)
|
||||||
if not line:
|
if not line:
|
||||||
@@ -303,7 +304,7 @@ class Qdb(bdb.Bdb):
|
|||||||
for bp in bdb.Breakpoint.bpbynumber:
|
for bp in bdb.Breakpoint.bpbynumber:
|
||||||
if bp:
|
if bp:
|
||||||
breaks.append((bp.number, bp.file, bp.line,
|
breaks.append((bp.number, bp.file, bp.line,
|
||||||
bp.temporary, bp.enabled, bp.hits, bp.cond, ))
|
bp.temporary, bp.enabled, bp.hits, bp.cond, ))
|
||||||
return breaks
|
return breaks
|
||||||
|
|
||||||
def do_clear_breakpoint(self, filename, lineno):
|
def do_clear_breakpoint(self, filename, lineno):
|
||||||
@@ -320,7 +321,7 @@ class Qdb(bdb.Bdb):
|
|||||||
|
|
||||||
def do_eval(self, arg, safe=True):
|
def do_eval(self, arg, safe=True):
|
||||||
ret = eval(arg, self.frame.f_globals,
|
ret = eval(arg, self.frame.f_globals,
|
||||||
self.frame_locals)
|
self.frame_locals)
|
||||||
if safe:
|
if safe:
|
||||||
ret = pydoc.cram(repr(ret), 255)
|
ret = pydoc.cram(repr(ret), 255)
|
||||||
return ret
|
return ret
|
||||||
@@ -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):
|
||||||
@@ -589,8 +592,8 @@ class Frontend(object):
|
|||||||
result = self.readline()
|
result = self.readline()
|
||||||
if result:
|
if result:
|
||||||
response = {'version': '1.1', 'id': request.get('id'),
|
response = {'version': '1.1', 'id': request.get('id'),
|
||||||
'result': result,
|
'result': result,
|
||||||
'error': None}
|
'error': None}
|
||||||
self.send(response)
|
self.send(response)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -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'])
|
||||||
@@ -736,7 +740,7 @@ class Cli(Frontend, cmd.Cmd):
|
|||||||
return raw_input()
|
return raw_input()
|
||||||
|
|
||||||
def postcmd(self, stop, line):
|
def postcmd(self, stop, line):
|
||||||
return not line.startswith("h") # stop
|
return not line.startswith("h") # stop
|
||||||
|
|
||||||
do_h = cmd.Cmd.do_help
|
do_h = cmd.Cmd.do_help
|
||||||
|
|
||||||
@@ -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))
|
||||||
@@ -877,7 +882,7 @@ def main(host='localhost', port=6000, authkey='secret password'):
|
|||||||
print "usage: pdb.py scriptfile [arg] ..."
|
print "usage: pdb.py scriptfile [arg] ..."
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
mainpyfile = sys.argv[1] # Get script filename
|
mainpyfile = sys.argv[1] # Get script filename
|
||||||
if not os.path.exists(mainpyfile):
|
if not os.path.exists(mainpyfile):
|
||||||
print 'Error:', mainpyfile, 'does not exist'
|
print 'Error:', mainpyfile, 'does not exist'
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -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
|
||||||
@@ -950,7 +957,7 @@ if __name__ == '__main__':
|
|||||||
# Check environment for configuration parameters:
|
# Check environment for configuration parameters:
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
for param in 'host', 'port', 'authkey':
|
for param in 'host', 'port', 'authkey':
|
||||||
if 'QDB_%s' % param.upper() in os.environ:
|
if 'QDB_%s' % param.upper() in os.environ:
|
||||||
kwargs[param] = os.environ['QDB_%s' % param.upper()]
|
kwargs[param] = os.environ['QDB_%s' % param.upper()]
|
||||||
|
|
||||||
if not sys.argv[1:]:
|
if not sys.argv[1:]:
|
||||||
|
|||||||
@@ -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,10 +48,11 @@ 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
|
||||||
host,port = (self.server.split(':')+['6379'])[:2]
|
host, port = (self.server.split(':') + ['6379'])[:2]
|
||||||
port = int(port)
|
port = int(port)
|
||||||
self.request = current.request
|
self.request = current.request
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
@@ -64,7 +66,7 @@ class RedisClient(object):
|
|||||||
CacheAbstract.cache_stats_name: {
|
CacheAbstract.cache_stats_name: {
|
||||||
'hit_total': 0,
|
'hit_total': 0,
|
||||||
'misses': 0,
|
'misses': 0,
|
||||||
}}
|
}}
|
||||||
else:
|
else:
|
||||||
self.storage = self.meta_storage[app]
|
self.storage = self.meta_storage[app]
|
||||||
|
|
||||||
@@ -72,8 +74,8 @@ 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
|
||||||
obj = self.r_server.get(newKey)
|
obj = self.r_server.get(newKey)
|
||||||
@@ -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,8 +144,8 @@ 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)
|
||||||
pipe.execute()
|
pipe.execute()
|
||||||
@@ -149,10 +154,10 @@ class RedisClient(object):
|
|||||||
statscollector = self.r_server.info()
|
statscollector = self.r_server.info()
|
||||||
if self.debug:
|
if self.debug:
|
||||||
statscollector['w2p_stats'] = dict(
|
statscollector['w2p_stats'] = dict(
|
||||||
hit_total = self.r_server.get(
|
hit_total=self.r_server.get(
|
||||||
'web2py_cache_statistics:hit_total'),
|
'web2py_cache_statistics:hit_total'),
|
||||||
misses=self.r_server.get('web2py_cache_statistics:misses')
|
misses=self.r_server.get('web2py_cache_statistics:misses')
|
||||||
)
|
)
|
||||||
statscollector['w2p_keys'] = dict()
|
statscollector['w2p_keys'] = dict()
|
||||||
for a in self.r_server.keys("w2p:%s:*" % (
|
for a in self.r_server.keys("w2p:%s:*" % (
|
||||||
self.request.application)):
|
self.request.application)):
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -48,7 +49,7 @@ class RedisClient(object):
|
|||||||
"""
|
"""
|
||||||
self.server = server
|
self.server = server
|
||||||
self.db = db or 0
|
self.db = db or 0
|
||||||
host,port = (self.server.split(':')+['6379'])[:2]
|
host, port = (self.server.split(':') + ['6379'])[:2]
|
||||||
port = int(port)
|
port = int(port)
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
if current and current.request:
|
if current and current.request:
|
||||||
@@ -63,12 +64,13 @@ class RedisClient(object):
|
|||||||
return self.tablename
|
return self.tablename
|
||||||
|
|
||||||
def Field(self, fieldname, type='string', length=None, default=None,
|
def Field(self, fieldname, type='string', length=None, default=None,
|
||||||
required=False,requires=None):
|
required=False, requires=None):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
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"""
|
||||||
|
|
||||||
|
|||||||
+10
-7
@@ -54,14 +54,14 @@ UNPICKLABLE_TYPES = (
|
|||||||
types.TypeType,
|
types.TypeType,
|
||||||
types.ClassType,
|
types.ClassType,
|
||||||
types.FunctionType,
|
types.FunctionType,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Unpicklable statements to seed new historys with.
|
# Unpicklable statements to seed new historys with.
|
||||||
INITIAL_UNPICKLABLES = [
|
INITIAL_UNPICKLABLES = [
|
||||||
'import logging',
|
'import logging',
|
||||||
'import os',
|
'import os',
|
||||||
'import sys',
|
'import sys',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class History:
|
class History:
|
||||||
@@ -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:
|
||||||
@@ -261,6 +263,7 @@ def run(history, statement, env={}):
|
|||||||
sys.modules['__main__'] = old_main
|
sys.modules['__main__'] = old_main
|
||||||
return output.getvalue()
|
return output.getvalue()
|
||||||
|
|
||||||
if __name__=='__main__':
|
if __name__ == '__main__':
|
||||||
history=History()
|
history = History()
|
||||||
while True: print run(history, raw_input('>>> ')).rstrip()
|
while True:
|
||||||
|
print run(history, raw_input('>>> ')).rstrip()
|
||||||
@@ -46,8 +46,10 @@ class JSONDummyParser:
|
|||||||
"json wrapper for xmlrpclib parser interfase"
|
"json wrapper for xmlrpclib parser interfase"
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.buf = StringIO()
|
self.buf = StringIO()
|
||||||
|
|
||||||
def feed(self, data):
|
def feed(self, data):
|
||||||
self.buf.write(data)
|
self.buf.write(data)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
return self.buf.getvalue()
|
return self.buf.getvalue()
|
||||||
|
|
||||||
@@ -72,6 +74,7 @@ class JSONTransportMixin:
|
|||||||
class JSONTransport(JSONTransportMixin, Transport):
|
class JSONTransport(JSONTransportMixin, Transport):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class JSONSafeTransport(JSONTransportMixin, SafeTransport):
|
class JSONSafeTransport(JSONTransportMixin, SafeTransport):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -79,7 +82,7 @@ class JSONSafeTransport(JSONTransportMixin, SafeTransport):
|
|||||||
class ServerProxy(object):
|
class ServerProxy(object):
|
||||||
"JSON RPC Simple Client Service Proxy"
|
"JSON RPC Simple Client Service Proxy"
|
||||||
|
|
||||||
def __init__(self, uri, transport=None, encoding=None, verbose=0):
|
def __init__(self, uri, transport=None, encoding=None, verbose=0):
|
||||||
self.location = uri # server location (url)
|
self.location = uri # server location (url)
|
||||||
self.trace = verbose # show debug messages
|
self.trace = verbose # show debug messages
|
||||||
self.exceptions = True # raise errors? (JSONRPCError)
|
self.exceptions = True # raise errors? (JSONRPCError)
|
||||||
@@ -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:
|
||||||
@@ -118,7 +121,7 @@ class ServerProxy(object):
|
|||||||
self.__handler,
|
self.__handler,
|
||||||
request,
|
request,
|
||||||
verbose=self.__verbose
|
verbose=self.__verbose
|
||||||
)
|
)
|
||||||
|
|
||||||
# store plain request and response for further debugging
|
# store plain request and response for further debugging
|
||||||
self.json_request = request
|
self.json_request = request
|
||||||
|
|||||||
+108
-105
@@ -1,112 +1,115 @@
|
|||||||
SMSCODES = {
|
SMSCODES = {
|
||||||
'Aliant':'@chat.wirefree.ca',
|
'Aliant': '@chat.wirefree.ca',
|
||||||
'Alltel':'@message.alltel.com',
|
'Alltel': '@message.alltel.com',
|
||||||
'Ameritech':'@paging.acswireless.com',
|
'Ameritech': '@paging.acswireless.com',
|
||||||
'AT&T':'@txt.att.net',
|
'AT&T': '@txt.att.net',
|
||||||
'AU by KDDI':'@ezweb.ne.jp',
|
'AU by KDDI': '@ezweb.ne.jp',
|
||||||
'BeeLine GSM':'@sms.beemail.ru',
|
'BeeLine GSM': '@sms.beemail.ru',
|
||||||
'Bell Mobility Canada':'@txt.bellmobility.ca',
|
'Bell Mobility Canada': '@txt.bellmobility.ca',
|
||||||
'Bellsouth':'@bellsouth.cl',
|
'Bellsouth': '@bellsouth.cl',
|
||||||
'BellSouth Mobility':'@blsdcs.net',
|
'BellSouth Mobility': '@blsdcs.net',
|
||||||
'Blue Sky Frog':'@blueskyfrog.com',
|
'Blue Sky Frog': '@blueskyfrog.com',
|
||||||
'Boost':'@myboostmobile.com',
|
'Boost': '@myboostmobile.com',
|
||||||
'Cellular South':'@csouth1.com',
|
'Cellular South': '@csouth1.com',
|
||||||
'CellularOne':'@mobile.celloneusa.com',
|
'CellularOne': '@mobile.celloneusa.com',
|
||||||
'CellularOne West':'@mycellone.com',
|
'CellularOne West': '@mycellone.com',
|
||||||
'Cincinnati Bell':'@gocbw.com',
|
'Cincinnati Bell': '@gocbw.com',
|
||||||
'Claro':'@clarotorpedo.com.br',
|
'Claro': '@clarotorpedo.com.br',
|
||||||
'Comviq':'@sms.comviq.se',
|
'Comviq': '@sms.comviq.se',
|
||||||
'Dutchtone/Orange-NL':'@sms.orange.nl',
|
'Dutchtone/Orange-NL': '@sms.orange.nl',
|
||||||
'Edge Wireless':'@sms.edgewireless.com',
|
'Edge Wireless': '@sms.edgewireless.com',
|
||||||
'EinsteinPCS / Airadigm Communications':'@einsteinsms.com',
|
'EinsteinPCS / Airadigm Communications': '@einsteinsms.com',
|
||||||
'EPlus':'@smsmail.eplus.de',
|
'EPlus': '@smsmail.eplus.de',
|
||||||
'Fido Canada':'@fido.ca',
|
'Fido Canada': '@fido.ca',
|
||||||
'Golden Telecom':'@sms.goldentele.com',
|
'Golden Telecom': '@sms.goldentele.com',
|
||||||
'Idea Cellular':'@ideacellular.net',
|
'Idea Cellular': '@ideacellular.net',
|
||||||
'Kyivstar':'@sms.kyivstar.net',
|
'Kyivstar': '@sms.kyivstar.net',
|
||||||
'LMT':'@sms.lmt.lv',
|
'LMT': '@sms.lmt.lv',
|
||||||
'Manitoba Telecom Systems':'@text.mtsmobility.com',
|
'Manitoba Telecom Systems': '@text.mtsmobility.com',
|
||||||
'Meteor':'@sms.mymeteor.ie',
|
'Meteor': '@sms.mymeteor.ie',
|
||||||
'Metro PCS':'@mymetropcs.com',
|
'Metro PCS': '@mymetropcs.com',
|
||||||
'Metrocall Pager':'@page.metrocall.com',
|
'Metrocall Pager': '@page.metrocall.com',
|
||||||
'MobileOne':'@m1.com.sg',
|
'MobileOne': '@m1.com.sg',
|
||||||
'Mobilfone':'@page.mobilfone.com',
|
'Mobilfone': '@page.mobilfone.com',
|
||||||
'Mobility Bermuda':'@ml.bm',
|
'Mobility Bermuda': '@ml.bm',
|
||||||
'Netcom':'@sms.netcom.no',
|
'Netcom': '@sms.netcom.no',
|
||||||
'Nextel':'@messaging.nextel.com',
|
'Nextel': '@messaging.nextel.com',
|
||||||
'NPI Wireless':'@npiwireless.com',
|
'NPI Wireless': '@npiwireless.com',
|
||||||
'O2':'@o2.co.uk',
|
'O2': '@o2.co.uk',
|
||||||
'O2 M-mail':'@mmail.co.uk',
|
'O2 M-mail': '@mmail.co.uk',
|
||||||
'Optus':'@optusmobile.com.au',
|
'Optus': '@optusmobile.com.au',
|
||||||
'Orange':'@orange.net',
|
'Orange': '@orange.net',
|
||||||
'Oskar':'@mujoskar.cz',
|
'Oskar': '@mujoskar.cz',
|
||||||
'Pagenet':'@pagenet.net',
|
'Pagenet': '@pagenet.net',
|
||||||
'PCS Rogers':'@pcs.rogers.com',
|
'PCS Rogers': '@pcs.rogers.com',
|
||||||
'Personal Communication':'@pcom.ru',
|
'Personal Communication': '@pcom.ru',
|
||||||
'Plus GSM Poland':'@text.plusgsm.pl',
|
'Plus GSM Poland': '@text.plusgsm.pl',
|
||||||
'Powertel':'@ptel.net',
|
'Powertel': '@ptel.net',
|
||||||
'Primtel':'@sms.primtel.ru',
|
'Primtel': '@sms.primtel.ru',
|
||||||
'PSC Wireless':'@sms.pscel.com',
|
'PSC Wireless': '@sms.pscel.com',
|
||||||
'Qualcomm':'@pager.qualcomm.com',
|
'Qualcomm': '@pager.qualcomm.com',
|
||||||
'Qwest':'@qwestmp.com',
|
'Qwest': '@qwestmp.com',
|
||||||
'Safaricom':'@safaricomsms.com',
|
'Safaricom': '@safaricomsms.com',
|
||||||
'Satelindo GSM':'@satelindogsm.com',
|
'Satelindo GSM': '@satelindogsm.com',
|
||||||
'SCS-900':'@scs-900.ru',
|
'SCS-900': '@scs-900.ru',
|
||||||
'Simple Freedom':'@text.simplefreedom.net',
|
'Simple Freedom': '@text.simplefreedom.net',
|
||||||
'Skytel - Alphanumeric':'@skytel.com',
|
'Skytel - Alphanumeric': '@skytel.com',
|
||||||
'Smart Telecom':'@mysmart.mymobile.ph',
|
'Smart Telecom': '@mysmart.mymobile.ph',
|
||||||
'Southern Linc':'@page.southernlinc.com',
|
'Southern Linc': '@page.southernlinc.com',
|
||||||
'Sprint PCS':'@messaging.sprintpcs.com',
|
'Sprint PCS': '@messaging.sprintpcs.com',
|
||||||
'Sprint PCS - Short Mail':'@sprintpcs.com',
|
'Sprint PCS - Short Mail': '@sprintpcs.com',
|
||||||
'SunCom':'@tms.suncom.com',
|
'SunCom': '@tms.suncom.com',
|
||||||
'SureWest Communications':'@mobile.surewest.com',
|
'SureWest Communications': '@mobile.surewest.com',
|
||||||
'SwissCom Mobile':'@bluewin.ch',
|
'SwissCom Mobile': '@bluewin.ch',
|
||||||
'T-Mobile Germany':'@T-D1-SMS.de',
|
'T-Mobile Germany': '@T-D1-SMS.de',
|
||||||
'T-Mobile Netherlands':'@gin.nl',
|
'T-Mobile Netherlands': '@gin.nl',
|
||||||
'T-Mobile UK':'@t-mobile.uk.net',
|
'T-Mobile UK': '@t-mobile.uk.net',
|
||||||
'T-Mobile USA (tmail)':'@tmail.com',
|
'T-Mobile USA (tmail)': '@tmail.com',
|
||||||
'T-Mobile USA (tmomail)':'@tmomail.net',
|
'T-Mobile USA (tmomail)': '@tmomail.net',
|
||||||
'Tele2 Latvia':'@sms.tele2.lv',
|
'Tele2 Latvia': '@sms.tele2.lv',
|
||||||
'Telefonica Movistar':'@movistar.net',
|
'Telefonica Movistar': '@movistar.net',
|
||||||
'Telenor':'@mobilpost.no',
|
'Telenor': '@mobilpost.no',
|
||||||
'Telia Denmark':'@gsm1800.telia.dk',
|
'Telia Denmark': '@gsm1800.telia.dk',
|
||||||
'Telus Mobility':'@msg.telus.com',
|
'Telus Mobility': '@msg.telus.com',
|
||||||
'The Phone House':'@sms.phonehouse.de',
|
'The Phone House': '@sms.phonehouse.de',
|
||||||
'TIM':'@timnet.com',
|
'TIM': '@timnet.com',
|
||||||
'UMC':'@sms.umc.com.ua',
|
'UMC': '@sms.umc.com.ua',
|
||||||
'Unicel':'@utext.com',
|
'Unicel': '@utext.com',
|
||||||
'US Cellular':'@email.uscc.net',
|
'US Cellular': '@email.uscc.net',
|
||||||
'Verizon Wireless (vtext)':'@vtext.com',
|
'Verizon Wireless (vtext)': '@vtext.com',
|
||||||
'Verizon Wireless (airtouchpaging)':'@airtouchpaging.com',
|
'Verizon Wireless (airtouchpaging)': '@airtouchpaging.com',
|
||||||
'Verizon Wireless (myairmail)':'@myairmail.com',
|
'Verizon Wireless (myairmail)': '@myairmail.com',
|
||||||
'Vessotel':'@pager.irkutsk.ru',
|
'Vessotel': '@pager.irkutsk.ru',
|
||||||
'Virgin Mobile Canada':'@vmobile.ca',
|
'Virgin Mobile Canada': '@vmobile.ca',
|
||||||
'Virgin Mobile USA':'@vmobl.com',
|
'Virgin Mobile USA': '@vmobl.com',
|
||||||
'Vodafone Italy':'@sms.vodafone.it',
|
'Vodafone Italy': '@sms.vodafone.it',
|
||||||
'Vodafone Japan (n)':'@n.vodafone.ne.jp',
|
'Vodafone Japan (n)': '@n.vodafone.ne.jp',
|
||||||
'Vodafone Japan (d)':'@d.vodafone.ne.jp',
|
'Vodafone Japan (d)': '@d.vodafone.ne.jp',
|
||||||
'Vodafone Japan (r)':'@r.vodafone.ne.jp',
|
'Vodafone Japan (r)': '@r.vodafone.ne.jp',
|
||||||
'Vodafone Japan (k)':'@k.vodafone.ne.jp',
|
'Vodafone Japan (k)': '@k.vodafone.ne.jp',
|
||||||
'Vodafone Japan (t)':'@t.vodafone.ne.jp',
|
'Vodafone Japan (t)': '@t.vodafone.ne.jp',
|
||||||
'Vodafone Japan (q)':'@q.vodafone.ne.jp',
|
'Vodafone Japan (q)': '@q.vodafone.ne.jp',
|
||||||
'Vodafone Japan (s)':'@s.vodafone.ne.jp',
|
'Vodafone Japan (s)': '@s.vodafone.ne.jp',
|
||||||
'Vodafone Japan (h)':'@h.vodafone.ne.jp',
|
'Vodafone Japan (h)': '@h.vodafone.ne.jp',
|
||||||
'Vodafone Japan (c)':'@c.vodafone.ne.jp',
|
'Vodafone Japan (c)': '@c.vodafone.ne.jp',
|
||||||
'Vodafone Spain':'@vodafone.es',
|
'Vodafone Spain': '@vodafone.es',
|
||||||
'Vodafone UK':'@vodafone.net',
|
'Vodafone UK': '@vodafone.net',
|
||||||
'Weblink Wireless':'@airmessage.net',
|
'Weblink Wireless': '@airmessage.net',
|
||||||
'WellCom':'@sms.welcome2well.com',
|
'WellCom': '@sms.welcome2well.com',
|
||||||
'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[:2]=='00': number=number[3:]
|
elif number[0] == '+':
|
||||||
number=re.sub('[^\d]','',number)
|
number = number[3:]
|
||||||
return number+SMSCODES[provider]
|
elif number[:2] == '00': number = number[3:]
|
||||||
|
number = re.sub('[^\d]', '', number)
|
||||||
|
return number + SMSCODES[provider]
|
||||||
@@ -9,13 +9,14 @@ 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("'", "\\'")
|
||||||
|
|
||||||
|
|
||||||
class Node:
|
class Node:
|
||||||
def __init__(self, name, value, url='.', readonly=False, active=True,
|
def __init__(self, name, value, url='.', readonly=False, active=True,
|
||||||
onchange=None, **kwarg):
|
onchange=None, **kwarg):
|
||||||
self.url = url
|
self.url = url
|
||||||
self.name = name
|
self.name = name
|
||||||
self.value = str(value)
|
self.value = str(value)
|
||||||
@@ -246,8 +247,8 @@ class Sheet:
|
|||||||
r, c = int(r), int(c)
|
r, c = int(r), int(c)
|
||||||
except (ValueError, IndexError, TypeError), e:
|
except (ValueError, IndexError, TypeError), e:
|
||||||
error = "%s. %s" % \
|
error = "%s. %s" % \
|
||||||
("Unexpected position parameter",
|
("Unexpected position parameter",
|
||||||
"Must be a key of type 'rncn'")
|
"Must be a key of type 'rncn'")
|
||||||
raise ValueError(error)
|
raise ValueError(error)
|
||||||
return r, c
|
return r, c
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -333,22 +333,22 @@ class Sheet:
|
|||||||
|
|
||||||
if request.args(0) == 'focus':
|
if request.args(0) == 'focus':
|
||||||
return "jQuery('#%(cell)s').val('%(value)s');" % \
|
return "jQuery('#%(cell)s').val('%(value)s');" % \
|
||||||
dict(cell=cell, value=quote(self[cell].value))
|
dict(cell=cell, value=quote(self[cell].value))
|
||||||
|
|
||||||
value = request.vars[cell]
|
value = request.vars[cell]
|
||||||
self[cell] = value
|
self[cell] = value
|
||||||
|
|
||||||
if request.args(0) == 'blur':
|
if request.args(0) == 'blur':
|
||||||
return "jQuery('#%(cell)s').val('%(value)s');" % \
|
return "jQuery('#%(cell)s').val('%(value)s');" % \
|
||||||
dict(cell=cell, value=quote(self[cell].computed_value))
|
dict(cell=cell, value=quote(self[cell].computed_value))
|
||||||
|
|
||||||
elif request.args(0) == 'keyup':
|
elif request.args(0) == 'keyup':
|
||||||
jquery = ''
|
jquery = ''
|
||||||
for other_key in self.modified:
|
for other_key in self.modified:
|
||||||
if other_key != cell:
|
if other_key != cell:
|
||||||
jquery += "jQuery('#%(other_key)s').val('%(value)s');" % \
|
jquery += "jQuery('#%(other_key)s').val('%(value)s');" % \
|
||||||
dict(other_key=other_key,
|
dict(other_key=other_key,
|
||||||
value=quote(self[other_key].computed_value))
|
value=quote(self[other_key].computed_value))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# spreadsheet db update
|
# spreadsheet db update
|
||||||
@@ -390,8 +390,8 @@ class Sheet:
|
|||||||
return "odd"
|
return "odd"
|
||||||
|
|
||||||
def __init__(self, rows, cols, url='.', readonly=False,
|
def __init__(self, rows, cols, url='.', readonly=False,
|
||||||
active=True, onchange=None, value=None, data=None,
|
active=True, onchange=None, value=None, data=None,
|
||||||
headers=None, update_button="", **kwarg):
|
headers=None, update_button="", **kwarg):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Arguments:
|
Arguments:
|
||||||
@@ -418,7 +418,7 @@ class Sheet:
|
|||||||
self.nodes = {}
|
self.nodes = {}
|
||||||
self.error = 'ERROR: %(error)s'
|
self.error = 'ERROR: %(error)s'
|
||||||
self.allowed_keywords = ['for', 'in', 'if', 'else', 'and', 'or', 'not',
|
self.allowed_keywords = ['for', 'in', 'if', 'else', 'and', 'or', 'not',
|
||||||
'i', 'j', 'k', 'x', 'y', 'z', 'sum']
|
'i', 'j', 'k', 'x', 'y', 'z', 'sum']
|
||||||
self.value = value
|
self.value = value
|
||||||
self.environment = {}
|
self.environment = {}
|
||||||
self.attributes = self.get_attributes(kwarg)
|
self.attributes = self.get_attributes(kwarg)
|
||||||
@@ -431,14 +431,14 @@ class Sheet:
|
|||||||
self.update_button = update_button
|
self.update_button = update_button
|
||||||
|
|
||||||
self.client = {
|
self.client = {
|
||||||
"columns": {},
|
"columns": {},
|
||||||
"colnames": {},
|
"colnames": {},
|
||||||
"id_columns": [],
|
"id_columns": [],
|
||||||
"id_colnames": {},
|
"id_colnames": {},
|
||||||
"cells": {},
|
"cells": {},
|
||||||
"modified": {},
|
"modified": {},
|
||||||
"headers": headers
|
"headers": headers
|
||||||
}
|
}
|
||||||
|
|
||||||
# if db and query:
|
# if db and query:
|
||||||
if self.data is not None:
|
if self.data is not None:
|
||||||
@@ -467,10 +467,10 @@ class Sheet:
|
|||||||
self.client["id_columns"].append(y)
|
self.client["id_columns"].append(y)
|
||||||
self.client["id_colnames"][colname] = y
|
self.client["id_colnames"][colname] = y
|
||||||
|
|
||||||
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"]:
|
||||||
@@ -485,7 +485,7 @@ class Sheet:
|
|||||||
else:
|
else:
|
||||||
value = '0.00'
|
value = '0.00'
|
||||||
self.cell(key, value,
|
self.cell(key, value,
|
||||||
readonly, active, onchange)
|
readonly, active, onchange)
|
||||||
|
|
||||||
exec('from math import *', {}, self.environment)
|
exec('from math import *', {}, self.environment)
|
||||||
|
|
||||||
@@ -565,7 +565,7 @@ class Sheet:
|
|||||||
for col, data in cells.iteritems():
|
for col, data in cells.iteritems():
|
||||||
key = "r%sc%s" % (row, col)
|
key = "r%sc%s" % (row, col)
|
||||||
active, onchange, readonly, cell_value = \
|
active, onchange, readonly, cell_value = \
|
||||||
self.get_cell_arguments(data, default=kwarg)
|
self.get_cell_arguments(data, default=kwarg)
|
||||||
if value is None:
|
if value is None:
|
||||||
v = cell_value
|
v = cell_value
|
||||||
else:
|
else:
|
||||||
@@ -575,7 +575,7 @@ class Sheet:
|
|||||||
onchange=onchange, **attributes)
|
onchange=onchange, **attributes)
|
||||||
else:
|
else:
|
||||||
active, onchange, readonly, all_value = \
|
active, onchange, readonly, all_value = \
|
||||||
self.get_cell_arguments(kwarg)
|
self.get_cell_arguments(kwarg)
|
||||||
for col, cell_value in enumerate(cells):
|
for col, cell_value in enumerate(cells):
|
||||||
key = "r%sc%s" % (row, col)
|
key = "r%sc%s" % (row, col)
|
||||||
if value is None:
|
if value is None:
|
||||||
@@ -601,7 +601,7 @@ class Sheet:
|
|||||||
for row, data in cells.iteritems():
|
for row, data in cells.iteritems():
|
||||||
key = "r%sc%s" % (row, col)
|
key = "r%sc%s" % (row, col)
|
||||||
active, onchange, readonly, cell_value = \
|
active, onchange, readonly, cell_value = \
|
||||||
self.get_cell_arguments(data, default=kwarg)
|
self.get_cell_arguments(data, default=kwarg)
|
||||||
if value is None:
|
if value is None:
|
||||||
v = cell_value
|
v = cell_value
|
||||||
else:
|
else:
|
||||||
@@ -610,7 +610,7 @@ class Sheet:
|
|||||||
onchange=onchange, **attributes)
|
onchange=onchange, **attributes)
|
||||||
else:
|
else:
|
||||||
active, onchange, readonly, all_value = \
|
active, onchange, readonly, all_value = \
|
||||||
self.get_cell_arguments(kwarg)
|
self.get_cell_arguments(kwarg)
|
||||||
for row, cell_value in enumerate(cells):
|
for row, cell_value in enumerate(cells):
|
||||||
key = "r%sc%s" % (row, col)
|
key = "r%sc%s" % (row, col)
|
||||||
if value is None:
|
if value is None:
|
||||||
@@ -646,36 +646,36 @@ class Sheet:
|
|||||||
if isinstance(cells, dict):
|
if isinstance(cells, dict):
|
||||||
for key, data in cells.iteritems():
|
for key, data in cells.iteritems():
|
||||||
r, c = self.position(key)
|
r, c = self.position(key)
|
||||||
key = "r%sc%s" % (r+starts_r, c+starts_c)
|
key = "r%sc%s" % (r + starts_r, c + starts_c)
|
||||||
active, onchange, readonly, cell_value = \
|
active, onchange, readonly, cell_value = \
|
||||||
self.get_cell_arguments(data, default=kwarg)
|
self.get_cell_arguments(data, default=kwarg)
|
||||||
if value is None:
|
if value is None:
|
||||||
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,
|
||||||
onchange=onchange, **attributes)
|
onchange=onchange, **attributes)
|
||||||
else:
|
else:
|
||||||
active, onchange, readonly, all_value = \
|
active, onchange, readonly, all_value = \
|
||||||
self.get_cell_arguments(kwarg)
|
self.get_cell_arguments(kwarg)
|
||||||
for r, row in enumerate(cells):
|
for r, row in enumerate(cells):
|
||||||
for c, cell_value in enumerate(row):
|
for c, cell_value in enumerate(row):
|
||||||
if value is None:
|
if value is None:
|
||||||
v = cell_value
|
v = cell_value
|
||||||
else:
|
else:
|
||||||
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,
|
||||||
onchange=onchange,
|
onchange=onchange,
|
||||||
readonly=readonly,
|
readonly=readonly,
|
||||||
**attributes)
|
**attributes)
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
key = str(key)
|
key = str(key)
|
||||||
@@ -694,7 +694,7 @@ class Sheet:
|
|||||||
other_key = match.group()
|
other_key = match.group()
|
||||||
if other_key == key:
|
if other_key == key:
|
||||||
self.computed_value = self.error % dict(error='cycle')
|
self.computed_value = self.error % dict(error='cycle')
|
||||||
self.modified={}
|
self.modified = {}
|
||||||
break
|
break
|
||||||
if other_key in self.nodes:
|
if other_key in self.nodes:
|
||||||
other_node = self.nodes[other_key]
|
other_node = self.nodes[other_key]
|
||||||
@@ -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)
|
||||||
@@ -737,7 +738,7 @@ class Sheet:
|
|||||||
output = {node.name: node.computed_value}
|
output = {node.name: node.computed_value}
|
||||||
changed_nodes = self.changed(node)
|
changed_nodes = self.changed(node)
|
||||||
while changed_nodes:
|
while changed_nodes:
|
||||||
ok=False
|
ok = False
|
||||||
set_changed_nodes = set(changed_nodes)
|
set_changed_nodes = set(changed_nodes)
|
||||||
for (k, other_node) in enumerate(changed_nodes):
|
for (k, other_node) in enumerate(changed_nodes):
|
||||||
#print other_node, changed_nodes
|
#print other_node, changed_nodes
|
||||||
@@ -758,7 +759,7 @@ class Sheet:
|
|||||||
return self.nodes[str(key)]
|
return self.nodes[str(key)]
|
||||||
|
|
||||||
def get_computed_values(self):
|
def get_computed_values(self):
|
||||||
d={}
|
d = {}
|
||||||
for key in self.nodes:
|
for key in self.nodes:
|
||||||
node = self.nodes[key]
|
node = self.nodes[key]
|
||||||
if node.value[:1] != '=' or not node.active:
|
if node.value[:1] != '=' or not node.active:
|
||||||
@@ -780,21 +781,21 @@ 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)])
|
||||||
rows.append(TR(*tds, **attributes))
|
rows.append(TR(*tds, **attributes))
|
||||||
@@ -805,8 +806,8 @@ 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";
|
||||||
var w2p_spreadsheet_update_result = null;
|
var w2p_spreadsheet_update_result = null;
|
||||||
@@ -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),
|
||||||
@@ -848,22 +850,22 @@ class Sheet:
|
|||||||
if self.client["headers"] is not None:
|
if self.client["headers"] is not None:
|
||||||
for fieldname, name in self.client["headers"].iteritems():
|
for fieldname, name in self.client["headers"].iteritems():
|
||||||
unsorted_headers.append((self.client["columns"][fieldname],
|
unsorted_headers.append((self.client["columns"][fieldname],
|
||||||
name))
|
name))
|
||||||
else:
|
else:
|
||||||
for fieldname, c in self.client["columns"].iteritems():
|
for fieldname, c in self.client["columns"].iteritems():
|
||||||
unsorted_headers.append((c, fieldname))
|
unsorted_headers.append((c, fieldname))
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
|||||||
+23
-21
@@ -1,6 +1,7 @@
|
|||||||
import urllib
|
import urllib
|
||||||
import simplejson
|
import simplejson
|
||||||
|
|
||||||
|
|
||||||
class Stripe:
|
class Stripe:
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
@@ -22,8 +23,9 @@ class Stripe:
|
|||||||
if paid is True than transaction was processed
|
if paid is True than transaction was processed
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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',
|
||||||
@@ -32,33 +34,33 @@ class Stripe:
|
|||||||
card_exp_year='2012',
|
card_exp_year='2012',
|
||||||
card_cvc_check='123',
|
card_cvc_check='123',
|
||||||
description='test charge'):
|
description='test charge'):
|
||||||
params = urllib.urlencode({'amount':amount,
|
params = urllib.urlencode({'amount': amount,
|
||||||
'currency':currency,
|
'currency': currency,
|
||||||
'card[number]':card_number,
|
'card[number]': card_number,
|
||||||
'card[exp_month]':card_exp_month,
|
'card[exp_month]': card_exp_month,
|
||||||
'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())
|
||||||
|
|
||||||
if __name__=='__main__':
|
if __name__ == '__main__':
|
||||||
key = raw_input('user>')
|
key = raw_input('user>')
|
||||||
d=Stripe(key).charge(100)
|
d = Stripe(key).charge(100)
|
||||||
print 'charged',d['paid']
|
print 'charged', d['paid']
|
||||||
s = Stripe(key).check(d[u'id'])
|
s = Stripe(key).check(d[u'id'])
|
||||||
print 'paid',s['paid'],s['amount'] ,s['currency']
|
print 'paid', s['paid'], s['amount'], s['currency']
|
||||||
s = Stripe(key).refund(d[u'id'])
|
s = Stripe(key).refund(d[u'id'])
|
||||||
print 'refunded',s['refunded']
|
print 'refunded', s['refunded']
|
||||||
@@ -28,7 +28,7 @@ class TaskBarIcon:
|
|||||||
win32con.WM_DESTROY: self.OnDestroy,
|
win32con.WM_DESTROY: self.OnDestroy,
|
||||||
win32con.WM_COMMAND: self.OnCommand,
|
win32con.WM_COMMAND: self.OnCommand,
|
||||||
win32con.WM_USER + 20: self.OnTaskbarNotify,
|
win32con.WM_USER + 20: self.OnTaskbarNotify,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Register the Window class.
|
# Register the Window class.
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ class TaskBarIcon:
|
|||||||
0,
|
0,
|
||||||
hinst,
|
hinst,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
win32gui.UpdateWindow(self.hwnd)
|
win32gui.UpdateWindow(self.hwnd)
|
||||||
self.SetServerStopped()
|
self.SetServerStopped()
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ class TaskBarIcon:
|
|||||||
hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
|
hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
|
||||||
|
|
||||||
flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE\
|
flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE\
|
||||||
| win32gui.NIF_TIP
|
| win32gui.NIF_TIP
|
||||||
nid = (
|
nid = (
|
||||||
self.hwnd,
|
self.hwnd,
|
||||||
0,
|
0,
|
||||||
@@ -90,7 +90,7 @@ class TaskBarIcon:
|
|||||||
win32con.WM_USER + 20,
|
win32con.WM_USER + 20,
|
||||||
hicon,
|
hicon,
|
||||||
'web2py Framework',
|
'web2py Framework',
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, nid)
|
win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, nid)
|
||||||
except:
|
except:
|
||||||
@@ -111,7 +111,7 @@ class TaskBarIcon:
|
|||||||
msg,
|
msg,
|
||||||
wparam,
|
wparam,
|
||||||
lparam,
|
lparam,
|
||||||
):
|
):
|
||||||
self._DoCreateIcons()
|
self._DoCreateIcons()
|
||||||
|
|
||||||
def OnDestroy(
|
def OnDestroy(
|
||||||
@@ -120,7 +120,7 @@ class TaskBarIcon:
|
|||||||
msg,
|
msg,
|
||||||
wparam,
|
wparam,
|
||||||
lparam,
|
lparam,
|
||||||
):
|
):
|
||||||
nid = (self.hwnd, 0)
|
nid = (self.hwnd, 0)
|
||||||
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
|
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ class TaskBarIcon:
|
|||||||
msg,
|
msg,
|
||||||
wparam,
|
wparam,
|
||||||
lparam,
|
lparam,
|
||||||
):
|
):
|
||||||
if lparam == win32con.WM_LBUTTONUP:
|
if lparam == win32con.WM_LBUTTONUP:
|
||||||
pass
|
pass
|
||||||
elif lparam == win32con.WM_LBUTTONDBLCLK:
|
elif lparam == win32con.WM_LBUTTONDBLCLK:
|
||||||
@@ -144,14 +144,14 @@ class TaskBarIcon:
|
|||||||
win32gui.AppendMenu(menu, win32con.MF_STRING, 1024,
|
win32gui.AppendMenu(menu, win32con.MF_STRING, 1024,
|
||||||
'Start Server')
|
'Start Server')
|
||||||
win32gui.AppendMenu(menu, win32con.MF_STRING
|
win32gui.AppendMenu(menu, win32con.MF_STRING
|
||||||
| win32con.MF_GRAYED, 1025,
|
| win32con.MF_GRAYED, 1025,
|
||||||
'Restart Server')
|
'Restart Server')
|
||||||
win32gui.AppendMenu(menu, win32con.MF_STRING
|
win32gui.AppendMenu(menu, win32con.MF_STRING
|
||||||
| win32con.MF_GRAYED, 1026,
|
| win32con.MF_GRAYED, 1026,
|
||||||
'Stop Server')
|
'Stop Server')
|
||||||
else:
|
else:
|
||||||
win32gui.AppendMenu(menu, win32con.MF_STRING
|
win32gui.AppendMenu(menu, win32con.MF_STRING
|
||||||
| win32con.MF_GRAYED, 1024,
|
| win32con.MF_GRAYED, 1024,
|
||||||
'Start Server')
|
'Start Server')
|
||||||
win32gui.AppendMenu(menu, win32con.MF_STRING, 1025,
|
win32gui.AppendMenu(menu, win32con.MF_STRING, 1025,
|
||||||
'Restart Server')
|
'Restart Server')
|
||||||
@@ -173,7 +173,7 @@ class TaskBarIcon:
|
|||||||
0,
|
0,
|
||||||
self.hwnd,
|
self.hwnd,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
win32api.PostMessage(self.hwnd, win32con.WM_NULL, 0, 0)
|
win32api.PostMessage(self.hwnd, win32con.WM_NULL, 0, 0)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ class TaskBarIcon:
|
|||||||
msg,
|
msg,
|
||||||
wparam,
|
wparam,
|
||||||
lparam,
|
lparam,
|
||||||
):
|
):
|
||||||
id = win32api.LOWORD(wparam)
|
id = win32api.LOWORD(wparam)
|
||||||
if id == 1023:
|
if id == 1023:
|
||||||
self.status.append(self.EnumStatus.TOGGLE)
|
self.status.append(self.EnumStatus.TOGGLE)
|
||||||
@@ -226,7 +226,7 @@ class TaskBarIcon:
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
icon_flags,
|
icon_flags,
|
||||||
)
|
)
|
||||||
return hicon
|
return hicon
|
||||||
|
|
||||||
class EnumStatus:
|
class EnumStatus:
|
||||||
|
|||||||
@@ -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),
|
||||||
@@ -25,23 +29,24 @@ class TimeCollector(object):
|
|||||||
total_time = self._stampDelta(-1, 0)
|
total_time = self._stampDelta(-1, 0)
|
||||||
data = []
|
data = []
|
||||||
for i in range(1, len(self.scores)):
|
for i in range(1, len(self.scores)):
|
||||||
delta = self._stampDelta(i, i-1)
|
delta = self._stampDelta(i, i - 1)
|
||||||
if abs(total_time) < 1e-6:
|
if abs(total_time) < 1e-6:
|
||||||
perc = 0
|
perc = 0
|
||||||
else:
|
else:
|
||||||
perc = delta / total_time * 100
|
perc = delta / total_time * 100
|
||||||
data.append(
|
data.append(
|
||||||
dict(
|
dict(
|
||||||
start = self._stampDelta(i-1, 0) * 1000,
|
start=self._stampDelta(i - 1, 0) * 1000,
|
||||||
end = self._stampDelta(i, 0) * 1000,
|
end=self._stampDelta(i, 0) * 1000,
|
||||||
delta = delta * 1000,
|
delta=delta * 1000,
|
||||||
perc = perc,
|
perc=perc,
|
||||||
tag = self.scores[i].tag
|
tag=self.scores[i].tag
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
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
|
||||||
@@ -51,14 +56,16 @@ class TimeCollector(object):
|
|||||||
headerData = ('Start(ms)', 'End(ms)', 'Delta(ms)', 'Time Cost',
|
headerData = ('Start(ms)', 'End(ms)', 'Delta(ms)', 'Time Cost',
|
||||||
'Description')
|
'Description')
|
||||||
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):
|
|
||||||
self.scores = [Score(tag='start',stamp=time.clock())]
|
|
||||||
|
|
||||||
if __name__=='__main__':
|
def restart(self):
|
||||||
|
self.scores = [Score(tag='start', stamp=time.clock())]
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
print('')
|
print('')
|
||||||
print('Testing:')
|
print('Testing:')
|
||||||
print('')
|
print('')
|
||||||
@@ -87,7 +94,7 @@ if __name__=='__main__':
|
|||||||
print(t.getReportText())
|
print(t.getReportText())
|
||||||
t.restart()
|
t.restart()
|
||||||
for y in range(1, 200, 20):
|
for y in range(1, 200, 20):
|
||||||
x = [i for i in range(10000)*y]
|
x = [i for i in range(10000) * y]
|
||||||
t.addStamp('Iteration when y = ' + str(y))
|
t.addStamp('Iteration when y = ' + str(y))
|
||||||
|
|
||||||
print('')
|
print('')
|
||||||
|
|||||||
@@ -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,24 +48,24 @@ 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:
|
||||||
self.register(d)
|
self.register(d)
|
||||||
|
|
||||||
|
|
||||||
class DetectorBase(object):
|
class DetectorBase(object):
|
||||||
name = "" # "to perform match in DetectorsHub object"
|
name = "" # "to perform match in DetectorsHub object"
|
||||||
info_type = '' #override me
|
info_type = '' # override me
|
||||||
result_key = '' #override me
|
result_key = '' # override me
|
||||||
order = 10 # 0 is highest
|
order = 10 # 0 is highest
|
||||||
look_for = [] # list of words to look for
|
look_for = [] # list of words to look for
|
||||||
skip_if_found = [] # strings if present stop processin
|
skip_if_found = [] # strings if present stop processin
|
||||||
can_register = False
|
can_register = False
|
||||||
is_mobile = False
|
is_mobile = False
|
||||||
is_tablet = False
|
is_tablet = False
|
||||||
prefs = dict() # dict(info_type = [name1, name2], ..)
|
prefs = dict() # dict(info_type = [name1, name2], ..)
|
||||||
version_splitters = ["/", " "]
|
version_splitters = ["/", " "]
|
||||||
_suggested_detectors = None
|
_suggested_detectors = None
|
||||||
|
|
||||||
@@ -75,8 +77,8 @@ class DetectorBase(object):
|
|||||||
def detect(self, agent, result):
|
def detect(self, agent, result):
|
||||||
if agent and self.checkWords(agent):
|
if agent and self.checkWords(agent):
|
||||||
result[self.info_type] = dict(name=self.name)
|
result[self.info_type] = dict(name=self.name)
|
||||||
is_mobile=self.is_mobile
|
is_mobile = self.is_mobile
|
||||||
is_tablet=self.is_tablet
|
is_tablet = self.is_tablet
|
||||||
if is_mobile:
|
if is_mobile:
|
||||||
result['is_mobile'] = is_mobile
|
result['is_mobile'] = is_mobile
|
||||||
if is_tablet:
|
if is_tablet:
|
||||||
@@ -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,9 +228,10 @@ 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'])
|
||||||
is_mobile = True
|
is_mobile = True
|
||||||
|
|
||||||
# Manual check for tablet
|
# Manual check for tablet
|
||||||
@@ -232,14 +243,16 @@ 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
|
||||||
is_tablet=True
|
is_tablet = True
|
||||||
|
|
||||||
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 = [';', ')']
|
||||||
@@ -269,7 +283,8 @@ class Windows(OS):
|
|||||||
|
|
||||||
def getVersion(self, agent):
|
def getVersion(self, agent):
|
||||||
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'
|
||||||
@@ -277,7 +292,8 @@ class WindowsMobile(Dist):
|
|||||||
is_mobile = True
|
is_mobile = True
|
||||||
|
|
||||||
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']
|
||||||
@@ -295,13 +311,16 @@ 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']
|
||||||
@@ -311,23 +330,27 @@ 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 = ['/', ';']
|
||||||
|
|
||||||
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,64 +447,82 @@ 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',
|
(
|
||||||
('Series SymbianOS 60 3.1', 'Safari', True),
|
'Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 Nokia6120c/3.83; Profile/MIDP-2.0 Configuration/CLDC-1.1) AppleWebKit/413 (KHTML, like Gecko) Safari/413',
|
||||||
{'is_mobile': True, 'is_tablet': False, 'flavor': {'name': 'Series', 'version': '60 3.1'}, 'os': {'name': 'SymbianOS', 'version': '9.2'}, 'browser': {'name': 'Safari'}},),
|
('Series SymbianOS 60 3.1', 'Safari', True),
|
||||||
('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',
|
{'is_mobile': True, 'is_tablet': False, 'flavor': {'name': 'Series', 'version': '60 3.1'}, 'os': {'name': 'SymbianOS', 'version': '9.2'}, 'browser': {'name': 'Safari'}},),
|
||||||
('Series SymbianOS 60 5.0', 'BrowserNG 7.1.18124', True),
|
(
|
||||||
|
'Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/20.0.019; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.18124',
|
||||||
|
('Series SymbianOS 60 5.0', 'BrowserNG 7.1.18124', True),
|
||||||
{'is_mobile': True, 'is_tablet': False, 'flavor': {'name': 'Series', 'version': '60 5.0'}, 'os': {'name': 'SymbianOS', 'version': '9.4'}, 'browser': {'name': 'BrowserNG', 'version': '7.1.18124'}},),
|
{'is_mobile': True, 'is_tablet': False, 'flavor': {'name': 'Series', 'version': '60 5.0'}, 'os': {'name': 'SymbianOS', 'version': '9.4'}, 'browser': {'name': 'BrowserNG', 'version': '7.1.18124'}},),
|
||||||
('Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Windows Phone 6.5.3.5)',
|
(
|
||||||
('Phone Windows 6.5.3.5', 'Microsoft Internet Explorer 6.0', True),
|
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Windows Phone 6.5.3.5)',
|
||||||
{'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'}},),
|
('Phone Windows 6.5.3.5', 'Microsoft Internet Explorer 6.0', True),
|
||||||
('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+',
|
{'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'}},),
|
||||||
('PlayBook BlackBerry 1.0.0', 'Safari 0.0.1', True),
|
(
|
||||||
|
'Mozilla/5.0 (PlayBook; U; RIM Tablet OS 1.0.0; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/0.0.1 Safari/534.8+',
|
||||||
|
('PlayBook BlackBerry 1.0.0', 'Safari 0.0.1', True),
|
||||||
{'is_mobile': True, 'is_tablet': True, 'flavor': {'name': 'PlayBook', 'version': '1.0.0'}, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '0.0.1'}},),
|
{'is_mobile': True, 'is_tablet': True, 'flavor': {'name': 'PlayBook', 'version': '1.0.0'}, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '0.0.1'}},),
|
||||||
('Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.246 Mobile Safari/534.1+',
|
(
|
||||||
('BlackBerry', 'Safari 6.0.0.246', True),
|
'Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.246 Mobile Safari/534.1+',
|
||||||
|
('BlackBerry', 'Safari 6.0.0.246', True),
|
||||||
{'is_mobile': True, 'is_tablet': False, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '6.0.0.246'}},),
|
{'is_mobile': True, 'is_tablet': False, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '6.0.0.246'}},),
|
||||||
('Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.600 Mobile Safari/534.8+',
|
(
|
||||||
('BlackBerry', 'Safari 6.0.0.600', True),
|
'Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.600 Mobile Safari/534.8+',
|
||||||
|
('BlackBerry', 'Safari 6.0.0.600', True),
|
||||||
{'is_mobile': True, 'is_tablet': False, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '6.0.0.600'}},),
|
{'is_mobile': True, 'is_tablet': False, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '6.0.0.600'}},),
|
||||||
('Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5',
|
(
|
||||||
('MacOS iPad X', 'Safari 5.0.2', True),
|
'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',
|
||||||
{'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'}},),
|
('MacOS iPad X', 'Safari 5.0.2', True),
|
||||||
('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20060127 Netscape/8.1',
|
{'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'}},),
|
||||||
('Windows NT 5.1', 'Netscape 8.1', False),
|
(
|
||||||
|
'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20060127 Netscape/8.1',
|
||||||
|
('Windows NT 5.1', 'Netscape 8.1', False),
|
||||||
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'Windows', 'version': 'NT 5.1'}, 'browser': {'name': 'Netscape', 'version': '8.1'}},),
|
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'Windows', 'version': 'NT 5.1'}, 'browser': {'name': 'Netscape', 'version': '8.1'}},),
|
||||||
('Mozilla/5.0 (Linux; U; Android 3.0.1; en-us; A500 Build/HRI66) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13',
|
(
|
||||||
('Android Linux 3.0.1', 'Safari 4.0', True),
|
'Mozilla/5.0 (Linux; U; Android 3.0.1; en-us; A500 Build/HRI66) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13',
|
||||||
|
('Android Linux 3.0.1', 'Safari 4.0', True),
|
||||||
{'is_mobile': True, 'is_tablet': True, 'dist': {'version': '3.0.1', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
|
{'is_mobile': True, 'is_tablet': True, 'dist': {'version': '3.0.1', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
|
||||||
('Mozilla/5.0 (Linux; U; Android 2.3.7; it-it; Dream/Sapphire Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',
|
(
|
||||||
('Android Linux 2.3.7', 'Safari 4.0', True),
|
'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',
|
||||||
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.3.7', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
|
('Android Linux 2.3.7', 'Safari 4.0', True),
|
||||||
('Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-GB; rv:1.9.0.10) Gecko/2009042315 Firefox/3.0.10',
|
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.3.7', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
|
||||||
('MacOS Macintosh X 10.5', 'Firefox 3.0.10', False),
|
(
|
||||||
{ 'is_mobile': False, 'is_tablet': False, 'flavor': {'version': 'X 10.5', 'name': 'MacOS'}, 'os': {'name': 'Macintosh'}, 'browser': {'version': '3.0.10', 'name': 'Firefox'}},),
|
'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-GB; rv:1.9.0.10) Gecko/2009042315 Firefox/3.0.10',
|
||||||
('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.5', 'Firefox 3.0.10', False),
|
||||||
('MacOS Macintosh X 10.6.6', 'Chrome 11.0.696.3', False),
|
{'is_mobile': False, 'is_tablet': False, 'flavor': {'version': 'X 10.5', 'name': 'MacOS'}, 'os': {'name': 'Macintosh'}, 'browser': {'version': '3.0.10', 'name': 'Firefox'}},),
|
||||||
|
(
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_6) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24,gzip(gfe)',
|
||||||
|
('MacOS Macintosh X 10.6.6', 'Chrome 11.0.696.3', False),
|
||||||
{'is_mobile': False, 'is_tablet': False, 'flavor': {'version': 'X 10.6.6', 'name': 'MacOS'}, 'os': {'name': 'Macintosh'}, 'browser': {'version': '11.0.696.3', 'name': 'Chrome'}},),
|
{'is_mobile': False, 'is_tablet': False, 'flavor': {'version': 'X 10.6.6', 'name': 'MacOS'}, 'os': {'name': 'Macintosh'}, 'browser': {'version': '11.0.696.3', 'name': 'Chrome'}},),
|
||||||
('Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2) Gecko/20100308 Ubuntu/10.04 (lucid) Firefox/3.6 GTB7.1',
|
(
|
||||||
('Ubuntu Linux 10.04', 'Firefox 3.6', False),
|
'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2) Gecko/20100308 Ubuntu/10.04 (lucid) Firefox/3.6 GTB7.1',
|
||||||
|
('Ubuntu Linux 10.04', 'Firefox 3.6', False),
|
||||||
{'is_mobile': False, 'is_tablet': False, 'dist': {'version': '10.04', 'name': 'Ubuntu'}, 'os': {'name': 'Linux'}, 'browser': {'version': '3.6', 'name': 'Firefox'}},),
|
{'is_mobile': False, 'is_tablet': False, 'dist': {'version': '10.04', 'name': 'Ubuntu'}, 'os': {'name': 'Linux'}, 'browser': {'version': '3.6', 'name': 'Firefox'}},),
|
||||||
('Mozilla/5.0 (Linux; U; Android 2.2.1; fr-ch; A43 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',
|
(
|
||||||
('Android Linux 2.2.1', 'Safari 4.0', True),
|
'Mozilla/5.0 (Linux; U; Android 2.2.1; fr-ch; A43 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',
|
||||||
|
('Android Linux 2.2.1', 'Safari 4.0', True),
|
||||||
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.2.1', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
|
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.2.1', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
|
||||||
('Mozilla/5.0 (Linux; U; Android 2.3.4; it-it; LG-P990 Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 MMS/LG-Android-MMS-V1.0/1.2',
|
(
|
||||||
('Android Linux 2.3.4', 'Safari 4.0', True),
|
'Mozilla/5.0 (Linux; U; Android 2.3.4; it-it; LG-P990 Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 MMS/LG-Android-MMS-V1.0/1.2',
|
||||||
|
('Android Linux 2.3.4', 'Safari 4.0', True),
|
||||||
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.3.4', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
|
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.3.4', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
|
||||||
('Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3',
|
(
|
||||||
('MacOS iPhone X', 'Safari 3.0', True),
|
'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3',
|
||||||
|
('MacOS iPhone X', 'Safari 3.0', True),
|
||||||
{'is_mobile': True, 'is_tablet': False, 'flavor': {'version': 'X', 'name': 'MacOS'}, 'dist': {'version': 'X', 'name': 'iPhone'}, 'browser': {'version': '3.0', 'name': 'Safari'}},),
|
{'is_mobile': True, 'is_tablet': False, 'flavor': {'version': 'X', 'name': 'MacOS'}, 'dist': {'version': 'X', 'name': 'iPhone'}, 'browser': {'version': '3.0', 'name': 'Safari'}},),
|
||||||
('Mozilla/5.0 (X11; CrOS i686 0.0.0) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.27 Safari/534.24,gzip(gfe)',
|
(
|
||||||
('ChromeOS 0.0.0', 'Chrome 11.0.696.27', False),
|
'Mozilla/5.0 (X11; CrOS i686 0.0.0) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.27 Safari/534.24,gzip(gfe)',
|
||||||
|
('ChromeOS 0.0.0', 'Chrome 11.0.696.27', False),
|
||||||
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'ChromeOS', 'version': '0.0.0'}, 'browser': {'name': 'Chrome', 'version': '11.0.696.27'}},),
|
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'ChromeOS', 'version': '0.0.0'}, 'browser': {'name': 'Chrome', 'version': '11.0.696.27'}},),
|
||||||
('Mozilla/4.0 (compatible; MSIE 6.0; MSIE 5.5; Windows NT 5.1) Opera 7.02 [en]',
|
(
|
||||||
('Windows NT 5.1', 'Opera 7.02', False),
|
'Mozilla/4.0 (compatible; MSIE 6.0; MSIE 5.5; Windows NT 5.1) Opera 7.02 [en]',
|
||||||
|
('Windows NT 5.1', 'Opera 7.02', False),
|
||||||
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'Windows', 'version': 'NT 5.1'}, 'browser': {'name': 'Opera', 'version': '7.02'}},),
|
{'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',
|
||||||
('Linux', 'Opera 9.80', False),
|
('Linux', 'Opera 9.80', False),
|
||||||
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'Linux'}, 'browser': {'name': 'Opera', 'version': '9.80'}},),
|
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'Linux'}, 'browser': {'name': 'Opera', 'version': '9.80'}},),
|
||||||
)
|
)
|
||||||
|
|
||||||
class TestHAP(unittest.TestCase):
|
class TestHAP(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -513,6 +561,6 @@ class mobilize(object):
|
|||||||
user_agent = current.request.user_agent()
|
user_agent = current.request.user_agent()
|
||||||
if user_agent.is_mobile:
|
if user_agent.is_mobile:
|
||||||
items = current.response.view.split('.')
|
items = current.response.view.split('.')
|
||||||
items.insert(-1,'mobile')
|
items.insert(-1, 'mobile')
|
||||||
current.response.view = '.'.join(items)
|
current.response.view = '.'.join(items)
|
||||||
return self.func()
|
return self.func()
|
||||||
+48
-45
@@ -22,21 +22,22 @@ import urllib
|
|||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
DEFAULT_HEADERS = {
|
DEFAULT_HEADERS = {
|
||||||
'user-agent': 'Mozilla/4.0', # some servers are picky
|
'user-agent': 'Mozilla/4.0', # some servers are picky
|
||||||
'accept-language': 'en',
|
'accept-language': 'en',
|
||||||
}
|
}
|
||||||
|
|
||||||
FORM_REGEX = re.compile('(\<input name\="_formkey" type\="hidden" value\="(?P<formkey>.+?)" \/\>)?\<input name\="_formname" type\="hidden" value\="(?P<formname>.+?)" \/\>')
|
FORM_REGEX = re.compile('(\<input name\="_formkey" type\="hidden" value\="(?P<formkey>.+?)" \/\>)?\<input name\="_formname" type\="hidden" value\="(?P<formname>.+?)" \/\>')
|
||||||
|
|
||||||
SESSION_REGEX = 'session_id_(?P<name>.+)'
|
SESSION_REGEX = 'session_id_(?P<name>.+)'
|
||||||
|
|
||||||
|
|
||||||
class WebClient(object):
|
class WebClient(object):
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
app = '',
|
app='',
|
||||||
postbacks = True,
|
postbacks=True,
|
||||||
default_headers = DEFAULT_HEADERS,
|
default_headers=DEFAULT_HEADERS,
|
||||||
session_regex = SESSION_REGEX):
|
session_regex=SESSION_REGEX):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.postbacks = postbacks
|
self.postbacks = postbacks
|
||||||
self.forms = {}
|
self.forms = {}
|
||||||
@@ -46,18 +47,18 @@ class WebClient(object):
|
|||||||
self.sessions = {}
|
self.sessions = {}
|
||||||
self.session_regex = session_regex and re.compile(session_regex)
|
self.session_regex = session_regex and re.compile(session_regex)
|
||||||
|
|
||||||
def get(self,url,cookies=None,headers=None,auth=None):
|
def get(self, url, cookies=None, headers=None, auth=None):
|
||||||
return self.post(url,data=None,cookies=cookies,headers=headers)
|
return self.post(url, data=None, cookies=cookies, headers=headers)
|
||||||
|
|
||||||
def post(self,url,data=None,cookies=None,headers=None,auth=None):
|
def post(self, url, data=None, cookies=None, headers=None, auth=None):
|
||||||
self.url = self.app+url
|
self.url = self.app + url
|
||||||
|
|
||||||
# if this POST form requires a postback do it
|
# if this POST form requires a postback do it
|
||||||
if data and '_formname' in data and self.postbacks and \
|
if data and '_formname' in data and self.postbacks and \
|
||||||
self.history and self.history[-1][1]!=self.url:
|
self.history and self.history[-1][1] != self.url:
|
||||||
# to bypass the web2py CSRF need to get formkey
|
# to bypass the web2py CSRF need to get formkey
|
||||||
# before submitting the form
|
# before submitting the form
|
||||||
self.get(url,cookies=cookies,headers=headers,auth=auth)
|
self.get(url, cookies=cookies, headers=headers, auth=auth)
|
||||||
|
|
||||||
# unless cookies are specified, recycle cookies
|
# unless cookies are specified, recycle cookies
|
||||||
if cookies is None:
|
if cookies is None:
|
||||||
@@ -75,22 +76,23 @@ class WebClient(object):
|
|||||||
|
|
||||||
# copy headers from dict to list of key,value
|
# copy headers from dict to list of key,value
|
||||||
headers_list = []
|
headers_list = []
|
||||||
for key,value in self.default_headers.iteritems():
|
for key, value in self.default_headers.iteritems():
|
||||||
if not key in headers:
|
if not key in headers:
|
||||||
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))
|
||||||
|
|
||||||
# move cookies to headers
|
# move cookies to headers
|
||||||
for key,value in cookies.iteritems():
|
for key, value in cookies.iteritems():
|
||||||
headers_list.append(('Cookie','%s=%s' % (key,value)))
|
headers_list.append(('Cookie', '%s=%s' % (key, value)))
|
||||||
|
|
||||||
# add headers to request
|
# add headers to request
|
||||||
for key,value in headers_list:
|
for key, value in headers_list:
|
||||||
opener.addheaders.append((key,str(value)))
|
opener.addheaders.append((key, str(value)))
|
||||||
|
|
||||||
# assume everything is ok and make http request
|
# assume everything is ok and make http request
|
||||||
error = None
|
error = None
|
||||||
@@ -99,7 +101,7 @@ class WebClient(object):
|
|||||||
self.method = 'POST'
|
self.method = 'POST'
|
||||||
|
|
||||||
# if there is only one form, set _formname automatically
|
# if there is only one form, set _formname automatically
|
||||||
if not '_formname' in data and len(self.forms)==1:
|
if not '_formname' in data and len(self.forms) == 1:
|
||||||
data['_formname'] = self.forms.keys()[0]
|
data['_formname'] = self.forms.keys()[0]
|
||||||
|
|
||||||
# if there is no formkey but it is known, set it
|
# if there is no formkey but it is known, set it
|
||||||
@@ -110,18 +112,18 @@ class WebClient(object):
|
|||||||
# time the POST request
|
# time the POST request
|
||||||
data = urllib.urlencode(data)
|
data = urllib.urlencode(data)
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
self.response = opener.open(self.url,data)
|
self.response = opener.open(self.url, data)
|
||||||
self.time = time.time()-t0
|
self.time = time.time() - t0
|
||||||
else:
|
else:
|
||||||
self.method = 'GET'
|
self.method = 'GET'
|
||||||
|
|
||||||
# time the GET request
|
# time the GET request
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
self.response = opener.open(self.url)
|
self.response = opener.open(self.url)
|
||||||
self.time = time.time()-t0
|
self.time = time.time() - t0
|
||||||
except urllib2.HTTPError, error:
|
except urllib2.HTTPError, error:
|
||||||
# catch HTTP errors
|
# catch HTTP errors
|
||||||
self.time = time.time()-t0
|
self.time = time.time() - t0
|
||||||
self.response = error
|
self.response = error
|
||||||
|
|
||||||
self.status = self.response.getcode()
|
self.status = self.response.getcode()
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -139,8 +141,8 @@ class WebClient(object):
|
|||||||
self.cookies = {}
|
self.cookies = {}
|
||||||
if 'set-cookie' in self.headers:
|
if 'set-cookie' in self.headers:
|
||||||
for item in self.headers['set-cookie'].split(','):
|
for item in self.headers['set-cookie'].split(','):
|
||||||
key,value = item[:item.find(';')].split('=')
|
key, value = item[:item.find(';')].split('=')
|
||||||
self.cookies[key.strip()]=value.strip()
|
self.cookies[key.strip()] = value.strip()
|
||||||
|
|
||||||
# check is a new session id has been issued, symptom of broken session
|
# check is a new session id has been issued, symptom of broken session
|
||||||
if self.session_regex is not None:
|
if self.session_regex is not None:
|
||||||
@@ -148,8 +150,8 @@ class WebClient(object):
|
|||||||
match = self.session_regex.match(cookie)
|
match = self.session_regex.match(cookie)
|
||||||
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
|
||||||
@@ -158,7 +160,8 @@ class WebClient(object):
|
|||||||
self.forms[match.group('formname')] = match.group('formkey')
|
self.forms[match.group('formname')] = match.group('formkey')
|
||||||
|
|
||||||
# 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
|
||||||
@@ -168,13 +171,13 @@ def test_web2py_registration_and_login():
|
|||||||
client.get('index')
|
client.get('index')
|
||||||
|
|
||||||
# register
|
# register
|
||||||
data = dict(first_name = 'Homer',
|
data = dict(first_name='Homer',
|
||||||
last_name = 'Simpson',
|
last_name='Simpson',
|
||||||
email = 'homer@web2py.com',
|
email='homer@web2py.com',
|
||||||
password = 'test',
|
password='test',
|
||||||
password_two = 'test',
|
password_two='test',
|
||||||
_formname = 'register')
|
_formname='register')
|
||||||
client.post('user/register',data = data)
|
client.post('user/register', data=data)
|
||||||
|
|
||||||
# logout
|
# logout
|
||||||
client.get('user/logout')
|
client.get('user/logout')
|
||||||
@@ -182,18 +185,18 @@ def test_web2py_registration_and_login():
|
|||||||
# login
|
# login
|
||||||
data = dict(email='homer@web2py.com',
|
data = dict(email='homer@web2py.com',
|
||||||
password='test',
|
password='test',
|
||||||
_formname = 'login')
|
_formname='login')
|
||||||
client.post('user/login',data = data)
|
client.post('user/login', data=data)
|
||||||
|
|
||||||
# check registration and login were successful
|
# check registration and login were successful
|
||||||
client.get('user/profile')
|
client.get('user/profile')
|
||||||
assert 'Welcome Homer' in client.text
|
assert 'Welcome Homer' in client.text
|
||||||
|
|
||||||
# print some variables
|
# print some variables
|
||||||
print '\nsessions:\n',client.sessions
|
print '\nsessions:\n', client.sessions
|
||||||
print '\nheaders:\n',client.headers
|
print '\nheaders:\n', client.headers
|
||||||
print '\ncookies:\n',client.cookies
|
print '\ncookies:\n', client.cookies
|
||||||
print '\nforms:\n',client.forms
|
print '\nforms:\n', client.forms
|
||||||
print
|
print
|
||||||
for method, url, status, t in client.history:
|
for method, url, status, t in client.history:
|
||||||
print method, url, status, t
|
print method, url, status, t
|
||||||
|
|||||||
@@ -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'):
|
|
||||||
sig = hmac_key and hmac.new(hmac_key,message).hexdigest() or ''
|
def websocket_send(url, message, hmac_key=None, group='default'):
|
||||||
params = urllib.urlencode({'message': message, 'signature': sig, 'group':group})
|
sig = hmac_key and hmac.new(hmac_key, message).hexdigest() or ''
|
||||||
|
params = urllib.urlencode(
|
||||||
|
{'message': message, 'signature': sig, 'group': group})
|
||||||
f = urllib.urlopen(url, params)
|
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,46 +123,55 @@ 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]
|
||||||
self.group = group or 'default'
|
self.group = group or 'default'
|
||||||
self.token = token or 'none'
|
self.token = token or 'none'
|
||||||
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__":
|
||||||
usage = __doc__
|
usage = __doc__
|
||||||
version= ""
|
version = ""
|
||||||
parser = optparse.OptionParser(usage, None, optparse.Option, version)
|
parser = optparse.OptionParser(usage, None, optparse.Option, version)
|
||||||
parser.add_option('-p',
|
parser.add_option('-p',
|
||||||
'--port',
|
'--port',
|
||||||
@@ -180,7 +197,7 @@ if __name__ == "__main__":
|
|||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
hmac_key = options.hmac_key
|
hmac_key = options.hmac_key
|
||||||
DistributeHandler.tokens = options.tokens
|
DistributeHandler.tokens = options.tokens
|
||||||
urls=[
|
urls = [
|
||||||
(r'/', PostHandler),
|
(r'/', PostHandler),
|
||||||
(r'/token', TokenHandler),
|
(r'/token', TokenHandler),
|
||||||
(r'/realtime/(.*)', DistributeHandler)]
|
(r'/realtime/(.*)', DistributeHandler)]
|
||||||
|
|||||||
+30
-23
@@ -10,24 +10,30 @@ import traceback
|
|||||||
from gluon import current
|
from gluon import current
|
||||||
|
|
||||||
NATIVE_IMPORTER = __builtin__.__import__
|
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
|
||||||
@@ -45,25 +51,26 @@ def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
|
|||||||
base_importer = TRACK_IMPORTER
|
base_importer = TRACK_IMPORTER
|
||||||
else:
|
else:
|
||||||
base_importer = NATIVE_IMPORTER
|
base_importer = NATIVE_IMPORTER
|
||||||
except: # there is no current.request (should never happen)
|
except: # there is no current.request (should never happen)
|
||||||
base_importer = NATIVE_IMPORTER
|
base_importer = NATIVE_IMPORTER
|
||||||
|
|
||||||
# if not relative and not from applications:
|
# if not relative and not from applications:
|
||||||
if hasattr(current,'request') \
|
if hasattr(current, 'request') \
|
||||||
and level<=0 \
|
and level <= 0 \
|
||||||
and not name.split('.')[0] in INVALID_MODULES \
|
and not name.split('.')[0] in INVALID_MODULES \
|
||||||
and isinstance(globals, dict):
|
and isinstance(globals, dict):
|
||||||
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]:
|
||||||
modules_prefix = '.'.join(items[-2:])+'.modules'
|
items = items[:-1]
|
||||||
|
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"
|
||||||
result = None
|
result = None
|
||||||
for itemname in name.split("."):
|
for itemname in name.split("."):
|
||||||
new_mod = base_importer(
|
new_mod = base_importer(
|
||||||
modules_prefix, globals,locals, [itemname], level)
|
modules_prefix, globals, locals, [itemname], level)
|
||||||
try:
|
try:
|
||||||
result = result or new_mod.__dict__[itemname]
|
result = result or new_mod.__dict__[itemname]
|
||||||
except KeyError, e:
|
except KeyError, e:
|
||||||
@@ -77,16 +84,16 @@ def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
|
|||||||
except ImportError, e1:
|
except ImportError, e1:
|
||||||
import_tb = sys.exc_info()[2]
|
import_tb = sys.exc_info()[2]
|
||||||
try:
|
try:
|
||||||
return NATIVE_IMPORTER(name,globals,locals,fromlist,level)
|
return NATIVE_IMPORTER(name, globals, locals, fromlist, level)
|
||||||
except ImportError, e3:
|
except ImportError, e3:
|
||||||
raise ImportError, e1, import_tb # there an import error in the module
|
raise ImportError, e1, import_tb # there an import error in the module
|
||||||
except Exception, e2:
|
except Exception, e2:
|
||||||
raise e2 # there is an error in the module
|
raise e2 # there is an error in the module
|
||||||
finally:
|
finally:
|
||||||
if import_tb:
|
if import_tb:
|
||||||
import_tb = None
|
import_tb = None
|
||||||
|
|
||||||
return NATIVE_IMPORTER(name,globals,locals,fromlist,level)
|
return NATIVE_IMPORTER(name, globals, locals, fromlist, level)
|
||||||
|
|
||||||
|
|
||||||
class TrackImporter(object):
|
class TrackImporter(object):
|
||||||
@@ -96,19 +103,19 @@ class TrackImporter(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
THREAD_LOCAL = threading.local()
|
THREAD_LOCAL = threading.local()
|
||||||
PACKAGE_PATH_SUFFIX = os.path.sep+"__init__.py"
|
PACKAGE_PATH_SUFFIX = os.path.sep + "__init__.py"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._import_dates = {} # Import dates of the files of the modules
|
self._import_dates = {} # Import dates of the files of the modules
|
||||||
|
|
||||||
def __call__(self,name,globals=None,locals=None,fromlist=None,level=-1):
|
def __call__(self, name, globals=None, locals=None, fromlist=None, level=-1):
|
||||||
"""
|
"""
|
||||||
The import method itself.
|
The import method itself.
|
||||||
"""
|
"""
|
||||||
globals = globals or {}
|
globals = globals or {}
|
||||||
locals = locals or {}
|
locals = locals or {}
|
||||||
fromlist = fromlist or []
|
fromlist = fromlist or []
|
||||||
if not hasattr(self.THREAD_LOCAL,'_modules_loaded'):
|
if not hasattr(self.THREAD_LOCAL, '_modules_loaded'):
|
||||||
self.THREAD_LOCAL._modules_loaded = set()
|
self.THREAD_LOCAL._modules_loaded = set()
|
||||||
try:
|
try:
|
||||||
# Check the date and reload if needed:
|
# Check the date and reload if needed:
|
||||||
@@ -119,7 +126,7 @@ class TrackImporter(object):
|
|||||||
self._update_dates(name, globals, locals, fromlist, level)
|
self._update_dates(name, globals, locals, fromlist, level)
|
||||||
return result
|
return result
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
raise # Don't hide something that went wrong
|
raise # Don't hide something that went wrong
|
||||||
|
|
||||||
def _update_dates(self, name, globals, locals, fromlist, level):
|
def _update_dates(self, name, globals, locals, fromlist, level):
|
||||||
"""
|
"""
|
||||||
@@ -143,7 +150,7 @@ class TrackImporter(object):
|
|||||||
date = self._import_dates.get(file)
|
date = self._import_dates.get(file)
|
||||||
new_date = None
|
new_date = None
|
||||||
reload_mod = False
|
reload_mod = False
|
||||||
mod_to_pack = False # Module turning into a package? (special case)
|
mod_to_pack = False # Module turning into a package? (special case)
|
||||||
try:
|
try:
|
||||||
new_date = os.path.getmtime(file)
|
new_date = os.path.getmtime(file)
|
||||||
except:
|
except:
|
||||||
@@ -154,13 +161,13 @@ class TrackImporter(object):
|
|||||||
# Get path without file ext:
|
# Get path without file ext:
|
||||||
file = os.path.splitext(file)[0]
|
file = os.path.splitext(file)[0]
|
||||||
reload_mod = os.path.isdir(file) \
|
reload_mod = os.path.isdir(file) \
|
||||||
and os.path.isfile(file+self.PACKAGE_PATH_SUFFIX)
|
and os.path.isfile(file + self.PACKAGE_PATH_SUFFIX)
|
||||||
mod_to_pack = reload_mod
|
mod_to_pack = reload_mod
|
||||||
else: # Package turning into module?
|
else: # Package turning into module?
|
||||||
file += ".py"
|
file += ".py"
|
||||||
reload_mod = os.path.isfile(file)
|
reload_mod = os.path.isfile(file)
|
||||||
if reload_mod:
|
if reload_mod:
|
||||||
new_date = os.path.getmtime(file) # Refresh file date
|
new_date = os.path.getmtime(file) # Refresh file date
|
||||||
if reload_mod or not date or new_date > date:
|
if reload_mod or not date or new_date > date:
|
||||||
self._import_dates[file] = new_date
|
self._import_dates[file] = new_date
|
||||||
if reload_mod or (date and new_date > date):
|
if reload_mod or (date and new_date > date):
|
||||||
@@ -168,7 +175,7 @@ class TrackImporter(object):
|
|||||||
if mod_to_pack:
|
if mod_to_pack:
|
||||||
# Module turning into a package:
|
# Module turning into a package:
|
||||||
mod_name = module.__name__
|
mod_name = module.__name__
|
||||||
del sys.modules[mod_name] # Delete the module
|
del sys.modules[mod_name] # Delete the module
|
||||||
# Reload the module:
|
# Reload the module:
|
||||||
NATIVE_IMPORTER(mod_name, globals, locals, [], level)
|
NATIVE_IMPORTER(mod_name, globals, locals, [], level)
|
||||||
else:
|
else:
|
||||||
@@ -182,7 +189,7 @@ class TrackImporter(object):
|
|||||||
file = getattr(module, "__file__", None)
|
file = getattr(module, "__file__", None)
|
||||||
if file:
|
if file:
|
||||||
# Make path absolute if not:
|
# Make path absolute if not:
|
||||||
file = os.path.splitext(file)[0]+".py" # Change .pyc for .py
|
file = os.path.splitext(file)[0] + ".py" # Change .pyc for .py
|
||||||
if file.endswith(self.PACKAGE_PATH_SUFFIX):
|
if file.endswith(self.PACKAGE_PATH_SUFFIX):
|
||||||
file = os.path.dirname(file) # Track dir for packages
|
file = os.path.dirname(file) # Track dir for packages
|
||||||
return file
|
return 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
@@ -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
|
||||||
|
|||||||
+27
-24
@@ -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.
|
||||||
@@ -9,16 +10,17 @@ Licensed under the PSF License
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# None represents a potentially variable byte. "##" in the XML spec...
|
# None represents a potentially variable byte. "##" in the XML spec...
|
||||||
autodetect_dict={ # bytepattern : ("name",
|
autodetect_dict = { # bytepattern : ("name",
|
||||||
(0x00, 0x00, 0xFE, 0xFF) : ("ucs4_be"),
|
(0x00, 0x00, 0xFE, 0xFF): ("ucs4_be"),
|
||||||
(0xFF, 0xFE, 0x00, 0x00) : ("ucs4_le"),
|
(0xFF, 0xFE, 0x00, 0x00): ("ucs4_le"),
|
||||||
(0xFE, 0xFF, None, None) : ("utf_16_be"),
|
(0xFE, 0xFF, None, None): ("utf_16_be"),
|
||||||
(0xFF, 0xFE, None, None) : ("utf_16_le"),
|
(0xFF, 0xFE, None, None): ("utf_16_le"),
|
||||||
(0x00, 0x3C, 0x00, 0x3F) : ("utf_16_be"),
|
(0x00, 0x3C, 0x00, 0x3F): ("utf_16_be"),
|
||||||
(0x3C, 0x00, 0x3F, 0x00) : ("utf_16_le"),
|
(0x3C, 0x00, 0x3F, 0x00): ("utf_16_le"),
|
||||||
(0x3C, 0x3F, 0x78, 0x6D): ("utf_8"),
|
(0x3C, 0x3F, 0x78, 0x6D): ("utf_8"),
|
||||||
(0x4C, 0x6F, 0xA7, 0x94): ("EBCDIC")
|
(0x4C, 0x6F, 0xA7, 0x94): ("EBCDIC")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def autoDetectXMLEncoding(buffer):
|
def autoDetectXMLEncoding(buffer):
|
||||||
""" buffer -> encoding_name
|
""" buffer -> encoding_name
|
||||||
@@ -31,14 +33,14 @@ def autoDetectXMLEncoding(buffer):
|
|||||||
# buffer at once but otherwise we'd have to decode a character at
|
# buffer at once but otherwise we'd have to decode a character at
|
||||||
# a time looking for the quote character...that's a pain
|
# a time looking for the quote character...that's a pain
|
||||||
|
|
||||||
encoding = "utf_8" # according to the XML spec, this is the default
|
encoding = "utf_8" # according to the XML spec, this is the default
|
||||||
# this code successively tries to refine the default
|
# this code successively tries to refine the default
|
||||||
# whenever it fails to refine, it falls back to
|
# whenever it fails to refine, it falls back to
|
||||||
# the last place encoding was set.
|
# the last place encoding was set.
|
||||||
if len(buffer)>=4:
|
if len(buffer) >= 4:
|
||||||
bytes = (byte1, byte2, byte3, byte4) = tuple(map(ord, buffer[0:4]))
|
bytes = (byte1, byte2, byte3, byte4) = tuple(map(ord, buffer[0:4]))
|
||||||
enc_info = autodetect_dict.get(bytes, None)
|
enc_info = autodetect_dict.get(bytes, None)
|
||||||
if not enc_info: # try autodetection again removing potentially
|
if not enc_info: # try autodetection again removing potentially
|
||||||
# variable bytes
|
# variable bytes
|
||||||
bytes = (byte1, byte2, None, None)
|
bytes = (byte1, byte2, None, None)
|
||||||
enc_info = autodetect_dict.get(bytes)
|
enc_info = autodetect_dict.get(bytes)
|
||||||
@@ -46,29 +48,30 @@ def autoDetectXMLEncoding(buffer):
|
|||||||
enc_info = None
|
enc_info = None
|
||||||
|
|
||||||
if enc_info:
|
if enc_info:
|
||||||
encoding = enc_info # we've got a guess... these are
|
encoding = enc_info # we've got a guess... these are
|
||||||
#the new defaults
|
#the new defaults
|
||||||
|
|
||||||
# try to find a more precise encoding using xml declaration
|
# try to find a more precise encoding using xml declaration
|
||||||
secret_decoder_ring = codecs.lookup(encoding)[1]
|
secret_decoder_ring = codecs.lookup(encoding)[1]
|
||||||
(decoded,length) = secret_decoder_ring(buffer)
|
(decoded, length) = secret_decoder_ring(buffer)
|
||||||
first_line = decoded.split("\n")[0]
|
first_line = decoded.split("\n")[0]
|
||||||
if first_line and first_line.startswith(u"<?xml"):
|
if first_line and first_line.startswith(u"<?xml"):
|
||||||
encoding_pos = first_line.find(u"encoding")
|
encoding_pos = first_line.find(u"encoding")
|
||||||
if encoding_pos!=-1:
|
if encoding_pos != -1:
|
||||||
# look for double quote
|
# look for double quote
|
||||||
quote_pos=first_line.find('"', encoding_pos)
|
quote_pos = first_line.find('"', encoding_pos)
|
||||||
|
|
||||||
if quote_pos==-1: # look for single quote
|
if quote_pos == -1: # look for single quote
|
||||||
quote_pos=first_line.find("'", encoding_pos)
|
quote_pos = first_line.find("'", encoding_pos)
|
||||||
|
|
||||||
if quote_pos>-1:
|
if quote_pos > -1:
|
||||||
quote_char,rest=(first_line[quote_pos],
|
quote_char, rest = (first_line[quote_pos],
|
||||||
first_line[quote_pos+1:])
|
first_line[quote_pos + 1:])
|
||||||
encoding=rest[:rest.find(quote_char)]
|
encoding = rest[:rest.find(quote_char)]
|
||||||
|
|
||||||
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')
|
||||||
+40
-24
@@ -40,15 +40,17 @@ __all__ = [
|
|||||||
'w2p_unpack_plugin',
|
'w2p_unpack_plugin',
|
||||||
'fix_newlines',
|
'fix_newlines',
|
||||||
'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)
|
||||||
a,b,c = int(m.group(1)),int(m.group(2)),int(m.group(3)),
|
a, b, c = int(m.group(1)), int(m.group(2)), int(m.group(3)),
|
||||||
s = m.group('type') or 'dev'
|
s = m.group('type') or 'dev'
|
||||||
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."
|
||||||
@@ -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,25 +69,28 @@ 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')
|
||||||
|
|
||||||
|
|
||||||
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='^.+$',
|
||||||
drop=True,
|
drop=True,
|
||||||
add_dirs=False,
|
add_dirs=False,
|
||||||
sort=True,
|
sort=True,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
like os.listdir() but you can specify a regex pattern to filter files.
|
like os.listdir() but you can specify a regex pattern to filter files.
|
||||||
if add_dirs is True, the returned items will have the full path.
|
if add_dirs is True, the returned items will have the full path.
|
||||||
@@ -115,7 +121,7 @@ def listdir(
|
|||||||
def recursive_unlink(f):
|
def recursive_unlink(f):
|
||||||
if os.path.isdir(f):
|
if os.path.isdir(f):
|
||||||
for s in os.listdir(f):
|
for s in os.listdir(f):
|
||||||
recursive_unlink(os.path.join(f,s))
|
recursive_unlink(os.path.join(f, s))
|
||||||
os.rmdir(f)
|
os.rmdir(f)
|
||||||
elif os.path.isfile(f):
|
elif os.path.isfile(f):
|
||||||
os.unlink(f)
|
os.unlink(f)
|
||||||
@@ -130,7 +136,7 @@ def cleanpath(path):
|
|||||||
items = path.split('.')
|
items = path.split('.')
|
||||||
if len(items) > 1:
|
if len(items) > 1:
|
||||||
path = re.sub('[^\w\.]+', '_', '_'.join(items[:-1]) + '.'
|
path = re.sub('[^\w\.]+', '_', '_'.join(items[:-1]) + '.'
|
||||||
+ ''.join(items[-1:]))
|
+ ''.join(items[-1:]))
|
||||||
else:
|
else:
|
||||||
path = re.sub('[^\w\.]+', '_', ''.join(items[-1:]))
|
path = re.sub('[^\w\.]+', '_', ''.join(items[-1:]))
|
||||||
return path
|
return path
|
||||||
@@ -161,7 +167,7 @@ def _extractall(filename, path='.', members=None):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
os.makedirs(os.path.join(path,
|
os.makedirs(os.path.join(path,
|
||||||
tarinfo.name), 0777)
|
tarinfo.name), 0777)
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
pass
|
pass
|
||||||
directories.append(tarinfo)
|
directories.append(tarinfo)
|
||||||
@@ -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,16 +269,18 @@ 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))
|
||||||
for file in files1+files2:
|
files2 = glob.glob(
|
||||||
plugin_tarball.add(file, arcname=file[len(app_dir)+1:])
|
os.path.join(app_dir, '*/plugin_%s/*' % plugin_name))
|
||||||
|
for file in files1 + files2:
|
||||||
|
plugin_tarball.add(file, arcname=file[len(app_dir) + 1:])
|
||||||
finally:
|
finally:
|
||||||
plugin_tarball.close()
|
plugin_tarball.close()
|
||||||
|
|
||||||
@@ -279,8 +289,8 @@ 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)
|
||||||
|
|
||||||
|
|
||||||
def tar_compiled(file, dir, expression='^.+$'):
|
def tar_compiled(file, dir, expression='^.+$'):
|
||||||
@@ -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))
|
||||||
|
|
||||||
@@ -317,13 +328,13 @@ def get_session(request, other_application='admin'):
|
|||||||
try:
|
try:
|
||||||
session_id = request.cookies['session_id_' + other_application].value
|
session_id = request.cookies['session_id_' + other_application].value
|
||||||
osession = storage.load_storage(os.path.join(
|
osession = storage.load_storage(os.path.join(
|
||||||
up(request.folder), other_application, 'sessions', session_id))
|
up(request.folder), other_application, 'sessions', session_id))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
osession = storage.Storage()
|
osession = storage.Storage()
|
||||||
return osession
|
return osession
|
||||||
|
|
||||||
|
|
||||||
def check_credentials(request, other_application='admin', expiration = 60*60):
|
def check_credentials(request, other_application='admin', expiration=60 * 60):
|
||||||
""" checks that user is authorized to access other_application"""
|
""" checks that user is authorized to access other_application"""
|
||||||
if request.env.web2py_runtime_gae:
|
if request.env.web2py_runtime_gae:
|
||||||
from google.appengine.api import users
|
from google.appengine.api import users
|
||||||
@@ -338,6 +349,7 @@ def check_credentials(request, other_application='admin', expiration = 60*60):
|
|||||||
s = get_session(request, other_application)
|
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,12 +360,13 @@ 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,
|
||||||
size,
|
size,
|
||||||
chunk_size=10 ** 5,
|
chunk_size=10 ** 5,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
this is here because I think there is a bug in shutil.copyfileobj
|
this is here because I think there is a bug in shutil.copyfileobj
|
||||||
"""
|
"""
|
||||||
@@ -379,13 +392,16 @@ def make_fake_file_like_object():
|
|||||||
class LogFile(object):
|
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()
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
+113
-87
@@ -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):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -82,7 +83,7 @@ class Request(Storage):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Storage.__init__(self)
|
Storage.__init__(self)
|
||||||
self.wsgi = Storage() # hooks to environ and start_response
|
self.wsgi = Storage() # hooks to environ and start_response
|
||||||
self.env = Storage()
|
self.env = Storage()
|
||||||
self.cookies = Cookie.SimpleCookie()
|
self.cookies = Cookie.SimpleCookie()
|
||||||
self.get_vars = Storage()
|
self.get_vars = Storage()
|
||||||
@@ -116,8 +117,8 @@ class Request(Storage):
|
|||||||
if session:
|
if session:
|
||||||
session._user_agent = user_agent
|
session._user_agent = user_agent
|
||||||
user_agent = Storage(user_agent)
|
user_agent = Storage(user_agent)
|
||||||
for key,value in user_agent.items():
|
for key, value in user_agent.items():
|
||||||
if isinstance(value,dict):
|
if isinstance(value, dict):
|
||||||
user_agent[key] = Storage(value)
|
user_agent[key] = Storage(value)
|
||||||
return user_agent
|
return user_agent
|
||||||
|
|
||||||
@@ -133,25 +134,26 @@ class Request(Storage):
|
|||||||
current.session.secure()
|
current.session.secure()
|
||||||
|
|
||||||
def restful(self):
|
def restful(self):
|
||||||
def wrapper(action,self=self):
|
def wrapper(action, self=self):
|
||||||
def f(_action=action,_self=self,*a,**b):
|
def f(_action=action, _self=self, *a, **b):
|
||||||
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']:
|
||||||
raise HTTP(400,"invalid method")
|
raise HTTP(400, "invalid method")
|
||||||
rest_action = _action().get(method,None)
|
rest_action = _action().get(method, None)
|
||||||
if not rest_action:
|
if not rest_action:
|
||||||
raise HTTP(400,"method not supported")
|
raise HTTP(400, "method not supported")
|
||||||
try:
|
try:
|
||||||
return rest_action(*_self.args,**_self.vars)
|
return rest_action(*_self.args, **_self.vars)
|
||||||
except TypeError, e:
|
except TypeError, e:
|
||||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||||
if len(traceback.extract_tb(exc_traceback))==1:
|
if len(traceback.extract_tb(exc_traceback)) == 1:
|
||||||
raise HTTP(400,"invalid arguments")
|
raise HTTP(400, "invalid arguments")
|
||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
f.__doc__ = action.__doc__
|
f.__doc__ = action.__doc__
|
||||||
@@ -180,8 +182,8 @@ class Response(Storage):
|
|||||||
self.meta = Storage() # used by web2py_ajax.html
|
self.meta = Storage() # used by web2py_ajax.html
|
||||||
self.menu = [] # used by the default view layout
|
self.menu = [] # used by the default view layout
|
||||||
self.files = [] # used by web2py_ajax.html
|
self.files = [] # used by web2py_ajax.html
|
||||||
self.generic_patterns = [] # patterns to allow generic views
|
self.generic_patterns = [] # patterns to allow generic views
|
||||||
self.delimiters = ('{{','}}')
|
self.delimiters = ('{{', '}}')
|
||||||
self._vars = None
|
self._vars = None
|
||||||
self._caller = lambda f: f()
|
self._caller = lambda f: f()
|
||||||
self._view_environment = None
|
self._view_environment = None
|
||||||
@@ -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):
|
||||||
@@ -225,9 +228,9 @@ class Response(Storage):
|
|||||||
|
|
||||||
def include_meta(self):
|
def include_meta(self):
|
||||||
s = '\n'.join(
|
s = '\n'.join(
|
||||||
'<meta name="%s" content="%s" />\n' % (k,xmlescape(v))
|
'<meta name="%s" content="%s" />\n' % (k, xmlescape(v))
|
||||||
for k,v in (self.meta or {}).iteritems())
|
for k, v in (self.meta or {}).iteritems())
|
||||||
self.write(s,escape=False)
|
self.write(s, escape=False)
|
||||||
|
|
||||||
def include_files(self, extensions=None):
|
def include_files(self, extensions=None):
|
||||||
|
|
||||||
@@ -256,44 +259,52 @@ class Response(Storage):
|
|||||||
# cache for 5 minutes by default
|
# cache for 5 minutes by default
|
||||||
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'),
|
||||||
current.request.folder,
|
current.request.folder,
|
||||||
self.optimize_css,
|
self.optimize_css,
|
||||||
self.optimize_js)
|
self.optimize_js)
|
||||||
if cache:
|
if cache:
|
||||||
cache_model, time_expire = cache
|
cache_model, time_expire = cache
|
||||||
files = cache_model('response.files.minified/'+key,
|
files = cache_model('response.files.minified/' + key,
|
||||||
call_minify,
|
call_minify,
|
||||||
time_expire)
|
time_expire)
|
||||||
else:
|
else:
|
||||||
files = call_minify()
|
files = call_minify()
|
||||||
s = ''
|
s = ''
|
||||||
for item in files:
|
for item in files:
|
||||||
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'):
|
||||||
elif isinstance(item,(list,tuple)):
|
s += js_template % item
|
||||||
|
elif f.endswith('.coffee'):
|
||||||
|
s += coffee_template % item
|
||||||
|
elif f.endswith('.less'):
|
||||||
|
s += less_template % item
|
||||||
|
elif isinstance(item, (list, tuple)):
|
||||||
f = item[0]
|
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(
|
||||||
self,
|
self,
|
||||||
stream,
|
stream,
|
||||||
chunk_size = DEFAULT_CHUNK_SIZE,
|
chunk_size=DEFAULT_CHUNK_SIZE,
|
||||||
request=None,
|
request=None,
|
||||||
attachment=False,
|
attachment=False,
|
||||||
filename=None,
|
filename=None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
if a controller function::
|
if a controller function::
|
||||||
|
|
||||||
@@ -347,7 +358,7 @@ class Response(Storage):
|
|||||||
|
|
||||||
env = request.env
|
env = request.env
|
||||||
# Internet Explorer < 9.0 will not allow downloads over SSL unless caching is enabled
|
# Internet Explorer < 9.0 will not allow downloads over SSL unless caching is enabled
|
||||||
if request.is_https and isinstance(env.http_user_agent,str) and \
|
if request.is_https and isinstance(env.http_user_agent, str) and \
|
||||||
not re.search(r'Opera', env.http_user_agent) and \
|
not re.search(r'Opera', env.http_user_agent) and \
|
||||||
re.search(r'MSIE [5-8][^0-9]', env.http_user_agent):
|
re.search(r'MSIE [5-8][^0-9]', env.http_user_agent):
|
||||||
headers['Pragma'] = 'cache'
|
headers['Pragma'] = 'cache'
|
||||||
@@ -359,7 +370,7 @@ class Response(Storage):
|
|||||||
wrapped = streamer(stream, chunk_size=chunk_size)
|
wrapped = streamer(stream, chunk_size=chunk_size)
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
def download(self, request, db, chunk_size = DEFAULT_CHUNK_SIZE, attachment=True):
|
def download(self, request, db, chunk_size=DEFAULT_CHUNK_SIZE, attachment=True):
|
||||||
"""
|
"""
|
||||||
example of usage in controller::
|
example of usage in controller::
|
||||||
|
|
||||||
@@ -373,7 +384,7 @@ class Response(Storage):
|
|||||||
raise HTTP(404)
|
raise HTTP(404)
|
||||||
name = request.args[-1]
|
name = request.args[-1]
|
||||||
items = re.compile('(?P<table>.*?)\.(?P<field>.*?)\..*')\
|
items = re.compile('(?P<table>.*?)\.(?P<field>.*?)\..*')\
|
||||||
.match(name)
|
.match(name)
|
||||||
if not items:
|
if not items:
|
||||||
raise HTTP(404)
|
raise HTTP(404)
|
||||||
(t, f) = (items.group('table'), items.group('field'))
|
(t, f) = (items.group('table'), items.group('field'))
|
||||||
@@ -389,9 +400,8 @@ 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)
|
||||||
|
|
||||||
def xmlrpc(self, request, methods):
|
def xmlrpc(self, request, methods):
|
||||||
"""
|
"""
|
||||||
@@ -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)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -418,14 +429,14 @@ class Response(Storage):
|
|||||||
def toolbar(self):
|
def toolbar(self):
|
||||||
from html import DIV, SCRIPT, BEAUTIFY, TAG, URL, A
|
from html import DIV, SCRIPT, BEAUTIFY, TAG, URL, A
|
||||||
BUTTON = TAG.button
|
BUTTON = TAG.button
|
||||||
admin = URL("admin","default","design",
|
admin = URL("admin", "default", "design",
|
||||||
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':
|
||||||
sorted(list(set(i.db.tables) -
|
sorted(list(set(i.db.tables) -
|
||||||
set(i.db._LAZY_TABLES.keys()))) or
|
set(i.db._LAZY_TABLES.keys()))) or
|
||||||
@@ -434,25 +445,35 @@ class Response(Storage):
|
|||||||
'[no lazy tables]'})
|
'[no lazy tables]'})
|
||||||
for i in THREAD_LOCAL.instances])
|
for i in THREAD_LOCAL.instances])
|
||||||
else:
|
else:
|
||||||
dbstats = [] # if no db or on GAE
|
dbstats = [] # if no db or on GAE
|
||||||
dbtables = {}
|
dbtables = {}
|
||||||
u = web2py_uuid()
|
u = web2py_uuid()
|
||||||
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):
|
||||||
|
|
||||||
@@ -468,19 +489,21 @@ class Session(Storage):
|
|||||||
tablename='web2py_session',
|
tablename='web2py_session',
|
||||||
masterapp=None,
|
masterapp=None,
|
||||||
migrate=True,
|
migrate=True,
|
||||||
separate = None,
|
separate=None,
|
||||||
check_client=False,
|
check_client=False,
|
||||||
cookie_key=None,
|
cookie_key=None,
|
||||||
cookie_expires=None,
|
cookie_expires=None,
|
||||||
compression_level=None
|
compression_level=None
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
separate can be separate=lambda(session_name): session_name[-2:]
|
separate can be separate=lambda(session_name): session_name[-2:]
|
||||||
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
|
||||||
@@ -527,7 +551,7 @@ class Session(Storage):
|
|||||||
if regex_session_id.match(response.session_id):
|
if regex_session_id.match(response.session_id):
|
||||||
response.session_filename = \
|
response.session_filename = \
|
||||||
os.path.join(up(request.folder), masterapp,
|
os.path.join(up(request.folder), masterapp,
|
||||||
'sessions', response.session_id)
|
'sessions', response.session_id)
|
||||||
else:
|
else:
|
||||||
response.session_id = None
|
response.session_id = None
|
||||||
# do not try load the data from file is these was data in cookie
|
# do not try load the data from file is these was data in cookie
|
||||||
@@ -543,8 +567,8 @@ class Session(Storage):
|
|||||||
response.session_file.seek(0)
|
response.session_file.seek(0)
|
||||||
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.
|
||||||
@@ -557,7 +581,7 @@ class Session(Storage):
|
|||||||
if separate:
|
if separate:
|
||||||
prefix = separate(response.session_id)
|
prefix = separate(response.session_id)
|
||||||
response.session_id = '%s/%s' % \
|
response.session_id = '%s/%s' % \
|
||||||
(prefix,response.session_id)
|
(prefix, response.session_id)
|
||||||
response.session_filename = \
|
response.session_filename = \
|
||||||
os.path.join(up(request.folder), masterapp,
|
os.path.join(up(request.folder), masterapp,
|
||||||
'sessions', response.session_id)
|
'sessions', response.session_id)
|
||||||
@@ -585,25 +609,25 @@ class Session(Storage):
|
|||||||
Field('locked', 'boolean', default=False),
|
Field('locked', 'boolean', default=False),
|
||||||
Field('client_ip', length=64),
|
Field('client_ip', length=64),
|
||||||
Field('created_datetime', 'datetime',
|
Field('created_datetime', 'datetime',
|
||||||
default=request.now),
|
default=request.now),
|
||||||
Field('modified_datetime', 'datetime'),
|
Field('modified_datetime', 'datetime'),
|
||||||
Field('unique_key', length=64),
|
Field('unique_key', length=64),
|
||||||
Field('session_data', 'blob'),
|
Field('session_data', 'blob'),
|
||||||
migrate=table_migrate,
|
migrate=table_migrate,
|
||||||
)
|
)
|
||||||
table = db[tname] # to allow for lazy table
|
table = db[tname] # to allow for lazy table
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# 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:
|
||||||
@@ -637,10 +662,10 @@ class Session(Storage):
|
|||||||
self._start_timestamp = datetime.datetime.today()
|
self._start_timestamp = datetime.datetime.today()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def is_expired(self, seconds = 3600):
|
def is_expired(self, seconds=3600):
|
||||||
now = datetime.datetime.today()
|
now = datetime.datetime.today()
|
||||||
if not self._last_timestamp or \
|
if not self._last_timestamp or \
|
||||||
self._last_timestamp + datetime.timedelta(seconds = seconds) > now:
|
self._last_timestamp + datetime.timedelta(seconds=seconds) > now:
|
||||||
self._last_timestamp = now
|
self._last_timestamp = now
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
@@ -654,12 +679,13 @@ 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
|
||||||
rcookies = response.cookies
|
rcookies = response.cookies
|
||||||
rcookies.pop(name,None)
|
rcookies.pop(name, None)
|
||||||
rcookies[name] = value
|
rcookies[name] = value
|
||||||
rcookies[name]['path'] = '/'
|
rcookies[name]['path'] = '/'
|
||||||
if expires:
|
if expires:
|
||||||
@@ -667,7 +693,7 @@ class Session(Storage):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def _unchanged(self):
|
def _unchanged(self):
|
||||||
previous_session_hash = self.pop('_session_hash',None)
|
previous_session_hash = self.pop('_session_hash', None)
|
||||||
if not previous_session_hash and not \
|
if not previous_session_hash and not \
|
||||||
any(value is not None for value in self.itervalues()):
|
any(value is not None for value in self.itervalues()):
|
||||||
return True
|
return True
|
||||||
@@ -683,7 +709,7 @@ class Session(Storage):
|
|||||||
# don't save if file-based sessions,
|
# don't save if file-based sessions,
|
||||||
# no session id, or session being forgotten
|
# no session id, or session being forgotten
|
||||||
# or no changes to session
|
# or no changes to session
|
||||||
if response.session_storage_type!='db' or not response.session_id \
|
if response.session_storage_type != 'db' or not response.session_id \
|
||||||
or self._forget or self._unchanged():
|
or self._forget or self._unchanged():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -692,7 +718,7 @@ class Session(Storage):
|
|||||||
unique_key = response.session_db_unique_key
|
unique_key = response.session_db_unique_key
|
||||||
|
|
||||||
dd = dict(locked=False,
|
dd = dict(locked=False,
|
||||||
client_ip=request.client.replace(':','.'),
|
client_ip=request.client.replace(':', '.'),
|
||||||
modified_datetime=request.now,
|
modified_datetime=request.now,
|
||||||
session_data=cPickle.dumps(dict(self)),
|
session_data=cPickle.dumps(dict(self)),
|
||||||
unique_key=unique_key)
|
unique_key=unique_key)
|
||||||
@@ -708,11 +734,11 @@ class Session(Storage):
|
|||||||
|
|
||||||
def _try_store_in_cookie_or_file(self, request, response):
|
def _try_store_in_cookie_or_file(self, request, response):
|
||||||
return \
|
return \
|
||||||
self._try_store_in_cookie(request,response) or \
|
self._try_store_in_cookie(request, response) or \
|
||||||
self._try_store_in_file(request,response)
|
self._try_store_in_file(request, response)
|
||||||
|
|
||||||
def _try_store_in_file(self, request, response):
|
def _try_store_in_file(self, request, response):
|
||||||
if response.session_storage_type!='file':
|
if response.session_storage_type != 'file':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -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):
|
||||||
|
|||||||
+41
-39
@@ -24,7 +24,7 @@ class Highlighter(object):
|
|||||||
mode,
|
mode,
|
||||||
link=None,
|
link=None,
|
||||||
styles=None,
|
styles=None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Initialise highlighter:
|
Initialise highlighter:
|
||||||
mode = language (PYTHON, WEB2PY,C, CPP, HTML, HTML_PLAIN)
|
mode = language (PYTHON, WEB2PY,C, CPP, HTML, HTML_PLAIN)
|
||||||
@@ -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(
|
||||||
@@ -58,7 +58,7 @@ class Highlighter(object):
|
|||||||
token,
|
token,
|
||||||
match,
|
match,
|
||||||
style,
|
style,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Callback for C specific highlighting.
|
Callback for C specific highlighting.
|
||||||
"""
|
"""
|
||||||
@@ -72,7 +72,7 @@ class Highlighter(object):
|
|||||||
token,
|
token,
|
||||||
match,
|
match,
|
||||||
style,
|
style,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Callback for python specific highlighting.
|
Callback for python specific highlighting.
|
||||||
"""
|
"""
|
||||||
@@ -93,10 +93,10 @@ class Highlighter(object):
|
|||||||
(url, style) = style[5:].split(';', 1)
|
(url, style) = style[5:].split(';', 1)
|
||||||
if url == 'None' or url == '':
|
if url == 'None' or url == '':
|
||||||
self.output.append('<span style="%s">%s</span>'
|
self.output.append('<span style="%s">%s</span>'
|
||||||
% (style, value))
|
% (style, value))
|
||||||
else:
|
else:
|
||||||
self.output.append('<a href="%s%s" style="%s">%s</a>'
|
self.output.append('<a href="%s%s" style="%s">%s</a>'
|
||||||
% (url, value, style, value))
|
% (url, value, style, value))
|
||||||
else:
|
else:
|
||||||
self.change_style(token, style)
|
self.change_style(token, style)
|
||||||
self.output.append(value)
|
self.output.append(value)
|
||||||
@@ -109,7 +109,7 @@ class Highlighter(object):
|
|||||||
token,
|
token,
|
||||||
match,
|
match,
|
||||||
style,
|
style,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Callback for HTML specific highlighting.
|
Callback for HTML specific highlighting.
|
||||||
"""
|
"""
|
||||||
@@ -135,51 +135,51 @@ class Highlighter(object):
|
|||||||
re.compile(r'0x[0-9a-fA-F]+|[+-]?\d+(\.\d+)?([eE][+-]\d+)?|\d+'),
|
re.compile(r'0x[0-9a-fA-F]+|[+-]?\d+(\.\d+)?([eE][+-]\d+)?|\d+'),
|
||||||
'color: red'),
|
'color: red'),
|
||||||
('KEYWORD', re.compile(r'(sizeof|int|long|short|char|void|'
|
('KEYWORD', re.compile(r'(sizeof|int|long|short|char|void|'
|
||||||
+ r'signed|unsigned|float|double|'
|
+ r'signed|unsigned|float|double|'
|
||||||
+ r'goto|break|return|continue|asm|'
|
+ r'goto|break|return|continue|asm|'
|
||||||
+ r'case|default|if|else|switch|while|for|do|'
|
+ r'case|default|if|else|switch|while|for|do|'
|
||||||
+ r'struct|union|enum|typedef|'
|
+ r'struct|union|enum|typedef|'
|
||||||
+ r'static|register|auto|volatile|extern|const)(?![a-zA-Z0-9_])'),
|
+ r'static|register|auto|volatile|extern|const)(?![a-zA-Z0-9_])'),
|
||||||
'color:#185369; font-weight: bold'),
|
'color:#185369; font-weight: bold'),
|
||||||
('CPPKEYWORD',
|
('CPPKEYWORD',
|
||||||
re.compile(r'(class|private|protected|public|template|new|delete|'
|
re.compile(r'(class|private|protected|public|template|new|delete|'
|
||||||
+ r'this|friend|using|inline|export|bool|throw|try|catch|'
|
+ r'this|friend|using|inline|export|bool|throw|try|catch|'
|
||||||
+ r'operator|typeid|virtual)(?![a-zA-Z0-9_])'),
|
+ r'operator|typeid|virtual)(?![a-zA-Z0-9_])'),
|
||||||
'color: blue; font-weight: bold'),
|
'color: blue; font-weight: bold'),
|
||||||
('STRING', re.compile(r'r?u?\'(.*?)(?<!\\)\'|"(.*?)(?<!\\)"'),
|
('STRING', re.compile(r'r?u?\'(.*?)(?<!\\)\'|"(.*?)(?<!\\)"'),
|
||||||
'color: #FF9966'),
|
'color: #FF9966'),
|
||||||
('IDENTIFIER', re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*'),
|
('IDENTIFIER', re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*'),
|
||||||
None),
|
None),
|
||||||
('WHITESPACE', re.compile(r'[ \r\n]+'), 'Keep'),
|
('WHITESPACE', re.compile(r'[ \r\n]+'), 'Keep'),
|
||||||
)),
|
)),
|
||||||
'PYTHON': (python_tokenizer, (
|
'PYTHON': (python_tokenizer, (
|
||||||
('GOTOHTML', re.compile(r'\}\}'), 'color: red'),
|
('GOTOHTML', re.compile(r'\}\}'), 'color: red'),
|
||||||
('PUNC', re.compile(r'[-+*!|&^~/%\=<>\[\]{}(),.:]'),
|
('PUNC', re.compile(r'[-+*!|&^~/%\=<>\[\]{}(),.:]'),
|
||||||
'font-weight: bold'),
|
'font-weight: bold'),
|
||||||
('NUMBER',
|
('NUMBER',
|
||||||
re.compile(r'0x[0-9a-fA-F]+|[+-]?\d+(\.\d+)?([eE][+-]\d+)?|\d+'
|
re.compile(r'0x[0-9a-fA-F]+|[+-]?\d+(\.\d+)?([eE][+-]\d+)?|\d+'
|
||||||
), 'color: red'),
|
), 'color: red'),
|
||||||
('KEYWORD',
|
('KEYWORD',
|
||||||
re.compile(r'(def|class|break|continue|del|exec|finally|pass|'
|
re.compile(r'(def|class|break|continue|del|exec|finally|pass|'
|
||||||
+ r'print|raise|return|try|except|global|assert|lambda|'
|
+ r'print|raise|return|try|except|global|assert|lambda|'
|
||||||
+ r'yield|for|while|if|elif|else|and|in|is|not|or|import|'
|
+ r'yield|for|while|if|elif|else|and|in|is|not|or|import|'
|
||||||
+ r'from|True|False)(?![a-zA-Z0-9_])'),
|
+ r'from|True|False)(?![a-zA-Z0-9_])'),
|
||||||
'color:#185369; font-weight: bold'),
|
'color:#185369; font-weight: bold'),
|
||||||
('WEB2PY',
|
('WEB2PY',
|
||||||
re.compile(r'(request|response|session|cache|redirect|local_import|HTTP|TR|XML|URL|BEAUTIFY|A|BODY|BR|B|CAT|CENTER|CODE|COL|COLGROUP|DIV|EM|EMBED|FIELDSET|LEGEND|FORM|H1|H2|H3|H4|H5|H6|IFRAME|HEAD|HR|HTML|I|IMG|INPUT|LABEL|LI|LINK|MARKMIN|MENU|META|OBJECT|OL|ON|OPTION|P|PRE|SCRIPT|SELECT|SPAN|STYLE|TABLE|THEAD|TBODY|TFOOT|TAG|TD|TEXTAREA|TH|TITLE|TT|T|UL|XHTML|IS_SLUG|IS_STRONG|IS_LOWER|IS_UPPER|IS_ALPHANUMERIC|IS_DATETIME|IS_DATETIME_IN_RANGE|IS_DATE|IS_DATE_IN_RANGE|IS_DECIMAL_IN_RANGE|IS_EMAIL|IS_EXPR|IS_FLOAT_IN_RANGE|IS_IMAGE|IS_INT_IN_RANGE|IS_IN_SET|IS_IPV4|IS_LIST_OF|IS_LENGTH|IS_MATCH|IS_EQUAL_TO|IS_EMPTY_OR|IS_NULL_OR|IS_NOT_EMPTY|IS_TIME|IS_UPLOAD_FILENAME|IS_URL|CLEANUP|CRYPT|IS_IN_DB|IS_NOT_IN_DB|DAL|Field|SQLFORM|SQLTABLE|xmlescape|embed64)(?![a-zA-Z0-9_])'
|
re.compile(r'(request|response|session|cache|redirect|local_import|HTTP|TR|XML|URL|BEAUTIFY|A|BODY|BR|B|CAT|CENTER|CODE|COL|COLGROUP|DIV|EM|EMBED|FIELDSET|LEGEND|FORM|H1|H2|H3|H4|H5|H6|IFRAME|HEAD|HR|HTML|I|IMG|INPUT|LABEL|LI|LINK|MARKMIN|MENU|META|OBJECT|OL|ON|OPTION|P|PRE|SCRIPT|SELECT|SPAN|STYLE|TABLE|THEAD|TBODY|TFOOT|TAG|TD|TEXTAREA|TH|TITLE|TT|T|UL|XHTML|IS_SLUG|IS_STRONG|IS_LOWER|IS_UPPER|IS_ALPHANUMERIC|IS_DATETIME|IS_DATETIME_IN_RANGE|IS_DATE|IS_DATE_IN_RANGE|IS_DECIMAL_IN_RANGE|IS_EMAIL|IS_EXPR|IS_FLOAT_IN_RANGE|IS_IMAGE|IS_INT_IN_RANGE|IS_IN_SET|IS_IPV4|IS_LIST_OF|IS_LENGTH|IS_MATCH|IS_EQUAL_TO|IS_EMPTY_OR|IS_NULL_OR|IS_NOT_EMPTY|IS_TIME|IS_UPLOAD_FILENAME|IS_URL|CLEANUP|CRYPT|IS_IN_DB|IS_NOT_IN_DB|DAL|Field|SQLFORM|SQLTABLE|xmlescape|embed64)(?![a-zA-Z0-9_])'
|
||||||
), 'link:%(link)s;text-decoration:None;color:#FF5C1F;'),
|
), 'link:%(link)s;text-decoration:None;color:#FF5C1F;'),
|
||||||
('MAGIC', re.compile(r'self|None'),
|
('MAGIC', re.compile(r'self|None'),
|
||||||
'color:#185369; font-weight: bold'),
|
'color:#185369; font-weight: bold'),
|
||||||
('MULTILINESTRING', re.compile(r'r?u?(\'\'\'|""")'),
|
('MULTILINESTRING', re.compile(r'r?u?(\'\'\'|""")'),
|
||||||
'color: #FF9966'),
|
'color: #FF9966'),
|
||||||
('STRING', re.compile(r'r?u?\'(.*?)(?<!\\)\'|"(.*?)(?<!\\)"'
|
('STRING', re.compile(r'r?u?\'(.*?)(?<!\\)\'|"(.*?)(?<!\\)"'
|
||||||
), 'color: #FF9966'),
|
), 'color: #FF9966'),
|
||||||
('IDENTIFIER', re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*'),
|
('IDENTIFIER', re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*'),
|
||||||
None),
|
None),
|
||||||
('COMMENT', re.compile(r'\#.*\r?\n'),
|
('COMMENT', re.compile(r'\#.*\r?\n'),
|
||||||
'color: green; font-style: italic'),
|
'color: green; font-style: italic'),
|
||||||
('WHITESPACE', re.compile(r'[ \r\n]+'), 'Keep'),
|
('WHITESPACE', re.compile(r'[ \r\n]+'), 'Keep'),
|
||||||
)),
|
)),
|
||||||
'PYTHONMultilineString': (python_tokenizer,
|
'PYTHONMultilineString': (python_tokenizer,
|
||||||
(('ENDMULTILINESTRING',
|
(('ENDMULTILINESTRING',
|
||||||
re.compile(r'.*?("""|\'\'\')',
|
re.compile(r'.*?("""|\'\'\')',
|
||||||
@@ -191,13 +191,13 @@ class Highlighter(object):
|
|||||||
('XMLCRAP', re.compile(r'<![^>]*>'),
|
('XMLCRAP', re.compile(r'<![^>]*>'),
|
||||||
'color: blue; font-style: italic'),
|
'color: blue; font-style: italic'),
|
||||||
('SCRIPT', re.compile(r'<script .*?</script>', re.IGNORECASE
|
('SCRIPT', re.compile(r'<script .*?</script>', re.IGNORECASE
|
||||||
+ re.DOTALL), 'color: black'),
|
+ re.DOTALL), 'color: black'),
|
||||||
('TAG', re.compile(r'</?\s*[a-zA-Z0-9]+'),
|
('TAG', re.compile(r'</?\s*[a-zA-Z0-9]+'),
|
||||||
'color: darkred; font-weight: bold'),
|
'color: darkred; font-weight: bold'),
|
||||||
('ENDTAG', re.compile(r'/?>'),
|
('ENDTAG', re.compile(r'/?>'),
|
||||||
'color: darkred; font-weight: bold'),
|
'color: darkred; font-weight: bold'),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
|
|
||||||
def highlight(self, data):
|
def highlight(self, data):
|
||||||
"""
|
"""
|
||||||
@@ -215,12 +215,12 @@ class Highlighter(object):
|
|||||||
if style:
|
if style:
|
||||||
new_mode = \
|
new_mode = \
|
||||||
Highlighter.all_styles[mode][0](self,
|
Highlighter.all_styles[mode][0](self,
|
||||||
token, match, style
|
token, match, style
|
||||||
% dict(link=self.link))
|
% dict(link=self.link))
|
||||||
else:
|
else:
|
||||||
new_mode = \
|
new_mode = \
|
||||||
Highlighter.all_styles[mode][0](self,
|
Highlighter.all_styles[mode][0](self,
|
||||||
token, match, style)
|
token, match, style)
|
||||||
if not new_mode is None:
|
if not new_mode is None:
|
||||||
mode = new_mode
|
mode = new_mode
|
||||||
i += max(1, len(match.group()))
|
i += max(1, len(match.group()))
|
||||||
@@ -257,7 +257,7 @@ def highlight(
|
|||||||
highlight_line=None,
|
highlight_line=None,
|
||||||
context_lines=None,
|
context_lines=None,
|
||||||
attributes=None,
|
attributes=None,
|
||||||
):
|
):
|
||||||
styles = styles or {}
|
styles = styles or {}
|
||||||
attributes = attributes or {}
|
attributes = attributes or {}
|
||||||
if not 'CODE' in styles:
|
if not 'CODE' in styles:
|
||||||
@@ -289,7 +289,7 @@ def highlight(
|
|||||||
linehighlight_style = styles['LINEHIGHLIGHT']
|
linehighlight_style = styles['LINEHIGHLIGHT']
|
||||||
|
|
||||||
if language and language.upper() in ['PYTHON', 'C', 'CPP', 'HTML',
|
if language and language.upper() in ['PYTHON', 'C', 'CPP', 'HTML',
|
||||||
'WEB2PY']:
|
'WEB2PY']:
|
||||||
code = Highlighter(language, link, styles).highlight(code)
|
code = Highlighter(language, link, styles).highlight(code)
|
||||||
else:
|
else:
|
||||||
code = cgi.escape(code)
|
code = cgi.escape(code)
|
||||||
@@ -301,22 +301,24 @@ def highlight(
|
|||||||
linenumbers = [cgi.escape(counter)] * len(lines)
|
linenumbers = [cgi.escape(counter)] * len(lines)
|
||||||
else:
|
else:
|
||||||
linenumbers = [str(i + counter) + '.' for i in
|
linenumbers = [str(i + counter) + '.' for i in
|
||||||
xrange(len(lines))]
|
xrange(len(lines))]
|
||||||
|
|
||||||
if highlight_line:
|
if highlight_line:
|
||||||
if counter and not isinstance(counter, str):
|
if counter and not isinstance(counter, str):
|
||||||
lineno = highlight_line - counter
|
lineno = highlight_line - counter
|
||||||
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):
|
||||||
del lines[lineno + context_lines:]
|
del lines[lineno + context_lines:]
|
||||||
del linenumbers[lineno + context_lines:]
|
del linenumbers[lineno + context_lines:]
|
||||||
if lineno -context_lines > 0:
|
if lineno - context_lines > 0:
|
||||||
del lines[0:lineno - context_lines]
|
del lines[0:lineno - context_lines]
|
||||||
del linenumbers[0:lineno - context_lines]
|
del linenumbers[0:lineno - context_lines]
|
||||||
|
|
||||||
@@ -326,13 +328,13 @@ def highlight(
|
|||||||
items = attributes.items()
|
items = attributes.items()
|
||||||
fa = ' '.join([key[1:].lower() for (key, value) in items if key[:1]
|
fa = ' '.join([key[1:].lower() for (key, value) in items if key[:1]
|
||||||
== '_' and value is None] + ['%s="%s"'
|
== '_' and value is None] + ['%s="%s"'
|
||||||
% (key[1:].lower(), str(value).replace('"', "'"))
|
% (key[1:].lower(), str(value).replace('"', "'"))
|
||||||
for (key, value) in attributes.items() if key[:1]
|
for (key, value) in attributes.items() if key[:1]
|
||||||
== '_' and value])
|
== '_' and value])
|
||||||
if fa:
|
if fa:
|
||||||
fa = ' ' + fa
|
fa = ' ' + fa
|
||||||
return '<table%s><tr valign="top"><td style="width:40px; text-align: right;"><pre style="%s">%s</pre></td><td><pre style="%s">%s</pre></td></tr></table>'\
|
return '<table%s><tr valign="top"><td style="width:40px; text-align: right;"><pre style="%s">%s</pre></td><td><pre style="%s">%s</pre></td></tr></table>'\
|
||||||
% (fa, linenumbers_style, numbers, code_style, code)
|
% (fa, linenumbers_style, numbers, code_style, code)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@@ -341,4 +343,4 @@ if __name__ == '__main__':
|
|||||||
data = argfp.read()
|
data = argfp.read()
|
||||||
argfp.close()
|
argfp.close()
|
||||||
print '<html><body>' + highlight(data, sys.argv[2])\
|
print '<html><body>' + highlight(data, sys.argv[2])\
|
||||||
+ '</body></html>'
|
+ '</body></html>'
|
||||||
+298
-223
File diff suppressed because it is too large.
Load diff
+10
-9
@@ -49,7 +49,7 @@ defined_status = {
|
|||||||
503: 'SERVICE UNAVAILABLE',
|
503: 'SERVICE UNAVAILABLE',
|
||||||
504: 'GATEWAY TIMEOUT',
|
504: 'GATEWAY TIMEOUT',
|
||||||
505: 'HTTP VERSION NOT SUPPORTED',
|
505: 'HTTP VERSION NOT SUPPORTED',
|
||||||
}
|
}
|
||||||
|
|
||||||
# If web2py is executed with python2.4 we need
|
# If web2py is executed with python2.4 we need
|
||||||
# to use Exception instead of BaseException
|
# to use Exception instead of BaseException
|
||||||
@@ -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__(
|
||||||
@@ -69,14 +70,14 @@ class HTTP(BaseException):
|
|||||||
body='',
|
body='',
|
||||||
cookies=None,
|
cookies=None,
|
||||||
**headers
|
**headers
|
||||||
):
|
):
|
||||||
self.status = status
|
self.status = status
|
||||||
self.body = body
|
self.body = body
|
||||||
self.headers = headers
|
self.headers = headers
|
||||||
self.cookies2headers(cookies)
|
self.cookies2headers(cookies)
|
||||||
|
|
||||||
def cookies2headers(self,cookies):
|
def cookies2headers(self, cookies):
|
||||||
if cookies and len(cookies)>0:
|
if cookies and len(cookies) > 0:
|
||||||
self.headers['Set-Cookie'] = [
|
self.headers['Set-Cookie'] = [
|
||||||
str(cookie)[11:] for cookie in cookies.values()]
|
str(cookie)[11:] for cookie in cookies.values()]
|
||||||
|
|
||||||
@@ -90,15 +91,15 @@ class HTTP(BaseException):
|
|||||||
status = str(status)
|
status = str(status)
|
||||||
if not regex_status.match(status):
|
if not regex_status.match(status):
|
||||||
status = '500 %s' % (defined_status[500])
|
status = '500 %s' % (defined_status[500])
|
||||||
headers.setdefault('Content-Type','text/html; charset=UTF-8')
|
headers.setdefault('Content-Type', 'text/html; charset=UTF-8')
|
||||||
body = self.body
|
body = self.body
|
||||||
if status[:1] == '4':
|
if status[:1] == '4':
|
||||||
if not body:
|
if not body:
|
||||||
body = status
|
body = status
|
||||||
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():
|
||||||
@@ -107,9 +108,9 @@ class HTTP(BaseException):
|
|||||||
elif not v is None:
|
elif not v is None:
|
||||||
rheaders.append((k, str(v)))
|
rheaders.append((k, str(v)))
|
||||||
responder(status, rheaders)
|
responder(status, rheaders)
|
||||||
if env.get('request_method','')=='HEAD':
|
if env.get('request_method', '') == 'HEAD':
|
||||||
return ['']
|
return ['']
|
||||||
elif isinstance(body,str):
|
elif isinstance(body, str):
|
||||||
return [body]
|
return [body]
|
||||||
elif hasattr(body, '__iter__'):
|
elif hasattr(body, '__iter__'):
|
||||||
return body
|
return body
|
||||||
|
|||||||
+7
-7
@@ -69,10 +69,10 @@ base_modules = ['aifc', 'anydbm', 'array', 'asynchat', 'asyncore', 'atexit',
|
|||||||
contributed_modules = []
|
contributed_modules = []
|
||||||
for root, dirs, files in os.walk('gluon'):
|
for root, dirs, files in os.walk('gluon'):
|
||||||
for candidate in ['.'.join(
|
for candidate in ['.'.join(
|
||||||
os.path.join(root, os.path.splitext(name)[0]).split(os.sep))
|
os.path.join(root, os.path.splitext(name)[0]).split(os.sep))
|
||||||
for name in files if name.endswith('.py')
|
for name in files if name.endswith('.py')
|
||||||
and root.split(os.sep) != ['gluon', 'tests']
|
and root.split(os.sep) != ['gluon', 'tests']
|
||||||
]:
|
]:
|
||||||
contributed_modules.append(candidate)
|
contributed_modules.append(candidate)
|
||||||
|
|
||||||
# Python base version
|
# Python base version
|
||||||
@@ -86,7 +86,7 @@ alert_dependency = ['hashlib', 'uuid']
|
|||||||
#
|
#
|
||||||
# List of modules deprecated in Python 2.6 or 2.7 that are in the above set
|
# List of modules deprecated in Python 2.6 or 2.7 that are in the above set
|
||||||
py26_deprecated = ['mhlib', 'multifile', 'mimify', 'sets', 'MimeWriter']
|
py26_deprecated = ['mhlib', 'multifile', 'mimify', 'sets', 'MimeWriter']
|
||||||
py27_deprecated = [] # ['optparse'] but we need it for now
|
py27_deprecated = [] # ['optparse'] but we need it for now
|
||||||
|
|
||||||
if python_version >= '2.6':
|
if python_version >= '2.6':
|
||||||
base_modules += ['json', 'multiprocessing']
|
base_modules += ['json', 'multiprocessing']
|
||||||
@@ -99,11 +99,11 @@ if python_version >= '2.7':
|
|||||||
# Now iterate in the base_modules, trying to do the import
|
# Now iterate in the base_modules, trying to do the import
|
||||||
for module in base_modules + contributed_modules:
|
for module in base_modules + contributed_modules:
|
||||||
try:
|
try:
|
||||||
__import__(module, globals(), locals(), [])
|
__import__(module, globals(), locals(), [])
|
||||||
except:
|
except:
|
||||||
# Raise an exception if the current module is a dependency
|
# Raise an exception if the current module is a dependency
|
||||||
if module in alert_dependency:
|
if module in alert_dependency:
|
||||||
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)
|
||||||
+141
-107
@@ -48,16 +48,16 @@ DEFAULT_GET_PLURAL_ID = lambda n: 0
|
|||||||
# word is unchangeable
|
# word is unchangeable
|
||||||
DEFAULT_CONSTRUCT_PLURAL_FORM = lambda word, plural_id: word
|
DEFAULT_CONSTRUCT_PLURAL_FORM = lambda word, plural_id: word
|
||||||
|
|
||||||
NUMBERS = (int,long,float)
|
NUMBERS = (int, long, float)
|
||||||
|
|
||||||
# pattern to find T(blah blah blah) expressions
|
# pattern to find T(blah blah blah) expressions
|
||||||
PY_STRING_LITERAL_RE = r'(?<=[^\w]T\()(?P<name>'\
|
PY_STRING_LITERAL_RE = r'(?<=[^\w]T\()(?P<name>'\
|
||||||
+ r"[uU]?[rR]?(?:'''(?:[^']|'{1,2}(?!'))*''')|"\
|
+ r"[uU]?[rR]?(?:'''(?:[^']|'{1,2}(?!'))*''')|"\
|
||||||
+ r"(?:'(?:[^'\\]|\\.)*')|" + r'(?:"""(?:[^"]|"{1,2}(?!"))*""")|'\
|
+ r"(?:'(?:[^'\\]|\\.)*')|" + r'(?:"""(?:[^"]|"{1,2}(?!"))*""")|'\
|
||||||
+ r'(?:"(?:[^"\\]|\\.)*"))'
|
+ r'(?:"(?:[^"\\]|\\.)*"))'
|
||||||
|
|
||||||
regex_translate = re.compile(PY_STRING_LITERAL_RE, re.DOTALL)
|
regex_translate = re.compile(PY_STRING_LITERAL_RE, re.DOTALL)
|
||||||
regex_param=re.compile(r'{(?P<s>.+?)}')
|
regex_param = re.compile(r'{(?P<s>.+?)}')
|
||||||
|
|
||||||
# pattern for a valid accept_language
|
# pattern for a valid accept_language
|
||||||
regex_language = \
|
regex_language = \
|
||||||
@@ -66,33 +66,43 @@ 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:
|
||||||
import ast
|
import ast
|
||||||
return ast.literal_eval(text)
|
return ast.literal_eval(text)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return eval(text,{},{})
|
return 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'))
|
||||||
return render(regex_param.sub(markmin_aux,s),
|
return render(regex_param.sub(markmin_aux, 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')
|
||||||
ttab_out = maketrans('\x1c\x1d\x1e\x1f', "\\%{}")
|
ttab_out = maketrans('\x1c\x1d\x1e\x1f', "\\%{}")
|
||||||
|
|
||||||
# cache of translated messages:
|
# cache of translated messages:
|
||||||
@@ -105,13 +115,14 @@ ttab_out = maketrans('\x1c\x1d\x1e\x1f', "\\%{}")
|
|||||||
# ...
|
# ...
|
||||||
# }
|
# }
|
||||||
|
|
||||||
global_language_cache={}
|
global_language_cache = {}
|
||||||
|
|
||||||
|
|
||||||
def get_from_cache(cache, val, fun):
|
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)
|
||||||
@@ -141,13 +154,14 @@ def read_dict_aux(filename):
|
|||||||
except Exception, e:
|
except Exception, e:
|
||||||
status = 'Syntax error in %s (%s)' % (filename, e)
|
status = 'Syntax error in %s (%s)' % (filename, e)
|
||||||
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
|
||||||
"""
|
"""
|
||||||
return getcfs('lang:'+filename, filename,
|
return getcfs('lang:' + filename, filename,
|
||||||
lambda: read_dict_aux(filename))
|
lambda: read_dict_aux(filename))
|
||||||
|
|
||||||
|
|
||||||
def read_possible_plural_rules():
|
def read_possible_plural_rules():
|
||||||
@@ -159,17 +173,17 @@ def read_possible_plural_rules():
|
|||||||
try:
|
try:
|
||||||
import contrib.plural_rules as package
|
import contrib.plural_rules as package
|
||||||
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
|
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
|
||||||
if len(modname)==2:
|
if len(modname) == 2:
|
||||||
module = __import__(package.__name__+'.'+modname,
|
module = __import__(package.__name__ + '.' + modname,
|
||||||
fromlist=[modname])
|
fromlist=[modname])
|
||||||
lang = modname
|
lang = modname
|
||||||
pname = modname+'.py'
|
pname = modname + '.py'
|
||||||
nplurals = getattr(module,'nplurals', DEFAULT_NPLURALS)
|
nplurals = getattr(module, 'nplurals', DEFAULT_NPLURALS)
|
||||||
get_plural_id = getattr(
|
get_plural_id = getattr(
|
||||||
module,'get_plural_id',
|
module, 'get_plural_id',
|
||||||
DEFAULT_GET_PLURAL_ID)
|
DEFAULT_GET_PLURAL_ID)
|
||||||
construct_plural_form = getattr(
|
construct_plural_form = getattr(
|
||||||
module,'construct_plural_form',
|
module, 'construct_plural_form',
|
||||||
DEFAULT_CONSTRUCT_PLURAL_FORM)
|
DEFAULT_CONSTRUCT_PLURAL_FORM)
|
||||||
plurals[lang] = (lang, nplurals, get_plural_id,
|
plurals[lang] = (lang, nplurals, get_plural_id,
|
||||||
construct_plural_form)
|
construct_plural_form)
|
||||||
@@ -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':
|
||||||
@@ -189,27 +204,28 @@ def read_possible_languages_aux(langdir):
|
|||||||
nplurals,
|
nplurals,
|
||||||
get_plural_id,
|
get_plural_id,
|
||||||
construct_plural_form
|
construct_plural_form
|
||||||
) = PLURAL_RULES.get(real_lang[:2],('default',
|
) = PLURAL_RULES.get(real_lang[:2], ('default',
|
||||||
DEFAULT_NPLURALS,
|
DEFAULT_NPLURALS,
|
||||||
DEFAULT_GET_PLURAL_ID,
|
DEFAULT_GET_PLURAL_ID,
|
||||||
DEFAULT_CONSTRUCT_PLURAL_FORM))
|
DEFAULT_CONSTRUCT_PLURAL_FORM))
|
||||||
if prules_langcode != 'default':
|
if prules_langcode != 'default':
|
||||||
(pluraldict_fname,
|
(pluraldict_fname,
|
||||||
pluraldict_mtime) = plurals.get(real_lang,
|
pluraldict_mtime) = plurals.get(real_lang,
|
||||||
plurals.get(real_lang[:2],
|
plurals.get(real_lang[:2],
|
||||||
('plural-%s.py'%real_lang,0)))
|
('plural-%s.py' % real_lang, 0)))
|
||||||
else:
|
else:
|
||||||
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
|
||||||
prules_langcode, # code of plural rules language or 'default'
|
prules_langcode, # code of plural rules language or 'default'
|
||||||
nplurals, # nplurals for current language
|
nplurals, # nplurals for current language
|
||||||
get_plural_id, # get_plural_id() for current language
|
get_plural_id, # get_plural_id() for current language
|
||||||
construct_plural_form) # construct_plural_form() for current language
|
construct_plural_form) # construct_plural_form() for current language
|
||||||
|
|
||||||
plurals = {}
|
plurals = {}
|
||||||
flist = oslistdir(langdir)
|
flist = oslistdir(langdir)
|
||||||
@@ -217,17 +233,17 @@ def read_possible_languages_aux(langdir):
|
|||||||
for pname in flist:
|
for pname in flist:
|
||||||
if regex_plural_file.match(pname):
|
if regex_plural_file.match(pname):
|
||||||
plurals[pname[7:-3]] = (pname,
|
plurals[pname[7:-3]] = (pname,
|
||||||
ostat(pjoin(langdir,pname)).st_mtime)
|
ostat(pjoin(langdir, pname)).st_mtime)
|
||||||
langs = {}
|
langs = {}
|
||||||
# scan languages directory for langfiles:
|
# scan languages directory for langfiles:
|
||||||
for fname in flist:
|
for fname in flist:
|
||||||
if regex_langfile.match(fname) or fname == 'default.py':
|
if regex_langfile.match(fname) or fname == 'default.py':
|
||||||
fname_with_path = pjoin(langdir,fname)
|
fname_with_path = pjoin(langdir, fname)
|
||||||
d = read_dict(fname_with_path)
|
d = read_dict(fname_with_path)
|
||||||
lang = fname[:-3]
|
lang = fname[:-3]
|
||||||
langcode = d.get('!langcode!', lang if lang != 'default'
|
langcode = d.get('!langcode!', lang if lang != 'default'
|
||||||
else DEFAULT_LANGUAGE)
|
else DEFAULT_LANGUAGE)
|
||||||
langname = d.get('!langname!',langcode)
|
langname = d.get('!langname!', langcode)
|
||||||
langfile_mtime = ostat(fname_with_path).st_mtime
|
langfile_mtime = ostat(fname_with_path).st_mtime
|
||||||
langs[lang] = get_lang_struct(lang, langcode,
|
langs[lang] = get_lang_struct(lang, langcode,
|
||||||
langname, langfile_mtime)
|
langname, langfile_mtime)
|
||||||
@@ -240,27 +256,31 @@ def read_possible_languages_aux(langdir):
|
|||||||
deflangcode = deflang[0]
|
deflangcode = deflang[0]
|
||||||
if deflangcode not in langs:
|
if deflangcode not in langs:
|
||||||
# create language from default.py:
|
# create language from default.py:
|
||||||
langs[deflangcode] = deflang[:2]+(0,)+deflang[3:]
|
langs[deflangcode] = deflang[:2] + (0,) + deflang[3:]
|
||||||
|
|
||||||
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:
|
||||||
return eval(lang_text) or {}
|
return eval(lang_text) or {}
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
status='Syntax error in %s (%s)' % (filename, e)
|
status = 'Syntax error in %s (%s)' % (filename, e)
|
||||||
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:
|
||||||
@@ -269,8 +289,9 @@ def write_plural_dict(filename, contents):
|
|||||||
fp = portalocker.LockedFile(filename, 'w')
|
fp = portalocker.LockedFile(filename, 'w')
|
||||||
fp.write('#!/usr/bin/env python\n{\n# "singular form (0)": ["first plural form (1)", "second plural form (2)", ...],\n')
|
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):
|
||||||
@@ -291,13 +312,12 @@ def write_dict(filename, contents):
|
|||||||
logging.warning('Unable to write to file %s' % filename)
|
logging.warning('Unable to write to file %s' % filename)
|
||||||
return
|
return
|
||||||
fp.write('# coding: utf8\n{\n')
|
fp.write('# coding: utf8\n{\n')
|
||||||
for key in sorted(contents,lambda x,y: cmp(unicode(x,'utf-8').lower(), unicode(y,'utf-8').lower())):
|
for key in sorted(contents, lambda x, y: cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())):
|
||||||
fp.write('%s: %s,\n' % (repr(Utf8(key)), repr(Utf8(contents[key]))))
|
fp.write('%s: %s,\n' % (repr(Utf8(key)), repr(Utf8(contents[key]))))
|
||||||
fp.write('}\n')
|
fp.write('}\n')
|
||||||
fp.close()
|
fp.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class lazyT(object):
|
class lazyT(object):
|
||||||
"""
|
"""
|
||||||
never to be called explicitly, returned by
|
never to be called explicitly, returned by
|
||||||
@@ -309,12 +329,12 @@ class lazyT(object):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
message,
|
message,
|
||||||
symbols = {},
|
symbols={},
|
||||||
T = None,
|
T=None,
|
||||||
filter = None,
|
filter=None,
|
||||||
ftag = None,
|
ftag=None,
|
||||||
M = False
|
M=False
|
||||||
):
|
):
|
||||||
if isinstance(message, lazyT):
|
if isinstance(message, lazyT):
|
||||||
self.m = message.m
|
self.m = message.m
|
||||||
self.s = message.s
|
self.s = message.s
|
||||||
@@ -354,7 +374,7 @@ class lazyT(object):
|
|||||||
def __mul__(self, other):
|
def __mul__(self, other):
|
||||||
return str(self) * other
|
return str(self) * other
|
||||||
|
|
||||||
def __cmp__(self,other):
|
def __cmp__(self, other):
|
||||||
return cmp(str(self), str(other))
|
return cmp(str(self), str(other))
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
@@ -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,14 +494,15 @@ 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):
|
||||||
""" get list of all possible languages for current applications """
|
""" get list of all possible languages for current applications """
|
||||||
return list(set(self.current_languages +
|
return list(set(self.current_languages +
|
||||||
[lang for lang in read_possible_languages(self.folder).iterkeys()
|
[lang for lang in read_possible_languages(self.folder).iterkeys()
|
||||||
if lang != 'default']))
|
if lang != 'default']))
|
||||||
|
|
||||||
def set_current_languages(self, *languages):
|
def set_current_languages(self, *languages):
|
||||||
"""
|
"""
|
||||||
@@ -486,12 +511,12 @@ class translator(object):
|
|||||||
turn translation off to use default language
|
turn translation off to use default language
|
||||||
"""
|
"""
|
||||||
if len(languages) == 1 and isinstance(
|
if len(languages) == 1 and isinstance(
|
||||||
languages[0], (tuple, list)):
|
languages[0], (tuple, list)):
|
||||||
languages = languages[0]
|
languages = languages[0]
|
||||||
if not languages or languages[0] is None:
|
if not languages or languages[0] is None:
|
||||||
# set default language from default.py/DEFAULT_LANGUAGE
|
# set default language from default.py/DEFAULT_LANGUAGE
|
||||||
pl_info = self.get_possible_languages_info('default')
|
pl_info = self.get_possible_languages_info('default')
|
||||||
if pl_info[2]==0: # langfile_mtime
|
if pl_info[2] == 0: # langfile_mtime
|
||||||
# if languages/default.py is not found
|
# if languages/default.py is not found
|
||||||
self.default_language_file = self.langpath
|
self.default_language_file = self.langpath
|
||||||
self.default_t = {}
|
self.default_t = {}
|
||||||
@@ -500,12 +525,11 @@ class translator(object):
|
|||||||
self.default_language_file = pjoin(self.langpath,
|
self.default_language_file = pjoin(self.langpath,
|
||||||
'default.py')
|
'default.py')
|
||||||
self.default_t = read_dict(self.default_language_file)
|
self.default_t = read_dict(self.default_language_file)
|
||||||
self.current_languages = [pl_info[0]] # !langcode!
|
self.current_languages = [pl_info[0]] # !langcode!
|
||||||
else:
|
else:
|
||||||
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
|
||||||
@@ -529,14 +553,14 @@ class translator(object):
|
|||||||
# etc.
|
# etc.
|
||||||
if id != 0:
|
if id != 0:
|
||||||
forms = self.plural_dict.get(word, [])
|
forms = self.plural_dict.get(word, [])
|
||||||
if len(forms)>=id:
|
if len(forms) >= id:
|
||||||
# have this plural form:
|
# have this plural form:
|
||||||
return forms[id-1]
|
return forms[id - 1]
|
||||||
else:
|
else:
|
||||||
# guessing this plural form
|
# guessing this plural form
|
||||||
forms += ['']*(self.nplurals-len(forms)-1)
|
forms += [''] * (self.nplurals - len(forms) - 1)
|
||||||
form = self.construct_plural_form(word, id)
|
form = self.construct_plural_form(word, id)
|
||||||
forms[id-1] = form
|
forms[id - 1] = form
|
||||||
self.plural_dict[word] = forms
|
self.plural_dict[word] = forms
|
||||||
if self.is_writable and self.plural_file:
|
if self.is_writable and self.plural_file:
|
||||||
write_plural_dict(self.plural_file,
|
write_plural_dict(self.plural_file,
|
||||||
@@ -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
|
||||||
@@ -570,7 +595,7 @@ class translator(object):
|
|||||||
self.nplurals,
|
self.nplurals,
|
||||||
self.get_plural_id,
|
self.get_plural_id,
|
||||||
self.construct_plural_form
|
self.construct_plural_form
|
||||||
) = lang_info[3:]
|
) = lang_info[3:]
|
||||||
pdict = {}
|
pdict = {}
|
||||||
if pname:
|
if pname:
|
||||||
pname = pjoin(self.langpath, pname)
|
pname = pjoin(self.langpath, pname)
|
||||||
@@ -586,7 +611,7 @@ class translator(object):
|
|||||||
self.plural_file = None
|
self.plural_file = None
|
||||||
self.plural_dict = {}
|
self.plural_dict = {}
|
||||||
language = ''
|
language = ''
|
||||||
if len(languages)==1 and isinstance(languages[0],str):
|
if len(languages) == 1 and isinstance(languages[0], str):
|
||||||
languages = regex_language.findall(languages[0].lower())
|
languages = regex_language.findall(languages[0].lower())
|
||||||
elif not languages or languages[0] is None:
|
elif not languages or languages[0] is None:
|
||||||
languages = []
|
languages = []
|
||||||
@@ -594,7 +619,7 @@ class translator(object):
|
|||||||
if languages:
|
if languages:
|
||||||
all_languages = set(lang for lang in pl_info.iterkeys()
|
all_languages = set(lang for lang in pl_info.iterkeys()
|
||||||
if lang != 'default') \
|
if lang != 'default') \
|
||||||
| set(self.current_languages)
|
| set(self.current_languages)
|
||||||
for lang in languages:
|
for lang in languages:
|
||||||
# compare "aa-bb" | "aa" from *language* parameter
|
# compare "aa-bb" | "aa" from *language* parameter
|
||||||
# with strings from langlist using such alghorythm:
|
# with strings from langlist using such alghorythm:
|
||||||
@@ -604,20 +629,20 @@ class translator(object):
|
|||||||
language = lang5
|
language = lang5
|
||||||
else:
|
else:
|
||||||
lang2 = lang[:2]
|
lang2 = lang[:2]
|
||||||
if len(lang5)>2 and lang2 in all_languages:
|
if len(lang5) > 2 and lang2 in all_languages:
|
||||||
language = lang2
|
language = lang2
|
||||||
else:
|
else:
|
||||||
for l in all_languages:
|
for l in all_languages:
|
||||||
if l[:2]==lang2:
|
if l[:2] == lang2:
|
||||||
language = l
|
language = l
|
||||||
if language:
|
if language:
|
||||||
if language in self.current_languages:
|
if language in self.current_languages:
|
||||||
break
|
break
|
||||||
self.language_file = pjoin(self.langpath, language+'.py')
|
self.language_file = pjoin(self.langpath, language + '.py')
|
||||||
self.t = read_dict(self.language_file)
|
self.t = read_dict(self.language_file)
|
||||||
self.cache = global_language_cache.setdefault(
|
self.cache = global_language_cache.setdefault(
|
||||||
self.language_file,
|
self.language_file,
|
||||||
({},allocate_lock()))
|
({}, allocate_lock()))
|
||||||
set_plural(language)
|
set_plural(language)
|
||||||
self.accepted_language = language
|
self.accepted_language = language
|
||||||
return languages
|
return languages
|
||||||
@@ -637,7 +662,7 @@ class translator(object):
|
|||||||
if lazy is None:
|
if lazy is None:
|
||||||
lazy = self.lazy
|
lazy = self.lazy
|
||||||
if not language:
|
if not language:
|
||||||
if lazy :
|
if lazy:
|
||||||
return lazyT(message, symbols, self)
|
return lazyT(message, symbols, self)
|
||||||
else:
|
else:
|
||||||
return self.translate(message, symbols)
|
return self.translate(message, symbols)
|
||||||
@@ -654,18 +679,18 @@ class translator(object):
|
|||||||
s = self.get_t(message, prefix)
|
s = self.get_t(message, prefix)
|
||||||
return filter(s) if filter else self.filter(s)
|
return filter(s) if filter else self.filter(s)
|
||||||
if filter:
|
if filter:
|
||||||
prefix = '@'+(ftag or 'userdef')+'\x01'
|
prefix = '@' + (ftag or 'userdef') + '\x01'
|
||||||
else:
|
else:
|
||||||
prefix = '@'+self.ftag+'\x01'
|
prefix = '@' + self.ftag + '\x01'
|
||||||
message = get_from_cache(
|
message = get_from_cache(
|
||||||
self.cache, prefix+message,
|
self.cache, prefix + message,
|
||||||
lambda: get_tr(message, prefix, filter))
|
lambda: get_tr(message, prefix, filter))
|
||||||
if symbols or symbols == 0 or symbols == "":
|
if symbols or symbols == 0 or symbols == "":
|
||||||
if isinstance(symbols, dict):
|
if isinstance(symbols, dict):
|
||||||
symbols.update(
|
symbols.update(
|
||||||
(key, xmlescape(value).translate(ttab_in))
|
(key, xmlescape(value).translate(ttab_in))
|
||||||
for key, value in symbols.iteritems()
|
for key, value in symbols.iteritems()
|
||||||
if not isinstance(value, NUMBERS) )
|
if not isinstance(value, NUMBERS))
|
||||||
else:
|
else:
|
||||||
if not isinstance(symbols, tuple):
|
if not isinstance(symbols, tuple):
|
||||||
symbols = (symbols,)
|
symbols = (symbols,)
|
||||||
@@ -686,7 +711,7 @@ class translator(object):
|
|||||||
lazy = self.lazy
|
lazy = self.lazy
|
||||||
if not language:
|
if not language:
|
||||||
if lazy:
|
if lazy:
|
||||||
return lazyT(message, symbols, self, filter, ftag, True)
|
return lazyT(message, symbols, self, filter, ftag, True)
|
||||||
else:
|
else:
|
||||||
return self.apply_filter(message, symbols, filter, ftag)
|
return self.apply_filter(message, symbols, filter, ftag)
|
||||||
else:
|
else:
|
||||||
@@ -714,11 +739,12 @@ class translator(object):
|
|||||||
message = message.encode('utf8')
|
message = message.encode('utf8')
|
||||||
if isinstance(prefix, unicode):
|
if isinstance(prefix, unicode):
|
||||||
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
|
||||||
message = message.rsplit('##', 1)[0]
|
message = message.rsplit('##', 1)[0]
|
||||||
# guess translation same as original
|
# guess translation same as original
|
||||||
@@ -756,7 +782,7 @@ class translator(object):
|
|||||||
word, !word, !!word, !!!word, ?word?number, ??number, ?number
|
word, !word, !!word, !!!word, ?word?number, ??number, ?number
|
||||||
?word?word[number], ?word?[number], ??word[number]
|
?word?word[number], ?word?[number], ??word[number]
|
||||||
"""
|
"""
|
||||||
w,i = m.group('w','i')
|
w, i = m.group('w', 'i')
|
||||||
c = w[0]
|
c = w[0]
|
||||||
if c not in '!?':
|
if c not in '!?':
|
||||||
return self.plural(w, symbols[int(i or 0)])
|
return self.plural(w, symbols[int(i or 0)])
|
||||||
@@ -764,15 +790,17 @@ 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:
|
||||||
num = int(part2)
|
return m.group(0)
|
||||||
|
num = int(part2)
|
||||||
else:
|
else:
|
||||||
# ?[word]?word2[?word3][number]
|
# ?[word]?word2[?word3][number]
|
||||||
num = int(symbols[int(i or 0)])
|
num = int(symbols[int(i or 0)])
|
||||||
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('!!!'):
|
||||||
word = w[3:]
|
word = w[3:]
|
||||||
fun = upper_fun
|
fun = upper_fun
|
||||||
@@ -783,7 +811,7 @@ class translator(object):
|
|||||||
word = w[1:]
|
word = w[1:]
|
||||||
fun = cap_fun
|
fun = cap_fun
|
||||||
if i is not None:
|
if i is not None:
|
||||||
return fun(self.plural(word, symbols[int(i)]))
|
return fun(self.plural(word, symbols[int(i)]))
|
||||||
return fun(word)
|
return fun(word)
|
||||||
|
|
||||||
def sub_dict(m):
|
def sub_dict(m):
|
||||||
@@ -792,7 +820,7 @@ class translator(object):
|
|||||||
?word2(var), ?word1?word2(var), ?word1?word2?word0(var)
|
?word2(var), ?word1?word2(var), ?word1?word2?word0(var)
|
||||||
?word2(num), ?word1?word2(num), ?word1?word2?word0(num)
|
?word2(num), ?word1?word2(num), ?word1?word2?word0(num)
|
||||||
"""
|
"""
|
||||||
w,n = m.group('w','n')
|
w, n = m.group('w', 'n')
|
||||||
c = w[0]
|
c = w[0]
|
||||||
n = int(n) if n.isdigit() else symbols[n]
|
n = int(n) if n.isdigit() else symbols[n]
|
||||||
if c not in '!?':
|
if c not in '!?':
|
||||||
@@ -802,9 +830,10 @@ 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('!!!'):
|
||||||
word = w[3:]
|
word = w[3:]
|
||||||
fun = upper_fun
|
fun = upper_fun
|
||||||
@@ -824,7 +853,7 @@ class translator(object):
|
|||||||
return m.group(0)
|
return m.group(0)
|
||||||
return part
|
return part
|
||||||
message = message % symbols
|
message = message % symbols
|
||||||
message = regex_plural.sub(sub_plural, message )
|
message = regex_plural.sub(sub_plural, message)
|
||||||
return message
|
return message
|
||||||
|
|
||||||
def translate(self, message, symbols):
|
def translate(self, message, symbols):
|
||||||
@@ -838,7 +867,7 @@ class translator(object):
|
|||||||
symbols.update(
|
symbols.update(
|
||||||
(key, str(value).translate(ttab_in))
|
(key, str(value).translate(ttab_in))
|
||||||
for key, value in symbols.iteritems()
|
for key, value in symbols.iteritems()
|
||||||
if not isinstance(value, NUMBERS) )
|
if not isinstance(value, NUMBERS))
|
||||||
else:
|
else:
|
||||||
if not isinstance(symbols, tuple):
|
if not isinstance(symbols, tuple):
|
||||||
symbols = (symbols,)
|
symbols = (symbols,)
|
||||||
@@ -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
|
||||||
@@ -860,22 +890,22 @@ def findT(path, language=DEFAULT_LANGUAGE):
|
|||||||
vp = pjoin(path, 'views')
|
vp = pjoin(path, 'views')
|
||||||
mop = pjoin(path, 'modules')
|
mop = pjoin(path, 'modules')
|
||||||
for filename in \
|
for filename in \
|
||||||
listdir(mp, '^.+\.py$', 0)+listdir(cp, '^.+\.py$', 0)\
|
listdir(mp, '^.+\.py$', 0) + listdir(cp, '^.+\.py$', 0)\
|
||||||
+listdir(vp, '^.+\.html$', 0)+listdir(mop, '^.+\.py$', 0):
|
+ listdir(vp, '^.+\.html$', 0) + listdir(mop, '^.+\.py$', 0):
|
||||||
data = portalocker.read_locked(filename)
|
data = portalocker.read_locked(filename)
|
||||||
items = regex_translate.findall(data)
|
items = regex_translate.findall(data)
|
||||||
for item in items:
|
for item in items:
|
||||||
try:
|
try:
|
||||||
message = safe_eval(item)
|
message = safe_eval(item)
|
||||||
except:
|
except:
|
||||||
continue # silently ignore inproperly formatted strings
|
continue # silently ignore inproperly formatted strings
|
||||||
if not message.startswith('#') and not '\n' in message:
|
if not message.startswith('#') and not '\n' in message:
|
||||||
tokens = message.rsplit('##', 1)
|
tokens = message.rsplit('##', 1)
|
||||||
else:
|
else:
|
||||||
# this allows markmin syntax in translations
|
# this allows markmin syntax in translations
|
||||||
tokens = [message]
|
tokens = [message]
|
||||||
if len(tokens) == 2:
|
if len(tokens) == 2:
|
||||||
message = tokens[0].strip()+'##'+tokens[1].strip()
|
message = tokens[0].strip() + '##' + tokens[1].strip()
|
||||||
if message and not message in sentences:
|
if message and not message in sentences:
|
||||||
sentences[message] = message
|
sentences[message] = message
|
||||||
if not '!langcode!' in sentences:
|
if not '!langcode!' in sentences:
|
||||||
@@ -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)
|
||||||
|
|||||||
+84
-75
@@ -51,7 +51,7 @@ from globals import current
|
|||||||
# The two are identical unless web2py_path is changed via the web2py.py -f folder option
|
# The two are identical unless web2py_path is changed via the web2py.py -f folder option
|
||||||
# main.web2py_path is the same as applications_parent (for backward compatibility)
|
# main.web2py_path is the same as applications_parent (for backward compatibility)
|
||||||
|
|
||||||
web2py_path = global_settings.applications_parent # backward compatibility
|
web2py_path = global_settings.applications_parent # backward compatibility
|
||||||
|
|
||||||
create_missing_folders()
|
create_missing_folders()
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -119,7 +119,8 @@ except:
|
|||||||
|
|
||||||
load()
|
load()
|
||||||
|
|
||||||
HTTPS_SCHEMES = set(('https','HTTPS'))
|
HTTPS_SCHEMES = set(('https', 'HTTPS'))
|
||||||
|
|
||||||
|
|
||||||
def get_client(env):
|
def get_client(env):
|
||||||
"""
|
"""
|
||||||
@@ -138,10 +139,11 @@ def get_client(env):
|
|||||||
else:
|
else:
|
||||||
client = '127.0.0.1'
|
client = '127.0.0.1'
|
||||||
if not is_valid_ip_address(client):
|
if not is_valid_ip_address(client):
|
||||||
raise HTTP(400,"Bad Request (request.client=%s)" % client)
|
raise HTTP(400, "Bad Request (request.client=%s)" % client)
|
||||||
return client
|
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
|
||||||
and stores progress upload status in cache_ram
|
and stores progress upload status in cache_ram
|
||||||
@@ -154,22 +156,22 @@ def copystream_progress(request, chunk_size= 10**5):
|
|||||||
try:
|
try:
|
||||||
size = int(env.content_length)
|
size = int(env.content_length)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise HTTP(400,"Invalid Content-Length header")
|
raise HTTP(400, "Invalid Content-Length header")
|
||||||
dest = tempfile.TemporaryFile()
|
dest = tempfile.TemporaryFile()
|
||||||
if not 'X-Progress-ID' in request.vars:
|
if not 'X-Progress-ID' in request.vars:
|
||||||
copystream(source, dest, size, chunk_size)
|
copystream(source, dest, size, chunk_size)
|
||||||
return dest
|
return dest
|
||||||
cache_key = 'X-Progress-ID:'+request.vars['X-Progress-ID']
|
cache_key = 'X-Progress-ID:' + request.vars['X-Progress-ID']
|
||||||
cache_ram = CacheInRam(request) # same as cache.ram because meta_storage
|
cache_ram = CacheInRam(request) # same as cache.ram because meta_storage
|
||||||
cache_ram(cache_key+':length', lambda: size, 0)
|
cache_ram(cache_key + ':length', lambda: size, 0)
|
||||||
cache_ram(cache_key+':uploaded', lambda: 0, 0)
|
cache_ram(cache_key + ':uploaded', lambda: 0, 0)
|
||||||
while size > 0:
|
while size > 0:
|
||||||
if size < chunk_size:
|
if size < chunk_size:
|
||||||
data = source.read(size)
|
data = source.read(size)
|
||||||
cache_ram.increment(cache_key+':uploaded', size)
|
cache_ram.increment(cache_key + ':uploaded', size)
|
||||||
else:
|
else:
|
||||||
data = source.read(chunk_size)
|
data = source.read(chunk_size)
|
||||||
cache_ram.increment(cache_key+':uploaded', chunk_size)
|
cache_ram.increment(cache_key + ':uploaded', chunk_size)
|
||||||
length = len(data)
|
length = len(data)
|
||||||
if length > size:
|
if length > size:
|
||||||
(data, length) = (data[:size], size)
|
(data, length) = (data[:size], size)
|
||||||
@@ -180,8 +182,8 @@ def copystream_progress(request, chunk_size= 10**5):
|
|||||||
if length < chunk_size:
|
if length < chunk_size:
|
||||||
break
|
break
|
||||||
dest.seek(0)
|
dest.seek(0)
|
||||||
cache_ram(cache_key+':length', None)
|
cache_ram(cache_key + ':length', None)
|
||||||
cache_ram(cache_key+':uploaded', None)
|
cache_ram(cache_key + ':uploaded', None)
|
||||||
return dest
|
return dest
|
||||||
|
|
||||||
|
|
||||||
@@ -224,8 +226,9 @@ def serve_controller(request, response, session):
|
|||||||
page = response.body.getvalue()
|
page = response.body.getvalue()
|
||||||
# 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
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
@@ -233,13 +236,14 @@ 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')]
|
||||||
for key,value in default_headers:
|
for key, value in default_headers:
|
||||||
response.headers.setdefault(key,value)
|
response.headers.setdefault(key, value)
|
||||||
|
|
||||||
raise HTTP(response.status, page, **response.headers)
|
raise HTTP(response.status, page, **response.headers)
|
||||||
|
|
||||||
@@ -253,9 +257,9 @@ def start_response_aux(status, headers, exc_info, response=None):
|
|||||||
|
|
||||||
to call third party WSGI applications
|
to call third party WSGI applications
|
||||||
"""
|
"""
|
||||||
response.status = str(status).split(' ',1)[0]
|
response.status = str(status).split(' ', 1)[0]
|
||||||
response.headers = dict(headers)
|
response.headers = dict(headers)
|
||||||
return lambda *args, **kargs: response.write(escape=False,*args,**kargs)
|
return lambda *args, **kargs: response.write(escape=False, *args, **kargs)
|
||||||
|
|
||||||
|
|
||||||
def middleware_aux(request, response, *middleware_apps):
|
def middleware_aux(request, response, *middleware_apps):
|
||||||
@@ -270,24 +274,27 @@ def middleware_aux(request, response, *middleware_apps):
|
|||||||
def middleware(f):
|
def middleware(f):
|
||||||
def app(environ, start_response):
|
def app(environ, start_response):
|
||||||
data = f()
|
data = f()
|
||||||
start_response(response.status,response.headers.items())
|
start_response(response.status, response.headers.items())
|
||||||
if isinstance(data,list):
|
if isinstance(data, list):
|
||||||
return data
|
return data
|
||||||
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
|
||||||
@@ -307,22 +314,23 @@ def parse_get_post_vars(request, environ):
|
|||||||
try:
|
try:
|
||||||
request.body = body = copystream_progress(request)
|
request.body = body = copystream_progress(request)
|
||||||
except IOError:
|
except IOError:
|
||||||
raise HTTP(400,"Bad Request - HTTP body is incomplete")
|
raise HTTP(400, "Bad Request - HTTP body is incomplete")
|
||||||
if (body and env.request_method in ('POST', 'PUT', 'BOTH')):
|
if (body and env.request_method in ('POST', 'PUT', 'BOTH')):
|
||||||
dpost = cgi.FieldStorage(fp=body,environ=environ,keep_blank_values=1)
|
dpost = cgi.FieldStorage(fp=body, environ=environ, keep_blank_values=1)
|
||||||
# The same detection used by FieldStorage to detect multipart POSTs
|
# The same detection used by FieldStorage to detect multipart POSTs
|
||||||
is_multipart = dpost.type[:10] == 'multipart/'
|
is_multipart = dpost.type[:10] == 'multipart/'
|
||||||
body.seek(0)
|
body.seek(0)
|
||||||
isle25 = sys.version_info[1] <= 5
|
isle25 = sys.version_info[1] <= 5
|
||||||
|
|
||||||
def listify(a):
|
def listify(a):
|
||||||
return (not isinstance(a,list) and [a]) or a
|
return (not isinstance(a, list) and [a]) or a
|
||||||
try:
|
try:
|
||||||
keys = sorted(dpost)
|
keys = sorted(dpost)
|
||||||
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):
|
||||||
@@ -407,22 +416,23 @@ def wsgibase(environ, responder):
|
|||||||
response.status = env.web2py_status_code or response.status
|
response.status = env.web2py_status_code or response.status
|
||||||
|
|
||||||
if static_file:
|
if static_file:
|
||||||
if environ.get('QUERY_STRING','').startswith(
|
if environ.get('QUERY_STRING', '').startswith(
|
||||||
'attachment'):
|
'attachment'):
|
||||||
response.headers['Content-Disposition'] \
|
response.headers['Content-Disposition'] \
|
||||||
= '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']
|
||||||
if not global_settings.web2py_runtime_gae:
|
if not global_settings.web2py_runtime_gae:
|
||||||
try:
|
try:
|
||||||
local_hosts.append(socket.gethostname())
|
local_hosts.append(socket.gethostname())
|
||||||
@@ -433,7 +443,7 @@ def wsgibase(environ, responder):
|
|||||||
local_hosts += [
|
local_hosts += [
|
||||||
env.server_name,
|
env.server_name,
|
||||||
socket.gethostbyname(env.server_name)]
|
socket.gethostbyname(env.server_name)]
|
||||||
except (socket.gaierror,TypeError):
|
except (socket.gaierror, TypeError):
|
||||||
pass
|
pass
|
||||||
global_settings.local_hosts = local_hosts
|
global_settings.local_hosts = local_hosts
|
||||||
else:
|
else:
|
||||||
@@ -442,15 +452,15 @@ def wsgibase(environ, responder):
|
|||||||
x_req_with = str(env.http_x_requested_with).lower()
|
x_req_with = str(env.http_x_requested_with).lower()
|
||||||
|
|
||||||
request.update(
|
request.update(
|
||||||
client = client,
|
client=client,
|
||||||
folder = abspath('applications',app) + os.sep,
|
folder=abspath('applications', app) + os.sep,
|
||||||
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']
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
@@ -460,7 +470,7 @@ def wsgibase(environ, responder):
|
|||||||
if not exists(request.folder):
|
if not exists(request.folder):
|
||||||
if app == rwthread.routes.default_application \
|
if app == rwthread.routes.default_application \
|
||||||
and app != 'welcome':
|
and app != 'welcome':
|
||||||
redirect(URL('welcome','default','index'))
|
redirect(URL('welcome', 'default', 'index'))
|
||||||
elif rwthread.routes.error_handler:
|
elif rwthread.routes.error_handler:
|
||||||
_handler = rwthread.routes.error_handler
|
_handler = rwthread.routes.error_handler
|
||||||
redirect(URL(_handler['application'],
|
redirect(URL(_handler['application'],
|
||||||
@@ -468,11 +478,11 @@ 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 \
|
||||||
exists(pjoin(request.folder,'DISABLED')):
|
exists(pjoin(request.folder, 'DISABLED')):
|
||||||
raise HTTP(503, "<html><body><h1>Temporarily down for maintenance</h1></body></html>")
|
raise HTTP(503, "<html><body><h1>Temporarily down for maintenance</h1></body></html>")
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
@@ -491,13 +501,13 @@ def wsgibase(environ, responder):
|
|||||||
# expose wsgi hooks for convenience
|
# expose wsgi hooks for convenience
|
||||||
# ##################################################
|
# ##################################################
|
||||||
|
|
||||||
request.wsgi.environ = environ_aux(environ,request)
|
request.wsgi.environ = environ_aux(environ, request)
|
||||||
request.wsgi.start_response = \
|
request.wsgi.start_response = \
|
||||||
lambda status='200', headers=[], \
|
lambda status='200', headers=[], \
|
||||||
exec_info=None, response=response: \
|
exec_info=None, response=response: \
|
||||||
start_response_aux(status, headers, exec_info, response)
|
start_response_aux(status, headers, exec_info, response)
|
||||||
request.wsgi.middleware = \
|
request.wsgi.middleware = \
|
||||||
lambda *a: middleware_aux(request,response,*a)
|
lambda *a: middleware_aux(request, response, *a)
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
# load cookies
|
# load cookies
|
||||||
@@ -507,7 +517,7 @@ def wsgibase(environ, responder):
|
|||||||
try:
|
try:
|
||||||
request.cookies.load(env.http_cookie)
|
request.cookies.load(env.http_cookie)
|
||||||
except Cookie.CookieError, e:
|
except Cookie.CookieError, e:
|
||||||
pass # invalid cookies
|
pass # invalid cookies
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
# try load session or create new session file
|
# try load session or create new session file
|
||||||
@@ -530,7 +540,7 @@ def wsgibase(environ, responder):
|
|||||||
except HTTP, http_response:
|
except HTTP, http_response:
|
||||||
|
|
||||||
if static_file:
|
if static_file:
|
||||||
return http_response.to(responder,env=env)
|
return http_response.to(responder, env=env)
|
||||||
|
|
||||||
if request.body:
|
if request.body:
|
||||||
request.body.close()
|
request.body.close()
|
||||||
@@ -562,9 +572,9 @@ def wsgibase(environ, responder):
|
|||||||
|
|
||||||
if request.cid:
|
if request.cid:
|
||||||
if response.flash:
|
if response.flash:
|
||||||
http_response.headers['web2py-component-flash'] = urllib2.quote(xmlescape(response.flash).replace('\n',''))
|
http_response.headers['web2py-component-flash'] = urllib2.quote(xmlescape(response.flash).replace('\n', ''))
|
||||||
if response.js:
|
if response.js:
|
||||||
http_response.headers['web2py-component-command'] = response.js.replace('\n','')
|
http_response.headers['web2py-component-command'] = response.js.replace('\n', '')
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
# store cookies in headers
|
# store cookies in headers
|
||||||
@@ -576,7 +586,7 @@ def wsgibase(environ, responder):
|
|||||||
elif session._secure:
|
elif session._secure:
|
||||||
rcookies[response.session_id_name]['secure'] = True
|
rcookies[response.session_id_name]['secure'] = True
|
||||||
http_response.cookies2headers(rcookies)
|
http_response.cookies2headers(rcookies)
|
||||||
ticket=None
|
ticket = None
|
||||||
|
|
||||||
except RestrictedError, e:
|
except RestrictedError, e:
|
||||||
|
|
||||||
@@ -594,8 +604,8 @@ 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)
|
||||||
|
|
||||||
except:
|
except:
|
||||||
@@ -617,8 +627,8 @@ 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)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
@@ -630,10 +640,10 @@ def wsgibase(environ, responder):
|
|||||||
http_response, new_environ = try_rewrite_on_error(
|
http_response, new_environ = try_rewrite_on_error(
|
||||||
http_response, request, environ, ticket)
|
http_response, request, environ, ticket)
|
||||||
if not http_response:
|
if not http_response:
|
||||||
return wsgibase(new_environ,responder)
|
return wsgibase(new_environ, responder)
|
||||||
if global_settings.web2py_crontype == 'soft':
|
if global_settings.web2py_crontype == 'soft':
|
||||||
newcron.softcron(global_settings.applications_parent).start()
|
newcron.softcron(global_settings.applications_parent).start()
|
||||||
return http_response.to(responder,env=env)
|
return http_response.to(responder, env=env)
|
||||||
|
|
||||||
|
|
||||||
def save_password(password, port):
|
def save_password(password, port):
|
||||||
@@ -712,14 +722,14 @@ def appfactory(wsgiapp=wsgibase,
|
|||||||
|
|
||||||
locker.acquire()
|
locker.acquire()
|
||||||
cProfile.runctx('ret[0] = wsgiapp(environ, responder2)',
|
cProfile.runctx('ret[0] = wsgiapp(environ, responder2)',
|
||||||
globals(), locals(), profilerfilename+'.tmp')
|
globals(), locals(), profilerfilename + '.tmp')
|
||||||
stat = pstats.Stats(profilerfilename+'.tmp')
|
stat = pstats.Stats(profilerfilename + '.tmp')
|
||||||
stat.stream = cStringIO.StringIO()
|
stat.stream = cStringIO.StringIO()
|
||||||
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()
|
||||||
try:
|
try:
|
||||||
@@ -731,7 +741,7 @@ def appfactory(wsgiapp=wsgibase,
|
|||||||
environ['SERVER_PROTOCOL'],
|
environ['SERVER_PROTOCOL'],
|
||||||
(status_headers[0])[:3],
|
(status_headers[0])[:3],
|
||||||
time.time() - time_in,
|
time.time() - time_in,
|
||||||
)
|
)
|
||||||
if not logfilename:
|
if not logfilename:
|
||||||
sys.stdout.write(line)
|
sys.stdout.write(line)
|
||||||
elif isinstance(logfilename, str):
|
elif isinstance(logfilename, str):
|
||||||
@@ -766,11 +776,11 @@ class HttpServer(object):
|
|||||||
server_name=None,
|
server_name=None,
|
||||||
request_queue_size=5,
|
request_queue_size=5,
|
||||||
timeout=10,
|
timeout=10,
|
||||||
socket_timeout = 1,
|
socket_timeout=1,
|
||||||
shutdown_timeout=None, # Rocket does not use a shutdown timeout
|
shutdown_timeout=None, # Rocket does not use a shutdown timeout
|
||||||
path=None,
|
path=None,
|
||||||
interfaces=None # Rocket is able to use several interfaces - must be list of socket-tuples as string
|
interfaces=None # Rocket is able to use several interfaces - must be list of socket-tuples as string
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
starts the web server.
|
starts the web server.
|
||||||
"""
|
"""
|
||||||
@@ -779,9 +789,9 @@ class HttpServer(object):
|
|||||||
# if interfaces is specified, it must be tested for rocket parameter correctness
|
# if interfaces is specified, it must be tested for rocket parameter correctness
|
||||||
# not necessarily completely tested (e.g. content of tuples or ip-format)
|
# not necessarily completely tested (e.g. content of tuples or ip-format)
|
||||||
import types
|
import types
|
||||||
if isinstance(interfaces,types.ListType):
|
if isinstance(interfaces, types.ListType):
|
||||||
for i in interfaces:
|
for i in interfaces:
|
||||||
if not isinstance(i,types.TupleType):
|
if not isinstance(i, types.TupleType):
|
||||||
raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/"
|
raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/"
|
||||||
else:
|
else:
|
||||||
raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/"
|
raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/"
|
||||||
@@ -822,7 +832,7 @@ class HttpServer(object):
|
|||||||
logger.info('SSL is ON')
|
logger.info('SSL is ON')
|
||||||
app_info = {'wsgi_app': appfactory(wsgibase,
|
app_info = {'wsgi_app': appfactory(wsgibase,
|
||||||
log_filename,
|
log_filename,
|
||||||
profiler_filename) }
|
profiler_filename)}
|
||||||
|
|
||||||
self.server = rocket.Rocket(interfaces or tuple(sock_list),
|
self.server = rocket.Rocket(interfaces or tuple(sock_list),
|
||||||
method='wsgi',
|
method='wsgi',
|
||||||
@@ -834,7 +844,6 @@ class HttpServer(object):
|
|||||||
handle_signals=False,
|
handle_signals=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""
|
"""
|
||||||
start the web server
|
start the web server
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ try:
|
|||||||
except:
|
except:
|
||||||
tkMessageBox = None
|
tkMessageBox = None
|
||||||
|
|
||||||
|
|
||||||
class MessageBoxHandler(logging.Handler):
|
class MessageBoxHandler(logging.Handler):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
logging.Handler.__init__(self)
|
logging.Handler.__init__(self)
|
||||||
|
|||||||
+5
-5
@@ -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)
|
||||||
+60
-51
@@ -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):
|
||||||
@@ -66,7 +70,7 @@ class hardcron(threading.Thread):
|
|||||||
def launch(self):
|
def launch(self):
|
||||||
if not _cron_stopping:
|
if not _cron_stopping:
|
||||||
logger.debug('hard cron invocation')
|
logger.debug('hard cron invocation')
|
||||||
crondance(self.path, 'hard', startup = False)
|
crondance(self.path, 'hard', startup=False)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
s = sched.scheduler(time.time, time.sleep)
|
s = sched.scheduler(time.time, time.sleep)
|
||||||
@@ -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,16 +93,17 @@ 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):
|
||||||
self.path = os.path.join(path, 'cron.master')
|
self.path = os.path.join(path, 'cron.master')
|
||||||
if not os.path.exists(self.path):
|
if not os.path.exists(self.path):
|
||||||
fileutils.write_file(self.path, '', 'wb')
|
fileutils.write_file(self.path, '', 'wb')
|
||||||
self.master = None
|
self.master = None
|
||||||
self.now = time.time()
|
self.now = time.time()
|
||||||
|
|
||||||
def acquire(self,startup=False):
|
def acquire(self, startup=False):
|
||||||
"""
|
"""
|
||||||
returns the time when the lock is acquired or
|
returns the time when the lock is acquired or
|
||||||
None if cron already running
|
None if cron already running
|
||||||
@@ -112,12 +118,12 @@ class Token(object):
|
|||||||
if portalocker.LOCK_EX is None:
|
if portalocker.LOCK_EX is None:
|
||||||
logger.warning('WEB2PY CRON: Disabled because no file locking')
|
logger.warning('WEB2PY CRON: Disabled because no file locking')
|
||||||
return None
|
return None
|
||||||
self.master = open(self.path,'rb+')
|
self.master = open(self.path, 'rb+')
|
||||||
try:
|
try:
|
||||||
ret = None
|
ret = None
|
||||||
portalocker.lock(self.master,portalocker.LOCK_EX)
|
portalocker.lock(self.master, portalocker.LOCK_EX)
|
||||||
try:
|
try:
|
||||||
(start, stop) = cPickle.load(self.master)
|
(start, stop) = cPickle.load(self.master)
|
||||||
except:
|
except:
|
||||||
(start, stop) = (0, 1)
|
(start, stop) = (0, 1)
|
||||||
if startup or self.now - start > 59.99:
|
if startup or self.now - start > 59.99:
|
||||||
@@ -127,7 +133,7 @@ class Token(object):
|
|||||||
logger.warning('WEB2PY CRON: Stale cron.master detected')
|
logger.warning('WEB2PY CRON: Stale cron.master detected')
|
||||||
logger.debug('WEB2PY CRON: Acquiring lock')
|
logger.debug('WEB2PY CRON: Acquiring lock')
|
||||||
self.master.seek(0)
|
self.master.seek(0)
|
||||||
cPickle.dump((self.now,0),self.master)
|
cPickle.dump((self.now, 0), self.master)
|
||||||
finally:
|
finally:
|
||||||
portalocker.unlock(self.master)
|
portalocker.unlock(self.master)
|
||||||
if not ret:
|
if not ret:
|
||||||
@@ -141,13 +147,13 @@ class Token(object):
|
|||||||
was completed
|
was completed
|
||||||
"""
|
"""
|
||||||
if not self.master.closed:
|
if not self.master.closed:
|
||||||
portalocker.lock(self.master,portalocker.LOCK_EX)
|
portalocker.lock(self.master, portalocker.LOCK_EX)
|
||||||
logger.debug('WEB2PY CRON: Releasing cron lock')
|
logger.debug('WEB2PY CRON: Releasing cron lock')
|
||||||
self.master.seek(0)
|
self.master.seek(0)
|
||||||
(start, stop) = cPickle.load(self.master)
|
(start, stop) = cPickle.load(self.master)
|
||||||
if start == self.now: # if this is my lock
|
if start == self.now: # if this is my lock
|
||||||
self.master.seek(0)
|
self.master.seek(0)
|
||||||
cPickle.dump((self.now,time.time()),self.master)
|
cPickle.dump((self.now, time.time()), self.master)
|
||||||
portalocker.unlock(self.master)
|
portalocker.unlock(self.master)
|
||||||
self.master.close()
|
self.master.close()
|
||||||
|
|
||||||
@@ -177,25 +183,26 @@ def rangetolist(s, period='min'):
|
|||||||
def parsecronline(line):
|
def parsecronline(line):
|
||||||
task = {}
|
task = {}
|
||||||
if line.startswith('@reboot'):
|
if line.startswith('@reboot'):
|
||||||
line=line.replace('@reboot', '-1 * * * *')
|
line = line.replace('@reboot', '-1 * * * *')
|
||||||
elif line.startswith('@yearly'):
|
elif line.startswith('@yearly'):
|
||||||
line=line.replace('@yearly', '0 0 1 1 *')
|
line = line.replace('@yearly', '0 0 1 1 *')
|
||||||
elif line.startswith('@annually'):
|
elif line.startswith('@annually'):
|
||||||
line=line.replace('@annually', '0 0 1 1 *')
|
line = line.replace('@annually', '0 0 1 1 *')
|
||||||
elif line.startswith('@monthly'):
|
elif line.startswith('@monthly'):
|
||||||
line=line.replace('@monthly', '0 0 1 * *')
|
line = line.replace('@monthly', '0 0 1 * *')
|
||||||
elif line.startswith('@weekly'):
|
elif line.startswith('@weekly'):
|
||||||
line=line.replace('@weekly', '0 0 * * 0')
|
line = line.replace('@weekly', '0 0 * * 0')
|
||||||
elif line.startswith('@daily'):
|
elif line.startswith('@daily'):
|
||||||
line=line.replace('@daily', '0 0 * * *')
|
line = line.replace('@daily', '0 0 * * *')
|
||||||
elif line.startswith('@midnight'):
|
elif line.startswith('@midnight'):
|
||||||
line=line.replace('@midnight', '0 0 * * *')
|
line = line.replace('@midnight', '0 0 * * *')
|
||||||
elif line.startswith('@hourly'):
|
elif line.startswith('@hourly'):
|
||||||
line=line.replace('@hourly', '0 * * * *')
|
line = line.replace('@hourly', '0 * * * *')
|
||||||
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] = []
|
||||||
@@ -205,9 +212,9 @@ def parsecronline(line):
|
|||||||
val = '%s/1' % val
|
val = '%s/1' % val
|
||||||
if '/' in val:
|
if '/' in val:
|
||||||
task[id] += rangetolist(val, id)
|
task[id] += rangetolist(val, id)
|
||||||
elif val.isdigit() or val=='-1':
|
elif val.isdigit() or val == '-1':
|
||||||
task[id].append(int(val))
|
task[id].append(int(val))
|
||||||
elif id=='dow' and val[:3].lower() in daysofweek:
|
elif id == 'dow' and val[:3].lower() in daysofweek:
|
||||||
task[id].append(daysofweek(val[:3].lower()))
|
task[id].append(daysofweek(val[:3].lower()))
|
||||||
task['user'] = params[5]
|
task['user'] = params[5]
|
||||||
task['cmd'] = params[6]
|
task['cmd'] = params[6]
|
||||||
@@ -225,7 +232,7 @@ class cronlauncher(threading.Thread):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
import subprocess
|
import subprocess
|
||||||
if isinstance(self.cmd, (list,tuple)):
|
if isinstance(self.cmd, (list, tuple)):
|
||||||
cmd = self.cmd
|
cmd = self.cmd
|
||||||
else:
|
else:
|
||||||
cmd = self.cmd.split()
|
cmd = self.cmd.split()
|
||||||
@@ -234,28 +241,29 @@ class cronlauncher(threading.Thread):
|
|||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE,
|
stderr=subprocess.PIPE,
|
||||||
shell=self.shell)
|
shell=self.shell)
|
||||||
(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)
|
||||||
token = Token(cron_path)
|
token = Token(cron_path)
|
||||||
cronmaster = token.acquire(startup=startup)
|
cronmaster = token.acquire(startup=startup)
|
||||||
if not cronmaster:
|
if not cronmaster:
|
||||||
return
|
return
|
||||||
now_s = time.localtime()
|
now_s = time.localtime()
|
||||||
checks=(('min',now_s.tm_min),
|
checks = (('min', now_s.tm_min),
|
||||||
('hr',now_s.tm_hour),
|
('hr', now_s.tm_hour),
|
||||||
('mon',now_s.tm_mon),
|
('mon', now_s.tm_mon),
|
||||||
('dom',now_s.tm_mday),
|
('dom', now_s.tm_mday),
|
||||||
('dow',(now_s.tm_wday+1)%7))
|
('dow', (now_s.tm_wday + 1) % 7))
|
||||||
|
|
||||||
if apps is None:
|
if apps is None:
|
||||||
apps = [x for x in os.listdir(apppath)
|
apps = [x for x in os.listdir(apppath)
|
||||||
@@ -265,8 +273,8 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
|
|||||||
|
|
||||||
for app in apps:
|
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
|
||||||
full_apath_link = absolute_path_link(apath)
|
full_apath_link = absolute_path_link(apath)
|
||||||
@@ -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,42 +298,42 @@ 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):
|
||||||
commands.append(w2p_path)
|
commands.append(w2p_path)
|
||||||
if global_settings.applications_parent != global_settings.gluon_parent:
|
if global_settings.applications_parent != global_settings.gluon_parent:
|
||||||
commands.extend(('-f', global_settings.applications_parent))
|
commands.extend(('-f', global_settings.applications_parent))
|
||||||
citems = [(k in task and not v in task[k]) for k,v in checks]
|
citems = [(k in task and not v in task[k]) for k, v in checks]
|
||||||
task_min= task.get('min',[])
|
task_min = task.get('min', [])
|
||||||
if not task:
|
if not task:
|
||||||
continue
|
continue
|
||||||
elif not startup and task_min == [-1]:
|
elif not startup and task_min == [-1]:
|
||||||
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'], ''
|
||||||
if command.startswith('**'):
|
if command.startswith('**'):
|
||||||
(action,models,command) = (True,'',command[2:])
|
(action, models, command) = (True, '', command[2:])
|
||||||
elif command.startswith('*'):
|
elif command.startswith('*'):
|
||||||
(action,models,command) = (True,'-M',command[1:])
|
(action, models, command) = (True, '-M', command[1:])
|
||||||
else:
|
else:
|
||||||
action=False
|
action = False
|
||||||
|
|
||||||
if action and command.endswith('.py'):
|
if action and command.endswith('.py'):
|
||||||
commands.extend(('-J', # cron job
|
commands.extend(('-J', # cron job
|
||||||
models, # import models?
|
models, # import models?
|
||||||
'-S', app, # app name
|
'-S', app, # app name
|
||||||
'-a', '"<recycle>"', # password
|
'-a', '"<recycle>"', # password
|
||||||
'-R', command)) # command
|
'-R', command)) # command
|
||||||
elif action:
|
elif action:
|
||||||
commands.extend(('-J', # cron job
|
commands.extend(('-J', # cron job
|
||||||
models, # import models?
|
models, # import models?
|
||||||
'-S', app+'/'+command, # app name
|
'-S', app + '/' + command, # app name
|
||||||
'-a', '"<recycle>"')) # password
|
'-a', '"<recycle>"')) # password
|
||||||
else:
|
else:
|
||||||
commands = command
|
commands = command
|
||||||
@@ -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()
|
||||||
+20
-12
@@ -110,54 +110,62 @@ else:
|
|||||||
|
|
||||||
|
|
||||||
class LockedFile(object):
|
class LockedFile(object):
|
||||||
def __init__(self,filename, mode='rb'):
|
def __init__(self, filename, mode='rb'):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.file = None
|
self.file = None
|
||||||
if 'r' in mode:
|
if 'r' in mode:
|
||||||
self.file = open(filename,mode)
|
self.file = open(filename, mode)
|
||||||
lock(self.file,LOCK_SH)
|
lock(self.file, LOCK_SH)
|
||||||
elif 'w' in mode or 'a' in mode:
|
elif 'w' in mode or 'a' in mode:
|
||||||
self.file = open(filename,mode.replace('w','a'))
|
self.file = open(filename, mode.replace('w', 'a'))
|
||||||
lock(self.file,LOCK_EX)
|
lock(self.file, LOCK_EX)
|
||||||
if not 'a' in mode:
|
if not 'a' in mode:
|
||||||
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)
|
||||||
fp.close()
|
fp.close()
|
||||||
|
|
||||||
if __name__=='__main__':
|
if __name__ == '__main__':
|
||||||
f = LockedFile('test.txt',mode='wb')
|
f = LockedFile('test.txt', mode='wb')
|
||||||
f.write('test ok')
|
f.write('test ok')
|
||||||
f.close()
|
f.close()
|
||||||
f = LockedFile('test.txt',mode='rb')
|
f = LockedFile('test.txt', mode='rb')
|
||||||
print f.read()
|
print f.read()
|
||||||
f.close()
|
f.close()
|
||||||
@@ -419,7 +419,7 @@ POSTGRESQL_NONRESERVED = set((
|
|||||||
'HOLD',
|
'HOLD',
|
||||||
'HOST',
|
'HOST',
|
||||||
'HOUR',
|
'HOUR',
|
||||||
# 'ID',
|
# 'ID',
|
||||||
'IDENTITY',
|
'IDENTITY',
|
||||||
'IF',
|
'IF',
|
||||||
'IGNORE',
|
'IGNORE',
|
||||||
@@ -501,7 +501,7 @@ POSTGRESQL_NONRESERVED = set((
|
|||||||
'MOVE',
|
'MOVE',
|
||||||
'MULTISET',
|
'MULTISET',
|
||||||
'MUMPS',
|
'MUMPS',
|
||||||
# 'NAME',
|
# 'NAME',
|
||||||
'NAMES',
|
'NAMES',
|
||||||
'NAMESPACE',
|
'NAMESPACE',
|
||||||
'NCLOB',
|
'NCLOB',
|
||||||
@@ -563,7 +563,7 @@ POSTGRESQL_NONRESERVED = set((
|
|||||||
'PARTITION',
|
'PARTITION',
|
||||||
'PASCAL',
|
'PASCAL',
|
||||||
'PASSING',
|
'PASSING',
|
||||||
# 'PASSWORD',
|
# 'PASSWORD',
|
||||||
'PATH',
|
'PATH',
|
||||||
'PERCENT_RANK',
|
'PERCENT_RANK',
|
||||||
'PERCENTILE_CONT',
|
'PERCENTILE_CONT',
|
||||||
@@ -622,7 +622,7 @@ POSTGRESQL_NONRESERVED = set((
|
|||||||
'RETURNED_SQLSTATE',
|
'RETURNED_SQLSTATE',
|
||||||
'RETURNS',
|
'RETURNS',
|
||||||
'REVOKE',
|
'REVOKE',
|
||||||
# 'ROLE',
|
# 'ROLE',
|
||||||
'ROLLBACK',
|
'ROLLBACK',
|
||||||
'ROLLUP',
|
'ROLLUP',
|
||||||
'ROUTINE',
|
'ROUTINE',
|
||||||
@@ -697,7 +697,7 @@ POSTGRESQL_NONRESERVED = set((
|
|||||||
'SYSTEM',
|
'SYSTEM',
|
||||||
'SYSTEM_USER',
|
'SYSTEM_USER',
|
||||||
'T',
|
'T',
|
||||||
# 'TABLE_NAME',
|
# 'TABLE_NAME',
|
||||||
'TABLESAMPLE',
|
'TABLESAMPLE',
|
||||||
'TABLESPACE',
|
'TABLESPACE',
|
||||||
'TEMP',
|
'TEMP',
|
||||||
@@ -785,7 +785,7 @@ POSTGRESQL_NONRESERVED = set((
|
|||||||
'YEAR',
|
'YEAR',
|
||||||
'YES',
|
'YES',
|
||||||
'ZONE',
|
'ZONE',
|
||||||
))
|
))
|
||||||
|
|
||||||
#Thanks villas
|
#Thanks villas
|
||||||
FIREBIRD = set((
|
FIREBIRD = set((
|
||||||
@@ -887,7 +887,7 @@ FIREBIRD = set((
|
|||||||
'PAGES',
|
'PAGES',
|
||||||
'PAGE_SIZE',
|
'PAGE_SIZE',
|
||||||
'PARAMETER',
|
'PARAMETER',
|
||||||
# 'PASSWORD',
|
# 'PASSWORD',
|
||||||
'PLAN',
|
'PLAN',
|
||||||
'POST_EVENT',
|
'POST_EVENT',
|
||||||
'QUIT',
|
'QUIT',
|
||||||
@@ -903,7 +903,7 @@ FIREBIRD = set((
|
|||||||
'RETURN',
|
'RETURN',
|
||||||
'RETURNING_VALUES',
|
'RETURNING_VALUES',
|
||||||
'RETURNS',
|
'RETURNS',
|
||||||
# 'ROLE',
|
# 'ROLE',
|
||||||
'ROW_COUNT',
|
'ROW_COUNT',
|
||||||
'ROWS',
|
'ROWS',
|
||||||
'RUNTIME',
|
'RUNTIME',
|
||||||
@@ -941,7 +941,7 @@ FIREBIRD = set((
|
|||||||
'WEEKDAY',
|
'WEEKDAY',
|
||||||
'WHILE',
|
'WHILE',
|
||||||
'YEARDAY',
|
'YEARDAY',
|
||||||
))
|
))
|
||||||
FIREBIRD_NONRESERVED = set((
|
FIREBIRD_NONRESERVED = set((
|
||||||
'BACKUP',
|
'BACKUP',
|
||||||
'BLOCK',
|
'BLOCK',
|
||||||
@@ -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()))
|
||||||
+46
-29
@@ -22,6 +22,7 @@ logger = logging.getLogger("web2py")
|
|||||||
|
|
||||||
__all__ = ['RestrictedError', 'restricted', 'TicketStorage', 'compile2']
|
__all__ = ['RestrictedError', 'restricted', 'TicketStorage', 'compile2']
|
||||||
|
|
||||||
|
|
||||||
class TicketStorage(Storage):
|
class TicketStorage(Storage):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -32,7 +33,7 @@ class TicketStorage(Storage):
|
|||||||
self,
|
self,
|
||||||
db=None,
|
db=None,
|
||||||
tablename='web2py_ticket'
|
tablename='web2py_ticket'
|
||||||
):
|
):
|
||||||
Storage.__init__(self)
|
Storage.__init__(self)
|
||||||
self.db = db
|
self.db = db
|
||||||
self.tablename = tablename
|
self.tablename = tablename
|
||||||
@@ -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):
|
||||||
@@ -78,7 +80,7 @@ class TicketStorage(Storage):
|
|||||||
db.Field('ticket_id', length=100),
|
db.Field('ticket_id', length=100),
|
||||||
db.Field('ticket_data', 'text'),
|
db.Field('ticket_data', 'text'),
|
||||||
db.Field('created_datetime', 'datetime'),
|
db.Field('created_datetime', 'datetime'),
|
||||||
)
|
)
|
||||||
return table
|
return table
|
||||||
|
|
||||||
def load(
|
def load(
|
||||||
@@ -86,7 +88,7 @@ class TicketStorage(Storage):
|
|||||||
request,
|
request,
|
||||||
app,
|
app,
|
||||||
ticket_id,
|
ticket_id,
|
||||||
):
|
):
|
||||||
if not self.db:
|
if not self.db:
|
||||||
try:
|
try:
|
||||||
ef = self._error_file(request, ticket_id, 'rb', app)
|
ef = self._error_file(request, ticket_id, 'rb', app)
|
||||||
@@ -102,7 +104,6 @@ class TicketStorage(Storage):
|
|||||||
return cPickle.loads(rows[0].ticket_data) if rows else {}
|
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
|
||||||
@@ -115,12 +116,13 @@ class RestrictedError(Exception):
|
|||||||
code='',
|
code='',
|
||||||
output='',
|
output='',
|
||||||
environment=None,
|
environment=None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
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
|
||||||
@@ -131,7 +133,7 @@ class RestrictedError(Exception):
|
|||||||
except:
|
except:
|
||||||
self.traceback = 'no traceback because template parting error'
|
self.traceback = 'no traceback because template parting error'
|
||||||
try:
|
try:
|
||||||
self.snapshot = snapshot(context=10,code=code,
|
self.snapshot = snapshot(context=10, code=code,
|
||||||
environment=self.environment)
|
environment=self.environment)
|
||||||
except:
|
except:
|
||||||
self.snapshot = {}
|
self.snapshot = {}
|
||||||
@@ -151,15 +153,14 @@ class RestrictedError(Exception):
|
|||||||
'output': str(self.output),
|
'output': str(self.output),
|
||||||
'traceback': str(self.traceback),
|
'traceback': str(self.traceback),
|
||||||
'snapshot': self.snapshot,
|
'snapshot': self.snapshot,
|
||||||
}
|
}
|
||||||
ticket_storage = TicketStorage(db=request.tickets_db)
|
ticket_storage = TicketStorage(db=request.tickets_db)
|
||||||
ticket_storage.store(request, request.uuid.split('/',1)[1], d)
|
ticket_storage.store(request, request.uuid.split('/', 1)[1], d)
|
||||||
return request.uuid
|
return request.uuid
|
||||||
except:
|
except:
|
||||||
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.
|
||||||
@@ -186,11 +187,12 @@ class RestrictedError(Exception):
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def compile2(code,layer):
|
def compile2(code, layer):
|
||||||
"""
|
"""
|
||||||
The +'\n' is necessary else compile fails when code ends in a comment.
|
The +'\n' is necessary else compile fails when code ends in a comment.
|
||||||
"""
|
"""
|
||||||
return compile(code.rstrip().replace('\r\n','\n')+'\n', layer, 'exec')
|
return compile(code.rstrip().replace('\r\n', '\n') + '\n', layer, 'exec')
|
||||||
|
|
||||||
|
|
||||||
def restricted(code, environment=None, layer='Unknown'):
|
def restricted(code, environment=None, layer='Unknown'):
|
||||||
"""
|
"""
|
||||||
@@ -198,14 +200,15 @@ def restricted(code, environment=None, layer='Unknown'):
|
|||||||
in code it raises a RestrictedError containing the traceback. layer is
|
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)
|
||||||
exec ccode in environment
|
exec ccode in environment
|
||||||
except HTTP:
|
except HTTP:
|
||||||
raise
|
raise
|
||||||
@@ -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
|
||||||
@@ -245,22 +255,26 @@ def snapshot(info=None, context=5, code=None, environment=None):
|
|||||||
call = ''
|
call = ''
|
||||||
if func != '?':
|
if func != '?':
|
||||||
call = inspect.formatargvalues(args, varargs, varkw, locals,
|
call = inspect.formatargvalues(args, varargs, varkw, locals,
|
||||||
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
|
||||||
if file.endswith('html'):
|
if file.endswith('html'):
|
||||||
lmin = lnum>context and (lnum-context) or 0
|
lmin = lnum > context and (lnum - context) or 0
|
||||||
lmax = lnum+context
|
lmax = lnum + context
|
||||||
lines = code.split("\n")[lmin:lmax]
|
lines = code.split("\n")[lmin:lmax]
|
||||||
index = min(context, lnum) - 1
|
index = min(context, lnum) - 1
|
||||||
|
|
||||||
@@ -273,10 +287,13 @@ def snapshot(info=None, context=5, code=None, environment=None):
|
|||||||
# dump local variables (referenced in current line only)
|
# 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'
|
||||||
@@ -290,7 +307,7 @@ def snapshot(info=None, context=5, code=None, environment=None):
|
|||||||
if isinstance(evalue, BaseException):
|
if isinstance(evalue, BaseException):
|
||||||
for name in dir(evalue):
|
for name in dir(evalue):
|
||||||
# prevent py26 DeprecatedWarning:
|
# prevent py26 DeprecatedWarning:
|
||||||
if name!='message' or sys.version_info<(2.6):
|
if name != 'message' or sys.version_info < (2.6):
|
||||||
value = pydoc.text.repr(getattr(evalue, name))
|
value = pydoc.text.repr(getattr(evalue, name))
|
||||||
s['exception'][name] = value
|
s['exception'][name] = value
|
||||||
|
|
||||||
@@ -300,7 +317,7 @@ def snapshot(info=None, context=5, code=None, environment=None):
|
|||||||
s['locals'][name] = pydoc.text.repr(value)
|
s['locals'][name] = pydoc.text.repr(value)
|
||||||
|
|
||||||
# add web2py environment variables
|
# add web2py environment variables
|
||||||
for k,v in environment.items():
|
for k, v in environment.items():
|
||||||
if k in ('request', 'response', 'session'):
|
if k in ('request', 'response', 'session'):
|
||||||
s[k] = BEAUTIFY(v)
|
s[k] = BEAUTIFY(v)
|
||||||
|
|
||||||
|
|||||||
+172
-127
@@ -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
|
||||||
@@ -60,25 +61,26 @@ regex_url = re.compile('^/((?P<a>\w+)(/(?P<c>\w+)(/(?P<z>(?P<f>\w+)(\.(?P<e>[\w.
|
|||||||
def _router_default():
|
def _router_default():
|
||||||
"return new copy of default base router"
|
"return new copy of default base router"
|
||||||
router = Storage(
|
router = Storage(
|
||||||
default_application = 'init',
|
default_application='init',
|
||||||
applications = 'ALL',
|
applications='ALL',
|
||||||
default_controller = 'default',
|
default_controller='default',
|
||||||
controllers = 'DEFAULT',
|
controllers='DEFAULT',
|
||||||
default_function = 'index',
|
default_function='index',
|
||||||
functions = dict(),
|
functions=dict(),
|
||||||
default_language = None,
|
default_language=None,
|
||||||
languages = None,
|
languages=None,
|
||||||
root_static = ['favicon.ico', 'robots.txt'],
|
root_static=['favicon.ico', 'robots.txt'],
|
||||||
map_static = None,
|
map_static=None,
|
||||||
domains = None,
|
domains=None,
|
||||||
exclusive_domain = False,
|
exclusive_domain=False,
|
||||||
map_hyphen = False,
|
map_hyphen=False,
|
||||||
acfe_match = r'\w+$', # legal app/ctlr/fcn/ext
|
acfe_match=r'\w+$', # legal app/ctlr/fcn/ext
|
||||||
file_match = r'([-+=@$%\w]+[./]?)+$', # legal static subpath
|
file_match=r'([-+=@$%\w]+[./]?)+$', # legal static subpath
|
||||||
args_match = r'([\w@ -]+[=.]?)*$', # legal arg in args
|
args_match=r'([\w@ -]+[=.]?)*$', # legal arg in args
|
||||||
)
|
)
|
||||||
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()
|
||||||
@@ -94,7 +96,7 @@ def _params_default(app=None):
|
|||||||
p.error_handler = None
|
p.error_handler = None
|
||||||
p.error_message = '<html><body><h1>%s</h1></body></html>'
|
p.error_message = '<html><body><h1>%s</h1></body></html>'
|
||||||
p.error_message_ticket = \
|
p.error_message_ticket = \
|
||||||
'<html><body><h1>Internal error</h1>Ticket issued: <a href="/admin/default/ticket/%(ticket)s" target="_blank">%(ticket)s</a></body><!-- this is junk text else IE does not display the page: '+('x'*512)+' //--></html>'
|
'<html><body><h1>Internal error</h1>Ticket issued: <a href="/admin/default/ticket/%(ticket)s" target="_blank">%(ticket)s</a></body><!-- this is junk text else IE does not display the page: ' + ('x' * 512) + ' //--></html>'
|
||||||
p.routers = None
|
p.routers = None
|
||||||
p.logging = 'off'
|
p.logging = 'off'
|
||||||
return p
|
return p
|
||||||
@@ -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')
|
||||||
@@ -165,12 +169,12 @@ def fixup_missing_path_info(environ):
|
|||||||
elif not request_uri:
|
elif not request_uri:
|
||||||
query_string = eget('QUERY_STRING')
|
query_string = eget('QUERY_STRING')
|
||||||
if query_string:
|
if query_string:
|
||||||
environ['REQUEST_URI'] = '%s?%s' % (path_info,query_string)
|
environ['REQUEST_URI'] = '%s?%s' % (path_info, query_string)
|
||||||
else:
|
else:
|
||||||
environ['REQUEST_URI'] = path_info
|
environ['REQUEST_URI'] = path_info
|
||||||
if not eget('HTTP_HOST'):
|
if not eget('HTTP_HOST'):
|
||||||
environ['HTTP_HOST'] = \
|
environ['HTTP_HOST'] = \
|
||||||
'%s:%s' % (eget('SERVER_NAME'),eget('SERVER_PORT'))
|
'%s:%s' % (eget('SERVER_NAME'), eget('SERVER_PORT'))
|
||||||
|
|
||||||
|
|
||||||
def url_in(request, environ):
|
def url_in(request, environ):
|
||||||
@@ -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,48 +198,51 @@ 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() \
|
||||||
if request else 'http'
|
if request else 'http'
|
||||||
if host:
|
if host:
|
||||||
host_port = host if not port else host.split(':',1)[0]+':%s'%port
|
host_port = host if not port else host.split(':', 1)[0] + ':%s' % port
|
||||||
url = '%s://%s%s' % (scheme, host_port, url)
|
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.
|
||||||
"""
|
"""
|
||||||
status = int(str(http_response.status).split()[0])
|
status = int(str(http_response.status).split()[0])
|
||||||
if status>=399 and THREAD_LOCAL.routes.routes_onerror:
|
if status >= 399 and THREAD_LOCAL.routes.routes_onerror:
|
||||||
keys=set(('%s/%s' % (request.application, status),
|
keys = set(('%s/%s' % (request.application, status),
|
||||||
'%s/*' % (request.application),
|
'%s/*' % (request.application),
|
||||||
'*/%s' % (status),
|
'*/%s' % (status),
|
||||||
'*/*'))
|
'*/*'))
|
||||||
for (key,uri) in THREAD_LOCAL.routes.routes_onerror:
|
for (key, uri) in THREAD_LOCAL.routes.routes_onerror:
|
||||||
if key in keys:
|
if key in keys:
|
||||||
if uri == '!':
|
if uri == '!':
|
||||||
# do nothing!
|
# do nothing!
|
||||||
return http_response, environ
|
return http_response, environ
|
||||||
elif '?' in uri:
|
elif '?' in uri:
|
||||||
path_info, query_string = uri.split('?',1)
|
path_info, query_string = uri.split('?', 1)
|
||||||
query_string += '&'
|
query_string += '&'
|
||||||
else:
|
else:
|
||||||
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
|
||||||
message = 'You are being redirected <a href="%s">here</a>'
|
message = 'You are being redirected <a href="%s">here</a>'
|
||||||
return HTTP(303, message % url, Location=url), environ
|
return HTTP(303, message % url, Location=url), environ
|
||||||
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,29 +256,30 @@ 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])
|
||||||
if status>399 and THREAD_LOCAL.routes.routes_onerror:
|
if status > 399 and THREAD_LOCAL.routes.routes_onerror:
|
||||||
keys=set(('%s/%s' % (request.application, status),
|
keys = set(('%s/%s' % (request.application, status),
|
||||||
'%s/*' % (request.application),
|
'%s/*' % (request.application),
|
||||||
'*/%s' % (status),
|
'*/%s' % (status),
|
||||||
'*/*'))
|
'*/*'))
|
||||||
for (key,redir) in THREAD_LOCAL.routes.routes_onerror:
|
for (key, redir) in THREAD_LOCAL.routes.routes_onerror:
|
||||||
if key in keys:
|
if key in keys:
|
||||||
if redir == '!':
|
if redir == '!':
|
||||||
break
|
break
|
||||||
elif '?' in redir:
|
elif '?' in redir:
|
||||||
url = '%s&code=%s&ticket=%s&requested_uri=%s&request_url=%s' % \
|
url = '%s&code=%s&ticket=%s&requested_uri=%s&request_url=%s' % \
|
||||||
(redir,status,ticket,
|
(redir, status, ticket,
|
||||||
urllib.quote_plus(request.env.request_uri),
|
urllib.quote_plus(request.env.request_uri),
|
||||||
request.url)
|
request.url)
|
||||||
else:
|
else:
|
||||||
url = '%s?code=%s&ticket=%s&requested_uri=%s&request_url=%s' % \
|
url = '%s?code=%s&ticket=%s&requested_uri=%s&request_url=%s' % \
|
||||||
(redir,status,ticket,
|
(redir, status, ticket,
|
||||||
urllib.quote_plus(request.env.request_uri),
|
urllib.quote_plus(request.env.request_uri),
|
||||||
request.url)
|
request.url)
|
||||||
return HTTP(303,'You are being redirected <a href="%s">here</a>' % url,Location=url)
|
return HTTP(303, 'You are being redirected <a href="%s">here</a>' % url, Location=url)
|
||||||
return http_object
|
return http_object
|
||||||
|
|
||||||
|
|
||||||
@@ -305,7 +314,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
|
|||||||
path = abspath('applications', app, routes)
|
path = abspath('applications', app, routes)
|
||||||
if not exists(path):
|
if not exists(path):
|
||||||
return
|
return
|
||||||
data = read_file(path).replace('\r\n','\n')
|
data = read_file(path).replace('\r\n', '\n')
|
||||||
|
|
||||||
symbols = dict(app=app)
|
symbols = dict(app=app)
|
||||||
try:
|
try:
|
||||||
@@ -323,8 +332,8 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
|
|||||||
for items in symbols[sym]:
|
for items in symbols[sym]:
|
||||||
p[sym].append(compile_regex(*items))
|
p[sym].append(compile_regex(*items))
|
||||||
for sym in ('routes_onerror', 'routes_apps_raw',
|
for sym in ('routes_onerror', 'routes_apps_raw',
|
||||||
'error_handler','error_message', 'error_message_ticket',
|
'error_handler', 'error_message', 'error_message_ticket',
|
||||||
'default_application','default_controller', 'default_function',
|
'default_application', 'default_controller', 'default_function',
|
||||||
'logging'):
|
'logging'):
|
||||||
if sym in symbols:
|
if sym in symbols:
|
||||||
p[sym] = symbols[sym]
|
p[sym] = symbols[sym]
|
||||||
@@ -357,15 +366,15 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
|
|||||||
apppath = abspath('applications')
|
apppath = abspath('applications')
|
||||||
for appname in os.listdir(apppath):
|
for appname in os.listdir(apppath):
|
||||||
if not appname.startswith('.') and \
|
if not appname.startswith('.') and \
|
||||||
isdir(abspath(apppath,appname)) and \
|
isdir(abspath(apppath, appname)) and \
|
||||||
isdir(abspath(apppath,appname,'controllers')):
|
isdir(abspath(apppath, appname, 'controllers')):
|
||||||
all_apps.append(appname)
|
all_apps.append(appname)
|
||||||
if routers:
|
if routers:
|
||||||
router = Storage(routers.BASE) # new copy
|
router = Storage(routers.BASE) # new copy
|
||||||
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)):
|
||||||
@@ -374,7 +383,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
|
|||||||
if routers:
|
if routers:
|
||||||
load_routers(all_apps)
|
load_routers(all_apps)
|
||||||
|
|
||||||
else: # app
|
else: # app
|
||||||
params_apps[app] = p
|
params_apps[app] = p
|
||||||
if routers and p.routers:
|
if routers and p.routers:
|
||||||
if app in p.routers:
|
if app in p.routers:
|
||||||
@@ -406,8 +415,8 @@ 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):
|
||||||
k = k.replace(item, '(?P<anything>.*)')
|
k = k.replace(item, '(?P<anything>.*)')
|
||||||
@@ -419,6 +428,7 @@ def compile_regex(k, v, env=None):
|
|||||||
v = v.replace(item, r'\g<%s>' % item[1:])
|
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.functions = { router.default_controller: functions }
|
router.default_function) # legacy compatibility
|
||||||
|
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,19 +530,20 @@ 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']
|
||||||
host = e.get('http_host', e.get('SERVER_NAME','localhost')).lower()
|
host = e.get('http_host', e.get('SERVER_NAME', 'localhost')).lower()
|
||||||
i = host.find(':')
|
i = host.find(':')
|
||||||
if i > 0:
|
if i > 0:
|
||||||
host = host[:i]
|
host = host[:i]
|
||||||
key = '%s:%s://%s:%s %s' % \
|
key = '%s:%s://%s:%s %s' % \
|
||||||
(e.get('REMOTE_ADDR','localhost'),
|
(e.get('REMOTE_ADDR', 'localhost'),
|
||||||
e.get('wsgi.url_scheme', 'http').lower(), host,
|
e.get('wsgi.url_scheme', 'http').lower(), host,
|
||||||
e.get('REQUEST_METHOD', 'get').lower(), path)
|
e.get('REQUEST_METHOD', 'get').lower(), path)
|
||||||
for (regex, value, custom_env) in regexes:
|
for (regex, value, custom_env) in regexes:
|
||||||
@@ -540,6 +555,7 @@ def regex_uri(e, regexes, tag, default=None):
|
|||||||
log_rewrite('%s: [%s] -> %s (not rewritten)' % (tag, key, default))
|
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
|
||||||
@@ -553,10 +569,11 @@ def regex_select(env=None, app=None, request=None):
|
|||||||
app = regex_uri(env, params.routes_app, "routes_app")
|
app = regex_uri(env, params.routes_app, "routes_app")
|
||||||
THREAD_LOCAL.routes = params_apps.get(app, params)
|
THREAD_LOCAL.routes = params_apps.get(app, params)
|
||||||
else:
|
else:
|
||||||
THREAD_LOCAL.routes = params # default to base rewrite parameters
|
THREAD_LOCAL.routes = params # default to base rewrite parameters
|
||||||
log_rewrite("select routing parameters: %s" % THREAD_LOCAL.routes.name)
|
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
|
||||||
@@ -567,7 +584,7 @@ def regex_filter_in(e):
|
|||||||
"routes_in", e['PATH_INFO'])
|
"routes_in", e['PATH_INFO'])
|
||||||
rmatch = regex_redirect.match(path)
|
rmatch = regex_redirect.match(path)
|
||||||
if rmatch:
|
if rmatch:
|
||||||
raise HTTP(int(rmatch.group(1)),location=rmatch.group(2))
|
raise HTTP(int(rmatch.group(1)), location=rmatch.group(2))
|
||||||
items = path.split('?', 1)
|
items = path.split('?', 1)
|
||||||
e['PATH_INFO'] = items[0]
|
e['PATH_INFO'] = items[0]
|
||||||
if len(items) > 1:
|
if len(items) > 1:
|
||||||
@@ -581,7 +598,8 @@ 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,26 +614,28 @@ 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
|
||||||
# ##################################################
|
# ##################################################
|
||||||
|
|
||||||
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,
|
||||||
routes.error_message % 'invalid request',
|
routes.error_message % 'invalid request',
|
||||||
web2py_error='invalid path')
|
web2py_error='invalid path')
|
||||||
elif match.group('c')=='static':
|
elif match.group('c') == 'static':
|
||||||
application = match.group('a')
|
application = match.group('a')
|
||||||
version, filename = None, match.group('z')
|
version, filename = None, match.group('z')
|
||||||
items = filename.split('/',1)
|
items = filename.split('/', 1)
|
||||||
if regex_version.match(items[0]):
|
if regex_version.match(items[0]):
|
||||||
version,filename = items
|
version, filename = items
|
||||||
static_file = pjoin(request.env.applications_parent,
|
static_file = pjoin(request.env.applications_parent,
|
||||||
'applications', application,
|
'applications', application,
|
||||||
'static', filename)
|
'static', filename)
|
||||||
@@ -655,9 +675,9 @@ def regex_filter_out(url, e=None):
|
|||||||
if i > 0:
|
if i > 0:
|
||||||
host = host[:i]
|
host = host[:i]
|
||||||
items[0] = '%s:%s://%s:%s %s' % \
|
items[0] = '%s:%s://%s:%s %s' % \
|
||||||
(e.get('remote_addr', ''),
|
(e.get('remote_addr', ''),
|
||||||
e.get('wsgi_url_scheme', 'http').lower(), host,
|
e.get('wsgi_url_scheme', 'http').lower(), host,
|
||||||
e.get('request_method', 'get').lower(), items[0])
|
e.get('request_method', 'get').lower(), items[0])
|
||||||
else:
|
else:
|
||||||
items[0] = ':http://localhost:get %s' % items[0]
|
items[0] = ':http://localhost:get %s' % items[0]
|
||||||
for (regex, value, tmp) in routes.routes_out:
|
for (regex, value, tmp) in routes.routes_out:
|
||||||
@@ -671,7 +691,7 @@ def regex_filter_out(url, e=None):
|
|||||||
|
|
||||||
def filter_url(url, method='get', remote='0.0.0.0',
|
def filter_url(url, method='get', remote='0.0.0.0',
|
||||||
out=False, app=False, lang=None,
|
out=False, app=False, lang=None,
|
||||||
domain=(None,None), env=False, scheme=None,
|
domain=(None, None), env=False, scheme=None,
|
||||||
host=None, port=None):
|
host=None, port=None):
|
||||||
"""
|
"""
|
||||||
doctest/unittest interface to regex_filter_in() and regex_filter_out()
|
doctest/unittest interface to regex_filter_in() and regex_filter_out()
|
||||||
@@ -685,21 +705,21 @@ def filter_url(url, method='get', remote='0.0.0.0',
|
|||||||
k = len(uri)
|
k = len(uri)
|
||||||
if isinstance(domain, str):
|
if isinstance(domain, str):
|
||||||
domain = (domain, None)
|
domain = (domain, None)
|
||||||
(path_info, query_string) = (uri[:k], uri[k+1:])
|
(path_info, query_string) = (uri[:k], uri[k + 1:])
|
||||||
path_info = urllib.unquote(path_info) # simulate server
|
path_info = urllib.unquote(path_info) # simulate server
|
||||||
e = {
|
e = {
|
||||||
'REMOTE_ADDR': remote,
|
'REMOTE_ADDR': remote,
|
||||||
'REQUEST_METHOD': method,
|
'REQUEST_METHOD': method,
|
||||||
'wsgi.url_scheme': urlscheme,
|
'wsgi.url_scheme': urlscheme,
|
||||||
'HTTP_HOST': urlhost,
|
'HTTP_HOST': urlhost,
|
||||||
'REQUEST_URI': uri,
|
'REQUEST_URI': uri,
|
||||||
'PATH_INFO': path_info,
|
'PATH_INFO': path_info,
|
||||||
'QUERY_STRING': query_string,
|
'QUERY_STRING': query_string,
|
||||||
#for filter_out request.env use lowercase
|
#for filter_out request.env use lowercase
|
||||||
'remote_addr': remote,
|
'remote_addr': remote,
|
||||||
'request_method': method,
|
'request_method': method,
|
||||||
'wsgi_url_scheme': urlscheme,
|
'wsgi_url_scheme': urlscheme,
|
||||||
'http_host': urlhost
|
'http_host': urlhost
|
||||||
}
|
}
|
||||||
|
|
||||||
request = Storage()
|
request = Storage()
|
||||||
@@ -717,17 +737,19 @@ 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
|
||||||
assert len(items) >= 3, "at least /a/c/f is required"
|
assert len(items) >= 3, "at least /a/c/f is required"
|
||||||
a = items.pop(0)
|
a = items.pop(0)
|
||||||
c = items.pop(0)
|
c = items.pop(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:
|
||||||
@@ -762,22 +785,24 @@ def filter_err(status, application='app', ticket='tkt'):
|
|||||||
routes = THREAD_LOCAL.routes
|
routes = THREAD_LOCAL.routes
|
||||||
if status > 399 and routes.routes_onerror:
|
if status > 399 and routes.routes_onerror:
|
||||||
keys = set(('%s/%s' % (application, status),
|
keys = set(('%s/%s' % (application, status),
|
||||||
'%s/*' % (application),
|
'%s/*' % (application),
|
||||||
'*/%s' % (status),
|
'*/%s' % (status),
|
||||||
'*/*'))
|
'*/*'))
|
||||||
for (key,redir) in routes.routes_onerror:
|
for (key, redir) in routes.routes_onerror:
|
||||||
if key in keys:
|
if key in keys:
|
||||||
if redir == '!':
|
if redir == '!':
|
||||||
break
|
break
|
||||||
elif '?' in redir:
|
elif '?' in redir:
|
||||||
url = redir + '&' + 'code=%s&ticket=%s' % (status,ticket)
|
url = redir + '&' + 'code=%s&ticket=%s' % (status, ticket)
|
||||||
else:
|
else:
|
||||||
url = redir + '?' + 'code=%s&ticket=%s' % (status,ticket)
|
url = redir + '?' + 'code=%s&ticket=%s' % (status, ticket)
|
||||||
return url # redirection
|
return url # redirection
|
||||||
return status # no action
|
return status # no action
|
||||||
|
|
||||||
# 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
|
||||||
@@ -814,12 +840,13 @@ class MapUrlIn(object):
|
|||||||
self.args = List(path and path.split('/') or [])
|
self.args = List(path and path.split('/') or [])
|
||||||
|
|
||||||
# see http://www.python.org/dev/peps/pep-3333/#url-reconstruction for URL composition
|
# see http://www.python.org/dev/peps/pep-3333/#url-reconstruction for URL composition
|
||||||
self.remote_addr = self.env.get('REMOTE_ADDR','localhost')
|
self.remote_addr = self.env.get('REMOTE_ADDR', 'localhost')
|
||||||
self.scheme = self.env.get('wsgi.url_scheme', 'http').lower()
|
self.scheme = self.env.get('wsgi.url_scheme', 'http').lower()
|
||||||
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:
|
||||||
@@ -837,7 +864,7 @@ class MapUrlIn(object):
|
|||||||
for i in xrange(prefixlen):
|
for i in xrange(prefixlen):
|
||||||
if prefix[i] != self.args[i]:
|
if prefix[i] != self.args[i]:
|
||||||
return # prefix didn't match
|
return # prefix didn't match
|
||||||
self.args = List(self.args[prefixlen:]) # strip the prefix
|
self.args = List(self.args[prefixlen:]) # strip the prefix
|
||||||
|
|
||||||
def map_app(self):
|
def map_app(self):
|
||||||
"determine application name"
|
"determine application name"
|
||||||
@@ -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(
|
||||||
web2py_error="invalid application: '%s'" % self.application)
|
400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
|
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
|
||||||
@@ -908,8 +939,8 @@ class MapUrlIn(object):
|
|||||||
if len(self.args) == 1 and self.arg0 in self.router.root_static:
|
if len(self.args) == 1 and self.arg0 in self.router.root_static:
|
||||||
self.controller = self.request.controller = 'static'
|
self.controller = self.request.controller = 'static'
|
||||||
root_static_file = pjoin(self.request.env.applications_parent,
|
root_static_file = pjoin(self.request.env.applications_parent,
|
||||||
'applications', self.application,
|
'applications', self.application,
|
||||||
self.controller, self.arg0)
|
self.controller, self.arg0)
|
||||||
log_rewrite("route: root static=%s" % root_static_file)
|
log_rewrite("route: root static=%s" % root_static_file)
|
||||||
return root_static_file, None
|
return root_static_file, None
|
||||||
return None, None
|
return None, None
|
||||||
@@ -938,8 +969,9 @@ 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(
|
||||||
web2py_error='invalid controller')
|
400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
|
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,9 +1023,10 @@ 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
|
||||||
if not arg0 or functions and arg0 not in functions:
|
if not arg0 or functions and arg0 not in functions:
|
||||||
self.function = default_function or ""
|
self.function = default_function or ""
|
||||||
@@ -1005,14 +1039,17 @@ 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(
|
||||||
web2py_error='invalid function')
|
400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
|
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(
|
||||||
web2py_error='invalid extension')
|
400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
|
web2py_error='invalid extension')
|
||||||
|
|
||||||
def validate_args(self):
|
def validate_args(self):
|
||||||
'''
|
'''
|
||||||
@@ -1020,13 +1057,14 @@ 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(
|
||||||
web2py_error='invalid arg <%s>' % arg)
|
400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
|
web2py_error='invalid arg <%s>' % arg)
|
||||||
|
|
||||||
def sluggify(self):
|
def sluggify(self):
|
||||||
""
|
""
|
||||||
self.request.env.update(
|
self.request.env.update(
|
||||||
(k.lower().replace('.','_'),v) for k,v in self.env.iteritems())
|
(k.lower().replace('.', '_'), v) for k, v in self.env.iteritems())
|
||||||
|
|
||||||
def update_request(self):
|
def update_request(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:
|
||||||
@@ -1237,7 +1279,7 @@ class MapUrlOut(object):
|
|||||||
return None # use regex filter
|
return None # use regex filter
|
||||||
self.omit_lang() # try to omit language
|
self.omit_lang() # try to omit language
|
||||||
self.omit_acf() # try to omit a/c/f
|
self.omit_acf() # try to omit a/c/f
|
||||||
return self.build_acf() # build and return the /a/lang/c/f string
|
return self.build_acf() # build and return the /a/lang/c/f string
|
||||||
|
|
||||||
|
|
||||||
def map_url_in(request, env, app=False):
|
def map_url_in(request, env, app=False):
|
||||||
@@ -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:
|
||||||
|
|||||||
+121
-92
@@ -16,21 +16,23 @@ 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?
|
||||||
IS_JYTHON = platform.system() == 'Java' # Handle special cases for Jython
|
IS_JYTHON = platform.system() == 'Java' # Handle special cases for Jython
|
||||||
IGNORE_ERRORS_ON_CLOSE = set([errno.ECONNABORTED, errno.ECONNRESET])
|
IGNORE_ERRORS_ON_CLOSE = set([errno.ECONNABORTED, errno.ECONNRESET])
|
||||||
DEFAULT_LISTEN_QUEUE_SIZE = 5
|
DEFAULT_LISTEN_QUEUE_SIZE = 5
|
||||||
DEFAULT_MIN_THREADS = 10
|
DEFAULT_MIN_THREADS = 10
|
||||||
DEFAULT_MAX_THREADS = 0
|
DEFAULT_MAX_THREADS = 0
|
||||||
DEFAULTS = dict(LISTEN_QUEUE_SIZE = DEFAULT_LISTEN_QUEUE_SIZE,
|
DEFAULTS = dict(LISTEN_QUEUE_SIZE=DEFAULT_LISTEN_QUEUE_SIZE,
|
||||||
MIN_THREADS = DEFAULT_MIN_THREADS,
|
MIN_THREADS=DEFAULT_MIN_THREADS,
|
||||||
MAX_THREADS = DEFAULT_MAX_THREADS)
|
MAX_THREADS=DEFAULT_MAX_THREADS)
|
||||||
|
|
||||||
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,13 +321,12 @@ 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."""
|
||||||
@@ -446,18 +455,18 @@ class Listener(Thread):
|
|||||||
elif not os.path.exists(interface[2]):
|
elif not os.path.exists(interface[2]):
|
||||||
data = (interface[2], interface[0], interface[1])
|
data = (interface[2], interface[0], interface[1])
|
||||||
self.err_log.error("Cannot find key file "
|
self.err_log.error("Cannot find key file "
|
||||||
"'%s'. Cannot bind to %s:%s" % data)
|
"'%s'. Cannot bind to %s:%s" % data)
|
||||||
return
|
return
|
||||||
elif not os.path.exists(interface[3]):
|
elif not os.path.exists(interface[3]):
|
||||||
data = (interface[3], interface[0], interface[1])
|
data = (interface[3], interface[0], interface[1])
|
||||||
self.err_log.error("Cannot find certificate file "
|
self.err_log.error("Cannot find certificate file "
|
||||||
"'%s'. Cannot bind to %s:%s" % data)
|
"'%s'. Cannot bind to %s:%s" % data)
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.clientcert_req and not os.path.exists(interface[4]):
|
if self.clientcert_req and not os.path.exists(interface[4]):
|
||||||
data = (interface[4], interface[0], interface[1])
|
data = (interface[4], interface[0], interface[1])
|
||||||
self.err_log.error("Cannot find root ca certificate file "
|
self.err_log.error("Cannot find root ca certificate file "
|
||||||
"'%s'. Cannot bind to %s:%s" % data)
|
"'%s'. Cannot bind to %s:%s" % data)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set socket options
|
# Set socket options
|
||||||
@@ -499,18 +508,18 @@ class Listener(Thread):
|
|||||||
ca_certs = self.interface[4]
|
ca_certs = self.interface[4]
|
||||||
cert_reqs = ssl.CERT_OPTIONAL
|
cert_reqs = ssl.CERT_OPTIONAL
|
||||||
sock = ssl.wrap_socket(sock,
|
sock = ssl.wrap_socket(sock,
|
||||||
keyfile = self.interface[2],
|
keyfile=self.interface[2],
|
||||||
certfile = self.interface[3],
|
certfile=self.interface[3],
|
||||||
server_side = True,
|
server_side=True,
|
||||||
cert_reqs = cert_reqs,
|
cert_reqs=cert_reqs,
|
||||||
ca_certs = ca_certs,
|
ca_certs=ca_certs,
|
||||||
ssl_version = ssl.PROTOCOL_SSLv23)
|
ssl_version=ssl.PROTOCOL_SSLv23)
|
||||||
else:
|
else:
|
||||||
sock = ssl.wrap_socket(sock,
|
sock = ssl.wrap_socket(sock,
|
||||||
keyfile = self.interface[2],
|
keyfile=self.interface[2],
|
||||||
certfile = self.interface[3],
|
certfile=self.interface[3],
|
||||||
server_side = True,
|
server_side=True,
|
||||||
ssl_version = ssl.PROTOCOL_SSLv23)
|
ssl_version=ssl.PROTOCOL_SSLv23)
|
||||||
|
|
||||||
except SSLError:
|
except SSLError:
|
||||||
# Generally this happens when an HTTP request is received on a
|
# Generally this happens when an HTTP request is received on a
|
||||||
@@ -598,19 +607,17 @@ 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."""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
interfaces = ('127.0.0.1', 8000),
|
interfaces=('127.0.0.1', 8000),
|
||||||
method = 'wsgi',
|
method = 'wsgi',
|
||||||
app_info = None,
|
app_info = None,
|
||||||
min_threads = None,
|
min_threads = None,
|
||||||
@@ -650,15 +657,16 @@ class Rocket(object):
|
|||||||
self.active_queue = Queue()
|
self.active_queue = Queue()
|
||||||
|
|
||||||
self._threadpool = ThreadPool(get_method(method),
|
self._threadpool = ThreadPool(get_method(method),
|
||||||
app_info = app_info,
|
app_info=app_info,
|
||||||
active_queue = self.active_queue,
|
active_queue=self.active_queue,
|
||||||
monitor_queue = self.monitor_queue,
|
monitor_queue=self.monitor_queue,
|
||||||
min_threads = min_threads,
|
min_threads=min_threads,
|
||||||
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(
|
||||||
for ndx in range(len(self.listeners)-1, 0, -1):
|
i, queue_size, self.active_queue) for i in self.interfaces]
|
||||||
|
for ndx in range(len(self.listeners) - 1, 0, -1):
|
||||||
if not self.listeners[ndx].ready:
|
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:
|
||||||
@@ -730,7 +739,7 @@ class Rocket(object):
|
|||||||
|
|
||||||
return self.stop()
|
return self.stop()
|
||||||
|
|
||||||
def stop(self, stoplogging = False):
|
def stop(self, stoplogging=False):
|
||||||
log.info('Stopping %s' % SERVER_SOFTWARE)
|
log.info('Stopping %s' % SERVER_SOFTWARE)
|
||||||
|
|
||||||
self.startstop_lock.acquire()
|
self.startstop_lock.acquire()
|
||||||
@@ -773,23 +782,24 @@ 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,
|
||||||
server_name = None,
|
server_name=None,
|
||||||
max = -1,
|
max=-1,
|
||||||
request_queue_size = 5,
|
request_queue_size=5,
|
||||||
timeout = 10,
|
timeout=10,
|
||||||
shutdown_timeout = 5):
|
shutdown_timeout=5):
|
||||||
""" A Cherrypy wsgiserver-compatible wrapper. """
|
""" A Cherrypy wsgiserver-compatible wrapper. """
|
||||||
max_threads = max
|
max_threads = max
|
||||||
if max_threads < 0:
|
if max_threads < 0:
|
||||||
max_threads = 0
|
max_threads = 0
|
||||||
return Rocket(bind_addr, 'wsgi', {'wsgi_app': wsgi_app},
|
return Rocket(bind_addr, 'wsgi', {'wsgi_app': wsgi_app},
|
||||||
min_threads = numthreads,
|
min_threads=numthreads,
|
||||||
max_threads = max_threads,
|
max_threads=max_threads,
|
||||||
queue_size = request_queue_size,
|
queue_size=request_queue_size,
|
||||||
timeout = timeout)
|
timeout=timeout)
|
||||||
|
|
||||||
# Monolithic build...end of module: rocket\main.py
|
# Monolithic build...end of module: rocket\main.py
|
||||||
# Monolithic build...start of module: rocket\monitor.py
|
# Monolithic build...start of module: rocket\monitor.py
|
||||||
@@ -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."""
|
||||||
@@ -1011,7 +1024,7 @@ class ThreadPool:
|
|||||||
self.alive = False
|
self.alive = False
|
||||||
|
|
||||||
# TODO - Optimize this based on some real-world usage data
|
# TODO - Optimize this based on some real-world usage data
|
||||||
self.grow_threshold = int(max_threads/10) + 2
|
self.grow_threshold = int(max_threads / 10) + 2
|
||||||
|
|
||||||
if not isinstance(app_info, dict):
|
if not isinstance(app_info, dict):
|
||||||
app_info = dict()
|
app_info = dict()
|
||||||
@@ -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
|
||||||
@@ -1223,7 +1238,7 @@ class Worker(Thread):
|
|||||||
self.req_log.addHandler(NullHandler())
|
self.req_log.addHandler(NullHandler())
|
||||||
|
|
||||||
# Error Log
|
# Error Log
|
||||||
self.err_log = logging.getLogger('Rocket.Errors.'+self.getName())
|
self.err_log = logging.getLogger('Rocket.Errors.' + self.getName())
|
||||||
self.err_log.addHandler(NullHandler())
|
self.err_log.addHandler(NullHandler())
|
||||||
|
|
||||||
def _handleError(self, typ, val, tb):
|
def _handleError(self, typ, val, tb):
|
||||||
@@ -1301,11 +1316,11 @@ class Worker(Thread):
|
|||||||
self.err_log.debug('Serving a request')
|
self.err_log.debug('Serving a request')
|
||||||
try:
|
try:
|
||||||
self.run_app(conn)
|
self.run_app(conn)
|
||||||
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)
|
request_line=self.request_line)
|
||||||
self.req_log.info(LOG_LINE % log_info)
|
self.req_log.info(LOG_LINE % log_info)
|
||||||
except:
|
except:
|
||||||
exc = sys.exc_info()
|
exc = sys.exc_info()
|
||||||
@@ -1314,11 +1329,12 @@ class Worker(Thread):
|
|||||||
break
|
break
|
||||||
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(
|
||||||
status = self.status.split(' ')[0],
|
).strftime('%c'),
|
||||||
size = self.size,
|
status=self.status.split(' ')[0],
|
||||||
request_line = self.request_line + ' - not stopping')
|
size=self.size,
|
||||||
|
request_line=self.request_line + ' - not stopping')
|
||||||
self.req_log.info(LOG_LINE % log_info)
|
self.req_log.info(LOG_LINE % log_info)
|
||||||
|
|
||||||
if self.closeConnection:
|
if self.closeConnection:
|
||||||
@@ -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
|
||||||
@@ -1408,11 +1426,12 @@ class Worker(Thread):
|
|||||||
raise BadRequest
|
raise BadRequest
|
||||||
|
|
||||||
req = match.groupdict()
|
req = match.groupdict()
|
||||||
for k,v in req.iteritems():
|
for k, v in req.iteritems():
|
||||||
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
|
||||||
|
|
||||||
@@ -1421,15 +1440,15 @@ class Worker(Thread):
|
|||||||
try:
|
try:
|
||||||
method, uri, proto = d.split(' ')
|
method, uri, proto = d.split(' ')
|
||||||
if not proto.startswith('HTTP') or \
|
if not proto.startswith('HTTP') or \
|
||||||
proto[-3:] not in ('1.0', '1.1') or \
|
proto[-3:] not in ('1.0', '1.1') or \
|
||||||
method not in HTTP_METHODS:
|
method not in HTTP_METHODS:
|
||||||
self.send_response('400 Bad Request')
|
self.send_response('400 Bad Request')
|
||||||
raise BadRequest
|
raise BadRequest
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.send_response('400 Bad Request')
|
self.send_response('400 Bad Request')
|
||||||
raise BadRequest
|
raise BadRequest
|
||||||
|
|
||||||
req = dict(method=method, protocol = proto)
|
req = dict(method=method, protocol=proto)
|
||||||
scheme = ''
|
scheme = ''
|
||||||
host = ''
|
host = ''
|
||||||
if uri == '*' or uri.startswith('/'):
|
if uri == '*' or uri.startswith('/'):
|
||||||
@@ -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,9 +1482,10 @@ 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
|
||||||
elif l[0] in ' \t' and lname:
|
elif l[0] in ' \t' and lname:
|
||||||
# Some headers take more than one line
|
# Some headers take more than one line
|
||||||
@@ -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,9 +1613,10 @@ 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(
|
||||||
'wsgi.multithread': multithreaded,
|
{'SERVER_SOFTWARE': self.app_info['server_software'],
|
||||||
})
|
'wsgi.multithread': multithreaded,
|
||||||
|
})
|
||||||
self.base_environ.update(BASE_ENV)
|
self.base_environ.update(BASE_ENV)
|
||||||
|
|
||||||
# Grab our application
|
# Grab our application
|
||||||
@@ -1614,7 +1640,7 @@ class WSGIWorker(Worker):
|
|||||||
environ = self.base_environ.copy()
|
environ = self.base_environ.copy()
|
||||||
|
|
||||||
# Grab the headers
|
# Grab the headers
|
||||||
self.read_headers(sock_file,environ)
|
self.read_headers(sock_file, environ)
|
||||||
|
|
||||||
# Add CGI Variables
|
# Add CGI Variables
|
||||||
environ['SERVER_PORT'] = str(conn.server_port)
|
environ['SERVER_PORT'] = str(conn.server_port)
|
||||||
@@ -1644,7 +1670,7 @@ class WSGIWorker(Worker):
|
|||||||
peercert = conn.socket.getpeercert(binary_form=True)
|
peercert = conn.socket.getpeercert(binary_form=True)
|
||||||
environ['SSL_CLIENT_RAW_CERT'] = \
|
environ['SSL_CLIENT_RAW_CERT'] = \
|
||||||
peercert and ssl.DER_cert_to_PEM_cert(peercert)
|
peercert and ssl.DER_cert_to_PEM_cert(peercert)
|
||||||
except Exception,e:
|
except Exception, e:
|
||||||
print e
|
print e
|
||||||
|
|
||||||
if environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked':
|
if environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked':
|
||||||
@@ -1822,7 +1848,7 @@ class WSGIWorker(Worker):
|
|||||||
if __debug__:
|
if __debug__:
|
||||||
self.err_log.debug('Finally closing output and sock_file')
|
self.err_log.debug('Finally closing output and sock_file')
|
||||||
|
|
||||||
if hasattr(output,'close'):
|
if hasattr(output, 'close'):
|
||||||
output.close()
|
output.close()
|
||||||
|
|
||||||
sock_file.close()
|
sock_file.close()
|
||||||
@@ -1833,19 +1859,21 @@ class WSGIWorker(Worker):
|
|||||||
# the following code is not part of Rocket but was added in web2py for testing purposes
|
# 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
|
||||||
types = {'htm': 'text/html','html': 'text/html','gif': 'image/gif',
|
types = {'htm': 'text/html', 'html': 'text/html', 'gif': 'image/gif',
|
||||||
'jpg': 'image/jpeg','png': 'image/png','pdf': 'applications/pdf'}
|
'jpg': 'image/jpeg', 'png': 'image/png', 'pdf': 'applications/pdf'}
|
||||||
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(
|
||||||
type = types.get(path.split('.')[-1],'text')
|
static_folder, environ['PATH_INFO'][1:] or 'index.html')
|
||||||
|
type = types.get(path.split('.')[-1], 'text')
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
try:
|
try:
|
||||||
data = open(path,'rb').read()
|
data = open(path, 'rb').read()
|
||||||
start_response('200 OK', [('Content-Type', type)])
|
start_response('200 OK', [('Content-Type', type)])
|
||||||
except IOError:
|
except IOError:
|
||||||
start_response('404 NOT FOUND', [])
|
start_response('404 NOT FOUND', [])
|
||||||
@@ -1858,21 +1886,22 @@ def demo_app(environ, start_response):
|
|||||||
data = '<html><body><h1>Hello from Rocket Web Server</h1></body></html>'
|
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()
|
||||||
parser.add_option("-i", "--ip", dest="ip",default="127.0.0.1",
|
parser.add_option("-i", "--ip", dest="ip", default="127.0.0.1",
|
||||||
help="ip address of the network interface")
|
help="ip address of the network interface")
|
||||||
parser.add_option("-p", "--port", dest="port",default="8000",
|
parser.add_option("-p", "--port", dest="port", default="8000",
|
||||||
help="post where to run web server")
|
help="post where to run web server")
|
||||||
parser.add_option("-s", "--static", dest="static",default=None,
|
parser.add_option("-s", "--static", dest="static", default=None,
|
||||||
help="folder containing static files")
|
help="folder containing static files")
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
global static_folder
|
global static_folder
|
||||||
static_folder = options.static
|
static_folder = options.static
|
||||||
print 'Rocket running on %s:%s' % (options.ip, options.port)
|
print 'Rocket running on %s:%s' % (options.ip, options.port)
|
||||||
r=Rocket((options.ip,int(options.port)),'wsgi', {'wsgi_app':demo_app})
|
r = Rocket((options.ip, int(options.port)), 'wsgi', {'wsgi_app': demo_app})
|
||||||
r.start()
|
r.start()
|
||||||
|
|
||||||
if __name__=='__main__':
|
if __name__ == '__main__':
|
||||||
demo()
|
demo()
|
||||||
+19
-17
@@ -47,12 +47,12 @@ class XssCleaner(HTMLParser):
|
|||||||
'code',
|
'code',
|
||||||
'pre',
|
'pre',
|
||||||
'img/',
|
'img/',
|
||||||
],
|
],
|
||||||
allowed_attributes={'a': ['href', 'title'], 'img': ['src', 'alt'
|
allowed_attributes={'a': ['href', 'title'], 'img': ['src', 'alt'
|
||||||
], 'blockquote': ['type']},
|
], 'blockquote': ['type']},
|
||||||
fmt=AbstractFormatter,
|
fmt=AbstractFormatter,
|
||||||
strip_disallowed = False
|
strip_disallowed=False
|
||||||
):
|
):
|
||||||
|
|
||||||
HTMLParser.__init__(self, fmt)
|
HTMLParser.__init__(self, fmt)
|
||||||
self.result = ''
|
self.result = ''
|
||||||
@@ -103,7 +103,7 @@ class XssCleaner(HTMLParser):
|
|||||||
tag,
|
tag,
|
||||||
method,
|
method,
|
||||||
attrs,
|
attrs,
|
||||||
):
|
):
|
||||||
if tag not in self.permitted_tags:
|
if tag not in self.permitted_tags:
|
||||||
if self.strip_disallowed:
|
if self.strip_disallowed:
|
||||||
self.in_disallowed = True
|
self.in_disallowed = True
|
||||||
@@ -114,16 +114,16 @@ class XssCleaner(HTMLParser):
|
|||||||
if tag in self.allowed_attributes:
|
if tag in self.allowed_attributes:
|
||||||
attrs = dict(attrs)
|
attrs = dict(attrs)
|
||||||
self.allowed_attributes_here = [x for x in
|
self.allowed_attributes_here = [x for x in
|
||||||
self.allowed_attributes[tag] if x in attrs
|
self.allowed_attributes[tag] if x in attrs
|
||||||
and len(attrs[x]) > 0]
|
and len(attrs[x]) > 0]
|
||||||
for attribute in self.allowed_attributes_here:
|
for attribute in self.allowed_attributes_here:
|
||||||
if attribute in ['href', 'src', 'background']:
|
if attribute in ['href', 'src', 'background']:
|
||||||
if self.url_is_acceptable(attrs[attribute]):
|
if self.url_is_acceptable(attrs[attribute]):
|
||||||
bt += ' %s="%s"' % (attribute,
|
bt += ' %s="%s"' % (attribute,
|
||||||
attrs[attribute])
|
attrs[attribute])
|
||||||
else:
|
else:
|
||||||
bt += ' %s=%s' % (xssescape(attribute),
|
bt += ' %s=%s' % (xssescape(attribute),
|
||||||
quoteattr(attrs[attribute]))
|
quoteattr(attrs[attribute]))
|
||||||
if bt == '<a' or bt == '<img':
|
if bt == '<a' or bt == '<img':
|
||||||
return
|
return
|
||||||
if tag in self.requires_no_close:
|
if tag in self.requires_no_close:
|
||||||
@@ -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:
|
||||||
@@ -209,16 +210,17 @@ def sanitize(text, permitted_tags=[
|
|||||||
'code',
|
'code',
|
||||||
'pre',
|
'pre',
|
||||||
'img/',
|
'img/',
|
||||||
'h1','h2','h3','h4','h5','h6',
|
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
||||||
'table','tr','td','div',
|
'table', 'tr', 'td', 'div',
|
||||||
],
|
],
|
||||||
allowed_attributes = {
|
allowed_attributes={
|
||||||
'a': ['href', 'title'],
|
'a': ['href', 'title'],
|
||||||
'img': ['src', 'alt'],
|
'img': ['src', 'alt'],
|
||||||
'blockquote': ['type'],
|
'blockquote': ['type'],
|
||||||
'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)
|
||||||
+244
-211
@@ -106,7 +106,7 @@ DISABLED = 'DISABLED'
|
|||||||
KILL = 'KILL'
|
KILL = 'KILL'
|
||||||
EXPIRED = 'EXPIRED'
|
EXPIRED = 'EXPIRED'
|
||||||
SECONDS = 1
|
SECONDS = 1
|
||||||
HEARTBEAT = 3*SECONDS
|
HEARTBEAT = 3 * SECONDS
|
||||||
MAXHIBERNATION = 10
|
MAXHIBERNATION = 10
|
||||||
CLEAROUT = '!clear!'
|
CLEAROUT = '!clear!'
|
||||||
|
|
||||||
@@ -114,20 +114,23 @@ 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))
|
||||||
self.app = app
|
self.app = app
|
||||||
self.function = function
|
self.function = function
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
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)
|
||||||
if tb:
|
if tb:
|
||||||
logger.debug(' traceback: %s' % tb)
|
logger.debug(' traceback: %s' % tb)
|
||||||
@@ -137,19 +140,23 @@ 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]):
|
||||||
print 'click',i
|
print 'click', i
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
return 'done'
|
return 'done'
|
||||||
|
|
||||||
#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,19 +167,21 @@ 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():
|
||||||
if isinstance(k, unicode):
|
if isinstance(k, unicode):
|
||||||
k = k.encode('utf-8')
|
k = k.encode('utf-8')
|
||||||
if isinstance(v, unicode):
|
if isinstance(v, unicode):
|
||||||
v = v.encode('utf-8')
|
v = v.encode('utf-8')
|
||||||
elif isinstance(v, list):
|
elif isinstance(v, list):
|
||||||
v = _decode_list(v)
|
v = _decode_list(v)
|
||||||
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,11 +191,14 @@ 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)
|
||||||
|
|
||||||
stdout = LogOutput(out)
|
stdout = LogOutput(out)
|
||||||
@@ -200,8 +212,8 @@ def executor(queue,task, out):
|
|||||||
logging.getLogger().setLevel(logging.WARN)
|
logging.getLogger().setLevel(logging.WARN)
|
||||||
# Get controller-specific subdirectory if task.app is of
|
# Get controller-specific subdirectory if task.app is of
|
||||||
# form 'app/controller'
|
# form 'app/controller'
|
||||||
(a,c,f) = parse_path_info(task.app)
|
(a, c, f) = parse_path_info(task.app)
|
||||||
_env = env(a=a,c=c,import_models=True)
|
_env = env(a=a, c=c, import_models=True)
|
||||||
logging.getLogger().setLevel(level)
|
logging.getLogger().setLevel(level)
|
||||||
scheduler = current._scheduler
|
scheduler = current._scheduler
|
||||||
f = task.function
|
f = task.function
|
||||||
@@ -212,22 +224,24 @@ 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)
|
||||||
result = dumps(_function(*args,**vars))
|
result = dumps(_function(*args, **vars))
|
||||||
else:
|
else:
|
||||||
### for testing purpose only
|
### for testing purpose only
|
||||||
result = eval(task.function)(
|
result = eval(task.function)(
|
||||||
*loads(task.args, object_hook=_decode_dict),
|
*loads(task.args, object_hook=_decode_dict),
|
||||||
**loads(task.vars, object_hook=_decode_dict))
|
**loads(task.vars, object_hook=_decode_dict))
|
||||||
queue.put(TaskReport(COMPLETED, result=result))
|
queue.put(TaskReport(COMPLETED, result=result))
|
||||||
except BaseException,e:
|
except BaseException, e:
|
||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
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)
|
||||||
@@ -235,7 +249,7 @@ class MetaScheduler(threading.Thread):
|
|||||||
self.have_heartbeat = True # set to False to kill
|
self.have_heartbeat = True # set to False to kill
|
||||||
self.empty_runs = 0
|
self.empty_runs = 0
|
||||||
|
|
||||||
def async(self,task):
|
def async(self, task):
|
||||||
"""
|
"""
|
||||||
starts the background process and returns:
|
starts the background process and returns:
|
||||||
('ok',result,output)
|
('ok',result,output)
|
||||||
@@ -247,7 +261,7 @@ class MetaScheduler(threading.Thread):
|
|||||||
sr = db.scheduler_run
|
sr = db.scheduler_run
|
||||||
out = multiprocessing.Queue()
|
out = multiprocessing.Queue()
|
||||||
queue = multiprocessing.Queue(maxsize=1)
|
queue = multiprocessing.Queue(maxsize=1)
|
||||||
p = multiprocessing.Process(target=executor,args=(queue,task,out))
|
p = multiprocessing.Process(target=executor, args=(queue, task, out))
|
||||||
self.process = p
|
self.process = p
|
||||||
logger.debug(' task starting')
|
logger.debug(' task starting')
|
||||||
p.start()
|
p.start()
|
||||||
@@ -264,11 +278,11 @@ class MetaScheduler(threading.Thread):
|
|||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
while p.is_alive() and (
|
while p.is_alive() and (
|
||||||
not task.timeout or time.time()-start < task.timeout):
|
not task.timeout or time.time() - start < task.timeout):
|
||||||
if tout:
|
if tout:
|
||||||
try:
|
try:
|
||||||
logger.debug(' partial output saved')
|
logger.debug(' partial output saved')
|
||||||
db(sr.id==task.run_id).update(output = task_output)
|
db(sr.id == task.run_id).update(output=task_output)
|
||||||
db.commit()
|
db.commit()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@@ -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:
|
||||||
@@ -322,7 +337,7 @@ class MetaScheduler(threading.Thread):
|
|||||||
try:
|
try:
|
||||||
self.process.terminate()
|
self.process.terminate()
|
||||||
except:
|
except:
|
||||||
pass # no process to terminate
|
pass # no process to terminate
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
""" the thread that sends heartbeat """
|
""" the thread that sends heartbeat """
|
||||||
@@ -334,19 +349,19 @@ class MetaScheduler(threading.Thread):
|
|||||||
def start_heartbeats(self):
|
def start_heartbeats(self):
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def send_heartbeat(self,counter):
|
def send_heartbeat(self, counter):
|
||||||
print 'thum'
|
print 'thum'
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
def pop_task(self):
|
def pop_task(self):
|
||||||
return Task(
|
return Task(
|
||||||
app = None,
|
app=None,
|
||||||
function = 'demo_function',
|
function='demo_function',
|
||||||
timeout = 7,
|
timeout=7,
|
||||||
args = '[2]',
|
args='[2]',
|
||||||
vars = '{}')
|
vars='{}')
|
||||||
|
|
||||||
def report_task(self,task,task_report):
|
def report_task(self, task, task_report):
|
||||||
print 'reporting task'
|
print 'reporting task'
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -361,14 +376,16 @@ class MetaScheduler(threading.Thread):
|
|||||||
task = self.pop_task()
|
task = self.pop_task()
|
||||||
if task:
|
if task:
|
||||||
self.empty_runs = 0
|
self.empty_runs = 0
|
||||||
self.report_task(task,self.async(task))
|
self.report_task(task, self.async(task))
|
||||||
else:
|
else:
|
||||||
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,33 +396,35 @@ 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
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,myclass=list,parse=False):
|
def __init__(self, myclass=list, parse=False):
|
||||||
self.myclass = myclass
|
self.myclass = myclass
|
||||||
self.parse = parse
|
self.parse = parse
|
||||||
|
|
||||||
def __call__(self,value):
|
def __call__(self, value):
|
||||||
from gluon import current
|
from gluon import current
|
||||||
try:
|
try:
|
||||||
obj = loads(value)
|
obj = loads(value)
|
||||||
except:
|
except:
|
||||||
return (value,current.T('invalid json'))
|
return (value, current.T('invalid json'))
|
||||||
else:
|
else:
|
||||||
if isinstance(obj,self.myclass):
|
if isinstance(obj, self.myclass):
|
||||||
if self.parse:
|
if self.parse:
|
||||||
return (obj,None)
|
return (obj, None)
|
||||||
else:
|
else:
|
||||||
return (value,None)
|
return (value, None)
|
||||||
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,
|
||||||
max_empty_runs=0, discard_results=False, utc_time=False):
|
max_empty_runs=0, discard_results=False, utc_time=False):
|
||||||
|
|
||||||
MetaScheduler.__init__(self)
|
MetaScheduler.__init__(self)
|
||||||
@@ -415,8 +434,9 @@ 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(
|
||||||
self.worker_status = RUNNING, 1 #tuple containing status as recorded in
|
) + '#' + str(os.getpid())
|
||||||
|
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
|
||||||
#worker acting on the scheduler_worker table)
|
#worker acting on the scheduler_worker table)
|
||||||
@@ -429,86 +449,85 @@ class Scheduler(MetaScheduler):
|
|||||||
from gluon import current
|
from gluon import current
|
||||||
current._scheduler = self
|
current._scheduler = self
|
||||||
|
|
||||||
self.define_tables(db,migrate=migrate)
|
self.define_tables(db, migrate=migrate)
|
||||||
|
|
||||||
def now(self):
|
def now(self):
|
||||||
return self.utc_time and datetime.datetime.utcnow() or datetime.datetime.now()
|
return self.utc_time and datetime.datetime.utcnow() or datetime.datetime.now()
|
||||||
|
|
||||||
def set_requirements(self, scheduler_task):
|
def set_requirements(self, scheduler_task):
|
||||||
from gluon import current
|
from gluon import current
|
||||||
if hasattr(current,'request'):
|
if hasattr(current, 'request'):
|
||||||
scheduler_task.application_name.default= '%s/%s' % (
|
scheduler_task.application_name.default = '%s/%s' % (
|
||||||
current.request.application, current.request.controller
|
current.request.application, current.request.controller
|
||||||
)
|
)
|
||||||
|
|
||||||
def define_tables(self,db,migrate):
|
def define_tables(self, db, migrate):
|
||||||
from gluon.dal import DEFAULT
|
from gluon.dal import DEFAULT
|
||||||
logger.debug('defining tables (migrate=%s)' % migrate)
|
logger.debug('defining tables (migrate=%s)' % migrate)
|
||||||
now = self.now
|
now = self.now
|
||||||
db.define_table(
|
db.define_table(
|
||||||
'scheduler_task',
|
'scheduler_task',
|
||||||
Field('application_name',requires=IS_NOT_EMPTY(),
|
Field('application_name', requires=IS_NOT_EMPTY(),
|
||||||
default=None,writable=False),
|
default=None, writable=False),
|
||||||
Field('task_name',default=None),
|
Field('task_name', default=None),
|
||||||
Field('group_name',default='main'),
|
Field('group_name', default='main'),
|
||||||
Field('status',requires=IS_IN_SET(TASK_STATUS),
|
Field('status', requires=IS_IN_SET(TASK_STATUS),
|
||||||
default=QUEUED,writable=False),
|
default=QUEUED, writable=False),
|
||||||
Field('function_name',
|
Field('function_name',
|
||||||
requires=IS_IN_SET(sorted(self.tasks.keys()))
|
requires=IS_IN_SET(sorted(self.tasks.keys()))
|
||||||
if self.tasks else DEFAULT),
|
if self.tasks else DEFAULT),
|
||||||
Field('uuid', requires=IS_NOT_IN_DB(db, 'scheduler_task.uuid'),
|
Field('uuid', requires=IS_NOT_IN_DB(db, 'scheduler_task.uuid'),
|
||||||
unique=True, default=web2py_uuid),
|
unique=True, default=web2py_uuid),
|
||||||
Field('args','text',default='[]',requires=TYPE(list)),
|
Field('args', 'text', default='[]', requires=TYPE(list)),
|
||||||
Field('vars','text',default='{}',requires=TYPE(dict)),
|
Field('vars', 'text', default='{}', requires=TYPE(dict)),
|
||||||
Field('enabled','boolean',default=True),
|
Field('enabled', 'boolean', default=True),
|
||||||
Field('start_time','datetime',default=now,
|
Field('start_time', 'datetime', default=now,
|
||||||
requires = IS_DATETIME()),
|
requires=IS_DATETIME()),
|
||||||
Field('next_run_time','datetime',default=now),
|
Field('next_run_time', 'datetime', default=now),
|
||||||
Field('stop_time','datetime'),
|
Field('stop_time', 'datetime'),
|
||||||
Field('repeats','integer',default=1,comment="0=unlimited",
|
Field('repeats', 'integer', default=1, comment="0=unlimited",
|
||||||
requires=IS_INT_IN_RANGE(0, None)),
|
requires=IS_INT_IN_RANGE(0, None)),
|
||||||
Field('retry_failed', 'integer', default=0, comment="-1=unlimited",
|
Field('retry_failed', 'integer', default=0, comment="-1=unlimited",
|
||||||
requires=IS_INT_IN_RANGE(-1, None)),
|
requires=IS_INT_IN_RANGE(-1, None)),
|
||||||
Field('period','integer',default=60,comment='seconds',
|
Field('period', 'integer', default=60, comment='seconds',
|
||||||
requires=IS_INT_IN_RANGE(0, None)),
|
requires=IS_INT_IN_RANGE(0, None)),
|
||||||
Field('timeout','integer',default=60,comment='seconds',
|
Field('timeout', 'integer', default=60, comment='seconds',
|
||||||
requires=IS_INT_IN_RANGE(0, None)),
|
requires=IS_INT_IN_RANGE(0, None)),
|
||||||
Field('sync_output', 'integer', default=0,
|
Field('sync_output', 'integer', default=0,
|
||||||
comment="update output every n sec: 0=never",
|
comment="update output every n sec: 0=never",
|
||||||
requires=IS_INT_IN_RANGE(0, None)),
|
requires=IS_INT_IN_RANGE(0, None)),
|
||||||
Field('times_run','integer',default=0,writable=False),
|
Field('times_run', 'integer', default=0, writable=False),
|
||||||
Field('times_failed','integer',default=0,writable=False),
|
Field('times_failed', 'integer', default=0, writable=False),
|
||||||
Field('last_run_time','datetime',writable=False,readable=False),
|
Field('last_run_time', 'datetime', writable=False, readable=False),
|
||||||
Field('assigned_worker_name',default='',writable=False),
|
Field('assigned_worker_name', default='', writable=False),
|
||||||
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'),
|
||||||
Field('status',requires=IS_IN_SET(RUN_STATUS)),
|
Field('status', requires=IS_IN_SET(RUN_STATUS)),
|
||||||
Field('start_time','datetime'),
|
Field('start_time', 'datetime'),
|
||||||
Field('stop_time','datetime'),
|
Field('stop_time', 'datetime'),
|
||||||
Field('output','text'),
|
Field('output', 'text'),
|
||||||
Field('result','text'),
|
Field('result', 'text'),
|
||||||
Field('traceback','text'),
|
Field('traceback', 'text'),
|
||||||
Field('worker_name',default=self.worker_name),
|
Field('worker_name', default=self.worker_name),
|
||||||
migrate=migrate)
|
migrate=migrate)
|
||||||
|
|
||||||
db.define_table(
|
db.define_table(
|
||||||
'scheduler_worker',
|
'scheduler_worker',
|
||||||
Field('worker_name', unique=True),
|
Field('worker_name', unique=True),
|
||||||
Field('first_heartbeat','datetime'),
|
Field('first_heartbeat', 'datetime'),
|
||||||
Field('last_heartbeat','datetime'),
|
Field('last_heartbeat', 'datetime'),
|
||||||
Field('status',requires=IS_IN_SET(WORKER_STATUS)),
|
Field('status', requires=IS_IN_SET(WORKER_STATUS)),
|
||||||
Field('is_ticker', 'boolean', default=False, writable=False),
|
Field('is_ticker', 'boolean', default=False, writable=False),
|
||||||
Field('group_names', 'list:string', default=self.group_names),
|
Field('group_names', 'list:string', default=self.group_names),
|
||||||
migrate=migrate)
|
migrate=migrate)
|
||||||
if migrate:
|
if migrate:
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
def loop(self,worker_name=None):
|
def loop(self, worker_name=None):
|
||||||
signal.signal(signal.SIGTERM, lambda signum, stack_frame: sys.exit(1))
|
signal.signal(signal.SIGTERM, lambda signum, stack_frame: sys.exit(1))
|
||||||
try:
|
try:
|
||||||
self.start_heartbeats()
|
self.start_heartbeats()
|
||||||
@@ -521,14 +540,16 @@ class Scheduler(MetaScheduler):
|
|||||||
task = self.pop_task()
|
task = self.pop_task()
|
||||||
if task:
|
if task:
|
||||||
self.empty_runs = 0
|
self.empty_runs = 0
|
||||||
self.report_task(task,self.async(task))
|
self.report_task(task, self.async(task))
|
||||||
else:
|
else:
|
||||||
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):
|
||||||
@@ -541,7 +562,7 @@ class Scheduler(MetaScheduler):
|
|||||||
if self.is_a_ticker and self.do_assign_tasks:
|
if self.is_a_ticker and self.do_assign_tasks:
|
||||||
#I'm a ticker, and 5 loops passed without reassigning tasks, let's do
|
#I'm a ticker, and 5 loops passed without reassigning tasks, let's do
|
||||||
#that and loop again
|
#that and loop again
|
||||||
db.commit() #?don't know if it's useful, let's be completely sure
|
db.commit() # ?don't know if it's useful, let's be completely sure
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
self.assign_tasks()
|
self.assign_tasks()
|
||||||
@@ -552,21 +573,22 @@ 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:
|
||||||
task.update_record(status=RUNNING,last_run_time=now)
|
task.update_record(status=RUNNING, last_run_time=now)
|
||||||
#noone will touch my task!
|
#noone will touch my task!
|
||||||
db.commit()
|
db.commit()
|
||||||
logger.debug(' work to do %s' % task.id)
|
logger.debug(' work to do %s' % task.id)
|
||||||
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
|
||||||
else:
|
else:
|
||||||
run_again = False
|
run_again = False
|
||||||
@@ -575,7 +597,7 @@ class Scheduler(MetaScheduler):
|
|||||||
logger.debug(' new scheduler_run record')
|
logger.debug(' new scheduler_run record')
|
||||||
try:
|
try:
|
||||||
run_id = db.scheduler_run.insert(
|
run_id = db.scheduler_run.insert(
|
||||||
scheduler_task = task.id,
|
scheduler_task=task.id,
|
||||||
status=RUNNING,
|
status=RUNNING,
|
||||||
start_time=now,
|
start_time=now,
|
||||||
worker_name=self.worker_name)
|
worker_name=self.worker_name)
|
||||||
@@ -585,22 +607,22 @@ class Scheduler(MetaScheduler):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
logger.info('new task %(id)s "%(task_name)s" %(application_name)s.%(function_name)s' % task)
|
logger.info('new task %(id)s "%(task_name)s" %(application_name)s.%(function_name)s' % task)
|
||||||
return Task(
|
return Task(
|
||||||
app = task.application_name,
|
app=task.application_name,
|
||||||
function = task.function_name,
|
function=task.function_name,
|
||||||
timeout = task.timeout,
|
timeout=task.timeout,
|
||||||
args = task.args, #in json
|
args=task.args, # in json
|
||||||
vars = task.vars, #in json
|
vars=task.vars, # in json
|
||||||
task_id = task.id,
|
task_id=task.id,
|
||||||
run_id = run_id,
|
run_id=run_id,
|
||||||
run_again = run_again,
|
run_again=run_again,
|
||||||
next_run_time=next_run_time,
|
next_run_time=next_run_time,
|
||||||
times_run = times_run,
|
times_run=times_run,
|
||||||
stop_time = task.stop_time,
|
stop_time=task.stop_time,
|
||||||
retry_failed = task.retry_failed,
|
retry_failed=task.retry_failed,
|
||||||
times_failed = task.times_failed,
|
times_failed=task.times_failed,
|
||||||
sync_output = task.sync_output)
|
sync_output=task.sync_output)
|
||||||
|
|
||||||
def report_task(self,task,task_report):
|
def report_task(self, task, task_report):
|
||||||
db = self.db
|
db = self.db
|
||||||
now = self.now()
|
now = self.now()
|
||||||
while True:
|
while True:
|
||||||
@@ -610,16 +632,17 @@ 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)' %
|
||||||
db(db.scheduler_run.id==task.run_id).update(
|
task_report.status)
|
||||||
status = task_report.status,
|
db(db.scheduler_run.id == task.run_id).update(
|
||||||
stop_time = now,
|
status=task_report.status,
|
||||||
result = task_report.result,
|
stop_time=now,
|
||||||
output = task_report.output,
|
result=task_report.result,
|
||||||
traceback = task_report.tb)
|
output=task_report.output,
|
||||||
|
traceback=task_report.tb)
|
||||||
else:
|
else:
|
||||||
logger.debug(' deleting task report in db because of no result')
|
logger.debug(' deleting task report in db because of no result')
|
||||||
db(db.scheduler_run.id==task.run_id).delete()
|
db(db.scheduler_run.id == task.run_id).delete()
|
||||||
is_expired = (task.stop_time
|
is_expired = (task.stop_time
|
||||||
and task.next_run_time > task.stop_time
|
and task.next_run_time > task.stop_time
|
||||||
and True or False)
|
and True or False)
|
||||||
@@ -627,26 +650,25 @@ class Scheduler(MetaScheduler):
|
|||||||
or task.run_again and not is_expired
|
or task.run_again and not is_expired
|
||||||
and QUEUED or COMPLETED)
|
and QUEUED or COMPLETED)
|
||||||
if task_report.status == COMPLETED:
|
if task_report.status == COMPLETED:
|
||||||
d = dict(status = status,
|
d = dict(status=status,
|
||||||
next_run_time = task.next_run_time,
|
next_run_time=task.next_run_time,
|
||||||
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',
|
||||||
'STOPPED':'QUEUED'}[task_report.status]
|
'STOPPED': 'QUEUED'}[task_report.status]
|
||||||
status = (task.retry_failed
|
status = (task.retry_failed
|
||||||
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)
|
|
||||||
db.commit()
|
db.commit()
|
||||||
logger.info('task completed (%s)' % task_report.status)
|
logger.info('task completed (%s)' % task_report.status)
|
||||||
break
|
break
|
||||||
@@ -655,34 +677,39 @@ 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.define_tables(self.db_thread,migrate=False)
|
self.db._uri, folder=self.db._adapter.folder)
|
||||||
|
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,
|
||||||
group_names = self.group_names)
|
group_names=self.group_names)
|
||||||
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(
|
||||||
last_heartbeat = now)
|
last_heartbeat=now)
|
||||||
|
|
||||||
elif mybackedstatus.status == TERMINATE:
|
elif mybackedstatus.status == TERMINATE:
|
||||||
self.worker_status = TERMINATE, self.worker_status[1]
|
self.worker_status = TERMINATE, self.worker_status[1]
|
||||||
@@ -695,23 +722,24 @@ class Scheduler(MetaScheduler):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
logger.debug('........recording heartbeat')
|
logger.debug('........recording heartbeat')
|
||||||
db(sw.worker_name==self.worker_name).update(
|
db(sw.worker_name == self.worker_name).update(
|
||||||
last_heartbeat = now, status = ACTIVE)
|
last_heartbeat=now, status=ACTIVE)
|
||||||
self.worker_status = ACTIVE, 1 #re-activating the process
|
self.worker_status = ACTIVE, 1 # re-activating the process
|
||||||
|
|
||||||
self.do_assign_tasks = False
|
self.do_assign_tasks = False
|
||||||
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()
|
||||||
if self.worker_status[0] == ACTIVE:
|
if self.worker_status[0] == ACTIVE:
|
||||||
@@ -727,15 +755,17 @@ 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)
|
||||||
logger.info("TICKER: I'm a ticker (%s)" % self.worker_name)
|
logger.info("TICKER: I'm a ticker (%s)" % self.worker_name)
|
||||||
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):
|
||||||
"""
|
"""
|
||||||
@@ -829,12 +859,12 @@ class Scheduler(MetaScheduler):
|
|||||||
tname = 'task_name' in kwargs and kwargs.pop('task_name') or function
|
tname = 'task_name' in kwargs and kwargs.pop('task_name') or function
|
||||||
print 'a', targs
|
print 'a', targs
|
||||||
rtn = self.db.scheduler_task.validate_and_insert(
|
rtn = self.db.scheduler_task.validate_and_insert(
|
||||||
function_name=function,
|
function_name=function,
|
||||||
task_name=tname,
|
task_name=tname,
|
||||||
args=targs,
|
args=targs,
|
||||||
vars=tvars,
|
vars=tvars,
|
||||||
uuid=tuuid,
|
uuid=tuuid,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
if not rtn.errors:
|
if not rtn.errors:
|
||||||
rtn.uuid = tuuid
|
rtn.uuid = tuuid
|
||||||
else:
|
else:
|
||||||
@@ -866,25 +896,28 @@ 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
|
||||||
if output:
|
if output:
|
||||||
fields = st.ALL, sr.ALL
|
fields = st.ALL, sr.ALL
|
||||||
left = sr.on(sr.scheduler_task == st.id)
|
left = sr.on(sr.scheduler_task == st.id)
|
||||||
orderby = ~st.id|~sr.id
|
orderby = ~st.id | ~sr.id
|
||||||
row = self.db(q).select(
|
row = self.db(q).select(
|
||||||
*fields,
|
*fields,
|
||||||
orderby=orderby,
|
orderby=orderby,
|
||||||
left=left,
|
left=left,
|
||||||
limitby=(0,1)
|
limitby=(0, 1)
|
||||||
).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
|
||||||
@@ -894,34 +927,34 @@ def main():
|
|||||||
"-w", "--worker_name", dest="worker_name", default=None,
|
"-w", "--worker_name", dest="worker_name", default=None,
|
||||||
help="start a worker with name")
|
help="start a worker with name")
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"-b", "--heartbeat",dest="heartbeat", default = 10,
|
"-b", "--heartbeat", dest="heartbeat", default=10,
|
||||||
type='int', help="heartbeat time in seconds (default 10)")
|
type='int', help="heartbeat time in seconds (default 10)")
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"-L", "--logger_level",dest="logger_level",
|
"-L", "--logger_level", dest="logger_level",
|
||||||
default=30,
|
default=30,
|
||||||
type='int',
|
type='int',
|
||||||
help="set debug output level (0-100, 0 means all, 100 means none;default is 30)")
|
help="set debug output level (0-100, 0 means all, 100 means none;default is 30)")
|
||||||
parser.add_option("-E", "--empty-runs",
|
parser.add_option("-E", "--empty-runs",
|
||||||
dest="max_empty_runs",
|
dest="max_empty_runs",
|
||||||
type='int',
|
type='int',
|
||||||
default = 0,
|
default=0,
|
||||||
help="max loops with no grabbed tasks permitted (0 for never check)")
|
help="max loops with no grabbed tasks permitted (0 for never check)")
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"-g", "--group_names",dest="group_names",
|
"-g", "--group_names", dest="group_names",
|
||||||
default = 'main',
|
default='main',
|
||||||
help="comma separated list of groups to be picked by the worker")
|
help="comma separated list of groups to be picked by the worker")
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"-f", "--db_folder",dest="db_folder",
|
"-f", "--db_folder", dest="db_folder",
|
||||||
default = '/Users/mdipierro/web2py/applications/scheduler/databases',
|
default='/Users/mdipierro/web2py/applications/scheduler/databases',
|
||||||
help="location of the dal database folder")
|
help="location of the dal database folder")
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"-u", "--db_uri",dest="db_uri",
|
"-u", "--db_uri", dest="db_uri",
|
||||||
default = 'sqlite://storage.sqlite',
|
default='sqlite://storage.sqlite',
|
||||||
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,
|
||||||
help="work with UTC timestamps"
|
help="work with UTC timestamps"
|
||||||
@@ -930,35 +963,35 @@ def main():
|
|||||||
if not options.tasks or not options.db_uri:
|
if not options.tasks or not options.db_uri:
|
||||||
print USAGE
|
print USAGE
|
||||||
if options.tasks:
|
if options.tasks:
|
||||||
path,filename = os.path.split(options.tasks)
|
path, filename = os.path.split(options.tasks)
|
||||||
if filename.endswith('.py'):
|
if filename.endswith('.py'):
|
||||||
filename = filename[:-3]
|
filename = filename[:-3]
|
||||||
sys.path.append(path)
|
sys.path.append(path)
|
||||||
print 'importing tasks...'
|
print 'importing tasks...'
|
||||||
tasks = __import__(filename, globals(), locals(), [], -1).tasks
|
tasks = __import__(filename, globals(), locals(), [], -1).tasks
|
||||||
print 'tasks found: '+', '.join(tasks.keys())
|
print 'tasks found: ' + ', '.join(tasks.keys())
|
||||||
else:
|
else:
|
||||||
tasks = {}
|
tasks = {}
|
||||||
group_names = [x.strip() for x in options.group_names.split(',')]
|
group_names = [x.strip() for x in options.group_names.split(',')]
|
||||||
|
|
||||||
logging.getLogger().setLevel(options.logger_level)
|
logging.getLogger().setLevel(options.logger_level)
|
||||||
|
|
||||||
print 'groups for this worker: '+', '.join(group_names)
|
print 'groups for this worker: ' + ', '.join(group_names)
|
||||||
print 'connecting to database in folder: ' + options.db_folder or './'
|
print 'connecting to database in folder: ' + options.db_folder or './'
|
||||||
print 'using URI: '+options.db_uri
|
print 'using URI: ' + options.db_uri
|
||||||
db = DAL(options.db_uri,folder=options.db_folder)
|
db = DAL(options.db_uri, folder=options.db_folder)
|
||||||
print 'instantiating scheduler...'
|
print 'instantiating scheduler...'
|
||||||
scheduler=Scheduler(db = db,
|
scheduler = Scheduler(db=db,
|
||||||
worker_name = options.worker_name,
|
worker_name=options.worker_name,
|
||||||
tasks = tasks,
|
tasks=tasks,
|
||||||
migrate = True,
|
migrate=True,
|
||||||
group_names = group_names,
|
group_names=group_names,
|
||||||
heartbeat = options.heartbeat,
|
heartbeat=options.heartbeat,
|
||||||
max_empty_runs = options.max_empty_runs,
|
max_empty_runs=options.max_empty_runs,
|
||||||
utc_time = options.utc_time)
|
utc_time=options.utc_time)
|
||||||
signal.signal(signal.SIGTERM, lambda signum, stack_frame: sys.exit(1))
|
signal.signal(signal.SIGTERM, lambda signum, stack_frame: sys.exit(1))
|
||||||
print 'starting main worker loop...'
|
print 'starting main worker loop...'
|
||||||
scheduler.loop()
|
scheduler.loop()
|
||||||
|
|
||||||
if __name__=='__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
+36
-32
@@ -19,61 +19,64 @@ 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()
|
||||||
if isinstance(o, (datetime.date,
|
if isinstance(o, (datetime.date,
|
||||||
datetime.datetime,
|
datetime.datetime,
|
||||||
datetime.time)):
|
datetime.time)):
|
||||||
return o.isoformat()[:19].replace('T',' ')
|
return o.isoformat()[:19].replace('T', ' ')
|
||||||
elif isinstance(o, (int, long)):
|
elif isinstance(o, (int, long)):
|
||||||
return int(o)
|
return int(o)
|
||||||
elif isinstance(o, decimal.Decimal):
|
elif isinstance(o, decimal.Decimal):
|
||||||
return str(o)
|
return str(o)
|
||||||
elif isinstance(o, lazyT):
|
elif isinstance(o, lazyT):
|
||||||
return str(o)
|
return str(o)
|
||||||
elif isinstance(o,XmlComponent):
|
elif isinstance(o, XmlComponent):
|
||||||
return str(o)
|
return str(o)
|
||||||
elif hasattr(o,'as_list') and callable(o.as_list):
|
elif hasattr(o, 'as_list') and callable(o.as_list):
|
||||||
return o.as_list()
|
return o.as_list()
|
||||||
elif hasattr(o,'as_dict') and callable(o.as_dict):
|
elif hasattr(o, 'as_dict') and callable(o.as_dict):
|
||||||
return o.as_dict()
|
return o.as_dict()
|
||||||
else:
|
else:
|
||||||
raise TypeError(repr(o) + " is not JSON serializable")
|
raise TypeError(repr(o) + " is not JSON serializable")
|
||||||
|
|
||||||
|
|
||||||
def xml_rec(value, key, quote=True):
|
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])
|
||||||
elif hasattr(value,'as_list') and callable(value.as_list):
|
elif hasattr(value, 'as_list') and callable(value.as_list):
|
||||||
return str(xml_rec(value.as_list(),'',quote))
|
return str(xml_rec(value.as_list(), '', quote))
|
||||||
elif hasattr(value,'as_dict') and callable(value.as_dict):
|
elif hasattr(value, 'as_dict') and callable(value.as_dict):
|
||||||
return str(xml_rec(value.as_dict(),'',quote))
|
return str(xml_rec(value.as_dict(), '', quote))
|
||||||
else:
|
else:
|
||||||
return xmlescape(value,quote)
|
return xmlescape(value, quote)
|
||||||
|
|
||||||
|
|
||||||
def xml(value, encoding='UTF-8', key='document', quote=True):
|
def xml(value, encoding='UTF-8', key='document', quote=True):
|
||||||
return ('<?xml version="1.0" encoding="%s"?>' % encoding) + str(xml_rec(value,key,quote))
|
return ('<?xml version="1.0" encoding="%s"?>' % encoding) + str(xml_rec(value, key, quote))
|
||||||
|
|
||||||
|
|
||||||
def json(value,default=custom_json):
|
def json(value, default=custom_json):
|
||||||
return json_parser.dumps(value,default=default)
|
return json_parser.dumps(value, default=default)
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
@@ -86,9 +89,9 @@ def ics(events, title=None, link=None, timeshift=0, **ignored):
|
|||||||
s += '\nUID:%s' % item['id']
|
s += '\nUID:%s' % item['id']
|
||||||
if link:
|
if link:
|
||||||
s += '\nURL:%s' % link(item)
|
s += '\nURL:%s' % link(item)
|
||||||
shift = datetime.timedelta(seconds=3600*timeshift)
|
shift = datetime.timedelta(seconds=3600 * timeshift)
|
||||||
start = item['start_datetime']+shift
|
start = item['start_datetime'] + shift
|
||||||
stop = item['stop_datetime']+shift
|
stop = item['stop_datetime'] + shift
|
||||||
s += '\nDTSTART:%s' % start.strftime('%Y%m%dT%H%M%S')
|
s += '\nDTSTART:%s' % start.strftime('%Y%m%dT%H%M%S')
|
||||||
s += '\nDTEND:%s' % stop.strftime('%Y%m%dT%H%M%S')
|
s += '\nDTEND:%s' % stop.strftime('%Y%m%dT%H%M%S')
|
||||||
s += '\nSUMMARY:%s' % item['title']
|
s += '\nSUMMARY:%s' % item['title']
|
||||||
@@ -96,18 +99,19 @@ 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']
|
||||||
now=datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
rss = rss2.RSS2(title = str(feed.get('title','(notitle)')),
|
rss = rss2.RSS2(title=str(feed.get('title', '(notitle)')),
|
||||||
link = str(feed.get('link',None)),
|
link=str(feed.get('link', None)),
|
||||||
description = str(feed.get('description','')),
|
description=str(feed.get('description', '')),
|
||||||
lastBuildDate = feed.get('created_on', now),
|
lastBuildDate=feed.get('created_on', now),
|
||||||
items = [rss2.RSSItem(
|
items=[rss2.RSSItem(
|
||||||
title=str(entry.get('title','(notitle)')),
|
title=str(entry.get('title', '(notitle)')),
|
||||||
link=str(entry.get('link',None)),
|
link=str(entry.get('link', None)),
|
||||||
description=str(entry.get('description','')),
|
description=str(entry.get('description', '')),
|
||||||
pubDate=entry.get('created_on', now)
|
pubDate=entry.get('created_on', now)
|
||||||
) for entry in feed.get('entries',[])])
|
) for entry in feed.get('entries', [])])
|
||||||
return rss2.dumps(rss)
|
return rss2.dumps(rss)
|
||||||
+1
-1
@@ -29,7 +29,7 @@ global_settings.app_folders = set()
|
|||||||
global_settings.debugging = False
|
global_settings.debugging = False
|
||||||
|
|
||||||
global_settings.is_pypy = \
|
global_settings.is_pypy = \
|
||||||
hasattr(platform,'python_implementation') and \
|
hasattr(platform, 'python_implementation') and \
|
||||||
platform.python_implementation() == 'PyPy'
|
platform.python_implementation() == 'PyPy'
|
||||||
|
|
||||||
global_settings.is_jython = \
|
global_settings.is_jython = \
|
||||||
|
|||||||
+47
-35
@@ -30,12 +30,13 @@ from dal import BaseAdapter
|
|||||||
|
|
||||||
logger = logging.getLogger("web2py")
|
logger = logging.getLogger("web2py")
|
||||||
|
|
||||||
|
|
||||||
def exec_environment(
|
def exec_environment(
|
||||||
pyfile='',
|
pyfile='',
|
||||||
request=None,
|
request=None,
|
||||||
response=None,
|
response=None,
|
||||||
session=None,
|
session=None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
.. function:: gluon.shell.exec_environment([pyfile=''[, request=Request()
|
.. function:: gluon.shell.exec_environment([pyfile=''[, request=Request()
|
||||||
[, response=Response[, session=Session()]]]])
|
[, response=Response[, session=Session()]]]])
|
||||||
@@ -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)
|
||||||
@@ -78,7 +82,7 @@ def env(
|
|||||||
f=None,
|
f=None,
|
||||||
dir='',
|
dir='',
|
||||||
extra_request={},
|
extra_request={},
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Return web2py execution environment for application (a), controller (c),
|
Return web2py execution environment for application (a), controller (c),
|
||||||
function (f).
|
function (f).
|
||||||
@@ -113,7 +117,7 @@ def env(
|
|||||||
request.env.remote_addr = '127.0.0.1'
|
request.env.remote_addr = '127.0.0.1'
|
||||||
request.env.web2py_runtime_gae = global_settings.web2py_runtime_gae
|
request.env.web2py_runtime_gae = global_settings.web2py_runtime_gae
|
||||||
|
|
||||||
for k,v in extra_request.items():
|
for k, v in extra_request.items():
|
||||||
request[k] = v
|
request[k] = v
|
||||||
|
|
||||||
# Monkey patch so credentials checks pass.
|
# Monkey patch so credentials checks pass.
|
||||||
@@ -129,7 +133,7 @@ def env(
|
|||||||
try:
|
try:
|
||||||
run_models_in(environment)
|
run_models_in(environment)
|
||||||
except RestrictedError, e:
|
except RestrictedError, e:
|
||||||
sys.stderr.write(e.traceback+'\n')
|
sys.stderr.write(e.traceback + '\n')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
environment['__name__'] = '__main__'
|
environment['__name__'] = '__main__'
|
||||||
@@ -156,7 +160,7 @@ def run(
|
|||||||
startfile=None,
|
startfile=None,
|
||||||
bpython=False,
|
bpython=False,
|
||||||
python_code=False
|
python_code=False
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Start interactive shell or run Python script (startfile) in web2py
|
Start interactive shell or run Python script (startfile) in web2py
|
||||||
controller environment. appname is formatted like:
|
controller environment. appname is formatted like:
|
||||||
@@ -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
|
||||||
@@ -180,16 +185,17 @@ def run(
|
|||||||
|
|
||||||
os.mkdir(adir)
|
os.mkdir(adir)
|
||||||
w2p_unpack('welcome.w2p', adir)
|
w2p_unpack('welcome.w2p', adir)
|
||||||
for subfolder in ['models','views','controllers', 'databases',
|
for subfolder in ['models', 'views', 'controllers', 'databases',
|
||||||
'modules','cron','errors','sessions',
|
'modules', 'cron', 'errors', 'sessions',
|
||||||
'languages','static','private','uploads']:
|
'languages', 'static', 'private', 'uploads']:
|
||||||
subpath = os.path.join(adir,subfolder)
|
subpath = os.path.join(adir, subfolder)
|
||||||
if not os.path.exists(subpath):
|
if not os.path.exists(subpath):
|
||||||
os.mkdir(subpath)
|
os.mkdir(subpath)
|
||||||
db = os.path.join(adir,'models/db.py')
|
db = os.path.join(adir, 'models/db.py')
|
||||||
if os.path.exists(db):
|
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:
|
||||||
@@ -248,7 +259,7 @@ def run(
|
|||||||
# IPython; thanks Michael Toomim
|
# IPython; thanks Michael Toomim
|
||||||
if '__builtins__' in _env:
|
if '__builtins__' in _env:
|
||||||
del _env['__builtins__']
|
del _env['__builtins__']
|
||||||
shell = IPython.Shell.IPShell(argv=[],user_ns=_env)
|
shell = IPython.Shell.IPShell(argv=[], user_ns=_env)
|
||||||
shell.mainloop()
|
shell.mainloop()
|
||||||
return
|
return
|
||||||
except:
|
except:
|
||||||
@@ -304,7 +315,7 @@ def test(testpath, import_models=True, verbose=False):
|
|||||||
mo = re.match(r'(|.*/)applications/(?P<a>[^/]+)', testpath)
|
mo = re.match(r'(|.*/)applications/(?P<a>[^/]+)', testpath)
|
||||||
if not mo:
|
if not mo:
|
||||||
die('test file is not in application directory: %s'
|
die('test file is not in application directory: %s'
|
||||||
% testpath)
|
% testpath)
|
||||||
a = mo.group('a')
|
a = mo.group('a')
|
||||||
c = f = None
|
c = f = None
|
||||||
files = [testpath]
|
files = [testpath]
|
||||||
@@ -340,8 +351,8 @@ def test(testpath, import_models=True, verbose=False):
|
|||||||
globs = env(a, c=c, f=f, import_models=import_models)
|
globs = env(a, c=c, f=f, import_models=import_models)
|
||||||
execfile(testfile, globs)
|
execfile(testfile, globs)
|
||||||
doctest.run_docstring_examples(obj, globs=globs,
|
doctest.run_docstring_examples(obj, globs=globs,
|
||||||
name='%s: %s' % (os.path.basename(testfile),
|
name='%s: %s' % (os.path.basename(testfile),
|
||||||
name), verbose=verbose)
|
name), verbose=verbose)
|
||||||
if type(obj) in (types.TypeType, types.ClassType):
|
if type(obj) in (types.TypeType, types.ClassType):
|
||||||
for attr_name in dir(obj):
|
for attr_name in dir(obj):
|
||||||
|
|
||||||
@@ -369,8 +380,8 @@ 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).'
|
||||||
msg += '\n Use combined with --shell'
|
msg += '\n Use combined with --shell'
|
||||||
@@ -381,7 +392,7 @@ def execute_from_command_line(argv=None):
|
|||||||
default=False,
|
default=False,
|
||||||
dest='bpython',
|
dest='bpython',
|
||||||
help=msg,
|
help=msg,
|
||||||
)
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
'-P',
|
'-P',
|
||||||
'--plain',
|
'--plain',
|
||||||
@@ -389,25 +400,25 @@ def execute_from_command_line(argv=None):
|
|||||||
default=False,
|
default=False,
|
||||||
dest='plain',
|
dest='plain',
|
||||||
help='only use plain python shell, should be used with --shell option',
|
help='only use plain python shell, should be used with --shell option',
|
||||||
)
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
'-M',
|
'-M',
|
||||||
'--import_models',
|
'--import_models',
|
||||||
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(
|
||||||
'-R',
|
'-R',
|
||||||
'--run',
|
'--run',
|
||||||
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',
|
||||||
)
|
)
|
||||||
|
|
||||||
(options, args) = parser.parse_args(argv[1:])
|
(options, args) = parser.parse_args(argv[1:])
|
||||||
|
|
||||||
@@ -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__':
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
# this file exists for backward compatibility
|
# this file exists for backward compatibility
|
||||||
|
|
||||||
__all__ = ['DAL','Field','DRIVERS']
|
__all__ = ['DAL', 'Field', 'DRIVERS']
|
||||||
|
|
||||||
from dal import DAL, Field, Table, Query, Set, Expression, Row, Rows, DRIVERS, BaseAdapter, SQLField, SQLTable, SQLXorable, SQLQuery, SQLSet, SQLRows, SQLStorage, SQLDB, GQLDB, SQLALL, SQLCustomType
|
from dal import DAL, Field, Table, Query, Set, Expression, Row, Rows, DRIVERS, BaseAdapter, SQLField, SQLTable, SQLXorable, SQLQuery, SQLSet, SQLRows, SQLStorage, SQLDB, GQLDB, SQLALL, SQLCustomType
|
||||||
+630
-564
File diff suppressed because it is too large.
Load diff
+43
-24
@@ -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
|
||||||
@@ -38,7 +39,7 @@ class Storage(dict):
|
|||||||
>>> print o.a
|
>>> print o.a
|
||||||
None
|
None
|
||||||
"""
|
"""
|
||||||
__slots__=()
|
__slots__ = ()
|
||||||
__setattr__ = dict.__setitem__
|
__setattr__ = dict.__setitem__
|
||||||
__delattr__ = dict.__delitem__
|
__delattr__ = dict.__delitem__
|
||||||
__getitem__ = dict.get
|
__getitem__ = dict.get
|
||||||
@@ -48,7 +49,7 @@ class Storage(dict):
|
|||||||
__getstate__ = lambda self: None
|
__getstate__ = lambda self: None
|
||||||
__copy__ = lambda self: Storage(self)
|
__copy__ = lambda self: Storage(self)
|
||||||
|
|
||||||
def getlist(self,key):
|
def getlist(self, key):
|
||||||
"""
|
"""
|
||||||
Return a Storage value as a list.
|
Return a Storage value as a list.
|
||||||
|
|
||||||
@@ -68,11 +69,11 @@ class Storage(dict):
|
|||||||
>>> request.vars.getlist('z')
|
>>> request.vars.getlist('z')
|
||||||
[]
|
[]
|
||||||
"""
|
"""
|
||||||
value = self.get(key,[])
|
value = self.get(key, [])
|
||||||
return value if not value else \
|
return value if not value else \
|
||||||
value if isinstance(value,(list,tuple)) else [value]
|
value if isinstance(value, (list, tuple)) else [value]
|
||||||
|
|
||||||
def getfirst(self,key,default=None):
|
def getfirst(self, key, default=None):
|
||||||
"""
|
"""
|
||||||
Return the first or only value when given a request.vars-style key.
|
Return the first or only value when given a request.vars-style key.
|
||||||
|
|
||||||
@@ -93,7 +94,7 @@ class Storage(dict):
|
|||||||
values = self.getlist(key)
|
values = self.getlist(key)
|
||||||
return values[0] if values else default
|
return values[0] if values else default
|
||||||
|
|
||||||
def getlast(self,key,default=None):
|
def getlast(self, key, default=None):
|
||||||
"""
|
"""
|
||||||
Returns the last or only single value when
|
Returns the last or only single value when
|
||||||
given a request.vars-style key.
|
given a request.vars-style key.
|
||||||
@@ -115,56 +116,66 @@ class Storage(dict):
|
|||||||
values = self.getlist(key)
|
values = self.getlist(key)
|
||||||
return values[-1] if values else default
|
return values[-1] if values else default
|
||||||
|
|
||||||
PICKABLE = (str,int,long,float,bool,list,dict,tuple,set)
|
PICKABLE = (str, int, long, float, bool, list, dict, tuple, set)
|
||||||
|
|
||||||
|
|
||||||
class StorageList(Storage):
|
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)
|
||||||
else:
|
else:
|
||||||
r = []
|
r = []
|
||||||
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,25 +214,33 @@ 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):
|
|
||||||
return getattr(self,key) if key in self else None
|
def __getattr__(self, key):
|
||||||
def __getitem__(self,key):
|
return getattr(self, key) if key in self else None
|
||||||
return dict.get(self,key,None)
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
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):
|
||||||
"""
|
"""
|
||||||
@@ -235,7 +254,7 @@ class List(list):
|
|||||||
request.args(0,default=0,cast=int,otherwise=lambda:...)
|
request.args(0,default=0,cast=int,otherwise=lambda:...)
|
||||||
"""
|
"""
|
||||||
n = len(self)
|
n = len(self)
|
||||||
if 0<=i<n or -n<=i<0:
|
if 0 <= i < n or -n <= i < 0:
|
||||||
value = self[i]
|
value = self[i]
|
||||||
else:
|
else:
|
||||||
value = default
|
value = default
|
||||||
@@ -246,12 +265,12 @@ class List(list):
|
|||||||
from http import HTTP, redirect
|
from http import HTTP, redirect
|
||||||
if otherwise is None:
|
if otherwise is None:
|
||||||
raise HTTP(404)
|
raise HTTP(404)
|
||||||
elif isinstance(otherwise,str):
|
elif isinstance(otherwise, str):
|
||||||
redirect(otherwise)
|
redirect(otherwise)
|
||||||
elif callable(otherwise):
|
elif callable(otherwise):
|
||||||
return otherwise()
|
return otherwise()
|
||||||
else:
|
else:
|
||||||
raise RuntimeError, "invalid otherwise"
|
raise RuntimeError("invalid otherwise")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+13
-11
@@ -20,9 +20,10 @@ from contenttype import contenttype
|
|||||||
regex_start_range = re.compile('\d+(?=\-)')
|
regex_start_range = re.compile('\d+(?=\-)')
|
||||||
regex_stop_range = re.compile('(?<=\-)\d+')
|
regex_stop_range = re.compile('(?<=\-)\d+')
|
||||||
|
|
||||||
DEFAULT_CHUNK_SIZE = 64*1024
|
DEFAULT_CHUNK_SIZE = 64 * 1024
|
||||||
|
|
||||||
def streamer(stream, chunk_size = DEFAULT_CHUNK_SIZE, bytes = None):
|
|
||||||
|
def streamer(stream, chunk_size=DEFAULT_CHUNK_SIZE, bytes=None):
|
||||||
offset = 0
|
offset = 0
|
||||||
while bytes is None or offset < bytes:
|
while bytes is None or offset < bytes:
|
||||||
if not bytes is None and bytes - offset < chunk_size:
|
if not bytes is None and bytes - offset < chunk_size:
|
||||||
@@ -38,14 +39,15 @@ def streamer(stream, chunk_size = DEFAULT_CHUNK_SIZE, bytes = None):
|
|||||||
offset += length
|
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,
|
||||||
request = None,
|
request=None,
|
||||||
headers = {},
|
headers={},
|
||||||
status = 200,
|
status=200,
|
||||||
error_message = None,
|
error_message=None,
|
||||||
):
|
):
|
||||||
if error_message is None:
|
if error_message is None:
|
||||||
error_message = rewrite.THREAD_LOCAL.routes.error_message % 'invalid request'
|
error_message = rewrite.THREAD_LOCAL.routes.error_message % 'invalid request'
|
||||||
try:
|
try:
|
||||||
@@ -62,7 +64,7 @@ def stream_file_or_304_or_206(
|
|||||||
stat_file = os.stat(static_file)
|
stat_file = os.stat(static_file)
|
||||||
fsize = stat_file[stat.ST_SIZE]
|
fsize = stat_file[stat.ST_SIZE]
|
||||||
modified = stat_file[stat.ST_MTIME]
|
modified = stat_file[stat.ST_MTIME]
|
||||||
mtime = time.strftime('%a, %d %b %Y %H:%M:%S GMT',time.gmtime(modified))
|
mtime = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(modified))
|
||||||
headers.setdefault('Content-Type', contenttype(static_file))
|
headers.setdefault('Content-Type', contenttype(static_file))
|
||||||
headers.setdefault('Last-Modified', mtime)
|
headers.setdefault('Last-Modified', mtime)
|
||||||
headers.setdefault('Pragma', 'cache')
|
headers.setdefault('Pragma', 'cache')
|
||||||
@@ -95,10 +97,10 @@ def stream_file_or_304_or_206(
|
|||||||
status = 206
|
status = 206
|
||||||
# in all the other cases (not 304, not 206, but 200 or error page)
|
# in all the other cases (not 304, not 206, but 200 or error page)
|
||||||
if status != 206:
|
if status != 206:
|
||||||
enc = request.env.http_accept_encoding
|
enc = request.env.http_accept_encoding
|
||||||
if enc and 'gzip' in enc and not 'Content-Encoding' in headers:
|
if enc and 'gzip' in enc and not 'Content-Encoding' in headers:
|
||||||
gzipped = static_file + '.gz'
|
gzipped = static_file + '.gz'
|
||||||
if os.path.isfile(gzipped) and os.path.getmtime(gzipped)>modified:
|
if os.path.isfile(gzipped) and os.path.getmtime(gzipped) > modified:
|
||||||
static_file = gzipped
|
static_file = gzipped
|
||||||
fsize = os.path.getsize(gzipped)
|
fsize = os.path.getsize(gzipped)
|
||||||
headers['Content-Encoding'] = 'gzip'
|
headers['Content-Encoding'] = 'gzip'
|
||||||
|
|||||||
+99
-77
@@ -26,23 +26,26 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
# do not have web2py
|
# do not have web2py
|
||||||
current = None
|
current = None
|
||||||
def RestrictedError(a,b,c):
|
|
||||||
logging.error(str(a)+':'+str(b)+':'+str(c))
|
def RestrictedError(a, b, c):
|
||||||
|
logging.error(str(a) + ':' + str(b) + ':' + str(c))
|
||||||
return RuntimeError
|
return RuntimeError
|
||||||
|
|
||||||
|
|
||||||
class Node(object):
|
class Node(object):
|
||||||
"""
|
"""
|
||||||
Basic Container Object
|
Basic Container Object
|
||||||
"""
|
"""
|
||||||
def __init__(self, value = None, pre_extend = False):
|
def __init__(self, value=None, pre_extend=False):
|
||||||
self.value = value
|
self.value = value
|
||||||
self.pre_extend = pre_extend
|
self.pre_extend = pre_extend
|
||||||
|
|
||||||
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
|
||||||
self.value = None
|
self.value = None
|
||||||
self.pre_extend = pre_extend
|
self.pre_extend = pre_extend
|
||||||
@@ -57,7 +60,8 @@ 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.
|
||||||
# Override block from vars.
|
# Override block from vars.
|
||||||
@@ -66,8 +70,9 @@ def output_aux(node,blocks):
|
|||||||
return (blocks[node.name].output(blocks)
|
return (blocks[node.name].output(blocks)
|
||||||
if node.name in blocks else
|
if node.name in blocks else
|
||||||
node.output(blocks)) \
|
node.output(blocks)) \
|
||||||
if isinstance(node, BlockNode) \
|
if isinstance(node, BlockNode) \
|
||||||
else str(node)
|
else str(node)
|
||||||
|
|
||||||
|
|
||||||
class BlockNode(Node):
|
class BlockNode(Node):
|
||||||
"""
|
"""
|
||||||
@@ -82,7 +87,7 @@ class BlockNode(Node):
|
|||||||
This is default block test
|
This is default block test
|
||||||
{{ end }}
|
{{ end }}
|
||||||
"""
|
"""
|
||||||
def __init__(self, name = '', pre_extend = False, delimiters = ('{{','}}')):
|
def __init__(self, name='', pre_extend=False, delimiters=('{{', '}}')):
|
||||||
"""
|
"""
|
||||||
name - Name of this Node.
|
name - Name of this Node.
|
||||||
"""
|
"""
|
||||||
@@ -92,7 +97,7 @@ class BlockNode(Node):
|
|||||||
self.left, self.right = delimiters
|
self.left, self.right = delimiters
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
lines = ['%sblock %s%s' % (self.left,self.name,self.right)]
|
lines = ['%sblock %s%s' % (self.left, self.name, self.right)]
|
||||||
lines += [str(node) for node in self.nodes]
|
lines += [str(node) for node in self.nodes]
|
||||||
lines.append('%send%s' % (self.left, self.right))
|
lines.append('%send%s' % (self.left, self.right))
|
||||||
return ''.join(lines)
|
return ''.join(lines)
|
||||||
@@ -101,8 +106,8 @@ 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):
|
||||||
"""
|
"""
|
||||||
@@ -137,7 +142,8 @@ class BlockNode(Node):
|
|||||||
blocks -- Dictionary of blocks that are extending
|
blocks -- Dictionary of blocks that are extending
|
||||||
from this template.
|
from this template.
|
||||||
"""
|
"""
|
||||||
return ''.join(output_aux(node,blocks) for node in self.nodes)
|
return ''.join(output_aux(node, blocks) for node in self.nodes)
|
||||||
|
|
||||||
|
|
||||||
class Content(BlockNode):
|
class Content(BlockNode):
|
||||||
"""
|
"""
|
||||||
@@ -145,7 +151,7 @@ class Content(BlockNode):
|
|||||||
|
|
||||||
Contains functions that operate as such.
|
Contains functions that operate as such.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name = "ContentBlock", pre_extend = False):
|
def __init__(self, name="ContentBlock", pre_extend=False):
|
||||||
"""
|
"""
|
||||||
Keyword Arguments
|
Keyword Arguments
|
||||||
|
|
||||||
@@ -157,18 +163,19 @@ class Content(BlockNode):
|
|||||||
self.pre_extend = pre_extend
|
self.pre_extend = pre_extend
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return ''.join(output_aux(node,self.blocks) for node in self.nodes)
|
return ''.join(output_aux(node, self.blocks) for node in self.nodes)
|
||||||
|
|
||||||
def _insert(self, other, index = 0):
|
def _insert(self, other, index=0):
|
||||||
"""
|
"""
|
||||||
Inserts object at index.
|
Inserts object at index.
|
||||||
"""
|
"""
|
||||||
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):
|
||||||
"""
|
"""
|
||||||
Inserts object at index.
|
Inserts object at index.
|
||||||
|
|
||||||
@@ -201,21 +208,23 @@ 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 = ('{{', '}}')
|
||||||
r_tag = compile(r'(\{\{.*?\}\})', DOTALL)
|
r_tag = compile(r'(\{\{.*?\}\})', DOTALL)
|
||||||
|
|
||||||
r_multiline = compile(r'(""".*?""")|(\'\'\'.*?\'\'\')', DOTALL)
|
r_multiline = compile(r'(""".*?""")|(\'\'\'.*?\'\'\')', DOTALL)
|
||||||
|
|
||||||
# These are used for re-indentation.
|
# These are used for re-indentation.
|
||||||
# Indent + 1
|
# Indent + 1
|
||||||
re_block = compile('^(elif |else:|except:|except |finally:).*$',DOTALL)
|
re_block = compile('^(elif |else:|except:|except |finally:).*$', DOTALL)
|
||||||
|
|
||||||
# Indent - 1
|
# Indent - 1
|
||||||
re_unblock = compile('^(return|continue|break|raise)( .*)?$', DOTALL)
|
re_unblock = compile('^(return|continue|break|raise)( .*)?$', DOTALL)
|
||||||
@@ -223,12 +232,12 @@ class TemplateParser(object):
|
|||||||
re_pass = compile('^pass( .*)?$', DOTALL)
|
re_pass = compile('^pass( .*)?$', DOTALL)
|
||||||
|
|
||||||
def __init__(self, text,
|
def __init__(self, text,
|
||||||
name = "ParserContainer",
|
name="ParserContainer",
|
||||||
context = dict(),
|
context=dict(),
|
||||||
path = 'views/',
|
path='views/',
|
||||||
writer = 'response.write',
|
writer='response.write',
|
||||||
lexers = {},
|
lexers={},
|
||||||
delimiters = ('{{','}}'),
|
delimiters=('{{', '}}'),
|
||||||
_super_nodes = [],
|
_super_nodes = [],
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@@ -270,7 +279,7 @@ class TemplateParser(object):
|
|||||||
escaped_delimiters = (escape(delimiters[0]),
|
escaped_delimiters = (escape(delimiters[0]),
|
||||||
escape(delimiters[1]))
|
escape(delimiters[1]))
|
||||||
self.r_tag = compile(r'(%s.*?%s)' % escaped_delimiters, DOTALL)
|
self.r_tag = compile(r'(%s.*?%s)' % escaped_delimiters, DOTALL)
|
||||||
elif hasattr(context.get('response',None),'delimiters'):
|
elif hasattr(context.get('response', None), 'delimiters'):
|
||||||
if context['response'].delimiters != self.default_delimiters:
|
if context['response'].delimiters != self.default_delimiters:
|
||||||
escaped_delimiters = (
|
escaped_delimiters = (
|
||||||
escape(context['response'].delimiters[0]),
|
escape(context['response'].delimiters[0]),
|
||||||
@@ -359,10 +368,10 @@ class TemplateParser(object):
|
|||||||
k = k + credit - 1
|
k = k + credit - 1
|
||||||
|
|
||||||
# We obviously can't have a negative indentation
|
# We obviously can't have a negative indentation
|
||||||
k = max(k,0)
|
k = max(k, 0)
|
||||||
|
|
||||||
# Add the indentation!
|
# Add the indentation!
|
||||||
new_lines.append(' '*(4*k)+line)
|
new_lines.append(' ' * (4 * k) + line)
|
||||||
|
|
||||||
# Bank account back to 0 again :(
|
# Bank account back to 0 again :(
|
||||||
credit = 0
|
credit = 0
|
||||||
@@ -416,7 +425,7 @@ class TemplateParser(object):
|
|||||||
# Allow Views to include other views dynamically
|
# Allow Views to include other views dynamically
|
||||||
context = self.context
|
context = self.context
|
||||||
if current and not "response" in context:
|
if current and not "response" in context:
|
||||||
context["response"] = getattr(current,'response',None)
|
context["response"] = getattr(current, 'response', None)
|
||||||
|
|
||||||
# Get the filename; filename looks like ``"template.html"``.
|
# Get the filename; filename looks like ``"template.html"``.
|
||||||
# We need to eval to remove the quotes and get the string type.
|
# We need to eval to remove the quotes and get the string type.
|
||||||
@@ -442,11 +451,11 @@ class TemplateParser(object):
|
|||||||
text = self._get_file_text(filename)
|
text = self._get_file_text(filename)
|
||||||
|
|
||||||
t = TemplateParser(text,
|
t = TemplateParser(text,
|
||||||
name = filename,
|
name=filename,
|
||||||
context = self.context,
|
context=self.context,
|
||||||
path = self.path,
|
path=self.path,
|
||||||
writer = self.writer,
|
writer=self.writer,
|
||||||
delimiters = self.delimiters)
|
delimiters=self.delimiters)
|
||||||
|
|
||||||
content.append(t.content)
|
content.append(t.content)
|
||||||
|
|
||||||
@@ -465,16 +474,17 @@ class TemplateParser(object):
|
|||||||
super_nodes.extend(self.super_nodes)
|
super_nodes.extend(self.super_nodes)
|
||||||
|
|
||||||
t = TemplateParser(text,
|
t = TemplateParser(text,
|
||||||
name = filename,
|
name=filename,
|
||||||
context = self.context,
|
context=self.context,
|
||||||
path = self.path,
|
path=self.path,
|
||||||
writer = self.writer,
|
writer=self.writer,
|
||||||
delimiters = self.delimiters,
|
delimiters=self.delimiters,
|
||||||
_super_nodes = super_nodes)
|
_super_nodes=super_nodes)
|
||||||
|
|
||||||
# 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
|
||||||
@@ -601,22 +611,22 @@ class TemplateParser(object):
|
|||||||
# You can define custom names such as
|
# You can define custom names such as
|
||||||
# '{{<<variable}}' which could potentially
|
# '{{<<variable}}' which could potentially
|
||||||
# write unescaped version of the variable.
|
# write unescaped version of the variable.
|
||||||
self.lexers[name](parser = self,
|
self.lexers[name](parser=self,
|
||||||
value = value,
|
value=value,
|
||||||
top = top,
|
top=top,
|
||||||
stack = stack)
|
stack=stack)
|
||||||
|
|
||||||
elif name == '=':
|
elif name == '=':
|
||||||
# So we have a variable to insert into
|
# So we have a variable to insert into
|
||||||
# the template
|
# the template
|
||||||
buf = "\n%s(%s)" % (self.writer, value)
|
buf = "\n%s(%s)" % (self.writer, value)
|
||||||
top.append(Node(buf, pre_extend = pre_extend))
|
top.append(Node(buf, pre_extend=pre_extend))
|
||||||
|
|
||||||
elif name == 'block' and not value.startswith('='):
|
elif name == 'block' and not value.startswith('='):
|
||||||
# Make a new node with name.
|
# Make a new node with name.
|
||||||
node = BlockNode(name = value.strip(),
|
node = BlockNode(name=value.strip(),
|
||||||
pre_extend = pre_extend,
|
pre_extend=pre_extend,
|
||||||
delimiters = self.delimiters)
|
delimiters=self.delimiters)
|
||||||
|
|
||||||
# Append this node to our active node
|
# Append this node to our active node
|
||||||
top.append(node)
|
top.append(node)
|
||||||
@@ -646,8 +656,8 @@ class TemplateParser(object):
|
|||||||
target_node = top.name
|
target_node = top.name
|
||||||
|
|
||||||
# Create a SuperNode instance
|
# Create a SuperNode instance
|
||||||
node = SuperNode(name = target_node,
|
node = SuperNode(name=target_node,
|
||||||
pre_extend = pre_extend)
|
pre_extend=pre_extend)
|
||||||
|
|
||||||
# Add this to our list to be taken care of
|
# Add this to our list to be taken care of
|
||||||
self.super_nodes.append(node)
|
self.super_nodes.append(node)
|
||||||
@@ -663,9 +673,10 @@ 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(
|
||||||
pre_extend = pre_extend,
|
name='__include__' + self.name,
|
||||||
delimiters = self.delimiters)
|
pre_extend=pre_extend,
|
||||||
|
delimiters=self.delimiters)
|
||||||
top.append(include_node)
|
top.append(include_node)
|
||||||
|
|
||||||
elif name == 'extend' and not value.startswith('='):
|
elif name == 'extend' and not value.startswith('='):
|
||||||
@@ -697,21 +708,22 @@ 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))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# It is HTML so just include it.
|
# It is HTML so just include it.
|
||||||
buf = "\n%s(%r, escape=False)" % (self.writer, i)
|
buf = "\n%s(%r, escape=False)" % (self.writer, i)
|
||||||
top.append(Node(buf, pre_extend = pre_extend))
|
top.append(Node(buf, pre_extend=pre_extend))
|
||||||
|
|
||||||
# Remember: tag, not tag, tag, not tag
|
# Remember: tag, not tag, tag, not tag
|
||||||
in_tag = not in_tag
|
in_tag = not in_tag
|
||||||
@@ -740,11 +752,13 @@ 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(),
|
||||||
lexers = {},
|
lexers={},
|
||||||
delimiters = ('{{','}}')
|
delimiters=('{{', '}}')
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
filename can be a view filename in the views folder or an input stream
|
filename can be a view filename in the views folder or an input stream
|
||||||
@@ -766,6 +780,7 @@ def parse_template(filename,
|
|||||||
# Use the file contents to get a parsed template and return it.
|
# 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,13 +788,15 @@ 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))
|
||||||
elif hasattr(data,'xml') and callable(data.xml):
|
elif hasattr(data, 'xml') and callable(data.xml):
|
||||||
self.body.write(data.xml())
|
self.body.write(data.xml())
|
||||||
else:
|
else:
|
||||||
# make it a string
|
# make it a string
|
||||||
@@ -787,27 +804,31 @@ class DummyResponse():
|
|||||||
data = str(data)
|
data = str(data)
|
||||||
elif isinstance(data, unicode):
|
elif isinstance(data, unicode):
|
||||||
data = data.encode('utf8', 'xmlcharrefreplace')
|
data = data.encode('utf8', 'xmlcharrefreplace')
|
||||||
data = cgi.escape(data, True).replace("'","'")
|
data = cgi.escape(data, True).replace("'", "'")
|
||||||
self.body.write(data)
|
self.body.write(data)
|
||||||
|
|
||||||
|
|
||||||
class NOESCAPE():
|
class NOESCAPE():
|
||||||
"""
|
"""
|
||||||
A little helper to avoid escaping.
|
A little helper to avoid escaping.
|
||||||
"""
|
"""
|
||||||
def __init__(self, text):
|
def __init__(self, text):
|
||||||
self.text = text
|
self.text = text
|
||||||
|
|
||||||
def xml(self):
|
def xml(self):
|
||||||
return self.text
|
return self.text
|
||||||
|
|
||||||
# And this is a generic render function.
|
# And this is a generic render function.
|
||||||
# Here for integration with gluon.
|
# Here for integration with gluon.
|
||||||
def render(content = "hello world",
|
|
||||||
stream = None,
|
|
||||||
filename = None,
|
def render(content="hello world",
|
||||||
path = None,
|
stream=None,
|
||||||
context = {},
|
filename=None,
|
||||||
lexers = {},
|
path=None,
|
||||||
delimiters = ('{{','}}')
|
context={},
|
||||||
|
lexers={},
|
||||||
|
delimiters=('{{', '}}')
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
>>> render()
|
>>> render()
|
||||||
@@ -856,7 +877,7 @@ def render(content = "hello world",
|
|||||||
|
|
||||||
# If we don't have anything to render, why bother?
|
# If we don't have anything to render, why bother?
|
||||||
if not content and not stream and not filename:
|
if not content and not stream and not filename:
|
||||||
raise SyntaxError, "Must specify a stream or filename or content"
|
raise SyntaxError("Must specify a stream or filename or content")
|
||||||
|
|
||||||
# Here for legacy purposes, probably can be reduced to
|
# Here for legacy purposes, probably can be reduced to
|
||||||
# something more simple.
|
# something more simple.
|
||||||
@@ -869,7 +890,8 @@ def render(content = "hello world",
|
|||||||
stream = cStringIO.StringIO(content)
|
stream = cStringIO.StringIO(content)
|
||||||
|
|
||||||
# Execute the template.
|
# Execute the template.
|
||||||
code = str(TemplateParser(stream.read(), context=context, path=path, lexers=lexers, delimiters=delimiters))
|
code = str(TemplateParser(stream.read(
|
||||||
|
), context=context, path=path, lexers=lexers, delimiters=delimiters))
|
||||||
try:
|
try:
|
||||||
exec(code) in context
|
exec(code) in context
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from cache import CacheInRam, CacheOnDisk
|
|||||||
|
|
||||||
oldcwd = None
|
oldcwd = None
|
||||||
|
|
||||||
|
|
||||||
def setUpModule():
|
def setUpModule():
|
||||||
global oldcwd
|
global oldcwd
|
||||||
if oldcwd is None:
|
if oldcwd is None:
|
||||||
@@ -25,12 +26,14 @@ def setUpModule():
|
|||||||
if not os.path.isdir('gluon'):
|
if not os.path.isdir('gluon'):
|
||||||
os.chdir(os.path.realpath('../../'))
|
os.chdir(os.path.realpath('../../'))
|
||||||
|
|
||||||
|
|
||||||
def tearDownModule():
|
def tearDownModule():
|
||||||
global oldcwd
|
global oldcwd
|
||||||
if oldcwd:
|
if oldcwd:
|
||||||
os.chdir(oldcwd)
|
os.chdir(oldcwd)
|
||||||
oldcwd = None
|
oldcwd = None
|
||||||
|
|
||||||
|
|
||||||
class TestCache(unittest.TestCase):
|
class TestCache(unittest.TestCase):
|
||||||
|
|
||||||
def testCacheInRam(self):
|
def testCacheInRam(self):
|
||||||
@@ -70,5 +73,3 @@ if __name__ == '__main__':
|
|||||||
setUpModule() # pre-python-2.7
|
setUpModule() # pre-python-2.7
|
||||||
unittest.main()
|
unittest.main()
|
||||||
tearDownModule()
|
tearDownModule()
|
||||||
|
|
||||||
|
|
||||||
@@ -22,13 +22,14 @@ 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()
|
||||||
pdf.add_page()
|
pdf.add_page()
|
||||||
pdf.compress = False
|
pdf.compress = False
|
||||||
pdf.set_font('Arial', '',14)
|
pdf.set_font('Arial', '', 14)
|
||||||
pdf.ln(10)
|
pdf.ln(10)
|
||||||
pdf.write(5, 'hello world')
|
pdf.write(5, 'hello world')
|
||||||
pdf_out = pdf.output('', 'S')
|
pdf_out = pdf.output('', 'S')
|
||||||
@@ -39,4 +40,3 @@ class TestContribs(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ class TestBareHelpers(unittest.TestCase):
|
|||||||
|
|
||||||
def testOPTION(self):
|
def testOPTION(self):
|
||||||
self.assertEqual(OPTION('<>', _a='1', _b='2').xml(),
|
self.assertEqual(OPTION('<>', _a='1', _b='2').xml(),
|
||||||
'<option a="1" b="2" value="<>"><>' + \
|
'<option a="1" b="2" value="<>"><>' +
|
||||||
'</option>')
|
'</option>')
|
||||||
|
|
||||||
def testP(self):
|
def testP(self):
|
||||||
@@ -149,7 +149,7 @@ class TestBareHelpers(unittest.TestCase):
|
|||||||
|
|
||||||
def testSELECT(self):
|
def testSELECT(self):
|
||||||
self.assertEqual(SELECT('<>', _a='1', _b='2').xml(),
|
self.assertEqual(SELECT('<>', _a='1', _b='2').xml(),
|
||||||
'<select a="1" b="2">'+ \
|
'<select a="1" b="2">' +
|
||||||
'<option value="<>"><></option></select>')
|
'<option value="<>"><></option></select>')
|
||||||
|
|
||||||
def testSPAN(self):
|
def testSPAN(self):
|
||||||
@@ -162,7 +162,7 @@ class TestBareHelpers(unittest.TestCase):
|
|||||||
|
|
||||||
def testTABLE(self):
|
def testTABLE(self):
|
||||||
self.assertEqual(TABLE('<>', _a='1', _b='2').xml(),
|
self.assertEqual(TABLE('<>', _a='1', _b='2').xml(),
|
||||||
'<table a="1" b="2"><tr><td><></td></tr>' + \
|
'<table a="1" b="2"><tr><td><></td></tr>' +
|
||||||
'</table>')
|
'</table>')
|
||||||
|
|
||||||
def testTBODY(self):
|
def testTBODY(self):
|
||||||
@@ -175,8 +175,8 @@ class TestBareHelpers(unittest.TestCase):
|
|||||||
|
|
||||||
def testTEXTAREA(self):
|
def testTEXTAREA(self):
|
||||||
self.assertEqual(TEXTAREA('<>', _a='1', _b='2').xml(),
|
self.assertEqual(TEXTAREA('<>', _a='1', _b='2').xml(),
|
||||||
'<textarea a="1" b="2" cols="40" rows="10"><>' + \
|
'<textarea a="1" b="2" cols="40" rows="10"><>' +
|
||||||
'</textarea>')
|
'</textarea>')
|
||||||
|
|
||||||
def testTFOOT(self):
|
def testTFOOT(self):
|
||||||
self.assertEqual(TFOOT('<>', _a='1', _b='2').xml(),
|
self.assertEqual(TFOOT('<>', _a='1', _b='2').xml(),
|
||||||
@@ -209,4 +209,3 @@ class TestBareHelpers(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
+93
-63
@@ -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
|
||||||
|
|
||||||
@@ -116,9 +117,9 @@ class TestIsUrl(unittest.TestCase):
|
|||||||
# 'generic' mode
|
# 'generic' mode
|
||||||
|
|
||||||
x = IS_URL(mode='generic')
|
x = IS_URL(mode='generic')
|
||||||
self.assertEqual(x('http://google.ca'), ('http://google.ca',None))
|
self.assertEqual(x('http://google.ca'), ('http://google.ca', None))
|
||||||
self.assertEqual(x('google.ca'), ('google.ca', None))
|
self.assertEqual(x('google.ca'), ('google.ca', None))
|
||||||
self.assertEqual(x('google.ca:80'), ('http://google.ca:80',None))
|
self.assertEqual(x('google.ca:80'), ('http://google.ca:80', None))
|
||||||
self.assertEqual(x('blargg://unreal'), ('blargg://unreal',
|
self.assertEqual(x('blargg://unreal'), ('blargg://unreal',
|
||||||
'enter a valid URL'))
|
'enter a valid URL'))
|
||||||
|
|
||||||
@@ -152,7 +153,7 @@ class TestIsUrl(unittest.TestCase):
|
|||||||
# 'generic' mode with overriden allowed_schemes and prepend_scheme
|
# 'generic' mode with overriden allowed_schemes and prepend_scheme
|
||||||
|
|
||||||
x = IS_URL(mode='generic', allowed_schemes=[None, 'ftp', 'ftps'
|
x = IS_URL(mode='generic', allowed_schemes=[None, 'ftp', 'ftps'
|
||||||
], prepend_scheme='ftp')
|
], prepend_scheme='ftp')
|
||||||
self.assertEqual(x('http://google.ca'), ('http://google.ca',
|
self.assertEqual(x('http://google.ca'), ('http://google.ca',
|
||||||
'enter a valid URL'))
|
'enter a valid URL'))
|
||||||
self.assertEqual(x('google.ca'), ('google.ca', None))
|
self.assertEqual(x('google.ca'), ('google.ca', None))
|
||||||
@@ -191,10 +192,11 @@ class TestIsUrl(unittest.TestCase):
|
|||||||
# error at calling time
|
# error at calling time
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if str(e)\
|
if str(e)\
|
||||||
!= "allowed_scheme value 'ftp' is not in [None, 'http', 'https']":
|
!= "allowed_scheme value 'ftp' is not in [None, 'http', 'https']":
|
||||||
self.fail('Wrong exception: ' + str(e))
|
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'])
|
||||||
@@ -205,7 +207,7 @@ class TestIsUrl(unittest.TestCase):
|
|||||||
# error at calling time
|
# error at calling time
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if str(e)\
|
if str(e)\
|
||||||
!= "prepend_scheme='ftp' is not in allowed_schemes=[None, 'http', 'https']":
|
!= "prepend_scheme='ftp' is not in allowed_schemes=[None, 'http', 'https']":
|
||||||
self.fail('Wrong exception: ' + str(e))
|
self.fail('Wrong exception: ' + str(e))
|
||||||
else:
|
else:
|
||||||
self.fail("Accepted invalid prepend_scheme: 'ftp'")
|
self.fail("Accepted invalid prepend_scheme: 'ftp'")
|
||||||
@@ -217,7 +219,7 @@ class TestIsUrl(unittest.TestCase):
|
|||||||
x = IS_URL(allowed_schemes=[None, 'https'])
|
x = IS_URL(allowed_schemes=[None, 'https'])
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if str(e)\
|
if str(e)\
|
||||||
!= "prepend_scheme='http' is not in allowed_schemes=[None, 'https']":
|
!= "prepend_scheme='http' is not in allowed_schemes=[None, 'https']":
|
||||||
self.fail('Wrong exception: ' + str(e))
|
self.fail('Wrong exception: ' + str(e))
|
||||||
else:
|
else:
|
||||||
self.fail("Accepted invalid prepend_scheme: 'http'")
|
self.fail("Accepted invalid prepend_scheme: 'http'")
|
||||||
@@ -229,7 +231,7 @@ class TestIsUrl(unittest.TestCase):
|
|||||||
prepend_scheme='https')
|
prepend_scheme='https')
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if str(e)\
|
if str(e)\
|
||||||
!= "prepend_scheme='https' is not in allowed_schemes=[None, 'http']":
|
!= "prepend_scheme='https' is not in allowed_schemes=[None, 'http']":
|
||||||
self.fail('Wrong exception: ' + str(e))
|
self.fail('Wrong exception: ' + str(e))
|
||||||
else:
|
else:
|
||||||
self.fail("Accepted invalid prepend_scheme: 'https'")
|
self.fail("Accepted invalid prepend_scheme: 'https'")
|
||||||
@@ -241,7 +243,7 @@ class TestIsUrl(unittest.TestCase):
|
|||||||
'ftps'])
|
'ftps'])
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if str(e)\
|
if str(e)\
|
||||||
!= "prepend_scheme='http' is not in allowed_schemes=[None, 'ftp', 'ftps']":
|
!= "prepend_scheme='http' is not in allowed_schemes=[None, 'ftp', 'ftps']":
|
||||||
self.fail('Wrong exception: ' + str(e))
|
self.fail('Wrong exception: ' + str(e))
|
||||||
else:
|
else:
|
||||||
self.fail("Accepted invalid prepend_scheme: 'http'")
|
self.fail("Accepted invalid prepend_scheme: 'http'")
|
||||||
@@ -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="):
|
||||||
@@ -266,7 +269,7 @@ class TestIsUrl(unittest.TestCase):
|
|||||||
prepend_scheme='blargg')
|
prepend_scheme='blargg')
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if str(e)\
|
if str(e)\
|
||||||
!= "prepend_scheme='blargg' is not in allowed_schemes=[None, 'http']":
|
!= "prepend_scheme='blargg' is not in allowed_schemes=[None, 'http']":
|
||||||
self.fail('Wrong exception: ' + str(e))
|
self.fail('Wrong exception: ' + str(e))
|
||||||
else:
|
else:
|
||||||
self.fail("Accepted invalid prepend_scheme: 'blargg'")
|
self.fail("Accepted invalid prepend_scheme: 'blargg'")
|
||||||
@@ -282,7 +285,7 @@ class TestIsUrl(unittest.TestCase):
|
|||||||
# prepend_scheme has the invalid value 'http', we don't care!
|
# prepend_scheme has the invalid value 'http', we don't care!
|
||||||
|
|
||||||
x = IS_URL(mode='generic', allowed_schemes=['https'],
|
x = IS_URL(mode='generic', allowed_schemes=['https'],
|
||||||
prepend_scheme='https')
|
prepend_scheme='https')
|
||||||
self.assertEqual(x('google.ca'), ('google.ca', 'enter a valid URL'))
|
self.assertEqual(x('google.ca'), ('google.ca', 'enter a valid URL'))
|
||||||
|
|
||||||
|
|
||||||
@@ -335,12 +338,12 @@ class TestIsGenericUrl(unittest.TestCase):
|
|||||||
'ht,tp://www.benn.ca',
|
'ht,tp://www.benn.ca',
|
||||||
'ht:tp://www.benn.ca',
|
'ht:tp://www.benn.ca',
|
||||||
'htp://invalid_scheme.com',
|
'htp://invalid_scheme.com',
|
||||||
]
|
]
|
||||||
|
|
||||||
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:
|
||||||
@@ -405,7 +407,7 @@ class TestIsGenericUrl(unittest.TestCase):
|
|||||||
# because a scheme is required
|
# because a scheme is required
|
||||||
|
|
||||||
y = IS_GENERIC_URL(allowed_schemes=['http', 'blargg'],
|
y = IS_GENERIC_URL(allowed_schemes=['http', 'blargg'],
|
||||||
prepend_scheme='http')
|
prepend_scheme='http')
|
||||||
self.assertEqual(y('google.ca'), ('google.ca', 'enter a valid URL'))
|
self.assertEqual(y('google.ca'), ('google.ca', 'enter a valid URL'))
|
||||||
|
|
||||||
|
|
||||||
@@ -459,12 +461,12 @@ class TestIsHttpUrl(unittest.TestCase):
|
|||||||
'path/segment/without/starting/slash',
|
'path/segment/without/starting/slash',
|
||||||
'http://www.math.uio.no;param=3',
|
'http://www.math.uio.no;param=3',
|
||||||
'://ABC.com:/%7esmith/home.html',
|
'://ABC.com:/%7esmith/home.html',
|
||||||
]
|
]
|
||||||
|
|
||||||
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',
|
||||||
@@ -534,12 +535,12 @@ class TestIsHttpUrl(unittest.TestCase):
|
|||||||
'http://localhost:8080/hello%20world/',
|
'http://localhost:8080/hello%20world/',
|
||||||
'http://www.a.3.be-nn.5.ca',
|
'http://www.a.3.be-nn.5.ca',
|
||||||
'http://www.amazon.COM',
|
'http://www.amazon.COM',
|
||||||
]
|
]
|
||||||
|
|
||||||
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,17 +558,20 @@ 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')
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if str(e)\
|
if str(e)\
|
||||||
!= "prepend_scheme='mailto' is not in allowed_schemes=[None, 'http', 'https']":
|
!= "prepend_scheme='mailto' is not in allowed_schemes=[None, 'http', 'https']":
|
||||||
self.fail('Wrong exception: ' + str(e))
|
self.fail('Wrong exception: ' + str(e))
|
||||||
else:
|
else:
|
||||||
self.fail("Got invalid prepend_scheme: 'mailto'")
|
self.fail("Got invalid 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()
|
||||||
|
|
||||||
@@ -49,12 +49,11 @@ try:
|
|||||||
|
|
||||||
def test_reads_and_writes(self):
|
def test_reads_and_writes(self):
|
||||||
readwriters = 10
|
readwriters = 10
|
||||||
pool = multiprocessing.Pool(processes = readwriters)
|
pool = multiprocessing.Pool(processes=readwriters)
|
||||||
results = pool.map(read_write, [[self.filename, 10]] * readwriters)
|
results = pool.map(read_write, [[self.filename, 10]] * readwriters)
|
||||||
for result in results:
|
for result in results:
|
||||||
self.assertTrue(result)
|
self.assertTrue(result)
|
||||||
|
|
||||||
|
|
||||||
class TestTranslations(unittest.TestCase):
|
class TestTranslations(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -62,11 +61,11 @@ try:
|
|||||||
if os.path.isdir('gluon'):
|
if os.path.isdir('gluon'):
|
||||||
self.request.folder = 'applications/welcome'
|
self.request.folder = 'applications/welcome'
|
||||||
else:
|
else:
|
||||||
self.request.folder = os.path.realpath('../../applications/welcome')
|
self.request.folder = os.path.realpath(
|
||||||
|
'../../applications/welcome')
|
||||||
self.request.env = Storage()
|
self.request.env = Storage()
|
||||||
self.request.env.http_accept_language = 'en'
|
self.request.env.http_accept_language = 'en'
|
||||||
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -99,4 +98,3 @@ except ImportError:
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
@@ -13,10 +13,10 @@ else:
|
|||||||
import unittest
|
import unittest
|
||||||
from contrib.markmin.markmin2html import run_doctests
|
from contrib.markmin.markmin2html import run_doctests
|
||||||
|
|
||||||
|
|
||||||
class TestMarkmin(unittest.TestCase):
|
class TestMarkmin(unittest.TestCase):
|
||||||
def testMarkmin(self):
|
def testMarkmin(self):
|
||||||
run_doctests()
|
run_doctests()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
+868
-513
File diff suppressed because it is too large.
Load diff
+172
-91
@@ -10,9 +10,9 @@ import tempfile
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
if os.path.isdir('gluon'):
|
if os.path.isdir('gluon'):
|
||||||
sys.path.append(os.path.realpath('gluon')) # running from web2py base
|
sys.path.append(os.path.realpath('gluon')) # running from web2py base
|
||||||
else:
|
else:
|
||||||
sys.path.append(os.path.realpath('../')) # running from gluon/tests/
|
sys.path.append(os.path.realpath('../')) # running from gluon/tests/
|
||||||
os.environ['web2py_path'] = os.path.realpath('../../') # for settings
|
os.environ['web2py_path'] = os.path.realpath('../../') # for settings
|
||||||
|
|
||||||
from rewrite import load, filter_url, filter_err, get_effective_router, regex_filter_out, regex_select
|
from rewrite import load, filter_url, filter_err, get_effective_router, regex_filter_out, regex_select
|
||||||
@@ -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 = [
|
||||||
@@ -276,8 +339,9 @@ 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 = [
|
||||||
@@ -285,8 +349,9 @@ 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 = [
|
||||||
@@ -294,13 +359,17 @@ 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):
|
||||||
@@ -311,37 +380,46 @@ routes_out = [
|
|||||||
r = Storage()
|
r = Storage()
|
||||||
r.env = Storage()
|
r.env = Storage()
|
||||||
r.env.http_host = 'domain.com'
|
r.env.http_host = 'domain.com'
|
||||||
r.env.wsgi_url_scheme = 'httpx' # distinguish incoming scheme
|
r.env.wsgi_url_scheme = 'httpx' # distinguish incoming scheme
|
||||||
self.assertEqual(str(URL(r=r, a='a', c='c', f='f')), "/a/c/f")
|
self.assertEqual(str(URL(r=r, a='a', c='c', f='f')), "/a/c/f")
|
||||||
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', host=True)),
|
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', host=True)),
|
||||||
"httpx://domain.com/a/c/f")
|
"httpx://domain.com/a/c/f")
|
||||||
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', host='host.com')),
|
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', host='host.com')),
|
||||||
"httpx://host.com/a/c/f")
|
"httpx://host.com/a/c/f")
|
||||||
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=True)),
|
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=True)),
|
||||||
"httpx://domain.com/a/c/f")
|
"httpx://domain.com/a/c/f")
|
||||||
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=False)),
|
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme=False)),
|
||||||
"/a/c/f")
|
"/a/c/f")
|
||||||
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme='https')),
|
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme='https')),
|
||||||
"https://domain.com/a/c/f")
|
"https://domain.com/a/c/f")
|
||||||
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme='wss')),
|
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")
|
||||||
|
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', scheme=True, port=1234)),
|
self.assertEqual(
|
||||||
"httpx://domain.com:1234/a/c/f")
|
str(URL(r=r, a='a', c='c', f='f', host='host.com', port=1234)),
|
||||||
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', host='host.com', port=1234)),
|
|
||||||
"httpx://host.com:1234/a/c/f")
|
"httpx://host.com:1234/a/c/f")
|
||||||
self.assertEqual(str(URL(r=r, a='a', c='c', f='f', scheme='wss', host='host.com', port=1234)),
|
self.assertEqual(
|
||||||
|
str(URL(r=r, a='a', c='c', f='f', scheme='wss',
|
||||||
|
host='host.com', port=1234)),
|
||||||
"wss://host.com:1234/a/c/f")
|
"wss://host.com:1234/a/c/f")
|
||||||
|
|
||||||
def test_request_uri(self):
|
def test_request_uri(self):
|
||||||
@@ -354,13 +432,17 @@ routes_out = [
|
|||||||
]
|
]
|
||||||
'''
|
'''
|
||||||
load(data=data)
|
load(data=data)
|
||||||
self.assertEqual(filter_url('http://domain.com/abc', env=True).request_uri,
|
self.assertEqual(
|
||||||
|
filter_url('http://domain.com/abc', env=True).request_uri,
|
||||||
'/init/default/abc')
|
'/init/default/abc')
|
||||||
self.assertEqual(filter_url('http://domain.com/abc?def', env=True).request_uri,
|
self.assertEqual(
|
||||||
|
filter_url('http://domain.com/abc?def', env=True).request_uri,
|
||||||
'/init/default/abc?def')
|
'/init/default/abc?def')
|
||||||
self.assertEqual(filter_url('http://domain.com/index/abc', env=True).request_uri,
|
self.assertEqual(
|
||||||
|
filter_url('http://domain.com/index/abc', env=True).request_uri,
|
||||||
"/init/default/index/abc")
|
"/init/default/index/abc")
|
||||||
self.assertEqual(filter_url('http://domain.com/index/a%20bc', env=True).request_uri,
|
self.assertEqual(
|
||||||
|
filter_url('http://domain.com/index/a%20bc', env=True).request_uri,
|
||||||
"/init/default/index/a bc")
|
"/init/default/index/a bc")
|
||||||
|
|
||||||
|
|
||||||
@@ -368,4 +450,3 @@ if __name__ == '__main__':
|
|||||||
setUpModule() # pre-2.7
|
setUpModule() # pre-2.7
|
||||||
unittest.main()
|
unittest.main()
|
||||||
tearDownModule()
|
tearDownModule()
|
||||||
|
|
||||||
@@ -39,7 +39,6 @@ class TestStorage(unittest.TestCase):
|
|||||||
s.d = list()
|
s.d = list()
|
||||||
self.assertTrue(s.d is s['d'])
|
self.assertTrue(s.d is s['d'])
|
||||||
|
|
||||||
|
|
||||||
def test_store_none(self):
|
def test_store_none(self):
|
||||||
""" Test Storage store-None handling
|
""" Test Storage store-None handling
|
||||||
s.key = None deletes an item
|
s.key = None deletes an item
|
||||||
@@ -59,7 +58,6 @@ class TestStorage(unittest.TestCase):
|
|||||||
self.assertTrue('a' in s)
|
self.assertTrue('a' in s)
|
||||||
self.assertTrue(s.a is None)
|
self.assertTrue(s.a is None)
|
||||||
|
|
||||||
|
|
||||||
def test_item(self):
|
def test_item(self):
|
||||||
""" Tests Storage item handling """
|
""" Tests Storage item handling """
|
||||||
|
|
||||||
@@ -76,4 +74,3 @@ class TestStorage(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
@@ -14,15 +14,17 @@ 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):
|
||||||
self.assertEqual(render(content='{{for i in range(n):}}{{=i}}{{pass}}',
|
self.assertEqual(render(content='{{for i in range(n):}}{{=i}}{{pass}}',
|
||||||
context=dict(n=3)), '012')
|
context=dict(n=3)), '012')
|
||||||
self.assertEqual(render(content='{{if n>2:}}ok{{pass}}',
|
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(
|
||||||
context=dict(n=3)), 'fail')
|
render(content='{{try:}}{{n/0}}{{except:}}fail{{pass}}',
|
||||||
|
context=dict(n=3)), 'fail')
|
||||||
self.assertEqual(render(content='{{="<&>"}}'), '<&>')
|
self.assertEqual(render(content='{{="<&>"}}'), '<&>')
|
||||||
self.assertEqual(render(content='"abc"'), '"abc"')
|
self.assertEqual(render(content='"abc"'), '"abc"')
|
||||||
self.assertEqual(render(content='"a\'bc"'), '"a\'bc"')
|
self.assertEqual(render(content='"a\'bc"'), '"a\'bc"')
|
||||||
@@ -38,13 +40,18 @@ class TestVirtualFields(unittest.TestCase):
|
|||||||
self.assertEqual(render(content='{{ ="abc" }}'), 'abc')
|
self.assertEqual(render(content='{{ ="abc" }}'), 'abc')
|
||||||
self.assertEqual(render(content='{{pass\n="abc" }}'), 'abc')
|
self.assertEqual(render(content='{{pass\n="abc" }}'), 'abc')
|
||||||
# = recognized only at the beginning of a physical line
|
# = recognized only at the beginning of a physical line
|
||||||
self.assertEqual(render(content='{{xyz = "xyz"\n="abc"\n="def"\n=xyz }}'), 'abcdefxyz')
|
self.assertEqual(render(
|
||||||
|
content='{{xyz = "xyz"\n="abc"\n="def"\n=xyz }}'), 'abcdefxyz')
|
||||||
# = in python blocks
|
# = in python blocks
|
||||||
self.assertEqual(render(content='{{if True:\n="abc"\npass }}'), 'abc')
|
self.assertEqual(render(content='{{if True:\n="abc"\npass }}'), 'abc')
|
||||||
self.assertEqual(render(content='{{if True:\n="abc"\npass\n="def" }}'), 'abcdef')
|
self.assertEqual(
|
||||||
self.assertEqual(render(content='{{if False:\n="abc"\npass\n="def" }}'), 'def')
|
render(content='{{if True:\n="abc"\npass\n="def" }}'), 'abcdef')
|
||||||
self.assertEqual(render(content='{{if True:\n="abc"\nelse:\n="def"\npass }}'), 'abc')
|
self.assertEqual(
|
||||||
self.assertEqual(render(content='{{if False:\n="abc"\nelse:\n="def"\npass }}'), 'def')
|
render(content='{{if False:\n="abc"\npass\n="def" }}'), 'def')
|
||||||
|
self.assertEqual(render(
|
||||||
|
content='{{if True:\n="abc"\nelse:\n="def"\npass }}'), 'abc')
|
||||||
|
self.assertEqual(render(
|
||||||
|
content='{{if False:\n="abc"\nelse:\n="def"\npass }}'), 'def')
|
||||||
# codeblock-leading = handles internal newlines, escaped or not
|
# codeblock-leading = handles internal newlines, escaped or not
|
||||||
self.assertEqual(render(content='{{=list((1,2,3))}}'), '[1, 2, 3]')
|
self.assertEqual(render(content='{{=list((1,2,3))}}'), '[1, 2, 3]')
|
||||||
self.assertEqual(render(content='{{=list((1,2,\\\n3))}}'), '[1, 2, 3]')
|
self.assertEqual(render(content='{{=list((1,2,\\\n3))}}'), '[1, 2, 3]')
|
||||||
@@ -52,10 +59,11 @@ class TestVirtualFields(unittest.TestCase):
|
|||||||
# ...but that means no more = operators in the codeblock
|
# ...but that means no more = operators in the codeblock
|
||||||
self.assertRaises(SyntaxError, render, content='{{="abc"\n="def" }}')
|
self.assertRaises(SyntaxError, render, content='{{="abc"\n="def" }}')
|
||||||
# = embedded in codeblock won't handle newlines in its argument
|
# = embedded in codeblock won't handle newlines in its argument
|
||||||
self.assertEqual(render(content='{{pass\n=list((1,2,\\\n3))}}'), '[1, 2, 3]')
|
self.assertEqual(
|
||||||
self.assertRaises(SyntaxError, render, content='{{pass\n=list((1,2,\n3))}}')
|
render(content='{{pass\n=list((1,2,\\\n3))}}'), '[1, 2, 3]')
|
||||||
|
self.assertRaises(
|
||||||
|
SyntaxError, render, content='{{pass\n=list((1,2,\n3))}}')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
@@ -25,4 +25,3 @@ class TestUtils(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
+13
-12
@@ -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/')
|
||||||
@@ -20,13 +21,13 @@ class TestWeb(unittest.TestCase):
|
|||||||
client.get('index')
|
client.get('index')
|
||||||
|
|
||||||
# register
|
# register
|
||||||
data = dict(first_name = 'Homer',
|
data = dict(first_name='Homer',
|
||||||
last_name = 'Simpson',
|
last_name='Simpson',
|
||||||
email = 'homer@web2py.com',
|
email='homer@web2py.com',
|
||||||
password = 'test',
|
password='test',
|
||||||
password_two = 'test',
|
password_two='test',
|
||||||
_formname = 'register')
|
_formname='register')
|
||||||
client.post('user/register',data = data)
|
client.post('user/register', data=data)
|
||||||
|
|
||||||
# logout
|
# logout
|
||||||
client.get('user/logout')
|
client.get('user/logout')
|
||||||
@@ -34,21 +35,22 @@ class TestWeb(unittest.TestCase):
|
|||||||
# login again
|
# login again
|
||||||
data = dict(email='homer@web2py.com',
|
data = dict(email='homer@web2py.com',
|
||||||
password='test',
|
password='test',
|
||||||
_formname = 'login')
|
_formname='login')
|
||||||
client.post('user/login',data = data)
|
client.post('user/login', data=data)
|
||||||
|
|
||||||
# check registration and login were successful
|
# check registration and login were successful
|
||||||
client.get('index')
|
client.get('index')
|
||||||
self.assertTrue('Welcome Homer' in client.text)
|
self.assertTrue('Welcome Homer' in client.text)
|
||||||
|
|
||||||
client = WebClient('http://127.0.0.1:8000/admin/default/')
|
client = WebClient('http://127.0.0.1:8000/admin/default/')
|
||||||
client.post('index',data=dict(password='hello'))
|
client.post('index', data=dict(password='hello'))
|
||||||
client.get('site')
|
client.get('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/')
|
||||||
s.get('static/js/web2py.js')
|
s.get('static/js/web2py.js')
|
||||||
assert('expires' not in s.headers)
|
assert('expires' not in s.headers)
|
||||||
assert(not s.headers['cache-control'].startswith('max-age'))
|
assert(not s.headers['cache-control'].startswith('max-age'))
|
||||||
@@ -60,4 +62,3 @@ class TestStaticCacheControl(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
Loaded 100 of 117 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user