Imported from SVN by Bitbucket
This commit is contained in:
133
Paste-1.7.5.1-py2.6.egg/paste/cascade.py
Executable file
133
Paste-1.7.5.1-py2.6.egg/paste/cascade.py
Executable file
@@ -0,0 +1,133 @@
|
||||
# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
|
||||
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""
|
||||
Cascades through several applications, so long as applications
|
||||
return ``404 Not Found``.
|
||||
"""
|
||||
from paste import httpexceptions
|
||||
from paste.util import converters
|
||||
import tempfile
|
||||
from cStringIO import StringIO
|
||||
|
||||
__all__ = ['Cascade']
|
||||
|
||||
def make_cascade(loader, global_conf, catch='404', **local_conf):
|
||||
"""
|
||||
Entry point for Paste Deploy configuration
|
||||
|
||||
Expects configuration like::
|
||||
|
||||
[composit:cascade]
|
||||
use = egg:Paste#cascade
|
||||
# all start with 'app' and are sorted alphabetically
|
||||
app1 = foo
|
||||
app2 = bar
|
||||
...
|
||||
catch = 404 500 ...
|
||||
"""
|
||||
catch = map(int, converters.aslist(catch))
|
||||
apps = []
|
||||
for name, value in local_conf.items():
|
||||
if not name.startswith('app'):
|
||||
raise ValueError(
|
||||
"Bad configuration key %r (=%r); all configuration keys "
|
||||
"must start with 'app'"
|
||||
% (name, value))
|
||||
app = loader.get_app(value, global_conf=global_conf)
|
||||
apps.append((name, app))
|
||||
apps.sort()
|
||||
apps = [app for name, app in apps]
|
||||
return Cascade(apps, catch=catch)
|
||||
|
||||
class Cascade(object):
|
||||
|
||||
"""
|
||||
Passed a list of applications, ``Cascade`` will try each of them
|
||||
in turn. If one returns a status code listed in ``catch`` (by
|
||||
default just ``404 Not Found``) then the next application is
|
||||
tried.
|
||||
|
||||
If all applications fail, then the last application's failure
|
||||
response is used.
|
||||
|
||||
Instances of this class are WSGI applications.
|
||||
"""
|
||||
|
||||
def __init__(self, applications, catch=(404,)):
|
||||
self.apps = applications
|
||||
self.catch_codes = {}
|
||||
self.catch_exceptions = []
|
||||
for error in catch:
|
||||
if isinstance(error, str):
|
||||
error = int(error.split(None, 1)[0])
|
||||
if isinstance(error, httpexceptions.HTTPException):
|
||||
exc = error
|
||||
code = error.code
|
||||
else:
|
||||
exc = httpexceptions.get_exception(error)
|
||||
code = error
|
||||
self.catch_codes[code] = exc
|
||||
self.catch_exceptions.append(exc)
|
||||
self.catch_exceptions = tuple(self.catch_exceptions)
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
"""
|
||||
WSGI application interface
|
||||
"""
|
||||
failed = []
|
||||
def repl_start_response(status, headers, exc_info=None):
|
||||
code = int(status.split(None, 1)[0])
|
||||
if code in self.catch_codes:
|
||||
failed.append(None)
|
||||
return _consuming_writer
|
||||
return start_response(status, headers, exc_info)
|
||||
|
||||
try:
|
||||
length = int(environ.get('CONTENT_LENGTH', 0) or 0)
|
||||
except ValueError:
|
||||
length = 0
|
||||
if length > 0:
|
||||
# We have to copy wsgi.input
|
||||
copy_wsgi_input = True
|
||||
if length > 4096 or length < 0:
|
||||
f = tempfile.TemporaryFile()
|
||||
if length < 0:
|
||||
f.write(environ['wsgi.input'].read())
|
||||
else:
|
||||
copy_len = length
|
||||
while copy_len > 0:
|
||||
chunk = environ['wsgi.input'].read(min(copy_len, 4096))
|
||||
if not chunk:
|
||||
raise IOError("Request body truncated")
|
||||
f.write(chunk)
|
||||
copy_len -= len(chunk)
|
||||
f.seek(0)
|
||||
else:
|
||||
f = StringIO(environ['wsgi.input'].read(length))
|
||||
environ['wsgi.input'] = f
|
||||
else:
|
||||
copy_wsgi_input = False
|
||||
for app in self.apps[:-1]:
|
||||
environ_copy = environ.copy()
|
||||
if copy_wsgi_input:
|
||||
environ_copy['wsgi.input'].seek(0)
|
||||
failed = []
|
||||
try:
|
||||
v = app(environ_copy, repl_start_response)
|
||||
if not failed:
|
||||
return v
|
||||
else:
|
||||
if hasattr(v, 'close'):
|
||||
# Exhaust the iterator first:
|
||||
list(v)
|
||||
# then close:
|
||||
v.close()
|
||||
except self.catch_exceptions, e:
|
||||
pass
|
||||
if copy_wsgi_input:
|
||||
environ['wsgi.input'].seek(0)
|
||||
return self.apps[-1](environ, start_response)
|
||||
|
||||
def _consuming_writer(s):
|
||||
pass
|
||||
Reference in New Issue
Block a user