Big refactoring to prepare even more refactoring

bzr revid: nicolas.vanhoren@openerp.com-20130619161627-543e0brldclvvw98
This commit is contained in:
niv-openerp 2013-06-19 18:16:27 +02:00
parent b3a99c860f
commit 69ae9cc0b5
3 changed files with 267 additions and 243 deletions

File diff suppressed because it is too large Load Diff

View File

@ -86,10 +86,8 @@ TESTING = Template(u"""<!DOCTYPE html>
""", default_filters=['h']) """, default_filters=['h'])
class TestRunnerController(http.Controller): class TestRunnerController(http.Controller):
_cp_path = '/web/tests'
@http.httprequest @http.route('/web/tests', type='http', authentication="nodb")
@http.nodb
def index(self, req, mod=None, **kwargs): def index(self, req, mod=None, **kwargs):
ms = module.get_modules() ms = module.get_modules()
manifests = dict( manifests = dict(

View File

@ -88,12 +88,12 @@ class WebRequest(object):
.. attribute:: db .. attribute:: db
``str``, the name of the database linked to the current request. Can be ``None`` ``str``, the name of the database linked to the current request. Can be ``None``
if the current request uses the @nodb decorator. if the current request uses the ``nodb`` authentication.
.. attribute:: uid .. attribute:: uid
``int``, the id of the user related to the current request. Can be ``None`` ``int``, the id of the user related to the current request. Can be ``None``
if the current request uses the @nodb or the @noauth decorator. if the current request uses the ``nodb`` or the ``noauth`` authenticatoin.
""" """
def __init__(self, httprequest): def __init__(self, httprequest):
self.httprequest = httprequest self.httprequest = httprequest
@ -171,14 +171,14 @@ class WebRequest(object):
def registry(self): def registry(self):
""" """
The registry to the database linked to this request. Can be ``None`` if the current request uses the The registry to the database linked to this request. Can be ``None`` if the current request uses the
@nodb decorator. ``nodb'' authentication.
""" """
return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None
@property @property
def cr(self): def cr(self):
""" """
The cursor initialized for the current method call. If the current request uses the @nodb decorator The cursor initialized for the current method call. If the current request uses the ``nodb`` authentication
trying to access this property will raise an exception. trying to access this property will raise an exception.
""" """
# some magic to lazy create the cr # some magic to lazy create the cr
@ -211,22 +211,43 @@ class WebRequest(object):
self.db = None self.db = None
self.uid = None self.uid = None
def route(route, type="http", authentication="auth"):
def noauth(f):
""" """
Decorator marking the decorated method as being a handler for requests. The method must be part of a subclass
of ``Controller``.
Decorator to put on a controller method to inform it does not require a user to be logged. When this decorator Decorator to put on a controller method to inform it does not require a user to be logged. When this decorator
is used, ``request.uid`` will be ``None``. The request will still try to detect the database and an exception is used, ``request.uid`` will be ``None``. The request will still try to detect the database and an exception
will be launched if there is no way to guess it. will be launched if there is no way to guess it.
:param route: string or array. The route part that will determine which http requests will match the decorated
method. Can be a single string or an array of strings. See werkzeug's routing documentation for the format of
route expression ( http://werkzeug.pocoo.org/docs/routing/ ).
:param type: The type of request, can be ``'http'`` or ``'json'``.
:param authentication: The type of authentication method, can on of the following:
* ``auth``: The user must be authenticated.
* ``noauth``: There is no need for the user to be authenticated but there must be a way to find the current
database.
* ``nodb``: The method is always active, even if there is no database. Mainly used by the framework and
authentication modules.
""" """
def decorator(f):
if isinstance(route, list):
f.routes = route
else:
f.routes = [route]
f.exposed = type
if getattr(f, "auth", None) is None:
f.auth = authentication
return f
return decorator
def noauth(f):
f.auth = "noauth" f.auth = "noauth"
return f return f
def nodb(f): def nodb(f):
"""
Decorator to put on a controller method to inform it does not require authentication nor any link to a database.
When this decorator is used, ``request.uid`` and ``request.db`` will be ``None``. Trying to use ``request.cr``
will launch an exception.
"""
f.auth = "nodb" f.auth = "nodb"
return f return f
@ -398,8 +419,11 @@ def jsonrequest(f):
the ``session_id``, ``context`` and ``debug`` keys (which are stripped out the ``session_id``, ``context`` and ``debug`` keys (which are stripped out
beforehand) beforehand)
""" """
f.exposed = 'json' f.combine = True
return f base = f.__name__
if f.__name__ == "index":
base = ""
return route([base, os.path.join(base, "<path:path>")], type="json", authentication="auth")(f)
class HttpRequest(WebRequest): class HttpRequest(WebRequest):
""" Regular GET/POST request """ Regular GET/POST request
@ -478,8 +502,11 @@ def httprequest(f):
merged in the same dictionary), apart from the ``session_id``, ``context`` merged in the same dictionary), apart from the ``session_id``, ``context``
and ``debug`` keys (which are stripped out beforehand) and ``debug`` keys (which are stripped out beforehand)
""" """
f.exposed = 'http' f.combine = True
return f base = f.__name__
if f.__name__ == "index":
base = ""
return route([base, os.path.join(base, "<path:path>")], type="http", authentication="auth")(f)
#---------------------------------------------------------- #----------------------------------------------------------
# Local storage of requests # Local storage of requests
@ -526,8 +553,7 @@ class ControllerType(type):
# store the controller in the controllers list # store the controller in the controllers list
name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls) name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls)
class_path = name_class[0].split(".") class_path = name_class[0].split(".")
path = attrs.get('_cp_path') if not class_path[:2] == ["openerp", "addons"]:
if not path or not class_path[:2] == ["openerp", "addons"]:
return return
module = class_path[2] module = class_path[2]
controllers_per_module.setdefault(module, []).append(name_class) controllers_per_module.setdefault(module, []).append(name_class)
@ -535,13 +561,13 @@ class ControllerType(type):
class Controller(object): class Controller(object):
__metaclass__ = ControllerType __metaclass__ = ControllerType
def __new__(cls, *args, **kwargs): """def __new__(cls, *args, **kwargs):
subclasses = [c for c in cls.__subclasses__() if c._cp_path == cls._cp_path] subclasses = [c for c in cls.__subclasses__() if getattr(c, "_cp_path", None) == getattr(cls, "_cp_path", None)]
if subclasses: if subclasses:
name = "%s (extended by %s)" % (cls.__name__, ', '.join(sub.__name__ for sub in subclasses)) name = "%s (extended by %s)" % (cls.__name__, ', '.join(sub.__name__ for sub in subclasses))
cls = type(name, tuple(reversed(subclasses)), {}) cls = type(name, tuple(reversed(subclasses)), {})
return object.__new__(cls) return object.__new__(cls)"""
def get_wrapped_method(self, name): def get_wrapped_method(self, name):
if name in self.__class__._methods_wrapper: if name in self.__class__._methods_wrapper:
@ -791,13 +817,13 @@ class Root(object):
for mk, mv in members: for mk, mv in members:
if inspect.ismethod(mv) and getattr(mv, 'exposed', False) and getattr(mv, 'auth', None) == "nodb": if inspect.ismethod(mv) and getattr(mv, 'exposed', False) and getattr(mv, 'auth', None) == "nodb":
o = v[1]() o = v[1]()
if mk == "index":
url = o._cp_path
else:
url = os.path.join(o._cp_path, mk)
function = (o.get_wrapped_method(mk), mv) function = (o.get_wrapped_method(mk), mv)
routing_map.add(routing.Rule(url, endpoint=function)) for url in mv.routes:
url = os.path.join(url, "<path:path>") if getattr(mv, "combine", False):
url = os.path.join(o._cp_path, url)
if url.endswith("/") and len(url) > 1:
url = url[: -1]
print "<<<<<<<<<<<<<<<< nodb", url
routing_map.add(routing.Rule(url, endpoint=function)) routing_map.add(routing.Rule(url, endpoint=function))
if not db: if not db:
@ -817,13 +843,13 @@ class Root(object):
members = inspect.getmembers(o) members = inspect.getmembers(o)
for mk, mv in members: for mk, mv in members:
if inspect.ismethod(mv) and getattr(mv, 'exposed', False) and getattr(mv, 'auth', None) != "nodb": if inspect.ismethod(mv) and getattr(mv, 'exposed', False) and getattr(mv, 'auth', None) != "nodb":
if mk == "index":
url = o._cp_path
else:
url = os.path.join(o._cp_path, mk)
function = (o.get_wrapped_method(mk), mv) function = (o.get_wrapped_method(mk), mv)
routing_map.add(routing.Rule(url, endpoint=function)) for url in mv.routes:
url = os.path.join(url, "<path:path>") if getattr(mv, "combine", False):
url = os.path.join(o._cp_path, url)
if url.endswith("/") and len(url) > 1:
url = url[: -1]
print "<<<<<<<<<<<<<<<< db", url
routing_map.add(routing.Rule(url, endpoint=function)) routing_map.add(routing.Rule(url, endpoint=function))
return routing_map return routing_map