From a05ff59e33a85dff062a00f5815a70bc0e0b84cb Mon Sep 17 00:00:00 2001 From: Christophe de Vienne Date: Thu, 27 Oct 2011 20:00:05 +0200 Subject: [PATCH] The decorators now wrap the exposed function so that children classes can expose the parent functions with different signatures --- wsme/controller.py | 46 +++++++++++++++++++++++++++++------ wsme/tests/test_controller.py | 7 +++--- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/wsme/controller.py b/wsme/controller.py index 8542239..f888b4a 100644 --- a/wsme/controller.py +++ b/wsme/controller.py @@ -72,6 +72,32 @@ class FunctionArgument(object): self.default = default +def funcproxy(func): + """ + A very simple wrapper for exposed function. + + It will carry the FunctionDefinition in place of the + decorared function so that a same function can be exposed + several times (for example a parent function can be exposed + in different ways in the children classes). + + The returned function also carry a ``_original_func`` attribute + so that it can be inspected if needed. + """ + def newfunc(*args, **kw): + return func(*args, **kw) + newfunc._is_wsme_funcproxy = True + newfunc._original_func = func + return newfunc + + +def isfuncproxy(func): + """ + Returns True if ``func`` is already a function proxy. + """ + return getattr(func, '_is_wsme_funcproxy', False) + + class FunctionDefinition(object): """ An api entry definition @@ -105,11 +131,12 @@ class FunctionDefinition(object): """ Returns the :class:`FunctionDefinition` of a method. """ - fd = getattr(func, '_wsme_definition', None) - if fd is None: + if not isfuncproxy(func): fd = FunctionDefinition(func) + func = funcproxy(func) func._wsme_definition = fd - return fd + + return func, func._wsme_definition def get_arg(self, name): """ @@ -157,7 +184,9 @@ class expose(object): register_type(return_type) def __call__(self, func): - fd = FunctionDefinition.get(func) + func, fd = FunctionDefinition.get(func) + if fd.return_type is not None: + raise ValueError("This function is already exposed") fd.return_type = self.return_type fd.extra_options = self.options return func @@ -170,7 +199,7 @@ class pexpose(object): register_type(return_type) def __call__(self, func): - fd = FunctionDefinition.get(func) + func, fd = FunctionDefinition.get(func) fd.return_type = self.return_type fd.protocol_specific = True fd.contenttype = self.contenttype @@ -194,8 +223,9 @@ class validate(object): self.param_types = param_types def __call__(self, func): - fd = FunctionDefinition.get(func) - args, varargs, keywords, defaults = inspect.getargspec(func) + func, fd = FunctionDefinition.get(func) + args, varargs, keywords, defaults = inspect.getargspec( + func._original_func) if args[0] == 'self': args = args[1:] for i, argname in enumerate(args): @@ -289,7 +319,7 @@ class WSRoot(object): raise exc.ClientSideError( u'The %s protocol was unable to extract a function ' u'path from the request' % protocol.name) - + context.func, context.funcdef = self._lookup_function(context.path) kw = protocol.read_arguments(context) diff --git a/wsme/tests/test_controller.py b/wsme/tests/test_controller.py index 50751de..3383907 100644 --- a/wsme/tests/test_controller.py +++ b/wsme/tests/test_controller.py @@ -63,9 +63,10 @@ def test_pexpose(): def ufunc(self): return u"

é

" - assert FunctionDefinition.get(Proto.func).return_type is None - assert FunctionDefinition.get(Proto.func).protocol_specific - assert FunctionDefinition.get(Proto.func).contenttype == "text/xml" + func, fd = FunctionDefinition.get(Proto.func) + assert fd.return_type is None + assert fd.protocol_specific + assert fd.contenttype == "text/xml" p = Proto() r = WSRoot()