From 17f19926980ddf0ebb79b8db438ca0ade5e5a86c Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 20 Feb 2014 12:50:16 +0100 Subject: [PATCH] [FIX] Correctly handle super() for decorated routes and keep the request type on all functions bzr revid: fme@openerp.com-20140220115016-axebl9ei9dvysko0 --- openerp/http.py | 82 +++++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/openerp/http.py b/openerp/http.py index c6075baf281..507afc748fd 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -130,12 +130,9 @@ class WebRequest(object): self.disable_db = False self.uid = None self.endpoint = None - self.func = None - self.func_arguments = {} self.auth_method = None self._cr_cm = None self._cr = None - self.func_request_type = None # set db/uid trackers - they're cleaned up at the WSGI # dispatching phase in openerp.service.wsgi_server.application if self.db: @@ -195,39 +192,36 @@ class WebRequest(object): self.disable_db = True self.uid = None - def set_handler(self, func, arguments, auth): + def set_handler(self, endpoint, arguments, auth): # is this needed ? arguments = dict((k, v) for k, v in arguments.iteritems() if not k.startswith("_ignored_")) - self.endpoint = func - # TODO: get rid of func_* - self.func = func - self.func_request_type = func.routing['type'] - self.func_arguments = arguments + endpoint.arguments = arguments + self.endpoint = endpoint self.auth_method = auth def _call_function(self, *args, **kwargs): request = self - if self.func_request_type != self._request_type: + if self.endpoint.routing['type'] != self._request_type: raise Exception("%s, %s: Function declared as capable of handling request of type '%s' but called with a request of type '%s'" \ - % (self.func, self.httprequest.path, self.func_request_type, self._request_type)) + % (self.endpoint.original, self.httprequest.path, self.endpoint.routing['type'], self._request_type)) - kwargs.update(self.func_arguments) + kwargs.update(self.endpoint.arguments) # Backward for 7.0 - if getattr(self.func.method, '_first_arg_is_req', False): + if self.endpoint.first_arg_is_req: args = (request,) + args # Correct exception handling and concurency retry @service_model.check def checked_call(___dbname, *a, **kw): - return self.func(*a, **kw) + return self.endpoint(*a, **kw) # FIXME: code and rollback management could be cleaned try: if self.db: return checked_call(self.db, *args, **kwargs) - return self.func(*args, **kwargs) + return self.endpoint(*args, **kwargs) except Exception: if self._cr: self._cr.rollback() @@ -274,17 +268,16 @@ def route(route=None, **kw): @functools.wraps(f) def response_wrap(*args, **kw): response = f(*args, **kw) - if request.endpoint.original == f: - if isinstance(response, Response) or request.func_request_type == 'json': - return response - elif isinstance(response, werkzeug.wrappers.BaseResponse): - response = Response.force_type(response) - response.set_default() - return response - elif isinstance(response, basestring): - return Response(response) - else: - _logger.warn(" returns an invalid response type for an http request" % (f.__module__, f.__name__)) + if isinstance(response, Response) or f.routing_type == 'json': + return response + elif isinstance(response, werkzeug.wrappers.BaseResponse): + response = Response.force_type(response) + response.set_default() + return response + elif isinstance(response, basestring): + return Response(response) + else: + _logger.warn(" returns an invalid response type for an http request" % (f.__module__, f.__name__)) return response response_wrap.routing = routing response_wrap.original_func = f @@ -460,14 +453,14 @@ class HttpRequest(WebRequest): def dispatch(self): # TODO: refactor this correctly. This is a quick fix for pos demo. - if request.httprequest.method == 'OPTIONS' and request.func and request.func.routing.get('cors'): + if request.httprequest.method == 'OPTIONS' and request.endpoint and request.endpoint.routing.get('cors'): response = Response(status=200) - response.headers.set('Access-Control-Allow-Origin', request.func.routing['cors']) + response.headers.set('Access-Control-Allow-Origin', request.endpoint.routing['cors']) methods = 'GET, POST' - if request.func_request_type == 'json': + if request.endpoint.routing['type'] == 'json': methods = 'POST' - elif request.func.routing.get('methods'): - methods = ', '.join(request.func.routing['methods']) + elif request.endpoint.routing.get('methods'): + methods = ', '.join(request.endpoint.routing['methods']) response.headers.set('Access-Control-Allow-Methods', methods) response.headers.set('Access-Control-Max-Age',60*60*24) response.headers.set('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept') @@ -566,6 +559,13 @@ class EndPoint(object): self.method = method self.original = getattr(method, 'original_func', method) self.routing = routing + self.arguments = {} + + @property + def first_arg_is_req(self): + # Backward for 7.0 + return getattr(self.method, '_first_arg_is_req', False) + def __call__(self, *args, **kw): return self.method(*args, **kw) @@ -588,9 +588,19 @@ def routing_map(modules, nodb_only, converters=None): if inspect.ismethod(mv) and hasattr(mv, 'routing'): routing = dict(type='http', auth='user', methods=None, routes=None) methods_done = list() + routing_type = None for claz in reversed(mv.im_class.mro()): fn = getattr(claz, mv.func_name, None) if fn and hasattr(fn, 'routing') and fn not in methods_done: + fn_type = fn.routing.get('type') + if not routing_type: + routing_type = fn_type + else: + if fn_type and routing_type != fn_type: + _logger.warn("Subclass re-defines with different type than original." + " Will use original type: %r", fn.__module__, fn.__name__, routing_type) + fn.routing['type'] = routing_type + fn.original_func.routing_type = routing_type methods_done.append(fn) routing.update(fn.routing) if not nodb_only or nodb_only == (routing['auth'] == "none"): @@ -1123,13 +1133,13 @@ class Root(object): response.set_cookie('session_id', httprequest.session.sid, max_age=90 * 24 * 60 * 60) # Support for Cross-Origin Resource Sharing - if request.func and 'cors' in request.func.routing: - response.headers.set('Access-Control-Allow-Origin', request.func.routing['cors']) + if request.endpoint and 'cors' in request.endpoint.routing: + response.headers.set('Access-Control-Allow-Origin', request.endpoint.routing['cors']) methods = 'GET, POST' - if request.func_request_type == 'json': + if request.endpoint.routing['type'] == 'json': methods = 'POST' - elif request.func.routing.get('methods'): - methods = ', '.join(request.func.routing['methods']) + elif request.endpoint.routing['methods']: + methods = ', '.join(request.endpoint.routing['methods']) response.headers.set('Access-Control-Allow-Methods', methods) return response