source: cows/trunk/cows/pylons/decorators.py @ 4228

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows/trunk/cows/pylons/decorators.py@4228
Revision 4228, 6.4 KB checked in by spascoe, 11 years ago (diff)

Changed print statements to loggging calls where appropriate.

Line 
1# Copyright (C) 2007 STFC & NERC (Science and Technology Facilities Council).
2# This software may be distributed under the terms of the
3# Q Public License, version 1.0 or later.
4# http://ndg.nerc.ac.uk/public_docs/QPublic_license.txt
5"""
6Decorators for annotating OWS server controllers with information used
7to populate the cows model.
8
9@author: Stephen Pascoe
10"""
11
12from cows.util import make_domain
13from cows.exceptions import * 
14import inspect
15
16def _wrap_sig(func, requiredArgs, optionalArgs, withVarkw=False, withVarargs=False):
17    """
18    Wrap a function in a function with a definied signature
19    (i.e. args, *args and **kwargs).  This function works around a
20    problem PEP-0262 is designed to address.  When wrapping functions using
21    decorators you loose the function signature (visible via
22    inspect.getargspec()).  This is a problem for Pylons because it uses inspection to
23    dispatch controller actions.
24
25    Not all signature information is retained in the wrapper.  Optional arguments are
26    supported but their default values are not visible (the wrapped method will handle
27    them as usual).
28
29    @param func: A function to be wrapped
30    @param requiredArgs: Required argument names
31    @param optionalArgs: Optional argument names
32    @param withVarargs: If True allow variable arguments
33    @param withVarkw: If True allow variable keyword arguments
34    @return: A function with the given argument signature which wraps func.
35
36    """
37
38    # Default acts as a singleton to mark optional arguments
39    class Default:
40        pass
41   
42    if withVarkw:
43        varkw = 'varkw_in'
44    else:
45        varkw = None
46    if withVarargs:
47        varargs = 'varargs_in'
48    else:
49        varargs = None
50
51    args = requiredArgs + optionalArgs
52    first_default = len(requiredArgs)
53
54    def formatarg(arg):
55        i = args.index(arg)
56        if i < first_default:
57            return arg
58        else:
59            return '%s=_wrap_default' % arg
60
61    def process(localVars):
62        args = [localVars[x] for x in requiredArgs]
63        args += localVars.get('varargs_in', [])
64        kwargs = localVars.get('varkw_in', {})
65        for arg in optionalArgs:
66            if localVars[arg] != Default:
67                kwargs[arg] = localVars[arg]
68        return args, kwargs
69
70    wrap_vars = dict(_wrap_func=func, _wrap_process=process, _wrap_default=Default)
71    wrap_sig = inspect.formatargspec(args, varargs, varkw,
72                                     formatarg=formatarg)
73
74    wrap_expr = """
75def %s%s:
76    args, kwargs = _wrap_process(locals())   
77    return _wrap_func(*args, **kwargs)""" % (func.__name__, wrap_sig)
78
79    exec wrap_expr in wrap_vars
80
81    return wrap_vars[func.__name__]
82
83
84def ows_operation(method):
85    """
86    A decorator which defines a method as a OWS operation.
87
88    The method is anotated with the attributes of the OWS protocol which are then
89    interogated during dispatch by OwsController to enforce OWS operation calling
90    behaviour.
91   
92    """
93
94    method._ows_name = method.__name__
95
96    return method
97
98#-----------------------------------------------------------------------------
99
100from unittest import TestCase
101
102class TestOperationDecorator(TestCase):
103    def setUp(self):
104        class Foo(object):
105            @ows_operation(['Bar'], ['Baz'])
106            def MyOp(self, bar, baz=1):
107                return bar+baz
108
109        self.foo = Foo()
110
111    def testOwsProtocol(self):
112        # Check OWS protocol is adhered to
113        assert self.foo.MyOp._ows_name == 'MyOp'
114        assert self.foo.MyOp._ows_required_parameters == ['Bar']
115        assert self.foo.MyOp._ows_optional_parameters == ['Baz']
116
117    def testCall(self):
118        assert self.foo.MyOp(2) == 3
119        assert self.foo.MyOp(bar=2) == 3
120        assert self.foo.MyOp(bar=2, baz=2) == 4
121
122    def testExtraArgs(self):
123        self.assertRaises(TypeError, lambda: self.foo.MyOp(1, 2, 3))
124        self.assertRaises(TypeError, lambda: self.foo.MyOp(1, 2, x=3))
125       
126
127
128class TestWrapSignature(TestCase):
129    def setUp(self):
130        def f(x, y, z='default', z2='default2', *args, **kwargs):
131            return dict(x=x, y=y, z=z, z2=z2, args=args, kwargs=kwargs)
132        self.wrap = _wrap_sig(f, ['x', 'y'], ['z', 'z2'], True, True)
133
134    def test1(self):
135        self.assertRaises(TypeError, lambda: self.wrap(1))
136
137    def test2(self):
138        d = self.wrap(1, 2)
139        self.assertEquals(d['x'], 1)
140        self.assertEquals(d['y'], 2)
141
142    def test3(self):
143        d = self.wrap(1,2)
144        self.assertEquals(d['z'], 'default')
145        self.assertEquals(d['z2'], 'default2')
146
147    def test4(self):
148        d = self.wrap(1, 2, 3)
149        self.assertEquals(d['z'], 3)
150        self.assertEquals(d['z2'], 'default2')
151
152    def test5(self):
153        d = self.wrap(1, 2, z2=3)
154        self.assertEquals(d['z'], 'default')
155        self.assertEquals(d['z2'], 3)
156
157    def test6(self):
158        d = self.wrap(*(1,2))
159        self.assertEquals(d['x'], 1)
160        self.assertEquals(d['y'], 2)
161
162    def test7(self):
163        d = self.wrap(1, 2, 3, 4)
164        self.assertEquals(d['x'], 1)
165        self.assertEquals(d['y'], 2)
166        self.assertEquals(d['z'], 3)
167        self.assertEquals(d['z2'], 4)
168
169    def test8(self):
170        d = self.wrap(1,2, **dict(z=3, w=4))
171        self.assertEquals(d['z'], 3)
172        self.assertEquals(d['kwargs']['w'],4)
173
174class TestWrapSignature2(TestWrapSignature):
175    def setUp(self):
176        """
177        Make a function that accepts anything but has the same return value as in
178        TestWrapSignature.
179        """
180        def f(*args, **kwargs):
181            d = {}
182
183            try:
184                d['x'] = args[0]
185            except IndexError:
186                d['x'] = kwargs['x']
187
188            try:
189                d['y'] = args[1]
190            except IndexError:
191                d['y'] = kwargs['y']
192
193            try:
194                d['z'] = args[2]
195            except IndexError:
196                try:
197                    d['z'] = kwargs['z']
198                except KeyError:
199                    d['z'] = 'default'
200
201            try:
202                d['z2'] = args[3]
203            except IndexError:
204                try:
205                    d['z2'] = kwargs['z2']
206                except KeyError:
207                    d['z2'] = 'default2'
208
209            for k in 'x', 'y', 'z', 'z2':
210                if k in kwargs:
211                    del kwargs[k]
212
213            d['args'] = args[4:]
214            d['kwargs'] = kwargs
215
216            return d
217
218        self.wrap = _wrap_sig(f, ['x', 'y'], ['z', 'z2'], True, True)
Note: See TracBrowser for help on using the repository browser.