plint

French poetry validator (local mirror of https://gitlab.com/a3nm/plint)
git clone https://a3nm.net/git/plint/
Log | Files | Refs | README

bottle.py (122190B)


      1 #!/usr/bin/env python
      2 # -*- coding: utf-8 -*-
      3 """
      4 Bottle is a fast and simple micro-framework for small web applications. It
      5 offers request dispatching (Routes) with url parameter support, templates,
      6 a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and
      7 template engines - all in a single file and with no dependencies other than the
      8 Python Standard Library.
      9 
     10 Homepage and documentation: http://bottlepy.org/
     11 
     12 Copyright (c) 2011, Marcel Hellkamp.
     13 License: MIT (see LICENSE for details)
     14 """
     15 
     16 from __future__ import with_statement
     17 
     18 __author__ = 'Marcel Hellkamp'
     19 __version__ = '0.11.dev'
     20 __license__ = 'MIT'
     21 
     22 # The gevent server adapter needs to patch some modules before they are imported
     23 # This is why we parse the commandline parameters here but handle them later
     24 if __name__ == '__main__':
     25     from optparse import OptionParser
     26     _cmd_parser = OptionParser(usage="usage: %prog [options] package.module:app")
     27     _opt = _cmd_parser.add_option
     28     _opt("--version", action="store_true", help="show version number.")
     29     _opt("-b", "--bind", metavar="ADDRESS", help="bind socket to ADDRESS.")
     30     _opt("-s", "--server", default='wsgiref', help="use SERVER as backend.")
     31     _opt("-p", "--plugin", action="append", help="install additional plugin/s.")
     32     _opt("--debug", action="store_true", help="start server in debug mode.")
     33     _opt("--reload", action="store_true", help="auto-reload on file changes.")
     34     _cmd_options, _cmd_args = _cmd_parser.parse_args()
     35     if _cmd_options.server and _cmd_options.server.startswith('gevent'):
     36         import gevent.monkey; gevent.monkey.patch_all()
     37 
     38 import base64, cgi, email.utils, functools, hmac, imp, itertools, mimetypes,\
     39         os, re, subprocess, sys, tempfile, threading, time, urllib, warnings
     40 
     41 from datetime import date as datedate, datetime, timedelta
     42 from tempfile import TemporaryFile
     43 from traceback import format_exc, print_exc
     44 
     45 try: from json import dumps as json_dumps, loads as json_lds
     46 except ImportError: # pragma: no cover
     47     try: from simplejson import dumps as json_dumps, loads as json_lds
     48     except ImportError:
     49         try: from django.utils.simplejson import dumps as json_dumps, loads as json_lds
     50         except ImportError:
     51             def json_dumps(data):
     52                 raise ImportError("JSON support requires Python 2.6 or simplejson.")
     53             json_lds = json_dumps
     54 
     55 
     56 
     57 # We now try to fix 2.5/2.6/3.1/3.2 incompatibilities.
     58 # It ain't pretty but it works... Sorry for the mess.
     59 
     60 py   = sys.version_info
     61 py3k = py >= (3,0,0)
     62 py25 = py <  (2,6,0)
     63 
     64 # Workaround for the missing "as" keyword in py3k.
     65 def _e(): return sys.exc_info()[1]
     66 
     67 # Workaround for the "print is a keyword/function" dilemma.
     68 _stdout, _stderr = sys.stdout.write, sys.stderr.write
     69 
     70 # Lots of stdlib and builtin differences.
     71 if py3k:
     72     import http.client as httplib
     73     import _thread as thread
     74     from urllib.parse import urljoin, parse_qsl, SplitResult as UrlSplitResult
     75     from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote
     76     from http.cookies import SimpleCookie
     77     from collections import MutableMapping as DictMixin
     78     import pickle
     79     from io import BytesIO
     80     basestring = str
     81     unicode = str
     82     json_loads = lambda s: json_lds(touni(s))
     83     callable = lambda x: hasattr(x, '__call__')
     84     imap = map
     85 else: # 2.x
     86     import httplib
     87     import thread
     88     from urlparse import urljoin, SplitResult as UrlSplitResult
     89     from urllib import urlencode, quote as urlquote, unquote as urlunquote
     90     from Cookie import SimpleCookie
     91     from itertools import imap
     92     import cPickle as pickle
     93     from StringIO import StringIO as BytesIO
     94     if py25:
     95         msg = "Python 2.5 support may be dropped in future versions of Bottle."
     96         warnings.warn(msg, DeprecationWarning)
     97         from cgi import parse_qsl
     98         from UserDict import DictMixin
     99         def next(it): return it.next()
    100         bytes = str
    101     else: # 2.6, 2.7
    102         from urlparse import parse_qsl
    103         from collections import MutableMapping as DictMixin
    104     json_loads = json_lds
    105 
    106 # Some helpers for string/byte handling
    107 def tob(s, enc='utf8'):
    108     return s.encode(enc) if isinstance(s, unicode) else bytes(s)
    109 def touni(s, enc='utf8', err='strict'):
    110     return s.decode(enc, err) if isinstance(s, bytes) else unicode(s)
    111 tonat = touni if py3k else tob
    112 
    113 # 3.2 fixes cgi.FieldStorage to accept bytes (which makes a lot of sense).
    114 # 3.1 needs a workaround.
    115 NCTextIOWrapper = None
    116 if (3,0,0) < py < (3,2,0):
    117     from io import TextIOWrapper
    118     class NCTextIOWrapper(TextIOWrapper):
    119         def close(self): pass # Keep wrapped buffer open.
    120 
    121 # A bug in functools causes it to break if the wrapper is an instance method
    122 def update_wrapper(wrapper, wrapped, *a, **ka):
    123     try: functools.update_wrapper(wrapper, wrapped, *a, **ka)
    124     except AttributeError: pass
    125 
    126 
    127 
    128 # These helpers are used at module level and need to be defined first.
    129 # And yes, I know PEP-8, but sometimes a lower-case classname makes more sense.
    130 
    131 def depr(message):
    132     warnings.warn(message, DeprecationWarning, stacklevel=3)
    133 
    134 def makelist(data): # This is just to handy
    135     if isinstance(data, (tuple, list, set, dict)): return list(data)
    136     elif data: return [data]
    137     else: return []
    138 
    139 
    140 class DictProperty(object):
    141     ''' Property that maps to a key in a local dict-like attribute. '''
    142     def __init__(self, attr, key=None, read_only=False):
    143         self.attr, self.key, self.read_only = attr, key, read_only
    144 
    145     def __call__(self, func):
    146         functools.update_wrapper(self, func, updated=[])
    147         self.getter, self.key = func, self.key or func.__name__
    148         return self
    149 
    150     def __get__(self, obj, cls):
    151         if obj is None: return self
    152         key, storage = self.key, getattr(obj, self.attr)
    153         if key not in storage: storage[key] = self.getter(obj)
    154         return storage[key]
    155 
    156     def __set__(self, obj, value):
    157         if self.read_only: raise AttributeError("Read-Only property.")
    158         getattr(obj, self.attr)[self.key] = value
    159 
    160     def __delete__(self, obj):
    161         if self.read_only: raise AttributeError("Read-Only property.")
    162         del getattr(obj, self.attr)[self.key]
    163 
    164 
    165 class cached_property(object):
    166     ''' A property that is only computed once per instance and then replaces
    167         itself with an ordinary attribute. Deleting the attribute resets the
    168         property. '''
    169 
    170     def __init__(self, func):
    171         self.func = func
    172 
    173     def __get__(self, obj, cls):
    174         if obj is None: return self
    175         value = obj.__dict__[self.func.__name__] = self.func(obj)
    176         return value
    177 
    178 
    179 class lazy_attribute(object):
    180     ''' A property that caches itself to the class object. '''
    181     def __init__(self, func):
    182         functools.update_wrapper(self, func, updated=[])
    183         self.getter = func
    184 
    185     def __get__(self, obj, cls):
    186         value = self.getter(cls)
    187         setattr(cls, self.__name__, value)
    188         return value
    189 
    190 
    191 
    192 
    193 
    194 
    195 ###############################################################################
    196 # Exceptions and Events ########################################################
    197 ###############################################################################
    198 
    199 
    200 class BottleException(Exception):
    201     """ A base class for exceptions used by bottle. """
    202     pass
    203 
    204 
    205 #TODO: This should subclass BaseRequest
    206 class HTTPResponse(BottleException):
    207     """ Used to break execution and immediately finish the response """
    208     def __init__(self, output='', status=200, header=None):
    209         super(BottleException, self).__init__("HTTP Response %d" % status)
    210         self.status = int(status)
    211         self.output = output
    212         self.headers = HeaderDict(header) if header else None
    213 
    214     def apply(self, response):
    215         if self.headers:
    216             for key, value in self.headers.allitems():
    217                 response.headers[key] = value
    218         response.status = self.status
    219 
    220 
    221 class HTTPError(HTTPResponse):
    222     """ Used to generate an error page """
    223     def __init__(self, code=500, output='Unknown Error', exception=None,
    224                  traceback=None, header=None):
    225         super(HTTPError, self).__init__(output, code, header)
    226         self.exception = exception
    227         self.traceback = traceback
    228 
    229     def __repr__(self):
    230         return tonat(template(ERROR_PAGE_TEMPLATE, e=self))
    231 
    232 
    233 
    234 
    235 
    236 
    237 ###############################################################################
    238 # Routing ######################################################################
    239 ###############################################################################
    240 
    241 
    242 class RouteError(BottleException):
    243     """ This is a base class for all routing related exceptions """
    244 
    245 
    246 class RouteReset(BottleException):
    247     """ If raised by a plugin or request handler, the route is reset and all
    248         plugins are re-applied. """
    249 
    250 class RouterUnknownModeError(RouteError): pass
    251 
    252 
    253 class RouteSyntaxError(RouteError):
    254     """ The route parser found something not supported by this router """
    255 
    256 
    257 class RouteBuildError(RouteError):
    258     """ The route could not been built """
    259 
    260 
    261 class Router(object):
    262     ''' A Router is an ordered collection of route->target pairs. It is used to
    263         efficiently match WSGI requests against a number of routes and return
    264         the first target that satisfies the request. The target may be anything,
    265         usually a string, ID or callable object. A route consists of a path-rule
    266         and a HTTP method.
    267 
    268         The path-rule is either a static path (e.g. `/contact`) or a dynamic
    269         path that contains wildcards (e.g. `/wiki/<page>`). The wildcard syntax
    270         and details on the matching order are described in docs:`routing`.
    271     '''
    272 
    273     default_pattern = '[^/]+'
    274     default_filter   = 're'
    275     #: Sorry for the mess. It works. Trust me.
    276     rule_syntax = re.compile('(\\\\*)'\
    277         '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)'\
    278           '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)'\
    279             '(?::((?:\\\\.|[^\\\\>]+)+)?)?)?>))')
    280 
    281     def __init__(self, strict=False):
    282         self.rules    = {} # A {rule: Rule} mapping
    283         self.builder  = {} # A rule/name->build_info mapping
    284         self.static   = {} # Cache for static routes: {path: {method: target}}
    285         self.dynamic  = [] # Cache for dynamic routes. See _compile()
    286         #: If true, static routes are no longer checked first.
    287         self.strict_order = strict
    288         self.filters = {'re': self.re_filter, 'int': self.int_filter,
    289                         'float': self.float_filter, 'path': self.path_filter}
    290 
    291     def re_filter(self, conf):
    292         return conf or self.default_pattern, None, None
    293 
    294     def int_filter(self, conf):
    295         return r'-?\d+', int, lambda x: str(int(x))
    296 
    297     def float_filter(self, conf):
    298         return r'-?[\d.]+', float, lambda x: str(float(x))
    299 
    300     def path_filter(self, conf):
    301         return r'.+?', None, None
    302 
    303     def add_filter(self, name, func):
    304         ''' Add a filter. The provided function is called with the configuration
    305         string as parameter and must return a (regexp, to_python, to_url) tuple.
    306         The first element is a string, the last two are callables or None. '''
    307         self.filters[name] = func
    308 
    309     def parse_rule(self, rule):
    310         ''' Parses a rule into a (name, filter, conf) token stream. If mode is
    311             None, name contains a static rule part. '''
    312         offset, prefix = 0, ''
    313         for match in self.rule_syntax.finditer(rule):
    314             prefix += rule[offset:match.start()]
    315             g = match.groups()
    316             if len(g[0])%2: # Escaped wildcard
    317                 prefix += match.group(0)[len(g[0]):]
    318                 offset = match.end()
    319                 continue
    320             if prefix: yield prefix, None, None
    321             name, filtr, conf = g[1:4] if not g[2] is None else g[4:7]
    322             if not filtr: filtr = self.default_filter
    323             yield name, filtr, conf or None
    324             offset, prefix = match.end(), ''
    325         if offset <= len(rule) or prefix:
    326             yield prefix+rule[offset:], None, None
    327 
    328     def add(self, rule, method, target, name=None):
    329         ''' Add a new route or replace the target for an existing route. '''
    330         if rule in self.rules:
    331             self.rules[rule][method] = target
    332             if name: self.builder[name] = self.builder[rule]
    333             return
    334 
    335         target = self.rules[rule] = {method: target}
    336 
    337         # Build pattern and other structures for dynamic routes
    338         anons = 0      # Number of anonymous wildcards
    339         pattern = ''   # Regular expression  pattern
    340         filters = []   # Lists of wildcard input filters
    341         builder = []   # Data structure for the URL builder
    342         is_static = True
    343         for key, mode, conf in self.parse_rule(rule):
    344             if mode:
    345                 is_static = False
    346                 mask, in_filter, out_filter = self.filters[mode](conf)
    347                 if key:
    348                     pattern += '(?P<%s>%s)' % (key, mask)
    349                 else:
    350                     pattern += '(?:%s)' % mask
    351                     key = 'anon%d' % anons; anons += 1
    352                 if in_filter: filters.append((key, in_filter))
    353                 builder.append((key, out_filter or str))
    354             elif key:
    355                 pattern += re.escape(key)
    356                 builder.append((None, key))
    357         self.builder[rule] = builder
    358         if name: self.builder[name] = builder
    359 
    360         if is_static and not self.strict_order:
    361             self.static[self.build(rule)] = target
    362             return
    363 
    364         def fpat_sub(m):
    365             return m.group(0) if len(m.group(1)) % 2 else m.group(1) + '(?:'
    366         flat_pattern = re.sub(r'(\\*)(\(\?P<[^>]*>|\((?!\?))', fpat_sub, pattern)
    367 
    368         try:
    369             re_match = re.compile('^(%s)$' % pattern).match
    370         except re.error:
    371             raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, _e()))
    372 
    373         def match(path):
    374             """ Return an url-argument dictionary. """
    375             url_args = re_match(path).groupdict()
    376             for name, wildcard_filter in filters:
    377                 try:
    378                     url_args[name] = wildcard_filter(url_args[name])
    379                 except ValueError:
    380                     raise HTTPError(400, 'Path has wrong format.')
    381             return url_args
    382 
    383         try:
    384             combined = '%s|(^%s$)' % (self.dynamic[-1][0].pattern, flat_pattern)
    385             self.dynamic[-1] = (re.compile(combined), self.dynamic[-1][1])
    386             self.dynamic[-1][1].append((match, target))
    387         except (AssertionError, IndexError): # AssertionError: Too many groups
    388             self.dynamic.append((re.compile('(^%s$)' % flat_pattern),
    389                                 [(match, target)]))
    390         return match
    391 
    392     def build(self, _name, *anons, **query):
    393         ''' Build an URL by filling the wildcards in a rule. '''
    394         builder = self.builder.get(_name)
    395         if not builder: raise RouteBuildError("No route with that name.", _name)
    396         try:
    397             for i, value in enumerate(anons): query['anon%d'%i] = value
    398             url = ''.join([f(query.pop(n)) if n else f for (n,f) in builder])
    399             return url if not query else url+'?'+urlencode(query)
    400         except KeyError:
    401             raise RouteBuildError('Missing URL argument: %r' % _e().args[0])
    402 
    403     def match(self, environ):
    404         ''' Return a (target, url_agrs) tuple or raise HTTPError(400/404/405). '''
    405         path, targets, urlargs = environ['PATH_INFO'] or '/', None, {}
    406         if path in self.static:
    407             targets = self.static[path]
    408         else:
    409             for combined, rules in self.dynamic:
    410                 match = combined.match(path)
    411                 if not match: continue
    412                 getargs, targets = rules[match.lastindex - 1]
    413                 urlargs = getargs(path) if getargs else {}
    414                 break
    415 
    416         if not targets:
    417             raise HTTPError(404, "Not found: " + repr(environ['PATH_INFO']))
    418         method = environ['REQUEST_METHOD'].upper()
    419         if method in targets:
    420             return targets[method], urlargs
    421         if method == 'HEAD' and 'GET' in targets:
    422             return targets['GET'], urlargs
    423         if 'ANY' in targets:
    424             return targets['ANY'], urlargs
    425         allowed = [verb for verb in targets if verb != 'ANY']
    426         if 'GET' in allowed and 'HEAD' not in allowed:
    427             allowed.append('HEAD')
    428         raise HTTPError(405, "Method not allowed.",
    429                         header=[('Allow',",".join(allowed))])
    430 
    431 
    432 class Route(object):
    433     ''' This class wraps a route callback along with route specific metadata and
    434         configuration and applies Plugins on demand. It is also responsible for
    435         turing an URL path rule into a regular expression usable by the Router.
    436     '''
    437 
    438     def __init__(self, app, rule, method, callback, name=None,
    439                  plugins=None, skiplist=None, **config):
    440         #: The application this route is installed to.
    441         self.app = app
    442         #: The path-rule string (e.g. ``/wiki/:page``).
    443         self.rule = rule
    444         #: The HTTP method as a string (e.g. ``GET``).
    445         self.method = method
    446         #: The original callback with no plugins applied. Useful for introspection.
    447         self.callback = callback
    448         #: The name of the route (if specified) or ``None``.
    449         self.name = name or None
    450         #: A list of route-specific plugins (see :meth:`Bottle.route`).
    451         self.plugins = plugins or []
    452         #: A list of plugins to not apply to this route (see :meth:`Bottle.route`).
    453         self.skiplist = skiplist or []
    454         #: Additional keyword arguments passed to the :meth:`Bottle.route`
    455         #: decorator are stored in this dictionary. Used for route-specific
    456         #: plugin configuration and meta-data.
    457         self.config = ConfigDict(config)
    458 
    459     def __call__(self, *a, **ka):
    460         depr("Some APIs changed to return Route() instances instead of"\
    461              " callables. Make sure to use the Route.call method and not to"\
    462              " call Route instances directly.")
    463         return self.call(*a, **ka)
    464 
    465     @cached_property
    466     def call(self):
    467         ''' The route callback with all plugins applied. This property is
    468             created on demand and then cached to speed up subsequent requests.'''
    469         return self._make_callback()
    470 
    471     def reset(self):
    472         ''' Forget any cached values. The next time :attr:`call` is accessed,
    473             all plugins are re-applied. '''
    474         self.__dict__.pop('call', None)
    475 
    476     def prepare(self):
    477         ''' Do all on-demand work immediately (useful for debugging).'''
    478         self.call
    479 
    480     @property
    481     def _context(self):
    482         depr('Switch to Plugin API v2 and access the Route object directly.')
    483         return dict(rule=self.rule, method=self.method, callback=self.callback,
    484                     name=self.name, app=self.app, config=self.config,
    485                     apply=self.plugins, skip=self.skiplist)
    486 
    487     def all_plugins(self):
    488         ''' Yield all Plugins affecting this route. '''
    489         unique = set()
    490         for p in reversed(self.app.plugins + self.plugins):
    491             if True in self.skiplist: break
    492             name = getattr(p, 'name', False)
    493             if name and (name in self.skiplist or name in unique): continue
    494             if p in self.skiplist or type(p) in self.skiplist: continue
    495             if name: unique.add(name)
    496             yield p
    497 
    498     def _make_callback(self):
    499         callback = self.callback
    500         for plugin in self.all_plugins():
    501             try:
    502                 if hasattr(plugin, 'apply'):
    503                     api = getattr(plugin, 'api', 1)
    504                     context = self if api > 1 else self._context
    505                     callback = plugin.apply(callback, context)
    506                 else:
    507                     callback = plugin(callback)
    508             except RouteReset: # Try again with changed configuration.
    509                 return self._make_callback()
    510             if not callback is self.callback:
    511                 update_wrapper(callback, self.callback)
    512         return callback
    513 
    514     def __repr__(self):
    515         return '<%s %r %r>' % (self.method, self.rule, self.callback)
    516 
    517 
    518 
    519 
    520 
    521 
    522 ###############################################################################
    523 # Application Object ###########################################################
    524 ###############################################################################
    525 
    526 
    527 class Bottle(object):
    528     """ Each Bottle object represents a single, distinct web application and
    529         consists of routes, callbacks, plugins and configuration. Instances are
    530         callable WSGI applications. """
    531 
    532     def __init__(self, catchall=True, autojson=True, config=None):
    533         self.routes = [] # List of installed :class:`Route` instances.
    534         self.router = Router() # Maps requests to :class:`Route` instances.
    535         self.plugins = [] # List of installed plugins.
    536 
    537         self.error_handler = {}
    538         self.config = ConfigDict(config or {})
    539         #: If true, most exceptions are catched and returned as :exc:`HTTPError`
    540         self.catchall = catchall
    541         #: An instance of :class:`HooksPlugin`. Empty by default.
    542         self.hooks = HooksPlugin()
    543         self.install(self.hooks)
    544         if autojson:
    545             self.install(JSONPlugin())
    546         self.install(TemplatePlugin())
    547 
    548     def mount(self, prefix, app, **options):
    549         ''' Mount an application (:class:`Bottle` or plain WSGI) to a specific
    550             URL prefix. Example::
    551 
    552                 root_app.mount('/admin/', admin_app)
    553 
    554             :param prefix: path prefix or `mount-point`. If it ends in a slash,
    555                 that slash is mandatory.
    556             :param app: an instance of :class:`Bottle` or a WSGI application.
    557 
    558             All other parameters are passed to the underlying :meth:`route` call.
    559         '''
    560         if isinstance(app, basestring):
    561             prefix, app = app, prefix
    562             depr('Parameter order of Bottle.mount() changed.') # 0.10
    563 
    564         parts = [p for p in prefix.split('/') if p]
    565         if not parts: raise ValueError('Empty path prefix.')
    566         path_depth = len(parts)
    567         options.setdefault('skip', True)
    568         options.setdefault('method', 'ANY')
    569 
    570         @self.route('/%s/:#.*#' % '/'.join(parts), **options)
    571         def mountpoint():
    572             try:
    573                 request.path_shift(path_depth)
    574                 rs = BaseResponse([], 200)
    575                 def start_response(status, header):
    576                     rs.status = status
    577                     for name, value in header: rs.add_header(name, value)
    578                     return rs.body.append
    579                 rs.body = itertools.chain(rs.body, app(request.environ, start_response))
    580                 return HTTPResponse(rs.body, rs.status_code, rs.headers)
    581             finally:
    582                 request.path_shift(-path_depth)
    583 
    584         if not prefix.endswith('/'):
    585             self.route('/' + '/'.join(parts), callback=mountpoint, **options)
    586 
    587     def merge(self, routes):
    588         ''' Merge the routes of another :cls:`Bottle` application or a list of
    589             :class:`Route` objects into this application. The routes keep their
    590             'owner', meaning that the :data:`Route.app` attribute is not
    591             changed. '''
    592         if isinstance(routes, Bottle):
    593             routes = routes.routes
    594         for route in routes:
    595             self.add_route(route)
    596 
    597     def install(self, plugin):
    598         ''' Add a plugin to the list of plugins and prepare it for being
    599             applied to all routes of this application. A plugin may be a simple
    600             decorator or an object that implements the :class:`Plugin` API.
    601         '''
    602         if hasattr(plugin, 'setup'): plugin.setup(self)
    603         if not callable(plugin) and not hasattr(plugin, 'apply'):
    604             raise TypeError("Plugins must be callable or implement .apply()")
    605         self.plugins.append(plugin)
    606         self.reset()
    607         return plugin
    608 
    609     def uninstall(self, plugin):
    610         ''' Uninstall plugins. Pass an instance to remove a specific plugin, a type
    611             object to remove all plugins that match that type, a string to remove
    612             all plugins with a matching ``name`` attribute or ``True`` to remove all
    613             plugins. Return the list of removed plugins. '''
    614         removed, remove = [], plugin
    615         for i, plugin in list(enumerate(self.plugins))[::-1]:
    616             if remove is True or remove is plugin or remove is type(plugin) \
    617             or getattr(plugin, 'name', True) == remove:
    618                 removed.append(plugin)
    619                 del self.plugins[i]
    620                 if hasattr(plugin, 'close'): plugin.close()
    621         if removed: self.reset()
    622         return removed
    623 
    624     def run(self, **kwargs):
    625         ''' Calls :func:`run` with the same parameters. '''
    626         run(self, **kwargs)
    627 
    628     def reset(self, route=None):
    629         ''' Reset all routes (force plugins to be re-applied) and clear all
    630             caches. If an ID or route object is given, only that specific route
    631             is affected. '''
    632         if route is None: routes = self.routes
    633         elif isinstance(route, Route): routes = [route]
    634         else: routes = [self.routes[route]]
    635         for route in routes: route.reset()
    636         if DEBUG:
    637             for route in routes: route.prepare()
    638         self.hooks.trigger('app_reset')
    639 
    640     def close(self):
    641         ''' Close the application and all installed plugins. '''
    642         for plugin in self.plugins:
    643             if hasattr(plugin, 'close'): plugin.close()
    644         self.stopped = True
    645 
    646     def match(self, environ):
    647         """ Search for a matching route and return a (:class:`Route` , urlargs)
    648             tuple. The second value is a dictionary with parameters extracted
    649             from the URL. Raise :exc:`HTTPError` (404/405) on a non-match."""
    650         return self.router.match(environ)
    651 
    652     def get_url(self, routename, **kargs):
    653         """ Return a string that matches a named route """
    654         scriptname = request.environ.get('SCRIPT_NAME', '').strip('/') + '/'
    655         location = self.router.build(routename, **kargs).lstrip('/')
    656         return urljoin(urljoin('/', scriptname), location)
    657 
    658     def add_route(self, route):
    659         ''' Add a route object, but do not change the :data:`Route.app`
    660             attribute.'''
    661         self.routes.append(route)
    662         self.router.add(route.rule, route.method, route, name=route.name)
    663         if DEBUG: route.prepare()
    664 
    665     def route(self, path=None, method='GET', callback=None, name=None,
    666               apply=None, skip=None, **config):
    667         """ A decorator to bind a function to a request URL. Example::
    668 
    669                 @app.route('/hello/:name')
    670                 def hello(name):
    671                     return 'Hello %s' % name
    672 
    673             The ``:name`` part is a wildcard. See :class:`Router` for syntax
    674             details.
    675 
    676             :param path: Request path or a list of paths to listen to. If no
    677               path is specified, it is automatically generated from the
    678               signature of the function.
    679             :param method: HTTP method (`GET`, `POST`, `PUT`, ...) or a list of
    680               methods to listen to. (default: `GET`)
    681             :param callback: An optional shortcut to avoid the decorator
    682               syntax. ``route(..., callback=func)`` equals ``route(...)(func)``
    683             :param name: The name for this route. (default: None)
    684             :param apply: A decorator or plugin or a list of plugins. These are
    685               applied to the route callback in addition to installed plugins.
    686             :param skip: A list of plugins, plugin classes or names. Matching
    687               plugins are not installed to this route. ``True`` skips all.
    688 
    689             Any additional keyword arguments are stored as route-specific
    690             configuration and passed to plugins (see :meth:`Plugin.apply`).
    691         """
    692         if callable(path): path, callback = None, path
    693         plugins = makelist(apply)
    694         skiplist = makelist(skip)
    695         def decorator(callback):
    696             # TODO: Documentation and test_data
    697             if isinstance(callback, basestring): callback = load(callback)
    698             for rule in makelist(path) or yieldroutes(callback):
    699                 for verb in makelist(method):
    700                     verb = verb.upper()
    701                     route = Route(self, rule, verb, callback, name=name,
    702                                   plugins=plugins, skiplist=skiplist, **config)
    703                     self.add_route(route)
    704             return callback
    705         return decorator(callback) if callback else decorator
    706 
    707     def get(self, path=None, method='GET', **options):
    708         """ Equals :meth:`route`. """
    709         return self.route(path, method, **options)
    710 
    711     def post(self, path=None, method='POST', **options):
    712         """ Equals :meth:`route` with a ``POST`` method parameter. """
    713         return self.route(path, method, **options)
    714 
    715     def put(self, path=None, method='PUT', **options):
    716         """ Equals :meth:`route` with a ``PUT`` method parameter. """
    717         return self.route(path, method, **options)
    718 
    719     def delete(self, path=None, method='DELETE', **options):
    720         """ Equals :meth:`route` with a ``DELETE`` method parameter. """
    721         return self.route(path, method, **options)
    722 
    723     def error(self, code=500):
    724         """ Decorator: Register an output handler for a HTTP error code"""
    725         def wrapper(handler):
    726             self.error_handler[int(code)] = handler
    727             return handler
    728         return wrapper
    729 
    730     def hook(self, name):
    731         """ Return a decorator that attaches a callback to a hook. Three hooks
    732             are currently implemented:
    733 
    734             - before_request: Executed once before each request
    735             - after_request: Executed once after each request
    736             - app_reset: Called whenever :meth:`reset` is called.
    737         """
    738         def wrapper(func):
    739             self.hooks.add(name, func)
    740             return func
    741         return wrapper
    742 
    743     def handle(self, path, method='GET'):
    744         """ (deprecated) Execute the first matching route callback and return
    745             the result. :exc:`HTTPResponse` exceptions are catched and returned.
    746             If :attr:`Bottle.catchall` is true, other exceptions are catched as
    747             well and returned as :exc:`HTTPError` instances (500).
    748         """
    749         depr("This method will change semantics in 0.10. Try to avoid it.")
    750         if isinstance(path, dict):
    751             return self._handle(path)
    752         return self._handle({'PATH_INFO': path, 'REQUEST_METHOD': method.upper()})
    753 
    754     def _handle(self, environ):
    755         try:
    756             environ['bottle.app'] = self
    757             request.bind(environ)
    758             response.bind()
    759             route, args = self.router.match(environ)
    760             environ['route.handle'] = environ['bottle.route'] = route
    761             environ['route.url_args'] = args
    762             return route.call(**args)
    763         except HTTPResponse:
    764             return _e()
    765         except RouteReset:
    766             route.reset()
    767             return self._handle(environ)
    768         except (KeyboardInterrupt, SystemExit, MemoryError):
    769             raise
    770         except Exception:
    771             if not self.catchall: raise
    772             stacktrace = format_exc(10)
    773             environ['wsgi.errors'].write(stacktrace)
    774             return HTTPError(500, "Internal Server Error", _e(), stacktrace)
    775 
    776     def _cast(self, out, peek=None):
    777         """ Try to convert the parameter into something WSGI compatible and set
    778         correct HTTP headers when possible.
    779         Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like,
    780         iterable of strings and iterable of unicodes
    781         """
    782 
    783         # Empty output is done here
    784         if not out:
    785             response['Content-Length'] = 0
    786             return []
    787         # Join lists of byte or unicode strings. Mixed lists are NOT supported
    788         if isinstance(out, (tuple, list))\
    789         and isinstance(out[0], (bytes, unicode)):
    790             out = out[0][0:0].join(out) # b'abc'[0:0] -> b''
    791         # Encode unicode strings
    792         if isinstance(out, unicode):
    793             out = out.encode(response.charset)
    794         # Byte Strings are just returned
    795         if isinstance(out, bytes):
    796             response['Content-Length'] = len(out)
    797             return [out]
    798         # HTTPError or HTTPException (recursive, because they may wrap anything)
    799         # TODO: Handle these explicitly in handle() or make them iterable.
    800         if isinstance(out, HTTPError):
    801             out.apply(response)
    802             out = self.error_handler.get(out.status, repr)(out)
    803             if isinstance(out, HTTPResponse):
    804                 depr('Error handlers must not return :exc:`HTTPResponse`.') #0.9
    805             return self._cast(out)
    806         if isinstance(out, HTTPResponse):
    807             out.apply(response)
    808             return self._cast(out.output)
    809 
    810         # File-like objects.
    811         if hasattr(out, 'read'):
    812             if 'wsgi.file_wrapper' in request.environ:
    813                 return request.environ['wsgi.file_wrapper'](out)
    814             elif hasattr(out, 'close') or not hasattr(out, '__iter__'):
    815                 return WSGIFileWrapper(out)
    816 
    817         # Handle Iterables. We peek into them to detect their inner type.
    818         try:
    819             out = iter(out)
    820             first = next(out)
    821             while not first:
    822                 first = next(out)
    823         except StopIteration:
    824             return self._cast('')
    825         except HTTPResponse:
    826             first = _e()
    827         except (KeyboardInterrupt, SystemExit, MemoryError):
    828             raise
    829         except Exception:
    830             if not self.catchall: raise
    831             first = HTTPError(500, 'Unhandled exception', _e(), format_exc(10))
    832 
    833         # These are the inner types allowed in iterator or generator objects.
    834         if isinstance(first, HTTPResponse):
    835             return self._cast(first)
    836         if isinstance(first, bytes):
    837             return itertools.chain([first], out)
    838         if isinstance(first, unicode):
    839             return imap(lambda x: x.encode(response.charset),
    840                                   itertools.chain([first], out))
    841         return self._cast(HTTPError(500, 'Unsupported response type: %s'\
    842                                          % type(first)))
    843 
    844     def wsgi(self, environ, start_response):
    845         """ The bottle WSGI-interface. """
    846         try:
    847             out = self._cast(self._handle(environ))
    848             # rfc2616 section 4.3
    849             if response._status_code in (100, 101, 204, 304)\
    850             or request.method == 'HEAD':
    851                 if hasattr(out, 'close'): out.close()
    852                 out = []
    853             if isinstance(response._status_line, unicode):
    854               response._status_line = str(response._status_line)
    855             start_response(response._status_line, list(response.iter_headers()))
    856             return out
    857         except (KeyboardInterrupt, SystemExit, MemoryError):
    858             raise
    859         except Exception:
    860             if not self.catchall: raise
    861             err = '<h1>Critical error while processing request: %s</h1>' \
    862                   % html_escape(environ.get('PATH_INFO', '/'))
    863             if DEBUG:
    864                 err += '<h2>Error:</h2>\n<pre>\n%s\n</pre>\n' \
    865                        '<h2>Traceback:</h2>\n<pre>\n%s\n</pre>\n' \
    866                        % (html_escape(repr(_e())), html_escape(format_exc(10)))
    867             environ['wsgi.errors'].write(err)
    868             headers = [('Content-Type', 'text/html; charset=UTF-8')]
    869             start_response('500 INTERNAL SERVER ERROR', headers)
    870             return [tob(err)]
    871 
    872     def __call__(self, environ, start_response):
    873         ''' Each instance of :class:'Bottle' is a WSGI application. '''
    874         return self.wsgi(environ, start_response)
    875 
    876 
    877 
    878 
    879 
    880 
    881 ###############################################################################
    882 # HTTP and WSGI Tools ##########################################################
    883 ###############################################################################
    884 
    885 
    886 class BaseRequest(object):
    887     """ A wrapper for WSGI environment dictionaries that adds a lot of
    888         convenient access methods and properties. Most of them are read-only."""
    889 
    890     #: Maximum size of memory buffer for :attr:`body` in bytes.
    891     MEMFILE_MAX = 102400
    892     #: Maximum number pr GET or POST parameters per request
    893     MAX_PARAMS  = 100
    894 
    895     def __init__(self, environ):
    896         """ Wrap a WSGI environ dictionary. """
    897         #: The wrapped WSGI environ dictionary. This is the only real attribute.
    898         #: All other attributes actually are read-only properties.
    899         self.environ = environ
    900         environ['bottle.request'] = self
    901 
    902     @DictProperty('environ', 'bottle.app', read_only=True)
    903     def app(self):
    904         ''' Bottle application handling this request. '''
    905         raise AttributeError('This request is not connected to an application.')
    906 
    907     @property
    908     def path(self):
    909         ''' The value of ``PATH_INFO`` with exactly one prefixed slash (to fix
    910             broken clients and avoid the "empty path" edge case). '''
    911         return '/' + self.environ.get('PATH_INFO','').lstrip('/')
    912 
    913     @property
    914     def method(self):
    915         ''' The ``REQUEST_METHOD`` value as an uppercase string. '''
    916         return self.environ.get('REQUEST_METHOD', 'GET').upper()
    917 
    918     @DictProperty('environ', 'bottle.request.headers', read_only=True)
    919     def headers(self):
    920         ''' A :class:`WSGIHeaderDict` that provides case-insensitive access to
    921             HTTP request headers. '''
    922         return WSGIHeaderDict(self.environ)
    923 
    924     def get_header(self, name, default=None):
    925         ''' Return the value of a request header, or a given default value. '''
    926         return self.headers.get(name, default)
    927 
    928     @DictProperty('environ', 'bottle.request.cookies', read_only=True)
    929     def cookies(self):
    930         """ Cookies parsed into a :class:`FormsDict`. Signed cookies are NOT
    931             decoded. Use :meth:`get_cookie` if you expect signed cookies. """
    932         cookies = SimpleCookie(self.environ.get('HTTP_COOKIE',''))
    933         cookies = list(cookies.values())[:self.MAX_PARAMS]
    934         return FormsDict((c.key, c.value) for c in cookies)
    935 
    936     def get_cookie(self, key, default=None, secret=None):
    937         """ Return the content of a cookie. To read a `Signed Cookie`, the
    938             `secret` must match the one used to create the cookie (see
    939             :meth:`BaseResponse.set_cookie`). If anything goes wrong (missing
    940             cookie or wrong signature), return a default value. """
    941         value = self.cookies.get(key)
    942         if secret and value:
    943             dec = cookie_decode(value, secret) # (key, value) tuple or None
    944             return dec[1] if dec and dec[0] == key else default
    945         return value or default
    946 
    947     @DictProperty('environ', 'bottle.request.query', read_only=True)
    948     def query(self):
    949         ''' The :attr:`query_string` parsed into a :class:`FormsDict`. These
    950             values are sometimes called "URL arguments" or "GET parameters", but
    951             not to be confused with "URL wildcards" as they are provided by the
    952             :class:`Router`. '''
    953         pairs = parse_qsl(self.query_string, keep_blank_values=True)
    954         get = self.environ['bottle.get'] = FormsDict()
    955         for key, value in pairs[:self.MAX_PARAMS]:
    956             get[key] = value
    957         return get
    958 
    959     @DictProperty('environ', 'bottle.request.forms', read_only=True)
    960     def forms(self):
    961         """ Form values parsed from an `url-encoded` or `multipart/form-data`
    962             encoded POST or PUT request body. The result is retuned as a
    963             :class:`FormsDict`. All keys and values are strings. File uploads
    964             are stored separately in :attr:`files`. """
    965         forms = FormsDict()
    966         for name, item in self.POST.allitems():
    967             if not hasattr(item, 'filename'):
    968                 forms[name] = item
    969         return forms
    970 
    971     @DictProperty('environ', 'bottle.request.params', read_only=True)
    972     def params(self):
    973         """ A :class:`FormsDict` with the combined values of :attr:`query` and
    974             :attr:`forms`. File uploads are stored in :attr:`files`. """
    975         params = FormsDict()
    976         for key, value in self.query.allitems():
    977             params[key] = value
    978         for key, value in self.forms.allitems():
    979             params[key] = value
    980         return params
    981 
    982     @DictProperty('environ', 'bottle.request.files', read_only=True)
    983     def files(self):
    984         """ File uploads parsed from an `url-encoded` or `multipart/form-data`
    985             encoded POST or PUT request body. The values are instances of
    986             :class:`cgi.FieldStorage`. The most important attributes are:
    987 
    988             filename
    989                 The filename, if specified; otherwise None; this is the client
    990                 side filename, *not* the file name on which it is stored (that's
    991                 a temporary file you don't deal with)
    992             file
    993                 The file(-like) object from which you can read the data.
    994             value
    995                 The value as a *string*; for file uploads, this transparently
    996                 reads the file every time you request the value. Do not do this
    997                 on big files.
    998         """
    999         files = FormsDict()
   1000         for name, item in self.POST.allitems():
   1001             if hasattr(item, 'filename'):
   1002                 files[name] = item
   1003         return files
   1004 
   1005     @DictProperty('environ', 'bottle.request.json', read_only=True)
   1006     def json(self):
   1007         ''' If the ``Content-Type`` header is ``application/json``, this
   1008             property holds the parsed content of the request body. Only requests
   1009             smaller than :attr:`MEMFILE_MAX` are processed to avoid memory
   1010             exhaustion. '''
   1011         if 'application/json' in self.environ.get('CONTENT_TYPE', '') \
   1012         and 0 < self.content_length < self.MEMFILE_MAX:
   1013             return json_loads(self.body.read(self.MEMFILE_MAX))
   1014         return None
   1015 
   1016     @DictProperty('environ', 'bottle.request.body', read_only=True)
   1017     def _body(self):
   1018         maxread = max(0, self.content_length)
   1019         stream = self.environ['wsgi.input']
   1020         body = BytesIO() if maxread < self.MEMFILE_MAX else TemporaryFile(mode='w+b')
   1021         while maxread > 0:
   1022             part = stream.read(min(maxread, self.MEMFILE_MAX))
   1023             if not part: break
   1024             body.write(part)
   1025             maxread -= len(part)
   1026         self.environ['wsgi.input'] = body
   1027         body.seek(0)
   1028         return body
   1029 
   1030     @property
   1031     def body(self):
   1032         """ The HTTP request body as a seek-able file-like object. Depending on
   1033             :attr:`MEMFILE_MAX`, this is either a temporary file or a
   1034             :class:`io.BytesIO` instance. Accessing this property for the first
   1035             time reads and replaces the ``wsgi.input`` environ variable.
   1036             Subsequent accesses just do a `seek(0)` on the file object. """
   1037         self._body.seek(0)
   1038         return self._body
   1039 
   1040     #: An alias for :attr:`query`.
   1041     GET = query
   1042 
   1043     @DictProperty('environ', 'bottle.request.post', read_only=True)
   1044     def POST(self):
   1045         """ The values of :attr:`forms` and :attr:`files` combined into a single
   1046             :class:`FormsDict`. Values are either strings (form values) or
   1047             instances of :class:`cgi.FieldStorage` (file uploads).
   1048         """
   1049         post = FormsDict()
   1050         safe_env = {'QUERY_STRING':''} # Build a safe environment for cgi
   1051         for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'):
   1052             if key in self.environ: safe_env[key] = self.environ[key]
   1053         if NCTextIOWrapper:
   1054             fb = NCTextIOWrapper(self.body, encoding='ISO-8859-1', newline='\n')
   1055         else:
   1056             fb = self.body
   1057         data = cgi.FieldStorage(fp=fb, environ=safe_env, keep_blank_values=True)
   1058         for item in (data.list or [])[:self.MAX_PARAMS]:
   1059             post[item.name] = item if item.filename else item.value
   1060         return post
   1061 
   1062     @property
   1063     def COOKIES(self):
   1064         ''' Alias for :attr:`cookies` (deprecated). '''
   1065         depr('BaseRequest.COOKIES was renamed to BaseRequest.cookies (lowercase).')
   1066         return self.cookies
   1067 
   1068     @property
   1069     def url(self):
   1070         """ The full request URI including hostname and scheme. If your app
   1071             lives behind a reverse proxy or load balancer and you get confusing
   1072             results, make sure that the ``X-Forwarded-Host`` header is set
   1073             correctly. """
   1074         return self.urlparts.geturl()
   1075 
   1076     @DictProperty('environ', 'bottle.request.urlparts', read_only=True)
   1077     def urlparts(self):
   1078         ''' The :attr:`url` string as an :class:`urlparse.SplitResult` tuple.
   1079             The tuple contains (scheme, host, path, query_string and fragment),
   1080             but the fragment is always empty because it is not visible to the
   1081             server. '''
   1082         env = self.environ
   1083         http = env.get('wsgi.url_scheme', 'http')
   1084         host = env.get('HTTP_X_FORWARDED_HOST') or env.get('HTTP_HOST')
   1085         if not host:
   1086             # HTTP 1.1 requires a Host-header. This is for HTTP/1.0 clients.
   1087             host = env.get('SERVER_NAME', '127.0.0.1')
   1088             port = env.get('SERVER_PORT')
   1089             if port and port != ('80' if http == 'http' else '443'):
   1090                 host += ':' + port
   1091         path = urlquote(self.fullpath)
   1092         return UrlSplitResult(http, host, path, env.get('QUERY_STRING'), '')
   1093 
   1094     @property
   1095     def fullpath(self):
   1096         """ Request path including :attr:`script_name` (if present). """
   1097         return urljoin(self.script_name, self.path.lstrip('/'))
   1098 
   1099     @property
   1100     def query_string(self):
   1101         """ The raw :attr:`query` part of the URL (everything in between ``?``
   1102             and ``#``) as a string. """
   1103         return self.environ.get('QUERY_STRING', '')
   1104 
   1105     @property
   1106     def script_name(self):
   1107         ''' The initial portion of the URL's `path` that was removed by a higher
   1108             level (server or routing middleware) before the application was
   1109             called. This script path is returned with leading and tailing
   1110             slashes. '''
   1111         script_name = self.environ.get('SCRIPT_NAME', '').strip('/')
   1112         return '/' + script_name + '/' if script_name else '/'
   1113 
   1114     def path_shift(self, shift=1):
   1115         ''' Shift path segments from :attr:`path` to :attr:`script_name` and
   1116             vice versa.
   1117 
   1118            :param shift: The number of path segments to shift. May be negative
   1119                          to change the shift direction. (default: 1)
   1120         '''
   1121         script = self.environ.get('SCRIPT_NAME','/')
   1122         self['SCRIPT_NAME'], self['PATH_INFO'] = path_shift(script, self.path, shift)
   1123 
   1124     @property
   1125     def content_length(self):
   1126         ''' The request body length as an integer. The client is responsible to
   1127             set this header. Otherwise, the real length of the body is unknown
   1128             and -1 is returned. In this case, :attr:`body` will be empty. '''
   1129         return int(self.environ.get('CONTENT_LENGTH') or -1)
   1130 
   1131     @property
   1132     def is_xhr(self):
   1133         ''' True if the request was triggered by a XMLHttpRequest. This only
   1134             works with JavaScript libraries that support the `X-Requested-With`
   1135             header (most of the popular libraries do). '''
   1136         requested_with = self.environ.get('HTTP_X_REQUESTED_WITH','')
   1137         return requested_with.lower() == 'xmlhttprequest'
   1138 
   1139     @property
   1140     def is_ajax(self):
   1141         ''' Alias for :attr:`is_xhr`. "Ajax" is not the right term. '''
   1142         return self.is_xhr
   1143 
   1144     @property
   1145     def auth(self):
   1146         """ HTTP authentication data as a (user, password) tuple. This
   1147             implementation currently supports basic (not digest) authentication
   1148             only. If the authentication happened at a higher level (e.g. in the
   1149             front web-server or a middleware), the password field is None, but
   1150             the user field is looked up from the ``REMOTE_USER`` environ
   1151             variable. On any errors, None is returned. """
   1152         basic = parse_auth(self.environ.get('HTTP_AUTHORIZATION',''))
   1153         if basic: return basic
   1154         ruser = self.environ.get('REMOTE_USER')
   1155         if ruser: return (ruser, None)
   1156         return None
   1157 
   1158     @property
   1159     def remote_route(self):
   1160         """ A list of all IPs that were involved in this request, starting with
   1161             the client IP and followed by zero or more proxies. This does only
   1162             work if all proxies support the ```X-Forwarded-For`` header. Note
   1163             that this information can be forged by malicious clients. """
   1164         proxy = self.environ.get('HTTP_X_FORWARDED_FOR')
   1165         if proxy: return [ip.strip() for ip in proxy.split(',')]
   1166         remote = self.environ.get('REMOTE_ADDR')
   1167         return [remote] if remote else []
   1168 
   1169     @property
   1170     def remote_addr(self):
   1171         """ The client IP as a string. Note that this information can be forged
   1172             by malicious clients. """
   1173         route = self.remote_route
   1174         return route[0] if route else None
   1175 
   1176     def copy(self):
   1177         """ Return a new :class:`Request` with a shallow :attr:`environ` copy. """
   1178         return Request(self.environ.copy())
   1179 
   1180     def get(self, value, default=None): return self.environ.get(value, default)
   1181     def __getitem__(self, key): return self.environ[key]
   1182     def __delitem__(self, key): self[key] = ""; del(self.environ[key])
   1183     def __iter__(self): return iter(self.environ)
   1184     def __len__(self): return len(self.environ)
   1185     def keys(self): return self.environ.keys()
   1186     def __setitem__(self, key, value):
   1187         """ Change an environ value and clear all caches that depend on it. """
   1188 
   1189         if self.environ.get('bottle.request.readonly'):
   1190             raise KeyError('The environ dictionary is read-only.')
   1191 
   1192         self.environ[key] = value
   1193         todelete = ()
   1194 
   1195         if key == 'wsgi.input':
   1196             todelete = ('body', 'forms', 'files', 'params', 'post', 'json')
   1197         elif key == 'QUERY_STRING':
   1198             todelete = ('query', 'params')
   1199         elif key.startswith('HTTP_'):
   1200             todelete = ('headers', 'cookies')
   1201 
   1202         for key in todelete:
   1203             self.environ.pop('bottle.request.'+key, None)
   1204 
   1205     def __repr__(self):
   1206         return '<%s: %s %s>' % (self.__class__.__name__, self.method, self.url)
   1207 
   1208 def _hkey(s):
   1209     return s.title().replace('_','-')
   1210 
   1211 
   1212 class HeaderProperty(object):
   1213     def __init__(self, name, reader=None, writer=str, default=''):
   1214         self.name, self.reader, self.writer, self.default = name, reader, writer, default
   1215         self.__doc__ = 'Current value of the %r header.' % name.title()
   1216 
   1217     def __get__(self, obj, cls):
   1218         if obj is None: return self
   1219         value = obj.headers.get(self.name)
   1220         return self.reader(value) if (value and self.reader) else (value or self.default)
   1221 
   1222     def __set__(self, obj, value):
   1223         if self.writer: value = self.writer(value)
   1224         obj.headers[self.name] = value
   1225 
   1226     def __delete__(self, obj):
   1227         if self.name in obj.headers:
   1228             del obj.headers[self.name]
   1229 
   1230 
   1231 class BaseResponse(object):
   1232     """ Storage class for a response body as well as headers and cookies.
   1233 
   1234         This class does support dict-like case-insensitive item-access to
   1235         headers, but is NOT a dict. Most notably, iterating over a response
   1236         yields parts of the body and not the headers.
   1237     """
   1238 
   1239     default_status = 200
   1240     default_content_type = 'text/html; charset=UTF-8'
   1241 
   1242     # Header blacklist for specific response codes
   1243     # (rfc2616 section 10.2.3 and 10.3.5)
   1244     bad_headers = {
   1245         204: set(('Content-Type',)),
   1246         304: set(('Allow', 'Content-Encoding', 'Content-Language',
   1247                   'Content-Length', 'Content-Range', 'Content-Type',
   1248                   'Content-Md5', 'Last-Modified'))}
   1249 
   1250     def __init__(self, body='', status=None, **headers):
   1251         self._status_line = None
   1252         self._status_code = None
   1253         self._cookies = None
   1254         self._headers = {'Content-Type': [self.default_content_type]}
   1255         self.body = body
   1256         self.status = status or self.default_status
   1257         if headers:
   1258             for name, value in headers.items():
   1259                 self[name] = value
   1260 
   1261     def copy(self):
   1262         ''' Returns a copy of self. '''
   1263         copy = Response()
   1264         copy.status = self.status
   1265         copy._headers = dict((k, v[:]) for (k, v) in self._headers.items())
   1266         return copy
   1267 
   1268     def __iter__(self):
   1269         return iter(self.body)
   1270 
   1271     def close(self):
   1272         if hasattr(self.body, 'close'):
   1273             self.body.close()
   1274 
   1275     @property
   1276     def status_line(self):
   1277         ''' The HTTP status line as a string (e.g. ``404 Not Found``).'''
   1278         return self._status_line
   1279 
   1280     @property
   1281     def status_code(self):
   1282         ''' The HTTP status code as an integer (e.g. 404).'''
   1283         return self._status_code
   1284 
   1285     def _set_status(self, status):
   1286         if isinstance(status, int):
   1287             code, status = status, _HTTP_STATUS_LINES.get(status)
   1288         elif ' ' in status:
   1289             status = status.strip()
   1290             code   = int(status.split()[0])
   1291         else:
   1292             raise ValueError('String status line without a reason phrase.')
   1293         if not 100 <= code <= 999: raise ValueError('Status code out of range.')
   1294         self._status_code = code
   1295         self._status_line = status or ('%d Unknown' % code)
   1296 
   1297     def _get_status(self):
   1298         return self._status_line
   1299 
   1300     status = property(_get_status, _set_status, None,
   1301         ''' A writeable property to change the HTTP response status. It accepts
   1302             either a numeric code (100-999) or a string with a custom reason
   1303             phrase (e.g. "404 Brain not found"). Both :data:`status_line` and
   1304             :data:`status_code` are updated accordingly. The return value is
   1305             always a status string. ''')
   1306     del _get_status, _set_status
   1307 
   1308     @property
   1309     def headers(self):
   1310         ''' An instance of :class:`HeaderDict`, a case-insensitive dict-like
   1311             view on the response headers. '''
   1312         self.__dict__['headers'] = hdict = HeaderDict()
   1313         hdict.dict = self._headers
   1314         return hdict
   1315 
   1316     def __contains__(self, name): return _hkey(name) in self._headers
   1317     def __delitem__(self, name):  del self._headers[_hkey(name)]
   1318     def __getitem__(self, name):  return self._headers[_hkey(name)][-1]
   1319     def __setitem__(self, name, value): self._headers[_hkey(name)] = [str(value)]
   1320 
   1321     def get_header(self, name, default=None):
   1322         ''' Return the value of a previously defined header. If there is no
   1323             header with that name, return a default value. '''
   1324         return self._headers.get(_hkey(name), [default])[-1]
   1325 
   1326     def set_header(self, name, value, append=False):
   1327         ''' Create a new response header, replacing any previously defined
   1328             headers with the same name. '''
   1329         if append:
   1330             self.add_header(name, value)
   1331         else:
   1332             self._headers[_hkey(name)] = [str(value)]
   1333 
   1334     def add_header(self, name, value):
   1335         ''' Add an additional response header, not removing duplicates. '''
   1336         self._headers.setdefault(_hkey(name), []).append(str(value))
   1337 
   1338     def iter_headers(self):
   1339         ''' Yield (header, value) tuples, skipping headers that are not
   1340             allowed with the current response status code. '''
   1341         headers = self._headers.items()
   1342         bad_headers = self.bad_headers.get(self._status_code)
   1343         if bad_headers:
   1344             headers = [h for h in headers if h[0] not in bad_headers]
   1345         for name, values in headers:
   1346             for value in values:
   1347                 yield name, value
   1348         if self._cookies:
   1349             for c in self._cookies.values():
   1350                 yield 'Set-Cookie', c.OutputString()
   1351 
   1352     def wsgiheader(self):
   1353         depr('The wsgiheader method is deprecated. See headerlist.') #0.10
   1354         return self.headerlist
   1355 
   1356     @property
   1357     def headerlist(self):
   1358         ''' WSGI conform list of (header, value) tuples. '''
   1359         return list(self.iter_headers())
   1360 
   1361     content_type = HeaderProperty('Content-Type')
   1362     content_length = HeaderProperty('Content-Length', reader=int)
   1363 
   1364     @property
   1365     def charset(self):
   1366         """ Return the charset specified in the content-type header (default: utf8). """
   1367         if 'charset=' in self.content_type:
   1368             return self.content_type.split('charset=')[-1].split(';')[0].strip()
   1369         return 'UTF-8'
   1370 
   1371     @property
   1372     def COOKIES(self):
   1373         """ A dict-like SimpleCookie instance. This should not be used directly.
   1374             See :meth:`set_cookie`. """
   1375         depr('The COOKIES dict is deprecated. Use `set_cookie()` instead.') # 0.10
   1376         if not self._cookies:
   1377             self._cookies = SimpleCookie()
   1378         return self._cookies
   1379 
   1380     def set_cookie(self, name, value, secret=None, **options):
   1381         ''' Create a new cookie or replace an old one. If the `secret` parameter is
   1382             set, create a `Signed Cookie` (described below).
   1383 
   1384             :param name: the name of the cookie.
   1385             :param value: the value of the cookie.
   1386             :param secret: a signature key required for signed cookies.
   1387 
   1388             Additionally, this method accepts all RFC 2109 attributes that are
   1389             supported by :class:`cookie.Morsel`, including:
   1390 
   1391             :param max_age: maximum age in seconds. (default: None)
   1392             :param expires: a datetime object or UNIX timestamp. (default: None)
   1393             :param domain: the domain that is allowed to read the cookie.
   1394               (default: current domain)
   1395             :param path: limits the cookie to a given path (default: current path)
   1396             :param secure: limit the cookie to HTTPS connections (default: off).
   1397             :param httponly: prevents client-side javascript to read this cookie
   1398               (default: off, requires Python 2.6 or newer).
   1399 
   1400             If neither `expires` nor `max_age` is set (default), the cookie will
   1401             expire at the end of the browser session (as soon as the browser
   1402             window is closed).
   1403 
   1404             Signed cookies may store any pickle-able object and are
   1405             cryptographically signed to prevent manipulation. Keep in mind that
   1406             cookies are limited to 4kb in most browsers.
   1407 
   1408             Warning: Signed cookies are not encrypted (the client can still see
   1409             the content) and not copy-protected (the client can restore an old
   1410             cookie). The main intention is to make pickling and unpickling
   1411             save, not to store secret information at client side.
   1412         '''
   1413         if not self._cookies:
   1414             self._cookies = SimpleCookie()
   1415 
   1416         if secret:
   1417             value = touni(cookie_encode((name, value), secret))
   1418         elif not isinstance(value, basestring):
   1419             raise TypeError('Secret key missing for non-string Cookie.')
   1420 
   1421         if len(value) > 4096: raise ValueError('Cookie value to long.')
   1422         self._cookies[name] = value
   1423 
   1424         for key, value in options.items():
   1425             if key == 'max_age':
   1426                 if isinstance(value, timedelta):
   1427                     value = value.seconds + value.days * 24 * 3600
   1428             if key == 'expires':
   1429                 if isinstance(value, (datedate, datetime)):
   1430                     value = value.timetuple()
   1431                 elif isinstance(value, (int, float)):
   1432                     value = time.gmtime(value)
   1433                 value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value)
   1434             self._cookies[name][key.replace('_', '-')] = value
   1435 
   1436     def delete_cookie(self, key, **kwargs):
   1437         ''' Delete a cookie. Be sure to use the same `domain` and `path`
   1438             settings as used to create the cookie. '''
   1439         kwargs['max_age'] = -1
   1440         kwargs['expires'] = 0
   1441         self.set_cookie(key, '', **kwargs)
   1442 
   1443     def __repr__(self):
   1444         out = ''
   1445         for name, value in self.headerlist:
   1446             out += '%s: %s\n' % (name.title(), value.strip())
   1447         return out
   1448 
   1449 #: Thread-local storage for :class:`LocalRequest` and :class:`LocalResponse`
   1450 #: attributes.
   1451 _lctx = threading.local()
   1452 
   1453 def local_property(name, doc=None):
   1454 
   1455     return property(
   1456         lambda self: getattr(_lctx, name),
   1457         lambda self, value: setattr(_lctx, name, value),
   1458         lambda self: delattr(_lctx, name),
   1459         doc or ('Thread-local property stored in :data:`_lctx.%s` ' % name)
   1460     )
   1461 
   1462 class LocalRequest(BaseRequest):
   1463     ''' A thread-local subclass of :class:`BaseRequest` with a different
   1464         set of attribues for each thread. There is usually only one global
   1465         instance of this class (:data:`request`). If accessed during a
   1466         request/response cycle, this instance always refers to the *current*
   1467         request (even on a multithreaded server). '''
   1468     def __init__(self): pass
   1469     bind = BaseRequest.__init__
   1470     environ = local_property('request_environ')
   1471 
   1472 
   1473 class LocalResponse(BaseResponse):
   1474     ''' A thread-local subclass of :class:`BaseResponse` with a different
   1475         set of attribues for each thread. There is usually only one global
   1476         instance of this class (:data:`response`). Its attributes are used
   1477         to build the HTTP response at the end of the request/response cycle.
   1478     '''
   1479     def __init__(self): pass
   1480     bind = BaseResponse.__init__
   1481     _status_line = local_property('response_status_line')
   1482     _status_code = local_property('response_status_code')
   1483     _cookies     = local_property('response_cookies')
   1484     _headers     = local_property('response_headers')
   1485     body         = local_property('response_body')
   1486 
   1487 Response = LocalResponse # BC 0.9
   1488 Request  = LocalRequest  # BC 0.9
   1489 
   1490 
   1491 
   1492 
   1493 
   1494 
   1495 ###############################################################################
   1496 # Plugins ######################################################################
   1497 ###############################################################################
   1498 
   1499 class PluginError(BottleException): pass
   1500 
   1501 class JSONPlugin(object):
   1502     name = 'json'
   1503     api  = 2
   1504 
   1505     def __init__(self, json_dumps=json_dumps):
   1506         self.json_dumps = json_dumps
   1507 
   1508     def apply(self, callback, context):
   1509         dumps = self.json_dumps
   1510         if not dumps: return callback
   1511         def wrapper(*a, **ka):
   1512             rv = callback(*a, **ka)
   1513             if isinstance(rv, dict):
   1514                 #Attempt to serialize, raises exception on failure
   1515                 json_response = dumps(rv)
   1516                 #Set content type only if serialization succesful
   1517                 response.content_type = 'application/json'
   1518                 return json_response
   1519             return rv
   1520         return wrapper
   1521 
   1522 
   1523 class HooksPlugin(object):
   1524     name = 'hooks'
   1525     api  = 2
   1526 
   1527     _names = 'before_request', 'after_request', 'app_reset'
   1528 
   1529     def __init__(self):
   1530         self.hooks = dict((name, []) for name in self._names)
   1531         self.app = None
   1532 
   1533     def _empty(self):
   1534         return not (self.hooks['before_request'] or self.hooks['after_request'])
   1535 
   1536     def setup(self, app):
   1537         self.app = app
   1538 
   1539     def add(self, name, func):
   1540         ''' Attach a callback to a hook. '''
   1541         was_empty = self._empty()
   1542         self.hooks.setdefault(name, []).append(func)
   1543         if self.app and was_empty and not self._empty(): self.app.reset()
   1544 
   1545     def remove(self, name, func):
   1546         ''' Remove a callback from a hook. '''
   1547         was_empty = self._empty()
   1548         if name in self.hooks and func in self.hooks[name]:
   1549             self.hooks[name].remove(func)
   1550         if self.app and not was_empty and self._empty(): self.app.reset()
   1551 
   1552     def trigger(self, name, *a, **ka):
   1553         ''' Trigger a hook and return a list of results. '''
   1554         hooks = self.hooks[name]
   1555         if ka.pop('reversed', False): hooks = hooks[::-1]
   1556         return [hook(*a, **ka) for hook in hooks]
   1557 
   1558     def apply(self, callback, context):
   1559         if self._empty(): return callback
   1560         def wrapper(*a, **ka):
   1561             self.trigger('before_request')
   1562             rv = callback(*a, **ka)
   1563             self.trigger('after_request', reversed=True)
   1564             return rv
   1565         return wrapper
   1566 
   1567 
   1568 class TemplatePlugin(object):
   1569     ''' This plugin applies the :func:`view` decorator to all routes with a
   1570         `template` config parameter. If the parameter is a tuple, the second
   1571         element must be a dict with additional options (e.g. `template_engine`)
   1572         or default variables for the template. '''
   1573     name = 'template'
   1574     api  = 2
   1575 
   1576     def apply(self, callback, route):
   1577         conf = route.config.get('template')
   1578         if isinstance(conf, (tuple, list)) and len(conf) == 2:
   1579             return view(conf[0], **conf[1])(callback)
   1580         elif isinstance(conf, str) and 'template_opts' in route.config:
   1581             depr('The `template_opts` parameter is deprecated.') #0.9
   1582             return view(conf, **route.config['template_opts'])(callback)
   1583         elif isinstance(conf, str):
   1584             return view(conf)(callback)
   1585         else:
   1586             return callback
   1587 
   1588 
   1589 #: Not a plugin, but part of the plugin API. TODO: Find a better place.
   1590 class _ImportRedirect(object):
   1591     def __init__(self, name, impmask):
   1592         ''' Create a virtual package that redirects imports (see PEP 302). '''
   1593         self.name = name
   1594         self.impmask = impmask
   1595         self.module = sys.modules.setdefault(name, imp.new_module(name))
   1596         self.module.__dict__.update({'__file__': __file__, '__path__': [],
   1597                                     '__all__': [], '__loader__': self})
   1598         sys.meta_path.append(self)
   1599 
   1600     def find_module(self, fullname, path=None):
   1601         if '.' not in fullname: return
   1602         packname, modname = fullname.rsplit('.', 1)
   1603         if packname != self.name: return
   1604         return self
   1605 
   1606     def load_module(self, fullname):
   1607         if fullname in sys.modules: return sys.modules[fullname]
   1608         packname, modname = fullname.rsplit('.', 1)
   1609         realname = self.impmask % modname
   1610         __import__(realname)
   1611         module = sys.modules[fullname] = sys.modules[realname]
   1612         setattr(self.module, modname, module)
   1613         module.__loader__ = self
   1614         return module
   1615 
   1616 
   1617 
   1618 
   1619 
   1620 
   1621 ###############################################################################
   1622 # Common Utilities #############################################################
   1623 ###############################################################################
   1624 
   1625 
   1626 class MultiDict(DictMixin):
   1627     """ This dict stores multiple values per key, but behaves exactly like a
   1628         normal dict in that it returns only the newest value for any given key.
   1629         There are special methods available to access the full list of values.
   1630     """
   1631 
   1632     def __init__(self, *a, **k):
   1633         self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items())
   1634 
   1635     def __len__(self): return len(self.dict)
   1636     def __iter__(self): return iter(self.dict)
   1637     def __contains__(self, key): return key in self.dict
   1638     def __delitem__(self, key): del self.dict[key]
   1639     def __getitem__(self, key): return self.dict[key][-1]
   1640     def __setitem__(self, key, value): self.append(key, value)
   1641     def keys(self): return self.dict.keys()
   1642 
   1643     if py3k:
   1644         def values(self): return (v[-1] for v in self.dict.values())
   1645         def items(self): return ((k, v[-1]) for k, v in self.dict.items())
   1646         def allitems(self):
   1647             return ((k, v) for k, vl in self.dict.items() for v in vl)
   1648         iterkeys = keys
   1649         itervalues = values
   1650         iteritems = items
   1651         iterallitems = allitems
   1652 
   1653     else:
   1654         def values(self): return [v[-1] for v in self.dict.values()]
   1655         def items(self): return [(k, v[-1]) for k, v in self.dict.items()]
   1656         def iterkeys(self): return self.dict.iterkeys()
   1657         def itervalues(self): return (v[-1] for v in self.dict.itervalues())
   1658         def iteritems(self):
   1659             return ((k, v[-1]) for k, v in self.dict.iteritems())
   1660         def iterallitems(self):
   1661             return ((k, v) for k, vl in self.dict.iteritems() for v in vl)
   1662         def allitems(self):
   1663             return [(k, v) for k, vl in self.dict.iteritems() for v in vl]
   1664 
   1665     def get(self, key, default=None, index=-1, type=None):
   1666         ''' Return the most recent value for a key.
   1667 
   1668             :param default: The default value to be returned if the key is not
   1669                    present or the type conversion fails.
   1670             :param index: An index for the list of available values.
   1671             :param type: If defined, this callable is used to cast the value
   1672                     into a specific type. Exception are suppressed and result in
   1673                     the default value to be returned.
   1674         '''
   1675         try:
   1676             val = self.dict[key][index]
   1677             return type(val) if type else val
   1678         except Exception:
   1679             pass
   1680         return default
   1681 
   1682     def append(self, key, value):
   1683         ''' Add a new value to the list of values for this key. '''
   1684         self.dict.setdefault(key, []).append(value)
   1685 
   1686     def replace(self, key, value):
   1687         ''' Replace the list of values with a single value. '''
   1688         self.dict[key] = [value]
   1689 
   1690     def getall(self, key):
   1691         ''' Return a (possibly empty) list of values for a key. '''
   1692         return self.dict.get(key) or []
   1693 
   1694     #: Aliases for WTForms to mimic other multi-dict APIs (Django)
   1695     getone = get
   1696     getlist = getall
   1697 
   1698 
   1699 
   1700 class FormsDict(MultiDict):
   1701     ''' This :class:`MultiDict` subclass is used to store request form data.
   1702         Additionally to the normal dict-like item access methods (which return
   1703         unmodified data as native strings), this container also supports
   1704         attribute-like access to its values. Attributes are automatically de-
   1705         or recoded to match :attr:`input_encoding` (default: 'utf8'). Missing
   1706         attributes default to an empty string. '''
   1707 
   1708     #: Encoding used for attribute values.
   1709     input_encoding = 'utf8'
   1710     #: If true (default), unicode strings are first encoded with `latin1`
   1711     #: and then decoded to match :attr:`input_encoding`.
   1712     recode_unicode = True
   1713 
   1714     def _fix(self, s, encoding=None):
   1715         if isinstance(s, unicode) and self.recode_unicode: # Python 3 WSGI
   1716             s = s.encode('latin1')
   1717         if isinstance(s, bytes): # Python 2 WSGI
   1718             return s.decode(encoding or self.input_encoding)
   1719         return s
   1720 
   1721     def decode(self, encoding=None):
   1722         ''' Returns a copy with all keys and values de- or recoded to match
   1723             :attr:`input_encoding`. Some libraries (e.g. WTForms) want a
   1724             unicode dictionary. '''
   1725         copy = FormsDict()
   1726         enc = copy.input_encoding = encoding or self.input_encoding
   1727         copy.recode_unicode = False
   1728         for key, value in self.allitems():
   1729             copy.append(self._fix(key, enc), self._fix(value, enc))
   1730         return copy
   1731 
   1732     def getunicode(self, name, default=None, encoding=None):
   1733         try:
   1734             return self._fix(self[name], encoding)
   1735         except (UnicodeError, KeyError):
   1736             return default
   1737 
   1738     def __getattr__(self, name, default=unicode()):
   1739         return self.getunicode(name, default=default)
   1740 
   1741 
   1742 class HeaderDict(MultiDict):
   1743     """ A case-insensitive version of :class:`MultiDict` that defaults to
   1744         replace the old value instead of appending it. """
   1745 
   1746     def __init__(self, *a, **ka):
   1747         self.dict = {}
   1748         if a or ka: self.update(*a, **ka)
   1749 
   1750     def __contains__(self, key): return _hkey(key) in self.dict
   1751     def __delitem__(self, key): del self.dict[_hkey(key)]
   1752     def __getitem__(self, key): return self.dict[_hkey(key)][-1]
   1753     def __setitem__(self, key, value): self.dict[_hkey(key)] = [str(value)]
   1754     def append(self, key, value):
   1755         self.dict.setdefault(_hkey(key), []).append(str(value))
   1756     def replace(self, key, value): self.dict[_hkey(key)] = [str(value)]
   1757     def getall(self, key): return self.dict.get(_hkey(key)) or []
   1758     def get(self, key, default=None, index=-1):
   1759         return MultiDict.get(self, _hkey(key), default, index)
   1760     def filter(self, names):
   1761         for name in [_hkey(n) for n in names]:
   1762             if name in self.dict:
   1763                 del self.dict[name]
   1764 
   1765 
   1766 class WSGIHeaderDict(DictMixin):
   1767     ''' This dict-like class wraps a WSGI environ dict and provides convenient
   1768         access to HTTP_* fields. Keys and values are native strings
   1769         (2.x bytes or 3.x unicode) and keys are case-insensitive. If the WSGI
   1770         environment contains non-native string values, these are de- or encoded
   1771         using a lossless 'latin1' character set.
   1772 
   1773         The API will remain stable even on changes to the relevant PEPs.
   1774         Currently PEP 333, 444 and 3333 are supported. (PEP 444 is the only one
   1775         that uses non-native strings.)
   1776     '''
   1777     #: List of keys that do not have a 'HTTP_' prefix.
   1778     cgikeys = ('CONTENT_TYPE', 'CONTENT_LENGTH')
   1779 
   1780     def __init__(self, environ):
   1781         self.environ = environ
   1782 
   1783     def _ekey(self, key):
   1784         ''' Translate header field name to CGI/WSGI environ key. '''
   1785         key = key.replace('-','_').upper()
   1786         if key in self.cgikeys:
   1787             return key
   1788         return 'HTTP_' + key
   1789 
   1790     def raw(self, key, default=None):
   1791         ''' Return the header value as is (may be bytes or unicode). '''
   1792         return self.environ.get(self._ekey(key), default)
   1793 
   1794     def __getitem__(self, key):
   1795         return tonat(self.environ[self._ekey(key)], 'latin1')
   1796 
   1797     def __setitem__(self, key, value):
   1798         raise TypeError("%s is read-only." % self.__class__)
   1799 
   1800     def __delitem__(self, key):
   1801         raise TypeError("%s is read-only." % self.__class__)
   1802 
   1803     def __iter__(self):
   1804         for key in self.environ:
   1805             if key[:5] == 'HTTP_':
   1806                 yield key[5:].replace('_', '-').title()
   1807             elif key in self.cgikeys:
   1808                 yield key.replace('_', '-').title()
   1809 
   1810     def keys(self): return [x for x in self]
   1811     def __len__(self): return len(self.keys())
   1812     def __contains__(self, key): return self._ekey(key) in self.environ
   1813 
   1814 
   1815 class ConfigDict(dict):
   1816     ''' A dict-subclass with some extras: You can access keys like attributes.
   1817         Uppercase attributes create new ConfigDicts and act as name-spaces.
   1818         Other missing attributes return None. Calling a ConfigDict updates its
   1819         values and returns itself.
   1820 
   1821         >>> cfg = ConfigDict()
   1822         >>> cfg.Namespace.value = 5
   1823         >>> cfg.OtherNamespace(a=1, b=2)
   1824         >>> cfg
   1825         {'Namespace': {'value': 5}, 'OtherNamespace': {'a': 1, 'b': 2}}
   1826     '''
   1827 
   1828     def __getattr__(self, key):
   1829         if key not in self and key[0].isupper():
   1830             self[key] = ConfigDict()
   1831         return self.get(key)
   1832 
   1833     def __setattr__(self, key, value):
   1834         if hasattr(dict, key):
   1835             raise AttributeError('Read-only attribute.')
   1836         if key in self and self[key] and isinstance(self[key], ConfigDict):
   1837             raise AttributeError('Non-empty namespace attribute.')
   1838         self[key] = value
   1839 
   1840     def __delattr__(self, key):
   1841         if key in self: del self[key]
   1842 
   1843     def __call__(self, *a, **ka):
   1844         for key, value in dict(*a, **ka).items(): setattr(self, key, value)
   1845         return self
   1846 
   1847 
   1848 class AppStack(list):
   1849     """ A stack-like list. Calling it returns the head of the stack. """
   1850 
   1851     def __call__(self):
   1852         """ Return the current default application. """
   1853         return self[-1]
   1854 
   1855     def push(self, value=None):
   1856         """ Add a new :class:`Bottle` instance to the stack """
   1857         if not isinstance(value, Bottle):
   1858             value = Bottle()
   1859         self.append(value)
   1860         return value
   1861 
   1862 
   1863 class WSGIFileWrapper(object):
   1864 
   1865     def __init__(self, fp, buffer_size=1024*64):
   1866         self.fp, self.buffer_size = fp, buffer_size
   1867         for attr in ('fileno', 'close', 'read', 'readlines', 'tell', 'seek'):
   1868             if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr))
   1869 
   1870     def __iter__(self):
   1871         buff, read = self.buffer_size, self.read
   1872         while True:
   1873             part = read(buff)
   1874             if not part: return
   1875             yield part
   1876 
   1877 
   1878 
   1879 
   1880 
   1881 
   1882 ###############################################################################
   1883 # Application Helper ###########################################################
   1884 ###############################################################################
   1885 
   1886 
   1887 def abort(code=500, text='Unknown Error: Application stopped.'):
   1888     """ Aborts execution and causes a HTTP error. """
   1889     raise HTTPError(code, text)
   1890 
   1891 
   1892 def redirect(url, code=None):
   1893     """ Aborts execution and causes a 303 or 302 redirect, depending on
   1894         the HTTP protocol version. """
   1895     if code is None:
   1896         code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302
   1897     location = urljoin(request.url, url)
   1898     raise HTTPResponse("", status=code, header=dict(Location=location))
   1899 
   1900 
   1901 def _file_iter_range(fp, offset, bytes, maxread=1024*1024):
   1902     ''' Yield chunks from a range in a file. No chunk is bigger than maxread.'''
   1903     fp.seek(offset)
   1904     while bytes > 0:
   1905         part = fp.read(min(bytes, maxread))
   1906         if not part: break
   1907         bytes -= len(part)
   1908         yield part
   1909 
   1910 
   1911 def static_file(filename, root, mimetype='auto', download=False):
   1912     """ Open a file in a safe way and return :exc:`HTTPResponse` with status
   1913         code 200, 305, 401 or 404. Set Content-Type, Content-Encoding,
   1914         Content-Length and Last-Modified header. Obey If-Modified-Since header
   1915         and HEAD requests.
   1916     """
   1917     root = os.path.abspath(root) + os.sep
   1918     filename = os.path.abspath(os.path.join(root, filename.strip('/\\')))
   1919     header = dict()
   1920 
   1921     if not filename.startswith(root):
   1922         return HTTPError(403, "Access denied.")
   1923     if not os.path.exists(filename) or not os.path.isfile(filename):
   1924         return HTTPError(404, "File does not exist.")
   1925     if not os.access(filename, os.R_OK):
   1926         return HTTPError(403, "You do not have permission to access this file.")
   1927 
   1928     if mimetype == 'auto':
   1929         mimetype, encoding = mimetypes.guess_type(filename)
   1930         if mimetype: header['Content-Type'] = mimetype
   1931         if encoding: header['Content-Encoding'] = encoding
   1932     elif mimetype:
   1933         header['Content-Type'] = mimetype
   1934 
   1935     if download:
   1936         download = os.path.basename(filename if download == True else download)
   1937         header['Content-Disposition'] = 'attachment; filename="%s"' % download
   1938 
   1939     stats = os.stat(filename)
   1940     header['Content-Length'] = clen = stats.st_size
   1941     lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
   1942     header['Last-Modified'] = lm
   1943 
   1944     ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')
   1945     if ims:
   1946         ims = parse_date(ims.split(";")[0].strip())
   1947     if ims is not None and ims >= int(stats.st_mtime):
   1948         header['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
   1949         return HTTPResponse(status=304, header=header)
   1950 
   1951     body = '' if request.method == 'HEAD' else open(filename, 'rb')
   1952 
   1953     header["Accept-Ranges"] = "bytes"
   1954     ranges = request.environ.get('HTTP_RANGE')
   1955     if 'HTTP_RANGE' in request.environ:
   1956         ranges = list(parse_range_header(request.environ['HTTP_RANGE'], clen))
   1957         if not ranges:
   1958             return HTTPError(416, "Requested Range Not Satisfiable")
   1959         offset, end = ranges[0]
   1960         header["Content-Range"] = "bytes %d-%d/%d" % (offset, end-1, clen)
   1961         header["Content-Length"] = str(end-offset)
   1962         if body: body = _file_iter_range(body, offset, end-offset)
   1963         return HTTPResponse(body, header=header, status=206)
   1964     return HTTPResponse(body, header=header)
   1965 
   1966 
   1967 
   1968 
   1969 
   1970 
   1971 ###############################################################################
   1972 # HTTP Utilities and MISC (TODO) ###############################################
   1973 ###############################################################################
   1974 
   1975 
   1976 def debug(mode=True):
   1977     """ Change the debug level.
   1978     There is only one debug level supported at the moment."""
   1979     global DEBUG
   1980     DEBUG = bool(mode)
   1981 
   1982 
   1983 def parse_date(ims):
   1984     """ Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch. """
   1985     try:
   1986         ts = email.utils.parsedate_tz(ims)
   1987         return time.mktime(ts[:8] + (0,)) - (ts[9] or 0) - time.timezone
   1988     except (TypeError, ValueError, IndexError, OverflowError):
   1989         return None
   1990 
   1991 
   1992 def parse_auth(header):
   1993     """ Parse rfc2617 HTTP authentication header string (basic) and return (user,pass) tuple or None"""
   1994     try:
   1995         method, data = header.split(None, 1)
   1996         if method.lower() == 'basic':
   1997             user, pwd = touni(base64.b64decode(tob(data))).split(':',1)
   1998             return user, pwd
   1999     except (KeyError, ValueError):
   2000         return None
   2001 
   2002 def parse_range_header(header, maxlen=0):
   2003     ''' Yield (start, end) ranges parsed from a HTTP Range header. Skip
   2004         unsatisfiable ranges. The end index is non-inclusive.'''
   2005     if not header or header[:6] != 'bytes=': return
   2006     ranges = [r.split('-', 1) for r in header[6:].split(',') if '-' in r]
   2007     for start, end in ranges:
   2008         try:
   2009             if not start:  # bytes=-100    -> last 100 bytes
   2010                 start, end = max(0, maxlen-int(end)), maxlen
   2011             elif not end:  # bytes=100-    -> all but the first 99 bytes
   2012                 start, end = int(start), maxlen
   2013             else:          # bytes=100-200 -> bytes 100-200 (inclusive)
   2014                 start, end = int(start), min(int(end)+1, maxlen)
   2015             if 0 <= start < end <= maxlen:
   2016                 yield start, end
   2017         except ValueError:
   2018             pass
   2019 
   2020 def _lscmp(a, b):
   2021     ''' Compares two strings in a cryptographically save way:
   2022         Runtime is not affected by length of common prefix. '''
   2023     return not sum(0 if x==y else 1 for x, y in zip(a, b)) and len(a) == len(b)
   2024 
   2025 
   2026 def cookie_encode(data, key):
   2027     ''' Encode and sign a pickle-able object. Return a (byte) string '''
   2028     msg = base64.b64encode(pickle.dumps(data, -1))
   2029     sig = base64.b64encode(hmac.new(tob(key), msg).digest())
   2030     return tob('!') + sig + tob('?') + msg
   2031 
   2032 
   2033 def cookie_decode(data, key):
   2034     ''' Verify and decode an encoded string. Return an object or None.'''
   2035     data = tob(data)
   2036     if cookie_is_encoded(data):
   2037         sig, msg = data.split(tob('?'), 1)
   2038         if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg).digest())):
   2039             return pickle.loads(base64.b64decode(msg))
   2040     return None
   2041 
   2042 
   2043 def cookie_is_encoded(data):
   2044     ''' Return True if the argument looks like a encoded cookie.'''
   2045     return bool(data.startswith(tob('!')) and tob('?') in data)
   2046 
   2047 
   2048 def html_escape(string):
   2049     ''' Escape HTML special characters ``&<>`` and quotes ``'"``. '''
   2050     return string.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;')\
   2051                  .replace('"','&quot;').replace("'",'&#039;')
   2052 
   2053 
   2054 def html_quote(string):
   2055     ''' Escape and quote a string to be used as an HTTP attribute.'''
   2056     return '"%s"' % html_escape(string).replace('\n','%#10;')\
   2057                     .replace('\r','&#13;').replace('\t','&#9;')
   2058 
   2059 
   2060 def yieldroutes(func):
   2061     """ Return a generator for routes that match the signature (name, args)
   2062     of the func parameter. This may yield more than one route if the function
   2063     takes optional keyword arguments. The output is best described by example::
   2064 
   2065         a()         -> '/a'
   2066         b(x, y)     -> '/b/:x/:y'
   2067         c(x, y=5)   -> '/c/:x' and '/c/:x/:y'
   2068         d(x=5, y=6) -> '/d' and '/d/:x' and '/d/:x/:y'
   2069     """
   2070     import inspect # Expensive module. Only import if necessary.
   2071     path = '/' + func.__name__.replace('__','/').lstrip('/')
   2072     spec = inspect.getargspec(func)
   2073     argc = len(spec[0]) - len(spec[3] or [])
   2074     path += ('/:%s' * argc) % tuple(spec[0][:argc])
   2075     yield path
   2076     for arg in spec[0][argc:]:
   2077         path += '/:%s' % arg
   2078         yield path
   2079 
   2080 
   2081 def path_shift(script_name, path_info, shift=1):
   2082     ''' Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa.
   2083 
   2084         :return: The modified paths.
   2085         :param script_name: The SCRIPT_NAME path.
   2086         :param script_name: The PATH_INFO path.
   2087         :param shift: The number of path fragments to shift. May be negative to
   2088           change the shift direction. (default: 1)
   2089     '''
   2090     if shift == 0: return script_name, path_info
   2091     pathlist = path_info.strip('/').split('/')
   2092     scriptlist = script_name.strip('/').split('/')
   2093     if pathlist and pathlist[0] == '': pathlist = []
   2094     if scriptlist and scriptlist[0] == '': scriptlist = []
   2095     if shift > 0 and shift <= len(pathlist):
   2096         moved = pathlist[:shift]
   2097         scriptlist = scriptlist + moved
   2098         pathlist = pathlist[shift:]
   2099     elif shift < 0 and shift >= -len(scriptlist):
   2100         moved = scriptlist[shift:]
   2101         pathlist = moved + pathlist
   2102         scriptlist = scriptlist[:shift]
   2103     else:
   2104         empty = 'SCRIPT_NAME' if shift < 0 else 'PATH_INFO'
   2105         raise AssertionError("Cannot shift. Nothing left from %s" % empty)
   2106     new_script_name = '/' + '/'.join(scriptlist)
   2107     new_path_info = '/' + '/'.join(pathlist)
   2108     if path_info.endswith('/') and pathlist: new_path_info += '/'
   2109     return new_script_name, new_path_info
   2110 
   2111 
   2112 def validate(**vkargs):
   2113     """
   2114     Validates and manipulates keyword arguments by user defined callables.
   2115     Handles ValueError and missing arguments by raising HTTPError(403).
   2116     """
   2117     depr('Use route wildcard filters instead.')
   2118     def decorator(func):
   2119         @functools.wraps(func)
   2120         def wrapper(*args, **kargs):
   2121             for key, value in vkargs.items():
   2122                 if key not in kargs:
   2123                     abort(403, 'Missing parameter: %s' % key)
   2124                 try:
   2125                     kargs[key] = value(kargs[key])
   2126                 except ValueError:
   2127                     abort(403, 'Wrong parameter format for: %s' % key)
   2128             return func(*args, **kargs)
   2129         return wrapper
   2130     return decorator
   2131 
   2132 
   2133 def auth_basic(check, realm="private", text="Access denied"):
   2134     ''' Callback decorator to require HTTP auth (basic).
   2135         TODO: Add route(check_auth=...) parameter. '''
   2136     def decorator(func):
   2137       def wrapper(*a, **ka):
   2138         user, password = request.auth or (None, None)
   2139         if user is None or not check(user, password):
   2140           response.headers['WWW-Authenticate'] = 'Basic realm="%s"' % realm
   2141           return HTTPError(401, text)
   2142         return func(*a, **ka)
   2143       return wrapper
   2144     return decorator
   2145 
   2146 
   2147 # Shortcuts for common Bottle methods.
   2148 # They all refer to the current default application.
   2149 
   2150 def make_default_app_wrapper(name):
   2151     ''' Return a callable that relays calls to the current default app. '''
   2152     @functools.wraps(getattr(Bottle, name))
   2153     def wrapper(*a, **ka):
   2154         return getattr(app(), name)(*a, **ka)
   2155     return wrapper
   2156 
   2157 route     = make_default_app_wrapper('route')
   2158 get       = make_default_app_wrapper('get')
   2159 post      = make_default_app_wrapper('post')
   2160 put       = make_default_app_wrapper('put')
   2161 delete    = make_default_app_wrapper('delete')
   2162 error     = make_default_app_wrapper('error')
   2163 mount     = make_default_app_wrapper('mount')
   2164 hook      = make_default_app_wrapper('hook')
   2165 install   = make_default_app_wrapper('install')
   2166 uninstall = make_default_app_wrapper('uninstall')
   2167 url       = make_default_app_wrapper('get_url')
   2168 
   2169 
   2170 
   2171 
   2172 
   2173 
   2174 
   2175 ###############################################################################
   2176 # Server Adapter ###############################################################
   2177 ###############################################################################
   2178 
   2179 
   2180 class ServerAdapter(object):
   2181     quiet = False
   2182     def __init__(self, host='127.0.0.1', port=8080, **config):
   2183         self.options = config
   2184         self.host = host
   2185         self.port = int(port)
   2186 
   2187     def run(self, handler): # pragma: no cover
   2188         pass
   2189 
   2190     def __repr__(self):
   2191         args = ', '.join(['%s=%s'%(k,repr(v)) for k, v in self.options.items()])
   2192         return "%s(%s)" % (self.__class__.__name__, args)
   2193 
   2194 
   2195 class CGIServer(ServerAdapter):
   2196     quiet = True
   2197     def run(self, handler): # pragma: no cover
   2198         from wsgiref.handlers import CGIHandler
   2199         def fixed_environ(environ, start_response):
   2200             environ.setdefault('PATH_INFO', '')
   2201             return handler(environ, start_response)
   2202         CGIHandler().run(fixed_environ)
   2203 
   2204 
   2205 class FlupFCGIServer(ServerAdapter):
   2206     def run(self, handler): # pragma: no cover
   2207         import flup.server.fcgi
   2208         self.options.setdefault('bindAddress', (self.host, self.port))
   2209         flup.server.fcgi.WSGIServer(handler, **self.options).run()
   2210 
   2211 
   2212 class WSGIRefServer(ServerAdapter):
   2213     def run(self, handler): # pragma: no cover
   2214         from wsgiref.simple_server import make_server, WSGIRequestHandler
   2215         if self.quiet:
   2216             class QuietHandler(WSGIRequestHandler):
   2217                 def log_request(*args, **kw): pass
   2218             self.options['handler_class'] = QuietHandler
   2219         srv = make_server(self.host, self.port, handler, **self.options)
   2220         srv.serve_forever()
   2221 
   2222 
   2223 class CherryPyServer(ServerAdapter):
   2224     def run(self, handler): # pragma: no cover
   2225         from cherrypy import wsgiserver
   2226         server = wsgiserver.CherryPyWSGIServer((self.host, self.port), handler)
   2227         try:
   2228             server.start()
   2229         finally:
   2230             server.stop()
   2231 
   2232 
   2233 class WaitressServer(ServerAdapter):
   2234     def run(self, handler):
   2235         from waitress import serve
   2236         serve(handler, host=self.host, port=self.port)
   2237 
   2238 
   2239 class PasteServer(ServerAdapter):
   2240     def run(self, handler): # pragma: no cover
   2241         from paste import httpserver
   2242         if not self.quiet:
   2243             from paste.translogger import TransLogger
   2244             handler = TransLogger(handler)
   2245         httpserver.serve(handler, host=self.host, port=str(self.port),
   2246                          **self.options)
   2247 
   2248 
   2249 class MeinheldServer(ServerAdapter):
   2250     def run(self, handler):
   2251         from meinheld import server
   2252         server.listen((self.host, self.port))
   2253         server.run(handler)
   2254 
   2255 
   2256 class FapwsServer(ServerAdapter):
   2257     """ Extremely fast webserver using libev. See http://www.fapws.org/ """
   2258     def run(self, handler): # pragma: no cover
   2259         import fapws._evwsgi as evwsgi
   2260         from fapws import base, config
   2261         port = self.port
   2262         if float(config.SERVER_IDENT[-2:]) > 0.4:
   2263             # fapws3 silently changed its API in 0.5
   2264             port = str(port)
   2265         evwsgi.start(self.host, port)
   2266         # fapws3 never releases the GIL. Complain upstream. I tried. No luck.
   2267         if 'BOTTLE_CHILD' in os.environ and not self.quiet:
   2268             _stderr("WARNING: Auto-reloading does not work with Fapws3.\n")
   2269             _stderr("         (Fapws3 breaks python thread support)\n")
   2270         evwsgi.set_base_module(base)
   2271         def app(environ, start_response):
   2272             environ['wsgi.multiprocess'] = False
   2273             return handler(environ, start_response)
   2274         evwsgi.wsgi_cb(('', app))
   2275         evwsgi.run()
   2276 
   2277 
   2278 class TornadoServer(ServerAdapter):
   2279     """ The super hyped asynchronous server by facebook. Untested. """
   2280     def run(self, handler): # pragma: no cover
   2281         import tornado.wsgi, tornado.httpserver, tornado.ioloop
   2282         container = tornado.wsgi.WSGIContainer(handler)
   2283         server = tornado.httpserver.HTTPServer(container)
   2284         server.listen(port=self.port)
   2285         tornado.ioloop.IOLoop.instance().start()
   2286 
   2287 
   2288 class AppEngineServer(ServerAdapter):
   2289     """ Adapter for Google App Engine. """
   2290     quiet = True
   2291     def run(self, handler):
   2292         from google.appengine.ext.webapp import util
   2293         # A main() function in the handler script enables 'App Caching'.
   2294         # Lets makes sure it is there. This _really_ improves performance.
   2295         module = sys.modules.get('__main__')
   2296         if module and not hasattr(module, 'main'):
   2297             module.main = lambda: util.run_wsgi_app(handler)
   2298         util.run_wsgi_app(handler)
   2299 
   2300 
   2301 class TwistedServer(ServerAdapter):
   2302     """ Untested. """
   2303     def run(self, handler):
   2304         from twisted.web import server, wsgi
   2305         from twisted.python.threadpool import ThreadPool
   2306         from twisted.internet import reactor
   2307         thread_pool = ThreadPool()
   2308         thread_pool.start()
   2309         reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop)
   2310         factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, handler))
   2311         reactor.listenTCP(self.port, factory, interface=self.host)
   2312         reactor.run()
   2313 
   2314 
   2315 class DieselServer(ServerAdapter):
   2316     """ Untested. """
   2317     def run(self, handler):
   2318         from diesel.protocols.wsgi import WSGIApplication
   2319         app = WSGIApplication(handler, port=self.port)
   2320         app.run()
   2321 
   2322 
   2323 class GeventServer(ServerAdapter):
   2324     """ Untested. Options:
   2325 
   2326         * `monkey` (default: True) fixes the stdlib to use greenthreads.
   2327         * `fast` (default: False) uses libevent's http server, but has some
   2328           issues: No streaming, no pipelining, no SSL.
   2329     """
   2330     def run(self, handler):
   2331         from gevent import wsgi as wsgi_fast, pywsgi, monkey, local
   2332         if self.options.get('monkey', True):
   2333             if not threading.local is local.local: monkey.patch_all()
   2334         wsgi = wsgi_fast if self.options.get('fast') else pywsgi
   2335         wsgi.WSGIServer((self.host, self.port), handler).serve_forever()
   2336 
   2337 
   2338 class GunicornServer(ServerAdapter):
   2339     """ Untested. See http://gunicorn.org/configure.html for options. """
   2340     def run(self, handler):
   2341         from gunicorn.app.base import Application
   2342 
   2343         config = {'bind': "%s:%d" % (self.host, int(self.port))}
   2344         config.update(self.options)
   2345 
   2346         class GunicornApplication(Application):
   2347             def init(self, parser, opts, args):
   2348                 return config
   2349 
   2350             def load(self):
   2351                 return handler
   2352 
   2353         GunicornApplication().run()
   2354 
   2355 
   2356 class EventletServer(ServerAdapter):
   2357     """ Untested """
   2358     def run(self, handler):
   2359         from eventlet import wsgi, listen
   2360         try:
   2361             wsgi.server(listen((self.host, self.port)), handler,
   2362                         log_output=(not self.quiet))
   2363         except TypeError:
   2364             # Fallback, if we have old version of eventlet
   2365             wsgi.server(listen((self.host, self.port)), handler)
   2366 
   2367 
   2368 class RocketServer(ServerAdapter):
   2369     """ Untested. """
   2370     def run(self, handler):
   2371         from rocket import Rocket
   2372         server = Rocket((self.host, self.port), 'wsgi', { 'wsgi_app' : handler })
   2373         server.start()
   2374 
   2375 
   2376 class BjoernServer(ServerAdapter):
   2377     """ Fast server written in C: https://github.com/jonashaag/bjoern """
   2378     def run(self, handler):
   2379         from bjoern import run
   2380         run(handler, self.host, self.port)
   2381 
   2382 
   2383 class AutoServer(ServerAdapter):
   2384     """ Untested. """
   2385     adapters = [WaitressServer, PasteServer, TwistedServer, CherryPyServer, WSGIRefServer]
   2386     def run(self, handler):
   2387         for sa in self.adapters:
   2388             try:
   2389                 return sa(self.host, self.port, **self.options).run(handler)
   2390             except ImportError:
   2391                 pass
   2392 
   2393 server_names = {
   2394     'cgi': CGIServer,
   2395     'flup': FlupFCGIServer,
   2396     'wsgiref': WSGIRefServer,
   2397     'waitress': WaitressServer,
   2398     'cherrypy': CherryPyServer,
   2399     'paste': PasteServer,
   2400     'fapws3': FapwsServer,
   2401     'tornado': TornadoServer,
   2402     'gae': AppEngineServer,
   2403     'twisted': TwistedServer,
   2404     'diesel': DieselServer,
   2405     'meinheld': MeinheldServer,
   2406     'gunicorn': GunicornServer,
   2407     'eventlet': EventletServer,
   2408     'gevent': GeventServer,
   2409     'rocket': RocketServer,
   2410     'bjoern' : BjoernServer,
   2411     'auto': AutoServer,
   2412 }
   2413 
   2414 
   2415 
   2416 
   2417 
   2418 
   2419 ###############################################################################
   2420 # Application Control ##########################################################
   2421 ###############################################################################
   2422 
   2423 
   2424 def load(target, **namespace):
   2425     """ Import a module or fetch an object from a module.
   2426 
   2427         * ``package.module`` returns `module` as a module object.
   2428         * ``pack.mod:name`` returns the module variable `name` from `pack.mod`.
   2429         * ``pack.mod:func()`` calls `pack.mod.func()` and returns the result.
   2430 
   2431         The last form accepts not only function calls, but any type of
   2432         expression. Keyword arguments passed to this function are available as
   2433         local variables. Example: ``import_string('re:compile(x)', x='[a-z]')``
   2434     """
   2435     module, target = target.split(":", 1) if ':' in target else (target, None)
   2436     if module not in sys.modules: __import__(module)
   2437     if not target: return sys.modules[module]
   2438     if target.isalnum(): return getattr(sys.modules[module], target)
   2439     package_name = module.split('.')[0]
   2440     namespace[package_name] = sys.modules[package_name]
   2441     return eval('%s.%s' % (module, target), namespace)
   2442 
   2443 
   2444 def load_app(target):
   2445     """ Load a bottle application from a module and make sure that the import
   2446         does not affect the current default application, but returns a separate
   2447         application object. See :func:`load` for the target parameter. """
   2448     global NORUN; NORUN, nr_old = True, NORUN
   2449     try:
   2450         tmp = default_app.push() # Create a new "default application"
   2451         rv = load(target) # Import the target module
   2452         return rv if callable(rv) else tmp
   2453     finally:
   2454         default_app.remove(tmp) # Remove the temporary added default application
   2455         NORUN = nr_old
   2456 
   2457 _debug = debug
   2458 def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,
   2459         interval=1, reloader=False, quiet=False, plugins=None,
   2460         debug=False, **kargs):
   2461     """ Start a server instance. This method blocks until the server terminates.
   2462 
   2463         :param app: WSGI application or target string supported by
   2464                :func:`load_app`. (default: :func:`default_app`)
   2465         :param server: Server adapter to use. See :data:`server_names` keys
   2466                for valid names or pass a :class:`ServerAdapter` subclass.
   2467                (default: `wsgiref`)
   2468         :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on
   2469                all interfaces including the external one. (default: 127.0.0.1)
   2470         :param port: Server port to bind to. Values below 1024 require root
   2471                privileges. (default: 8080)
   2472         :param reloader: Start auto-reloading server? (default: False)
   2473         :param interval: Auto-reloader interval in seconds (default: 1)
   2474         :param quiet: Suppress output to stdout and stderr? (default: False)
   2475         :param options: Options passed to the server adapter.
   2476      """
   2477     if NORUN: return
   2478     if reloader and not os.environ.get('BOTTLE_CHILD'):
   2479         try:
   2480             lockfile = None
   2481             fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock')
   2482             os.close(fd) # We only need this file to exist. We never write to it
   2483             while os.path.exists(lockfile):
   2484                 args = [sys.executable] + sys.argv
   2485                 environ = os.environ.copy()
   2486                 environ['BOTTLE_CHILD'] = 'true'
   2487                 environ['BOTTLE_LOCKFILE'] = lockfile
   2488                 p = subprocess.Popen(args, env=environ)
   2489                 while p.poll() is None: # Busy wait...
   2490                     os.utime(lockfile, None) # I am alive!
   2491                     time.sleep(interval)
   2492                 if p.poll() != 3:
   2493                     if os.path.exists(lockfile): os.unlink(lockfile)
   2494                     sys.exit(p.poll())
   2495         except KeyboardInterrupt:
   2496             pass
   2497         finally:
   2498             if os.path.exists(lockfile):
   2499                 os.unlink(lockfile)
   2500         return
   2501 
   2502     try:
   2503         _debug(debug)
   2504         app = app or default_app()
   2505         if isinstance(app, basestring):
   2506             app = load_app(app)
   2507         if not callable(app):
   2508             raise ValueError("Application is not callable: %r" % app)
   2509 
   2510         for plugin in plugins or []:
   2511             app.install(plugin)
   2512 
   2513         if server in server_names:
   2514             server = server_names.get(server)
   2515         if isinstance(server, basestring):
   2516             server = load(server)
   2517         if isinstance(server, type):
   2518             server = server(host=host, port=port, **kargs)
   2519         if not isinstance(server, ServerAdapter):
   2520             raise ValueError("Unknown or unsupported server: %r" % server)
   2521 
   2522         server.quiet = server.quiet or quiet
   2523         if not server.quiet:
   2524             _stderr("Bottle v%s server starting up (using %s)...\n" % (__version__, repr(server)))
   2525             _stderr("Listening on http://%s:%d/\n" % (server.host, server.port))
   2526             _stderr("Hit Ctrl-C to quit.\n\n")
   2527 
   2528         if reloader:
   2529             lockfile = os.environ.get('BOTTLE_LOCKFILE')
   2530             bgcheck = FileCheckerThread(lockfile, interval)
   2531             with bgcheck:
   2532                 server.run(app)
   2533             if bgcheck.status == 'reload':
   2534                 sys.exit(3)
   2535         else:
   2536             server.run(app)
   2537     except KeyboardInterrupt:
   2538         pass
   2539     except (SystemExit, MemoryError):
   2540         raise
   2541     except:
   2542         if not reloader: raise
   2543         if not getattr(server, 'quiet', quiet):
   2544             print_exc()
   2545         time.sleep(interval)
   2546         sys.exit(3)
   2547 
   2548 
   2549 
   2550 class FileCheckerThread(threading.Thread):
   2551     ''' Interrupt main-thread as soon as a changed module file is detected,
   2552         the lockfile gets deleted or gets to old. '''
   2553 
   2554     def __init__(self, lockfile, interval):
   2555         threading.Thread.__init__(self)
   2556         self.lockfile, self.interval = lockfile, interval
   2557         #: Is one of 'reload', 'error' or 'exit'
   2558         self.status = None
   2559 
   2560     def run(self):
   2561         exists = os.path.exists
   2562         mtime = lambda path: os.stat(path).st_mtime
   2563         files = dict()
   2564 
   2565         for module in list(sys.modules.values()):
   2566             path = getattr(module, '__file__', '')
   2567             if path[-4:] in ('.pyo', '.pyc'): path = path[:-1]
   2568             if path and exists(path): files[path] = mtime(path)
   2569 
   2570         while not self.status:
   2571             if not exists(self.lockfile)\
   2572             or mtime(self.lockfile) < time.time() - self.interval - 5:
   2573                 self.status = 'error'
   2574                 thread.interrupt_main()
   2575             for path, lmtime in list(files.items()):
   2576                 if not exists(path) or mtime(path) > lmtime:
   2577                     self.status = 'reload'
   2578                     thread.interrupt_main()
   2579                     break
   2580             time.sleep(self.interval)
   2581 
   2582     def __enter__(self):
   2583         self.start()
   2584 
   2585     def __exit__(self, exc_type, exc_val, exc_tb):
   2586         if not self.status: self.status = 'exit' # silent exit
   2587         self.join()
   2588         return exc_type is not None and issubclass(exc_type, KeyboardInterrupt)
   2589 
   2590 
   2591 
   2592 
   2593 
   2594 ###############################################################################
   2595 # Template Adapters ############################################################
   2596 ###############################################################################
   2597 
   2598 
   2599 class TemplateError(HTTPError):
   2600     def __init__(self, message):
   2601         HTTPError.__init__(self, 500, message)
   2602 
   2603 
   2604 class BaseTemplate(object):
   2605     """ Base class and minimal API for template adapters """
   2606     extensions = ['tpl','html','thtml','stpl']
   2607     settings = {} #used in prepare()
   2608     defaults = {} #used in render()
   2609 
   2610     def __init__(self, source=None, name=None, lookup=[], encoding='utf8', **settings):
   2611         """ Create a new template.
   2612         If the source parameter (str or buffer) is missing, the name argument
   2613         is used to guess a template filename. Subclasses can assume that
   2614         self.source and/or self.filename are set. Both are strings.
   2615         The lookup, encoding and settings parameters are stored as instance
   2616         variables.
   2617         The lookup parameter stores a list containing directory paths.
   2618         The encoding parameter should be used to decode byte strings or files.
   2619         The settings parameter contains a dict for engine-specific settings.
   2620         """
   2621         self.name = name
   2622         self.source = source.read() if hasattr(source, 'read') else source
   2623         self.filename = source.filename if hasattr(source, 'filename') else None
   2624         self.lookup = [os.path.abspath(x) for x in lookup]
   2625         self.encoding = encoding
   2626         self.settings = self.settings.copy() # Copy from class variable
   2627         self.settings.update(settings) # Apply
   2628         if not self.source and self.name:
   2629             self.filename = self.search(self.name, self.lookup)
   2630             if not self.filename:
   2631                 raise TemplateError('Template %s not found.' % repr(name))
   2632         if not self.source and not self.filename:
   2633             raise TemplateError('No template specified.')
   2634         self.prepare(**self.settings)
   2635 
   2636     @classmethod
   2637     def search(cls, name, lookup=[]):
   2638         """ Search name in all directories specified in lookup.
   2639         First without, then with common extensions. Return first hit. """
   2640         if os.path.isfile(name): return name
   2641         for spath in lookup:
   2642             fname = os.path.join(spath, name)
   2643             if os.path.isfile(fname):
   2644                 return fname
   2645             for ext in cls.extensions:
   2646                 if os.path.isfile('%s.%s' % (fname, ext)):
   2647                     return '%s.%s' % (fname, ext)
   2648 
   2649     @classmethod
   2650     def global_config(cls, key, *args):
   2651         ''' This reads or sets the global settings stored in class.settings. '''
   2652         if args:
   2653             cls.settings = cls.settings.copy() # Make settings local to class
   2654             cls.settings[key] = args[0]
   2655         else:
   2656             return cls.settings[key]
   2657 
   2658     def prepare(self, **options):
   2659         """ Run preparations (parsing, caching, ...).
   2660         It should be possible to call this again to refresh a template or to
   2661         update settings.
   2662         """
   2663         raise NotImplementedError
   2664 
   2665     def render(self, *args, **kwargs):
   2666         """ Render the template with the specified local variables and return
   2667         a single byte or unicode string. If it is a byte string, the encoding
   2668         must match self.encoding. This method must be thread-safe!
   2669         Local variables may be provided in dictionaries (*args)
   2670         or directly, as keywords (**kwargs).
   2671         """
   2672         raise NotImplementedError
   2673 
   2674 
   2675 class MakoTemplate(BaseTemplate):
   2676     def prepare(self, **options):
   2677         from mako.template import Template
   2678         from mako.lookup import TemplateLookup
   2679         options.update({'input_encoding':self.encoding})
   2680         options.setdefault('format_exceptions', bool(DEBUG))
   2681         lookup = TemplateLookup(directories=self.lookup, **options)
   2682         if self.source:
   2683             self.tpl = Template(self.source, lookup=lookup, **options)
   2684         else:
   2685             self.tpl = Template(uri=self.name, filename=self.filename, lookup=lookup, **options)
   2686 
   2687     def render(self, *args, **kwargs):
   2688         for dictarg in args: kwargs.update(dictarg)
   2689         _defaults = self.defaults.copy()
   2690         _defaults.update(kwargs)
   2691         return self.tpl.render(**_defaults)
   2692 
   2693 
   2694 class CheetahTemplate(BaseTemplate):
   2695     def prepare(self, **options):
   2696         from Cheetah.Template import Template
   2697         self.context = threading.local()
   2698         self.context.vars = {}
   2699         options['searchList'] = [self.context.vars]
   2700         if self.source:
   2701             self.tpl = Template(source=self.source, **options)
   2702         else:
   2703             self.tpl = Template(file=self.filename, **options)
   2704 
   2705     def render(self, *args, **kwargs):
   2706         for dictarg in args: kwargs.update(dictarg)
   2707         self.context.vars.update(self.defaults)
   2708         self.context.vars.update(kwargs)
   2709         out = str(self.tpl)
   2710         self.context.vars.clear()
   2711         return out
   2712 
   2713 
   2714 class Jinja2Template(BaseTemplate):
   2715     def prepare(self, filters=None, tests=None, **kwargs):
   2716         from jinja2 import Environment, FunctionLoader
   2717         if 'prefix' in kwargs: # TODO: to be removed after a while
   2718             raise RuntimeError('The keyword argument `prefix` has been removed. '
   2719                 'Use the full jinja2 environment name line_statement_prefix instead.')
   2720         self.env = Environment(loader=FunctionLoader(self.loader), **kwargs)
   2721         if filters: self.env.filters.update(filters)
   2722         if tests: self.env.tests.update(tests)
   2723         if self.source:
   2724             self.tpl = self.env.from_string(self.source)
   2725         else:
   2726             self.tpl = self.env.get_template(self.filename)
   2727 
   2728     def render(self, *args, **kwargs):
   2729         for dictarg in args: kwargs.update(dictarg)
   2730         _defaults = self.defaults.copy()
   2731         _defaults.update(kwargs)
   2732         return self.tpl.render(**_defaults)
   2733 
   2734     def loader(self, name):
   2735         fname = self.search(name, self.lookup)
   2736         if not fname: return
   2737         with open(fname, "rb") as f:
   2738             return f.read().decode(self.encoding)
   2739 
   2740 
   2741 class SimpleTALTemplate(BaseTemplate):
   2742     ''' Deprecated, do not use. '''
   2743     def prepare(self, **options):
   2744         depr('The SimpleTAL template handler is deprecated'\
   2745              ' and will be removed in 0.12')
   2746         from simpletal import simpleTAL
   2747         if self.source:
   2748             self.tpl = simpleTAL.compileHTMLTemplate(self.source)
   2749         else:
   2750             with open(self.filename, 'rb') as fp:
   2751                 self.tpl = simpleTAL.compileHTMLTemplate(tonat(fp.read()))
   2752 
   2753     def render(self, *args, **kwargs):
   2754         from simpletal import simpleTALES
   2755         for dictarg in args: kwargs.update(dictarg)
   2756         context = simpleTALES.Context()
   2757         for k,v in self.defaults.items():
   2758             context.addGlobal(k, v)
   2759         for k,v in kwargs.items():
   2760             context.addGlobal(k, v)
   2761         output = StringIO()
   2762         self.tpl.expand(context, output)
   2763         return output.getvalue()
   2764 
   2765 
   2766 class SimpleTemplate(BaseTemplate):
   2767     blocks = ('if', 'elif', 'else', 'try', 'except', 'finally', 'for', 'while',
   2768               'with', 'def', 'class')
   2769     dedent_blocks = ('elif', 'else', 'except', 'finally')
   2770 
   2771     @lazy_attribute
   2772     def re_pytokens(cls):
   2773         ''' This matches comments and all kinds of quoted strings but does
   2774             NOT match comments (#...) within quoted strings. (trust me) '''
   2775         return re.compile(r'''
   2776             (''(?!')|""(?!")|'{6}|"{6}    # Empty strings (all 4 types)
   2777              |'(?:[^\\']|\\.)+?'          # Single quotes (')
   2778              |"(?:[^\\"]|\\.)+?"          # Double quotes (")
   2779              |'{3}(?:[^\\]|\\.|\n)+?'{3}  # Triple-quoted strings (')
   2780              |"{3}(?:[^\\]|\\.|\n)+?"{3}  # Triple-quoted strings (")
   2781              |\#.*                        # Comments
   2782             )''', re.VERBOSE)
   2783 
   2784     def prepare(self, escape_func=html_escape, noescape=False, **kwargs):
   2785         self.cache = {}
   2786         enc = self.encoding
   2787         self._str = lambda x: touni(x, enc)
   2788         self._escape = lambda x: escape_func(touni(x, enc))
   2789         if noescape:
   2790             self._str, self._escape = self._escape, self._str
   2791 
   2792     @classmethod
   2793     def split_comment(cls, code):
   2794         """ Removes comments (#...) from python code. """
   2795         if '#' not in code: return code
   2796         #: Remove comments only (leave quoted strings as they are)
   2797         subf = lambda m: '' if m.group(0)[0]=='#' else m.group(0)
   2798         return re.sub(cls.re_pytokens, subf, code)
   2799 
   2800     @cached_property
   2801     def co(self):
   2802         return compile(self.code, self.filename or '<string>', 'exec')
   2803 
   2804     @cached_property
   2805     def code(self):
   2806         stack = [] # Current Code indentation
   2807         lineno = 0 # Current line of code
   2808         ptrbuffer = [] # Buffer for printable strings and token tuple instances
   2809         codebuffer = [] # Buffer for generated python code
   2810         multiline = dedent = oneline = False
   2811         template = self.source or open(self.filename, 'rb').read()
   2812 
   2813         def yield_tokens(line):
   2814             for i, part in enumerate(re.split(r'\{\{(.*?)\}\}', line)):
   2815                 if i % 2:
   2816                     if part.startswith('!'): yield 'RAW', part[1:]
   2817                     else: yield 'CMD', part
   2818                 else: yield 'TXT', part
   2819 
   2820         def flush(): # Flush the ptrbuffer
   2821             if not ptrbuffer: return
   2822             cline = ''
   2823             for line in ptrbuffer:
   2824                 for token, value in line:
   2825                     if token == 'TXT': cline += repr(value)
   2826                     elif token == 'RAW': cline += '_str(%s)' % value
   2827                     elif token == 'CMD': cline += '_escape(%s)' % value
   2828                     cline +=  ', '
   2829                 cline = cline[:-2] + '\\\n'
   2830             cline = cline[:-2]
   2831             if cline[:-1].endswith('\\\\\\\\\\n'):
   2832                 cline = cline[:-7] + cline[-1] # 'nobr\\\\\n' --> 'nobr'
   2833             cline = '_printlist([' + cline + '])'
   2834             del ptrbuffer[:] # Do this before calling code() again
   2835             code(cline)
   2836 
   2837         def code(stmt):
   2838             for line in stmt.splitlines():
   2839                 codebuffer.append('  ' * len(stack) + line.strip())
   2840 
   2841         for line in template.splitlines(True):
   2842             lineno += 1
   2843             line = touni(line, self.encoding)
   2844             sline = line.lstrip()
   2845             if lineno <= 2:
   2846                 m = re.match(r"%\s*#.*coding[:=]\s*([-\w.]+)", sline)
   2847                 if m: self.encoding = m.group(1)
   2848                 if m: line = line.replace('coding','coding (removed)')
   2849             if sline and sline[0] == '%' and sline[:2] != '%%':
   2850                 line = line.split('%',1)[1].lstrip() # Full line following the %
   2851                 cline = self.split_comment(line).strip()
   2852                 cmd = re.split(r'[^a-zA-Z0-9_]', cline)[0]
   2853                 flush() # You are actually reading this? Good luck, it's a mess :)
   2854                 if cmd in self.blocks or multiline:
   2855                     cmd = multiline or cmd
   2856                     dedent = cmd in self.dedent_blocks # "else:"
   2857                     if dedent and not oneline and not multiline:
   2858                         cmd = stack.pop()
   2859                     code(line)
   2860                     oneline = not cline.endswith(':') # "if 1: pass"
   2861                     multiline = cmd if cline.endswith('\\') else False
   2862                     if not oneline and not multiline:
   2863                         stack.append(cmd)
   2864                 elif cmd == 'end' and stack:
   2865                     code('#end(%s) %s' % (stack.pop(), line.strip()[3:]))
   2866                 elif cmd == 'include':
   2867                     p = cline.split(None, 2)[1:]
   2868                     if len(p) == 2:
   2869                         code("_=_include(%s, _stdout, %s)" % (repr(p[0]), p[1]))
   2870                     elif p:
   2871                         code("_=_include(%s, _stdout)" % repr(p[0]))
   2872                     else: # Empty %include -> reverse of %rebase
   2873                         code("_printlist(_base)")
   2874                 elif cmd == 'rebase':
   2875                     p = cline.split(None, 2)[1:]
   2876                     if len(p) == 2:
   2877                         code("globals()['_rebase']=(%s, dict(%s))" % (repr(p[0]), p[1]))
   2878                     elif p:
   2879                         code("globals()['_rebase']=(%s, {})" % repr(p[0]))
   2880                 else:
   2881                     code(line)
   2882             else: # Line starting with text (not '%') or '%%' (escaped)
   2883                 if line.strip().startswith('%%'):
   2884                     line = line.replace('%%', '%', 1)
   2885                 ptrbuffer.append(yield_tokens(line))
   2886         flush()
   2887         return '\n'.join(codebuffer) + '\n'
   2888 
   2889     def subtemplate(self, _name, _stdout, *args, **kwargs):
   2890         for dictarg in args: kwargs.update(dictarg)
   2891         if _name not in self.cache:
   2892             self.cache[_name] = self.__class__(name=_name, lookup=self.lookup)
   2893         return self.cache[_name].execute(_stdout, kwargs)
   2894 
   2895     def execute(self, _stdout, *args, **kwargs):
   2896         for dictarg in args: kwargs.update(dictarg)
   2897         env = self.defaults.copy()
   2898         env.update({'_stdout': _stdout, '_printlist': _stdout.extend,
   2899                '_include': self.subtemplate, '_str': self._str,
   2900                '_escape': self._escape, 'get': env.get,
   2901                'setdefault': env.setdefault, 'defined': env.__contains__})
   2902         env.update(kwargs)
   2903         eval(self.co, env)
   2904         if '_rebase' in env:
   2905             subtpl, rargs = env['_rebase']
   2906             rargs['_base'] = _stdout[:] #copy stdout
   2907             del _stdout[:] # clear stdout
   2908             return self.subtemplate(subtpl,_stdout,rargs)
   2909         return env
   2910 
   2911     def render(self, *args, **kwargs):
   2912         """ Render the template using keyword arguments as local variables. """
   2913         for dictarg in args: kwargs.update(dictarg)
   2914         stdout = []
   2915         self.execute(stdout, kwargs)
   2916         return ''.join(stdout)
   2917 
   2918 
   2919 def template(*args, **kwargs):
   2920     '''
   2921     Get a rendered template as a string iterator.
   2922     You can use a name, a filename or a template string as first parameter.
   2923     Template rendering arguments can be passed as dictionaries
   2924     or directly (as keyword arguments).
   2925     '''
   2926     tpl = args[0] if args else None
   2927     template_adapter = kwargs.pop('template_adapter', SimpleTemplate)
   2928     if tpl not in TEMPLATES or DEBUG:
   2929         settings = kwargs.pop('template_settings', {})
   2930         lookup = kwargs.pop('template_lookup', TEMPLATE_PATH)
   2931         if isinstance(tpl, template_adapter):
   2932             TEMPLATES[tpl] = tpl
   2933             if settings: TEMPLATES[tpl].prepare(**settings)
   2934         elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl:
   2935             TEMPLATES[tpl] = template_adapter(source=tpl, lookup=lookup, **settings)
   2936         else:
   2937             TEMPLATES[tpl] = template_adapter(name=tpl, lookup=lookup, **settings)
   2938     if not TEMPLATES[tpl]:
   2939         abort(500, 'Template (%s) not found' % tpl)
   2940     for dictarg in args[1:]: kwargs.update(dictarg)
   2941     return TEMPLATES[tpl].render(kwargs)
   2942 
   2943 mako_template = functools.partial(template, template_adapter=MakoTemplate)
   2944 cheetah_template = functools.partial(template, template_adapter=CheetahTemplate)
   2945 jinja2_template = functools.partial(template, template_adapter=Jinja2Template)
   2946 simpletal_template = functools.partial(template, template_adapter=SimpleTALTemplate)
   2947 
   2948 
   2949 def view(tpl_name, **defaults):
   2950     ''' Decorator: renders a template for a handler.
   2951         The handler can control its behavior like that:
   2952 
   2953           - return a dict of template vars to fill out the template
   2954           - return something other than a dict and the view decorator will not
   2955             process the template, but return the handler result as is.
   2956             This includes returning a HTTPResponse(dict) to get,
   2957             for instance, JSON with autojson or other castfilters.
   2958     '''
   2959     def decorator(func):
   2960         @functools.wraps(func)
   2961         def wrapper(*args, **kwargs):
   2962             result = func(*args, **kwargs)
   2963             if isinstance(result, (dict, DictMixin)):
   2964                 tplvars = defaults.copy()
   2965                 tplvars.update(result)
   2966                 return template(tpl_name, **tplvars)
   2967             return result
   2968         return wrapper
   2969     return decorator
   2970 
   2971 mako_view = functools.partial(view, template_adapter=MakoTemplate)
   2972 cheetah_view = functools.partial(view, template_adapter=CheetahTemplate)
   2973 jinja2_view = functools.partial(view, template_adapter=Jinja2Template)
   2974 simpletal_view = functools.partial(view, template_adapter=SimpleTALTemplate)
   2975 
   2976 
   2977 
   2978 
   2979 
   2980 
   2981 ###############################################################################
   2982 # Constants and Globals ########################################################
   2983 ###############################################################################
   2984 
   2985 
   2986 TEMPLATE_PATH = ['./', './views/']
   2987 TEMPLATES = {}
   2988 DEBUG = False
   2989 NORUN = False # If set, run() does nothing. Used by load_app()
   2990 
   2991 #: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found')
   2992 HTTP_CODES = httplib.responses
   2993 HTTP_CODES[418] = "I'm a teapot" # RFC 2324
   2994 HTTP_CODES[428] = "Precondition Required"
   2995 HTTP_CODES[429] = "Too Many Requests"
   2996 HTTP_CODES[431] = "Request Header Fields Too Large"
   2997 HTTP_CODES[511] = "Network Authentication Required"
   2998 _HTTP_STATUS_LINES = dict((k, '%d %s'%(k,v)) for (k,v) in HTTP_CODES.items())
   2999 
   3000 #: The default template used for error pages. Override with @error()
   3001 ERROR_PAGE_TEMPLATE = """
   3002 %try:
   3003     %from bottle import DEBUG, HTTP_CODES, request, touni
   3004     %status_name = HTTP_CODES.get(e.status, 'Unknown').title()
   3005     <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
   3006     <html>
   3007         <head>
   3008             <title>Error {{e.status}}: {{status_name}}</title>
   3009             <style type="text/css">
   3010               html {background-color: #eee; font-family: sans;}
   3011               body {background-color: #fff; border: 1px solid #ddd;
   3012                     padding: 15px; margin: 15px;}
   3013               pre {background-color: #eee; border: 1px solid #ddd; padding: 5px;}
   3014             </style>
   3015         </head>
   3016         <body>
   3017             <h1>Error {{e.status}}: {{status_name}}</h1>
   3018             <p>Sorry, the requested URL <tt>{{repr(request.url)}}</tt>
   3019                caused an error:</p>
   3020             <pre>{{e.output}}</pre>
   3021             %if DEBUG and e.exception:
   3022               <h2>Exception:</h2>
   3023               <pre>{{repr(e.exception)}}</pre>
   3024             %end
   3025             %if DEBUG and e.traceback:
   3026               <h2>Traceback:</h2>
   3027               <pre>{{e.traceback}}</pre>
   3028             %end
   3029         </body>
   3030     </html>
   3031 %except ImportError:
   3032     <b>ImportError:</b> Could not generate the error page. Please add bottle to
   3033     the import path.
   3034 %end
   3035 """
   3036 
   3037 #: A thread-safe instance of :class:`LocalRequest`. If accessed from within a 
   3038 #: request callback, this instance always refers to the *current* request
   3039 #: (even on a multithreaded server).
   3040 request = LocalRequest()
   3041 
   3042 #: A thread-safe instance of :class:`LocalResponse`. It is used to change the
   3043 #: HTTP response for the *current* request.
   3044 response = LocalResponse()
   3045 
   3046 #: A thread-safe namespace. Not used by Bottle.
   3047 local = threading.local()
   3048 
   3049 # Initialize app stack (create first empty Bottle app)
   3050 # BC: 0.6.4 and needed for run()
   3051 app = default_app = AppStack()
   3052 app.push()
   3053 
   3054 #: A virtual package that redirects import statements.
   3055 #: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`.
   3056 ext = _ImportRedirect(__name__+'.ext', 'bottle_%s').module
   3057 
   3058 if __name__ == '__main__':
   3059     opt, args, parser = _cmd_options, _cmd_args, _cmd_parser
   3060     if opt.version:
   3061         _stdout('Bottle %s\n'%__version__)
   3062         sys.exit(0)
   3063     if not args:
   3064         parser.print_help()
   3065         _stderr('\nError: No application specified.\n')
   3066         sys.exit(1)
   3067 
   3068     sys.path.insert(0, '.')
   3069     sys.modules.setdefault('bottle', sys.modules['__main__'])
   3070 
   3071     host, port = (opt.bind or 'localhost'), 8080
   3072     if ':' in host:
   3073         host, port = host.rsplit(':', 1)
   3074 
   3075     run(args[0], host=host, port=port, server=opt.server,
   3076         reloader=opt.reload, plugins=opt.plugin, debug=opt.debug)
   3077 
   3078 
   3079 
   3080 
   3081 # THE END