|
1 from werkzeug.wrappers import Request, Response |
|
2 from werkzeug.exceptions import ( |
|
3 HTTPException, |
|
4 BadRequest, # 400 |
|
5 NotFound, # 404 |
|
6 ) |
|
7 from werkzeug.utils import redirect |
|
8 |
|
9 class Application: |
|
10 URLS = None |
|
11 |
|
12 def __init__ (self, urls=None): |
|
13 """ |
|
14 urls - werkzeug.routing.Map -> Handler |
|
15 """ |
|
16 |
|
17 if not urls: |
|
18 urls = self.URLS |
|
19 |
|
20 if not urls: |
|
21 raise ValueError("Required URLS/urls=...") |
|
22 |
|
23 self.urls = urls |
|
24 |
|
25 def respond (self, request): |
|
26 """ |
|
27 Lookup Request -> Handler, params -> Response |
|
28 """ |
|
29 |
|
30 # bind to request |
|
31 urls = self.urls.bind_to_environ(request) |
|
32 |
|
33 # lookup |
|
34 handler, params = urls.match() |
|
35 |
|
36 # handler instance |
|
37 handler = handler(self, request, urls) |
|
38 |
|
39 try : |
|
40 handler.init() |
|
41 |
|
42 # apply |
|
43 return handler.respond(**params) |
|
44 |
|
45 finally : |
|
46 handler.cleanup() |
|
47 |
|
48 @Request.application |
|
49 def __call__ (self, request) : |
|
50 """ |
|
51 WSGI entry point, werkzeug Request -> Response |
|
52 """ |
|
53 |
|
54 try : |
|
55 return self.respond(request) |
|
56 |
|
57 except HTTPException as ex : |
|
58 return ex |
|
59 |
|
60 class Handler (object) : |
|
61 """ |
|
62 Per-Request controller/view, containing the request context and generating the response. |
|
63 """ |
|
64 |
|
65 # werkzeug defaults to UTF-8 |
|
66 MIMETYPE = 'text/html' |
|
67 |
|
68 def __init__ (self, app, request, urls) : |
|
69 """ |
|
70 app - wsgi.Application |
|
71 request - werkzeug.Request |
|
72 urls - werkzeug.routing.Map.bind_to_environ() |
|
73 """ |
|
74 |
|
75 self.app = app |
|
76 self.request = request |
|
77 self.urls = urls |
|
78 |
|
79 def url (self, handler=None, **params) : |
|
80 """ |
|
81 Return an URL for given endpoint, with parameters, |
|
82 """ |
|
83 |
|
84 if not handler : |
|
85 handler = self.__class__ |
|
86 |
|
87 return self.urls.build(handler, params) |
|
88 |
|
89 ## processing stages |
|
90 def init (self) : |
|
91 """ |
|
92 Initialize on request start. |
|
93 """ |
|
94 |
|
95 pass |
|
96 |
|
97 def process (self, **params) : |
|
98 """ |
|
99 Process request args to build internal request state. |
|
100 |
|
101 May optionally return a Response, to e.g. redirect after POST. |
|
102 """ |
|
103 |
|
104 pass |
|
105 |
|
106 def render (self) : |
|
107 """ |
|
108 Render response. |
|
109 """ |
|
110 |
|
111 raise NotImplementedError() |
|
112 |
|
113 def mimetype (self): |
|
114 return self.MIMETYPE |
|
115 |
|
116 def status (self): |
|
117 return 200 |
|
118 |
|
119 def respond (self, **params) : |
|
120 """ |
|
121 Generate a response, or raise an HTTPException |
|
122 """ |
|
123 |
|
124 # returning e.g. redirect? |
|
125 response = self.process(**params) |
|
126 |
|
127 if response : |
|
128 return response |
|
129 |
|
130 return Response(self.render_response(), mimetype=self.mimetype(), status=self.status()) |
|
131 |
|
132 def cleanup (self) : |
|
133 """ |
|
134 After request processing. Do not fail :) |
|
135 """ |
|
136 |
|
137 pass |
|
138 |