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

リビジョン 3, 14.4 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"""
4Middleware to make internal requests and forward requests internally.
5
6When applied, several keys are added to the environment that will allow
7you to trigger recursive redirects and forwards.
8
9  paste.recursive.include:
10      When you call
11      ``environ['paste.recursive.include'](new_path_info)`` a response
12      will be returned.  The response has a ``body`` attribute, a
13      ``status`` attribute, and a ``headers`` attribute.
14
15  paste.recursive.script_name:
16      The ``SCRIPT_NAME`` at the point that recursive lives.  Only
17      paths underneath this path can be redirected to.
18
19  paste.recursive.old_path_info:
20      A list of previous ``PATH_INFO`` values from previous redirects.
21
22Raise ``ForwardRequestException(new_path_info)`` to do a forward
23(aborting the current request).
24"""
25
26from cStringIO import StringIO
27import warnings
28
29__all__ = ['RecursiveMiddleware']
30__pudge_all__ =  ['RecursiveMiddleware', 'ForwardRequestException']
31
32class CheckForRecursionMiddleware(object):
33    def __init__(self, app, env):
34        self.app = app
35        self.env = env
36       
37    def __call__(self, environ, start_response):
38        path_info = environ.get('PATH_INFO','')
39        if path_info in self.env.get(
40            'paste.recursive.old_path_info', []):
41            raise AssertionError(
42                "Forwarding loop detected; %r visited twice (internal "
43                "redirect path: %s)"
44                % (path_info, self.env['paste.recursive.old_path_info']))
45        old_path_info = self.env.setdefault('paste.recursive.old_path_info', [])
46        old_path_info.append(self.env.get('PATH_INFO', ''))
47        return self.app(environ, start_response)
48
49class RecursiveMiddleware(object):
50
51    """
52    A WSGI middleware that allows for recursive and forwarded calls.
53    All these calls go to the same 'application', but presumably that
54    application acts differently with different URLs.  The forwarded
55    URLs must be relative to this container.
56
57    Interface is entirely through the ``paste.recursive.forward`` and
58    ``paste.recursive.include`` environmental keys.
59    """
60
61    def __init__(self, application, global_conf=None):
62        self.application = application
63
64    def __call__(self, environ, start_response):
65        environ['paste.recursive.forward'] = Forwarder(
66            self.application,
67            environ,
68            start_response)
69        environ['paste.recursive.include'] = Includer(
70            self.application,
71            environ,
72            start_response)
73        environ['paste.recursive.include_app_iter'] = IncluderAppIter(
74            self.application,
75            environ,
76            start_response)
77        my_script_name = environ.get('SCRIPT_NAME', '')
78        environ['paste.recursive.script_name'] = my_script_name
79        try:
80            return self.application(environ, start_response)
81        except ForwardRequestException, e:
82            middleware = CheckForRecursionMiddleware(
83                e.factory(self), environ)
84            return middleware(environ, start_response)
85
86class ForwardRequestException(Exception):
87    """
88    Used to signal that a request should be forwarded to a different location.
89   
90    ``url``
91        The URL to forward to starting with a ``/`` and relative to
92        ``RecursiveMiddleware``. URL fragments can also contain query strings
93        so ``/error?code=404`` would be a valid URL fragment.
94   
95    ``environ``
96        An altertative WSGI environment dictionary to use for the forwarded
97        request. If specified is used *instead* of the ``url_fragment``
98     
99    ``factory``
100        If specifed ``factory`` is used instead of ``url`` or ``environ``.
101        ``factory`` is a callable that takes a WSGI application object
102        as the first argument and returns an initialised WSGI middleware
103        which can alter the forwarded response.
104
105    Basic usage (must have ``RecursiveMiddleware`` present) :
106   
107    .. code-block:: Python
108   
109        from paste.recursive import ForwardRequestException
110        def app(environ, start_response):
111            if environ['PATH_INFO'] == '/hello':
112                start_response("200 OK", [('Content-type', 'text/plain')])
113                return ['Hello World!']
114            elif environ['PATH_INFO'] == '/error':
115                start_response("404 Not Found", [('Content-type', 'text/plain')])
116                return ['Page not found']
117            else:
118                raise ForwardRequestException('/error')
119               
120        from paste.recursive import RecursiveMiddleware
121        app = RecursiveMiddleware(app)
122       
123    If you ran this application and visited ``/hello`` you would get a
124    ``Hello World!`` message. If you ran the application and visited
125    ``/not_found`` a ``ForwardRequestException`` would be raised and the caught
126    by the ``RecursiveMiddleware``. The ``RecursiveMiddleware`` would then
127    return the headers and response from the ``/error`` URL but would display
128    a ``404 Not found`` status message.
129   
130    You could also specify an ``environ`` dictionary instead of a url. Using
131    the same example as before:
132   
133    .. code-block:: Python
134   
135        def app(environ, start_response):
136            ... same as previous example ...
137            else:
138                new_environ = environ.copy()
139                new_environ['PATH_INFO'] = '/error'
140                raise ForwardRequestException(environ=new_environ)
141               
142    Finally, if you want complete control over every aspect of the forward you
143    can specify a middleware factory. For example to keep the old status code
144    but use the headers and resposne body from the forwarded response you might
145    do this:
146   
147    .. code-block:: Python
148
149        from paste.recursive import ForwardRequestException
150        from paste.recursive import RecursiveMiddleware
151        from paste.errordocument import StatusKeeper
152
153        def app(environ, start_response):
154            if environ['PATH_INFO'] == '/hello':
155                start_response("200 OK", [('Content-type', 'text/plain')])
156                return ['Hello World!']
157            elif environ['PATH_INFO'] == '/error':
158                start_response("404 Not Found", [('Content-type', 'text/plain')])
159                return ['Page not found']
160            else:
161                def factory(app):
162                    return StatusKeeper(app, status='404 Not Found', url='/error')
163                raise ForwardRequestException(factory=factory)
164
165        app = RecursiveMiddleware(app)
166    """
167
168    def __init__(
169        self,
170        url=None,
171        environ={},
172        factory=None,
173        path_info=None):
174        # Check no incompatible options have been chosen
175        if factory and url:
176            raise TypeError(
177                'You cannot specify factory and a url in '
178                'ForwardRequestException')
179        elif factory and environ:
180            raise TypeError(
181                'You cannot specify factory and environ in '
182                'ForwardRequestException')
183        if url and environ:
184            raise TypeError(
185                'You cannot specify environ and url in '
186                'ForwardRequestException')
187
188        # set the path_info or warn about its use.
189        if path_info:
190            if not url:
191                warnings.warn(
192                    "ForwardRequestException(path_info=...) has been deprecated; please "
193                    "use ForwardRequestException(url=...)",
194                    DeprecationWarning, 2)
195            else:
196                raise TypeError('You cannot use url and path_info in ForwardRequestException')
197            self.path_info = path_info
198   
199        # If the url can be treated as a path_info do that
200        if url and not '?' in str(url):
201            self.path_info = url
202           
203        # Base middleware
204        class ForwardRequestExceptionMiddleware(object):
205            def __init__(self, app):
206                self.app = app
207                           
208        # Otherwise construct the appropriate middleware factory
209        if hasattr(self, 'path_info'):
210            p = self.path_info
211            def factory_(app):
212                class PathInfoForward(ForwardRequestExceptionMiddleware):
213                    def __call__(self, environ, start_response):
214                        environ['PATH_INFO'] = p
215                        return self.app(environ, start_response)
216                return PathInfoForward(app)
217            self.factory = factory_
218        elif url:
219            def factory_(app):
220                class URLForward(ForwardRequestExceptionMiddleware):
221                    def __call__(self, environ, start_response):
222                        environ['PATH_INFO'] = url.split('?')[0]
223                        environ['QUERY_STRING'] = url.split('?')[1]
224                        return self.app(environ, start_response)
225                return URLForward(app)
226            self.factory = factory_
227        elif environ:
228            def factory_(app):
229                class EnvironForward(ForwardRequestExceptionMiddleware):
230                    def __call__(self, environ_, start_response):
231                        return self.app(environ, start_response)
232                return EnvironForward(app)
233            self.factory = factory_
234        else:
235            self.factory = factory
236
237class Recursive(object):
238
239    def __init__(self, application, environ, start_response):
240        self.application = application
241        self.original_environ = environ.copy()
242        self.previous_environ = environ
243        self.start_response = start_response
244
245    def __call__(self, path, extra_environ=None):
246        """
247        `extra_environ` is an optional dictionary that is also added
248        to the forwarded request.  E.g., ``{'HTTP_HOST': 'new.host'}``
249        could be used to forward to a different virtual host.
250        """
251        environ = self.original_environ.copy()
252        if extra_environ:
253            environ.update(extra_environ)
254        environ['paste.recursive.previous_environ'] = self.previous_environ
255        base_path = self.original_environ.get('SCRIPT_NAME')
256        if path.startswith('/'):
257            assert path.startswith(base_path), (
258                "You can only forward requests to resources under the "
259                "path %r (not %r)" % (base_path, path))
260            path = path[len(base_path)+1:]
261        assert not path.startswith('/')
262        path_info = '/' + path
263        environ['PATH_INFO'] = path_info
264        environ['REQUEST_METHOD'] = 'GET'
265        environ['CONTENT_LENGTH'] = '0'
266        environ['CONTENT_TYPE'] = ''
267        environ['wsgi.input'] = StringIO('')
268        return self.activate(environ)
269
270    def activate(self, environ):
271        raise NotImplementedError
272
273    def __repr__(self):
274        return '<%s.%s from %s>' % (
275            self.__class__.__module__,
276            self.__class__.__name__,
277            self.original_environ.get('SCRIPT_NAME') or '/')
278
279class Forwarder(Recursive):
280
281    """
282    The forwarder will try to restart the request, except with
283    the new `path` (replacing ``PATH_INFO`` in the request).
284
285    It must not be called after and headers have been returned.
286    It returns an iterator that must be returned back up the call
287    stack, so it must be used like:
288   
289    .. code-block:: Python
290
291        return environ['paste.recursive.forward'](path)
292
293    Meaningful transformations cannot be done, since headers are
294    sent directly to the server and cannot be inspected or
295    rewritten.
296    """
297
298    def activate(self, environ):
299        warnings.warn(
300            "recursive.Forwarder has been deprecated; please use "
301            "ForwardRequestException",
302            DeprecationWarning, 2)
303        return self.application(environ, self.start_response)
304   
305
306class Includer(Recursive):
307
308    """
309    Starts another request with the given path and adding or
310    overwriting any values in the `extra_environ` dictionary.
311    Returns an IncludeResponse object.
312    """
313   
314    def activate(self, environ):
315        response = IncludedResponse()
316        def start_response(status, headers, exc_info=None):
317            if exc_info:
318                raise exc_info[0], exc_info[1], exc_info[2]
319            response.status = status
320            response.headers = headers
321            return response.write
322        app_iter = self.application(environ, start_response)
323        try:
324            for s in app_iter:
325                response.write(s)
326        finally:
327            if hasattr(app_iter, 'close'):
328                app_iter.close()
329        response.close()
330        return response
331
332class IncludedResponse(object):
333
334    def __init__(self):
335        self.headers = None
336        self.status = None
337        self.output = StringIO()
338        self.str = None
339
340    def close(self):
341        self.str = self.output.getvalue()
342        self.output.close()
343        self.output = None
344
345    def write(self, s):
346        assert self.output is not None, (
347            "This response has already been closed and no further data "
348            "can be written.")
349        self.output.write(s)
350
351    def __str__(self):
352        return self.body
353
354    def body__get(self):
355        if self.str is None:
356            return self.output.getvalue()
357        else:
358            return self.str
359    body = property(body__get)
360
361
362class IncluderAppIter(Recursive):
363    """
364    Like Includer, but just stores the app_iter response
365    (be sure to call close on the response!)
366    """
367
368    def activate(self, environ):
369        response = IncludedAppIterResponse()
370        def start_response(status, headers, exc_info=None):
371            if exc_info:
372                raise exc_info[0], exc_info[1], exc_info[2]
373            response.status = status
374            response.headers = headers
375            return response.write
376        app_iter = self.application(environ, start_response)
377        response.app_iter = app_iter
378        return response
379
380class IncludedAppIterResponse(object):
381
382    def __init__(self):
383        self.status = None
384        self.headers = None
385        self.accumulated = []
386        self.app_iter = None
387        self._closed = False
388
389    def close(self):
390        assert not self._closed, (
391            "Tried to close twice")
392        if hasattr(self.app_iter, 'close'):
393            self.app_iter.close()
394           
395    def write(self, s):
396        self.accumulated.append
397
398def make_recursive_middleware(app, global_conf):
399    return RecursiveMiddleware(app)
400
401make_recursive_middleware.__doc__ = __doc__
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。