github.com/emc-advanced-dev/unik@v0.0.0-20190717152701-a58d3e8e33b7/docs/examples/example-python3-httpd/bin/bottle.py (about)

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