1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3"""
4Bottle is a fast and simple micro-framework for small web applications. It
5offers request dispatching (Routes) with URL parameter support, templates,
6a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and
7template engines - all in a single file and with no dependencies other than the
8Python Standard Library.
9
10Homepage and documentation: http://bottlepy.org/
11
12Copyright (c) 2014, Marcel Hellkamp.
13License: MIT (see LICENSE for details)
14"""
15
16from __future__ import with_statement
17import sys
18
19__author__ = 'Marcel Hellkamp'
20__version__ = '0.13-dev'
21__license__ = 'MIT'
22
23###############################################################################
24# Command-line interface ########################################################
25###############################################################################
26# INFO: Some server adapters need to monkey-patch std-lib modules before they
27# are imported. This is why some of the command-line handling is done here, but
28# the actual call to main() is at the end of the file.
29
30
31def _cli_parse(args):
32    from optparse import OptionParser
33    parser = OptionParser(
34        usage="usage: %prog [options] package.module:app")
35    opt = parser.add_option
36    opt("--version", action="store_true", help="show version number.")
37    opt("-b", "--bind", metavar="ADDRESS", help="bind socket to ADDRESS.")
38    opt("-s", "--server", default='wsgiref', help="use SERVER as backend.")
39    opt("-p", "--plugin", action="append", help="install additional plugin/s.")
40    opt("-c", "--conf", action="append", metavar="FILE",
41        help="load config values from FILE.")
42    opt("-C", "--param", action="append", metavar="NAME=VALUE",
43        help="override config values.")
44    opt("--debug", action="store_true", help="start server in debug mode.")
45    opt("--reload", action="store_true", help="auto-reload on file changes.")
46    opts, args = parser.parse_args(args[1:])
47
48    return opts, args, parser
49
50
51def _cli_patch(args):
52    opts, _, _ = _cli_parse(args)
53    if opts.server:
54        if opts.server.startswith('gevent'):
55            import gevent.monkey
56            gevent.monkey.patch_all()
57        elif opts.server.startswith('eventlet'):
58            import eventlet
59            eventlet.monkey_patch()
60
61
62if __name__ == '__main__':
63    _cli_patch(sys.argv)
64
65###############################################################################
66# Imports and Python 2/3 unification ###########################################
67###############################################################################
68
69
70import base64, cgi, email.utils, functools, hmac, imp, itertools, mimetypes,\
71        os, re, tempfile, threading, time, warnings
72
73from types import FunctionType
74from datetime import date as datedate, datetime, timedelta
75from tempfile import TemporaryFile
76from traceback import format_exc, print_exc
77from unicodedata import normalize
78
79# inspect.getargspec was removed in Python 3.6, use
80# Signature-based version where we can (Python 3.3+)
81try:
82    from inspect import signature
83    def getargspec(func):
84        params = signature(func).parameters
85        args, varargs, keywords, defaults = [], None, None, []
86        for name, param in params.items():
87            if param.kind == param.VAR_POSITIONAL:
88                varargs = name
89            elif param.kind == param.VAR_KEYWORD:
90                keywords = name
91            else:
92                args.append(name)
93                if param.default is not param.empty:
94                    defaults.append(param.default)
95        return (args, varargs, keywords, tuple(defaults) or None)
96except ImportError:
97    from inspect import getargspec
98
99try:
100    from simplejson import dumps as json_dumps, loads as json_lds
101except ImportError:  # pragma: no cover
102    try:
103        from json import dumps as json_dumps, loads as json_lds
104    except ImportError:
105        try:
106            from django.utils.simplejson import dumps as json_dumps, loads as json_lds
107        except ImportError:
108
109            def json_dumps(data):
110                raise ImportError(
111                    "JSON support requires Python 2.6 or simplejson.")
112
113            json_lds = json_dumps
114
115# We now try to fix 2.5/2.6/3.1/3.2 incompatibilities.
116# It ain't pretty but it works... Sorry for the mess.
117
118py = sys.version_info
119py3k = py >= (3, 0, 0)
120py25 = py <  (2, 6, 0)
121py31 = (3, 1, 0) <= py < (3, 2, 0)
122
123# Workaround for the missing "as" keyword in py3k.
124def _e():
125    return sys.exc_info()[1]
126
127# Workaround for the "print is a keyword/function" Python 2/3 dilemma
128# and a fallback for mod_wsgi (resticts stdout/err attribute access)
129try:
130    _stdout, _stderr = sys.stdout.write, sys.stderr.write
131except IOError:
132    _stdout = lambda x: sys.stdout.write(x)
133    _stderr = lambda x: sys.stderr.write(x)
134
135# Lots of stdlib and builtin differences.
136if py3k:
137    import http.client as httplib
138    import _thread as thread
139    from urllib.parse import urljoin, SplitResult as UrlSplitResult
140    from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote
141    urlunquote = functools.partial(urlunquote, encoding='latin1')
142    from http.cookies import SimpleCookie
143    from collections import MutableMapping as DictMixin
144    import pickle
145    from io import BytesIO
146    from configparser import ConfigParser, Error as ConfigParserError
147    basestring = str
148    unicode = str
149    json_loads = lambda s: json_lds(touni(s))
150    callable = lambda x: hasattr(x, '__call__')
151    imap = map
152
153    def _raise(*a):
154        raise a[0](a[1]).with_traceback(a[2])
155else:  # 2.x
156    import httplib
157    import thread
158    from urlparse import urljoin, SplitResult as UrlSplitResult
159    from urllib import urlencode, quote as urlquote, unquote as urlunquote
160    from Cookie import SimpleCookie
161    from itertools import imap
162    import cPickle as pickle
163    from StringIO import StringIO as BytesIO
164    from ConfigParser import SafeConfigParser as ConfigParser, \
165                             Error as ConfigParserError
166    if py25:
167        msg = "Python 2.5 support may be dropped in future versions of Bottle."
168        warnings.warn(msg, DeprecationWarning)
169        from UserDict import DictMixin
170
171        def next(it):
172            return it.next()
173
174        bytes = str
175    else:  # 2.6, 2.7
176        from collections import MutableMapping as DictMixin
177    unicode = unicode
178    json_loads = json_lds
179    eval(compile('def _raise(*a): raise a[0], a[1], a[2]', '<py3fix>', 'exec'))
180
181
182# Some helpers for string/byte handling
183def tob(s, enc='utf8'):
184    return s.encode(enc) if isinstance(s, unicode) else bytes(s)
185
186
187def touni(s, enc='utf8', err='strict'):
188    if isinstance(s, bytes):
189        return s.decode(enc, err)
190    else:
191        return unicode(s or ("" if s is None else s))
192
193
194tonat = touni if py3k else tob
195
196# 3.2 fixes cgi.FieldStorage to accept bytes (which makes a lot of sense).
197# 3.1 needs a workaround.
198if py31:
199    from io import TextIOWrapper
200
201    class NCTextIOWrapper(TextIOWrapper):
202        def close(self):
203            pass  # Keep wrapped buffer open.
204
205
206# A bug in functools causes it to break if the wrapper is an instance method
207def update_wrapper(wrapper, wrapped, *a, **ka):
208    try:
209        functools.update_wrapper(wrapper, wrapped, *a, **ka)
210    except AttributeError:
211        pass
212
213# These helpers are used at module level and need to be defined first.
214# And yes, I know PEP-8, but sometimes a lower-case classname makes more sense.
215
216
217def depr(message, strict=False):
218    warnings.warn(message, DeprecationWarning, stacklevel=3)
219
220
221def makelist(data):  # This is just too handy
222    if isinstance(data, (tuple, list, set, dict)):
223        return list(data)
224    elif data:
225        return [data]
226    else:
227        return []
228
229
230class DictProperty(object):
231    """ Property that maps to a key in a local dict-like attribute. """
232
233    def __init__(self, attr, key=None, read_only=False):
234        self.attr, self.key, self.read_only = attr, key, read_only
235
236    def __call__(self, func):
237        functools.update_wrapper(self, func, updated=[])
238        self.getter, self.key = func, self.key or func.__name__
239        return self
240
241    def __get__(self, obj, cls):
242        if obj is None: return self
243        key, storage = self.key, getattr(obj, self.attr)
244        if key not in storage: storage[key] = self.getter(obj)
245        return storage[key]
246
247    def __set__(self, obj, value):
248        if self.read_only: raise AttributeError("Read-Only property.")
249        getattr(obj, self.attr)[self.key] = value
250
251    def __delete__(self, obj):
252        if self.read_only: raise AttributeError("Read-Only property.")
253        del getattr(obj, self.attr)[self.key]
254
255
256class cached_property(object):
257    """ A property that is only computed once per instance and then replaces
258        itself with an ordinary attribute. Deleting the attribute resets the
259        property. """
260
261    def __init__(self, func):
262        self.__doc__ = getattr(func, '__doc__')
263        self.func = func
264
265    def __get__(self, obj, cls):
266        if obj is None: return self
267        value = obj.__dict__[self.func.__name__] = self.func(obj)
268        return value
269
270
271class lazy_attribute(object):
272    """ A property that caches itself to the class object. """
273
274    def __init__(self, func):
275        functools.update_wrapper(self, func, updated=[])
276        self.getter = func
277
278    def __get__(self, obj, cls):
279        value = self.getter(cls)
280        setattr(cls, self.__name__, value)
281        return value
282
283###############################################################################
284# Exceptions and Events ########################################################
285###############################################################################
286
287
288class BottleException(Exception):
289    """ A base class for exceptions used by bottle. """
290    pass
291
292###############################################################################
293# Routing ######################################################################
294###############################################################################
295
296
297class RouteError(BottleException):
298    """ This is a base class for all routing related exceptions """
299
300
301class RouteReset(BottleException):
302    """ If raised by a plugin or request handler, the route is reset and all
303        plugins are re-applied. """
304
305
306class RouterUnknownModeError(RouteError):
307
308    pass
309
310
311class RouteSyntaxError(RouteError):
312    """ The route parser found something not supported by this router. """
313
314
315class RouteBuildError(RouteError):
316    """ The route could not be built. """
317
318
319def _re_flatten(p):
320    """ Turn all capturing groups in a regular expression pattern into
321        non-capturing groups. """
322    if '(' not in p:
323        return p
324    return re.sub(r'(\\*)(\(\?P<[^>]+>|\((?!\?))', lambda m: m.group(0) if
325                  len(m.group(1)) % 2 else m.group(1) + '(?:', p)
326
327
328class Router(object):
329    """ A Router is an ordered collection of route->target pairs. It is used to
330        efficiently match WSGI requests against a number of routes and return
331        the first target that satisfies the request. The target may be anything,
332        usually a string, ID or callable object. A route consists of a path-rule
333        and a HTTP method.
334
335        The path-rule is either a static path (e.g. `/contact`) or a dynamic
336        path that contains wildcards (e.g. `/wiki/<page>`). The wildcard syntax
337        and details on the matching order are described in docs:`routing`.
338    """
339
340    default_pattern = '[^/]+'
341    default_filter = 're'
342
343    #: The current CPython regexp implementation does not allow more
344    #: than 99 matching groups per regular expression.
345    _MAX_GROUPS_PER_PATTERN = 99
346
347    def __init__(self, strict=False):
348        self.rules = []  # All rules in order
349        self._groups = {}  # index of regexes to find them in dyna_routes
350        self.builder = {}  # Data structure for the url builder
351        self.static = {}  # Search structure for static routes
352        self.dyna_routes = {}
353        self.dyna_regexes = {}  # Search structure for dynamic routes
354        #: If true, static routes are no longer checked first.
355        self.strict_order = strict
356        self.filters = {
357            're': lambda conf: (_re_flatten(conf or self.default_pattern),
358                                None, None),
359            'int': lambda conf: (r'-?\d+', int, lambda x: str(int(x))),
360            'float': lambda conf: (r'-?[\d.]+', float, lambda x: str(float(x))),
361            'path': lambda conf: (r'.+?', None, None)
362        }
363
364    def add_filter(self, name, func):
365        """ Add a filter. The provided function is called with the configuration
366        string as parameter and must return a (regexp, to_python, to_url) tuple.
367        The first element is a string, the last two are callables or None. """
368        self.filters[name] = func
369
370    rule_syntax = re.compile('(\\\\*)'
371        '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)'
372          '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)'
373            '(?::((?:\\\\.|[^\\\\>]+)+)?)?)?>))')
374
375    def _itertokens(self, rule):
376        offset, prefix = 0, ''
377        for match in self.rule_syntax.finditer(rule):
378            prefix += rule[offset:match.start()]
379            g = match.groups()
380            if len(g[0]) % 2:  # Escaped wildcard
381                prefix += match.group(0)[len(g[0]):]
382                offset = match.end()
383                continue
384            if prefix:
385                yield prefix, None, None
386            name, filtr, conf = g[4:7] if g[2] is None else g[1:4]
387            yield name, filtr or 'default', conf or None
388            offset, prefix = match.end(), ''
389        if offset <= len(rule) or prefix:
390            yield prefix + rule[offset:], None, None
391
392    def add(self, rule, method, target, name=None):
393        """ Add a new rule or replace the target for an existing rule. """
394        anons = 0  # Number of anonymous wildcards found
395        keys = []  # Names of keys
396        pattern = ''  # Regular expression pattern with named groups
397        filters = []  # Lists of wildcard input filters
398        builder = []  # Data structure for the URL builder
399        is_static = True
400
401        for key, mode, conf in self._itertokens(rule):
402            if mode:
403                is_static = False
404                if mode == 'default': mode = self.default_filter
405                mask, in_filter, out_filter = self.filters[mode](conf)
406                if not key:
407                    pattern += '(?:%s)' % mask
408                    key = 'anon%d' % anons
409                    anons += 1
410                else:
411                    pattern += '(?P<%s>%s)' % (key, mask)
412                    keys.append(key)
413                if in_filter: filters.append((key, in_filter))
414                builder.append((key, out_filter or str))
415            elif key:
416                pattern += re.escape(key)
417                builder.append((None, key))
418
419        self.builder[rule] = builder
420        if name: self.builder[name] = builder
421
422        if is_static and not self.strict_order:
423            self.static.setdefault(method, {})
424            self.static[method][self.build(rule)] = (target, None)
425            return
426
427        try:
428            re_pattern = re.compile('^(%s)$' % pattern)
429            re_match = re_pattern.match
430        except re.error:
431            raise RouteSyntaxError("Could not add Route: %s (%s)" %
432                                   (rule, _e()))
433
434        if filters:
435
436            def getargs(path):
437                url_args = re_match(path).groupdict()
438                for name, wildcard_filter in filters:
439                    try:
440                        url_args[name] = wildcard_filter(url_args[name])
441                    except ValueError:
442                        raise HTTPError(400, 'Path has wrong format.')
443                return url_args
444        elif re_pattern.groupindex:
445
446            def getargs(path):
447                return re_match(path).groupdict()
448        else:
449            getargs = None
450
451        flatpat = _re_flatten(pattern)
452        whole_rule = (rule, flatpat, target, getargs)
453
454        if (flatpat, method) in self._groups:
455            if DEBUG:
456                msg = 'Route <%s %s> overwrites a previously defined route'
457                warnings.warn(msg % (method, rule), RuntimeWarning)
458            self.dyna_routes[method][
459                self._groups[flatpat, method]] = whole_rule
460        else:
461            self.dyna_routes.setdefault(method, []).append(whole_rule)
462            self._groups[flatpat, method] = len(self.dyna_routes[method]) - 1
463
464        self._compile(method)
465
466    def _compile(self, method):
467        all_rules = self.dyna_routes[method]
468        comborules = self.dyna_regexes[method] = []
469        maxgroups = self._MAX_GROUPS_PER_PATTERN
470        for x in range(0, len(all_rules), maxgroups):
471            some = all_rules[x:x + maxgroups]
472            combined = (flatpat for (_, flatpat, _, _) in some)
473            combined = '|'.join('(^%s$)' % flatpat for flatpat in combined)
474            combined = re.compile(combined).match
475            rules = [(target, getargs) for (_, _, target, getargs) in some]
476            comborules.append((combined, rules))
477
478    def build(self, _name, *anons, **query):
479        """ Build an URL by filling the wildcards in a rule. """
480        builder = self.builder.get(_name)
481        if not builder:
482            raise RouteBuildError("No route with that name.", _name)
483        try:
484            for i, value in enumerate(anons):
485                query['anon%d' % i] = value
486            url = ''.join([f(query.pop(n)) if n else f for (n, f) in builder])
487            return url if not query else url + '?' + urlencode(query)
488        except KeyError:
489            raise RouteBuildError('Missing URL argument: %r' % _e().args[0])
490
491    def match(self, environ):
492        """ Return a (target, url_args) tuple or raise HTTPError(400/404/405). """
493        verb = environ['REQUEST_METHOD'].upper()
494        path = environ['PATH_INFO'] or '/'
495
496        if verb == 'HEAD':
497            methods = ['PROXY', verb, 'GET', 'ANY']
498        else:
499            methods = ['PROXY', verb, 'ANY']
500
501        for method in methods:
502            if method in self.static and path in self.static[method]:
503                target, getargs = self.static[method][path]
504                return target, getargs(path) if getargs else {}
505            elif method in self.dyna_regexes:
506                for combined, rules in self.dyna_regexes[method]:
507                    match = combined(path)
508                    if match:
509                        target, getargs = rules[match.lastindex - 1]
510                        return target, getargs(path) if getargs else {}
511
512        # No matching route found. Collect alternative methods for 405 response
513        allowed = set([])
514        nocheck = set(methods)
515        for method in set(self.static) - nocheck:
516            if path in self.static[method]:
517                allowed.add(verb)
518        for method in set(self.dyna_regexes) - allowed - nocheck:
519            for combined, rules in self.dyna_regexes[method]:
520                match = combined(path)
521                if match:
522                    allowed.add(method)
523        if allowed:
524            allow_header = ",".join(sorted(allowed))
525            raise HTTPError(405, "Method not allowed.", Allow=allow_header)
526
527        # No matching route and no alternative method found. We give up
528        raise HTTPError(404, "Not found: " + repr(path))
529
530
531class Route(object):
532    """ This class wraps a route callback along with route specific metadata and
533        configuration and applies Plugins on demand. It is also responsible for
534        turing an URL path rule into a regular expression usable by the Router.
535    """
536
537    def __init__(self, app, rule, method, callback,
538                 name=None,
539                 plugins=None,
540                 skiplist=None, **config):
541        #: The application this route is installed to.
542        self.app = app
543        #: The path-rule string (e.g. ``/wiki/<page>``).
544        self.rule = rule
545        #: The HTTP method as a string (e.g. ``GET``).
546        self.method = method
547        #: The original callback with no plugins applied. Useful for introspection.
548        self.callback = callback
549        #: The name of the route (if specified) or ``None``.
550        self.name = name or None
551        #: A list of route-specific plugins (see :meth:`Bottle.route`).
552        self.plugins = plugins or []
553        #: A list of plugins to not apply to this route (see :meth:`Bottle.route`).
554        self.skiplist = skiplist or []
555        #: Additional keyword arguments passed to the :meth:`Bottle.route`
556        #: decorator are stored in this dictionary. Used for route-specific
557        #: plugin configuration and meta-data.
558        self.config = ConfigDict().load_dict(config)
559
560    @cached_property
561    def call(self):
562        """ The route callback with all plugins applied. This property is
563            created on demand and then cached to speed up subsequent requests."""
564        return self._make_callback()
565
566    def reset(self):
567        """ Forget any cached values. The next time :attr:`call` is accessed,
568            all plugins are re-applied. """
569        self.__dict__.pop('call', None)
570
571    def prepare(self):
572        """ Do all on-demand work immediately (useful for debugging)."""
573        self.call
574
575    def all_plugins(self):
576        """ Yield all Plugins affecting this route. """
577        unique = set()
578        for p in reversed(self.app.plugins + self.plugins):
579            if True in self.skiplist: break
580            name = getattr(p, 'name', False)
581            if name and (name in self.skiplist or name in unique): continue
582            if p in self.skiplist or type(p) in self.skiplist: continue
583            if name: unique.add(name)
584            yield p
585
586    def _make_callback(self):
587        callback = self.callback
588        for plugin in self.all_plugins():
589            try:
590                if hasattr(plugin, 'apply'):
591                    callback = plugin.apply(callback, self)
592                else:
593                    callback = plugin(callback)
594            except RouteReset:  # Try again with changed configuration.
595                return self._make_callback()
596            if not callback is self.callback:
597                update_wrapper(callback, self.callback)
598        return callback
599
600    def get_undecorated_callback(self):
601        """ Return the callback. If the callback is a decorated function, try to
602            recover the original function. """
603        func = self.callback
604        func = getattr(func, '__func__' if py3k else 'im_func', func)
605        closure_attr = '__closure__' if py3k else 'func_closure'
606        while hasattr(func, closure_attr) and getattr(func, closure_attr):
607            attributes = getattr(func, closure_attr)
608            func = attributes[0].cell_contents
609
610            # in case of decorators with multiple arguments
611            if not isinstance(func, FunctionType):
612                # pick first FunctionType instance from multiple arguments
613                func = filter(lambda x: isinstance(x, FunctionType),
614                              map(lambda x: x.cell_contents, attributes))
615                func = list(func)[0]  # py3 support
616        return func
617
618    def get_callback_args(self):
619        """ Return a list of argument names the callback (most likely) accepts
620            as keyword arguments. If the callback is a decorated function, try
621            to recover the original function before inspection. """
622        return getargspec(self.get_undecorated_callback())[0]
623
624    def get_config(self, key, default=None):
625        """ Lookup a config field and return its value, first checking the
626            route.config, then route.app.config."""
627        for conf in (self.config, self.app.config):
628            if key in conf: return conf[key]
629        return default
630
631    def __repr__(self):
632        cb = self.get_undecorated_callback()
633        return '<%s %r %r>' % (self.method, self.rule, cb)
634
635###############################################################################
636# Application Object ###########################################################
637###############################################################################
638
639
640class Bottle(object):
641    """ Each Bottle object represents a single, distinct web application and
642        consists of routes, callbacks, plugins, resources and configuration.
643        Instances are callable WSGI applications.
644
645        :param catchall: If true (default), handle all exceptions. Turn off to
646                         let debugging middleware handle exceptions.
647    """
648
649    def __init__(self, catchall=True, autojson=True):
650        #: A :class:`ConfigDict` for app specific configuration.
651        self.config = ConfigDict()
652        self.config._on_change = functools.partial(self.trigger_hook, 'config')
653        self.config.meta_set('autojson', 'validate', bool)
654        self.config.meta_set('catchall', 'validate', bool)
655        self.config['catchall'] = catchall
656        self.config['autojson'] = autojson
657
658        #: A :class:`ResourceManager` for application files
659        self.resources = ResourceManager()
660
661        self.routes = []  # List of installed :class:`Route` instances.
662        self.router = Router()  # Maps requests to :class:`Route` instances.
663        self.error_handler = {}
664
665        # Core plugins
666        self.plugins = []  # List of installed plugins.
667        if self.config['autojson']:
668            self.install(JSONPlugin())
669        self.install(TemplatePlugin())
670
671    #: If true, most exceptions are caught and returned as :exc:`HTTPError`
672    catchall = DictProperty('config', 'catchall')
673
674    __hook_names = 'before_request', 'after_request', 'app_reset', 'config'
675    __hook_reversed = 'after_request'
676
677    @cached_property
678    def _hooks(self):
679        return dict((name, []) for name in self.__hook_names)
680
681    def add_hook(self, name, func):
682        """ Attach a callback to a hook. Three hooks are currently implemented:
683
684            before_request
685                Executed once before each request. The request context is
686                available, but no routing has happened yet.
687            after_request
688                Executed once after each request regardless of its outcome.
689            app_reset
690                Called whenever :meth:`Bottle.reset` is called.
691        """
692        if name in self.__hook_reversed:
693            self._hooks[name].insert(0, func)
694        else:
695            self._hooks[name].append(func)
696
697    def remove_hook(self, name, func):
698        """ Remove a callback from a hook. """
699        if name in self._hooks and func in self._hooks[name]:
700            self._hooks[name].remove(func)
701            return True
702
703    def trigger_hook(self, __name, *args, **kwargs):
704        """ Trigger a hook and return a list of results. """
705        return [hook(*args, **kwargs) for hook in self._hooks[__name][:]]
706
707    def hook(self, name):
708        """ Return a decorator that attaches a callback to a hook. See
709            :meth:`add_hook` for details."""
710
711        def decorator(func):
712            self.add_hook(name, func)
713            return func
714
715        return decorator
716
717    def mount(self, prefix, app, **options):
718        """ Mount an application (:class:`Bottle` or plain WSGI) to a specific
719            URL prefix. Example::
720
721                root_app.mount('/admin/', admin_app)
722
723            :param prefix: path prefix or `mount-point`. If it ends in a slash,
724                that slash is mandatory.
725            :param app: an instance of :class:`Bottle` or a WSGI application.
726
727            All other parameters are passed to the underlying :meth:`route` call.
728        """
729
730        segments = [p for p in prefix.split('/') if p]
731        if not segments: raise ValueError('Empty path prefix.')
732        path_depth = len(segments)
733
734        def mountpoint_wrapper():
735            try:
736                request.path_shift(path_depth)
737                rs = HTTPResponse([])
738
739                def start_response(status, headerlist, exc_info=None):
740                    if exc_info:
741                        _raise(*exc_info)
742                    rs.status = status
743                    for name, value in headerlist:
744                        rs.add_header(name, value)
745                    return rs.body.append
746
747                body = app(request.environ, start_response)
748                rs.body = itertools.chain(rs.body, body) if rs.body else body
749                return rs
750            finally:
751                request.path_shift(-path_depth)
752
753        options.setdefault('skip', True)
754        options.setdefault('method', 'PROXY')
755        options.setdefault('mountpoint', {'prefix': prefix, 'target': app})
756        options['callback'] = mountpoint_wrapper
757
758        self.route('/%s/<:re:.*>' % '/'.join(segments), **options)
759        if not prefix.endswith('/'):
760            self.route('/' + '/'.join(segments), **options)
761
762    def merge(self, routes):
763        """ Merge the routes of another :class:`Bottle` application or a list of
764            :class:`Route` objects into this application. The routes keep their
765            'owner', meaning that the :data:`Route.app` attribute is not
766            changed. """
767        if isinstance(routes, Bottle):
768            routes = routes.routes
769        for route in routes:
770            self.add_route(route)
771
772    def install(self, plugin):
773        """ Add a plugin to the list of plugins and prepare it for being
774            applied to all routes of this application. A plugin may be a simple
775            decorator or an object that implements the :class:`Plugin` API.
776        """
777        if hasattr(plugin, 'setup'): plugin.setup(self)
778        if not callable(plugin) and not hasattr(plugin, 'apply'):
779            raise TypeError("Plugins must be callable or implement .apply()")
780        self.plugins.append(plugin)
781        self.reset()
782        return plugin
783
784    def uninstall(self, plugin):
785        """ Uninstall plugins. Pass an instance to remove a specific plugin, a type
786            object to remove all plugins that match that type, a string to remove
787            all plugins with a matching ``name`` attribute or ``True`` to remove all
788            plugins. Return the list of removed plugins. """
789        removed, remove = [], plugin
790        for i, plugin in list(enumerate(self.plugins))[::-1]:
791            if remove is True or remove is plugin or remove is type(plugin) \
792            or getattr(plugin, 'name', True) == remove:
793                removed.append(plugin)
794                del self.plugins[i]
795                if hasattr(plugin, 'close'): plugin.close()
796        if removed: self.reset()
797        return removed
798
799    def reset(self, route=None):
800        """ Reset all routes (force plugins to be re-applied) and clear all
801            caches. If an ID or route object is given, only that specific route
802            is affected. """
803        if route is None: routes = self.routes
804        elif isinstance(route, Route): routes = [route]
805        else: routes = [self.routes[route]]
806        for route in routes:
807            route.reset()
808        if DEBUG:
809            for route in routes:
810                route.prepare()
811        self.trigger_hook('app_reset')
812
813    def close(self):
814        """ Close the application and all installed plugins. """
815        for plugin in self.plugins:
816            if hasattr(plugin, 'close'): plugin.close()
817
818    def run(self, **kwargs):
819        """ Calls :func:`run` with the same parameters. """
820        run(self, **kwargs)
821
822    def match(self, environ):
823        """ Search for a matching route and return a (:class:`Route` , urlargs)
824            tuple. The second value is a dictionary with parameters extracted
825            from the URL. Raise :exc:`HTTPError` (404/405) on a non-match."""
826        return self.router.match(environ)
827
828    def get_url(self, routename, **kargs):
829        """ Return a string that matches a named route """
830        scriptname = request.environ.get('SCRIPT_NAME', '').strip('/') + '/'
831        location = self.router.build(routename, **kargs).lstrip('/')
832        return urljoin(urljoin('/', scriptname), location)
833
834    def add_route(self, route):
835        """ Add a route object, but do not change the :data:`Route.app`
836            attribute."""
837        self.routes.append(route)
838        self.router.add(route.rule, route.method, route, name=route.name)
839        if DEBUG: route.prepare()
840
841    def route(self,
842              path=None,
843              method='GET',
844              callback=None,
845              name=None,
846              apply=None,
847              skip=None, **config):
848        """ A decorator to bind a function to a request URL. Example::
849
850                @app.route('/hello/<name>')
851                def hello(name):
852                    return 'Hello %s' % name
853
854            The ``<name>`` part is a wildcard. See :class:`Router` for syntax
855            details.
856
857            :param path: Request path or a list of paths to listen to. If no
858              path is specified, it is automatically generated from the
859              signature of the function.
860            :param method: HTTP method (`GET`, `POST`, `PUT`, ...) or a list of
861              methods to listen to. (default: `GET`)
862            :param callback: An optional shortcut to avoid the decorator
863              syntax. ``route(..., callback=func)`` equals ``route(...)(func)``
864            :param name: The name for this route. (default: None)
865            :param apply: A decorator or plugin or a list of plugins. These are
866              applied to the route callback in addition to installed plugins.
867            :param skip: A list of plugins, plugin classes or names. Matching
868              plugins are not installed to this route. ``True`` skips all.
869
870            Any additional keyword arguments are stored as route-specific
871            configuration and passed to plugins (see :meth:`Plugin.apply`).
872        """
873        if callable(path): path, callback = None, path
874        plugins = makelist(apply)
875        skiplist = makelist(skip)
876
877        def decorator(callback):
878            if isinstance(callback, basestring): callback = load(callback)
879            for rule in makelist(path) or yieldroutes(callback):
880                for verb in makelist(method):
881                    verb = verb.upper()
882                    route = Route(self, rule, verb, callback,
883                                  name=name,
884                                  plugins=plugins,
885                                  skiplist=skiplist, **config)
886                    self.add_route(route)
887            return callback
888
889        return decorator(callback) if callback else decorator
890
891    def get(self, path=None, method='GET', **options):
892        """ Equals :meth:`route`. """
893        return self.route(path, method, **options)
894
895    def post(self, path=None, method='POST', **options):
896        """ Equals :meth:`route` with a ``POST`` method parameter. """
897        return self.route(path, method, **options)
898
899    def put(self, path=None, method='PUT', **options):
900        """ Equals :meth:`route` with a ``PUT`` method parameter. """
901        return self.route(path, method, **options)
902
903    def delete(self, path=None, method='DELETE', **options):
904        """ Equals :meth:`route` with a ``DELETE`` method parameter. """
905        return self.route(path, method, **options)
906
907    def patch(self, path=None, method='PATCH', **options):
908        """ Equals :meth:`route` with a ``PATCH`` method parameter. """
909        return self.route(path, method, **options)
910
911    def error(self, code=500):
912        """ Decorator: Register an output handler for a HTTP error code"""
913
914        def wrapper(handler):
915            self.error_handler[int(code)] = handler
916            return handler
917
918        return wrapper
919
920    def default_error_handler(self, res):
921        return tob(template(ERROR_PAGE_TEMPLATE, e=res))
922
923    def _handle(self, environ):
924        path = environ['bottle.raw_path'] = environ['PATH_INFO']
925        if py3k:
926            environ['PATH_INFO'] = path.encode('latin1').decode('utf8', 'ignore')
927
928        def _inner_handle():
929            # Maybe pass variables as locals for better performance?
930            try:
931                route, args = self.router.match(environ)
932                environ['route.handle'] = route
933                environ['bottle.route'] = route
934                environ['route.url_args'] = args
935                return route.call(**args)
936            except HTTPResponse:
937                return _e()
938            except RouteReset:
939                route.reset()
940                return _inner_handle()
941            except (KeyboardInterrupt, SystemExit, MemoryError):
942                raise
943            except Exception:
944                if not self.catchall: raise
945                stacktrace = format_exc()
946                environ['wsgi.errors'].write(stacktrace)
947                return HTTPError(500, "Internal Server Error", _e(), stacktrace)
948
949        try:
950            out = None
951            environ['bottle.app'] = self
952            request.bind(environ)
953            response.bind()
954            self.trigger_hook('before_request')
955            out = _inner_handle()
956            return out;
957        finally:
958            if isinstance(out, HTTPResponse):
959                out.apply(response)
960            self.trigger_hook('after_request')
961
962    def _cast(self, out, peek=None):
963        """ Try to convert the parameter into something WSGI compatible and set
964        correct HTTP headers when possible.
965        Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like,
966        iterable of strings and iterable of unicodes
967        """
968
969        # Empty output is done here
970        if not out:
971            if 'Content-Length' not in response:
972                response['Content-Length'] = 0
973            return []
974        # Join lists of byte or unicode strings. Mixed lists are NOT supported
975        if isinstance(out, (tuple, list))\
976        and isinstance(out[0], (bytes, unicode)):
977            out = out[0][0:0].join(out)  # b'abc'[0:0] -> b''
978        # Encode unicode strings
979        if isinstance(out, unicode):
980            out = out.encode(response.charset)
981        # Byte Strings are just returned
982        if isinstance(out, bytes):
983            if 'Content-Length' not in response:
984                response['Content-Length'] = len(out)
985            return [out]
986        # HTTPError or HTTPException (recursive, because they may wrap anything)
987        # TODO: Handle these explicitly in handle() or make them iterable.
988        if isinstance(out, HTTPError):
989            out.apply(response)
990            out = self.error_handler.get(out.status_code,
991                                         self.default_error_handler)(out)
992            return self._cast(out)
993        if isinstance(out, HTTPResponse):
994            out.apply(response)
995            return self._cast(out.body)
996
997        # File-like objects.
998        if hasattr(out, 'read'):
999            if 'wsgi.file_wrapper' in request.environ:
1000                return request.environ['wsgi.file_wrapper'](out)
1001            elif hasattr(out, 'close') or not hasattr(out, '__iter__'):
1002                return WSGIFileWrapper(out)
1003
1004        # Handle Iterables. We peek into them to detect their inner type.
1005        try:
1006            iout = iter(out)
1007            first = next(iout)
1008            while not first:
1009                first = next(iout)
1010        except StopIteration:
1011            return self._cast('')
1012        except HTTPResponse:
1013            first = _e()
1014        except (KeyboardInterrupt, SystemExit, MemoryError):
1015            raise
1016        except:
1017            if not self.catchall: raise
1018            first = HTTPError(500, 'Unhandled exception', _e(), format_exc())
1019
1020        # These are the inner types allowed in iterator or generator objects.
1021        if isinstance(first, HTTPResponse):
1022            return self._cast(first)
1023        elif isinstance(first, bytes):
1024            new_iter = itertools.chain([first], iout)
1025        elif isinstance(first, unicode):
1026            encoder = lambda x: x.encode(response.charset)
1027            new_iter = imap(encoder, itertools.chain([first], iout))
1028        else:
1029            msg = 'Unsupported response type: %s' % type(first)
1030            return self._cast(HTTPError(500, msg))
1031        if hasattr(out, 'close'):
1032            new_iter = _closeiter(new_iter, out.close)
1033        return new_iter
1034
1035    def wsgi(self, environ, start_response):
1036        """ The bottle WSGI-interface. """
1037        try:
1038            out = self._cast(self._handle(environ))
1039            # rfc2616 section 4.3
1040            if response._status_code in (100, 101, 204, 304)\
1041            or environ['REQUEST_METHOD'] == 'HEAD':
1042                if hasattr(out, 'close'): out.close()
1043                out = []
1044            start_response(response._status_line, response.headerlist)
1045            return out
1046        except (KeyboardInterrupt, SystemExit, MemoryError):
1047            raise
1048        except:
1049            if not self.catchall: raise
1050            err = '<h1>Critical error while processing request: %s</h1>' \
1051                  % html_escape(environ.get('PATH_INFO', '/'))
1052            if DEBUG:
1053                err += '<h2>Error:</h2>\n<pre>\n%s\n</pre>\n' \
1054                       '<h2>Traceback:</h2>\n<pre>\n%s\n</pre>\n' \
1055                       % (html_escape(repr(_e())), html_escape(format_exc()))
1056            environ['wsgi.errors'].write(err)
1057            headers = [('Content-Type', 'text/html; charset=UTF-8')]
1058            start_response('500 INTERNAL SERVER ERROR', headers, sys.exc_info())
1059            return [tob(err)]
1060
1061    def __call__(self, environ, start_response):
1062        """ Each instance of :class:'Bottle' is a WSGI application. """
1063        return self.wsgi(environ, start_response)
1064
1065    def __enter__(self):
1066        """ Use this application as default for all module-level shortcuts. """
1067        default_app.push(self)
1068        return self
1069
1070    def __exit__(self, exc_type, exc_value, traceback):
1071        default_app.pop()
1072
1073    def __setattr__(self, name, value):
1074        if name in self.__dict__:
1075            raise AttributeError("Attribute %s already defined. Plugin conflict?" % name)
1076        self.__dict__[name] = value
1077
1078
1079###############################################################################
1080# HTTP and WSGI Tools ##########################################################
1081###############################################################################
1082
1083
1084class BaseRequest(object):
1085    """ A wrapper for WSGI environment dictionaries that adds a lot of
1086        convenient access methods and properties. Most of them are read-only.
1087
1088        Adding new attributes to a request actually adds them to the environ
1089        dictionary (as 'bottle.request.ext.<name>'). This is the recommended
1090        way to store and access request-specific data.
1091    """
1092
1093    __slots__ = ('environ', )
1094
1095    #: Maximum size of memory buffer for :attr:`body` in bytes.
1096    MEMFILE_MAX = 102400
1097
1098    def __init__(self, environ=None):
1099        """ Wrap a WSGI environ dictionary. """
1100        #: The wrapped WSGI environ dictionary. This is the only real attribute.
1101        #: All other attributes actually are read-only properties.
1102        self.environ = {} if environ is None else environ
1103        self.environ['bottle.request'] = self
1104
1105    @DictProperty('environ', 'bottle.app', read_only=True)
1106    def app(self):
1107        """ Bottle application handling this request. """
1108        raise RuntimeError('This request is not connected to an application.')
1109
1110    @DictProperty('environ', 'bottle.route', read_only=True)
1111    def route(self):
1112        """ The bottle :class:`Route` object that matches this request. """
1113        raise RuntimeError('This request is not connected to a route.')
1114
1115    @DictProperty('environ', 'route.url_args', read_only=True)
1116    def url_args(self):
1117        """ The arguments extracted from the URL. """
1118        raise RuntimeError('This request is not connected to a route.')
1119
1120    @property
1121    def path(self):
1122        """ The value of ``PATH_INFO`` with exactly one prefixed slash (to fix
1123            broken clients and avoid the "empty path" edge case). """
1124        return '/' + self.environ.get('PATH_INFO', '').lstrip('/')
1125
1126    @property
1127    def method(self):
1128        """ The ``REQUEST_METHOD`` value as an uppercase string. """
1129        return self.environ.get('REQUEST_METHOD', 'GET').upper()
1130
1131    @DictProperty('environ', 'bottle.request.headers', read_only=True)
1132    def headers(self):
1133        """ A :class:`WSGIHeaderDict` that provides case-insensitive access to
1134            HTTP request headers. """
1135        return WSGIHeaderDict(self.environ)
1136
1137    def get_header(self, name, default=None):
1138        """ Return the value of a request header, or a given default value. """
1139        return self.headers.get(name, default)
1140
1141    @DictProperty('environ', 'bottle.request.cookies', read_only=True)
1142    def cookies(self):
1143        """ Cookies parsed into a :class:`FormsDict`. Signed cookies are NOT
1144            decoded. Use :meth:`get_cookie` if you expect signed cookies. """
1145        cookies = SimpleCookie(self.environ.get('HTTP_COOKIE', '')).values()
1146        return FormsDict((c.key, c.value) for c in cookies)
1147
1148    def get_cookie(self, key, default=None, secret=None):
1149        """ Return the content of a cookie. To read a `Signed Cookie`, the
1150            `secret` must match the one used to create the cookie (see
1151            :meth:`BaseResponse.set_cookie`). If anything goes wrong (missing
1152            cookie or wrong signature), return a default value. """
1153        value = self.cookies.get(key)
1154        if secret and value:
1155            dec = cookie_decode(value, secret)  # (key, value) tuple or None
1156            return dec[1] if dec and dec[0] == key else default
1157        return value or default
1158
1159    @DictProperty('environ', 'bottle.request.query', read_only=True)
1160    def query(self):
1161        """ The :attr:`query_string` parsed into a :class:`FormsDict`. These
1162            values are sometimes called "URL arguments" or "GET parameters", but
1163            not to be confused with "URL wildcards" as they are provided by the
1164            :class:`Router`. """
1165        get = self.environ['bottle.get'] = FormsDict()
1166        pairs = _parse_qsl(self.environ.get('QUERY_STRING', ''))
1167        for key, value in pairs:
1168            get[key] = value
1169        return get
1170
1171    @DictProperty('environ', 'bottle.request.forms', read_only=True)
1172    def forms(self):
1173        """ Form values parsed from an `url-encoded` or `multipart/form-data`
1174            encoded POST or PUT request body. The result is returned as a
1175            :class:`FormsDict`. All keys and values are strings. File uploads
1176            are stored separately in :attr:`files`. """
1177        forms = FormsDict()
1178        for name, item in self.POST.allitems():
1179            if not isinstance(item, FileUpload):
1180                forms[name] = item
1181        return forms
1182
1183    @DictProperty('environ', 'bottle.request.params', read_only=True)
1184    def params(self):
1185        """ A :class:`FormsDict` with the combined values of :attr:`query` and
1186            :attr:`forms`. File uploads are stored in :attr:`files`. """
1187        params = FormsDict()
1188        for key, value in self.query.allitems():
1189            params[key] = value
1190        for key, value in self.forms.allitems():
1191            params[key] = value
1192        return params
1193
1194    @DictProperty('environ', 'bottle.request.files', read_only=True)
1195    def files(self):
1196        """ File uploads parsed from `multipart/form-data` encoded POST or PUT
1197            request body. The values are instances of :class:`FileUpload`.
1198
1199        """
1200        files = FormsDict()
1201        for name, item in self.POST.allitems():
1202            if isinstance(item, FileUpload):
1203                files[name] = item
1204        return files
1205
1206    @DictProperty('environ', 'bottle.request.json', read_only=True)
1207    def json(self):
1208        """ If the ``Content-Type`` header is ``application/json``, this
1209            property holds the parsed content of the request body. Only requests
1210            smaller than :attr:`MEMFILE_MAX` are processed to avoid memory
1211            exhaustion. Invalid JSON raises a 400 error response. """
1212        ctype = self.environ.get('CONTENT_TYPE', '').lower().split(';')[0]
1213        if ctype == 'application/json':
1214            b = self._get_body_string()
1215            if not b:
1216                return None
1217            try:
1218                return json_loads(b)
1219            except (ValueError, TypeError):
1220                raise HTTPError(400, 'Invalid JSON')
1221        return None
1222
1223    def _iter_body(self, read, bufsize):
1224        maxread = max(0, self.content_length)
1225        while maxread:
1226            part = read(min(maxread, bufsize))
1227            if not part: break
1228            yield part
1229            maxread -= len(part)
1230
1231    @staticmethod
1232    def _iter_chunked(read, bufsize):
1233        err = HTTPError(400, 'Error while parsing chunked transfer body.')
1234        rn, sem, bs = tob('\r\n'), tob(';'), tob('')
1235        while True:
1236            header = read(1)
1237            while header[-2:] != rn:
1238                c = read(1)
1239                header += c
1240                if not c: raise err
1241                if len(header) > bufsize: raise err
1242            size, _, _ = header.partition(sem)
1243            try:
1244                maxread = int(tonat(size.strip()), 16)
1245            except ValueError:
1246                raise err
1247            if maxread == 0: break
1248            buff = bs
1249            while maxread > 0:
1250                if not buff:
1251                    buff = read(min(maxread, bufsize))
1252                part, buff = buff[:maxread], buff[maxread:]
1253                if not part: raise err
1254                yield part
1255                maxread -= len(part)
1256            if read(2) != rn:
1257                raise err
1258
1259    @DictProperty('environ', 'bottle.request.body', read_only=True)
1260    def _body(self):
1261        try:
1262            read_func = self.environ['wsgi.input'].read
1263        except KeyError:
1264            self.environ['wsgi.input'] = BytesIO()
1265            return self.environ['wsgi.input']
1266        body_iter = self._iter_chunked if self.chunked else self._iter_body
1267        body, body_size, is_temp_file = BytesIO(), 0, False
1268        for part in body_iter(read_func, self.MEMFILE_MAX):
1269            body.write(part)
1270            body_size += len(part)
1271            if not is_temp_file and body_size > self.MEMFILE_MAX:
1272                body, tmp = TemporaryFile(mode='w+b'), body
1273                body.write(tmp.getvalue())
1274                del tmp
1275                is_temp_file = True
1276        self.environ['wsgi.input'] = body
1277        body.seek(0)
1278        return body
1279
1280    def _get_body_string(self):
1281        """ read body until content-length or MEMFILE_MAX into a string. Raise
1282            HTTPError(413) on requests that are to large. """
1283        clen = self.content_length
1284        if clen > self.MEMFILE_MAX:
1285            raise HTTPError(413, 'Request entity too large')
1286        if clen < 0: clen = self.MEMFILE_MAX + 1
1287        data = self.body.read(clen)
1288        if len(data) > self.MEMFILE_MAX:  # Fail fast
1289            raise HTTPError(413, 'Request entity too large')
1290        return data
1291
1292    @property
1293    def body(self):
1294        """ The HTTP request body as a seek-able file-like object. Depending on
1295            :attr:`MEMFILE_MAX`, this is either a temporary file or a
1296            :class:`io.BytesIO` instance. Accessing this property for the first
1297            time reads and replaces the ``wsgi.input`` environ variable.
1298            Subsequent accesses just do a `seek(0)` on the file object. """
1299        self._body.seek(0)
1300        return self._body
1301
1302    @property
1303    def chunked(self):
1304        """ True if Chunked transfer encoding was. """
1305        return 'chunked' in self.environ.get(
1306            'HTTP_TRANSFER_ENCODING', '').lower()
1307
1308    #: An alias for :attr:`query`.
1309    GET = query
1310
1311    @DictProperty('environ', 'bottle.request.post', read_only=True)
1312    def POST(self):
1313        """ The values of :attr:`forms` and :attr:`files` combined into a single
1314            :class:`FormsDict`. Values are either strings (form values) or
1315            instances of :class:`cgi.FieldStorage` (file uploads).
1316        """
1317        post = FormsDict()
1318        # We default to application/x-www-form-urlencoded for everything that
1319        # is not multipart and take the fast path (also: 3.1 workaround)
1320        if not self.content_type.startswith('multipart/'):
1321            pairs = _parse_qsl(tonat(self._get_body_string(), 'latin1'))
1322            for key, value in pairs:
1323                post[key] = value
1324            return post
1325
1326        safe_env = {'QUERY_STRING': ''}  # Build a safe environment for cgi
1327        for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'):
1328            if key in self.environ: safe_env[key] = self.environ[key]
1329        args = dict(fp=self.body, environ=safe_env, keep_blank_values=True)
1330        if py31:
1331            args['fp'] = NCTextIOWrapper(args['fp'],
1332                                         encoding='utf8',
1333                                         newline='\n')
1334        elif py3k:
1335            args['encoding'] = 'utf8'
1336        data = cgi.FieldStorage(**args)
1337        self['_cgi.FieldStorage'] = data  #http://bugs.python.org/issue18394
1338        data = data.list or []
1339        for item in data:
1340            if item.filename:
1341                post[item.name] = FileUpload(item.file, item.name,
1342                                             item.filename, item.headers)
1343            else:
1344                post[item.name] = item.value
1345        return post
1346
1347    @property
1348    def url(self):
1349        """ The full request URI including hostname and scheme. If your app
1350            lives behind a reverse proxy or load balancer and you get confusing
1351            results, make sure that the ``X-Forwarded-Host`` header is set
1352            correctly. """
1353        return self.urlparts.geturl()
1354
1355    @DictProperty('environ', 'bottle.request.urlparts', read_only=True)
1356    def urlparts(self):
1357        """ The :attr:`url` string as an :class:`urlparse.SplitResult` tuple.
1358            The tuple contains (scheme, host, path, query_string and fragment),
1359            but the fragment is always empty because it is not visible to the
1360            server. """
1361        env = self.environ
1362        http = env.get('HTTP_X_FORWARDED_PROTO') \
1363             or env.get('wsgi.url_scheme', 'http')
1364        host = env.get('HTTP_X_FORWARDED_HOST') or env.get('HTTP_HOST')
1365        if not host:
1366            # HTTP 1.1 requires a Host-header. This is for HTTP/1.0 clients.
1367            host = env.get('SERVER_NAME', '127.0.0.1')
1368            port = env.get('SERVER_PORT')
1369            if port and port != ('80' if http == 'http' else '443'):
1370                host += ':' + port
1371        path = urlquote(self.fullpath)
1372        return UrlSplitResult(http, host, path, env.get('QUERY_STRING'), '')
1373
1374    @property
1375    def fullpath(self):
1376        """ Request path including :attr:`script_name` (if present). """
1377        return urljoin(self.script_name, self.path.lstrip('/'))
1378
1379    @property
1380    def query_string(self):
1381        """ The raw :attr:`query` part of the URL (everything in between ``?``
1382            and ``#``) as a string. """
1383        return self.environ.get('QUERY_STRING', '')
1384
1385    @property
1386    def script_name(self):
1387        """ The initial portion of the URL's `path` that was removed by a higher
1388            level (server or routing middleware) before the application was
1389            called. This script path is returned with leading and tailing
1390            slashes. """
1391        script_name = self.environ.get('SCRIPT_NAME', '').strip('/')
1392        return '/' + script_name + '/' if script_name else '/'
1393
1394    def path_shift(self, shift=1):
1395        """ Shift path segments from :attr:`path` to :attr:`script_name` and
1396            vice versa.
1397
1398           :param shift: The number of path segments to shift. May be negative
1399                         to change the shift direction. (default: 1)
1400        """
1401        script, path = path_shift(self.environ.get('SCRIPT_NAME', '/'), self.path, shift)
1402        self['SCRIPT_NAME'], self['PATH_INFO'] = script, path
1403
1404    @property
1405    def content_length(self):
1406        """ The request body length as an integer. The client is responsible to
1407            set this header. Otherwise, the real length of the body is unknown
1408            and -1 is returned. In this case, :attr:`body` will be empty. """
1409        return int(self.environ.get('CONTENT_LENGTH') or -1)
1410
1411    @property
1412    def content_type(self):
1413        """ The Content-Type header as a lowercase-string (default: empty). """
1414        return self.environ.get('CONTENT_TYPE', '').lower()
1415
1416    @property
1417    def is_xhr(self):
1418        """ True if the request was triggered by a XMLHttpRequest. This only
1419            works with JavaScript libraries that support the `X-Requested-With`
1420            header (most of the popular libraries do). """
1421        requested_with = self.environ.get('HTTP_X_REQUESTED_WITH', '')
1422        return requested_with.lower() == 'xmlhttprequest'
1423
1424    @property
1425    def is_ajax(self):
1426        """ Alias for :attr:`is_xhr`. "Ajax" is not the right term. """
1427        return self.is_xhr
1428
1429    @property
1430    def auth(self):
1431        """ HTTP authentication data as a (user, password) tuple. This
1432            implementation currently supports basic (not digest) authentication
1433            only. If the authentication happened at a higher level (e.g. in the
1434            front web-server or a middleware), the password field is None, but
1435            the user field is looked up from the ``REMOTE_USER`` environ
1436            variable. On any errors, None is returned. """
1437        basic = parse_auth(self.environ.get('HTTP_AUTHORIZATION', ''))
1438        if basic: return basic
1439        ruser = self.environ.get('REMOTE_USER')
1440        if ruser: return (ruser, None)
1441        return None
1442
1443    @property
1444    def remote_route(self):
1445        """ A list of all IPs that were involved in this request, starting with
1446            the client IP and followed by zero or more proxies. This does only
1447            work if all proxies support the ```X-Forwarded-For`` header. Note
1448            that this information can be forged by malicious clients. """
1449        proxy = self.environ.get('HTTP_X_FORWARDED_FOR')
1450        if proxy: return [ip.strip() for ip in proxy.split(',')]
1451        remote = self.environ.get('REMOTE_ADDR')
1452        return [remote] if remote else []
1453
1454    @property
1455    def remote_addr(self):
1456        """ The client IP as a string. Note that this information can be forged
1457            by malicious clients. """
1458        route = self.remote_route
1459        return route[0] if route else None
1460
1461    def copy(self):
1462        """ Return a new :class:`Request` with a shallow :attr:`environ` copy. """
1463        return Request(self.environ.copy())
1464
1465    def get(self, value, default=None):
1466        return self.environ.get(value, default)
1467
1468    def __getitem__(self, key):
1469        return self.environ[key]
1470
1471    def __delitem__(self, key):
1472        self[key] = ""
1473        del (self.environ[key])
1474
1475    def __iter__(self):
1476        return iter(self.environ)
1477
1478    def __len__(self):
1479        return len(self.environ)
1480
1481    def keys(self):
1482        return self.environ.keys()
1483
1484    def __setitem__(self, key, value):
1485        """ Change an environ value and clear all caches that depend on it. """
1486
1487        if self.environ.get('bottle.request.readonly'):
1488            raise KeyError('The environ dictionary is read-only.')
1489
1490        self.environ[key] = value
1491        todelete = ()
1492
1493        if key == 'wsgi.input':
1494            todelete = ('body', 'forms', 'files', 'params', 'post', 'json')
1495        elif key == 'QUERY_STRING':
1496            todelete = ('query', 'params')
1497        elif key.startswith('HTTP_'):
1498            todelete = ('headers', 'cookies')
1499
1500        for key in todelete:
1501            self.environ.pop('bottle.request.' + key, None)
1502
1503    def __repr__(self):
1504        return '<%s: %s %s>' % (self.__class__.__name__, self.method, self.url)
1505
1506    def __getattr__(self, name):
1507        """ Search in self.environ for additional user defined attributes. """
1508        try:
1509            var = self.environ['bottle.request.ext.%s' % name]
1510            return var.__get__(self) if hasattr(var, '__get__') else var
1511        except KeyError:
1512            raise AttributeError('Attribute %r not defined.' % name)
1513
1514    def __setattr__(self, name, value):
1515        if name == 'environ': return object.__setattr__(self, name, value)
1516        key = 'bottle.request.ext.%s' % name
1517        if key in self.environ:
1518            raise AttributeError("Attribute already defined: %s" % name)
1519        self.environ[key] = value
1520
1521    def __delattr__(self, name, value):
1522        try:
1523            del self.environ['bottle.request.ext.%s' % name]
1524        except KeyError:
1525            raise AttributeError("Attribute not defined: %s" % name)
1526
1527def _hkey(s):
1528    return s.title().replace('_', '-')
1529
1530
1531class HeaderProperty(object):
1532    def __init__(self, name, reader=None, writer=str, default=''):
1533        self.name, self.default = name, default
1534        self.reader, self.writer = reader, writer
1535        self.__doc__ = 'Current value of the %r header.' % name.title()
1536
1537    def __get__(self, obj, _):
1538        if obj is None: return self
1539        value = obj.headers.get(self.name, self.default)
1540        return self.reader(value) if self.reader else value
1541
1542    def __set__(self, obj, value):
1543        obj.headers[self.name] = self.writer(value)
1544
1545    def __delete__(self, obj):
1546        del obj.headers[self.name]
1547
1548
1549class BaseResponse(object):
1550    """ Storage class for a response body as well as headers and cookies.
1551
1552        This class does support dict-like case-insensitive item-access to
1553        headers, but is NOT a dict. Most notably, iterating over a response
1554        yields parts of the body and not the headers.
1555
1556        :param body: The response body as one of the supported types.
1557        :param status: Either an HTTP status code (e.g. 200) or a status line
1558                       including the reason phrase (e.g. '200 OK').
1559        :param headers: A dictionary or a list of name-value pairs.
1560
1561        Additional keyword arguments are added to the list of headers.
1562        Underscores in the header name are replaced with dashes.
1563    """
1564
1565    default_status = 200
1566    default_content_type = 'text/html; charset=UTF-8'
1567
1568    # Header blacklist for specific response codes
1569    # (rfc2616 section 10.2.3 and 10.3.5)
1570    bad_headers = {
1571        204: set(('Content-Type', )),
1572        304: set(('Allow', 'Content-Encoding', 'Content-Language',
1573                  'Content-Length', 'Content-Range', 'Content-Type',
1574                  'Content-Md5', 'Last-Modified'))
1575    }
1576
1577    def __init__(self, body='', status=None, headers=None, **more_headers):
1578        self._cookies = None
1579        self._headers = {}
1580        self.body = body
1581        self.status = status or self.default_status
1582        if headers:
1583            if isinstance(headers, dict):
1584                headers = headers.items()
1585            for name, value in headers:
1586                self.add_header(name, value)
1587        if more_headers:
1588            for name, value in more_headers.items():
1589                self.add_header(name, value)
1590
1591    def copy(self, cls=None):
1592        """ Returns a copy of self. """
1593        cls = cls or BaseResponse
1594        assert issubclass(cls, BaseResponse)
1595        copy = cls()
1596        copy.status = self.status
1597        copy._headers = dict((k, v[:]) for (k, v) in self._headers.items())
1598        if self._cookies:
1599            copy._cookies = SimpleCookie()
1600            copy._cookies.load(self._cookies.output(header=''))
1601        return copy
1602
1603    def __iter__(self):
1604        return iter(self.body)
1605
1606    def close(self):
1607        if hasattr(self.body, 'close'):
1608            self.body.close()
1609
1610    @property
1611    def status_line(self):
1612        """ The HTTP status line as a string (e.g. ``404 Not Found``)."""
1613        return self._status_line
1614
1615    @property
1616    def status_code(self):
1617        """ The HTTP status code as an integer (e.g. 404)."""
1618        return self._status_code
1619
1620    def _set_status(self, status):
1621        if isinstance(status, int):
1622            code, status = status, _HTTP_STATUS_LINES.get(status)
1623        elif ' ' in status:
1624            status = status.strip()
1625            code = int(status.split()[0])
1626        else:
1627            raise ValueError('String status line without a reason phrase.')
1628        if not 100 <= code <= 999:
1629            raise ValueError('Status code out of range.')
1630        self._status_code = code
1631        self._status_line = str(status or ('%d Unknown' % code))
1632
1633    def _get_status(self):
1634        return self._status_line
1635
1636    status = property(
1637        _get_status, _set_status, None,
1638        ''' A writeable property to change the HTTP response status. It accepts
1639            either a numeric code (100-999) or a string with a custom reason
1640            phrase (e.g. "404 Brain not found"). Both :data:`status_line` and
1641            :data:`status_code` are updated accordingly. The return value is
1642            always a status string. ''')
1643    del _get_status, _set_status
1644
1645    @property
1646    def headers(self):
1647        """ An instance of :class:`HeaderDict`, a case-insensitive dict-like
1648            view on the response headers. """
1649        hdict = HeaderDict()
1650        hdict.dict = self._headers
1651        return hdict
1652
1653    def __contains__(self, name):
1654        return _hkey(name) in self._headers
1655
1656    def __delitem__(self, name):
1657        del self._headers[_hkey(name)]
1658
1659    def __getitem__(self, name):
1660        return self._headers[_hkey(name)][-1]
1661
1662    def __setitem__(self, name, value):
1663        self._headers[_hkey(name)] = [value if isinstance(value, unicode) else
1664                                      str(value)]
1665
1666    def get_header(self, name, default=None):
1667        """ Return the value of a previously defined header. If there is no
1668            header with that name, return a default value. """
1669        return self._headers.get(_hkey(name), [default])[-1]
1670
1671    def set_header(self, name, value):
1672        """ Create a new response header, replacing any previously defined
1673            headers with the same name. """
1674        self._headers[_hkey(name)] = [value if isinstance(value, unicode)
1675                                            else str(value)]
1676
1677    def add_header(self, name, value):
1678        """ Add an additional response header, not removing duplicates. """
1679        self._headers.setdefault(_hkey(name), []).append(
1680            value if isinstance(value, unicode) else str(value))
1681
1682    def iter_headers(self):
1683        """ Yield (header, value) tuples, skipping headers that are not
1684            allowed with the current response status code. """
1685        return self.headerlist
1686
1687    @property
1688    def headerlist(self):
1689        """ WSGI conform list of (header, value) tuples. """
1690        out = []
1691        headers = list(self._headers.items())
1692        if 'Content-Type' not in self._headers:
1693            headers.append(('Content-Type', [self.default_content_type]))
1694        if self._status_code in self.bad_headers:
1695            bad_headers = self.bad_headers[self._status_code]
1696            headers = [h for h in headers if h[0] not in bad_headers]
1697        out += [(name, val) for (name, vals) in headers for val in vals]
1698        if self._cookies:
1699            for c in self._cookies.values():
1700                out.append(('Set-Cookie', c.OutputString()))
1701        if py3k:
1702            return [(k, v.encode('utf8').decode('latin1')) for (k, v) in out]
1703        else:
1704            return [(k, v.encode('utf8') if isinstance(v, unicode) else v)
1705                    for (k, v) in out]
1706
1707    content_type = HeaderProperty('Content-Type')
1708    content_length = HeaderProperty('Content-Length', reader=int)
1709    expires = HeaderProperty(
1710        'Expires',
1711        reader=lambda x: datetime.utcfromtimestamp(parse_date(x)),
1712        writer=lambda x: http_date(x))
1713
1714    @property
1715    def charset(self, default='UTF-8'):
1716        """ Return the charset specified in the content-type header (default: utf8). """
1717        if 'charset=' in self.content_type:
1718            return self.content_type.split('charset=')[-1].split(';')[0].strip()
1719        return default
1720
1721    def set_cookie(self, name, value, secret=None, **options):
1722        """ Create a new cookie or replace an old one. If the `secret` parameter is
1723            set, create a `Signed Cookie` (described below).
1724
1725            :param name: the name of the cookie.
1726            :param value: the value of the cookie.
1727            :param secret: a signature key required for signed cookies.
1728
1729            Additionally, this method accepts all RFC 2109 attributes that are
1730            supported by :class:`cookie.Morsel`, including:
1731
1732            :param max_age: maximum age in seconds. (default: None)
1733            :param expires: a datetime object or UNIX timestamp. (default: None)
1734            :param domain: the domain that is allowed to read the cookie.
1735              (default: current domain)
1736            :param path: limits the cookie to a given path (default: current path)
1737            :param secure: limit the cookie to HTTPS connections (default: off).
1738            :param httponly: prevents client-side javascript to read this cookie
1739              (default: off, requires Python 2.6 or newer).
1740
1741            If neither `expires` nor `max_age` is set (default), the cookie will
1742            expire at the end of the browser session (as soon as the browser
1743            window is closed).
1744
1745            Signed cookies may store any pickle-able object and are
1746            cryptographically signed to prevent manipulation. Keep in mind that
1747            cookies are limited to 4kb in most browsers.
1748
1749            Warning: Signed cookies are not encrypted (the client can still see
1750            the content) and not copy-protected (the client can restore an old
1751            cookie). The main intention is to make pickling and unpickling
1752            save, not to store secret information at client side.
1753        """
1754        if not self._cookies:
1755            self._cookies = SimpleCookie()
1756
1757        if secret:
1758            value = touni(cookie_encode((name, value), secret))
1759        elif not isinstance(value, basestring):
1760            raise TypeError('Secret key missing for non-string Cookie.')
1761
1762        # Cookie size plus options must not exceed 4kb.
1763        if len(name) + len(value) > 3800:
1764            raise ValueError('Content does not fit into a cookie.')
1765
1766        self._cookies[name] = value
1767
1768        for key, value in options.items():
1769            if key == 'max_age':
1770                if isinstance(value, timedelta):
1771                    value = value.seconds + value.days * 24 * 3600
1772            if key == 'expires':
1773                if isinstance(value, (datedate, datetime)):
1774                    value = value.timetuple()
1775                elif isinstance(value, (int, float)):
1776                    value = time.gmtime(value)
1777                value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value)
1778            if key in ('secure', 'httponly') and not value:
1779                continue
1780            self._cookies[name][key.replace('_', '-')] = value
1781
1782    def delete_cookie(self, key, **kwargs):
1783        """ Delete a cookie. Be sure to use the same `domain` and `path`
1784            settings as used to create the cookie. """
1785        kwargs['max_age'] = -1
1786        kwargs['expires'] = 0
1787        self.set_cookie(key, '', **kwargs)
1788
1789    def __repr__(self):
1790        out = ''
1791        for name, value in self.headerlist:
1792            out += '%s: %s\n' % (name.title(), value.strip())
1793        return out
1794
1795
1796def _local_property():
1797    ls = threading.local()
1798
1799    def fget(_):
1800        try:
1801            return ls.var
1802        except AttributeError:
1803            raise RuntimeError("Request context not initialized.")
1804
1805    def fset(_, value):
1806        ls.var = value
1807
1808    def fdel(_):
1809        del ls.var
1810
1811    return property(fget, fset, fdel, 'Thread-local property')
1812
1813
1814class LocalRequest(BaseRequest):
1815    """ A thread-local subclass of :class:`BaseRequest` with a different
1816        set of attributes for each thread. There is usually only one global
1817        instance of this class (:data:`request`). If accessed during a
1818        request/response cycle, this instance always refers to the *current*
1819        request (even on a multithreaded server). """
1820    bind = BaseRequest.__init__
1821    environ = _local_property()
1822
1823
1824class LocalResponse(BaseResponse):
1825    """ A thread-local subclass of :class:`BaseResponse` with a different
1826        set of attributes for each thread. There is usually only one global
1827        instance of this class (:data:`response`). Its attributes are used
1828        to build the HTTP response at the end of the request/response cycle.
1829    """
1830    bind = BaseResponse.__init__
1831    _status_line = _local_property()
1832    _status_code = _local_property()
1833    _cookies = _local_property()
1834    _headers = _local_property()
1835    body = _local_property()
1836
1837
1838Request = BaseRequest
1839Response = BaseResponse
1840
1841
1842class HTTPResponse(Response, BottleException):
1843    def __init__(self, body='', status=None, headers=None, **more_headers):
1844        super(HTTPResponse, self).__init__(body, status, headers, **more_headers)
1845
1846    def apply(self, other):
1847        other._status_code = self._status_code
1848        other._status_line = self._status_line
1849        other._headers = self._headers
1850        other._cookies = self._cookies
1851        other.body = self.body
1852
1853
1854class HTTPError(HTTPResponse):
1855    default_status = 500
1856
1857    def __init__(self,
1858                 status=None,
1859                 body=None,
1860                 exception=None,
1861                 traceback=None, **more_headers):
1862        self.exception = exception
1863        self.traceback = traceback
1864        super(HTTPError, self).__init__(body, status, **more_headers)
1865
1866###############################################################################
1867# Plugins ######################################################################
1868###############################################################################
1869
1870
1871class PluginError(BottleException):
1872    pass
1873
1874
1875class JSONPlugin(object):
1876    name = 'json'
1877    api = 2
1878
1879    def __init__(self, json_dumps=json_dumps):
1880        self.json_dumps = json_dumps
1881
1882    def apply(self, callback, _):
1883        dumps = self.json_dumps
1884        if not dumps: return callback
1885
1886        def wrapper(*a, **ka):
1887            try:
1888                rv = callback(*a, **ka)
1889            except HTTPError:
1890                rv = _e()
1891
1892            if isinstance(rv, dict):
1893                #Attempt to serialize, raises exception on failure
1894                json_response = dumps(rv)
1895                #Set content type only if serialization successful
1896                response.content_type = 'application/json'
1897                return json_response
1898            elif isinstance(rv, HTTPResponse) and isinstance(rv.body, dict):
1899                rv.body = dumps(rv.body)
1900                rv.content_type = 'application/json'
1901            return rv
1902
1903        return wrapper
1904
1905
1906class TemplatePlugin(object):
1907    """ This plugin applies the :func:`view` decorator to all routes with a
1908        `template` config parameter. If the parameter is a tuple, the second
1909        element must be a dict with additional options (e.g. `template_engine`)
1910        or default variables for the template. """
1911    name = 'template'
1912    api = 2
1913
1914    def setup(self, app):
1915        app.tpl = self
1916
1917    def apply(self, callback, route):
1918        conf = route.config.get('template')
1919        if isinstance(conf, (tuple, list)) and len(conf) == 2:
1920            return view(conf[0], **conf[1])(callback)
1921        elif isinstance(conf, str):
1922            return view(conf)(callback)
1923        else:
1924            return callback
1925
1926
1927#: Not a plugin, but part of the plugin API. TODO: Find a better place.
1928class _ImportRedirect(object):
1929    def __init__(self, name, impmask):
1930        """ Create a virtual package that redirects imports (see PEP 302). """
1931        self.name = name
1932        self.impmask = impmask
1933        self.module = sys.modules.setdefault(name, imp.new_module(name))
1934        self.module.__dict__.update({
1935            '__file__': __file__,
1936            '__path__': [],
1937            '__all__': [],
1938            '__loader__': self
1939        })
1940        sys.meta_path.append(self)
1941
1942    def find_module(self, fullname, path=None):
1943        if '.' not in fullname: return
1944        packname = fullname.rsplit('.', 1)[0]
1945        if packname != self.name: return
1946        return self
1947
1948    def load_module(self, fullname):
1949        if fullname in sys.modules: return sys.modules[fullname]
1950        modname = fullname.rsplit('.', 1)[1]
1951        realname = self.impmask % modname
1952        __import__(realname)
1953        module = sys.modules[fullname] = sys.modules[realname]
1954        setattr(self.module, modname, module)
1955        module.__loader__ = self
1956        return module
1957
1958###############################################################################
1959# Common Utilities #############################################################
1960###############################################################################
1961
1962
1963class MultiDict(DictMixin):
1964    """ This dict stores multiple values per key, but behaves exactly like a
1965        normal dict in that it returns only the newest value for any given key.
1966        There are special methods available to access the full list of values.
1967    """
1968
1969    def __init__(self, *a, **k):
1970        self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items())
1971
1972    def __len__(self):
1973        return len(self.dict)
1974
1975    def __iter__(self):
1976        return iter(self.dict)
1977
1978    def __contains__(self, key):
1979        return key in self.dict
1980
1981    def __delitem__(self, key):
1982        del self.dict[key]
1983
1984    def __getitem__(self, key):
1985        return self.dict[key][-1]
1986
1987    def __setitem__(self, key, value):
1988        self.append(key, value)
1989
1990    def keys(self):
1991        return self.dict.keys()
1992
1993    if py3k:
1994
1995        def values(self):
1996            return (v[-1] for v in self.dict.values())
1997
1998        def items(self):
1999            return ((k, v[-1]) for k, v in self.dict.items())
2000
2001        def allitems(self):
2002            return ((k, v) for k, vl in self.dict.items() for v in vl)
2003
2004        iterkeys = keys
2005        itervalues = values
2006        iteritems = items
2007        iterallitems = allitems
2008
2009    else:
2010
2011        def values(self):
2012            return [v[-1] for v in self.dict.values()]
2013
2014        def items(self):
2015            return [(k, v[-1]) for k, v in self.dict.items()]
2016
2017        def iterkeys(self):
2018            return self.dict.iterkeys()
2019
2020        def itervalues(self):
2021            return (v[-1] for v in self.dict.itervalues())
2022
2023        def iteritems(self):
2024            return ((k, v[-1]) for k, v in self.dict.iteritems())
2025
2026        def iterallitems(self):
2027            return ((k, v) for k, vl in self.dict.iteritems() for v in vl)
2028
2029        def allitems(self):
2030            return [(k, v) for k, vl in self.dict.iteritems() for v in vl]
2031
2032    def get(self, key, default=None, index=-1, type=None):
2033        """ Return the most recent value for a key.
2034
2035            :param default: The default value to be returned if the key is not
2036                   present or the type conversion fails.
2037            :param index: An index for the list of available values.
2038            :param type: If defined, this callable is used to cast the value
2039                    into a specific type. Exception are suppressed and result in
2040                    the default value to be returned.
2041        """
2042        try:
2043            val = self.dict[key][index]
2044            return type(val) if type else val
2045        except Exception:
2046            pass
2047        return default
2048
2049    def append(self, key, value):
2050        """ Add a new value to the list of values for this key. """
2051        self.dict.setdefault(key, []).append(value)
2052
2053    def replace(self, key, value):
2054        """ Replace the list of values with a single value. """
2055        self.dict[key] = [value]
2056
2057    def getall(self, key):
2058        """ Return a (possibly empty) list of values for a key. """
2059        return self.dict.get(key) or []
2060
2061    #: Aliases for WTForms to mimic other multi-dict APIs (Django)
2062    getone = get
2063    getlist = getall
2064
2065
2066class FormsDict(MultiDict):
2067    """ This :class:`MultiDict` subclass is used to store request form data.
2068        Additionally to the normal dict-like item access methods (which return
2069        unmodified data as native strings), this container also supports
2070        attribute-like access to its values. Attributes are automatically de-
2071        or recoded to match :attr:`input_encoding` (default: 'utf8'). Missing
2072        attributes default to an empty string. """
2073
2074    #: Encoding used for attribute values.
2075    input_encoding = 'utf8'
2076    #: If true (default), unicode strings are first encoded with `latin1`
2077    #: and then decoded to match :attr:`input_encoding`.
2078    recode_unicode = True
2079
2080    def _fix(self, s, encoding=None):
2081        if isinstance(s, unicode) and self.recode_unicode:  # Python 3 WSGI
2082            return s.encode('latin1').decode(encoding or self.input_encoding)
2083        elif isinstance(s, bytes):  # Python 2 WSGI
2084            return s.decode(encoding or self.input_encoding)
2085        else:
2086            return s
2087
2088    def decode(self, encoding=None):
2089        """ Returns a copy with all keys and values de- or recoded to match
2090            :attr:`input_encoding`. Some libraries (e.g. WTForms) want a
2091            unicode dictionary. """
2092        copy = FormsDict()
2093        enc = copy.input_encoding = encoding or self.input_encoding
2094        copy.recode_unicode = False
2095        for key, value in self.allitems():
2096            copy.append(self._fix(key, enc), self._fix(value, enc))
2097        return copy
2098
2099    def getunicode(self, name, default=None, encoding=None):
2100        """ Return the value as a unicode string, or the default. """
2101        try:
2102            return self._fix(self[name], encoding)
2103        except (UnicodeError, KeyError):
2104            return default
2105
2106    def __getattr__(self, name, default=unicode()):
2107        # Without this guard, pickle generates a cryptic TypeError:
2108        if name.startswith('__') and name.endswith('__'):
2109            return super(FormsDict, self).__getattr__(name)
2110        return self.getunicode(name, default=default)
2111
2112
2113class HeaderDict(MultiDict):
2114    """ A case-insensitive version of :class:`MultiDict` that defaults to
2115        replace the old value instead of appending it. """
2116
2117    def __init__(self, *a, **ka):
2118        self.dict = {}
2119        if a or ka: self.update(*a, **ka)
2120
2121    def __contains__(self, key):
2122        return _hkey(key) in self.dict
2123
2124    def __delitem__(self, key):
2125        del self.dict[_hkey(key)]
2126
2127    def __getitem__(self, key):
2128        return self.dict[_hkey(key)][-1]
2129
2130    def __setitem__(self, key, value):
2131        self.dict[_hkey(key)] = [value if isinstance(value, unicode) else
2132                                 str(value)]
2133
2134    def append(self, key, value):
2135        self.dict.setdefault(_hkey(key), []).append(
2136            value if isinstance(value, unicode) else str(value))
2137
2138    def replace(self, key, value):
2139        self.dict[_hkey(key)] = [value if isinstance(value, unicode) else
2140                                 str(value)]
2141
2142    def getall(self, key):
2143        return self.dict.get(_hkey(key)) or []
2144
2145    def get(self, key, default=None, index=-1):
2146        return MultiDict.get(self, _hkey(key), default, index)
2147
2148    def filter(self, names):
2149        for name in [_hkey(n) for n in names]:
2150            if name in self.dict:
2151                del self.dict[name]
2152
2153
2154class WSGIHeaderDict(DictMixin):
2155    """ This dict-like class wraps a WSGI environ dict and provides convenient
2156        access to HTTP_* fields. Keys and values are native strings
2157        (2.x bytes or 3.x unicode) and keys are case-insensitive. If the WSGI
2158        environment contains non-native string values, these are de- or encoded
2159        using a lossless 'latin1' character set.
2160
2161        The API will remain stable even on changes to the relevant PEPs.
2162        Currently PEP 333, 444 and 3333 are supported. (PEP 444 is the only one
2163        that uses non-native strings.)
2164    """
2165    #: List of keys that do not have a ``HTTP_`` prefix.
2166    cgikeys = ('CONTENT_TYPE', 'CONTENT_LENGTH')
2167
2168    def __init__(self, environ):
2169        self.environ = environ
2170
2171    def _ekey(self, key):
2172        """ Translate header field name to CGI/WSGI environ key. """
2173        key = key.replace('-', '_').upper()
2174        if key in self.cgikeys:
2175            return key
2176        return 'HTTP_' + key
2177
2178    def raw(self, key, default=None):
2179        """ Return the header value as is (may be bytes or unicode). """
2180        return self.environ.get(self._ekey(key), default)
2181
2182    def __getitem__(self, key):
2183        val = self.environ[self._ekey(key)]
2184        if py3k:
2185            if isinstance(val, unicode):
2186                val = val.encode('latin1').decode('utf8')
2187            else:
2188                val = val.decode('utf8')
2189        return val
2190
2191    def __setitem__(self, key, value):
2192        raise TypeError("%s is read-only." % self.__class__)
2193
2194    def __delitem__(self, key):
2195        raise TypeError("%s is read-only." % self.__class__)
2196
2197    def __iter__(self):
2198        for key in self.environ:
2199            if key[:5] == 'HTTP_':
2200                yield _hkey(key[5:])
2201            elif key in self.cgikeys:
2202                yield _hkey(key)
2203
2204    def keys(self):
2205        return [x for x in self]
2206
2207    def __len__(self):
2208        return len(self.keys())
2209
2210    def __contains__(self, key):
2211        return self._ekey(key) in self.environ
2212
2213
2214class ConfigDict(dict):
2215    """ A dict-like configuration storage with additional support for
2216        namespaces, validators, meta-data, on_change listeners and more.
2217    """
2218
2219    __slots__ = ('_meta', '_on_change')
2220
2221    def __init__(self):
2222        self._meta = {}
2223        self._on_change = lambda name, value: None
2224
2225    def load_module(self, path, squash):
2226        """ Load values from a Python module.
2227            :param squash: Squash nested dicts into namespaces by using
2228                           load_dict(), otherwise use update()
2229            Example: load_config('my.app.settings', True)
2230            Example: load_config('my.app.settings', False)
2231        """
2232        config_obj = __import__(path)
2233        obj = dict([(key, getattr(config_obj, key))
2234                for key in dir(config_obj) if key.isupper()])
2235        if squash:
2236            self.load_dict(obj)
2237        else:
2238            self.update(obj)
2239        return self
2240
2241    def load_config(self, filename):
2242        """ Load values from an ``*.ini`` style config file.
2243
2244            If the config file contains sections, their names are used as
2245            namespaces for the values within. The two special sections
2246            ``DEFAULT`` and ``bottle`` refer to the root namespace (no prefix).
2247        """
2248        conf = ConfigParser()
2249        conf.read(filename)
2250        for section in conf.sections():
2251            for key, value in conf.items(section):
2252                if section not in ('DEFAULT', 'bottle'):
2253                    key = section + '.' + key
2254                self[key] = value
2255        return self
2256
2257    def load_dict(self, source, namespace=''):
2258        """ Load values from a dictionary structure. Nesting can be used to
2259            represent namespaces.
2260
2261            >>> c = ConfigDict()
2262            >>> c.load_dict({'some': {'namespace': {'key': 'value'} } })
2263            {'some.namespace.key': 'value'}
2264        """
2265        for key, value in source.items():
2266            if isinstance(key, basestring):
2267                nskey = (namespace + '.' + key).strip('.')
2268                if isinstance(value, dict):
2269                    self.load_dict(value, namespace=nskey)
2270                else:
2271                    self[nskey] = value
2272            else:
2273                raise TypeError('Key has type %r (not a string)' % type(key))
2274        return self
2275
2276    def update(self, *a, **ka):
2277        """ If the first parameter is a string, all keys are prefixed with this
2278            namespace. Apart from that it works just as the usual dict.update().
2279            Example: ``update('some.namespace', key='value')`` """
2280        prefix = ''
2281        if a and isinstance(a[0], basestring):
2282            prefix = a[0].strip('.') + '.'
2283            a = a[1:]
2284        for key, value in dict(*a, **ka).items():
2285            self[prefix + key] = value
2286
2287    def setdefault(self, key, value):
2288        if key not in self:
2289            self[key] = value
2290        return self[key]
2291
2292    def __setitem__(self, key, value):
2293        if not isinstance(key, basestring):
2294            raise TypeError('Key has type %r (not a string)' % type(key))
2295        value = self.meta_get(key, 'filter', lambda x: x)(value)
2296        if key in self and self[key] is value:
2297            return
2298        self._on_change(key, value)
2299        dict.__setitem__(self, key, value)
2300
2301    def __delitem__(self, key):
2302        self._on_change(key, None)
2303        dict.__delitem__(self, key)
2304
2305    def meta_get(self, key, metafield, default=None):
2306        """ Return the value of a meta field for a key. """
2307        return self._meta.get(key, {}).get(metafield, default)
2308
2309    def meta_set(self, key, metafield, value):
2310        """ Set the meta field for a key to a new value. This triggers the
2311            on-change handler for existing keys. """
2312        self._meta.setdefault(key, {})[metafield] = value
2313        if key in self:
2314            self[key] = self[key]
2315
2316    def meta_list(self, key):
2317        """ Return an iterable of meta field names defined for a key. """
2318        return self._meta.get(key, {}).keys()
2319
2320
2321class AppStack(list):
2322    """ A stack-like list. Calling it returns the head of the stack. """
2323
2324    def __call__(self):
2325        """ Return the current default application. """
2326        return self[-1]
2327
2328    def push(self, value=None):
2329        """ Add a new :class:`Bottle` instance to the stack """
2330        if not isinstance(value, Bottle):
2331            value = Bottle()
2332        self.append(value)
2333        return value
2334
2335
2336class WSGIFileWrapper(object):
2337    def __init__(self, fp, buffer_size=1024 * 64):
2338        self.fp, self.buffer_size = fp, buffer_size
2339        for attr in ('fileno', 'close', 'read', 'readlines', 'tell', 'seek'):
2340            if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr))
2341
2342    def __iter__(self):
2343        buff, read = self.buffer_size, self.read
2344        while True:
2345            part = read(buff)
2346            if not part: return
2347            yield part
2348
2349
2350class _closeiter(object):
2351    """ This only exists to be able to attach a .close method to iterators that
2352        do not support attribute assignment (most of itertools). """
2353
2354    def __init__(self, iterator, close=None):
2355        self.iterator = iterator
2356        self.close_callbacks = makelist(close)
2357
2358    def __iter__(self):
2359        return iter(self.iterator)
2360
2361    def close(self):
2362        for func in self.close_callbacks:
2363            func()
2364
2365
2366class ResourceManager(object):
2367    """ This class manages a list of search paths and helps to find and open
2368        application-bound resources (files).
2369
2370        :param base: default value for :meth:`add_path` calls.
2371        :param opener: callable used to open resources.
2372        :param cachemode: controls which lookups are cached. One of 'all',
2373                         'found' or 'none'.
2374    """
2375
2376    def __init__(self, base='./', opener=open, cachemode='all'):
2377        self.opener = opener
2378        self.base = base
2379        self.cachemode = cachemode
2380
2381        #: A list of search paths. See :meth:`add_path` for details.
2382        self.path = []
2383        #: A cache for resolved paths. ``res.cache.clear()`` clears the cache.
2384        self.cache = {}
2385
2386    def add_path(self, path, base=None, index=None, create=False):
2387        """ Add a new path to the list of search paths. Return False if the
2388            path does not exist.
2389
2390            :param path: The new search path. Relative paths are turned into
2391                an absolute and normalized form. If the path looks like a file
2392                (not ending in `/`), the filename is stripped off.
2393            :param base: Path used to absolutize relative search paths.
2394                Defaults to :attr:`base` which defaults to ``os.getcwd()``.
2395            :param index: Position within the list of search paths. Defaults
2396                to last index (appends to the list).
2397
2398            The `base` parameter makes it easy to reference files installed
2399            along with a python module or package::
2400
2401                res.add_path('./resources/', __file__)
2402        """
2403        base = os.path.abspath(os.path.dirname(base or self.base))
2404        path = os.path.abspath(os.path.join(base, os.path.dirname(path)))
2405        path += os.sep
2406        if path in self.path:
2407            self.path.remove(path)
2408        if create and not os.path.isdir(path):
2409            os.makedirs(path)
2410        if index is None:
2411            self.path.append(path)
2412        else:
2413            self.path.insert(index, path)
2414        self.cache.clear()
2415        return os.path.exists(path)
2416
2417    def __iter__(self):
2418        """ Iterate over all existing files in all registered paths. """
2419        search = self.path[:]
2420        while search:
2421            path = search.pop()
2422            if not os.path.isdir(path): continue
2423            for name in os.listdir(path):
2424                full = os.path.join(path, name)
2425                if os.path.isdir(full): search.append(full)
2426                else: yield full
2427
2428    def lookup(self, name):
2429        """ Search for a resource and return an absolute file path, or `None`.
2430
2431            The :attr:`path` list is searched in order. The first match is
2432            returend. Symlinks are followed. The result is cached to speed up
2433            future lookups. """
2434        if name not in self.cache or DEBUG:
2435            for path in self.path:
2436                fpath = os.path.join(path, name)
2437                if os.path.isfile(fpath):
2438                    if self.cachemode in ('all', 'found'):
2439                        self.cache[name] = fpath
2440                    return fpath
2441            if self.cachemode == 'all':
2442                self.cache[name] = None
2443        return self.cache[name]
2444
2445    def open(self, name, mode='r', *args, **kwargs):
2446        """ Find a resource and return a file object, or raise IOError. """
2447        fname = self.lookup(name)
2448        if not fname: raise IOError("Resource %r not found." % name)
2449        return self.opener(fname, mode=mode, *args, **kwargs)
2450
2451
2452class FileUpload(object):
2453    def __init__(self, fileobj, name, filename, headers=None):
2454        """ Wrapper for file uploads. """
2455        #: Open file(-like) object (BytesIO buffer or temporary file)
2456        self.file = fileobj
2457        #: Name of the upload form field
2458        self.name = name
2459        #: Raw filename as sent by the client (may contain unsafe characters)
2460        self.raw_filename = filename
2461        #: A :class:`HeaderDict` with additional headers (e.g. content-type)
2462        self.headers = HeaderDict(headers) if headers else HeaderDict()
2463
2464    content_type = HeaderProperty('Content-Type')
2465    content_length = HeaderProperty('Content-Length', reader=int, default=-1)
2466
2467    @cached_property
2468    def filename(self):
2469        """ Name of the file on the client file system, but normalized to ensure
2470            file system compatibility. An empty filename is returned as 'empty'.
2471
2472            Only ASCII letters, digits, dashes, underscores and dots are
2473            allowed in the final filename. Accents are removed, if possible.
2474            Whitespace is replaced by a single dash. Leading or tailing dots
2475            or dashes are removed. The filename is limited to 255 characters.
2476        """
2477        fname = self.raw_filename
2478        if not isinstance(fname, unicode):
2479            fname = fname.decode('utf8', 'ignore')
2480        fname = normalize('NFKD', fname)
2481        fname = fname.encode('ASCII', 'ignore').decode('ASCII')
2482        fname = os.path.basename(fname.replace('\\', os.path.sep))
2483        fname = re.sub(r'[^a-zA-Z0-9-_.\s]', '', fname).strip()
2484        fname = re.sub(r'[-\s]+', '-', fname).strip('.-')
2485        return fname[:255] or 'empty'
2486
2487    def _copy_file(self, fp, chunk_size=2 ** 16):
2488        read, write, offset = self.file.read, fp.write, self.file.tell()
2489        while 1:
2490            buf = read(chunk_size)
2491            if not buf: break
2492            write(buf)
2493        self.file.seek(offset)
2494
2495    def save(self, destination, overwrite=False, chunk_size=2 ** 16):
2496        """ Save file to disk or copy its content to an open file(-like) object.
2497            If *destination* is a directory, :attr:`filename` is added to the
2498            path. Existing files are not overwritten by default (IOError).
2499
2500            :param destination: File path, directory or file(-like) object.
2501            :param overwrite: If True, replace existing files. (default: False)
2502            :param chunk_size: Bytes to read at a time. (default: 64kb)
2503        """
2504        if isinstance(destination, basestring):  # Except file-likes here
2505            if os.path.isdir(destination):
2506                destination = os.path.join(destination, self.filename)
2507            if not overwrite and os.path.exists(destination):
2508                raise IOError('File exists.')
2509            with open(destination, 'wb') as fp:
2510                self._copy_file(fp, chunk_size)
2511        else:
2512            self._copy_file(destination, chunk_size)
2513
2514###############################################################################
2515# Application Helper ###########################################################
2516###############################################################################
2517
2518
2519def abort(code=500, text='Unknown Error.'):
2520    """ Aborts execution and causes a HTTP error. """
2521    raise HTTPError(code, text)
2522
2523
2524def redirect(url, code=None):
2525    """ Aborts execution and causes a 303 or 302 redirect, depending on
2526        the HTTP protocol version. """
2527    if not code:
2528        code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302
2529    res = response.copy(cls=HTTPResponse)
2530    res.status = code
2531    res.body = ""
2532    res.set_header('Location', urljoin(request.url, url))
2533    raise res
2534
2535
2536def _file_iter_range(fp, offset, bytes, maxread=1024 * 1024):
2537    """ Yield chunks from a range in a file. No chunk is bigger than maxread."""
2538    fp.seek(offset)
2539    while bytes > 0:
2540        part = fp.read(min(bytes, maxread))
2541        if not part: break
2542        bytes -= len(part)
2543        yield part
2544
2545
2546def static_file(filename, root,
2547                mimetype='auto',
2548                download=False,
2549                charset='UTF-8'):
2550    """ Open a file in a safe way and return :exc:`HTTPResponse` with status
2551        code 200, 305, 403 or 404. The ``Content-Type``, ``Content-Encoding``,
2552        ``Content-Length`` and ``Last-Modified`` headers are set if possible.
2553        Special support for ``If-Modified-Since``, ``Range`` and ``HEAD``
2554        requests.
2555
2556        :param filename: Name or path of the file to send.
2557        :param root: Root path for file lookups. Should be an absolute directory
2558            path.
2559        :param mimetype: Defines the content-type header (default: guess from
2560            file extension)
2561        :param download: If True, ask the browser to open a `Save as...` dialog
2562            instead of opening the file with the associated program. You can
2563            specify a custom filename as a string. If not specified, the
2564            original filename is used (default: False).
2565        :param charset: The charset to use for files with a ``text/*``
2566            mime-type. (default: UTF-8)
2567    """
2568
2569    root = os.path.abspath(root) + os.sep
2570    filename = os.path.abspath(os.path.join(root, filename.strip('/\\')))
2571    headers = dict()
2572
2573    if not filename.startswith(root):
2574        return HTTPError(403, "Access denied.")
2575    if not os.path.exists(filename) or not os.path.isfile(filename):
2576        return HTTPError(404, "File does not exist.")
2577    if not os.access(filename, os.R_OK):
2578        return HTTPError(403, "You do not have permission to access this file.")
2579
2580    if mimetype == 'auto':
2581        if download and download != True:
2582            mimetype, encoding = mimetypes.guess_type(download)
2583        else:
2584            mimetype, encoding = mimetypes.guess_type(filename)
2585        if encoding: headers['Content-Encoding'] = encoding
2586
2587    if mimetype:
2588        if mimetype[:5] == 'text/' and charset and 'charset' not in mimetype:
2589            mimetype += '; charset=%s' % charset
2590        headers['Content-Type'] = mimetype
2591
2592    if download:
2593        download = os.path.basename(filename if download == True else download)
2594        headers['Content-Disposition'] = 'attachment; filename="%s"' % download
2595
2596    stats = os.stat(filename)
2597    headers['Content-Length'] = clen = stats.st_size
2598    lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
2599    headers['Last-Modified'] = lm
2600
2601    ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')
2602    if ims:
2603        ims = parse_date(ims.split(";")[0].strip())
2604    if ims is not None and ims >= int(stats.st_mtime):
2605        headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
2606                                        time.gmtime())
2607        return HTTPResponse(status=304, **headers)
2608
2609    body = '' if request.method == 'HEAD' else open(filename, 'rb')
2610
2611    headers["Accept-Ranges"] = "bytes"
2612    ranges = request.environ.get('HTTP_RANGE')
2613    if 'HTTP_RANGE' in request.environ:
2614        ranges = list(parse_range_header(request.environ['HTTP_RANGE'], clen))
2615        if not ranges:
2616            return HTTPError(416, "Requested Range Not Satisfiable")
2617        offset, end = ranges[0]
2618        headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end - 1, clen)
2619        headers["Content-Length"] = str(end - offset)
2620        if body: body = _file_iter_range(body, offset, end - offset)
2621        return HTTPResponse(body, status=206, **headers)
2622    return HTTPResponse(body, **headers)
2623
2624###############################################################################
2625# HTTP Utilities and MISC (TODO) ###############################################
2626###############################################################################
2627
2628
2629def debug(mode=True):
2630    """ Change the debug level.
2631    There is only one debug level supported at the moment."""
2632    global DEBUG
2633    if mode: warnings.simplefilter('default')
2634    DEBUG = bool(mode)
2635
2636
2637def http_date(value):
2638    if isinstance(value, (datedate, datetime)):
2639        value = value.utctimetuple()
2640    elif isinstance(value, (int, float)):
2641        value = time.gmtime(value)
2642    if not isinstance(value, basestring):
2643        value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value)
2644    return value
2645
2646
2647def parse_date(ims):
2648    """ Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch. """
2649    try:
2650        ts = email.utils.parsedate_tz(ims)
2651        return time.mktime(ts[:8] + (0, )) - (ts[9] or 0) - time.timezone
2652    except (TypeError, ValueError, IndexError, OverflowError):
2653        return None
2654
2655
2656def parse_auth(header):
2657    """ Parse rfc2617 HTTP authentication header string (basic) and return (user,pass) tuple or None"""
2658    try:
2659        method, data = header.split(None, 1)
2660        if method.lower() == 'basic':
2661            user, pwd = touni(base64.b64decode(tob(data))).split(':', 1)
2662            return user, pwd
2663    except (KeyError, ValueError):
2664        return None
2665
2666
2667def parse_range_header(header, maxlen=0):
2668    """ Yield (start, end) ranges parsed from a HTTP Range header. Skip
2669        unsatisfiable ranges. The end index is non-inclusive."""
2670    if not header or header[:6] != 'bytes=': return
2671    ranges = [r.split('-', 1) for r in header[6:].split(',') if '-' in r]
2672    for start, end in ranges:
2673        try:
2674            if not start:  # bytes=-100    -> last 100 bytes
2675                start, end = max(0, maxlen - int(end)), maxlen
2676            elif not end:  # bytes=100-    -> all but the first 99 bytes
2677                start, end = int(start), maxlen
2678            else:  # bytes=100-200 -> bytes 100-200 (inclusive)
2679                start, end = int(start), min(int(end) + 1, maxlen)
2680            if 0 <= start < end <= maxlen:
2681                yield start, end
2682        except ValueError:
2683            pass
2684
2685
2686def _parse_qsl(qs):
2687    r = []
2688    for pair in qs.replace(';', '&').split('&'):
2689        if not pair: continue
2690        nv = pair.split('=', 1)
2691        if len(nv) != 2: nv.append('')
2692        key = urlunquote(nv[0].replace('+', ' '))
2693        value = urlunquote(nv[1].replace('+', ' '))
2694        r.append((key, value))
2695    return r
2696
2697
2698def _lscmp(a, b):
2699    """ Compares two strings in a cryptographically safe way:
2700        Runtime is not affected by length of common prefix. """
2701    return not sum(0 if x == y else 1
2702                   for x, y in zip(a, b)) and len(a) == len(b)
2703
2704
2705def cookie_encode(data, key):
2706    """ Encode and sign a pickle-able object. Return a (byte) string """
2707    msg = base64.b64encode(pickle.dumps(data, -1))
2708    sig = base64.b64encode(hmac.new(tob(key), msg).digest())
2709    return tob('!') + sig + tob('?') + msg
2710
2711
2712def cookie_decode(data, key):
2713    """ Verify and decode an encoded string. Return an object or None."""
2714    data = tob(data)
2715    if cookie_is_encoded(data):
2716        sig, msg = data.split(tob('?'), 1)
2717        if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg).digest())):
2718            return pickle.loads(base64.b64decode(msg))
2719    return None
2720
2721
2722def cookie_is_encoded(data):
2723    """ Return True if the argument looks like a encoded cookie."""
2724    return bool(data.startswith(tob('!')) and tob('?') in data)
2725
2726
2727def html_escape(string):
2728    """ Escape HTML special characters ``&<>`` and quotes ``'"``. """
2729    return string.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')\
2730                 .replace('"', '&quot;').replace("'", '&#039;')
2731
2732
2733def html_quote(string):
2734    """ Escape and quote a string to be used as an HTTP attribute."""
2735    return '"%s"' % html_escape(string).replace('\n', '&#10;')\
2736                    .replace('\r', '&#13;').replace('\t', '&#9;')
2737
2738
2739def yieldroutes(func):
2740    """ Return a generator for routes that match the signature (name, args)
2741    of the func parameter. This may yield more than one route if the function
2742    takes optional keyword arguments. The output is best described by example::
2743
2744        a()         -> '/a'
2745        b(x, y)     -> '/b/<x>/<y>'
2746        c(x, y=5)   -> '/c/<x>' and '/c/<x>/<y>'
2747        d(x=5, y=6) -> '/d' and '/d/<x>' and '/d/<x>/<y>'
2748    """
2749    path = '/' + func.__name__.replace('__', '/').lstrip('/')
2750    spec = getargspec(func)
2751    argc = len(spec[0]) - len(spec[3] or [])
2752    path += ('/<%s>' * argc) % tuple(spec[0][:argc])
2753    yield path
2754    for arg in spec[0][argc:]:
2755        path += '/<%s>' % arg
2756        yield path
2757
2758
2759def path_shift(script_name, path_info, shift=1):
2760    """ Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa.
2761
2762        :return: The modified paths.
2763        :param script_name: The SCRIPT_NAME path.
2764        :param script_name: The PATH_INFO path.
2765        :param shift: The number of path fragments to shift. May be negative to
2766          change the shift direction. (default: 1)
2767    """
2768    if shift == 0: return script_name, path_info
2769    pathlist = path_info.strip('/').split('/')
2770    scriptlist = script_name.strip('/').split('/')
2771    if pathlist and pathlist[0] == '': pathlist = []
2772    if scriptlist and scriptlist[0] == '': scriptlist = []
2773    if 0 < shift <= len(pathlist):
2774        moved = pathlist[:shift]
2775        scriptlist = scriptlist + moved
2776        pathlist = pathlist[shift:]
2777    elif 0 > shift >= -len(scriptlist):
2778        moved = scriptlist[shift:]
2779        pathlist = moved + pathlist
2780        scriptlist = scriptlist[:shift]
2781    else:
2782        empty = 'SCRIPT_NAME' if shift < 0 else 'PATH_INFO'
2783        raise AssertionError("Cannot shift. Nothing left from %s" % empty)
2784    new_script_name = '/' + '/'.join(scriptlist)
2785    new_path_info = '/' + '/'.join(pathlist)
2786    if path_info.endswith('/') and pathlist: new_path_info += '/'
2787    return new_script_name, new_path_info
2788
2789
2790def auth_basic(check, realm="private", text="Access denied"):
2791    """ Callback decorator to require HTTP auth (basic).
2792        TODO: Add route(check_auth=...) parameter. """
2793
2794    def decorator(func):
2795
2796        @functools.wraps(func)
2797        def wrapper(*a, **ka):
2798            user, password = request.auth or (None, None)
2799            if user is None or not check(user, password):
2800                err = HTTPError(401, text)
2801                err.add_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
2802                return err
2803            return func(*a, **ka)
2804
2805        return wrapper
2806
2807    return decorator
2808
2809# Shortcuts for common Bottle methods.
2810# They all refer to the current default application.
2811
2812
2813def make_default_app_wrapper(name):
2814    """ Return a callable that relays calls to the current default app. """
2815
2816    @functools.wraps(getattr(Bottle, name))
2817    def wrapper(*a, **ka):
2818        return getattr(app(), name)(*a, **ka)
2819
2820    return wrapper
2821
2822
2823route     = make_default_app_wrapper('route')
2824get       = make_default_app_wrapper('get')
2825post      = make_default_app_wrapper('post')
2826put       = make_default_app_wrapper('put')
2827delete    = make_default_app_wrapper('delete')
2828patch     = make_default_app_wrapper('patch')
2829error     = make_default_app_wrapper('error')
2830mount     = make_default_app_wrapper('mount')
2831hook      = make_default_app_wrapper('hook')
2832install   = make_default_app_wrapper('install')
2833uninstall = make_default_app_wrapper('uninstall')
2834url       = make_default_app_wrapper('get_url')
2835
2836###############################################################################
2837# Server Adapter ###############################################################
2838###############################################################################
2839
2840
2841class ServerAdapter(object):
2842    quiet = False
2843
2844    def __init__(self, host='127.0.0.1', port=8080, **options):
2845        self.options = options
2846        self.host = host
2847        self.port = int(port)
2848
2849    def run(self, handler):  # pragma: no cover
2850        pass
2851
2852    def __repr__(self):
2853        args = ', '.join(['%s=%s' % (k, repr(v))
2854                          for k, v in self.options.items()])
2855        return "%s(%s)" % (self.__class__.__name__, args)
2856
2857
2858class CGIServer(ServerAdapter):
2859    quiet = True
2860
2861    def run(self, handler):  # pragma: no cover
2862        from wsgiref.handlers import CGIHandler
2863
2864        def fixed_environ(environ, start_response):
2865            environ.setdefault('PATH_INFO', '')
2866            return handler(environ, start_response)
2867
2868        CGIHandler().run(fixed_environ)
2869
2870
2871class FlupFCGIServer(ServerAdapter):
2872    def run(self, handler):  # pragma: no cover
2873        import flup.server.fcgi
2874        self.options.setdefault('bindAddress', (self.host, self.port))
2875        flup.server.fcgi.WSGIServer(handler, **self.options).run()
2876
2877
2878class WSGIRefServer(ServerAdapter):
2879    def run(self, app):  # pragma: no cover
2880        from wsgiref.simple_server import make_server
2881        from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
2882        import socket
2883
2884        class FixedHandler(WSGIRequestHandler):
2885            def address_string(self):  # Prevent reverse DNS lookups please.
2886                return self.client_address[0]
2887
2888            def log_request(*args, **kw):
2889                if not self.quiet:
2890                    return WSGIRequestHandler.log_request(*args, **kw)
2891
2892        handler_cls = self.options.get('handler_class', FixedHandler)
2893        server_cls = self.options.get('server_class', WSGIServer)
2894
2895        if ':' in self.host:  # Fix wsgiref for IPv6 addresses.
2896            if getattr(server_cls, 'address_family') == socket.AF_INET:
2897
2898                class server_cls(server_cls):
2899                    address_family = socket.AF_INET6
2900
2901        self.srv = make_server(self.host, self.port, app, server_cls,
2902                               handler_cls)
2903        self.port = self.srv.server_port  # update port actual port (0 means random)
2904        try:
2905            self.srv.serve_forever()
2906        except KeyboardInterrupt:
2907            self.srv.server_close()  # Prevent ResourceWarning: unclosed socket
2908            raise
2909
2910
2911class CherryPyServer(ServerAdapter):
2912    def run(self, handler):  # pragma: no cover
2913        from cherrypy import wsgiserver
2914        self.options['bind_addr'] = (self.host, self.port)
2915        self.options['wsgi_app'] = handler
2916
2917        certfile = self.options.get('certfile')
2918        if certfile:
2919            del self.options['certfile']
2920        keyfile = self.options.get('keyfile')
2921        if keyfile:
2922            del self.options['keyfile']
2923
2924        server = wsgiserver.CherryPyWSGIServer(**self.options)
2925        if certfile:
2926            server.ssl_certificate = certfile
2927        if keyfile:
2928            server.ssl_private_key = keyfile
2929
2930        try:
2931            server.start()
2932        finally:
2933            server.stop()
2934
2935
2936class WaitressServer(ServerAdapter):
2937    def run(self, handler):
2938        from waitress import serve
2939        serve(handler, host=self.host, port=self.port, _quiet=self.quiet)
2940
2941
2942class PasteServer(ServerAdapter):
2943    def run(self, handler):  # pragma: no cover
2944        from paste import httpserver
2945        from paste.translogger import TransLogger
2946        handler = TransLogger(handler, setup_console_handler=(not self.quiet))
2947        httpserver.serve(handler,
2948                         host=self.host,
2949                         port=str(self.port), **self.options)
2950
2951
2952class MeinheldServer(ServerAdapter):
2953    def run(self, handler):
2954        from meinheld import server
2955        server.listen((self.host, self.port))
2956        server.run(handler)
2957
2958
2959class FapwsServer(ServerAdapter):
2960    """ Extremely fast webserver using libev. See http://www.fapws.org/ """
2961
2962    def run(self, handler):  # pragma: no cover
2963        import fapws._evwsgi as evwsgi
2964        from fapws import base, config
2965        port = self.port
2966        if float(config.SERVER_IDENT[-2:]) > 0.4:
2967            # fapws3 silently changed its API in 0.5
2968            port = str(port)
2969        evwsgi.start(self.host, port)
2970        # fapws3 never releases the GIL. Complain upstream. I tried. No luck.
2971        if 'BOTTLE_CHILD' in os.environ and not self.quiet:
2972            _stderr("WARNING: Auto-reloading does not work with Fapws3.\n")
2973            _stderr("         (Fapws3 breaks python thread support)\n")
2974        evwsgi.set_base_module(base)
2975
2976        def app(environ, start_response):
2977            environ['wsgi.multiprocess'] = False
2978            return handler(environ, start_response)
2979
2980        evwsgi.wsgi_cb(('', app))
2981        evwsgi.run()
2982
2983
2984class TornadoServer(ServerAdapter):
2985    """ The super hyped asynchronous server by facebook. Untested. """
2986
2987    def run(self, handler):  # pragma: no cover
2988        import tornado.wsgi, tornado.httpserver, tornado.ioloop
2989        container = tornado.wsgi.WSGIContainer(handler)
2990        server = tornado.httpserver.HTTPServer(container)
2991        server.listen(port=self.port, address=self.host)
2992        tornado.ioloop.IOLoop.instance().start()
2993
2994
2995class AppEngineServer(ServerAdapter):
2996    """ Adapter for Google App Engine. """
2997    quiet = True
2998
2999    def run(self, handler):
3000        from google.appengine.ext.webapp import util
3001        # A main() function in the handler script enables 'App Caching'.
3002        # Lets makes sure it is there. This _really_ improves performance.
3003        module = sys.modules.get('__main__')
3004        if module and not hasattr(module, 'main'):
3005            module.main = lambda: util.run_wsgi_app(handler)
3006        util.run_wsgi_app(handler)
3007
3008
3009class TwistedServer(ServerAdapter):
3010    """ Untested. """
3011
3012    def run(self, handler):
3013        from twisted.web import server, wsgi
3014        from twisted.python.threadpool import ThreadPool
3015        from twisted.internet import reactor
3016        thread_pool = ThreadPool()
3017        thread_pool.start()
3018        reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop)
3019        factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, handler))
3020        reactor.listenTCP(self.port, factory, interface=self.host)
3021        if not reactor.running:
3022            reactor.run()
3023
3024
3025class DieselServer(ServerAdapter):
3026    """ Untested. """
3027
3028    def run(self, handler):
3029        from diesel.protocols.wsgi import WSGIApplication
3030        app = WSGIApplication(handler, port=self.port)
3031        app.run()
3032
3033
3034class GeventServer(ServerAdapter):
3035    """ Untested. Options:
3036
3037        * `fast` (default: False) uses libevent's http server, but has some
3038          issues: No streaming, no pipelining, no SSL.
3039        * See gevent.wsgi.WSGIServer() documentation for more options.
3040    """
3041
3042    def run(self, handler):
3043        from gevent import wsgi, pywsgi, local
3044        if not isinstance(threading.local(), local.local):
3045            msg = "Bottle requires gevent.monkey.patch_all() (before import)"
3046            raise RuntimeError(msg)
3047        if not self.options.pop('fast', None): wsgi = pywsgi
3048        self.options['log'] = None if self.quiet else 'default'
3049        address = (self.host, self.port)
3050        server = wsgi.WSGIServer(address, handler, **self.options)
3051        if 'BOTTLE_CHILD' in os.environ:
3052            import signal
3053            signal.signal(signal.SIGINT, lambda s, f: server.stop())
3054        server.serve_forever()
3055
3056
3057class GeventSocketIOServer(ServerAdapter):
3058    def run(self, handler):
3059        from socketio import server
3060        address = (self.host, self.port)
3061        server.SocketIOServer(address, handler, **self.options).serve_forever()
3062
3063
3064class GunicornServer(ServerAdapter):
3065    """ Untested. See http://gunicorn.org/configure.html for options. """
3066
3067    def run(self, handler):
3068        from gunicorn.app.base import Application
3069
3070        config = {'bind': "%s:%d" % (self.host, int(self.port))}
3071        config.update(self.options)
3072
3073        class GunicornApplication(Application):
3074            def init(self, parser, opts, args):
3075                return config
3076
3077            def load(self):
3078                return handler
3079
3080        GunicornApplication().run()
3081
3082
3083class EventletServer(ServerAdapter):
3084    """ Untested. Options:
3085
3086        * `backlog` adjust the eventlet backlog parameter which is the maximum
3087          number of queued connections. Should be at least 1; the maximum
3088          value is system-dependent.
3089        * `family`: (default is 2) socket family, optional. See socket
3090          documentation for available families.
3091    """
3092
3093    def run(self, handler):
3094        from eventlet import wsgi, listen, patcher
3095        if not patcher.is_monkey_patched(os):
3096            msg = "Bottle requires eventlet.monkey_patch() (before import)"
3097            raise RuntimeError(msg)
3098        socket_args = {}
3099        for arg in ('backlog', 'family'):
3100            try:
3101                socket_args[arg] = self.options.pop(arg)
3102            except KeyError:
3103                pass
3104        address = (self.host, self.port)
3105        try:
3106            wsgi.server(listen(address, **socket_args), handler,
3107                        log_output=(not self.quiet))
3108        except TypeError:
3109            # Fallback, if we have old version of eventlet
3110            wsgi.server(listen(address), handler)
3111
3112
3113class RocketServer(ServerAdapter):
3114    """ Untested. """
3115
3116    def run(self, handler):
3117        from rocket import Rocket
3118        server = Rocket((self.host, self.port), 'wsgi', {'wsgi_app': handler})
3119        server.start()
3120
3121
3122class BjoernServer(ServerAdapter):
3123    """ Fast server written in C: https://github.com/jonashaag/bjoern """
3124
3125    def run(self, handler):
3126        from bjoern import run
3127        run(handler, self.host, self.port)
3128
3129
3130class AiohttpServer(ServerAdapter):
3131    """ Untested.
3132        aiohttp
3133        https://pypi.python.org/pypi/aiohttp/
3134    """
3135
3136    def run(self, handler):
3137        import asyncio
3138        from aiohttp.wsgi import WSGIServerHttpProtocol
3139        self.loop = asyncio.new_event_loop()
3140        asyncio.set_event_loop(self.loop)
3141
3142        protocol_factory = lambda: WSGIServerHttpProtocol(
3143            handler,
3144            readpayload=True,
3145            debug=(not self.quiet))
3146        self.loop.run_until_complete(self.loop.create_server(protocol_factory,
3147                                                             self.host,
3148                                                             self.port))
3149
3150        if 'BOTTLE_CHILD' in os.environ:
3151            import signal
3152            signal.signal(signal.SIGINT, lambda s, f: self.loop.stop())
3153
3154        try:
3155            self.loop.run_forever()
3156        except KeyboardInterrupt:
3157            self.loop.stop()
3158
3159
3160class AutoServer(ServerAdapter):
3161    """ Untested. """
3162    adapters = [WaitressServer, PasteServer, TwistedServer, CherryPyServer,
3163                WSGIRefServer]
3164
3165    def run(self, handler):
3166        for sa in self.adapters:
3167            try:
3168                return sa(self.host, self.port, **self.options).run(handler)
3169            except ImportError:
3170                pass
3171
3172
3173server_names = {
3174    'cgi': CGIServer,
3175    'flup': FlupFCGIServer,
3176    'wsgiref': WSGIRefServer,
3177    'waitress': WaitressServer,
3178    'cherrypy': CherryPyServer,
3179    'paste': PasteServer,
3180    'fapws3': FapwsServer,
3181    'tornado': TornadoServer,
3182    'gae': AppEngineServer,
3183    'twisted': TwistedServer,
3184    'diesel': DieselServer,
3185    'meinheld': MeinheldServer,
3186    'gunicorn': GunicornServer,
3187    'eventlet': EventletServer,
3188    'gevent': GeventServer,
3189    'geventSocketIO': GeventSocketIOServer,
3190    'rocket': RocketServer,
3191    'bjoern': BjoernServer,
3192    'aiohttp': AiohttpServer,
3193    'auto': AutoServer,
3194}
3195
3196###############################################################################
3197# Application Control ##########################################################
3198###############################################################################
3199
3200
3201def load(target, **namespace):
3202    """ Import a module or fetch an object from a module.
3203
3204        * ``package.module`` returns `module` as a module object.
3205        * ``pack.mod:name`` returns the module variable `name` from `pack.mod`.
3206        * ``pack.mod:func()`` calls `pack.mod.func()` and returns the result.
3207
3208        The last form accepts not only function calls, but any type of
3209        expression. Keyword arguments passed to this function are available as
3210        local variables. Example: ``import_string('re:compile(x)', x='[a-z]')``
3211    """
3212    module, target = target.split(":", 1) if ':' in target else (target, None)
3213    if module not in sys.modules: __import__(module)
3214    if not target: return sys.modules[module]
3215    if target.isalnum(): return getattr(sys.modules[module], target)
3216    package_name = module.split('.')[0]
3217    namespace[package_name] = sys.modules[package_name]
3218    return eval('%s.%s' % (module, target), namespace)
3219
3220
3221def load_app(target):
3222    """ Load a bottle application from a module and make sure that the import
3223        does not affect the current default application, but returns a separate
3224        application object. See :func:`load` for the target parameter. """
3225    global NORUN
3226    NORUN, nr_old = True, NORUN
3227    tmp = default_app.push()  # Create a new "default application"
3228    try:
3229        rv = load(target)  # Import the target module
3230        return rv if callable(rv) else tmp
3231    finally:
3232        default_app.remove(tmp)  # Remove the temporary added default application
3233        NORUN = nr_old
3234
3235
3236_debug = debug
3237
3238
3239def run(app=None,
3240        server='wsgiref',
3241        host='127.0.0.1',
3242        port=8080,
3243        interval=1,
3244        reloader=False,
3245        quiet=False,
3246        plugins=None,
3247        debug=None,
3248        config=None, **kargs):
3249    """ Start a server instance. This method blocks until the server terminates.
3250
3251        :param app: WSGI application or target string supported by
3252               :func:`load_app`. (default: :func:`default_app`)
3253        :param server: Server adapter to use. See :data:`server_names` keys
3254               for valid names or pass a :class:`ServerAdapter` subclass.
3255               (default: `wsgiref`)
3256        :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on
3257               all interfaces including the external one. (default: 127.0.0.1)
3258        :param port: Server port to bind to. Values below 1024 require root
3259               privileges. (default: 8080)
3260        :param reloader: Start auto-reloading server? (default: False)
3261        :param interval: Auto-reloader interval in seconds (default: 1)
3262        :param quiet: Suppress output to stdout and stderr? (default: False)
3263        :param options: Options passed to the server adapter.
3264     """
3265    if NORUN: return
3266    if reloader and not os.environ.get('BOTTLE_CHILD'):
3267        import subprocess
3268        lockfile = None
3269        try:
3270            fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock')
3271            os.close(fd)  # We only need this file to exist. We never write to it
3272            while os.path.exists(lockfile):
3273                args = [sys.executable] + sys.argv
3274                environ = os.environ.copy()
3275                environ['BOTTLE_CHILD'] = 'true'
3276                environ['BOTTLE_LOCKFILE'] = lockfile
3277                p = subprocess.Popen(args, env=environ)
3278                while p.poll() is None:  # Busy wait...
3279                    os.utime(lockfile, None)  # I am alive!
3280                    time.sleep(interval)
3281                if p.poll() != 3:
3282                    if os.path.exists(lockfile): os.unlink(lockfile)
3283                    sys.exit(p.poll())
3284        except KeyboardInterrupt:
3285            pass
3286        finally:
3287            if os.path.exists(lockfile):
3288                os.unlink(lockfile)
3289        return
3290
3291    try:
3292        if debug is not None: _debug(debug)
3293        app = app or default_app()
3294        if isinstance(app, basestring):
3295            app = load_app(app)
3296        if not callable(app):
3297            raise ValueError("Application is not callable: %r" % app)
3298
3299        for plugin in plugins or []:
3300            if isinstance(plugin, basestring):
3301                plugin = load(plugin)
3302            app.install(plugin)
3303
3304        if config:
3305            app.config.update(config)
3306
3307        if server in server_names:
3308            server = server_names.get(server)
3309        if isinstance(server, basestring):
3310            server = load(server)
3311        if isinstance(server, type):
3312            server = server(host=host, port=port, **kargs)
3313        if not isinstance(server, ServerAdapter):
3314            raise ValueError("Unknown or unsupported server: %r" % server)
3315
3316        server.quiet = server.quiet or quiet
3317        if not server.quiet:
3318            _stderr("Bottle v%s server starting up (using %s)...\n" %
3319                    (__version__, repr(server)))
3320            _stderr("Listening on http://%s:%d/\n" %
3321                    (server.host, server.port))
3322            _stderr("Hit Ctrl-C to quit.\n\n")
3323
3324        if reloader:
3325            lockfile = os.environ.get('BOTTLE_LOCKFILE')
3326            bgcheck = FileCheckerThread(lockfile, interval)
3327            with bgcheck:
3328                server.run(app)
3329            if bgcheck.status == 'reload':
3330                sys.exit(3)
3331        else:
3332            server.run(app)
3333    except KeyboardInterrupt:
3334        pass
3335    except (SystemExit, MemoryError):
3336        raise
3337    except:
3338        if not reloader: raise
3339        if not getattr(server, 'quiet', quiet):
3340            print_exc()
3341        time.sleep(interval)
3342        sys.exit(3)
3343
3344
3345class FileCheckerThread(threading.Thread):
3346    """ Interrupt main-thread as soon as a changed module file is detected,
3347        the lockfile gets deleted or gets to old. """
3348
3349    def __init__(self, lockfile, interval):
3350        threading.Thread.__init__(self)
3351        self.daemon = True
3352        self.lockfile, self.interval = lockfile, interval
3353        #: Is one of 'reload', 'error' or 'exit'
3354        self.status = None
3355
3356    def run(self):
3357        exists = os.path.exists
3358        mtime = lambda p: os.stat(p).st_mtime
3359        files = dict()
3360
3361        for module in list(sys.modules.values()):
3362            path = getattr(module, '__file__', '')
3363            if path[-4:] in ('.pyo', '.pyc'): path = path[:-1]
3364            if path and exists(path): files[path] = mtime(path)
3365
3366        while not self.status:
3367            if not exists(self.lockfile)\
3368            or mtime(self.lockfile) < time.time() - self.interval - 5:
3369                self.status = 'error'
3370                thread.interrupt_main()
3371            for path, lmtime in list(files.items()):
3372                if not exists(path) or mtime(path) > lmtime:
3373                    self.status = 'reload'
3374                    thread.interrupt_main()
3375                    break
3376            time.sleep(self.interval)
3377
3378    def __enter__(self):
3379        self.start()
3380
3381    def __exit__(self, exc_type, *_):
3382        if not self.status: self.status = 'exit'  # silent exit
3383        self.join()
3384        return exc_type is not None and issubclass(exc_type, KeyboardInterrupt)
3385
3386###############################################################################
3387# Template Adapters ############################################################
3388###############################################################################
3389
3390
3391class TemplateError(HTTPError):
3392    def __init__(self, message):
3393        HTTPError.__init__(self, 500, message)
3394
3395
3396class BaseTemplate(object):
3397    """ Base class and minimal API for template adapters """
3398    extensions = ['tpl', 'html', 'thtml', 'stpl']
3399    settings = {}  #used in prepare()
3400    defaults = {}  #used in render()
3401
3402    def __init__(self,
3403                 source=None,
3404                 name=None,
3405                 lookup=None,
3406                 encoding='utf8', **settings):
3407        """ Create a new template.
3408        If the source parameter (str or buffer) is missing, the name argument
3409        is used to guess a template filename. Subclasses can assume that
3410        self.source and/or self.filename are set. Both are strings.
3411        The lookup, encoding and settings parameters are stored as instance
3412        variables.
3413        The lookup parameter stores a list containing directory paths.
3414        The encoding parameter should be used to decode byte strings or files.
3415        The settings parameter contains a dict for engine-specific settings.
3416        """
3417        self.name = name
3418        self.source = source.read() if hasattr(source, 'read') else source
3419        self.filename = source.filename if hasattr(source, 'filename') else None
3420        self.lookup = [os.path.abspath(x) for x in lookup] if lookup else []
3421        self.encoding = encoding
3422        self.settings = self.settings.copy()  # Copy from class variable
3423        self.settings.update(settings)  # Apply
3424        if not self.source and self.name:
3425            self.filename = self.search(self.name, self.lookup)
3426            if not self.filename:
3427                raise TemplateError('Template %s not found.' % repr(name))
3428        if not self.source and not self.filename:
3429            raise TemplateError('No template specified.')
3430        self.prepare(**self.settings)
3431
3432    @classmethod
3433    def search(cls, name, lookup=None):
3434        """ Search name in all directories specified in lookup.
3435        First without, then with common extensions. Return first hit. """
3436        if not lookup:
3437            depr('The template lookup path list should not be empty.',
3438                 True)  #0.12
3439            lookup = ['.']
3440
3441        if os.path.isabs(name) and os.path.isfile(name):
3442            depr('Absolute template path names are deprecated.', True)  #0.12
3443            return os.path.abspath(name)
3444
3445        for spath in lookup:
3446            spath = os.path.abspath(spath) + os.sep
3447            fname = os.path.abspath(os.path.join(spath, name))
3448            if not fname.startswith(spath): continue
3449            if os.path.isfile(fname): return fname
3450            for ext in cls.extensions:
3451                if os.path.isfile('%s.%s' % (fname, ext)):
3452                    return '%s.%s' % (fname, ext)
3453
3454    @classmethod
3455    def global_config(cls, key, *args):
3456        """ This reads or sets the global settings stored in class.settings. """
3457        if args:
3458            cls.settings = cls.settings.copy()  # Make settings local to class
3459            cls.settings[key] = args[0]
3460        else:
3461            return cls.settings[key]
3462
3463    def prepare(self, **options):
3464        """ Run preparations (parsing, caching, ...).
3465        It should be possible to call this again to refresh a template or to
3466        update settings.
3467        """
3468        raise NotImplementedError
3469
3470    def render(self, *args, **kwargs):
3471        """ Render the template with the specified local variables and return
3472        a single byte or unicode string. If it is a byte string, the encoding
3473        must match self.encoding. This method must be thread-safe!
3474        Local variables may be provided in dictionaries (args)
3475        or directly, as keywords (kwargs).
3476        """
3477        raise NotImplementedError
3478
3479
3480class MakoTemplate(BaseTemplate):
3481    def prepare(self, **options):
3482        from mako.template import Template
3483        from mako.lookup import TemplateLookup
3484        options.update({'input_encoding': self.encoding})
3485        options.setdefault('format_exceptions', bool(DEBUG))
3486        lookup = TemplateLookup(directories=self.lookup, **options)
3487        if self.source:
3488            self.tpl = Template(self.source, lookup=lookup, **options)
3489        else:
3490            self.tpl = Template(uri=self.name,
3491                                filename=self.filename,
3492                                lookup=lookup, **options)
3493
3494    def render(self, *args, **kwargs):
3495        for dictarg in args:
3496            kwargs.update(dictarg)
3497        _defaults = self.defaults.copy()
3498        _defaults.update(kwargs)
3499        return self.tpl.render(**_defaults)
3500
3501
3502class CheetahTemplate(BaseTemplate):
3503    def prepare(self, **options):
3504        from Cheetah.Template import Template
3505        self.context = threading.local()
3506        self.context.vars = {}
3507        options['searchList'] = [self.context.vars]
3508        if self.source:
3509            self.tpl = Template(source=self.source, **options)
3510        else:
3511            self.tpl = Template(file=self.filename, **options)
3512
3513    def render(self, *args, **kwargs):
3514        for dictarg in args:
3515            kwargs.update(dictarg)
3516        self.context.vars.update(self.defaults)
3517        self.context.vars.update(kwargs)
3518        out = str(self.tpl)
3519        self.context.vars.clear()
3520        return out
3521
3522
3523class Jinja2Template(BaseTemplate):
3524    def prepare(self, filters=None, tests=None, globals={}, **kwargs):
3525        from jinja2 import Environment, FunctionLoader
3526        self.env = Environment(loader=FunctionLoader(self.loader), **kwargs)
3527        if filters: self.env.filters.update(filters)
3528        if tests: self.env.tests.update(tests)
3529        if globals: self.env.globals.update(globals)
3530        if self.source:
3531            self.tpl = self.env.from_string(self.source)
3532        else:
3533            self.tpl = self.env.get_template(self.filename)
3534
3535    def render(self, *args, **kwargs):
3536        for dictarg in args:
3537            kwargs.update(dictarg)
3538        _defaults = self.defaults.copy()
3539        _defaults.update(kwargs)
3540        return self.tpl.render(**_defaults)
3541
3542    def loader(self, name):
3543        fname = self.search(name, self.lookup)
3544        if not fname: return
3545        with open(fname, "rb") as f:
3546            return f.read().decode(self.encoding)
3547
3548
3549class SimpleTemplate(BaseTemplate):
3550    def prepare(self,
3551                escape_func=html_escape,
3552                noescape=False,
3553                syntax=None, **ka):
3554        self.cache = {}
3555        enc = self.encoding
3556        self._str = lambda x: touni(x, enc)
3557        self._escape = lambda x: escape_func(touni(x, enc))
3558        self.syntax = syntax
3559        if noescape:
3560            self._str, self._escape = self._escape, self._str
3561
3562    @cached_property
3563    def co(self):
3564        return compile(self.code, self.filename or '<string>', 'exec')
3565
3566    @cached_property
3567    def code(self):
3568        source = self.source
3569        if not source:
3570            with open(self.filename, 'rb') as f:
3571                source = f.read()
3572        try:
3573            source, encoding = touni(source), 'utf8'
3574        except UnicodeError:
3575            depr('Template encodings other than utf8 are not supported.')  #0.11
3576            source, encoding = touni(source, 'latin1'), 'latin1'
3577        parser = StplParser(source, encoding=encoding, syntax=self.syntax)
3578        code = parser.translate()
3579        self.encoding = parser.encoding
3580        return code
3581
3582    def _rebase(self, _env, _name=None, **kwargs):
3583        _env['_rebase'] = (_name, kwargs)
3584
3585    def _include(self, _env, _name=None, **kwargs):
3586        env = _env.copy()
3587        env.update(kwargs)
3588        if _name not in self.cache:
3589            self.cache[_name] = self.__class__(name=_name, lookup=self.lookup)
3590        return self.cache[_name].execute(env['_stdout'], env)
3591
3592    def execute(self, _stdout, kwargs):
3593        env = self.defaults.copy()
3594        env.update(kwargs)
3595        env.update({
3596            '_stdout': _stdout,
3597            '_printlist': _stdout.extend,
3598            'include': functools.partial(self._include, env),
3599            'rebase': functools.partial(self._rebase, env),
3600            '_rebase': None,
3601            '_str': self._str,
3602            '_escape': self._escape,
3603            'get': env.get,
3604            'setdefault': env.setdefault,
3605            'defined': env.__contains__
3606        })
3607        eval(self.co, env)
3608        if env.get('_rebase'):
3609            subtpl, rargs = env.pop('_rebase')
3610            rargs['base'] = ''.join(_stdout)  #copy stdout
3611            del _stdout[:]  # clear stdout
3612            return self._include(env, subtpl, **rargs)
3613        return env
3614
3615    def render(self, *args, **kwargs):
3616        """ Render the template using keyword arguments as local variables. """
3617        env = {}
3618        stdout = []
3619        for dictarg in args:
3620            env.update(dictarg)
3621        env.update(kwargs)
3622        self.execute(stdout, env)
3623        return ''.join(stdout)
3624
3625
3626class StplSyntaxError(TemplateError):
3627
3628    pass
3629
3630
3631class StplParser(object):
3632    """ Parser for stpl templates. """
3633    _re_cache = {}  #: Cache for compiled re patterns
3634
3635    # This huge pile of voodoo magic splits python code into 8 different tokens.
3636    # We use the verbose (?x) regex mode to make this more manageable
3637
3638    _re_tok = _re_inl = r'''((?mx)         # verbose and dot-matches-newline mode
3639        [urbURB]*
3640        (?:  ''(?!')
3641            |""(?!")
3642            |'{6}
3643            |"{6}
3644            |'(?:[^\\']|\\.)+?'
3645            |"(?:[^\\"]|\\.)+?"
3646            |'{3}(?:[^\\]|\\.|\n)+?'{3}
3647            |"{3}(?:[^\\]|\\.|\n)+?"{3}
3648        )
3649    )'''
3650
3651    _re_inl = _re_tok.replace(r'|\n', '')  # We re-use this string pattern later
3652
3653    _re_tok += r'''
3654        # 2: Comments (until end of line, but not the newline itself)
3655        |(\#.*)
3656
3657        # 3: Open and close (4) grouping tokens
3658        |([\[\{\(])
3659        |([\]\}\)])
3660
3661        # 5,6: Keywords that start or continue a python block (only start of line)
3662        |^([\ \t]*(?:if|for|while|with|try|def|class)\b)
3663        |^([\ \t]*(?:elif|else|except|finally)\b)
3664
3665        # 7: Our special 'end' keyword (but only if it stands alone)
3666        |((?:^|;)[\ \t]*end[\ \t]*(?=(?:%(block_close)s[\ \t]*)?\r?$|;|\#))
3667
3668        # 8: A customizable end-of-code-block template token (only end of line)
3669        |(%(block_close)s[\ \t]*(?=\r?$))
3670
3671        # 9: And finally, a single newline. The 10th token is 'everything else'
3672        |(\r?\n)
3673    '''
3674
3675    # Match the start tokens of code areas in a template
3676    _re_split = r'''(?m)^[ \t]*(\\?)((%(line_start)s)|(%(block_start)s))'''
3677    # Match inline statements (may contain python strings)
3678    _re_inl = r'''%%(inline_start)s((?:%s|[^'"\n]+?)*?)%%(inline_end)s''' % _re_inl
3679
3680    default_syntax = '<% %> % {{ }}'
3681
3682    def __init__(self, source, syntax=None, encoding='utf8'):
3683        self.source, self.encoding = touni(source, encoding), encoding
3684        self.set_syntax(syntax or self.default_syntax)
3685        self.code_buffer, self.text_buffer = [], []
3686        self.lineno, self.offset = 1, 0
3687        self.indent, self.indent_mod = 0, 0
3688        self.paren_depth = 0
3689
3690    def get_syntax(self):
3691        """ Tokens as a space separated string (default: <% %> % {{ }}) """
3692        return self._syntax
3693
3694    def set_syntax(self, syntax):
3695        self._syntax = syntax
3696        self._tokens = syntax.split()
3697        if not syntax in self._re_cache:
3698            names = 'block_start block_close line_start inline_start inline_end'
3699            etokens = map(re.escape, self._tokens)
3700            pattern_vars = dict(zip(names.split(), etokens))
3701            patterns = (self._re_split, self._re_tok, self._re_inl)
3702            patterns = [re.compile(p % pattern_vars) for p in patterns]
3703            self._re_cache[syntax] = patterns
3704        self.re_split, self.re_tok, self.re_inl = self._re_cache[syntax]
3705
3706    syntax = property(get_syntax, set_syntax)
3707
3708    def translate(self):
3709        if self.offset: raise RuntimeError('Parser is a one time instance.')
3710        while True:
3711            m = self.re_split.search(self.source, pos=self.offset)
3712            if m:
3713                text = self.source[self.offset:m.start()]
3714                self.text_buffer.append(text)
3715                self.offset = m.end()
3716                if m.group(1):  # Escape syntax
3717                    line, sep, _ = self.source[self.offset:].partition('\n')
3718                    self.text_buffer.append(self.source[m.start():m.start(1)] +
3719                                            m.group(2) + line + sep)
3720                    self.offset += len(line + sep)
3721                    continue
3722                self.flush_text()
3723                self.offset += self.read_code(self.source[self.offset:],
3724                                              multiline=bool(m.group(4)))
3725            else:
3726                break
3727        self.text_buffer.append(self.source[self.offset:])
3728        self.flush_text()
3729        return ''.join(self.code_buffer)
3730
3731    def read_code(self, pysource, multiline):
3732        code_line, comment = '', ''
3733        offset = 0
3734        while True:
3735            m = self.re_tok.search(pysource, pos=offset)
3736            if not m:
3737                code_line += pysource[offset:]
3738                offset = len(pysource)
3739                self.write_code(code_line.strip(), comment)
3740                break
3741            code_line += pysource[offset:m.start()]
3742            offset = m.end()
3743            _str, _com, _po, _pc, _blk1, _blk2, _end, _cend, _nl = m.groups()
3744            if self.paren_depth > 0 and (_blk1 or _blk2):  # a if b else c
3745                code_line += _blk1 or _blk2
3746                continue
3747            if _str:  # Python string
3748                code_line += _str
3749            elif _com:  # Python comment (up to EOL)
3750                comment = _com
3751                if multiline and _com.strip().endswith(self._tokens[1]):
3752                    multiline = False  # Allow end-of-block in comments
3753            elif _po:  # open parenthesis
3754                self.paren_depth += 1
3755                code_line += _po
3756            elif _pc:  # close parenthesis
3757                if self.paren_depth > 0:
3758                    # we could check for matching parentheses here, but it's
3759                    # easier to leave that to python - just check counts
3760                    self.paren_depth -= 1
3761                code_line += _pc
3762            elif _blk1:  # Start-block keyword (if/for/while/def/try/...)
3763                code_line, self.indent_mod = _blk1, -1
3764                self.indent += 1
3765            elif _blk2:  # Continue-block keyword (else/elif/except/...)
3766                code_line, self.indent_mod = _blk2, -1
3767            elif _end:  # The non-standard 'end'-keyword (ends a block)
3768                self.indent -= 1
3769            elif _cend:  # The end-code-block template token (usually '%>')
3770                if multiline: multiline = False
3771                else: code_line += _cend
3772            else:  # \n
3773                self.write_code(code_line.strip(), comment)
3774                self.lineno += 1
3775                code_line, comment, self.indent_mod = '', '', 0
3776                if not multiline:
3777                    break
3778
3779        return offset
3780
3781    def flush_text(self):
3782        text = ''.join(self.text_buffer)
3783        del self.text_buffer[:]
3784        if not text: return
3785        parts, pos, nl = [], 0, '\\\n' + '  ' * self.indent
3786        for m in self.re_inl.finditer(text):
3787            prefix, pos = text[pos:m.start()], m.end()
3788            if prefix:
3789                parts.append(nl.join(map(repr, prefix.splitlines(True))))
3790            if prefix.endswith('\n'): parts[-1] += nl
3791            parts.append(self.process_inline(m.group(1).strip()))
3792        if pos < len(text):
3793            prefix = text[pos:]
3794            lines = prefix.splitlines(True)
3795            if lines[-1].endswith('\\\\\n'): lines[-1] = lines[-1][:-3]
3796            elif lines[-1].endswith('\\\\\r\n'): lines[-1] = lines[-1][:-4]
3797            parts.append(nl.join(map(repr, lines)))
3798        code = '_printlist((%s,))' % ', '.join(parts)
3799        self.lineno += code.count('\n') + 1
3800        self.write_code(code)
3801
3802    @staticmethod
3803    def process_inline(chunk):
3804        if chunk[0] == '!': return '_str(%s)' % chunk[1:]
3805        return '_escape(%s)' % chunk
3806
3807    def write_code(self, line, comment=''):
3808        code = '  ' * (self.indent + self.indent_mod)
3809        code += line.lstrip() + comment + '\n'
3810        self.code_buffer.append(code)
3811
3812
3813def template(*args, **kwargs):
3814    """
3815    Get a rendered template as a string iterator.
3816    You can use a name, a filename or a template string as first parameter.
3817    Template rendering arguments can be passed as dictionaries
3818    or directly (as keyword arguments).
3819    """
3820    tpl = args[0] if args else None
3821    adapter = kwargs.pop('template_adapter', SimpleTemplate)
3822    lookup = kwargs.pop('template_lookup', TEMPLATE_PATH)
3823    tplid = (id(lookup), tpl)
3824    if tplid not in TEMPLATES or DEBUG:
3825        settings = kwargs.pop('template_settings', {})
3826        if isinstance(tpl, adapter):
3827            TEMPLATES[tplid] = tpl
3828            if settings: TEMPLATES[tplid].prepare(**settings)
3829        elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl:
3830            TEMPLATES[tplid] = adapter(source=tpl, lookup=lookup, **settings)
3831        else:
3832            TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings)
3833    if not TEMPLATES[tplid]:
3834        abort(500, 'Template (%s) not found' % tpl)
3835    for dictarg in args[1:]:
3836        kwargs.update(dictarg)
3837    return TEMPLATES[tplid].render(kwargs)
3838
3839
3840mako_template = functools.partial(template, template_adapter=MakoTemplate)
3841cheetah_template = functools.partial(template,
3842                                     template_adapter=CheetahTemplate)
3843jinja2_template = functools.partial(template, template_adapter=Jinja2Template)
3844
3845
3846def view(tpl_name, **defaults):
3847    """ Decorator: renders a template for a handler.
3848        The handler can control its behavior like that:
3849
3850          - return a dict of template vars to fill out the template
3851          - return something other than a dict and the view decorator will not
3852            process the template, but return the handler result as is.
3853            This includes returning a HTTPResponse(dict) to get,
3854            for instance, JSON with autojson or other castfilters.
3855    """
3856
3857    def decorator(func):
3858
3859        @functools.wraps(func)
3860        def wrapper(*args, **kwargs):
3861            result = func(*args, **kwargs)
3862            if isinstance(result, (dict, DictMixin)):
3863                tplvars = defaults.copy()
3864                tplvars.update(result)
3865                return template(tpl_name, **tplvars)
3866            elif result is None:
3867                return template(tpl_name, defaults)
3868            return result
3869
3870        return wrapper
3871
3872    return decorator
3873
3874
3875mako_view = functools.partial(view, template_adapter=MakoTemplate)
3876cheetah_view = functools.partial(view, template_adapter=CheetahTemplate)
3877jinja2_view = functools.partial(view, template_adapter=Jinja2Template)
3878
3879###############################################################################
3880# Constants and Globals ########################################################
3881###############################################################################
3882
3883TEMPLATE_PATH = ['./', './views/']
3884TEMPLATES = {}
3885DEBUG = False
3886NORUN = False  # If set, run() does nothing. Used by load_app()
3887
3888#: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found')
3889HTTP_CODES = httplib.responses.copy()
3890HTTP_CODES[418] = "I'm a teapot"  # RFC 2324
3891HTTP_CODES[428] = "Precondition Required"
3892HTTP_CODES[429] = "Too Many Requests"
3893HTTP_CODES[431] = "Request Header Fields Too Large"
3894HTTP_CODES[511] = "Network Authentication Required"
3895_HTTP_STATUS_LINES = dict((k, '%d %s' % (k, v))
3896                          for (k, v) in HTTP_CODES.items())
3897
3898#: The default template used for error pages. Override with @error()
3899ERROR_PAGE_TEMPLATE = """
3900%%try:
3901    %%from %s import DEBUG, request
3902    <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
3903    <html>
3904        <head>
3905            <title>Error: {{e.status}}</title>
3906            <style type="text/css">
3907              html {background-color: #eee; font-family: sans-serif;}
3908              body {background-color: #fff; border: 1px solid #ddd;
3909                    padding: 15px; margin: 15px;}
3910              pre {background-color: #eee; border: 1px solid #ddd; padding: 5px;}
3911            </style>
3912        </head>
3913        <body>
3914            <h1>Error: {{e.status}}</h1>
3915            <p>Sorry, the requested URL <tt>{{repr(request.url)}}</tt>
3916               caused an error:</p>
3917            <pre>{{e.body}}</pre>
3918            %%if DEBUG and e.exception:
3919              <h2>Exception:</h2>
3920              <pre>{{repr(e.exception)}}</pre>
3921            %%end
3922            %%if DEBUG and e.traceback:
3923              <h2>Traceback:</h2>
3924              <pre>{{e.traceback}}</pre>
3925            %%end
3926        </body>
3927    </html>
3928%%except ImportError:
3929    <b>ImportError:</b> Could not generate the error page. Please add bottle to
3930    the import path.
3931%%end
3932""" % __name__
3933
3934#: A thread-safe instance of :class:`LocalRequest`. If accessed from within a
3935#: request callback, this instance always refers to the *current* request
3936#: (even on a multi-threaded server).
3937request = LocalRequest()
3938
3939#: A thread-safe instance of :class:`LocalResponse`. It is used to change the
3940#: HTTP response for the *current* request.
3941response = LocalResponse()
3942
3943#: A thread-safe namespace. Not used by Bottle.
3944local = threading.local()
3945
3946# Initialize app stack (create first empty Bottle app)
3947# BC: 0.6.4 and needed for run()
3948app = default_app = AppStack()
3949app.push()
3950
3951#: A virtual package that redirects import statements.
3952#: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`.
3953ext = _ImportRedirect('bottle.ext' if __name__ == '__main__' else
3954                      __name__ + ".ext", 'bottle_%s').module
3955
3956
3957
3958if __name__ == '__main__':
3959    opt, args, parser = _cli_parse(sys.argv)
3960
3961    def _cli_error(msg):
3962        parser.print_help()
3963        _stderr('\nError: %s\n' % msg)
3964        sys.exit(1)
3965
3966    if opt.version:
3967        _stdout('Bottle %s\n' % __version__)
3968        sys.exit(0)
3969    if not args:
3970        _cli_error("No application entry point specified.")
3971
3972    sys.path.insert(0, '.')
3973    sys.modules.setdefault('bottle', sys.modules['__main__'])
3974
3975    host, port = (opt.bind or 'localhost'), 8080
3976    if ':' in host and host.rfind(']') < host.rfind(':'):
3977        host, port = host.rsplit(':', 1)
3978    host = host.strip('[]')
3979
3980    config = ConfigDict()
3981
3982    for cfile in opt.conf or []:
3983        try:
3984            if cfile.endswith('.json'):
3985                with open(cfile, 'rb') as fp:
3986                    config.load_dict(json_loads(fp.read()))
3987            else:
3988                config.load_config(cfile)
3989        except ConfigParserError:
3990            _cli_error(str(_e()))
3991        except IOError:
3992            _cli_error("Unable to read config file %r" % cfile)
3993        except (UnicodeError, TypeError, ValueError):
3994            _cli_error("Unable to parse config file %r: %s" % (cfile, _e()))
3995
3996    for cval in opt.param or []:
3997        if '=' in cval:
3998            config.update((cval.split('=', 1),))
3999        else:
4000            config[cval] = True
4001
4002    run(args[0],
4003        host=host,
4004        port=int(port),
4005        server=opt.server,
4006        reloader=opt.reload,
4007        plugins=opt.plugin,
4008        debug=opt.debug,
4009        config=config)
4010
4011# THE END
4012