source: publico/il.spdo/trunk/Paste-1.7.5.1-py2.6.egg/paste/errordocument.py @ 5327

Última Alteração nesse arquivo desde 5327 foi 5327, incluída por fabianosantos, 8 anos atrás

Import inicial.

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

The contents and data of this website are published under license:
Creative Commons 4.0 Brasil - Atribuir Fonte - Compartilhar Igual.