root/galaxy-central/eggs/Paste-1.6-py2.6.egg/paste/wsgilib.py @ 3

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

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

行番号 
1# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
2# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
3
4"""
5A module of many disparate routines.
6"""
7
8# functions which moved to paste.request and paste.response
9# Deprecated around 15 Dec 2005
10from paste.request import get_cookies, parse_querystring, parse_formvars
11from paste.request import construct_url, path_info_split, path_info_pop
12from paste.response import HeaderDict, has_header, header_value, remove_header
13from paste.response import error_body_response, error_response, error_response_app
14
15from traceback import print_exception
16import urllib
17from cStringIO import StringIO
18import sys
19from urlparse import urlsplit
20import warnings
21
22__all__ = ['add_close', 'add_start_close', 'capture_output', 'catch_errors',
23           'catch_errors_app', 'chained_app_iters', 'construct_url',
24           'dump_environ', 'encode_unicode_app_iter', 'error_body_response',
25           'error_response', 'get_cookies', 'has_header', 'header_value',
26           'interactive', 'intercept_output', 'path_info_pop',
27           'path_info_split', 'raw_interactive', 'send_file']
28
29class add_close(object):
30    """
31    An an iterable that iterates over app_iter, then calls
32    close_func.
33    """
34
35    def __init__(self, app_iterable, close_func):
36        self.app_iterable = app_iterable
37        self.app_iter = iter(app_iterable)
38        self.close_func = close_func
39        self._closed = False
40
41    def __iter__(self):
42        return self
43
44    def next(self):
45        return self.app_iter.next()
46
47    def close(self):
48        self._closed = True
49        if hasattr(self.app_iterable, 'close'):
50            self.app_iterable.close()
51        self.close_func()
52
53    def __del__(self):
54        if not self._closed:
55            # We can't raise an error or anything at this stage
56            print >> sys.stderr, (
57                "Error: app_iter.close() was not called when finishing "
58                "WSGI request. finalization function %s not called"
59                % self.close_func)
60
61class add_start_close(object):
62    """
63    An an iterable that iterates over app_iter, calls start_func
64    before the first item is returned, then calls close_func at the
65    end.
66    """
67
68    def __init__(self, app_iterable, start_func, close_func=None):
69        self.app_iterable = app_iterable
70        self.app_iter = iter(app_iterable)
71        self.first = True
72        self.start_func = start_func
73        self.close_func = close_func
74        self._closed = False
75
76    def __iter__(self):
77        return self
78
79    def next(self):
80        if self.first:
81            self.start_func()
82            self.first = False
83        return self.app_iter.next()
84
85    def close(self):
86        self._closed = True
87        if hasattr(self.app_iterable, 'close'):
88            self.app_iterable.close()
89        if self.close_func is not None:
90            self.close_func()
91
92    def __del__(self):
93        if not self._closed:
94            # We can't raise an error or anything at this stage
95            print >> sys.stderr, (
96                "Error: app_iter.close() was not called when finishing "
97                "WSGI request. finalization function %s not called"
98                % self.close_func)
99
100class chained_app_iters(object):
101
102    """
103    Chains several app_iters together, also delegating .close() to each
104    of them.
105    """
106
107    def __init__(self, *chained):
108        self.app_iters = chained
109        self.chained = [iter(item) for item in chained]
110        self._closed = False
111
112    def __iter__(self):
113        return self
114
115    def next(self):
116        if len(self.chained) == 1:
117            return self.chained[0].next()
118        else:
119            try:
120                return self.chained[0].next()
121            except StopIteration:
122                self.chained.pop(0)
123                return self.next()
124
125    def close(self):
126        self._closed = True
127        got_exc = None
128        for app_iter in self.app_iters:
129            try:
130                if hasattr(app_iter, 'close'):
131                    app_iter.close()
132            except:
133                got_exc = sys.exc_info()
134        if got_exc:
135            raise got_exc[0], got_exc[1], got_exc[2]
136
137    def __del__(self):
138        if not self._closed:
139            # We can't raise an error or anything at this stage
140            print >> sys.stderr, (
141                "Error: app_iter.close() was not called when finishing "
142                "WSGI request. finalization function %s not called"
143                % self.close_func)
144
145class encode_unicode_app_iter(object):
146    """
147    Encodes an app_iterable's unicode responses as strings
148    """
149
150    def __init__(self, app_iterable, encoding=sys.getdefaultencoding(),
151                 errors='strict'):
152        self.app_iterable = app_iterable
153        self.app_iter = iter(app_iterable)
154        self.encoding = encoding
155        self.errors = errors
156
157    def __iter__(self):
158        return self
159
160    def next(self):
161        content = self.app_iter.next()
162        if isinstance(content, unicode):
163            content = content.encode(self.encoding, self.errors)
164        return content
165
166    def close(self):
167        if hasattr(self.app_iterable, 'close'):
168            self.app_iterable.close()
169
170def catch_errors(application, environ, start_response, error_callback,
171                 ok_callback=None):
172    """
173    Runs the application, and returns the application iterator (which should be
174    passed upstream).  If an error occurs then error_callback will be called with
175    exc_info as its sole argument.  If no errors occur and ok_callback is given,
176    then it will be called with no arguments.
177    """
178    try:
179        app_iter = application(environ, start_response)
180    except:
181        error_callback(sys.exc_info())
182        raise
183    if type(app_iter) in (list, tuple):
184        # These won't produce exceptions
185        if ok_callback:
186            ok_callback()
187        return app_iter
188    else:
189        return _wrap_app_iter(app_iter, error_callback, ok_callback)
190
191class _wrap_app_iter(object):
192
193    def __init__(self, app_iterable, error_callback, ok_callback):
194        self.app_iterable = app_iterable
195        self.app_iter = iter(app_iterable)
196        self.error_callback = error_callback
197        self.ok_callback = ok_callback
198        if hasattr(self.app_iterable, 'close'):
199            self.close = self.app_iterable.close
200
201    def __iter__(self):
202        return self
203
204    def next(self):
205        try:
206            return self.app_iter.next()
207        except StopIteration:
208            if self.ok_callback:
209                self.ok_callback()
210            raise
211        except:
212            self.error_callback(sys.exc_info())
213            raise
214
215def catch_errors_app(application, environ, start_response, error_callback_app,
216                     ok_callback=None, catch=Exception):
217    """
218    Like ``catch_errors``, except error_callback_app should be a
219    callable that will receive *three* arguments -- ``environ``,
220    ``start_response``, and ``exc_info``.  It should call
221    ``start_response`` (*with* the exc_info argument!) and return an
222    iterator.
223    """
224    try:
225        app_iter = application(environ, start_response)
226    except catch:
227        return error_callback_app(environ, start_response, sys.exc_info())
228    if type(app_iter) in (list, tuple):
229        # These won't produce exceptions
230        if ok_callback is not None:
231            ok_callback()
232        return app_iter
233    else:
234        return _wrap_app_iter_app(
235            environ, start_response, app_iter,
236            error_callback_app, ok_callback, catch=catch)
237
238class _wrap_app_iter_app(object):
239
240    def __init__(self, environ, start_response, app_iterable,
241                 error_callback_app, ok_callback, catch=Exception):
242        self.environ = environ
243        self.start_response = start_response
244        self.app_iterable = app_iterable
245        self.app_iter = iter(app_iterable)
246        self.error_callback_app = error_callback_app
247        self.ok_callback = ok_callback
248        self.catch = catch
249        if hasattr(self.app_iterable, 'close'):
250            self.close = self.app_iterable.close
251
252    def __iter__(self):
253        return self
254
255    def next(self):
256        try:
257            return self.app_iter.next()
258        except StopIteration:
259            if self.ok_callback:
260                self.ok_callback()
261            raise
262        except self.catch:
263            if hasattr(self.app_iterable, 'close'):
264                try:
265                    self.app_iterable.close()
266                except:
267                    # @@: Print to wsgi.errors?
268                    pass
269            new_app_iterable = self.error_callback_app(
270                self.environ, self.start_response, sys.exc_info())
271            app_iter = iter(new_app_iterable)
272            if hasattr(new_app_iterable, 'close'):
273                self.close = new_app_iterable.close
274            self.next = app_iter.next
275            return self.next()
276
277def raw_interactive(application, path='', raise_on_wsgi_error=False,
278                    **environ):
279    """
280    Runs the application in a fake environment.
281    """
282    assert "path_info" not in environ, "argument list changed"
283    if raise_on_wsgi_error:
284        errors = ErrorRaiser()
285    else:
286        errors = StringIO()
287    basic_environ = {
288        # mandatory CGI variables
289        'REQUEST_METHOD': 'GET',     # always mandatory
290        'SCRIPT_NAME': '',           # may be empty if app is at the root
291        'PATH_INFO': '',             # may be empty if at root of app
292        'SERVER_NAME': 'localhost',  # always mandatory
293        'SERVER_PORT': '80',         # always mandatory
294        'SERVER_PROTOCOL': 'HTTP/1.0',
295        # mandatory wsgi variables
296        'wsgi.version': (1, 0),
297        'wsgi.url_scheme': 'http',
298        'wsgi.input': StringIO(''),
299        'wsgi.errors': errors,
300        'wsgi.multithread': False,
301        'wsgi.multiprocess': False,
302        'wsgi.run_once': False,
303        }
304    if path:
305        (_, _, path_info, query, fragment) = urlsplit(str(path))
306        path_info = urllib.unquote(path_info)
307        basic_environ['PATH_INFO'] = path_info
308        if query:
309            basic_environ['QUERY_STRING'] = query
310    for name, value in environ.items():
311        name = name.replace('__', '.')
312        basic_environ[name] = value
313    if ('SERVER_NAME' in basic_environ
314        and 'HTTP_HOST' not in basic_environ):
315        basic_environ['HTTP_HOST'] = basic_environ['SERVER_NAME']
316    istream = basic_environ['wsgi.input']
317    if isinstance(istream, str):
318        basic_environ['wsgi.input'] = StringIO(istream)
319        basic_environ['CONTENT_LENGTH'] = len(istream)
320    data = {}
321    output = []
322    headers_set = []
323    headers_sent = []
324    def start_response(status, headers, exc_info=None):
325        if exc_info:
326            try:
327                if headers_sent:
328                    # Re-raise original exception only if headers sent
329                    raise exc_info[0], exc_info[1], exc_info[2]
330            finally:
331                # avoid dangling circular reference
332                exc_info = None
333        elif headers_set:
334            # You cannot set the headers more than once, unless the
335            # exc_info is provided.
336            raise AssertionError("Headers already set and no exc_info!")
337        headers_set.append(True)
338        data['status'] = status
339        data['headers'] = headers
340        return output.append
341    app_iter = application(basic_environ, start_response)
342    try:
343        try:
344            for s in app_iter:
345                if not isinstance(s, str):
346                    raise ValueError(
347                        "The app_iter response can only contain str (not "
348                        "unicode); got: %r" % s)
349                headers_sent.append(True)
350                if not headers_set:
351                    raise AssertionError("Content sent w/o headers!")
352                output.append(s)
353        except TypeError, e:
354            # Typically "iteration over non-sequence", so we want
355            # to give better debugging information...
356            e.args = ((e.args[0] + ' iterable: %r' % app_iter),) + e.args[1:]
357            raise
358    finally:
359        if hasattr(app_iter, 'close'):
360            app_iter.close()
361    return (data['status'], data['headers'], ''.join(output),
362            errors.getvalue())
363
364class ErrorRaiser(object):
365
366    def flush(self):
367        pass
368
369    def write(self, value):
370        if not value:
371            return
372        raise AssertionError(
373            "No errors should be written (got: %r)" % value)
374
375    def writelines(self, seq):
376        raise AssertionError(
377            "No errors should be written (got lines: %s)" % list(seq))
378
379    def getvalue(self):
380        return ''
381
382def interactive(*args, **kw):
383    """
384    Runs the application interatively, wrapping `raw_interactive` but
385    returning the output in a formatted way.
386    """
387    status, headers, content, errors = raw_interactive(*args, **kw)
388    full = StringIO()
389    if errors:
390        full.write('Errors:\n')
391        full.write(errors.strip())
392        full.write('\n----------end errors\n')
393    full.write(status + '\n')
394    for name, value in headers:
395        full.write('%s: %s\n' % (name, value))
396    full.write('\n')
397    full.write(content)
398    return full.getvalue()
399interactive.proxy = 'raw_interactive'
400
401def dump_environ(environ, start_response):
402    """
403    Application which simply dumps the current environment
404    variables out as a plain text response.
405    """
406    output = []
407    keys = environ.keys()
408    keys.sort()
409    for k in keys:
410        v = str(environ[k]).replace("\n","\n    ")
411        output.append("%s: %s\n" % (k, v))
412    output.append("\n")
413    content_length = environ.get("CONTENT_LENGTH", '')
414    if content_length:
415        output.append(environ['wsgi.input'].read(int(content_length)))
416        output.append("\n")
417    output = "".join(output)
418    headers = [('Content-Type', 'text/plain'),
419               ('Content-Length', str(len(output)))]
420    start_response("200 OK", headers)
421    return [output]
422
423def send_file(filename):
424    warnings.warn(
425        "wsgilib.send_file has been moved to paste.fileapp.FileApp",
426        DeprecationWarning, 2)
427    from paste import fileapp
428    return fileapp.FileApp(filename)
429
430def capture_output(environ, start_response, application):
431    """
432    Runs application with environ and start_response, and captures
433    status, headers, and body.
434
435    Sends status and header, but *not* body.  Returns (status,
436    headers, body).  Typically this is used like:
437   
438    .. code-block:: Python
439
440        def dehtmlifying_middleware(application):
441            def replacement_app(environ, start_response):
442                status, headers, body = capture_output(
443                    environ, start_response, application)
444                content_type = header_value(headers, 'content-type')
445                if (not content_type
446                    or not content_type.startswith('text/html')):
447                    return [body]
448                body = re.sub(r'<.*?>', '', body)
449                return [body]
450            return replacement_app
451
452    """
453    warnings.warn(
454        'wsgilib.capture_output has been deprecated in favor '
455        'of wsgilib.intercept_output',
456        DeprecationWarning, 2)
457    data = []
458    output = StringIO()
459    def replacement_start_response(status, headers, exc_info=None):
460        if data:
461            data[:] = []
462        data.append(status)
463        data.append(headers)
464        start_response(status, headers, exc_info)
465        return output.write
466    app_iter = application(environ, replacement_start_response)
467    try:
468        for item in app_iter:
469            output.write(item)
470    finally:
471        if hasattr(app_iter, 'close'):
472            app_iter.close()
473    if not data:
474        data.append(None)
475    if len(data) < 2:
476        data.append(None)
477    data.append(output.getvalue())
478    return data
479
480def intercept_output(environ, application, conditional=None,
481                     start_response=None):
482    """
483    Runs application with environ and captures status, headers, and
484    body.  None are sent on; you must send them on yourself (unlike
485    ``capture_output``)
486
487    Typically this is used like:
488   
489    .. code-block:: Python
490
491        def dehtmlifying_middleware(application):
492            def replacement_app(environ, start_response):
493                status, headers, body = intercept_output(
494                    environ, application)
495                content_type = header_value(headers, 'content-type')
496                if (not content_type
497                    or not content_type.startswith('text/html')):
498                    return [body]
499                body = re.sub(r'<.*?>', '', body)
500                return [body]
501            return replacement_app
502
503    A third optional argument ``conditional`` should be a function
504    that takes ``conditional(status, headers)`` and returns False if
505    the request should not be intercepted.  In that case
506    ``start_response`` will be called and ``(None, None, app_iter)``
507    will be returned.  You must detect that in your code and return
508    the app_iter, like:
509   
510    .. code-block:: Python
511
512        def dehtmlifying_middleware(application):
513            def replacement_app(environ, start_response):
514                status, headers, body = intercept_output(
515                    environ, application,
516                    lambda s, h: header_value(headers, 'content-type').startswith('text/html'),
517                    start_response)
518                if status is None:
519                    return body
520                body = re.sub(r'<.*?>', '', body)
521                return [body]
522            return replacement_app
523    """
524    if conditional is not None and start_response is None:
525        raise TypeError(
526            "If you provide conditional you must also provide "
527            "start_response")
528    data = []
529    output = StringIO()
530    def replacement_start_response(status, headers, exc_info=None):
531        if conditional is not None and not conditional(status, headers):
532            data.append(None)
533            return start_response(status, headers, exc_info)
534        if data:
535            data[:] = []
536        data.append(status)
537        data.append(headers)
538        return output.write
539    app_iter = application(environ, replacement_start_response)
540    if data[0] is None:
541        return (None, None, app_iter)
542    try:
543        for item in app_iter:
544            output.write(item)
545    finally:
546        if hasattr(app_iter, 'close'):
547            app_iter.close()
548    if not data:
549        data.append(None)
550    if len(data) < 2:
551        data.append(None)
552    data.append(output.getvalue())
553    return data
554
555## Deprecation warning wrapper:
556
557class ResponseHeaderDict(HeaderDict):
558
559    def __init__(self, *args, **kw):
560        warnings.warn(
561            "The class wsgilib.ResponseHeaderDict has been moved "
562            "to paste.response.HeaderDict",
563            DeprecationWarning, 2)
564        HeaderDict.__init__(self, *args, **kw)
565
566def _warn_deprecated(new_func):
567    new_name = new_func.func_name
568    new_path = new_func.func_globals['__name__'] + '.' + new_name
569    def replacement(*args, **kw):
570        warnings.warn(
571            "The function wsgilib.%s has been moved to %s"
572            % (new_name, new_path),
573            DeprecationWarning, 2)
574        return new_func(*args, **kw)
575    try:
576        replacement.func_name = new_func.func_name
577    except:
578        pass
579    return replacement
580
581# Put warnings wrapper in place for all public functions that
582# were imported from elsewhere:
583
584for _name in __all__:
585    _func = globals()[_name]
586    if (hasattr(_func, 'func_globals')
587        and _func.func_globals['__name__'] != __name__):
588        globals()[_name] = _warn_deprecated(_func)
589
590if __name__ == '__main__':
591    import doctest
592    doctest.testmod()
593   
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。