[IMP] new routing thing

* fix nameerror on SessionExpired exception not being imported
* remove pointless RequestUID instantiation by single placeholder object
  - may be replaceable with a LocalProxy or something along those lines?
* rename default/nodb routing map
* make better use of werkzeug API
* move lazy routing_map instantiation to property in ir_http.find_handler
  - do we have some sort of lazy_property?

bzr revid: xmo@openerp.com-20131115100901-s3skmwv9d1jgk9y0
This commit is contained in:
Xavier Morel 2013-11-15 11:09:01 +01:00
parent 34c3beda2b
commit 321d4681e1
2 changed files with 42 additions and 48 deletions

View File

@ -9,12 +9,13 @@ import werkzeug.routing
import openerp import openerp
from openerp import http from openerp import http
from openerp.http import request from openerp.http import request
from openerp.osv import osv from openerp.osv import osv, orm
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
class RequestUID(object):
pass # FIXME: replace by proxy on request.uid?
_uid = object()
class ModelConverter(werkzeug.routing.BaseConverter): class ModelConverter(werkzeug.routing.BaseConverter):
@ -28,7 +29,7 @@ class ModelConverter(werkzeug.routing.BaseConverter):
# TODO: # TODO:
# - raise routing.ValidationError() if no browse record can be createdm # - raise routing.ValidationError() if no browse record can be createdm
# - support slug # - support slug
return request.registry[self.model].browse(request.cr, RequestUID(), int(value), context=request.context) return request.registry[self.model].browse(request.cr, _uid, int(value), context=request.context)
def to_url(self, value): def to_url(self, value):
return value.id return value.id
@ -45,10 +46,10 @@ class ModelsConverter(werkzeug.routing.BaseConverter):
# TODO: # TODO:
# - raise routing.ValidationError() if no browse record can be createdm # - raise routing.ValidationError() if no browse record can be createdm
# - support slug # - support slug
return request.registry[self.model].browse(request.cr, RequestUID(), [int(i) for i in value.split(',')], context=request.context) return request.registry[self.model].browse(request.cr, _uid, [int(i) for i in value.split(',')], context=request.context)
def to_url(self, value): def to_url(self, value):
return ",".join([i.id for i in value]) return ",".join(i.id for i in value)
class ir_http(osv.AbstractModel): class ir_http(osv.AbstractModel):
_name = 'ir.http' _name = 'ir.http'
@ -59,30 +60,16 @@ class ir_http(osv.AbstractModel):
return {'model': ModelConverter, 'models': ModelsConverter} return {'model': ModelConverter, 'models': ModelsConverter}
def _find_handler(self): def _find_handler(self):
# TODO move to __init__(self, registry, cr) return self.routing_map.bind_to_environ(request.httprequest.environ).match()
if not hasattr(self, 'routing_map'):
_logger.info("Generating routing map")
cr = request.cr
m = request.registry.get('ir.module.module')
ids = m.search(cr, openerp.SUPERUSER_ID, [('state', '=', 'installed'), ('name', '!=', 'web')])
installed = set(x['name'] for x in m.read(cr, 1, ids, ['name']))
mods = ['', "web"] + sorted(installed)
self.routing_map = http.routing_map(mods, False, converters=self._get_converters())
# fallback to non-db handlers
path = request.httprequest.path
urls = self.routing_map.bind_to_environ(request.httprequest.environ)
return urls.match(path)
def _auth_method_user(self): def _auth_method_user(self):
request.uid = request.session.uid request.uid = request.session.uid
if not request.uid: if not request.uid:
raise SessionExpiredException("Session expired") raise http.SessionExpiredException("Session expired")
def _auth_method_admin(self): def _auth_method_admin(self):
if not request.db: if not request.db:
raise SessionExpiredException("No valid database for request %s" % request.httprequest) raise http.SessionExpiredException("No valid database for request %s" % request.httprequest)
request.uid = openerp.SUPERUSER_ID request.uid = openerp.SUPERUSER_ID
def _auth_method_none(self): def _auth_method_none(self):
@ -94,9 +81,12 @@ class ir_http(osv.AbstractModel):
if request.session.uid: if request.session.uid:
try: try:
request.session.check_security() request.session.check_security()
except SessionExpiredException, e: # what if error in security.check()
# -> res_users.check()
# -> res_users.check_credentials()
except http.SessionExpiredException:
request.session.logout() request.session.logout()
raise SessionExpiredException("Session expired for request %s" % request.httprequest) raise http.SessionExpiredException("Session expired for request %s" % request.httprequest)
getattr(self, "_auth_method_%s" % auth_method)() getattr(self, "_auth_method_%s" % auth_method)()
return auth_method return auth_method
@ -124,27 +114,33 @@ class ir_http(osv.AbstractModel):
# post process arg to set uid on browse records # post process arg to set uid on browse records
for arg in arguments: for arg in arguments:
if isinstance(arg, openerp.osv.orm.browse_record) and isinstance(arg._uid, RequestUID): if isinstance(arg, orm.browse_record) and arg._uid is _uid:
arg._uid = request.uid arg._uid = request.uid
# set and execute handler # set and execute handler
try: try:
request.set_handler(func, arguments, auth_method) request.set_handler(func, arguments, auth_method)
result = request.dispatch() result = request.dispatch()
except werkzeug.exceptions.HTTPException, e: if isinstance(result, Exception):
fn = getattr(self, '_handle_%s' % (e.code,), None) raise result
if not fn:
fn = self._handle_500
return fn(e)
except Exception, e: except Exception, e:
return self._handle_500(e) fn = getattr(self, '_handle_%s' % getattr(e, 'code', 500),
self._handle_500)
if isinstance(result, werkzeug.exceptions.HTTPException): return fn(e)
fn = getattr(self, '_handle_%s' % (result.code,), None)
if not fn:
fn = self._handle_500
return fn(result)
return result return result
@property
def routing_map(self):
if not hasattr(self, '_routing_map'):
_logger.info("Generating routing map")
cr = request.cr
m = request.registry.get('ir.module.module')
ids = m.search(cr, openerp.SUPERUSER_ID, [('state', '=', 'installed'), ('name', '!=', 'web')], context=request.context)
installed = set(x['name'] for x in m.read(cr, 1, ids, ['name'], context=request.context))
mods = ['', "web"] + sorted(installed)
self._routing_map = http.routing_map(mods, False, converters=self._get_converters())
return self._routing_map
# vim:et: # vim:et:

View File

@ -4,6 +4,7 @@
#---------------------------------------------------------- #----------------------------------------------------------
import ast import ast
import cgi import cgi
import collections
import contextlib import contextlib
import errno import errno
import functools import functools
@ -467,7 +468,7 @@ def set_request(req):
#---------------------------------------------------------- #----------------------------------------------------------
addons_module = {} addons_module = {}
addons_manifest = {} addons_manifest = {}
controllers_per_module = {} controllers_per_module = collections.defaultdict(list)
class ControllerType(type): class ControllerType(type):
def __init__(cls, name, bases, attrs): def __init__(cls, name, bases, attrs):
@ -492,7 +493,7 @@ class ControllerType(type):
# but we only store controllers directly inheriting from Controller # but we only store controllers directly inheriting from Controller
if not "Controller" in globals() or not Controller in bases: if not "Controller" in globals() or not Controller in bases:
return return
controllers_per_module.setdefault(module, []).append(name_class) controllers_per_module[module].append(name_class)
class Controller(object): class Controller(object):
__metaclass__ = ControllerType __metaclass__ = ControllerType
@ -502,9 +503,8 @@ def routing_map(modules, nodb_only, converters=None):
for module in modules: for module in modules:
if module not in controllers_per_module: if module not in controllers_per_module:
continue continue
for v in controllers_per_module[module]:
cls = v[1]
for _, cls in controllers_per_module[module]:
subclasses = cls.__subclasses__() subclasses = cls.__subclasses__()
subclasses = [c for c in subclasses if c.__module__.startswith('openerp.addons.') and c.__module__.split(".")[2] in modules] subclasses = [c for c in subclasses if c.__module__.startswith('openerp.addons.') and c.__module__.split(".")[2] in modules]
if subclasses: if subclasses:
@ -867,7 +867,7 @@ class Root(object):
self.load_addons() self.load_addons()
_logger.info("Generating nondb routing") _logger.info("Generating nondb routing")
self.routing_map = routing_map(['', "web"], True) self.nodb_routing_map = routing_map(['', "web"], True)
def __call__(self, environ, start_response): def __call__(self, environ, start_response):
""" Handle a WSGI request """ Handle a WSGI request
@ -938,8 +938,7 @@ class Root(object):
def get_response(self, httprequest, result, explicit_session): def get_response(self, httprequest, result, explicit_session):
if isinstance(result, basestring): if isinstance(result, basestring):
headers=[('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', len(result))] response = werkzeug.wrappers.Response(result, mimetype='text/html')
response = werkzeug.wrappers.Response(result, headers=headers)
else: else:
response = result response = result
@ -979,8 +978,7 @@ class Root(object):
openerp.modules.registry.RegistryManager.signal_caches_change(db) openerp.modules.registry.RegistryManager.signal_caches_change(db)
else: else:
# fallback to non-db handlers # fallback to non-db handlers
urls = self.routing_map.bind_to_environ(request.httprequest.environ) func, arguments = self.nodb_routing_map.bind_to_environ(request.httprequest.environ).match()
func, arguments = urls.match(request.httprequest.path)
request.set_handler(func, arguments, "none") request.set_handler(func, arguments, "none")
result = request.dispatch() result = request.dispatch()
response = self.get_response(httprequest, result, explicit_session) response = self.get_response(httprequest, result, explicit_session)
@ -1027,7 +1025,7 @@ def db_monodb(httprequest=None):
return None return None
#---------------------------------------------------------- #----------------------------------------------------------
# RPC controlller # RPC controller
#---------------------------------------------------------- #----------------------------------------------------------
class CommonController(Controller): class CommonController(Controller):