1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 r"""Create a WSGI application providing a web API.
21
22 """
23 __docformat__ = "restructuredtext en"
24
25 import sys
26
27 from decorators import jsonreturning, _get_props
28 from logging import StdoutLogger
29 from validation import ValidationError
30 from wsgisupport import Request, \
31 HTTPError, \
32 HTTPNotFound, \
33 HTTPServerError, \
34 WSGIResponse, \
35 Response
36
38 """
39 """
40 response = Response(u"Validation Error: " + err.message)
41 response.status = 400
42 return response
43
49 """Make a web application for a given set of URLs.
50
51 - `urls` is a dict of urls to support: keys are url components, values are
52 either sub dictionaries, or callables.
53
54 - `logger` is a callable which returns a Logger. When the application
55 object returned is instantiated, it will call this callable, and use the
56 returned object for logging.
57
58 FIXME - document the other parameters to this function.
59
60 """
61 if logger is None:
62 logger = StdoutLogger
63
64 class Application(object):
65 """WSGI application wrapping the search server.
66
67 """
68 def __init__(self):
69 self.logger = logger()
70
71 def __call__(self, environ, start_response):
72 logstart = self.logger.request_start(environ)
73 try:
74 logged, request, response = \
75 self._do_call(environ, start_response, logstart)
76 except Exception, e:
77
78
79 self.logger.request_failed(environ, logstart, sys.exc_info())
80 return HTTPServerError(str(e))
81 else:
82 if not logged:
83 self.logger.request_end(environ, logstart,
84 request, response)
85 return response
86
87 def _do_call(self, environ, start_response, logstart):
88 request = Request(environ)
89 try:
90 handlers = urls
91 handler = None
92 for i in xrange(0, len(request.path_components)):
93 handler = handlers.get(request.path_components[i], None)
94 if handler is None:
95 break
96 if hasattr(handler, '__call__'):
97 break
98 handlers = handler
99 if handler is None:
100 raise HTTPNotFound(request.path)
101 if not hasattr(handler, '__call__'):
102 raise HTTPNotFound(request.path)
103
104
105 handler_props = _get_props(handler)
106 request._set_handler_props(handler_props)
107
108
109
110 tail_components = request.path_components[i + 1:]
111 if len(tail_components) != 0:
112
113
114 if handler_props is None or \
115 handler_props.get('pathinfo_allow', None) is None:
116 raise HTTPNotFound(request.path)
117
118
119 request._set_pathinfo(tail_components)
120
121
122 response = handler(request)
123
124
125
126 if isinstance(response, basestring):
127 response = Response(body=response)
128 assert isinstance(response, Response)
129
130 return False, request, WSGIResponse(start_response, response)
131 except ValidationError, e:
132 response = validation_error_handler(e)
133 return False, request, WSGIResponse(start_response, response)
134 except HTTPError, e:
135 return False, request, WSGIResponse(start_response, e)
136 except Exception, e:
137
138 self.logger.request_failed(environ, logstart, sys.exc_info())
139 return True, request, WSGIResponse(start_response, HTTPServerError(str(e)))
140
141 if autodoc:
142 components = autodoc.split('/')
143 suburls = urls
144 for component in components[:-1]:
145 suburls = suburls.setdefault(component, {})
146 from autodoc import make_doc
147 suburls[components[-1]] = make_doc(urls)
148
149 return Application
150
152 """Make a server for an application.
153
154 This uses CherryPy's standalone WSGI server. The first argument is the
155 WSGI application to run; all subsequent arguments are passed directly to
156 the server. The CherryPyWSGIServer is accessible as
157 wsgiwebapi.cpwsgiserver: see the documentation in that module for calling
158 details.
159
160 Note that you will always need to set the bind_addr parameter; this is a
161 (host, port) tuple for TCP sockets, or a filename for UNIX sockets. The
162 host part may be set to '0.0.0.0' to listen on all active IPv4 interfaces
163 (or similarly, '::' to listen on all active IPv6 interfaces).
164
165 """
166
167 import cpwsgiserver
168 server = cpwsgiserver.CherryPyWSGIServer(bind_addr, app, *args, **kwargs)
169 return server
170
171
172