[REF] first shot at converting everything to werkzeug
bzr revid: xmo@openerp.com-20110902085853-38y28g2x0d13p8ne
This commit is contained in:
parent
6e739caf10
commit
80d0859837
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/python
|
||||
import urllib
|
||||
from __future__ import with_statement
|
||||
|
||||
import functools
|
||||
import logging
|
||||
import os
|
||||
|
@ -8,11 +9,15 @@ import traceback
|
|||
import uuid
|
||||
import xmlrpclib
|
||||
|
||||
import cherrypy
|
||||
import cherrypy.lib.static
|
||||
import simplejson
|
||||
import werkzeug.exceptions
|
||||
import werkzeug.utils
|
||||
import werkzeug.wrappers
|
||||
import werkzeug.wsgi
|
||||
|
||||
import ast
|
||||
import nonliterals
|
||||
import http
|
||||
# import backendlocal as backend
|
||||
import backendrpc as backend
|
||||
|
||||
|
@ -40,27 +45,28 @@ controllers_path = {}
|
|||
#----------------------------------------------------------
|
||||
# OpenERP Web RequestHandler
|
||||
#----------------------------------------------------------
|
||||
class CherryPyRequest(object):
|
||||
class WebRequest(object):
|
||||
""" CherryPy request handling
|
||||
"""
|
||||
def init(self,params):
|
||||
self.params = params
|
||||
def __init__(self, request, config):
|
||||
# Move cherrypy thread local objects to attributes
|
||||
self.applicationsession = applicationsession
|
||||
self.httprequest = cherrypy.request
|
||||
self.httpresponse = cherrypy.response
|
||||
self.httpsession = cherrypy.session
|
||||
self.httpsession_id = "cookieid"
|
||||
self.httprequest = request
|
||||
self.httpresponse = None
|
||||
self.httpsession = request.session
|
||||
self.config = config
|
||||
# Request attributes
|
||||
def init(self, params):
|
||||
self.params = dict(params)
|
||||
# OpenERP session setup
|
||||
self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
|
||||
host = cherrypy.config['openerp.server.host']
|
||||
port = cherrypy.config['openerp.server.port']
|
||||
self.session = self.httpsession.setdefault(self.session_id, backend.OpenERPSession(host, port))
|
||||
# Request attributes
|
||||
self.session = self.httpsession.setdefault(
|
||||
self.session_id, backend.OpenERPSession(
|
||||
self.config.server_host, self.config.server_port))
|
||||
self.context = self.params.pop('context', None)
|
||||
self.debug = self.params.pop('debug',False) != False
|
||||
self.debug = self.params.pop('debug', False) != False
|
||||
|
||||
class JsonRequest(CherryPyRequest):
|
||||
class JsonRequest(WebRequest):
|
||||
""" JSON-RPC2 over HTTP.
|
||||
|
||||
Sucessful request::
|
||||
|
@ -138,8 +144,8 @@ class JsonRequest(CherryPyRequest):
|
|||
}
|
||||
}
|
||||
except Exception:
|
||||
cherrypy.log("An error occured while handling a json request",
|
||||
severity=logging.ERROR, traceback=True)
|
||||
logging.getLogger('openerp.JSONRequest.dispatch').exception\
|
||||
("An error occured while handling a json request")
|
||||
error = {
|
||||
'code': 300,
|
||||
'message': "OpenERP WebClient Error",
|
||||
|
@ -156,42 +162,55 @@ class JsonRequest(CherryPyRequest):
|
|||
print
|
||||
|
||||
content = simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder)
|
||||
cherrypy.response.headers['Content-Type'] = 'application/json'
|
||||
cherrypy.response.headers['Content-Length'] = len(content)
|
||||
return content
|
||||
return werkzeug.wrappers.Response(
|
||||
content, headers=[('Content-Type', 'application/json'),
|
||||
('Content-Length', len(content))])
|
||||
|
||||
def jsonrequest(f):
|
||||
@cherrypy.expose
|
||||
@functools.wraps(f)
|
||||
def json_handler(controller):
|
||||
return JsonRequest().dispatch(controller, f, requestf=cherrypy.request.body)
|
||||
def json_handler(controller, request, config):
|
||||
return JsonRequest(request, config).dispatch(
|
||||
controller, f, requestf=request.stream)
|
||||
json_handler.exposed = True
|
||||
return json_handler
|
||||
|
||||
class HttpRequest(CherryPyRequest):
|
||||
class HttpRequest(WebRequest):
|
||||
""" Regular GET/POST request
|
||||
"""
|
||||
def dispatch(self, controller, method, **kw):
|
||||
self.init(kw)
|
||||
def dispatch(self, controller, method):
|
||||
self.init(self.httprequest.args)
|
||||
akw = {}
|
||||
for key in kw.keys():
|
||||
if isinstance(kw[key], basestring) and len(kw[key]) < 1024:
|
||||
akw[key] = kw[key]
|
||||
for key, value in self.httprequest.args.iteritems():
|
||||
if isinstance(value, basestring) and len(value) < 1024:
|
||||
akw[key] = value
|
||||
else:
|
||||
akw[key] = type(kw[key])
|
||||
akw[key] = type(value)
|
||||
if self.debug or 1:
|
||||
print "%s --> %s.%s %r" % (self.httprequest.method, controller.__class__.__name__, method.__name__, akw)
|
||||
r = method(controller, self, **kw)
|
||||
r = method(controller, self, **self.params)
|
||||
if self.debug or 1:
|
||||
print "<--", 'size:', len(r)
|
||||
if isinstance(r, werkzeug.wrappers.BaseResponse):
|
||||
print '<--', r
|
||||
else:
|
||||
print "<--", 'size:', len(r)
|
||||
print
|
||||
return r
|
||||
|
||||
def make_response(self, data, headers=None, cookies=None):
|
||||
response = werkzeug.wrappers.Response(data, headers=headers)
|
||||
if cookies:
|
||||
for k, v in cookies.iteritems():
|
||||
response.set_cookie(k, v)
|
||||
return response
|
||||
|
||||
def not_found(self, description=None):
|
||||
return werkzeug.exceptions.NotFound(description)
|
||||
|
||||
def httprequest(f):
|
||||
# check cleaner wrapping:
|
||||
# functools.wraps(f)(lambda x: JsonRequest().dispatch(x, f))
|
||||
def http_handler(controller,*l, **kw):
|
||||
return HttpRequest().dispatch(controller, f, **kw)
|
||||
http_handler.exposed = 1
|
||||
@functools.wraps(f)
|
||||
def http_handler(controller, request, config):
|
||||
return HttpRequest(request, config).dispatch(controller, f)
|
||||
http_handler.exposed = True
|
||||
return http_handler
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
@ -207,41 +226,82 @@ class Controller(object):
|
|||
__metaclass__ = ControllerType
|
||||
|
||||
class Root(object):
|
||||
def __init__(self):
|
||||
def __init__(self, options):
|
||||
self.config = options
|
||||
|
||||
self.session_cookie = 'sessionid'
|
||||
self.addons = {}
|
||||
self._load_addons()
|
||||
|
||||
static_dirs = self._load_addons()
|
||||
if options.serve_static:
|
||||
self.dispatch = werkzeug.wsgi.SharedDataMiddleware(
|
||||
self.dispatch, static_dirs)
|
||||
|
||||
if options.session_storage:
|
||||
if not os.path.exists(options.session_storage):
|
||||
os.mkdir(options.session_storage, 0700)
|
||||
self.session_storage = options.session_storage
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
return self.dispatch(environ, start_response)
|
||||
|
||||
def dispatch(self, environ, start_response):
|
||||
request = werkzeug.wrappers.Request(environ)
|
||||
|
||||
if request.path == '/':
|
||||
return werkzeug.utils.redirect(
|
||||
'/base/webclient/home', 301)(environ, start_response)
|
||||
elif request.path == '/mobile':
|
||||
return werkzeug.utils.redirect(
|
||||
'/web_mobile/static/src/web_mobile.html', 301)(
|
||||
environ, start_response)
|
||||
|
||||
handler = self.find_handler(*(request.path.split('/')[1:]))
|
||||
|
||||
if not handler:
|
||||
response = werkzeug.exceptions.NotFound()
|
||||
else:
|
||||
with http.session(request, self.session_storage, self.session_cookie) as session:
|
||||
result = handler(
|
||||
request, self.config)
|
||||
|
||||
if isinstance(result, werkzeug.wrappers.Response):
|
||||
response = result
|
||||
else:
|
||||
response = werkzeug.wrappers.Response(
|
||||
result, headers=[('Content-Type', 'text/html; charset=utf-8'),
|
||||
('Content-Length', len(result))])
|
||||
|
||||
response.set_cookie(self.session_cookie, session.sid)
|
||||
|
||||
return response(environ, start_response)
|
||||
|
||||
def _load_addons(self):
|
||||
statics = {}
|
||||
if path_addons not in sys.path:
|
||||
sys.path.insert(0, path_addons)
|
||||
for i in os.listdir(path_addons):
|
||||
if i not in addons_module:
|
||||
manifest_path = os.path.join(path_addons, i, '__openerp__.py')
|
||||
for module in os.listdir(path_addons):
|
||||
if module not in addons_module:
|
||||
manifest_path = os.path.join(path_addons, module, '__openerp__.py')
|
||||
if os.path.isfile(manifest_path):
|
||||
manifest = eval(open(manifest_path).read())
|
||||
print "Loading", i
|
||||
m = __import__(i)
|
||||
addons_module[i] = m
|
||||
addons_manifest[i] = manifest
|
||||
manifest = ast.literal_eval(open(manifest_path).read())
|
||||
print "Loading", module
|
||||
m = __import__(module)
|
||||
addons_module[module] = m
|
||||
addons_manifest[module] = manifest
|
||||
|
||||
statics['/%s/static' % module] = \
|
||||
os.path.join(path_addons, module, 'static')
|
||||
for k, v in controllers_class.items():
|
||||
if k not in controllers_object:
|
||||
o = v()
|
||||
controllers_object[k] = o
|
||||
if hasattr(o, '_cp_path'):
|
||||
controllers_path[o._cp_path] = o
|
||||
return statics
|
||||
|
||||
def default(self, *l, **kw):
|
||||
print "default",l,kw
|
||||
# handle static files
|
||||
if len(l) > 2 and l[1] == 'static':
|
||||
# sanitize path
|
||||
p = os.path.normpath(os.path.join(*l))
|
||||
p2 = os.path.join(path_addons, p)
|
||||
print "p",p
|
||||
print "p2",p2
|
||||
|
||||
return cherrypy.lib.static.serve_file(p2)
|
||||
elif len(l) > 1:
|
||||
def find_handler(self, *l):
|
||||
if len(l) > 1:
|
||||
for i in range(len(l), 1, -1):
|
||||
ps = "/" + "/".join(l[0:i])
|
||||
if ps in controllers_path:
|
||||
|
@ -249,19 +309,7 @@ class Root(object):
|
|||
rest = l[i:] or ['index']
|
||||
meth = rest[0]
|
||||
m = getattr(c, meth)
|
||||
if getattr(m, 'exposed', 0):
|
||||
print "Calling", ps, c, meth, m
|
||||
return m(**kw)
|
||||
raise cherrypy.NotFound('/' + '/'.join(l))
|
||||
elif l and l[0] == 'mobile':
|
||||
#for the mobile web client we are supposed to use a different url to just add '/mobile'
|
||||
raise cherrypy.HTTPRedirect('/web_mobile/static/src/web_mobile.html', 301)
|
||||
else:
|
||||
if kw:
|
||||
qs = '?' + urllib.urlencode(kw)
|
||||
else:
|
||||
qs = ''
|
||||
raise cherrypy.HTTPRedirect('/base/webclient/home' + qs, 301)
|
||||
default.exposed = True
|
||||
|
||||
#
|
||||
if getattr(m, 'exposed', False):
|
||||
print "Dispatching to", ps, c, meth, m
|
||||
return m
|
||||
return None
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import contextlib
|
||||
|
||||
import werkzeug.contrib.sessions
|
||||
|
||||
STORES = {}
|
||||
|
||||
@contextlib.contextmanager
|
||||
def session(request, storage_path, session_cookie='sessionid'):
|
||||
session_store = STORES.get(storage_path)
|
||||
if not session_store:
|
||||
session_store = werkzeug.contrib.sessions.FilesystemSessionStore(
|
||||
storage_path)
|
||||
STORES[storage_path] = session_store
|
||||
|
||||
sid = request.cookies.get(session_cookie)
|
||||
if sid:
|
||||
request.session = session_store.get(sid)
|
||||
else:
|
||||
request.session = session_store.new()
|
||||
|
||||
yield request.session
|
||||
|
||||
session_store.save(request.session)
|
|
@ -53,29 +53,29 @@ class DispatcherTest(unittest2.TestCase):
|
|||
def test_default_redirect(self):
|
||||
self.assertRaises(
|
||||
cherrypy.HTTPRedirect,
|
||||
openerpweb.openerpweb.Root().default)
|
||||
openerpweb.openerpweb.Root().find_handler)
|
||||
def test_serve_static_missing(self):
|
||||
self.assertRaises(
|
||||
cherrypy.NotFound,
|
||||
openerpweb.openerpweb.Root().default,
|
||||
openerpweb.openerpweb.Root().find_handler,
|
||||
'does-not-exist', 'static', 'bar')
|
||||
|
||||
def test_serve_controller_missing(self):
|
||||
self.assertRaises(
|
||||
cherrypy.NotFound,
|
||||
openerpweb.openerpweb.Root().default,
|
||||
openerpweb.openerpweb.Root().find_handler,
|
||||
'controller', 'does', 'not', 'exist')
|
||||
|
||||
def test_find_controller_method(self):
|
||||
openerpweb.openerpweb.Root().default(
|
||||
openerpweb.openerpweb.Root().find_handler(
|
||||
'some', 'controller', 'path', 'method')
|
||||
self.mock_method.assert_called_once_with()
|
||||
def test_find_controller_index(self):
|
||||
openerpweb.openerpweb.Root().default(
|
||||
openerpweb.openerpweb.Root().find_handler(
|
||||
'some', 'controller', 'path')
|
||||
self.mock_index.assert_called_once_with()
|
||||
|
||||
def test_nested_paths(self):
|
||||
openerpweb.openerpweb.Root().default(
|
||||
openerpweb.openerpweb.Root().find_handler(
|
||||
'some', 'other', 'controller', '2')
|
||||
self.mock_index.assert_called_once_with()
|
||||
|
|
|
@ -13,8 +13,6 @@ import time
|
|||
from xml.etree import ElementTree
|
||||
from cStringIO import StringIO
|
||||
|
||||
import cherrypy
|
||||
|
||||
import base.common as openerpweb
|
||||
import base.common.ast
|
||||
import base.common.nonliterals
|
||||
|
@ -130,19 +128,17 @@ class WebClient(openerpweb.Controller):
|
|||
|
||||
@openerpweb.httprequest
|
||||
def css(self, req, mods='base'):
|
||||
req.httpresponse.headers['Content-Type'] = 'text/css'
|
||||
files = manifest_glob(mods.split(','), 'css')
|
||||
content,timestamp = concat_files(files)
|
||||
# TODO request set the Date of last modif and Etag
|
||||
return content
|
||||
return req.make_response(content, [('Content-Type', 'text/css')])
|
||||
|
||||
@openerpweb.httprequest
|
||||
def js(self, req, mods='base'):
|
||||
req.httpresponse.headers['Content-Type'] = 'application/javascript'
|
||||
files = manifest_glob(mods.split(','), 'js')
|
||||
content,timestamp = concat_files(files)
|
||||
# TODO request set the Date of last modif and Etag
|
||||
return content
|
||||
return req.make_response(content, [('Content-Type', 'application/javascript')])
|
||||
|
||||
@openerpweb.httprequest
|
||||
def home(self, req, s_action=None, **kw):
|
||||
|
@ -208,7 +204,7 @@ class Database(openerpweb.Controller):
|
|||
dbs = proxy.list()
|
||||
h = req.httprequest.headers['Host'].split(':')[0]
|
||||
d = h.split('.')[0]
|
||||
r = cherrypy.config['openerp.dbfilter'].replace('%h', h).replace('%d', d)
|
||||
r = req.config.dbfilter.replace('%h', h).replace('%d', d)
|
||||
dbs = [i for i in dbs if re.match(r, i)]
|
||||
return {"db_list": dbs}
|
||||
|
||||
|
@ -253,11 +249,11 @@ class Database(openerpweb.Controller):
|
|||
try:
|
||||
db_dump = base64.decodestring(
|
||||
req.session.proxy("db").dump(backup_pwd, backup_db))
|
||||
req.httpresponse.headers['Content-Type'] = "application/octet-stream; charset=binary"
|
||||
req.httpresponse.headers['Content-Disposition'] = 'attachment; filename="' + backup_db + '.dump"'
|
||||
req.httpresponse.cookie['fileToken'] = token
|
||||
req.httpresponse.cookie['fileToken']['path'] = '/'
|
||||
return db_dump
|
||||
return req.make_response(db_dump,
|
||||
[('Content-Type', 'application/octet-stream; charset=binary'),
|
||||
('Content-Disposition', 'attachment; filename="' + backup_db + '.dump"')],
|
||||
{'fileToken': token}
|
||||
)
|
||||
except xmlrpclib.Fault, e:
|
||||
if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied':
|
||||
return 'Backup Database|' + e.faultCode
|
||||
|
@ -985,18 +981,17 @@ class Binary(openerpweb.Controller):
|
|||
res = Model.read([int(id)], [field, fieldname], context)[0]
|
||||
filecontent = res.get(field, '')
|
||||
if not filecontent:
|
||||
raise cherrypy.NotFound
|
||||
return req.not_found()
|
||||
else:
|
||||
req.httpresponse.headers['Content-Type'] = 'application/octet-stream'
|
||||
filename = '%s_%s' % (model.replace('.', '_'), id)
|
||||
if fieldname:
|
||||
filename = res.get(fieldname, '') or filename
|
||||
req.httpresponse.headers['Content-Disposition'] = 'attachment; filename=' + filename
|
||||
return base64.decodestring(filecontent)
|
||||
return req.make_response(filecontent,
|
||||
[('Content-Type', 'application/octet-stream'),
|
||||
('Content-Disposition', 'attachment; filename=' + filename)])
|
||||
|
||||
@openerpweb.httprequest
|
||||
def upload(self, req, callback, ufile=None):
|
||||
cherrypy.response.timeout = 500
|
||||
headers = {}
|
||||
for key, val in req.httprequest.headers.iteritems():
|
||||
headers[key.lower()] = val
|
||||
|
@ -1023,7 +1018,6 @@ class Binary(openerpweb.Controller):
|
|||
|
||||
@openerpweb.httprequest
|
||||
def upload_attachment(self, req, callback, model, id, ufile=None):
|
||||
cherrypy.response.timeout = 500
|
||||
context = req.session.eval_context(req.context)
|
||||
Model = req.session.model('ir.attachment')
|
||||
try:
|
||||
|
|
|
@ -1,25 +1,22 @@
|
|||
#!/usr/bin/env python
|
||||
import optparse
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import cherrypy
|
||||
import cherrypy.lib.static
|
||||
import werkzeug.serving
|
||||
|
||||
optparser = optparse.OptionParser()
|
||||
optparser.add_option("-p", "--port", dest="socket_port", default=8002,
|
||||
help="listening port", type="int", metavar="NUMBER")
|
||||
optparser.add_option("-s", "--session-path", dest="tools.sessions.storage_path",
|
||||
default=os.path.join(tempfile.gettempdir(), "cpsessions"),
|
||||
optparser.add_option("-s", "--session-path", dest="session_storage",
|
||||
default=os.path.join(tempfile.gettempdir(), "oe-sessions"),
|
||||
help="directory used for session storage", metavar="DIR")
|
||||
optparser.add_option("--server-host", dest="openerp.server.host",
|
||||
optparser.add_option("--server-host", dest="server_host",
|
||||
default='127.0.0.1', help="OpenERP server hostname", metavar="HOST")
|
||||
optparser.add_option("--server-port", dest="openerp.server.port", default=8069,
|
||||
optparser.add_option("--server-port", dest="server_port", default=8069,
|
||||
help="OpenERP server port", type="int", metavar="NUMBER")
|
||||
optparser.add_option("--db-filter", dest="openerp.dbfilter", default='.*',
|
||||
optparser.add_option("--db-filter", dest="dbfilter", default='.*',
|
||||
help="Filter listed database", metavar="REGEXP")
|
||||
|
||||
path_root = os.path.dirname(os.path.abspath(__file__))
|
||||
|
@ -29,34 +26,14 @@ if path_addons not in sys.path:
|
|||
|
||||
import base
|
||||
|
||||
def main(options):
|
||||
# change the timezone of the program to the OpenERP server's assumed timezone
|
||||
os.environ["TZ"] = "UTC"
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
'server.socket_host': '0.0.0.0',
|
||||
'tools.sessions.on': True,
|
||||
'tools.sessions.storage_type': 'file',
|
||||
'tools.sessions.timeout': 60
|
||||
}
|
||||
|
||||
cherrypy.config.update(config=DEFAULT_CONFIG)
|
||||
if os.path.exists(os.path.join(path_root,'openerp-web.cfg')):
|
||||
cherrypy.config.update(os.path.join(path_root,'openerp-web.cfg'))
|
||||
if os.path.exists(os.path.expanduser('~/.openerp_webrc')):
|
||||
cherrypy.config.update(os.path.expanduser('~/.openerp_webrc'))
|
||||
cherrypy.config.update(options)
|
||||
|
||||
if not os.path.exists(cherrypy.config['tools.sessions.storage_path']):
|
||||
os.makedirs(cherrypy.config['tools.sessions.storage_path'], 0700)
|
||||
|
||||
return cherrypy.tree.mount(base.common.Root())
|
||||
|
||||
if __name__ == "__main__":
|
||||
(options, args) = optparser.parse_args(sys.argv[1:])
|
||||
o = dict((k, v) for k, v in vars(options).iteritems() if v is not None)
|
||||
options.serve_static = True
|
||||
|
||||
os.environ["TZ"] = "UTC"
|
||||
app = base.common.Root(options)
|
||||
|
||||
werkzeug.serving.run_simple(
|
||||
'0.0.0.0', options.socket_port, main(o),
|
||||
'0.0.0.0', options.socket_port, app,
|
||||
use_reloader=True, threaded=True)
|
||||
|
||||
|
|
Loading…
Reference in New Issue