1b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# -*- coding: utf-8 -*- 2b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik""" 3b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik webapp2 4b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ======= 5b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 6b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Taking Google App Engine's webapp to the next level! 7b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 8b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :copyright: 2011 by tipfy.org. 9b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :license: Apache Sotware License, see LICENSE for details. 10b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik""" 11b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom __future__ import with_statement 12b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 13b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport cgi 14b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport inspect 15b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport logging 16b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport os 17b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport re 18b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport sys 19b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport threading 20b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport traceback 21b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport urllib 22b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport urlparse 23b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom wsgiref import handlers 24b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 25b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport webob 26b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom webob import exc 27b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 28b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_webapp = _webapp_util = _local = None 29b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 30b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craiktry: # pragma: no cover 31b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # WebOb < 1.0 (App Engine Python 2.5). 32b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik from webob.statusreasons import status_reasons 33b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik from webob.headerdict import HeaderDict as BaseResponseHeaders 34b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikexcept ImportError: # pragma: no cover 35b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # WebOb >= 1.0. 36b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik from webob.util import status_reasons 37b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik from webob.headers import ResponseHeaders as BaseResponseHeaders 38b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 39b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# google.appengine.ext.webapp imports webapp2 in the 40b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# App Engine Python 2.7 runtime. 41b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikif os.environ.get('APPENGINE_RUNTIME') != 'python27': # pragma: no cover 42b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 43b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik from google.appengine.ext import webapp as _webapp 44b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except ImportError: # pragma: no cover 45b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Running webapp2 outside of GAE. 46b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik pass 47b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 48b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craiktry: # pragma: no cover 49b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Thread-local variables container. 50b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik from webapp2_extras import local 51b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik _local = local.Local() 52b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikexcept ImportError: # pragma: no cover 53b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik logging.warning("webapp2_extras.local is not available " 54b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik "so webapp2 won't be thread-safe!") 55b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 56b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 57b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik__version_info__ = (2, 5, 1) 58b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik__version__ = '.'.join(str(n) for n in __version_info__) 59b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 60b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik#: Base HTTP exception, set here as public interface. 61b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikHTTPException = exc.HTTPException 62b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 63b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik#: Regex for route definitions. 64b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_route_re = re.compile(r""" 65b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik \< # The exact character "<" 66b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ([a-zA-Z_]\w*)? # The optional variable name 67b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?:\:([^\>]*))? # The optional :regex part 68b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik \> # The exact character ">" 69b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """, re.VERBOSE) 70b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik#: Regex extract charset from environ. 71b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_charset_re = re.compile(r';\s*charset=([^;]*)', re.I) 72b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 73b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik#: To show exceptions in debug mode. 74b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_debug_template = """<html> 75b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <head> 76b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <title>Internal Server Error</title> 77b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <style> 78b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik body { 79b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik padding: 20px; 80b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik font-family: arial, sans-serif; 81b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik font-size: 14px; 82b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik } 83b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik pre { 84b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik background: #F2F2F2; 85b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik padding: 10px; 86b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik } 87b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik </style> 88b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik </head> 89b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <body> 90b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <h1>Internal Server Error</h1> 91b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <p>The server has either erred or is incapable of performing 92b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik the requested operation.</p> 93b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <pre>%s</pre> 94b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik </body> 95b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik</html>""" 96b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 97b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# Set same default messages from webapp plus missing ones. 98b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_webapp_status_reasons = { 99b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 203: 'Non-Authoritative Information', 100b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 302: 'Moved Temporarily', 101b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 306: 'Unused', 102b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 408: 'Request Time-out', 103b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 414: 'Request-URI Too Large', 104b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 504: 'Gateway Time-out', 105b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 505: 'HTTP Version not supported', 106b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik} 107b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikstatus_reasons.update(_webapp_status_reasons) 108b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfor code, message in _webapp_status_reasons.iteritems(): 109b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik cls = exc.status_map.get(code) 110b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if cls: 111b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik cls.title = message 112b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 113b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 114b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass Request(webob.Request): 115b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Abstraction for an HTTP request. 116b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 117b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Most extra methods and attributes are ported from webapp. Check the 118b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik `WebOb documentation <WebOb>`_ for the ones not listed here. 119b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 120b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 121b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: A reference to the active :class:`WSGIApplication` instance. 122b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik app = None 123b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: A reference to the active :class:`Response` instance. 124b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response = None 125b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: A reference to the matched :class:`Route`. 126b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik route = None 127b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: The matched route positional arguments. 128b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik route_args = None 129b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: The matched route keyword arguments. 130b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik route_kwargs = None 131b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: A dictionary to register objects used during the request lifetime. 132b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik registry = None 133b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Attributes from webapp. 134b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request_body_tempfile_limit = 0 135b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik uri = property(lambda self: self.url) 136b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik query = property(lambda self: self.query_string) 137b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 138b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, environ, *args, **kwargs): 139b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Constructs a Request object from a WSGI environment. 140b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 141b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param environ: 142b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A WSGI-compliant environment dictionary. 143b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 144b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if kwargs.get('charset') is None and not hasattr(webob, '__version__'): 145b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # webob 0.9 didn't have a __version__ attribute and also defaulted 146b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # to None rather than UTF-8 if no charset was provided. Providing a 147b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # default charset is required for backwards compatibility. 148b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik match = _charset_re.search(environ.get('CONTENT_TYPE', '')) 149b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if match: 150b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik charset = match.group(1).lower().strip().strip('"').strip() 151b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 152b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik charset = 'utf-8' 153b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik kwargs['charset'] = charset 154b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 155b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik super(Request, self).__init__(environ, *args, **kwargs) 156b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.registry = {} 157b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 158b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def get(self, argument_name, default_value='', allow_multiple=False): 159b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns the query or POST argument with the given name. 160b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 161b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik We parse the query string and POST payload lazily, so this will be a 162b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik slower operation on the first call. 163b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 164b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param argument_name: 165b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The name of the query or POST argument. 166b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param default_value: 167b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The value to return if the given argument is not present. 168b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param allow_multiple: 169b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Return a list of values with the given name (deprecated). 170b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 171b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If allow_multiple is False (which it is by default), we return 172b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik the first value with the given name given in the request. If it 173b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik is True, we always return a list. 174b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 175b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik param_value = self.get_all(argument_name) 176b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if allow_multiple: 177b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik logging.warning('allow_multiple is a deprecated param. ' 178b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'Please use the Request.get_all() method instead.') 179b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 180b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if len(param_value) > 0: 181b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if allow_multiple: 182b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return param_value 183b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 184b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return param_value[0] 185b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 186b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if allow_multiple and not default_value: 187b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return [] 188b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 189b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return default_value 190b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 191b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def get_all(self, argument_name, default_value=None): 192b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns a list of query or POST arguments with the given name. 193b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 194b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik We parse the query string and POST payload lazily, so this will be a 195b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik slower operation on the first call. 196b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 197b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param argument_name: 198b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The name of the query or POST argument. 199b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param default_value: 200b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The value to return if the given argument is not present, 201b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik None may not be used as a default, if it is then an empty 202b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik list will be returned instead. 203b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 204b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A (possibly empty) list of values. 205b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 206b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if self.charset: 207b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik argument_name = argument_name.encode(self.charset) 208b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 209b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if default_value is None: 210b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik default_value = [] 211b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 212b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik param_value = self.params.getall(argument_name) 213b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 214b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if param_value is None or len(param_value) == 0: 215b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return default_value 216b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 217b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for i in xrange(len(param_value)): 218b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if isinstance(param_value[i], cgi.FieldStorage): 219b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik param_value[i] = param_value[i].value 220b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 221b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return param_value 222b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 223b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def arguments(self): 224b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns a list of the arguments provided in the query and/or POST. 225b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 226b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The return value is a list of strings. 227b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 228b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return list(set(self.params.keys())) 229b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 230b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def get_range(self, name, min_value=None, max_value=None, default=0): 231b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Parses the given int argument, limiting it to the given range. 232b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 233b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param name: 234b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The name of the argument. 235b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param min_value: 236b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The minimum int value of the argument (if any). 237b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param max_value: 238b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The maximum int value of the argument (if any). 239b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param default: 240b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The default value of the argument if it is not given. 241b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 242b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik An int within the given range for the argument. 243b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 244b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = self.get(name, default) 245b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if value is None: 246b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return value 247b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 248b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 249b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = int(value) 250b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except ValueError: 251b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = default 252b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if value is not None: 253b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if max_value is not None: 254b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = min(value, max_value) 255b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 256b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if min_value is not None: 257b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = max(value, min_value) 258b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 259b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return value 260b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 261b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik @classmethod 262b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def blank(cls, path, environ=None, base_url=None, 263b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik headers=None, **kwargs): # pragma: no cover 264b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Adds parameters compatible with WebOb >= 1.0: POST and **kwargs.""" 265b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 266b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return super(Request, cls).blank(path, environ=environ, 267b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik base_url=base_url, 268b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik headers=headers, **kwargs) 269b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except TypeError: 270b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not kwargs: 271b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise 272b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 273b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = kwargs.pop('POST', None) 274b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if data is not None: 275b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik from cStringIO import StringIO 276b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ = environ or {} 277b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ['REQUEST_METHOD'] = 'POST' 278b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if hasattr(data, 'items'): 279b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = data.items() 280b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not isinstance(data, str): 281b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = urllib.urlencode(data) 282b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ['wsgi.input'] = StringIO(data) 283b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ['webob.is_body_seekable'] = True 284b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ['CONTENT_LENGTH'] = str(len(data)) 285b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ['CONTENT_TYPE'] = 'application/x-www-form-urlencoded' 286b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 287b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik base = super(Request, cls).blank(path, environ=environ, 288b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik base_url=base_url, headers=headers) 289b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if kwargs: 290b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik obj = cls(base.environ, **kwargs) 291b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik obj.headers.update(base.headers) 292b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return obj 293b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 294b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return base 295b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 296b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 297b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass ResponseHeaders(BaseResponseHeaders): 298b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Implements methods from ``wsgiref.headers.Headers``, used by webapp.""" 299b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 300b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik get_all = BaseResponseHeaders.getall 301b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 302b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def add_header(self, _name, _value, **_params): 303b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Extended header setting. 304b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 305b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik _name is the header field to add. keyword arguments can be used to set 306b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik additional parameters for the header field, with underscores converted 307b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik to dashes. Normally the parameter will be added as key="value" unless 308b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value is None, in which case only the key will be added. 309b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 310b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Example:: 311b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 312b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik h.add_header('content-disposition', 'attachment', 313b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik filename='bud.gif') 314b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 315b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Note that unlike the corresponding 'email.message' method, this does 316b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik *not* handle '(charset, language, value)' tuples: all values must be 317b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik strings or None. 318b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 319b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik parts = [] 320b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if _value is not None: 321b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik parts.append(_value) 322b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 323b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for k, v in _params.items(): 324b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik k = k.replace('_', '-') 325b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if v is not None and len(v) > 0: 326b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik v = v.replace('\\', '\\\\').replace('"', r'\"') 327b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik parts.append('%s="%s"' % (k, v)) 328b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 329b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik parts.append(k) 330b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 331b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.add(_name, '; '.join(parts)) 332b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 333b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __str__(self): 334b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns the formatted headers ready for HTTP transmission.""" 335b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return '\r\n'.join(['%s: %s' % v for v in self.items()] + ['', '']) 336b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 337b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 338b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass Response(webob.Response): 339b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Abstraction for an HTTP response. 340b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 341b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Most extra methods and attributes are ported from webapp. Check the 342b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik `WebOb documentation <WebOb>`_ for the ones not listed here. 343b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 344b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Differences from webapp.Response: 345b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 346b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik - ``out`` is not a ``StringIO.StringIO`` instance. Instead it is the 347b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response itself, as it has the method ``write()``. 348b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik - As in WebOb, ``status`` is the code plus message, e.g., '200 OK', while 349b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik in webapp it is the integer code. The status code as an integer is 350b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik available in ``status_int``, and the status message is available in 351b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``status_message``. 352b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik - ``response.headers`` raises an exception when a key that doesn't exist 353b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik is accessed or deleted, differently from ``wsgiref.headers.Headers``. 354b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 355b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 356b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Default charset as in webapp. 357b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik default_charset = 'utf-8' 358b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 359b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, *args, **kwargs): 360b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Constructs a response with the default settings.""" 361b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik super(Response, self).__init__(*args, **kwargs) 362b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.headers['Cache-Control'] = 'no-cache' 363b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 364b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik @property 365b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def out(self): 366b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """A reference to the Response instance itself, for compatibility with 367b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik webapp only: webapp uses `Response.out.write()`, so we point `out` to 368b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik `self` and it will use `Response.write()`. 369b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 370b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self 371b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 372b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def write(self, text): 373b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Appends a text to the response body.""" 374b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # webapp uses StringIO as Response.out, so we need to convert anything 375b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # that is not str or unicode to string to keep same behavior. 376b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not isinstance(text, basestring): 377b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik text = unicode(text) 378b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 379b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if isinstance(text, unicode) and not self.charset: 380b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.charset = self.default_charset 381b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 382b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik super(Response, self).write(text) 383b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 384b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def _set_status(self, value): 385b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """The status string, including code and message.""" 386b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik message = None 387b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Accept long because urlfetch in App Engine returns codes as longs. 388b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if isinstance(value, (int, long)): 389b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik code = int(value) 390b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 391b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if isinstance(value, unicode): 392b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Status messages have to be ASCII safe, so this is OK. 393b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = str(value) 394b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 395b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not isinstance(value, str): 396b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise TypeError( 397b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'You must set status to a string or integer (not %s)' % 398b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik type(value)) 399b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 400b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik parts = value.split(' ', 1) 401b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik code = int(parts[0]) 402b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if len(parts) == 2: 403b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik message = parts[1] 404b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 405b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik message = message or Response.http_status_message(code) 406b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self._status = '%d %s' % (code, message) 407b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 408b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def _get_status(self): 409b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self._status 410b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 411b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik status = property(_get_status, _set_status, doc=_set_status.__doc__) 412b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 413b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def set_status(self, code, message=None): 414b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Sets the HTTP status code of this response. 415b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 416b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param code: 417b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The HTTP status string to use 418b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param message: 419b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A status string. If none is given, uses the default from the 420b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik HTTP/1.1 specification. 421b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 422b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if message: 423b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.status = '%d %s' % (code, message) 424b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 425b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.status = code 426b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 427b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def _get_status_message(self): 428b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """The response status message, as a string.""" 429b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self.status.split(' ', 1)[1] 430b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 431b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def _set_status_message(self, message): 432b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.status = '%d %s' % (self.status_int, message) 433b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 434b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik status_message = property(_get_status_message, _set_status_message, 435b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik doc=_get_status_message.__doc__) 436b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 437b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def _get_headers(self): 438b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """The headers as a dictionary-like object.""" 439b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if self._headers is None: 440b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self._headers = ResponseHeaders.view_list(self.headerlist) 441b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 442b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self._headers 443b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 444b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def _set_headers(self, value): 445b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if hasattr(value, 'items'): 446b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = value.items() 447b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif not isinstance(value, list): 448b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise TypeError('Response headers must be a list or dictionary.') 449b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 450b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.headerlist = value 451b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self._headers = None 452b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 453b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik headers = property(_get_headers, _set_headers, doc=_get_headers.__doc__) 454b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 455b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def has_error(self): 456b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Indicates whether the response was an error response.""" 457b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self.status_int >= 400 458b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 459b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def clear(self): 460b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Clears all data written to the output stream so that it is empty.""" 461b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.body = '' 462b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 463b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def wsgi_write(self, start_response): 464b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Writes this response using using the given WSGI function. 465b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 466b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This is only here for compatibility with ``webapp.WSGIApplication``. 467b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 468b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param start_response: 469b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The WSGI-compatible start_response function. 470b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 471b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if (self.headers.get('Cache-Control') == 'no-cache' and 472b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik not self.headers.get('Expires')): 473b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT' 474b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.headers['Content-Length'] = str(len(self.body)) 475b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 476b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik write = start_response(self.status, self.headerlist) 477b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik write(self.body) 478b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 479b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik @staticmethod 480b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def http_status_message(code): 481b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns the default HTTP status message for the given code. 482b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 483b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param code: 484b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The HTTP code for which we want a message. 485b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 486b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik message = status_reasons.get(code) 487b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not message: 488b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise KeyError('Invalid HTTP status code: %d' % code) 489b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 490b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return message 491b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 492b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 493b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass RequestHandler(object): 494b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Base HTTP request handler. 495b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 496b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Implements most of ``webapp.RequestHandler`` interface. 497b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 498b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 499b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: A :class:`Request` instance. 500b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request = None 501b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: A :class:`Response` instance. 502b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response = None 503b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: A :class:`WSGIApplication` instance. 504b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik app = None 505b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 506b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, request=None, response=None): 507b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Initializes this request handler with the given WSGI application, 508b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Request and Response. 509b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 510b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik When instantiated by ``webapp.WSGIApplication``, request and response 511b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik are not set on instantiation. Instead, initialize() is called right 512b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik after the handler is created to set them. 513b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 514b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Also in webapp dispatching is done by the WSGI app, while webapp2 515b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik does it here to allow more flexibility in extended classes: handlers 516b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik can wrap :meth:`dispatch` to check for conditions before executing the 517b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik requested method and/or post-process the response. 518b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 519b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik .. note:: 520b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Parameters are optional only to support webapp's constructor which 521b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik doesn't take any arguments. Consider them as required. 522b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 523b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param request: 524b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Request` instance. 525b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param response: 526b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Response` instance. 527b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 528b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.initialize(request, response) 529b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 530b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def initialize(self, request, response): 531b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Initializes this request handler with the given WSGI application, 532b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Request and Response. 533b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 534b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param request: 535b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Request` instance. 536b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param response: 537b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Response` instance. 538b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 539b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.request = request 540b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.response = response 541b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.app = WSGIApplication.active_instance 542b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 543b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def dispatch(self): 544b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Dispatches the request. 545b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 546b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This will first check if there's a handler_method defined in the 547b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik matched route, and if not it'll use the method correspondent to the 548b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request method (``get()``, ``post()`` etc). 549b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 550b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request = self.request 551b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik method_name = request.route.handler_method 552b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not method_name: 553b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik method_name = _normalize_handler_method(request.method) 554b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 555b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik method = getattr(self, method_name, None) 556b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if method is None: 557b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # 405 Method Not Allowed. 558b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # The response MUST include an Allow header containing a 559b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # list of valid methods for the requested resource. 560b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.6 561b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik valid = ', '.join(_get_handler_methods(self)) 562b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.abort(405, headers=[('Allow', valid)]) 563b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 564b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # The handler only receives *args if no named variables are set. 565b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args, kwargs = request.route_args, request.route_kwargs 566b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if kwargs: 567b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args = () 568b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 569b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 570b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return method(*args, **kwargs) 571b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except Exception, e: 572b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self.handle_exception(e, self.app.debug) 573b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 574b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def error(self, code): 575b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Clears the response and sets the given HTTP status code. 576b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 577b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This doesn't stop code execution; for this, use :meth:`abort`. 578b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 579b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param code: 580b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik HTTP status error code (e.g., 501). 581b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 582b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.response.status = code 583b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.response.clear() 584b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 585b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def abort(self, code, *args, **kwargs): 586b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Raises an :class:`HTTPException`. 587b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 588b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This stops code execution, leaving the HTTP exception to be handled 589b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik by an exception handler. 590b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 591b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param code: 592b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik HTTP status code (e.g., 404). 593b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param args: 594b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Positional arguments to be passed to the exception class. 595b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param kwargs: 596b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Keyword arguments to be passed to the exception class. 597b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 598b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik abort(code, *args, **kwargs) 599b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 600b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def redirect(self, uri, permanent=False, abort=False, code=None, 601b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik body=None): 602b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Issues an HTTP redirect to the given relative URI. 603b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 604b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The arguments are described in :func:`redirect`. 605b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 606b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return redirect(uri, permanent=permanent, abort=abort, code=code, 607b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik body=body, request=self.request, 608b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response=self.response) 609b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 610b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def redirect_to(self, _name, _permanent=False, _abort=False, _code=None, 611b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik _body=None, *args, **kwargs): 612b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Convenience method mixing :meth:`redirect` and :meth:`uri_for`. 613b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 614b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The arguments are described in :func:`redirect` and :func:`uri_for`. 615b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 616b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik uri = self.uri_for(_name, *args, **kwargs) 617b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self.redirect(uri, permanent=_permanent, abort=_abort, 618b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik code=_code, body=_body) 619b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 620b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def uri_for(self, _name, *args, **kwargs): 621b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns a URI for a named :class:`Route`. 622b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 623b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik .. seealso:: :meth:`Router.build`. 624b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 625b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self.app.router.build(self.request, _name, args, kwargs) 626b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Alias. 627b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik url_for = uri_for 628b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 629b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def handle_exception(self, exception, debug): 630b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Called if this handler throws an exception during execution. 631b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 632b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The default behavior is to re-raise the exception to be handled by 633b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :meth:`WSGIApplication.handle_exception`. 634b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 635b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param exception: 636b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The exception that was thrown. 637b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param debug_mode: 638b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik True if the web application is running in debug mode. 639b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 640b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise 641b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 642b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 643b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass RedirectHandler(RequestHandler): 644b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Redirects to the given URI for all GET requests. 645b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 646b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This is intended to be used when defining URI routes. You must provide at 647b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik least the keyword argument *url* in the route default values. Example:: 648b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 649b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def get_redirect_url(handler, *args, **kwargs): 650b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return handler.uri_for('new-route-name') 651b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 652b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik app = WSGIApplication([ 653b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Route('/old-url', RedirectHandler, defaults={'_uri': '/new-url'}), 654b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Route('/other-old-url', RedirectHandler, defaults={ 655b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '_uri': get_redirect_url}), 656b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ]) 657b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 658b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Based on idea from `Tornado`_. 659b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 660b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 661b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def get(self, *args, **kwargs): 662b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Performs a redirect. 663b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 664b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Two keyword arguments can be passed through the URI route: 665b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 666b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik - **_uri**: A URI string or a callable that returns a URI. The callable 667b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik is called passing ``(handler, *args, **kwargs)`` as arguments. 668b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik - **_code**: The redirect status code. Default is 301 (permanent 669b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik redirect). 670b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 671b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik uri = kwargs.pop('_uri', '/') 672b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik permanent = kwargs.pop('_permanent', True) 673b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik code = kwargs.pop('_code', None) 674b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 675b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik func = getattr(uri, '__call__', None) 676b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if func: 677b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik uri = func(self, *args, **kwargs) 678b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 679b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.redirect(uri, permanent=permanent, code=code) 680b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 681b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 682b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass cached_property(object): 683b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """A decorator that converts a function into a lazy property. 684b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 685b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The function wrapped is called the first time to retrieve the result 686b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik and then that calculated result is used the next time you access 687b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik the value:: 688b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 689b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik class Foo(object): 690b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 691b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik @cached_property 692b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def foo(self): 693b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # calculate something important here 694b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return 42 695b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 696b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The class has to have a `__dict__` in order for this property to 697b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik work. 698b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 699b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik .. note:: Implementation detail: this property is implemented as non-data 700b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik descriptor. non-data descriptors are only invoked if there is 701b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik no entry with the same name in the instance's __dict__. 702b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik this allows us to completely get rid of the access function call 703b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik overhead. If one choses to invoke __get__ by hand the property 704b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik will still work as expected because the lookup logic is replicated 705b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik in __get__ for manual invocation. 706b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 707b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This class was ported from `Werkzeug`_ and `Flask`_. 708b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 709b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 710b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik _default_value = object() 711b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 712b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, func, name=None, doc=None): 713b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.__name__ = name or func.__name__ 714b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.__module__ = func.__module__ 715b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.__doc__ = doc or func.__doc__ 716b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.func = func 717b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.lock = threading.RLock() 718b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 719b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __get__(self, obj, type=None): 720b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if obj is None: 721b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self 722b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 723b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik with self.lock: 724b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = obj.__dict__.get(self.__name__, self._default_value) 725b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if value is self._default_value: 726b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = self.func(obj) 727b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik obj.__dict__[self.__name__] = value 728b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 729b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return value 730b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 731b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 732b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass BaseRoute(object): 733b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Interface for URI routes.""" 734b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 735b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: The regex template. 736b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik template = None 737b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Route name, used to build URIs. 738b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik name = None 739b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: True if this route is only used for URI generation and never matches. 740b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik build_only = False 741b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: The handler or string in dotted notation to be lazily imported. 742b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler = None 743b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: The custom handler method, if handler is a class. 744b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler_method = None 745b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: The handler, imported and ready for dispatching. 746b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler_adapter = None 747b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 748b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, template, handler=None, name=None, build_only=False): 749b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Initializes this route. 750b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 751b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param template: 752b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A regex to be matched. 753b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param handler: 754b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A callable or string in dotted notation to be lazily imported, 755b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik e.g., ``'my.module.MyHandler'`` or ``'my.module.my_function'``. 756b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param name: 757b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The name of this route, used to build URIs based on it. 758b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param build_only: 759b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If True, this route never matches and is used only to build URIs. 760b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 761b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if build_only and name is None: 762b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise ValueError( 763b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik "Route %r is build_only but doesn't have a name." % self) 764b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 765b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.template = template 766b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.handler = handler 767b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.name = name 768b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.build_only = build_only 769b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 770b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def match(self, request): 771b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Matches all routes against a request object. 772b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 773b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The first one that matches is returned. 774b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 775b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param request: 776b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Request` instance. 777b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 778b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A tuple ``(route, args, kwargs)`` if a route matched, or None. 779b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 780b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise NotImplementedError() 781b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 782b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def build(self, request, args, kwargs): 783b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns a URI for this route. 784b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 785b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param request: 786b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The current :class:`Request` object. 787b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param args: 788b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Tuple of positional arguments to build the URI. 789b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param kwargs: 790b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Dictionary of keyword arguments to build the URI. 791b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 792b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik An absolute or relative URI. 793b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 794b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise NotImplementedError() 795b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 796b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def get_routes(self): 797b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Generator to get all routes from a route. 798b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 799b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :yields: 800b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This route or all nested routes that it contains. 801b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 802b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik yield self 803b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 804b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def get_match_routes(self): 805b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Generator to get all routes that can be matched from a route. 806b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 807b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Match routes must implement :meth:`match`. 808b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 809b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :yields: 810b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This route or all nested routes that can be matched. 811b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 812b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not self.build_only: 813b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik yield self 814b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 815b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def get_build_routes(self): 816b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Generator to get all routes that can be built from a route. 817b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 818b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Build routes must implement :meth:`build`. 819b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 820b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :yields: 821b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A tuple ``(name, route)`` for all nested routes that can be built. 822b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 823b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if self.name is not None: 824b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik yield self.name, self 825b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 826b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 827b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass SimpleRoute(BaseRoute): 828b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """A route that is compatible with webapp's routing mechanism. 829b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 830b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik URI building is not implemented as webapp has rudimentar support for it, 831b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik and this is the most unknown webapp feature anyway. 832b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 833b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 834b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik @cached_property 835b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def regex(self): 836b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Lazy regex compiler.""" 837b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not self.template.startswith('^'): 838b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.template = '^' + self.template 839b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 840b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not self.template.endswith('$'): 841b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.template += '$' 842b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 843b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return re.compile(self.template) 844b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 845b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def match(self, request): 846b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Matches this route against the current request. 847b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 848b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik .. seealso:: :meth:`BaseRoute.match`. 849b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 850b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik match = self.regex.match(urllib.unquote(request.path)) 851b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if match: 852b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self, match.groups(), {} 853b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 854b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __repr__(self): 855b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return '<SimpleRoute(%r, %r)>' % (self.template, self.handler) 856b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 857b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 858b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass Route(BaseRoute): 859b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """A route definition that maps a URI path to a handler. 860b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 861b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The initial concept was based on `Another Do-It-Yourself Framework`_, by 862b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Ian Bicking. 863b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 864b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 865b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Default parameters values. 866b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik defaults = None 867b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Sequence of allowed HTTP methods. If not set, all methods are allowed. 868b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik methods = None 869b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Sequence of allowed URI schemes. If not set, all schemes are allowed. 870b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik schemes = None 871b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Lazy properties extracted from the route template. 872b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik regex = None 873b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik reverse_template = None 874b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik variables = None 875b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args_count = 0 876b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik kwargs_count = 0 877b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 878b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, template, handler=None, name=None, defaults=None, 879b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik build_only=False, handler_method=None, methods=None, 880b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik schemes=None): 881b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Initializes this route. 882b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 883b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param template: 884b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A route template to match against the request path. A template 885b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik can have variables enclosed by ``<>`` that define a name, a 886b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik regular expression or both. Examples: 887b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 888b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ================= ================================== 889b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Format Example 890b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ================= ================================== 891b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``<name>`` ``'/blog/<year>/<month>'`` 892b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``<:regex>`` ``'/blog/<:\d{4}>/<:\d{2}>'`` 893b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``<name:regex>`` ``'/blog/<year:\d{4}>/<month:\d{2}>'`` 894b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ================= ================================== 895b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 896b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The same template can mix parts with name, regular expression or 897b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik both. 898b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 899b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If the name is set, the value of the matched regular expression 900b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik is passed as keyword argument to the handler. Otherwise it is 901b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik passed as positional argument. 902b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 903b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If only the name is set, it will match anything except a slash. 904b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik So these routes are equivalent:: 905b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 906b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Route('/<user_id>/settings', handler=SettingsHandler, 907b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik name='user-settings') 908b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Route('/<user_id:[^/]+>/settings', handler=SettingsHandler, 909b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik name='user-settings') 910b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 911b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik .. note:: 912b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The handler only receives ``*args`` if no named variables are 913b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik set. Otherwise, the handler only receives ``**kwargs``. This 914b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik allows you to set regular expressions that are not captured: 915b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik just mix named and unnamed variables and the handler will 916b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik only receive the named ones. 917b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 918b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param handler: 919b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A callable or string in dotted notation to be lazily imported, 920b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik e.g., ``'my.module.MyHandler'`` or ``'my.module.my_function'``. 921b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik It is possible to define a method if the callable is a class, 922b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik separating it by a colon: ``'my.module.MyHandler:my_method'``. 923b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This is a shortcut and has the same effect as defining the 924b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik `handler_method` parameter. 925b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param name: 926b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The name of this route, used to build URIs based on it. 927b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param defaults: 928b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Default or extra keywords to be returned by this route. Values 929b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik also present in the route variables are used to build the URI 930b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik when they are missing. 931b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param build_only: 932b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If True, this route never matches and is used only to build URIs. 933b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param handler_method: 934b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The name of a custom handler method to be called, in case `handler` 935b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik is a class. If not defined, the default behavior is to call the 936b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler method correspondent to the HTTP request method in lower 937b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik case (e.g., `get()`, `post()` etc). 938b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param methods: 939b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A sequence of HTTP methods. If set, the route will only match if 940b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik the request method is allowed. 941b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param schemes: 942b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A sequence of URI schemes, e.g., ``['http']`` or ``['https']``. 943b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If set, the route will only match requests with these schemes. 944b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 945b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik super(Route, self).__init__(template, handler=handler, name=name, 946b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik build_only=build_only) 947b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.defaults = defaults or {} 948b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.methods = methods 949b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.schemes = schemes 950b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if isinstance(handler, basestring) and ':' in handler: 951b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if handler_method: 952b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise ValueError( 953b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik "If handler_method is defined in a Route, handler " 954b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik "can't have a colon (got %r)." % handler) 955b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 956b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.handler, self.handler_method = handler.rsplit(':', 1) 957b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 958b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.handler_method = handler_method 959b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 960b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik @cached_property 961b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def regex(self): 962b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Lazy route template parser.""" 963b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik regex, self.reverse_template, self.args_count, self.kwargs_count, \ 964b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.variables = _parse_route_template(self.template, 965b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik default_sufix='[^/]+') 966b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return regex 967b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 968b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def match(self, request): 969b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Matches this route against the current request. 970b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 971b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :raises: 972b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``exc.HTTPMethodNotAllowed`` if the route defines :attr:`methods` 973b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik and the request method isn't allowed. 974b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 975b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik .. seealso:: :meth:`BaseRoute.match`. 976b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 977b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik match = self.regex.match(urllib.unquote(request.path)) 978b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not match or self.schemes and request.scheme not in self.schemes: 979b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return None 980b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 981b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if self.methods and request.method not in self.methods: 982b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # This will be caught by the router, so routes with different 983b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # methods can be tried. 984b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise exc.HTTPMethodNotAllowed() 985b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 986b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args, kwargs = _get_route_variables(match, self.defaults.copy()) 987b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self, args, kwargs 988b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 989b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def build(self, request, args, kwargs): 990b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns a URI for this route. 991b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 992b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik .. seealso:: :meth:`Router.build`. 993b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 994b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik scheme = kwargs.pop('_scheme', None) 995b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik netloc = kwargs.pop('_netloc', None) 996b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik anchor = kwargs.pop('_fragment', None) 997b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik full = kwargs.pop('_full', False) and not scheme and not netloc 998b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 999b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if full or scheme or netloc: 1000b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik netloc = netloc or request.host 1001b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik scheme = scheme or request.scheme 1002b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1003b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik path, query = self._build(args, kwargs) 1004b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return _urlunsplit(scheme, netloc, path, query, anchor) 1005b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1006b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def _build(self, args, kwargs): 1007b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns the URI path for this route. 1008b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1009b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1010b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A tuple ``(path, kwargs)`` with the built URI path and extra 1011b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik keywords to be used as URI query arguments. 1012b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1013b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Access self.regex just to set the lazy properties. 1014b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik regex = self.regex 1015b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik variables = self.variables 1016b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if self.args_count: 1017b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for index, value in enumerate(args): 1018b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik key = '__%d__' % index 1019b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if key in variables: 1020b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik kwargs[key] = value 1021b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1022b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik values = {} 1023b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for name, regex in variables.iteritems(): 1024b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = kwargs.pop(name, self.defaults.get(name)) 1025b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if value is None: 1026b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise KeyError('Missing argument "%s" to build URI.' % \ 1027b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik name.strip('_')) 1028b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1029b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not isinstance(value, basestring): 1030b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = str(value) 1031b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1032b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not regex.match(value): 1033b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise ValueError('URI buiding error: Value "%s" is not ' 1034b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'supported for argument "%s".' % (value, name.strip('_'))) 1035b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1036b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik values[name] = value 1037b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1038b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return (self.reverse_template % values, kwargs) 1039b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1040b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __repr__(self): 1041b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return '<Route(%r, %r, name=%r, defaults=%r, build_only=%r)>' % \ 1042b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (self.template, self.handler, self.name, self.defaults, 1043b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.build_only) 1044b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1045b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1046b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass BaseHandlerAdapter(object): 1047b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """A basic adapter to dispatch a handler. 1048b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1049b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This is used when the handler is a simple function: it just calls the 1050b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler and returns the resulted response. 1051b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1052b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1053b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: The handler to be dispatched. 1054b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler = None 1055b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1056b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, handler): 1057b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.handler = handler 1058b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1059b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __call__(self, request, response): 1060b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # The handler only receives *args if no named variables are set. 1061b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args, kwargs = request.route_args, request.route_kwargs 1062b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if kwargs: 1063b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args = () 1064b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1065b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self.handler(request, *args, **kwargs) 1066b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1067b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1068b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass WebappHandlerAdapter(BaseHandlerAdapter): 1069b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """An adapter to dispatch a ``webapp.RequestHandler``. 1070b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1071b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Like in webapp, the handler is constructed, then ``initialize()`` is 1072b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik called, then the method corresponding to the HTTP request method is called. 1073b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1074b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1075b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __call__(self, request, response): 1076b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler = self.handler() 1077b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler.initialize(request, response) 1078b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik method_name = _normalize_handler_method(request.method) 1079b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik method = getattr(handler, method_name, None) 1080b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not method: 1081b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik abort(501) 1082b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1083b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # The handler only receives *args if no named variables are set. 1084b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args, kwargs = request.route_args, request.route_kwargs 1085b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if kwargs: 1086b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args = () 1087b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1088b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 1089b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik method(*args, **kwargs) 1090b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except Exception, e: 1091b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler.handle_exception(e, request.app.debug) 1092b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1093b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1094b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass Webapp2HandlerAdapter(BaseHandlerAdapter): 1095b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """An adapter to dispatch a ``webapp2.RequestHandler``. 1096b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1097b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The handler is constructed then ``dispatch()`` is called. 1098b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1099b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1100b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __call__(self, request, response): 1101b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler = self.handler(request, response) 1102b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return handler.dispatch() 1103b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1104b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1105b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass Router(object): 1106b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """A URI router used to match, dispatch and build URIs.""" 1107b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1108b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Class used when the route is set as a tuple. 1109b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik route_class = SimpleRoute 1110b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: All routes that can be matched. 1111b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik match_routes = None 1112b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: All routes that can be built. 1113b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik build_routes = None 1114b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Handler classes imported lazily. 1115b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handlers = None 1116b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1117b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, routes=None): 1118b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Initializes the router. 1119b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1120b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param routes: 1121b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A sequence of :class:`Route` instances or, for simple routes, 1122b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik tuples ``(regex, handler)``. 1123b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1124b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.match_routes = [] 1125b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.build_routes = {} 1126b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.handlers = {} 1127b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if routes: 1128b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for route in routes: 1129b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.add(route) 1130b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1131b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def add(self, route): 1132b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Adds a route to this router. 1133b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1134b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param route: 1135b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Route` instance or, for simple routes, a tuple 1136b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``(regex, handler)``. 1137b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1138b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if isinstance(route, tuple): 1139b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Exceptional case: simple routes defined as a tuple. 1140b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik route = self.route_class(*route) 1141b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1142b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for r in route.get_match_routes(): 1143b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.match_routes.append(r) 1144b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1145b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for name, r in route.get_build_routes(): 1146b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.build_routes[name] = r 1147b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1148b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def set_matcher(self, func): 1149b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Sets the function called to match URIs. 1150b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1151b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param func: 1152b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A function that receives ``(router, request)`` and returns 1153b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik a tuple ``(route, args, kwargs)`` if any route matches, or 1154b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise ``exc.HTTPNotFound`` if no route matched or 1155b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``exc.HTTPMethodNotAllowed`` if a route matched but the HTTP 1156b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik method was not allowed. 1157b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1158b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Functions are descriptors, so bind it to this instance with __get__. 1159b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.match = func.__get__(self, self.__class__) 1160b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1161b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def set_builder(self, func): 1162b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Sets the function called to build URIs. 1163b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1164b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param func: 1165b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A function that receives ``(router, request, name, args, kwargs)`` 1166b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik and returns a URI. 1167b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1168b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.build = func.__get__(self, self.__class__) 1169b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1170b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def set_dispatcher(self, func): 1171b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Sets the function called to dispatch the handler. 1172b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1173b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param func: 1174b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A function that receives ``(router, request, response)`` 1175b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik and returns the value returned by the dispatched handler. 1176b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1177b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.dispatch = func.__get__(self, self.__class__) 1178b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1179b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def set_adapter(self, func): 1180b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Sets the function that adapts loaded handlers for dispatching. 1181b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1182b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param func: 1183b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A function that receives ``(router, handler)`` and returns a 1184b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler callable. 1185b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1186b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.adapt = func.__get__(self, self.__class__) 1187b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1188b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def default_matcher(self, request): 1189b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Matches all routes against a request object. 1190b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1191b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The first one that matches is returned. 1192b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1193b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param request: 1194b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Request` instance. 1195b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1196b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A tuple ``(route, args, kwargs)`` if a route matched, or None. 1197b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :raises: 1198b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``exc.HTTPNotFound`` if no route matched or 1199b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``exc.HTTPMethodNotAllowed`` if a route matched but the HTTP 1200b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik method was not allowed. 1201b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1202b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik method_not_allowed = False 1203b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for route in self.match_routes: 1204b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 1205b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik match = route.match(request) 1206b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if match: 1207b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return match 1208b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except exc.HTTPMethodNotAllowed: 1209b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik method_not_allowed = True 1210b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1211b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if method_not_allowed: 1212b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise exc.HTTPMethodNotAllowed() 1213b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1214b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise exc.HTTPNotFound() 1215b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1216b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def default_builder(self, request, name, args, kwargs): 1217b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns a URI for a named :class:`Route`. 1218b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1219b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param request: 1220b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The current :class:`Request` object. 1221b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param name: 1222b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The route name. 1223b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param args: 1224b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Tuple of positional arguments to build the URI. All positional 1225b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik variables defined in the route must be passed and must conform 1226b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik to the format set in the route. Extra arguments are ignored. 1227b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param kwargs: 1228b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Dictionary of keyword arguments to build the URI. All variables 1229b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik not set in the route default values must be passed and must 1230b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik conform to the format set in the route. Extra keywords are 1231b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik appended as a query string. 1232b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1233b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A few keywords have special meaning: 1234b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1235b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik - **_full**: If True, builds an absolute URI. 1236b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik - **_scheme**: URI scheme, e.g., `http` or `https`. If defined, 1237b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik an absolute URI is always returned. 1238b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik - **_netloc**: Network location, e.g., `www.google.com`. If 1239b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik defined, an absolute URI is always returned. 1240b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik - **_fragment**: If set, appends a fragment (or "anchor") to the 1241b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik generated URI. 1242b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1243b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik An absolute or relative URI. 1244b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1245b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik route = self.build_routes.get(name) 1246b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if route is None: 1247b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise KeyError('Route named %r is not defined.' % name) 1248b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1249b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return route.build(request, args, kwargs) 1250b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1251b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def default_dispatcher(self, request, response): 1252b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Dispatches a handler. 1253b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1254b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param request: 1255b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Request` instance. 1256b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param response: 1257b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Response` instance. 1258b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :raises: 1259b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``exc.HTTPNotFound`` if no route matched or 1260b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``exc.HTTPMethodNotAllowed`` if a route matched but the HTTP 1261b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik method was not allowed. 1262b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1263b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The returned value from the handler. 1264b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1265b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik route, args, kwargs = rv = self.match(request) 1266b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request.route, request.route_args, request.route_kwargs = rv 1267b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1268b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if route.handler_adapter is None: 1269b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler = route.handler 1270b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if isinstance(handler, basestring): 1271b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if handler not in self.handlers: 1272b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.handlers[handler] = handler = import_string(handler) 1273b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1274b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler = self.handlers[handler] 1275b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1276b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik route.handler_adapter = self.adapt(handler) 1277b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1278b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return route.handler_adapter(request, response) 1279b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1280b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def default_adapter(self, handler): 1281b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Adapts a handler for dispatching. 1282b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1283b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Because handlers use or implement different dispatching mechanisms, 1284b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik they can be wrapped to use a unified API for dispatching. 1285b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This way webapp2 can support, for example, a :class:`RequestHandler` 1286b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik class and function views or, for compatibility purposes, a 1287b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``webapp.RequestHandler`` class. The adapters follow the same router 1288b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dispatching API but dispatch each handler type differently. 1289b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1290b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param handler: 1291b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A handler callable. 1292b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1293b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A wrapped handler callable. 1294b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1295b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if inspect.isclass(handler): 1296b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if _webapp and issubclass(handler, _webapp.RequestHandler): 1297b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Compatible with webapp.RequestHandler. 1298b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik adapter = WebappHandlerAdapter 1299b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1300b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Default, compatible with webapp2.RequestHandler. 1301b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik adapter = Webapp2HandlerAdapter 1302b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1303b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # A "view" function. 1304b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik adapter = BaseHandlerAdapter 1305b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1306b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return adapter(handler) 1307b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1308b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __repr__(self): 1309b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik routes = self.match_routes + [v for k, v in \ 1310b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.build_routes.iteritems() if v not in self.match_routes] 1311b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1312b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return '<Router(%r)>' % routes 1313b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1314b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Default matcher, builder, dispatcher and adapter. 1315b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik match = default_matcher 1316b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik build = default_builder 1317b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dispatch = default_dispatcher 1318b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik adapt = default_adapter 1319b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1320b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1321b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass Config(dict): 1322b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """A simple configuration dictionary for the :class:`WSGIApplication`.""" 1323b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1324b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Loaded configurations. 1325b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik loaded = None 1326b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1327b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, defaults=None): 1328b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dict.__init__(self, defaults or ()) 1329b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.loaded = [] 1330b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1331b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def load_config(self, key, default_values=None, user_values=None, 1332b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik required_keys=None): 1333b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns a configuration for a given key. 1334b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1335b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This can be used by objects that define a default configuration. It 1336b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik will update the app configuration with the default values the first 1337b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik time it is requested, and mark the key as loaded. 1338b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1339b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param key: 1340b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A configuration key. 1341b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param default_values: 1342b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Default values defined by a module or class. 1343b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param user_values: 1344b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik User values, used when an object can be initialized with 1345b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik configuration. This overrides the app configuration. 1346b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param required_keys: 1347b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Keys that can not be None. 1348b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :raises: 1349b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Exception, when a required key is not set or is None. 1350b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1351b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if key in self.loaded: 1352b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik config = self[key] 1353b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1354b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik config = dict(default_values or ()) 1355b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if key in self: 1356b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik config.update(self[key]) 1357b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1358b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self[key] = config 1359b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.loaded.append(key) 1360b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if required_keys and not user_values: 1361b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self._validate_required(key, config, required_keys) 1362b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1363b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if user_values: 1364b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik config = config.copy() 1365b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik config.update(user_values) 1366b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if required_keys: 1367b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self._validate_required(key, config, required_keys) 1368b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1369b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return config 1370b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1371b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def _validate_required(self, key, config, required_keys): 1372b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik missing = [k for k in required_keys if config.get(k) is None] 1373b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if missing: 1374b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise Exception( 1375b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'Missing configuration keys for %r: %r.' % (key, missing)) 1376b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1377b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1378b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass RequestContext(object): 1379b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Context for a single request. 1380b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1381b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The context is responsible for setting and cleaning global variables for 1382b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik a request. 1383b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1384b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1385b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: A :class:`WSGIApplication` instance. 1386b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik app = None 1387b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: WSGI environment dictionary. 1388b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ = None 1389b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1390b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, app, environ): 1391b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Initializes the request context. 1392b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1393b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param app: 1394b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik An :class:`WSGIApplication` instance. 1395b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param environ: 1396b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A WSGI environment dictionary. 1397b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1398b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.app = app 1399b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.environ = environ 1400b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1401b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __enter__(self): 1402b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Enters the request context. 1403b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1404b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1405b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A tuple ``(request, response)``. 1406b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1407b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Build request and response. 1408b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request = self.app.request_class(self.environ) 1409b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response = self.app.response_class() 1410b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Make active app and response available through the request object. 1411b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request.app = self.app 1412b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request.response = response 1413b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Register global variables. 1414b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.app.set_globals(app=self.app, request=request) 1415b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return request, response 1416b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1417b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __exit__(self, exc_type, exc_value, traceback): 1418b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Exits the request context. 1419b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1420b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This release the context locals except if an exception is caught 1421b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik in debug mode. In this case they are kept to be inspected. 1422b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1423b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if exc_type is None or not self.app.debug: 1424b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Unregister global variables. 1425b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.app.clear_globals() 1426b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1427b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1428b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass WSGIApplication(object): 1429b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """A WSGI-compliant application.""" 1430b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1431b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Allowed request methods. 1432b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik allowed_methods = frozenset(('GET', 'POST', 'HEAD', 'OPTIONS', 'PUT', 1433b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'DELETE', 'TRACE')) 1434b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Class used for the request object. 1435b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request_class = Request 1436b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Class used for the response object. 1437b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response_class = Response 1438b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Class used for the router object. 1439b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik router_class = Router 1440b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Class used for the request context object. 1441b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request_context_class = RequestContext 1442b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Class used for the configuration object. 1443b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik config_class = Config 1444b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: A general purpose flag to indicate development mode: if True, uncaught 1445b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: exceptions are raised instead of using ``HTTPInternalServerError``. 1446b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik debug = False 1447b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: A :class:`Router` instance with all URIs registered for the application. 1448b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik router = None 1449b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: A :class:`Config` instance with the application configuration. 1450b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik config = None 1451b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: A dictionary to register objects used during the app lifetime. 1452b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik registry = None 1453b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: A dictionary mapping HTTP error codes to callables to handle those 1454b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: HTTP exceptions. See :meth:`handle_exception`. 1455b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik error_handlers = None 1456b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Active :class:`WSGIApplication` instance. See :meth:`set_globals`. 1457b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik app = None 1458b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Active :class:`Request` instance. See :meth:`set_globals`. 1459b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request = None 1460b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Same as :attr:`app`, for webapp compatibility. See :meth:`set_globals`. 1461b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik active_instance = None 1462b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1463b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, routes=None, debug=False, config=None): 1464b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Initializes the WSGI application. 1465b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1466b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param routes: 1467b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A sequence of :class:`Route` instances or, for simple routes, 1468b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik tuples ``(regex, handler)``. 1469b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param debug: 1470b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik True to enable debug mode, False otherwise. 1471b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param config: 1472b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A configuration dictionary for the application. 1473b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1474b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.debug = debug 1475b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.registry = {} 1476b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.error_handlers = {} 1477b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.set_globals(app=self) 1478b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.config = self.config_class(config) 1479b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.router = self.router_class(routes) 1480b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1481b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def set_globals(self, app=None, request=None): 1482b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Registers the global variables for app and request. 1483b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1484b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If :mod:`webapp2_extras.local` is available the app and request 1485b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik class attributes are assigned to a proxy object that returns them 1486b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik using thread-local, making the application thread-safe. This can also 1487b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik be used in environments that don't support threading. 1488b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1489b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If :mod:`webapp2_extras.local` is not available app and request will 1490b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik be assigned directly as class attributes. This should only be used in 1491b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik non-threaded environments (e.g., App Engine Python 2.5). 1492b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1493b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param app: 1494b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`WSGIApplication` instance. 1495b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param request: 1496b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Request` instance. 1497b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1498b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if _local is not None: # pragma: no cover 1499b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik _local.app = app 1500b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik _local.request = request 1501b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: # pragma: no cover 1502b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik WSGIApplication.app = WSGIApplication.active_instance = app 1503b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik WSGIApplication.request = request 1504b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1505b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def clear_globals(self): 1506b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Clears global variables. See :meth:`set_globals`.""" 1507b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if _local is not None: # pragma: no cover 1508b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik _local.__release_local__() 1509b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: # pragma: no cover 1510b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik WSGIApplication.app = WSGIApplication.active_instance = None 1511b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik WSGIApplication.request = None 1512b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1513b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __call__(self, environ, start_response): 1514b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Called by WSGI when a request comes in. 1515b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1516b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param environ: 1517b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A WSGI environment. 1518b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param start_response: 1519b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A callable accepting a status code, a list of headers and an 1520b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik optional exception context to start the response. 1521b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1522b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik An iterable with the response to return to the client. 1523b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1524b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik with self.request_context_class(self, environ) as (request, response): 1525b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 1526b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if request.method not in self.allowed_methods: 1527b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # 501 Not Implemented. 1528b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise exc.HTTPNotImplemented() 1529b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1530b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik rv = self.router.dispatch(request, response) 1531b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if rv is not None: 1532b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response = rv 1533b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except Exception, e: 1534b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 1535b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Try to handle it with a custom error handler. 1536b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik rv = self.handle_exception(request, response, e) 1537b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if rv is not None: 1538b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response = rv 1539b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except HTTPException, e: 1540b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Use the HTTP exception as response. 1541b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response = e 1542b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except Exception, e: 1543b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Error wasn't handled so we have nothing else to do. 1544b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response = self._internal_error(e) 1545b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1546b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 1547b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return response(environ, start_response) 1548b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except Exception, e: 1549b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self._internal_error(e)(environ, start_response) 1550b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1551b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def _internal_error(self, exception): 1552b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Last resource error for :meth:`__call__`.""" 1553b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik logging.exception(exception) 1554b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if self.debug: 1555b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik lines = ''.join(traceback.format_exception(*sys.exc_info())) 1556b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik html = _debug_template % (cgi.escape(lines, quote=True)) 1557b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return Response(body=html, status=500) 1558b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1559b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return exc.HTTPInternalServerError() 1560b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1561b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def handle_exception(self, request, response, e): 1562b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Handles a uncaught exception occurred in :meth:`__call__`. 1563b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1564b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Uncaught exceptions can be handled by error handlers registered in 1565b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :attr:`error_handlers`. This is a dictionary that maps HTTP status 1566b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik codes to callables that will handle the corresponding error code. 1567b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If the exception is not an ``HTTPException``, the status code 500 1568b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik is used. 1569b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1570b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The error handlers receive (request, response, exception) and can be 1571b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik a callable or a string in dotted notation to be lazily imported. 1572b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1573b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If no error handler is found, the exception is re-raised. 1574b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1575b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Based on idea from `Flask`_. 1576b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1577b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param request: 1578b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Request` instance. 1579b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param response: 1580b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Response` instance. 1581b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param e: 1582b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The uncaught exception. 1583b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1584b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The returned value from the error handler. 1585b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1586b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if isinstance(e, HTTPException): 1587b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik code = e.code 1588b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1589b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik code = 500 1590b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1591b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handler = self.error_handlers.get(code) 1592b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if handler: 1593b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if isinstance(handler, basestring): 1594b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.error_handlers[code] = handler = import_string(handler) 1595b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1596b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return handler(request, response, e) 1597b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1598b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Re-raise it to be caught by the WSGI app. 1599b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise 1600b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1601b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def run(self, bare=False): 1602b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Runs this WSGI-compliant application in a CGI environment. 1603b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1604b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This uses functions provided by ``google.appengine.ext.webapp.util``, 1605b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if available: ``run_bare_wsgi_app`` and ``run_wsgi_app``. 1606b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1607b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Otherwise, it uses ``wsgiref.handlers.CGIHandler().run()``. 1608b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1609b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param bare: 1610b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If True, doesn't add registered WSGI middleware: use 1611b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``run_bare_wsgi_app`` instead of ``run_wsgi_app``. 1612b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1613b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if _webapp_util: 1614b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if bare: 1615b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik _webapp_util.run_bare_wsgi_app(self) 1616b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1617b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik _webapp_util.run_wsgi_app(self) 1618b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: # pragma: no cover 1619b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik handlers.CGIHandler().run(self) 1620b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1621b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def get_response(self, *args, **kwargs): 1622b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Creates a request and returns a response for this app. 1623b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1624b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This is a convenience for unit testing purposes. It receives 1625b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik parameters to build a request and calls the application, returning 1626b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik the resulting response:: 1627b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1628b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik class HelloHandler(webapp2.RequestHandler): 1629b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def get(self): 1630b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.response.write('Hello, world!') 1631b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1632b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik app = webapp2.WSGIapplication([('/', HelloHandler)]) 1633b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1634b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Test the app, passing parameters to build a request. 1635b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response = app.get_response('/') 1636b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik assert response.status_int == 200 1637b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik assert response.body == 'Hello, world!' 1638b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1639b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param args: 1640b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Positional arguments to be passed to ``Request.blank()``. 1641b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param kwargs: 1642b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Keyword arguments to be passed to ``Request.blank()``. 1643b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1644b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Response` object. 1645b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1646b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self.request_class.blank(*args, **kwargs).get_response(self) 1647b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1648b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1649b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_import_string_error = """\ 1650b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport_string() failed for %r. Possible reasons are: 1651b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1652b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik- missing __init__.py in a package; 1653b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik- package or module path not included in sys.path; 1654b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik- duplicated package or module name taking precedence in sys.path; 1655b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik- missing module, class, function or variable; 1656b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1657b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikOriginal exception: 1658b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1659b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik%s: %s 1660b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1661b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikDebugged import: 1662b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1663b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik%s""" 1664b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1665b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1666b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass ImportStringError(Exception): 1667b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Provides information about a failed :func:`import_string` attempt.""" 1668b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1669b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: String in dotted notation that failed to be imported. 1670b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik import_name = None 1671b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #: Wrapped exception. 1672b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exception = None 1673b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1674b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, import_name, exception): 1675b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.import_name = import_name 1676b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.exception = exception 1677b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik msg = _import_string_error 1678b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik name = '' 1679b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik tracked = [] 1680b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for part in import_name.split('.'): 1681b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik name += (name and '.') + part 1682b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik imported = import_string(name, silent=True) 1683b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if imported: 1684b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik tracked.append((name, imported.__file__)) 1685b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1686b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik track = ['- %r found in %r.' % rv for rv in tracked] 1687b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik track.append('- %r not found.' % name) 1688b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik msg = msg % (import_name, exception.__class__.__name__, 1689b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik str(exception), '\n'.join(track)) 1690b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik break 1691b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1692b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Exception.__init__(self, msg) 1693b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1694b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1695b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_get_app_error = 'WSGIApplication global variable is not set.' 1696b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_get_request_error = 'Request global variable is not set.' 1697b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1698b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1699b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef get_app(): 1700b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns the active app instance. 1701b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1702b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1703b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`WSGIApplication` instance. 1704b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1705b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if _local: 1706b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik assert getattr(_local, 'app', None) is not None, _get_app_error 1707b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1708b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik assert WSGIApplication.app is not None, _get_app_error 1709b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1710b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return WSGIApplication.app 1711b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1712b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1713b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef get_request(): 1714b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns the active request instance. 1715b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1716b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1717b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Request` instance. 1718b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1719b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if _local: 1720b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik assert getattr(_local, 'request', None) is not None, _get_request_error 1721b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1722b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik assert WSGIApplication.request is not None, _get_request_error 1723b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1724b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return WSGIApplication.request 1725b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1726b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1727b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef uri_for(_name, _request=None, *args, **kwargs): 1728b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """A standalone uri_for version that can be passed to templates. 1729b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1730b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik .. seealso:: :meth:`Router.build`. 1731b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1732b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request = _request or get_request() 1733b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return request.app.router.build(request, _name, args, kwargs) 1734b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1735b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1736b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef redirect(uri, permanent=False, abort=False, code=None, body=None, 1737b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request=None, response=None): 1738b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Issues an HTTP redirect to the given relative URI. 1739b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1740b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This won't stop code execution unless **abort** is True. A common 1741b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik practice is to return when calling this method:: 1742b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1743b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return redirect('/some-path') 1744b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1745b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param uri: 1746b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A relative or absolute URI (e.g., ``'../flowers.html'``). 1747b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param permanent: 1748b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If True, uses a 301 redirect instead of a 302 redirect. 1749b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param abort: 1750b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If True, raises an exception to perform the redirect. 1751b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param code: 1752b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The redirect status code. Supported codes are 301, 302, 303, 305, 1753b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik and 307. 300 is not supported because it's not a real redirect 1754b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik and 304 because it's the answer for a request with defined 1755b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``If-Modified-Since`` headers. 1756b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param body: 1757b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Response body, if any. 1758b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param request: 1759b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Optional request object. If not set, uses :func:`get_request`. 1760b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param response: 1761b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Optional response object. If not set, a new response is created. 1762b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1763b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Response` instance. 1764b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1765b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if uri.startswith(('.', '/')): 1766b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request = request or get_request() 1767b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik uri = str(urlparse.urljoin(request.url, uri)) 1768b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1769b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if code is None: 1770b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if permanent: 1771b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik code = 301 1772b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1773b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik code = 302 1774b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1775b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik assert code in (301, 302, 303, 305, 307), \ 1776b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'Invalid redirect status code.' 1777b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1778b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if abort: 1779b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik _abort(code, headers=[('Location', uri)]) 1780b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1781b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if response is None: 1782b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik request = request or get_request() 1783b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response = request.app.response_class() 1784b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1785b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response.clear() 1786b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1787b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response.headers['Location'] = uri 1788b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response.status = code 1789b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if body is not None: 1790b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik response.write(body) 1791b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1792b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return response 1793b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1794b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1795b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef redirect_to(_name, _permanent=False, _abort=False, _code=None, 1796b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik _body=None, _request=None, _response=None, *args, **kwargs): 1797b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Convenience function mixing :func:`redirect` and :func:`uri_for`. 1798b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1799b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Issues an HTTP redirect to a named URI built using :func:`uri_for`. 1800b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1801b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param _name: 1802b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The route name to redirect to. 1803b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param args: 1804b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Positional arguments to build the URI. 1805b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param kwargs: 1806b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Keyword arguments to build the URI. 1807b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1808b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`Response` instance. 1809b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1810b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The other arguments are described in :func:`redirect`. 1811b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1812b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik uri = uri_for(_name, _request=_request, *args, **kwargs) 1813b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return redirect(uri, permanent=_permanent, abort=_abort, code=_code, 1814b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik body=_body, request=_request, response=_response) 1815b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1816b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1817b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef abort(code, *args, **kwargs): 1818b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Raises an ``HTTPException``. 1819b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1820b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param code: 1821b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik An integer that represents a valid HTTP status code. 1822b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param args: 1823b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Positional arguments to instantiate the exception. 1824b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param kwargs: 1825b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Keyword arguments to instantiate the exception. 1826b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1827b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik cls = exc.status_map.get(code) 1828b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not cls: 1829b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise KeyError('No exception is defined for code %r.' % code) 1830b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1831b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise cls(*args, **kwargs) 1832b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1833b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1834b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef import_string(import_name, silent=False): 1835b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Imports an object based on a string in dotted notation. 1836b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1837b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Simplified version of the function with same name from `Werkzeug`_. 1838b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1839b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param import_name: 1840b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik String in dotted notation of the object to be imported. 1841b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param silent: 1842b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If True, import or attribute errors are ignored and None is returned 1843b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik instead of raising an exception. 1844b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1845b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik The imported object. 1846b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1847b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik import_name = _to_utf8(import_name) 1848b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 1849b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if '.' in import_name: 1850b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik module, obj = import_name.rsplit('.', 1) 1851b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return getattr(__import__(module, None, None, [obj]), obj) 1852b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1853b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return __import__(import_name) 1854b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except (ImportError, AttributeError), e: 1855b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not silent: 1856b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise ImportStringError(import_name, e), None, sys.exc_info()[2] 1857b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1858b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1859b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef _urlunsplit(scheme=None, netloc=None, path=None, query=None, 1860b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik fragment=None): 1861b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Like ``urlparse.urlunsplit``, but will escape values and urlencode and 1862b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik sort query arguments. 1863b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1864b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param scheme: 1865b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik URI scheme, e.g., `http` or `https`. 1866b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param netloc: 1867b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Network location, e.g., `localhost:8080` or `www.google.com`. 1868b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param path: 1869b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik URI path. 1870b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param query: 1871b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik URI query as an escaped string, or a dictionary or list of key-values 1872b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik tuples to build a query. 1873b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param fragment: 1874b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Fragment identifier, also known as "anchor". 1875b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1876b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik An assembled absolute or relative URI. 1877b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1878b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not scheme or not netloc: 1879b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik scheme = None 1880b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik netloc = None 1881b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1882b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if path: 1883b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik path = urllib.quote(_to_utf8(path)) 1884b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1885b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if query and not isinstance(query, basestring): 1886b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if isinstance(query, dict): 1887b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik query = query.iteritems() 1888b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1889b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Sort args: commonly needed to build signatures for services. 1890b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik query = urllib.urlencode(sorted(query)) 1891b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1892b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if fragment: 1893b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik fragment = urllib.quote(_to_utf8(fragment)) 1894b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1895b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) 1896b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1897b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1898b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef _get_handler_methods(handler): 1899b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns a list of HTTP methods supported by a handler. 1900b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1901b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :param handler: 1902b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A :class:`RequestHandler` instance. 1903b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik :returns: 1904b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A list of HTTP methods supported by the handler. 1905b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 1906b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik methods = [] 1907b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for method in get_app().allowed_methods: 1908b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if getattr(handler, _normalize_handler_method(method), None): 1909b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik methods.append(method) 1910b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1911b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return methods 1912b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1913b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1914b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef _normalize_handler_method(method): 1915b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Transforms an HTTP method into a valid Python identifier.""" 1916b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return method.lower().replace('-', '_') 1917b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1918b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1919b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef _to_utf8(value): 1920b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Encodes a unicode value to UTF-8 if not yet encoded.""" 1921b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if isinstance(value, str): 1922b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return value 1923b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1924b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return value.encode('utf-8') 1925b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1926b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1927b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef _parse_route_template(template, default_sufix=''): 1928b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Lazy route template parser.""" 1929b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik variables = {} 1930b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik reverse_template = pattern = '' 1931b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args_count = last = 0 1932b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for match in _route_re.finditer(template): 1933b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik part = template[last:match.start()] 1934b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik name = match.group(1) 1935b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik expr = match.group(2) or default_sufix 1936b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik last = match.end() 1937b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1938b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not name: 1939b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik name = '__%d__' % args_count 1940b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args_count += 1 1941b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1942b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik pattern += '%s(?P<%s>%s)' % (re.escape(part), name, expr) 1943b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik reverse_template += '%s%%(%s)s' % (part, name) 1944b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik variables[name] = re.compile('^%s$' % expr) 1945b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1946b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik part = template[last:] 1947b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik kwargs_count = len(variables) - args_count 1948b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik reverse_template += part 1949b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik regex = re.compile('^%s%s$' % (pattern, re.escape(part))) 1950b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return regex, reverse_template, args_count, kwargs_count, variables 1951b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1952b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1953b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef _get_route_variables(match, default_kwargs=None): 1954b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Returns (args, kwargs) for a route match.""" 1955b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik kwargs = default_kwargs or {} 1956b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik kwargs.update(match.groupdict()) 1957b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if kwargs: 1958b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args = tuple(value[1] for value in sorted( 1959b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (int(key[2:-2]), kwargs.pop(key)) for key in kwargs.keys() \ 1960b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if key.startswith('__') and key.endswith('__'))) 1961b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 1962b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args = () 1963b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1964b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return args, kwargs 1965b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1966b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1967b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef _set_thread_safe_app(): 1968b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Assigns WSGIApplication globals to a proxy pointing to thread-local.""" 1969b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if _local is not None: # pragma: no cover 1970b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik WSGIApplication.app = WSGIApplication.active_instance = _local('app') 1971b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik WSGIApplication.request = _local('request') 1972b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1973b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1974b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikRequest.ResponseClass = Response 1975b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikResponse.RequestClass = Request 1976b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# Alias. 1977b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_abort = abort 1978b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# Thread-safety support. 1979b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_set_thread_safe_app() 1980b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 1981b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# Defer importing google.appengine.ext.webapp.util until every public symbol 1982b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# has been defined since google.appengine.ext.webapp in App Engine Python 2.7 1983b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# runtime imports this module to provide its public interface. 1984b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craiktry: 1985b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik from google.appengine.ext.webapp import util as _webapp_util 1986b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikexcept ImportError: # pragma: no cover 1987b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik pass 1988