1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 """
18 Application decorators
19 ======================
20
21 This module implements various application decorators.
22 """
23 __author__ = u"Andr\xe9 Malo"
24 __docformat__ = "restructuredtext en"
25
26 from wtf import util as _util
27 from wtf.app import response as _response
28
29
31 """
32 General purpose base request decorator
33
34 Implement the `decorate` method in order to add the action.
35 If the response is finised before calling the decorated function,
36 raise `response.Done`.
37 """
38
46
47 - def decorate(self, request, response, args, kwargs):
48 """
49 Decorating action has to be implemented here.
50
51 :Parameters:
52 - `request`: Request object
53 - `response`: Response object
54 - `args`: Full argument tuple for the decorated function
55 - `kwargs`: Full keyword dict for the decorated function
56
57 :Types:
58 - `request`: `wtf.app.request.Request`
59 - `response`: `wtf.app.response.Response`
60 - `args`: ``tuple``
61 - `kwargs`: ``dict``
62
63 :return: The return value of the decorated function (maybe modified
64 or replaced by the decoator)
65 :rtype: any
66 """
67 raise NotImplementedError()
68
69
71 """
72 Decorator which ensures certain HTTP methods
73
74 :IVariables:
75 - `methods`: The method set to ensure (``set(['method', ...])``)
76 - `options`: Should the decorator handle OPTIONS requests?
77 - `h2g`: automatically transform HEAD to GET methods?
78
79 :Types:
80 - `methods`: ``set``
81 - `options`: ``bool``
82 - `h2g`: ``bool``
83 """
84
86 """
87 Initialization
88
89 :Parameters:
90 - `methods`: method list to ensure (``('method', ...)``)
91 - `kwargs`: Behaviour options. The following ones are recognized:
92 ``bool(handle_options)``: Should the decorator handle OPTIONS
93 requests (defaults to ``True``); ``bool(head_to_get)``:
94 automatically transform HEAD to GET methods (defaults to ``True``)?
95
96 :Types:
97 - `methods`: ``tuple``
98 - `kwargs`: ``dict``
99
100 :Exceptions:
101 - `TypeError`: Unrecognized keyword arguments presented
102 """
103 handle_options = bool(kwargs.pop('handle_options', True))
104 head_to_get = bool(kwargs.pop('head_to_get', True))
105 if kwargs:
106 raise TypeError("Unrecognized keyword arguments")
107
108 methods = set(methods)
109 if not methods:
110 methods += set(['GET', 'HEAD'])
111 elif 'GET' in methods:
112 methods.add('HEAD')
113 if handle_options:
114 methods.add('OPTIONS')
115 self.methods = methods
116 self.options = handle_options
117 self.h2g = head_to_get
118
120 """
121 Decorate the callable
122
123 :Parameters:
124 - `func`: The callable to decorate
125
126 :Types:
127 - `func`: ``callable``
128
129 :return: The decorated callable
130 :rtype: ``callable``
131 """
132 class MethodDecorator(RequestDecorator):
133 """
134 Actual Method checking decorator
135
136 :IVariables:
137 - `_config`: Config tuple, containing the method set, option
138 handling and head handling flags
139 (``(set(['method', ...]), bool, bool)``)
140
141 :Types:
142 - `_config`: ``tuple``
143 """
144
145 def __init__(self, method, func):
146 """
147 Initialization
148
149 :Parameters:
150 - `method`: `Method` instance
151 - `func`: decorated callable
152
153 :Types:
154 - `method`: `Method`
155 - `func`: ``callable``
156 """
157 super(MethodDecorator, self).__init__(func)
158 self._config = method.methods, method.options, method.h2g
159
160 def decorate(self, request, response, args, kwargs):
161 """ Handle method filtering and OPTION requests. """
162 method, (methods, options, h2g) = request.method, self._config
163
164 opt = options and method == 'OPTIONS'
165 if opt or method not in methods:
166 if opt:
167 response.headers.set('Allow', ', '.join(methods))
168 raise _response.Done(request)
169 response.raise_error(405, allowed=methods)
170 if h2g and method == 'HEAD':
171 request.env['REQUEST_METHOD'] = 'GET'
172
173 return self._func(*args, **kwargs)
174 return MethodDecorator(self, func)
175