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