root/galaxy-central/eggs/Paste-1.6-py2.6.egg/paste/errordocument.py

リビジョン 3, 13.1 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1# (c) 2005-2006 James Gardner <james@pythonweb.org>
2# This module is part of the Python Paste Project and is released under
3# the MIT License: http://www.opensource.org/licenses/mit-license.php
4"""
5Middleware to display error documents for certain status codes
6
7The middleware in this module can be used to intercept responses with
8specified status codes and internally forward the request to an appropriate
9URL where the content can be displayed to the user as an error document.
10"""
11
12import warnings
13from urlparse import urlparse
14from paste.recursive import ForwardRequestException, RecursiveMiddleware
15from paste.util import converters
16from paste.response import replace_header
17
18def forward(app, codes):
19    """
20    Intercepts a response with a particular status code and returns the
21    content from a specified URL instead.
22   
23    The arguments are:
24   
25    ``app``
26        The WSGI application or middleware chain.
27
28    ``codes``
29        A dictionary of integer status codes and the URL to be displayed
30        if the response uses that code.
31       
32    For example, you might want to create a static file to display a
33    "File Not Found" message at the URL ``/error404.html`` and then use
34    ``forward`` middleware to catch all 404 status codes and display the page
35    you created. In this example ``app`` is your exisiting WSGI
36    applicaiton::
37
38        from paste.errordocument import forward
39        app = forward(app, codes={404:'/error404.html'})
40
41    """
42    for code in codes:
43        if not isinstance(code, int):
44            raise TypeError('All status codes should be type int. '
45                '%s is not valid'%repr(code))
46               
47    def error_codes_mapper(code, message, environ, global_conf, codes):
48        if codes.has_key(code):
49            return codes[code]
50        else:
51            return None
52
53    #return _StatusBasedRedirect(app, error_codes_mapper, codes=codes)
54    return RecursiveMiddleware(
55        StatusBasedForward(
56            app,
57            error_codes_mapper,
58            codes=codes,
59        )
60    )
61
62class StatusKeeper(object):
63    def __init__(self, app, status, url, headers):
64        self.app = app
65        self.status = status
66        self.url = url
67        self.headers = headers
68
69    def __call__(self, environ, start_response):
70        def keep_status_start_response(status, headers, exc_info=None):
71            for header, value in headers:
72                if header.lower() == 'set-cookie':
73                    self.headers.append((header, value))
74                else:
75                    replace_header(self.headers, header, value)
76            return start_response(self.status, self.headers, exc_info)
77        parts = self.url.split('?')
78        environ['PATH_INFO'] = parts[0]
79        if len(parts) > 1:
80            environ['QUERY_STRING'] = parts[1]
81        else:
82            environ['QUERY_STRING'] = ''
83        #raise Exception(self.url, self.status)
84        return self.app(environ, keep_status_start_response)
85       
86class StatusBasedForward(object):
87    """
88    Middleware that lets you test a response against a custom mapper object to
89    programatically determine whether to internally forward to another URL and
90    if so, which URL to forward to.
91   
92    If you don't need the full power of this middleware you might choose to use
93    the simpler ``forward`` middleware instead.
94
95    The arguments are:
96   
97    ``app``
98        The WSGI application or middleware chain.
99       
100    ``mapper``
101        A callable that takes a status code as the
102        first parameter, a message as the second, and accepts optional environ,
103        global_conf and named argments afterwards. It should return a
104        URL to forward to or ``None`` if the code is not to be intercepted.
105
106    ``global_conf``
107        Optional default configuration from your config file. If ``debug`` is
108        set to ``true`` a message will be written to ``wsgi.errors`` on each
109        internal forward stating the URL forwarded to.
110   
111    ``**params``
112        Optional, any other configuration and extra arguments you wish to
113        pass which will in turn be passed back to the custom mapper object.
114
115    Here is an example where a ``404 File Not Found`` status response would be
116    redirected to the URL ``/error?code=404&message=File%20Not%20Found``. This
117    could be useful for passing the status code and message into another
118    application to display an error document:
119   
120    .. code-block:: Python
121   
122        from paste.errordocument import StatusBasedForward
123        from paste.recursive import RecursiveMiddleware
124        from urllib import urlencode
125       
126        def error_mapper(code, message, environ, global_conf, kw)
127            if code in [404, 500]:
128                params = urlencode({'message':message, 'code':code})
129                url = '/error?'%(params)
130                return url
131            else:
132                return None
133   
134        app = RecursiveMiddleware(
135            StatusBasedForward(app, mapper=error_mapper),
136        )
137
138    """
139
140    def __init__(self, app, mapper, global_conf=None, **params):
141        if global_conf is None:
142            global_conf = {}
143        # @@: global_conf shouldn't really come in here, only in a
144        # separate make_status_based_forward function
145        if global_conf:
146            self.debug = converters.asbool(global_conf.get('debug', False))
147        else:
148            self.debug = False
149        self.application = app
150        self.mapper = mapper
151        self.global_conf = global_conf
152        self.params = params
153
154    def __call__(self, environ, start_response):
155        url = []
156       
157        def change_response(status, headers, exc_info=None):
158            status_code = status.split(' ')
159            try:
160                code = int(status_code[0])
161            except (ValueError, TypeError):
162                raise Exception(
163                    'StatusBasedForward middleware '
164                    'received an invalid status code %s'%repr(status_code[0])
165                )
166            message = ' '.join(status_code[1:])
167            new_url = self.mapper(
168                code,
169                message,
170                environ,
171                self.global_conf,
172                **self.params
173            )
174            if not (new_url == None or isinstance(new_url, str)):
175                raise TypeError(
176                    'Expected the url to internally '
177                    'redirect to in the StatusBasedForward mapper'
178                    'to be a string or None, not %s'%repr(new_url)
179                )
180            if new_url:
181                url.append([new_url, status, headers])
182            else:
183                return start_response(status, headers, exc_info)
184
185        app_iter = self.application(environ, change_response)
186        if url:
187            if hasattr(app_iter, 'close'):
188                app_iter.close()
189
190            def factory(app):
191                return StatusKeeper(app, status=url[0][1], url=url[0][0],
192                                    headers=url[0][2])
193            raise ForwardRequestException(factory=factory)
194        else:
195            return app_iter
196
197def make_errordocument(app, global_conf, **kw):
198    """
199    Paste Deploy entry point to create a error document wrapper.
200   
201    Use like::
202
203        [filter-app:main]
204        use = egg:Paste#errordocument
205        next = real-app
206        500 = /lib/msg/500.html
207        404 = /lib/msg/404.html
208    """
209    map = {}
210    for status, redir_loc in kw.items():
211        try:
212            status = int(status)
213        except ValueError:
214            raise ValueError('Bad status code: %r' % status)
215        map[status] = redir_loc
216    forwarder = forward(app, map)
217    return forwarder
218
219__pudge_all__ = [
220    'forward',
221    'make_errordocument',
222    'empty_error',
223    'make_empty_error',
224    'StatusBasedForward',
225]
226
227
228###############################################################################
229## Deprecated
230###############################################################################
231
232def custom_forward(app, mapper, global_conf=None, **kw):
233    """
234    Deprectated; use StatusBasedForward instead.
235    """
236    warnings.warn(
237        "errordocuments.custom_forward has been deprecated; please "
238        "use errordocuments.StatusBasedForward",
239        DeprecationWarning, 2)
240    if global_conf is None:
241        global_conf = {}
242    return _StatusBasedRedirect(app, mapper, global_conf, **kw)
243
244class _StatusBasedRedirect(object):
245    """
246    Deprectated; use StatusBasedForward instead.
247    """
248    def __init__(self, app, mapper, global_conf=None, **kw):
249
250        warnings.warn(
251            "errordocuments._StatusBasedRedirect has been deprecated; please "
252            "use errordocuments.StatusBasedForward",
253            DeprecationWarning, 2)
254
255        if global_conf is None:
256            global_conf = {}
257        self.application = app
258        self.mapper = mapper
259        self.global_conf = global_conf
260        self.kw = kw
261        self.fallback_template = """
262            <html>
263            <head>
264            <title>Error %(code)s</title>
265            </html>
266            <body>
267            <h1>Error %(code)s</h1>
268            <p>%(message)s</p>
269            <hr>
270            <p>
271                Additionally an error occurred trying to produce an
272                error document.  A description of the error was logged
273                to <tt>wsgi.errors</tt>.
274            </p>
275            </body>
276            </html>               
277        """
278       
279    def __call__(self, environ, start_response):
280        url = []
281        code_message = []
282        try:
283            def change_response(status, headers, exc_info=None):
284                new_url = None
285                parts = status.split(' ')
286                try:
287                    code = int(parts[0])
288                except ValueError, TypeError:
289                    raise Exception(
290                        '_StatusBasedRedirect middleware '
291                        'received an invalid status code %s'%repr(parts[0])
292                    )
293                message = ' '.join(parts[1:])
294                new_url = self.mapper(
295                    code,
296                    message,
297                    environ,
298                    self.global_conf,
299                    self.kw
300                )
301                if not (new_url == None or isinstance(new_url, str)):
302                    raise TypeError(
303                        'Expected the url to internally '
304                        'redirect to in the _StatusBasedRedirect error_mapper'
305                        'to be a string or None, not %s'%repr(new_url)
306                    )
307                if new_url:
308                    url.append(new_url)
309                code_message.append([code, message])
310                return start_response(status, headers, exc_info)
311            app_iter = self.application(environ, change_response)
312        except:
313            try:
314                import sys
315                error = str(sys.exc_info()[1])
316            except:
317                error = ''
318            try:
319                code, message = code_message[0]
320            except:
321                code, message = ['', '']
322            environ['wsgi.errors'].write(
323                'Error occurred in _StatusBasedRedirect '
324                'intercepting the response: '+str(error)
325            )
326            return [self.fallback_template
327                    % {'message': message, 'code': code}]
328        else:
329            if url:
330                url_ = url[0]
331                new_environ = {}
332                for k, v in environ.items():
333                    if k != 'QUERY_STRING':
334                        new_environ['QUERY_STRING'] = urlparse(url_)[4]
335                    else:
336                        new_environ[k] = v
337                class InvalidForward(Exception):
338                    pass
339                def eat_start_response(status, headers, exc_info=None):
340                    """
341                    We don't want start_response to do anything since it
342                    has already been called
343                    """
344                    if status[:3] != '200':
345                        raise InvalidForward(
346                            "The URL %s to internally forward "
347                            "to in order to create an error document did not "
348                            "return a '200' status code." % url_
349                        )
350                forward = environ['paste.recursive.forward']
351                old_start_response = forward.start_response
352                forward.start_response = eat_start_response
353                try:
354                    app_iter = forward(url_, new_environ)
355                except InvalidForward, e:
356                    code, message = code_message[0]
357                    environ['wsgi.errors'].write(
358                        'Error occurred in '
359                        '_StatusBasedRedirect redirecting '
360                        'to new URL: '+str(url[0])
361                    )
362                    return [
363                        self.fallback_template%{
364                            'message':message,
365                            'code':code,
366                        }
367                    ]
368                else:
369                    forward.start_response = old_start_response
370                    return app_iter
371            else:
372                return app_iter
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。